diff options
author | Petr Mrázek <peterix@gmail.com> | 2013-08-17 13:40:51 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2013-08-17 13:40:51 +0200 |
commit | 253067c782955380bbf66ac0475dc954375b1ff4 (patch) | |
tree | ca97e231fd3a764256d95b5fc8d08fc25ff72161 /depends | |
parent | 77e80665422c4e97e2286418ab55e20c4030023b (diff) | |
download | MultiMC-253067c782955380bbf66ac0475dc954375b1ff4.tar MultiMC-253067c782955380bbf66ac0475dc954375b1ff4.tar.gz MultiMC-253067c782955380bbf66ac0475dc954375b1ff4.tar.lz MultiMC-253067c782955380bbf66ac0475dc954375b1ff4.tar.xz MultiMC-253067c782955380bbf66ac0475dc954375b1ff4.zip |
Move all the things (YES. Move them.)
Also, implemented some basic modlist logic, to be wired up.
Diffstat (limited to 'depends')
106 files changed, 22680 insertions, 0 deletions
diff --git a/depends/classparser/CMakeLists.txt b/depends/classparser/CMakeLists.txt new file mode 100644 index 00000000..5a48e002 --- /dev/null +++ b/depends/classparser/CMakeLists.txt @@ -0,0 +1,41 @@ +project(classparser) + +set(CMAKE_AUTOMOC ON) + +# Find Qt +find_package(Qt5Core REQUIRED) + +# Include Qt headers. +include_directories(${Qt5Base_INCLUDE_DIRS}) + +SET(CLASSPARSER_HEADERS +include/classparser_config.h + +# Public headers +include/javautils.h + +# Private headers +src/annotations.h +src/classfile.h +src/constants.h +src/errors.h +src/javaendian.h +src/membuffer.h +) + +SET(CLASSPARSER_SOURCES +src/javautils.cpp +src/annotations.cpp +) + +# Set the include dir path. +SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) + +# Include self. +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +add_definitions(-DCLASSPARSER_LIBRARY) + +add_library(classparser SHARED ${CLASSPARSER_SOURCES} ${CLASSPARSER_HEADERS}) +qt5_use_modules(classparser Core) diff --git a/depends/classparser/include/classparser_config.h b/depends/classparser/include/classparser_config.h new file mode 100644 index 00000000..fe6a2ab9 --- /dev/null +++ b/depends/classparser/include/classparser_config.h @@ -0,0 +1,23 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QtCore/QtGlobal> + +#ifdef CLASSPARSER_LIBRARY +# define CLASSPARSER_EXPORT Q_DECL_EXPORT +#else +# define CLASSPARSER_EXPORT Q_DECL_IMPORT +#endif + diff --git a/depends/classparser/include/javautils.h b/depends/classparser/include/javautils.h new file mode 100644 index 00000000..63e5ec26 --- /dev/null +++ b/depends/classparser/include/javautils.h @@ -0,0 +1,29 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include <QString> +#include "classparser_config.h" + +#define MCVer_Unknown "Unknown" + +namespace javautils +{ + /** + * @brief Get the version from a minecraft.jar by parsing its class files. Expensive! + */ + QString GetMinecraftJarVersion(QString jar); +} diff --git a/depends/classparser/src/annotations.cpp b/depends/classparser/src/annotations.cpp new file mode 100644 index 00000000..fc0c98fa --- /dev/null +++ b/depends/classparser/src/annotations.cpp @@ -0,0 +1,83 @@ +#include "classfile.h" +#include "annotations.h" +#include <sstream> + +namespace java +{ + std::string annotation::toString() + { + std::ostringstream ss; + ss << "Annotation type : " << type_index << " - " << pool[type_index].str_data << std::endl; + ss << "Contains " << name_val_pairs.size() << " pairs:" << std::endl; + for(unsigned i = 0; i < name_val_pairs.size(); i++) + { + std::pair<uint16_t, element_value *> &val = name_val_pairs[i]; + auto name_idx = val.first; + ss << pool[name_idx].str_data << "(" << name_idx << ")" << " = " << val.second->toString() << std::endl; + } + return ss.str(); + } + + annotation * annotation::read (util::membuffer& input, constant_pool& pool) + { + uint16_t type_index = 0; + input.read_be(type_index); + annotation * ann = new annotation(type_index,pool); + + uint16_t num_pairs = 0; + input.read_be(num_pairs); + while(num_pairs) + { + uint16_t name_idx = 0; + // read name index + input.read_be(name_idx); + auto elem = element_value::readElementValue(input,pool); + // read value + ann->add_pair(name_idx, elem); + num_pairs --; + } + return ann; + } + + element_value* element_value::readElementValue ( util::membuffer& input, java::constant_pool& pool ) + { + element_value_type type = INVALID; + input.read(type); + uint16_t index = 0; + uint16_t index2 = 0; + std::vector <element_value *> vals; + switch (type) + { + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_DOUBLE: + case PRIMITIVE_FLOAT: + case PRIMITIVE_INT: + case PRIMITIVE_LONG: + case PRIMITIVE_SHORT: + case PRIMITIVE_BOOLEAN: + case STRING: + input.read_be(index); + return new element_value_simple(type, index, pool); + case ENUM_CONSTANT: + input.read_be(index); + input.read_be(index2); + return new element_value_enum(type, index, index2, pool); + case CLASS: // Class + input.read_be(index); + return new element_value_class(type, index, pool); + case ANNOTATION: // Annotation + // FIXME: runtime visibility info needs to be passed from parent + return new element_value_annotation(ANNOTATION, annotation::read(input, pool), pool); + case ARRAY: // Array + input.read_be(index); + for (int i = 0; i < index; i++) + { + vals.push_back(element_value::readElementValue(input, pool)); + } + return new element_value_array(ARRAY, vals, pool); + default: + throw new java::classfile_exception(); + } + } +}
\ No newline at end of file diff --git a/depends/classparser/src/annotations.h b/depends/classparser/src/annotations.h new file mode 100644 index 00000000..b115dc0b --- /dev/null +++ b/depends/classparser/src/annotations.h @@ -0,0 +1,252 @@ +#pragma once +#include "classfile.h" +#include <map> +#include <vector> + +namespace java +{ + enum element_value_type : uint8_t + { + INVALID = 0, + STRING = 's', + ENUM_CONSTANT = 'e', + CLASS = 'c', + ANNOTATION = '@', + ARRAY = '[', // one array dimension + PRIMITIVE_INT = 'I', // integer + PRIMITIVE_BYTE = 'B', // signed byte + PRIMITIVE_CHAR = 'C', // Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16 + PRIMITIVE_DOUBLE = 'D', // double-precision floating-point value + PRIMITIVE_FLOAT = 'F', // single-precision floating-point value + PRIMITIVE_LONG = 'J', // long integer + PRIMITIVE_SHORT = 'S', // signed short + PRIMITIVE_BOOLEAN = 'Z' // true or false + }; + /** + * The element_value structure is a discriminated union representing the value of an element-value pair. + * It is used to represent element values in all attributes that describe annotations + * - RuntimeVisibleAnnotations + * - RuntimeInvisibleAnnotations + * - RuntimeVisibleParameterAnnotations + * - RuntimeInvisibleParameterAnnotations). + * + * The element_value structure has the following format: + */ + class element_value + { + protected: + element_value_type type; + constant_pool & pool; + + public: + element_value(element_value_type type, constant_pool & pool): type(type), pool(pool) {}; + + element_value_type getElementValueType() + { + return type; + } + + virtual std::string toString() = 0; + + static element_value * readElementValue(util::membuffer & input, constant_pool & pool); + }; + + /** + * Each value of the annotations table represents a single runtime-visible annotation on a program element. + * The annotation structure has the following format: + */ + class annotation + { + public: + typedef std::vector< std::pair<uint16_t, element_value * > > value_list; + protected: + /** + * The value of the type_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a field descriptor representing the annotation type corresponding + * to the annotation represented by this annotation structure. + */ + uint16_t type_index; + /** + * map between element_name_index and value. + * + * The value of the element_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure representing + * a valid field descriptor (§4.3.2) that denotes the name of the annotation type element represented + * by this element_value_pairs entry. + */ + value_list name_val_pairs; + /** + * Reference to the parent constant pool + */ + constant_pool & pool; + public: + annotation(uint16_t type_index, constant_pool& pool):type_index(type_index), pool(pool) {}; + ~annotation() + { + for(unsigned i = 0 ; i < name_val_pairs.size(); i++) + { + delete name_val_pairs[i].second; + } + } + void add_pair(uint16_t key, element_value * value) + { + name_val_pairs.push_back(std::make_pair(key, value)); + }; + value_list::const_iterator begin() + { + return name_val_pairs.cbegin(); + } + value_list::const_iterator end() + { + return name_val_pairs.cend(); + } + std::string toString(); + static annotation * read(util::membuffer & input, constant_pool & pool); + }; + typedef std::vector<annotation *> annotation_table; + + + /// type for simple value annotation elements + class element_value_simple : public element_value + { + protected: + /// index of the constant in the constant pool + uint16_t index; + public: + element_value_simple(element_value_type type, uint16_t index , constant_pool& pool): + element_value(type, pool), index(index) + { + // TODO: verify consistency + }; + uint16_t getIndex() + { + return index; + } + virtual std::string toString() + { + return pool[index].toString(); + }; + }; + /// The enum_const_value item is used if the tag item is 'e'. + class element_value_enum : public element_value + { + protected: + /** + * The value of the type_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a valid field descriptor (§4.3.2) that denotes the internal form of the binary + * name (§4.2.1) of the type of the enum constant represented by this element_value structure. + */ + uint16_t typeIndex; + /** + * The value of the const_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the simple name of the enum constant represented by this element_value structure. + */ + uint16_t valueIndex; + public: + element_value_enum(element_value_type type, uint16_t typeIndex, uint16_t valueIndex, constant_pool& pool): + element_value(type, pool), typeIndex(typeIndex), valueIndex(valueIndex) + { + // TODO: verify consistency + } + uint16_t getValueIndex() + { + return valueIndex; + } + uint16_t getTypeIndex() + { + return typeIndex; + } + virtual std::string toString() + { + return "enum value"; + }; + }; + + class element_value_class : public element_value + { + protected: + /** + * The class_info_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the return descriptor (§4.3.3) of the type that is reified by the class + * represented by this element_value structure. + * + * For example, 'V' for Void.class, 'Ljava/lang/Object;' for Object, etc. + * + * Or in plain english, you can store type information in annotations. Yay. + */ + uint16_t classIndex; + public: + element_value_class(element_value_type type, uint16_t classIndex, constant_pool& pool): + element_value(type, pool), classIndex(classIndex) + { + // TODO: verify consistency + } + uint16_t getIndex() + { + return classIndex; + } + virtual std::string toString() + { + return "class"; + }; + }; + + /// nested annotations... yay + class element_value_annotation : public element_value + { + private: + annotation * nestedAnnotation; + public: + element_value_annotation(element_value_type type, annotation * nestedAnnotation, constant_pool& pool): + element_value(type, pool), nestedAnnotation(nestedAnnotation) + {}; + ~element_value_annotation() + { + if(nestedAnnotation) + { + delete nestedAnnotation; + nestedAnnotation = nullptr; + } + } + virtual std::string toString() + { + return "nested annotation"; + }; + }; + + /// and arrays! + class element_value_array : public element_value + { + public: + typedef std::vector <element_value *> elem_vec; + protected: + elem_vec values; + public: + element_value_array ( element_value_type type, std::vector <element_value *>& values, constant_pool& pool ): + element_value(type, pool), values(values) + {}; + ~element_value_array () + { + for(unsigned i = 0; i < values.size();i++) + { + delete values[i]; + } + }; + elem_vec::const_iterator begin() + { + return values.cbegin(); + } + elem_vec::const_iterator end() + { + return values.cend(); + } + virtual std::string toString() + { + return "array"; + }; + }; +}
\ No newline at end of file diff --git a/depends/classparser/src/classfile.h b/depends/classparser/src/classfile.h new file mode 100644 index 00000000..33207e99 --- /dev/null +++ b/depends/classparser/src/classfile.h @@ -0,0 +1,153 @@ +#pragma once +#include "membuffer.h" +#include "constants.h" +#include "annotations.h" +#include <map> +namespace java +{ + /** + * Class representing a Java .class file + */ + class classfile : public util::membuffer + { + public: + classfile(char * data, std::size_t size) : membuffer(data, size) + { + valid = false; + is_synthetic = false; + read_be(magic); + if(magic != 0xCAFEBABE) + throw new classfile_exception(); + read_be(minor_version); + read_be(major_version); + constants.load(*this); + read_be(access_flags); + read_be(this_class); + read_be(super_class); + + // Interfaces + uint16_t iface_count = 0; + read_be(iface_count); + while (iface_count) + { + uint16_t iface; + read_be(iface); + interfaces.push_back(iface); + iface_count --; + } + + // Fields + // read fields (and attributes from inside fields) (and possible inner classes. yay for recursion!) + // for now though, we will ignore all attributes + /* + * field_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t field_count = 0; + read_be(field_count); + while (field_count) + { + // skip field stuff + skip(6); + // and skip field attributes + uint16_t attr_count = 0; + read_be(attr_count); + while(attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count --; + } + field_count --; + } + + // class methods + /* + * method_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t method_count = 0; + read_be(method_count); + while( method_count ) + { + skip(6); + // and skip method attributes + uint16_t attr_count = 0; + read_be(attr_count); + while(attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count --; + } + method_count --; + } + + // class attributes + // there are many kinds of attributes. this is just the generic wrapper structure. + // type is decided by attribute name. extensions to the standard are *possible* + // class annotations are one kind of a attribute (one per class) + /* + * attribute_info + * { + * u2 attribute_name_index; + * u4 attribute_length; + * u1 info[attribute_length]; + * } + */ + uint16_t class_attr_count = 0; + read_be(class_attr_count); + while(class_attr_count) + { + uint16_t name_idx = 0; + read_be(name_idx); + uint32_t attr_length = 0; + read_be(attr_length); + + auto name = constants[name_idx]; + if(name.str_data == "RuntimeVisibleAnnotations") + { + uint16_t num_annotations = 0; + read_be(num_annotations); + while (num_annotations) + { + visible_class_annotations.push_back(annotation::read(*this, constants)); + num_annotations --; + } + } + else skip(attr_length); + class_attr_count --; + } + valid = true; + }; + bool valid; + bool is_synthetic; + uint32_t magic; + uint16_t minor_version; + uint16_t major_version; + constant_pool constants; + uint16_t access_flags; + uint16_t this_class; + uint16_t super_class; + // interfaces this class implements ? must be. investigate. + std::vector<uint16_t> interfaces; + // FIXME: doesn't free up memory on delete + java::annotation_table visible_class_annotations; + }; +}
\ No newline at end of file diff --git a/depends/classparser/src/constants.h b/depends/classparser/src/constants.h new file mode 100644 index 00000000..61aa5687 --- /dev/null +++ b/depends/classparser/src/constants.h @@ -0,0 +1,212 @@ +#pragma once +#include "errors.h" +#include <sstream> + +namespace java +{ + class constant + { + public: + enum type_t : uint8_t + { + j_hole = 0, // HACK: this is a hole in the array, because java is crazy + j_string_data = 1, + j_int = 3, + j_float = 4, + j_long = 5, + j_double = 6, + j_class = 7, + j_string = 8, + j_fieldref = 9, + j_methodref = 10, + j_interface_methodref = 11, + j_nameandtype = 12 + } type; + + constant(util::membuffer & buf ) + { + buf.read(type); + // invalid constant type! + if(type > j_nameandtype || type == (type_t)0 || type == (type_t)2) + throw new classfile_exception(); + + // load data depending on type + switch(type) + { + case j_float: + case j_int: + buf.read_be(int_data); // same as float data really + break; + case j_double: + case j_long: + buf.read_be(long_data); // same as double + break; + case j_class: + buf.read_be(ref_type.class_idx); + break; + case j_fieldref: + case j_methodref: + case j_interface_methodref: + buf.read_be(ref_type.class_idx); + buf.read_be(ref_type.name_and_type_idx); + break; + case j_string: + buf.read_be(index); + break; + case j_string_data: + // HACK HACK: for now, we call these UTF-8 and do no further processing. + // Later, we should do some decoding. It's really modified UTF-8 + // * U+0000 is represented as 0xC0,0x80 invalid character + // * any single zero byte ends the string + // * characters above U+10000 are encoded like in CESU-8 + buf.read_jstr(str_data); + break; + case j_nameandtype: + buf.read_be(name_and_type.name_index); + buf.read_be(name_and_type.descriptor_index); + break; + } + } + + constant(int fake) + { + type = j_hole; + } + + std::string toString() + { + std::ostringstream ss; + switch(type) + { + case j_hole: + ss << "Fake legacy entry"; + break; + case j_float: + ss << "Float: " << float_data; + break; + case j_double: + ss << "Double: " << double_data; + break; + case j_int: + ss << "Int: " << int_data; + break; + case j_long: + ss << "Long: " << long_data; + break; + case j_string_data: + ss << "StrData: " << str_data; + break; + case j_string: + ss << "Str: " << index; + break; + case j_fieldref: + ss << "FieldRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_methodref: + ss << "MethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_interface_methodref: + ss << "IfMethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_class: + ss << "Class: " << ref_type.class_idx; + break; + case j_nameandtype: + ss << "NameAndType: " << name_and_type.name_index << " " << name_and_type.descriptor_index; + break; + } + return ss.str(); + } + + std::string str_data; /** String data in 'modified utf-8'.*/ + // store everything here. + union + { + int32_t int_data; + int64_t long_data; + float float_data; + double double_data; + uint16_t index; + struct + { + /** + * Class reference: + * an index within the constant pool to a UTF-8 string containing + * the fully qualified class name (in internal format) + * Used for j_class, j_fieldref, j_methodref and j_interface_methodref + */ + uint16_t class_idx; + // used for j_fieldref, j_methodref and j_interface_methodref + uint16_t name_and_type_idx; + } ref_type; + struct + { + uint16_t name_index; + uint16_t descriptor_index; + } name_and_type; + }; + }; + + /** + * A helper class that represents the custom container used in Java class file for storage of constants + */ + class constant_pool + { + public: + /** + * Create a pool of constants + */ + constant_pool(){} + /** + * Load a java constant pool + */ + void load(util::membuffer & buf) + { + uint16_t length = 0; + buf.read_be(length); + length --; + uint16_t index = 1; + const constant * last_constant = nullptr; + while(length) + { + const constant & cnst = constant(buf); + constants.push_back(cnst); + last_constant = &constants[constants.size() - 1]; + if(last_constant->type == constant::j_double || last_constant->type == constant::j_long) + { + // push in a fake constant to preserve indexing + constants.push_back(constant(0)); + length-=2; + index+=2; + } + else + { + length--; + index++; + } + } + } + typedef std::vector<java::constant> container_type; + /** + * Access constants based on jar file index numbers (index of the first element is 1) + */ + java::constant & operator[](std::size_t constant_index) + { + if(constant_index == 0 || constant_index > constants.size()) + { + throw new classfile_exception(); + } + return constants[constant_index - 1]; + }; + container_type::const_iterator begin() const + { + return constants.begin(); + }; + container_type::const_iterator end() const + { + return constants.end(); + } + private: + container_type constants; + }; +} diff --git a/depends/classparser/src/errors.h b/depends/classparser/src/errors.h new file mode 100644 index 00000000..c02b07c8 --- /dev/null +++ b/depends/classparser/src/errors.h @@ -0,0 +1,6 @@ +#pragma once +#include <exception> +namespace java +{ + class classfile_exception : public std::exception {}; +} diff --git a/depends/classparser/src/javaendian.h b/depends/classparser/src/javaendian.h new file mode 100644 index 00000000..fa6207fe --- /dev/null +++ b/depends/classparser/src/javaendian.h @@ -0,0 +1,62 @@ +#pragma once +#include <stdint.h> + +/** + * Swap bytes between big endian and local number representation + */ +namespace util +{ +#ifdef MULTIMC_BIG_ENDIAN +inline uint64_t bigswap(uint64_t x) +{ + return x; +}; +inline uint32_t bigswap(uint32_t x) +{ + return x; +}; +inline uint16_t bigswap(uint16_t x) +{ + return x; +}; +inline int64_t bigswap(int64_t x) +{ + return x; +}; +inline int32_t bigswap(int32_t x) +{ + return x; +}; +inline int16_t bigswap(int16_t x) +{ + return x; +}; +#else +inline uint64_t bigswap(uint64_t x) +{ + return (x>>56) | ((x<<40) & 0x00FF000000000000) | ((x<<24) & 0x0000FF0000000000) | ((x<<8) & 0x000000FF00000000) | + ((x>>8) & 0x00000000FF000000) | ((x>>24) & 0x0000000000FF0000) | ((x>>40) & 0x000000000000FF00) | (x<<56); +}; +inline uint32_t bigswap(uint32_t x) +{ + return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); +}; +inline uint16_t bigswap(uint16_t x) +{ + return (x>>8) | (x<<8); +}; +inline int64_t bigswap(int64_t x) +{ + return (x>>56) | ((x<<40) & 0x00FF000000000000) | ((x<<24) & 0x0000FF0000000000) | ((x<<8) & 0x000000FF00000000) | + ((x>>8) & 0x00000000FF000000) | ((x>>24) & 0x0000000000FF0000) | ((x>>40) & 0x000000000000FF00) | (x<<56); +}; +inline int32_t bigswap(int32_t x) +{ + return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); +}; +inline int16_t bigswap(int16_t x) +{ + return (x>>8) | (x<<8); +}; +#endif +} diff --git a/depends/classparser/src/javautils.cpp b/depends/classparser/src/javautils.cpp new file mode 100644 index 00000000..4a359031 --- /dev/null +++ b/depends/classparser/src/javautils.cpp @@ -0,0 +1,81 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "multimc_pragma.h" +#include "classfile.h" +#include "javautils.h" + +#include <QFile> +#include <quazipfile.h> + +namespace javautils +{ + +QString GetMinecraftJarVersion(QString jarName) +{ + QString version = MCVer_Unknown; + + // check if minecraft.jar exists + QFile jar(jarName); + if (!jar.exists()) + return version; + + // open minecraft.jar + QuaZip zip(&jar); + if (!zip.open(QuaZip::mdUnzip)) + return version; + + // open Minecraft.class + zip.setCurrentFile("net/minecraft/client/Minecraft.class", QuaZip::csSensitive); + QuaZipFile Minecraft(&zip); + if (!Minecraft.open(QuaZipFile::ReadOnly)) + return version; + + // read Minecraft.class + qint64 size = Minecraft.size(); + char *classfile = new char[size]; + Minecraft.read(classfile, size); + + // parse Minecraft.class + try { + char *temp = classfile; + java::classfile MinecraftClass(temp, size); + java::constant_pool constants = MinecraftClass.constants; + for(java::constant_pool::container_type::const_iterator iter=constants.begin(); + iter != constants.end(); iter++) + { + const java::constant & constant = *iter; + if (constant.type != java::constant::j_string_data) + continue; + const std::string & str = constant.str_data; + if (str.compare(0, 20, "Minecraft Minecraft ") == 0) + { + version = str.substr(20).data(); + break; + } + } + } catch(java::classfile_exception &) {} + + // clean up + delete[] classfile; + Minecraft.close(); + zip.close(); + jar.close(); + + return version; +} + +} diff --git a/depends/classparser/src/membuffer.h b/depends/classparser/src/membuffer.h new file mode 100644 index 00000000..2ea3a69b --- /dev/null +++ b/depends/classparser/src/membuffer.h @@ -0,0 +1,64 @@ +#pragma once +#include <stdint.h> +#include <string> +#include <vector> +#include <exception> +#include "javaendian.h" + +namespace util +{ + class membuffer + { + public: + membuffer(char * buffer, std::size_t size) + { + current = start = buffer; + end = start + size; + } + ~membuffer() + { + // maybe? possibly? left out to avoid confusion. for now. + //delete start; + } + /** + * Read some value. That's all ;) + */ + template <class T> + void read(T& val) + { + val = *(T *)current; + current += sizeof(T); + } + /** + * Read a big-endian number + * valid for 2-byte, 4-byte and 8-byte variables + */ + template <class T> + void read_be(T& val) + { + val = util::bigswap(*(T *)current); + current += sizeof(T); + } + /** + * Read a string in the format: + * 2B length (big endian, unsigned) + * length bytes data + */ + void read_jstr(std::string & str) + { + uint16_t length = 0; + read_be(length); + str.append(current,length); + current += length; + } + /** + * Skip N bytes + */ + void skip (std::size_t N) + { + current += N; + } + private: + char * start, *end, *current; + }; +} diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt new file mode 100644 index 00000000..3fa2b044 --- /dev/null +++ b/depends/groupview/CMakeLists.txt @@ -0,0 +1,41 @@ +project(libGroupView) + +set(CMAKE_AUTOMOC ON) + +# Find Qt +find_package(Qt5Core REQUIRED) +find_package(Qt5Widgets REQUIRED) + +# Include Qt headers. +include_directories(${Qt5Base_INCLUDE_DIRS}) + +SET(LIBGROUPVIEW_HEADERS +include/groupview_config.h + +# Public headers +include/categorizedsortfilterproxymodel.h +include/categorizedview.h +include/categorydrawer.h + +# Private headers +src/categorizedsortfilterproxymodel_p.h +src/categorizedview_p.h +) + +SET(LIBGROUPVIEW_SOURCES +src/categorizedsortfilterproxymodel.cpp +src/categorizedview.cpp +src/categorydrawer.cpp +) + +# Set the include dir path. +SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) + +# Include self. +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +add_definitions(-DLIBGROUPVIEW_LIBRARY) + +add_library(libGroupView SHARED ${LIBGROUPVIEW_SOURCES} ${LIBGROUPVIEW_HEADERS}) +qt5_use_modules(libGroupView Core Widgets) diff --git a/depends/groupview/include/categorizedsortfilterproxymodel.h b/depends/groupview/include/categorizedsortfilterproxymodel.h new file mode 100644 index 00000000..d90fb254 --- /dev/null +++ b/depends/groupview/include/categorizedsortfilterproxymodel.h @@ -0,0 +1,175 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org> + * Copyright (C) 2007 John Tapsell <tapsell@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_H +#define KCATEGORIZEDSORTFILTERPROXYMODEL_H + +#include <QSortFilterProxyModel> + +#include <groupview_config.h> + +class QItemSelection; + + +/** + * This class lets you categorize a view. It is meant to be used along with + * KCategorizedView class. + * + * In general terms all you need to do is to reimplement subSortLessThan() and + * compareCategories() methods. In order to make categorization work, you need + * to also call setCategorizedModel() class to enable it, since the categorization + * is disabled by default. + * + * @see KCategorizedView + * + * @author Rafael Fernández López <ereslibre@kde.org> + */ +class LIBGROUPVIEW_EXPORT KCategorizedSortFilterProxyModel + : public QSortFilterProxyModel +{ +public: + enum AdditionalRoles + { + // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) + // to define additional roles. + CategoryDisplayRole = 0x17CE990A, ///< This role is used for asking the category to a given index + + CategorySortRole = 0x27857E60 ///< This role is used for sorting categories. You can return a + ///< string or a long long value. Strings will be sorted alphabetically + ///< while long long will be sorted by their value. Please note that this + ///< value won't be shown on the view, is only for sorting purposes. What will + ///< be shown as "Category" on the view will be asked with the role + ///< CategoryDisplayRole. + }; + + KCategorizedSortFilterProxyModel ( QObject *parent = 0 ); + virtual ~KCategorizedSortFilterProxyModel(); + + /** + * Overridden from QSortFilterProxyModel. Sorts the source model using + * @p column for the given @p order. + */ + virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder ); + + /** + * @return whether the model is categorized or not. Disabled by default. + */ + bool isCategorizedModel() const; + + /** + * Enables or disables the categorization feature. + * + * @param categorizedModel whether to enable or disable the categorization feature. + */ + void setCategorizedModel ( bool categorizedModel ); + + /** + * @return the column being used for sorting. + */ + int sortColumn() const; + + /** + * @return the sort order being used for sorting. + */ + Qt::SortOrder sortOrder() const; + + /** + * Set if the sorting using CategorySortRole will use a natural comparison + * in the case that strings were returned. If enabled, QString::localeAwareCompare + * will be used for sorting. + * + * @param sortCategoriesByNaturalComparison whether to sort using a natural comparison or not. + */ + void setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison ); + + /** + * @return whether it is being used a natural comparison for sorting. Enabled by default. + */ + bool sortCategoriesByNaturalComparison() const; + +protected: + /** + * Overridden from QSortFilterProxyModel. If you are subclassing + * KCategorizedSortFilterProxyModel, you will probably not need to reimplement this + * method. + * + * It calls compareCategories() to sort by category. If the both items are in the + * same category (i.e. compareCategories returns 0), then subSortLessThan is called. + * + * @return Returns true if the item @p left is less than the item @p right when sorting. + * + * @warning You usually won't need to reimplement this method when subclassing + * from KCategorizedSortFilterProxyModel. + */ + virtual bool lessThan ( const QModelIndex &left, const QModelIndex &right ) const; + + /** + * This method has a similar purpose as lessThan() has on QSortFilterProxyModel. + * It is used for sorting items that are in the same category. + * + * @return Returns true if the item @p left is less than the item @p right when sorting. + */ + virtual bool subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const; + + /** + * This method compares the category of the @p left index with the category + * of the @p right index. + * + * Internally and if not reimplemented, this method will ask for @p left and + * @p right models for role CategorySortRole. In order to correctly sort + * categories, the data() metod of the model should return a qlonglong (or numeric) value, or + * a QString object. QString objects will be sorted with QString::localeAwareCompare if + * sortCategoriesByNaturalComparison() is true. + * + * @note Please have present that: + * QString(QChar(QChar::ObjectReplacementCharacter)) > + * QString(QChar(QChar::ReplacementCharacter)) > + * [ all possible strings ] > + * QString(); + * + * This means that QString() will be sorted the first one, while + * QString(QChar(QChar::ObjectReplacementCharacter)) and + * QString(QChar(QChar::ReplacementCharacter)) will be sorted in last + * position. + * + * @warning Please note that data() method of the model should return always + * information of the same type. If you return a QString for an index, + * you should return always QStrings for all indexes for role CategorySortRole + * in order to correctly sort categories. You can't mix by returning + * a QString for one index, and a qlonglong for other. + * + * @note If you need a more complex layout, you will have to reimplement this + * method. + * + * @return A negative value if the category of @p left should be placed before the + * category of @p right. 0 if @p left and @p right are on the same category, and + * a positive value if the category of @p left should be placed after the + * category of @p right. + */ + virtual int compareCategories ( const QModelIndex &left, const QModelIndex &right ) const; + +private: + class Private; + Private *const d; +}; + + +#endif // KCATEGORIZEDSORTFILTERPROXYMODEL_H diff --git a/depends/groupview/include/categorizedview.h b/depends/groupview/include/categorizedview.h new file mode 100644 index 00000000..81b1dbb1 --- /dev/null +++ b/depends/groupview/include/categorizedview.h @@ -0,0 +1,332 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCATEGORIZEDVIEW_H +#define KCATEGORIZEDVIEW_H + +#include <QListView> + +#include <groupview_config.h> + +class KCategoryDrawer; + +/** + * @short Item view for listing items in a categorized fashion optionally + * + * KCategorizedView basically has the same functionality as QListView, only that it also lets you + * layout items in a way that they are categorized visually. + * + * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer + * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be + * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true). + * + * The way it works (if categorization enabled): + * + * - When sorting, it does more things than QListView does. It will ask the model for the + * special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return + * a QString or an int in order to tell the view the order of categories. In this sense, for + * instance, if we are sorting by name ascending, "A" would be before than "B". If we are + * sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are + * also sorted. + * + * - When the view has to paint, it will ask the model with the role CategoryDisplayRole + * (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if + * we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example. + * + * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own + * drawing. + * + * @note All examples cited before talk about filesystems and such, but have present that this + * is a completely generic class, and it can be used for whatever your purpose is. For + * instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In + * this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the + * same ("Mammal" and "Oviparous"). + * + * @note There is a really performance boost if CategorySortRole returns an int instead of a QString. + * Have present that this role is asked (n * log n) times when sorting and compared. Comparing + * ints is always faster than comparing strings, whithout mattering how fast the string + * comparison is. Consider thinking of a way of returning ints instead of QStrings if your + * model can contain a high number of items. + * + * @warning Note that for really drawing items in blocks you will need some things to be done: + * - The model set to this view has to be (or inherit if you want to do special stuff + * in it) KCategorizedSortFilterProxyModel. + * - This model needs to be set setCategorizedModel to true. + * - Set a category drawer by calling setCategoryDrawer. + * + * @see KCategorizedSortFilterProxyModel, KCategoryDrawer + * + * @author Rafael Fernández López <ereslibre@kde.org> + */ +class LIBGROUPVIEW_EXPORT KCategorizedView + : public QListView +{ + Q_OBJECT + Q_PROPERTY ( int categorySpacing READ categorySpacing WRITE setCategorySpacing ) + Q_PROPERTY ( bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors ) + Q_PROPERTY ( bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks ) + +public: + KCategorizedView ( QWidget *parent = 0 ); + + ~KCategorizedView(); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void setModel ( QAbstractItemModel *model ); + + /** + * Calls to setGridSizeOwn(). + */ + void setGridSize ( const QSize &size ); + + /** + * @warning note that setGridSize is not virtual in the base class (QListView), so if you are + * calling to this method, make sure you have a KCategorizedView pointer around. This + * means that something like: + * @code + * QListView *lv = new KCategorizedView(); + * lv->setGridSize(mySize); + * @endcode + * + * will not call to the expected setGridSize method. Instead do something like this: + * + * @code + * QListView *lv; + * ... + * KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv); + * if (cv) { + * cv->setGridSizeOwn(mySize); + * } else { + * lv->setGridSize(mySize); + * } + * @endcode + * + * @note this method will call to QListView::setGridSize among other operations. + * + * @since 4.4 + */ + void setGridSizeOwn ( const QSize &size ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual QRect visualRect ( const QModelIndex &index ) const; + + /** + * Returns the current category drawer. + */ + KCategoryDrawer *categoryDrawer() const; + + /** + * The category drawer that will be used for drawing categories. + */ + void setCategoryDrawer ( KCategoryDrawer *categoryDrawer ); + + /** + * @return Category spacing. The spacing between categories. + * + * @since 4.4 + */ + int categorySpacing() const; + + /** + * Stablishes the category spacing. This is the spacing between categories. + * + * @since 4.4 + */ + void setCategorySpacing ( int categorySpacing ); + + /** + * @return Whether blocks should be drawn with alternating colors. + * + * @since 4.4 + */ + bool alternatingBlockColors() const; + + /** + * Sets whether blocks should be drawn with alternating colors. + * + * @since 4.4 + */ + void setAlternatingBlockColors ( bool enable ); + + /** + * @return Whether blocks can be collapsed or not. + * + * @since 4.4 + */ + bool collapsibleBlocks() const; + + /** + * Sets whether blocks can be collapsed or not. + * + * @since 4.4 + */ + void setCollapsibleBlocks ( bool enable ); + + /** + * @return Block of indexes that are into @p category. + * + * @since 4.5 + */ + QModelIndexList block ( const QString &category ); + + /** + * @return Block of indexes that are represented by @p representative. + * + * @since 4.5 + */ + QModelIndexList block ( const QModelIndex &representative ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual QModelIndex indexAt ( const QPoint &point ) const; + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void reset(); + + /** + * Signify that all item delegates size hints return the same fixed size + */ + void setUniformItemWidths(bool enable); + + /** + * Do all item delegate size hints return the same fixed size? + */ + bool uniformItemWidths() const; + +protected: + /** + * Reimplemented from QWidget. + */ + virtual void paintEvent ( QPaintEvent *event ); + + /** + * Reimplemented from QWidget. + */ + virtual void resizeEvent ( QResizeEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void setSelection ( const QRect &rect, + QItemSelectionModel::SelectionFlags flags ); + + /** + * Reimplemented from QWidget. + */ + virtual void mouseMoveEvent ( QMouseEvent *event ); + + /** + * Reimplemented from QWidget. + */ + virtual void mousePressEvent ( QMouseEvent *event ); + + /** + * Reimplemented from QWidget. + */ + virtual void mouseReleaseEvent ( QMouseEvent *event ); + + /** + * Reimplemented from QWidget. + */ + virtual void leaveEvent ( QEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void startDrag ( Qt::DropActions supportedActions ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void dragMoveEvent ( QDragMoveEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void dragEnterEvent ( QDragEnterEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void dragLeaveEvent ( QDragLeaveEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void dropEvent ( QDropEvent *event ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual QModelIndex moveCursor ( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void rowsAboutToBeRemoved ( const QModelIndex &parent, + int start, + int end ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void updateGeometries(); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void currentChanged ( const QModelIndex ¤t, + const QModelIndex &previous ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void dataChanged ( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + + /** + * Reimplemented from QAbstractItemView. + */ + virtual void rowsInserted ( const QModelIndex &parent, + int start, + int end ); + +protected Q_SLOTS: + /** + * @internal + * Reposition items as needed. + */ + virtual void slotLayoutChanged(); + virtual void slotCollapseOrExpandClicked ( QModelIndex ); + +private: + class Private; + Private *const d; +}; + +#endif // KCATEGORIZEDVIEW_H diff --git a/depends/groupview/include/categorydrawer.h b/depends/groupview/include/categorydrawer.h new file mode 100644 index 00000000..f37422ec --- /dev/null +++ b/depends/groupview/include/categorydrawer.h @@ -0,0 +1,179 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCATEGORYDRAWER_H +#define KCATEGORYDRAWER_H + +#include <groupview_config.h> + +#include <QtCore/QObject> +#include <QtGui/QMouseEvent> + +class QPainter; +class QModelIndex; +class QStyleOption; +class KCategorizedView; + + +/** + * @since 4.5 + */ +class LIBGROUPVIEW_EXPORT KCategoryDrawer + : public QObject +{ + friend class KCategorizedView; + Q_OBJECT + + +public: + KCategoryDrawer ( KCategorizedView *view ); + virtual ~KCategoryDrawer(); + + /** + * @return The view this category drawer is associated with. + */ + KCategorizedView *view() const; + + /** + * This method purpose is to draw a category represented by the given + * @param index with the given @param sortRole sorting role + * + * @note This method will be called one time per category, always with the + * first element in that category + */ + virtual void drawCategory ( const QModelIndex &index, + int sortRole, + const QStyleOption &option, + QPainter *painter ) const; + + /** + * @return The category height for the category representated by index @p index with + * style options @p option. + */ + virtual int categoryHeight ( const QModelIndex &index, const QStyleOption &option ) const; + + //TODO KDE5: make virtual as leftMargin + /** + * @note 0 by default + * + * @since 4.4 + */ + int leftMargin() const; + + /** + * @note call to this method on the KCategoryDrawer constructor to set the left margin + * + * @since 4.4 + */ + void setLeftMargin ( int leftMargin ); + + //TODO KDE5: make virtual as rightMargin + /** + * @note 0 by default + * + * @since 4.4 + */ + int rightMargin() const; + + /** + * @note call to this method on the KCategoryDrawer constructor to set the right margin + * + * @since 4.4 + */ + void setRightMargin ( int rightMargin ); + + KCategoryDrawer &operator= ( const KCategoryDrawer &cd ); +protected: + /** + * Method called when the mouse button has been pressed. + * + * @param index The representative index of the block of items. + * @param blockRect The rect occupied by the block of items. + * @param event The mouse event. + * + * @warning You explicitly have to determine whether the event has been accepted or not. You + * have to call event->accept() or event->ignore() at all possible case branches in + * your code. + */ + virtual void mouseButtonPressed ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); + + /** + * Method called when the mouse button has been released. + * + * @param index The representative index of the block of items. + * @param blockRect The rect occupied by the block of items. + * @param event The mouse event. + * + * @warning You explicitly have to determine whether the event has been accepted or not. You + * have to call event->accept() or event->ignore() at all possible case branches in + * your code. + */ + virtual void mouseButtonReleased ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); + + /** + * Method called when the mouse has been moved. + * + * @param index The representative index of the block of items. + * @param blockRect The rect occupied by the block of items. + * @param event The mouse event. + */ + virtual void mouseMoved ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); + + /** + * Method called when the mouse button has been double clicked. + * + * @param index The representative index of the block of items. + * @param blockRect The rect occupied by the block of items. + * @param event The mouse event. + * + * @warning You explicitly have to determine whether the event has been accepted or not. You + * have to call event->accept() or event->ignore() at all possible case branches in + * your code. + */ + virtual void mouseButtonDoubleClicked ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); + + /** + * Method called when the mouse button has left this block. + * + * @param index The representative index of the block of items. + * @param blockRect The rect occupied by the block of items. + */ + virtual void mouseLeft ( const QModelIndex &index, const QRect &blockRect ); + +private: + class Private; + Private *const d; +Q_SIGNALS: + /** + * This signal becomes emitted when collapse or expand has been clicked. + */ + void collapseOrExpandClicked ( const QModelIndex &index ); + + /** + * Emit this signal on your subclass implementation to notify that something happened. Usually + * this will be triggered when you have received an event, and its position matched some "hot spot". + * + * You give this action the integer you want, and having connected this signal to your code, + * the connected slot can perform the needed changes (view, model, selection model, delegate...) + */ + void actionRequested ( int action, const QModelIndex &index ); +}; + +#endif // KCATEGORYDRAWER_H diff --git a/depends/groupview/include/groupview_config.h b/depends/groupview/include/groupview_config.h new file mode 100644 index 00000000..86bed139 --- /dev/null +++ b/depends/groupview/include/groupview_config.h @@ -0,0 +1,27 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#ifndef LIBINSTANCE_CONFIG_H +//#define LIBINSTANCE_CONFIG_H + +#include <QtCore/QtGlobal> + +#ifdef LIBGROUPVIEW_LIBRARY +# define LIBGROUPVIEW_EXPORT Q_DECL_EXPORT +#else +# define LIBGROUPVIEW_EXPORT Q_DECL_IMPORT +#endif + +//#endif // LIBINSTANCE_CONFIG_H diff --git a/depends/groupview/src/categorizedsortfilterproxymodel.cpp b/depends/groupview/src/categorizedsortfilterproxymodel.cpp new file mode 100644 index 00000000..09da9dd3 --- /dev/null +++ b/depends/groupview/src/categorizedsortfilterproxymodel.cpp @@ -0,0 +1,168 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org> + * Copyright (C) 2007 John Tapsell <tapsell@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "categorizedsortfilterproxymodel.h" +#include "categorizedsortfilterproxymodel_p.h" + +#include <limits.h> + +#include <QItemSelection> +#include <QStringList> +#include <QSize> + +KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel ( QObject *parent ) + : QSortFilterProxyModel ( parent ) + , d ( new Private() ) +{ +} + +KCategorizedSortFilterProxyModel::~KCategorizedSortFilterProxyModel() +{ + delete d; +} + +void KCategorizedSortFilterProxyModel::sort ( int column, Qt::SortOrder order ) +{ + d->sortColumn = column; + d->sortOrder = order; + + QSortFilterProxyModel::sort ( column, order ); +} + +bool KCategorizedSortFilterProxyModel::isCategorizedModel() const +{ + return d->categorizedModel; +} + +void KCategorizedSortFilterProxyModel::setCategorizedModel ( bool categorizedModel ) +{ + if ( categorizedModel == d->categorizedModel ) + { + return; + } + + d->categorizedModel = categorizedModel; + + invalidate(); +} + +int KCategorizedSortFilterProxyModel::sortColumn() const +{ + return d->sortColumn; +} + +Qt::SortOrder KCategorizedSortFilterProxyModel::sortOrder() const +{ + return d->sortOrder; +} + +void KCategorizedSortFilterProxyModel::setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison ) +{ + if ( sortCategoriesByNaturalComparison == d->sortCategoriesByNaturalComparison ) + { + return; + } + + d->sortCategoriesByNaturalComparison = sortCategoriesByNaturalComparison; + + invalidate(); +} + +bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const +{ + return d->sortCategoriesByNaturalComparison; +} + +bool KCategorizedSortFilterProxyModel::lessThan ( const QModelIndex &left, const QModelIndex &right ) const +{ + if ( d->categorizedModel ) + { + int compare = compareCategories ( left, right ); + + if ( compare > 0 ) // left is greater than right + { + return false; + } + else if ( compare < 0 ) // left is less than right + { + return true; + } + } + + return subSortLessThan ( left, right ); +} + +bool KCategorizedSortFilterProxyModel::subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const +{ + return QSortFilterProxyModel::lessThan ( left, right ); +} + +int KCategorizedSortFilterProxyModel::compareCategories ( const QModelIndex &left, const QModelIndex &right ) const +{ + QVariant l = ( left.model() ? left.model()->data ( left, CategorySortRole ) : QVariant() ); + QVariant r = ( right.model() ? right.model()->data ( right, CategorySortRole ) : QVariant() ); + + Q_ASSERT ( l.isValid() ); + Q_ASSERT ( r.isValid() ); + Q_ASSERT ( l.type() == r.type() ); + + if ( l.type() == QVariant::String ) + { + QString lstr = l.toString(); + QString rstr = r.toString(); + + /* + if ( d->sortCategoriesByNaturalComparison ) + { + return KStringHandler::naturalCompare ( lstr, rstr ); + } + else + { + */ + if ( lstr < rstr ) + { + return -1; + } + + if ( lstr > rstr ) + { + return 1; + } + + return 0; + //} + } + + qlonglong lint = l.toLongLong(); + qlonglong rint = r.toLongLong(); + + if ( lint < rint ) + { + return -1; + } + + if ( lint > rint ) + { + return 1; + } + + return 0; +} diff --git a/depends/groupview/src/categorizedsortfilterproxymodel_p.h b/depends/groupview/src/categorizedsortfilterproxymodel_p.h new file mode 100644 index 00000000..d7e7c9a0 --- /dev/null +++ b/depends/groupview/src/categorizedsortfilterproxymodel_p.h @@ -0,0 +1,48 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org> + * Copyright (C) 2007 John Tapsell <tapsell@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_P_H +#define KCATEGORIZEDSORTFILTERPROXYMODEL_P_H + +class KCategorizedSortFilterProxyModel; + +class KCategorizedSortFilterProxyModel::Private +{ +public: + Private() + : sortColumn ( 0 ) + , sortOrder ( Qt::AscendingOrder ) + , categorizedModel ( false ) + , sortCategoriesByNaturalComparison ( true ) + { + } + + ~Private() + { + } + + int sortColumn; + Qt::SortOrder sortOrder; + bool categorizedModel; + bool sortCategoriesByNaturalComparison; +}; + +#endif diff --git a/depends/groupview/src/categorizedview.cpp b/depends/groupview/src/categorizedview.cpp new file mode 100644 index 00000000..f4449949 --- /dev/null +++ b/depends/groupview/src/categorizedview.cpp @@ -0,0 +1,1713 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * IMPLEMENTATION NOTES: + * + * QListView::setRowHidden() and QListView::isRowHidden() are not taken into + * account. This methods should actually not exist. This effect should be handled + * by an hypothetical QSortFilterProxyModel which filters out the desired rows. + * + * In case this needs to be implemented, contact me, but I consider this a faulty + * design. + */ + +#include "categorizedview.h" +#include "categorizedview_p.h" + +#include <math.h> // trunc on C99 compliant systems +//#include <kdefakes.h> // trunc for not C99 compliant systems + +#include <QPainter> +#include <QScrollBar> +#include <QPaintEvent> + +#include "categorydrawer.h" +#include "categorizedsortfilterproxymodel.h" + +//BEGIN: Private part + +struct KCategorizedView::Private::Item +{ + Item() + : topLeft ( QPoint() ) + , size ( QSize() ) + { + } + + QPoint topLeft; + QSize size; +}; + +struct KCategorizedView::Private::Block +{ + Block() + : topLeft ( QPoint() ) + , height ( -1 ) + , firstIndex ( QModelIndex() ) + , quarantineStart ( QModelIndex() ) + , items ( QList<Item>() ) + , outOfQuarantine ( false ) + , alternate ( false ) + , collapsed ( false ) + { + } + + bool operator!= ( const Block &rhs ) const + { + return firstIndex != rhs.firstIndex; + } + + static bool lessThan ( const Block &left, const Block &right ) + { + Q_ASSERT ( left.firstIndex.isValid() ); + Q_ASSERT ( right.firstIndex.isValid() ); + return left.firstIndex.row() < right.firstIndex.row(); + } + + QPoint topLeft; + int height; + QPersistentModelIndex firstIndex; + // if we have n elements on this block, and we inserted an element at position i. The quarantine + // will start at index (i, column, parent). This means that for all elements j where i <= j <= n, the + // visual rect position of item j will have to be recomputed (cannot use the cached point). The quarantine + // will only affect the current block, since the rest of blocks can be affected only in the way + // that the whole block will have different offset, but items will keep the same relative position + // in terms of their parent blocks. + QPersistentModelIndex quarantineStart; + QList<Item> items; + + // this affects the whole block, not items separately. items contain the topLeft point relative + // to the block. Because of insertions or removals a whole block can be moved, so the whole block + // will enter in quarantine, what is faster than moving all items in absolute terms. + bool outOfQuarantine; + + // should we alternate its color ? is just a hint, could not be used + bool alternate; + bool collapsed; +}; + +KCategorizedView::Private::Private ( KCategorizedView *q ) + : q ( q ) + , proxyModel ( 0 ) + , categoryDrawer ( 0 ) + , categorySpacing ( 5 ) + , alternatingBlockColors ( false ) + , collapsibleBlocks ( false ) + , hoveredBlock ( new Block() ) + , hoveredIndex ( QModelIndex() ) + , pressedPosition ( QPoint() ) + , rubberBandRect ( QRect() ) + , constantItemWidth( 0 ) +{ +} + +KCategorizedView::Private::~Private() +{ + delete hoveredBlock; +} + +bool KCategorizedView::Private::isCategorized() const +{ + return proxyModel && categoryDrawer && proxyModel->isCategorizedModel(); +} + +QStyleOptionViewItemV4 KCategorizedView::Private::blockRect ( const QModelIndex &representative ) +{ + QStyleOptionViewItemV4 option ( q->viewOptions() ); + const int height = categoryDrawer->categoryHeight ( representative, option ); + const QString categoryDisplay = representative.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); + QPoint pos = blockPosition ( categoryDisplay ); + pos.ry() -= height; + option.rect.setTopLeft ( pos ); + option.rect.setWidth ( viewportWidth() + categoryDrawer->leftMargin() + categoryDrawer->rightMargin() ); + option.rect.setHeight ( height + blockHeight ( categoryDisplay ) ); + option.rect = mapToViewport ( option.rect ); + + return option; +} + +QPair<QModelIndex, QModelIndex> KCategorizedView::Private::intersectingIndexesWithRect ( const QRect &_rect ) const +{ + const int rowCount = proxyModel->rowCount(); + + const QRect rect = _rect.normalized(); + + // binary search to find out the top border + int bottom = 0; + int top = rowCount - 1; + while ( bottom <= top ) + { + const int middle = ( bottom + top ) / 2; + const QModelIndex index = proxyModel->index ( middle, q->modelColumn(), q->rootIndex() ); + QRect itemRect = q->visualRect ( index ); + const int verticalOff = q->verticalOffset(); + const int horizontalOff = q->horizontalOffset(); + itemRect.topLeft().ry() += verticalOff; + itemRect.topLeft().rx() += horizontalOff; + itemRect.bottomRight().ry() += verticalOff; + itemRect.bottomRight().rx() += horizontalOff; + if ( itemRect.bottomRight().y() <= rect.topLeft().y() ) + { + bottom = middle + 1; + } + else + { + top = middle - 1; + } + } + + const QModelIndex bottomIndex = proxyModel->index ( bottom, q->modelColumn(), q->rootIndex() ); + + // binary search to find out the bottom border + bottom = 0; + top = rowCount - 1; + while ( bottom <= top ) + { + const int middle = ( bottom + top ) / 2; + const QModelIndex index = proxyModel->index ( middle, q->modelColumn(), q->rootIndex() ); + QRect itemRect = q->visualRect ( index ); + const int verticalOff = q->verticalOffset(); + const int horizontalOff = q->horizontalOffset(); + itemRect.topLeft().ry() += verticalOff; + itemRect.topLeft().rx() += horizontalOff; + itemRect.bottomRight().ry() += verticalOff; + itemRect.bottomRight().rx() += horizontalOff; + if ( itemRect.topLeft().y() <= rect.bottomRight().y() ) + { + bottom = middle + 1; + } + else + { + top = middle - 1; + } + } + + const QModelIndex topIndex = proxyModel->index ( top, q->modelColumn(), q->rootIndex() ); + + return qMakePair ( bottomIndex, topIndex ); +} + +QPoint KCategorizedView::Private::blockPosition ( const QString &category ) +{ + Block &block = blocks[category]; + + if ( block.outOfQuarantine && !block.topLeft.isNull() ) + { + return block.topLeft; + } + + QPoint res ( categorySpacing, 0 ); + + const QModelIndex index = block.firstIndex; + + for ( QHash<QString, Private::Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it ) + { + Block &block = *it; + const QModelIndex categoryIndex = block.firstIndex; + if ( index.row() < categoryIndex.row() ) + { + continue; + } + res.ry() += categoryDrawer->categoryHeight ( categoryIndex, q->viewOptions() ) + categorySpacing; + if ( index.row() == categoryIndex.row() ) + { + continue; + } + res.ry() += blockHeight ( it.key() ); + } + + block.outOfQuarantine = true; + block.topLeft = res; + + return res; +} + +int KCategorizedView::Private::blockHeight ( const QString &category ) +{ + Block &block = blocks[category]; + + if ( block.collapsed ) + { + return 0; + } + + if ( block.height > -1 ) + { + return block.height; + } + + const QModelIndex firstIndex = block.firstIndex; + const QModelIndex lastIndex = proxyModel->index ( firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex() ); + const QRect topLeft = q->visualRect ( firstIndex ); + QRect bottomRight = q->visualRect ( lastIndex ); + + if ( hasGrid() ) + { + bottomRight.setHeight ( qMax ( bottomRight.height(), q->gridSize().height() ) ); + } + else + { + if ( !q->uniformItemSizes() ) + { + bottomRight.setHeight ( highestElementInLastRow ( block ) + q->spacing() * 2 ); + } + } + + const int height = bottomRight.bottomRight().y() - topLeft.topLeft().y() + 1; + block.height = height; + + return height; +} + +int KCategorizedView::Private::viewportWidth() const +{ + return q->viewport()->width() - categorySpacing * 2 - categoryDrawer->leftMargin() - categoryDrawer->rightMargin(); +} + +void KCategorizedView::Private::regenerateAllElements() +{ + for ( QHash<QString, Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it ) + { + Block &block = *it; + block.outOfQuarantine = false; + block.quarantineStart = block.firstIndex; + block.height = -1; + } +} + +void KCategorizedView::Private::rowsInserted ( const QModelIndex &parent, int start, int end ) +{ + if ( !isCategorized() ) + { + return; + } + + for ( int i = start; i <= end; ++i ) + { + const QModelIndex index = proxyModel->index ( i, q->modelColumn(), parent ); + + Q_ASSERT ( index.isValid() ); + + const QString category = categoryForIndex ( index ); + + Block &block = blocks[category]; + + //BEGIN: update firstIndex + // save as firstIndex in block if + // - it forced the category creation (first element on this category) + // - it is before the first row on that category + const QModelIndex firstIndex = block.firstIndex; + if ( !firstIndex.isValid() || index.row() < firstIndex.row() ) + { + block.firstIndex = index; + } + //END: update firstIndex + + Q_ASSERT ( block.firstIndex.isValid() ); + + const int firstIndexRow = block.firstIndex.row(); + + block.items.insert ( index.row() - firstIndexRow, Private::Item() ); + block.height = -1; + + q->visualRect ( index ); + q->viewport()->update(); + } + + //BEGIN: update the items that are in quarantine in affected categories + { + const QModelIndex lastIndex = proxyModel->index ( end, q->modelColumn(), parent ); + const QString category = categoryForIndex ( lastIndex ); + Private::Block &block = blocks[category]; + block.quarantineStart = block.firstIndex; + } + //END: update the items that are in quarantine in affected categories + + //BEGIN: mark as in quarantine those categories that are under the affected ones + { + const QModelIndex firstIndex = proxyModel->index ( start, q->modelColumn(), parent ); + const QString category = categoryForIndex ( firstIndex ); + const QModelIndex firstAffectedCategory = blocks[category].firstIndex; + //BEGIN: order for marking as alternate those blocks that are alternate + QList<Block> blockList = blocks.values(); + qSort ( blockList.begin(), blockList.end(), Block::lessThan ); + QList<int> firstIndexesRows; + foreach ( const Block &block, blockList ) + { + firstIndexesRows << block.firstIndex.row(); + } + //END: order for marking as alternate those blocks that are alternate + for ( QHash<QString, Private::Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it ) + { + Private::Block &block = *it; + if ( block.firstIndex.row() > firstAffectedCategory.row() ) + { + block.outOfQuarantine = false; + block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; + } + else if ( block.firstIndex.row() == firstAffectedCategory.row() ) + { + block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; + } + } + } + //END: mark as in quarantine those categories that are under the affected ones +} + +QRect KCategorizedView::Private::mapToViewport ( const QRect &rect ) const +{ + const int dx = -q->horizontalOffset(); + const int dy = -q->verticalOffset(); + return rect.adjusted ( dx, dy, dx, dy ); +} + +QRect KCategorizedView::Private::mapFromViewport ( const QRect &rect ) const +{ + const int dx = q->horizontalOffset(); + const int dy = q->verticalOffset(); + return rect.adjusted ( dx, dy, dx, dy ); +} + +int KCategorizedView::Private::highestElementInLastRow ( const Block &block ) const +{ + //Find the highest element in the last row + const QModelIndex lastIndex = proxyModel->index ( block.firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex() ); + const QRect prevRect = q->visualRect ( lastIndex ); + int res = prevRect.height(); + QModelIndex prevIndex = proxyModel->index ( lastIndex.row() - 1, q->modelColumn(), q->rootIndex() ); + if ( !prevIndex.isValid() ) + { + return res; + } + Q_FOREVER + { + const QRect tempRect = q->visualRect ( prevIndex ); + if ( tempRect.topLeft().y() < prevRect.topLeft().y() ) + { + break; + } + res = qMax ( res, tempRect.height() ); + if ( prevIndex == block.firstIndex ) + { + break; + } + prevIndex = proxyModel->index ( prevIndex.row() - 1, q->modelColumn(), q->rootIndex() ); + } + + return res; +} + +bool KCategorizedView::Private::hasGrid() const +{ + const QSize gridSize = q->gridSize(); + return gridSize.isValid() && !gridSize.isNull(); +} + +QString KCategorizedView::Private::categoryForIndex ( const QModelIndex &index ) const +{ + const QModelIndex categoryIndex = index.model()->index ( index.row(), proxyModel->sortColumn(), index.parent() ); + return categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); +} + +void KCategorizedView::Private::leftToRightVisualRect ( const QModelIndex &index, Item &item, + const Block &block, const QPoint &blockPos ) const +{ + const int firstIndexRow = block.firstIndex.row(); + + if ( hasGrid() ) + { + const int relativeRow = index.row() - firstIndexRow; + const int maxItemsPerRow = qMax ( viewportWidth() / q->gridSize().width(), 1 ); + if ( q->layoutDirection() == Qt::LeftToRight ) + { + item.topLeft.rx() = ( relativeRow % maxItemsPerRow ) * q->gridSize().width() + blockPos.x() + categoryDrawer->leftMargin(); + } + else + { + item.topLeft.rx() = viewportWidth() - ( ( relativeRow % maxItemsPerRow ) + 1 ) * q->gridSize().width() + categoryDrawer->leftMargin() + categorySpacing; + } + item.topLeft.ry() = ( relativeRow / maxItemsPerRow ) * q->gridSize().height(); + } + else + { + if ( q->uniformItemSizes() /*|| q->uniformItemWidths()*/ ) + { + const int relativeRow = index.row() - firstIndexRow; + const QSize itemSize = q->sizeHintForIndex ( index ); + //HACK: Why is the -2 needed? + const int maxItemsPerRow = qMax ( ( viewportWidth() - q->spacing() - 2 ) / ( itemSize.width() + q->spacing() ), 1 ); + if ( q->layoutDirection() == Qt::LeftToRight ) + { + item.topLeft.rx() = ( relativeRow % maxItemsPerRow ) * itemSize.width() + blockPos.x() + categoryDrawer->leftMargin(); + } + else + { + item.topLeft.rx() = viewportWidth() - ( relativeRow % maxItemsPerRow ) * itemSize.width() + categoryDrawer->leftMargin() + categorySpacing; + } + item.topLeft.ry() = ( relativeRow / maxItemsPerRow ) * itemSize.height(); + } + else + { + const QSize currSize = q->sizeHintForIndex ( index ); + if ( index != block.firstIndex ) + { + const int viewportW = viewportWidth() - q->spacing(); + QModelIndex prevIndex = proxyModel->index ( index.row() - 1, q->modelColumn(), q->rootIndex() ); + QRect prevRect = q->visualRect ( prevIndex ); + prevRect = mapFromViewport ( prevRect ); + if ( ( prevRect.bottomRight().x() + 1 ) + currSize.width() - blockPos.x() + q->spacing() > viewportW ) + { + // we have to check the whole previous row, and see which one was the + // highest. + Q_FOREVER + { + prevIndex = proxyModel->index ( prevIndex.row() - 1, q->modelColumn(), q->rootIndex() ); + QRect tempRect = q->visualRect ( prevIndex ); + tempRect = mapFromViewport ( tempRect ); + if ( tempRect.topLeft().y() < prevRect.topLeft().y() ) + { + break; + } + if ( tempRect.bottomRight().y() > prevRect.bottomRight().y() ) + { + prevRect = tempRect; + } + if ( prevIndex == block.firstIndex ) + { + break; + } + } + if ( q->layoutDirection() == Qt::LeftToRight ) + { + item.topLeft.rx() = categoryDrawer->leftMargin() + blockPos.x() + q->spacing(); + } + else + { + item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; + } + item.topLeft.ry() = ( prevRect.bottomRight().y() + 1 ) + q->spacing() - blockPos.y(); + } + else + { + if ( q->layoutDirection() == Qt::LeftToRight ) + { + item.topLeft.rx() = ( prevRect.bottomRight().x() + 1 ) + q->spacing(); + } + else + { + item.topLeft.rx() = ( prevRect.bottomLeft().x() - 1 ) - q->spacing() - item.size.width() + categoryDrawer->leftMargin() + categorySpacing; + } + item.topLeft.ry() = prevRect.topLeft().y() - blockPos.y(); + } + } + else + { + if ( q->layoutDirection() == Qt::LeftToRight ) + { + item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); + } + else + { + item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; + } + item.topLeft.ry() = q->spacing(); + } + } + } + item.size = q->sizeHintForIndex ( index ); +} + +void KCategorizedView::Private::topToBottomVisualRect ( const QModelIndex &index, Item &item, + const Block &block, const QPoint &blockPos ) const +{ + const int firstIndexRow = block.firstIndex.row(); + + if ( hasGrid() ) + { + const int relativeRow = index.row() - firstIndexRow; + item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); + item.topLeft.ry() = relativeRow * q->gridSize().height(); + } + else + { + if ( q->uniformItemSizes() ) + { + const int relativeRow = index.row() - firstIndexRow; + const QSize itemSize = q->sizeHintForIndex ( index ); + item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); + item.topLeft.ry() = relativeRow * itemSize.height(); + } + else + { + if ( index != block.firstIndex ) + { + QModelIndex prevIndex = proxyModel->index ( index.row() - 1, q->modelColumn(), q->rootIndex() ); + QRect prevRect = q->visualRect ( prevIndex ); + prevRect = mapFromViewport ( prevRect ); + item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); + item.topLeft.ry() = ( prevRect.bottomRight().y() + 1 ) + q->spacing() - blockPos.y(); + } + else + { + item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); + item.topLeft.ry() = q->spacing(); + } + } + } + item.size = q->sizeHintForIndex ( index ); + item.size.setWidth ( viewportWidth() ); +} + +void KCategorizedView::Private::_k_slotCollapseOrExpandClicked ( QModelIndex ) +{ +} + +//END: Private part + +//BEGIN: Public part + +KCategorizedView::KCategorizedView ( QWidget *parent ) + : QListView ( parent ) + , d ( new Private ( this ) ) +{ +} + +KCategorizedView::~KCategorizedView() +{ + delete d; +} + +void KCategorizedView::setModel ( QAbstractItemModel *model ) +{ + if ( d->proxyModel == model ) + { + return; + } + + d->blocks.clear(); + + if ( d->proxyModel ) + { + disconnect ( d->proxyModel, SIGNAL ( layoutChanged() ), this, SLOT ( slotLayoutChanged() ) ); + } + + d->proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*> ( model ); + + if ( d->proxyModel ) + { + connect ( d->proxyModel, SIGNAL ( layoutChanged() ), this, SLOT ( slotLayoutChanged() ) ); + } + + QListView::setModel ( model ); + + // if the model already had information inserted, update our data structures to it + if ( model->rowCount() ) + { + slotLayoutChanged(); + } +} + + +void KCategorizedView::setUniformItemWidths(bool enable) +{ + d->constantItemWidth = enable; +} + + +bool KCategorizedView::uniformItemWidths() const +{ + return d->constantItemWidth; +} + +void KCategorizedView::setGridSize ( const QSize &size ) +{ + setGridSizeOwn ( size ); +} + +void KCategorizedView::setGridSizeOwn ( const QSize &size ) +{ + d->regenerateAllElements(); + QListView::setGridSize ( size ); +} + +QRect KCategorizedView::visualRect ( const QModelIndex &index ) const +{ + if ( !d->isCategorized() ) + { + return QListView::visualRect ( index ); + } + + if ( !index.isValid() ) + { + return QRect(); + } + + const QString category = d->categoryForIndex ( index ); + + if ( !d->blocks.contains ( category ) ) + { + return QRect(); + } + + Private::Block &block = d->blocks[category]; + const int firstIndexRow = block.firstIndex.row(); + + Q_ASSERT ( block.firstIndex.isValid() ); + + if ( index.row() - firstIndexRow < 0 || index.row() - firstIndexRow >= block.items.count() ) + { + return QRect(); + } + + const QPoint blockPos = d->blockPosition ( category ); + + Private::Item &ritem = block.items[index.row() - firstIndexRow]; + + if ( ritem.topLeft.isNull() || ( block.quarantineStart.isValid() && + index.row() >= block.quarantineStart.row() ) ) + { + if ( flow() == LeftToRight ) + { + d->leftToRightVisualRect ( index, ritem, block, blockPos ); + } + else + { + d->topToBottomVisualRect ( index, ritem, block, blockPos ); + } + + //BEGIN: update the quarantine start + const bool wasLastIndex = ( index.row() == ( block.firstIndex.row() + block.items.count() - 1 ) ); + if ( index.row() == block.quarantineStart.row() ) + { + if ( wasLastIndex ) + { + block.quarantineStart = QModelIndex(); + } + else + { + const QModelIndex nextIndex = d->proxyModel->index ( index.row() + 1, modelColumn(), rootIndex() ); + block.quarantineStart = nextIndex; + } + } + //END: update the quarantine start + } + + // we get now the absolute position through the relative position of the parent block. do not + // save this on ritem, since this would override the item relative position in block terms. + Private::Item item ( ritem ); + item.topLeft.ry() += blockPos.y(); + + const QSize sizeHint = item.size; + + if ( d->hasGrid() ) + { + const QSize sizeGrid = gridSize(); + const QSize resultingSize = sizeHint.boundedTo ( sizeGrid ); + QRect res ( item.topLeft.x() + ( ( sizeGrid.width() - resultingSize.width() ) / 2 ), + item.topLeft.y(), resultingSize.width(), resultingSize.height() ); + if ( block.collapsed ) + { + // we can still do binary search, while we "hide" items. We move those items in collapsed + // blocks to the left and set a 0 height. + res.setLeft ( -resultingSize.width() ); + res.setHeight ( 0 ); + } + return d->mapToViewport ( res ); + } + + QRect res ( item.topLeft.x(), item.topLeft.y(), sizeHint.width(), sizeHint.height() ); + if ( block.collapsed ) + { + // we can still do binary search, while we "hide" items. We move those items in collapsed + // blocks to the left and set a 0 height. + res.setLeft ( -sizeHint.width() ); + res.setHeight ( 0 ); + } + return d->mapToViewport ( res ); +} + +KCategoryDrawer *KCategorizedView::categoryDrawer() const +{ + return d->categoryDrawer; +} + +void KCategorizedView::setCategoryDrawer ( KCategoryDrawer *categoryDrawer ) +{ + disconnect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ), + this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) ); + d->categoryDrawer = categoryDrawer; + + connect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ), + this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) ); +} + +int KCategorizedView::categorySpacing() const +{ + return d->categorySpacing; +} + +void KCategorizedView::setCategorySpacing ( int categorySpacing ) +{ + if ( d->categorySpacing == categorySpacing ) + { + return; + } + + d->categorySpacing = categorySpacing; + + for ( QHash<QString, Private::Block>::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it ) + { + Private::Block &block = *it; + block.outOfQuarantine = false; + } +} + +bool KCategorizedView::alternatingBlockColors() const +{ + return d->alternatingBlockColors; +} + +void KCategorizedView::setAlternatingBlockColors ( bool enable ) +{ + d->alternatingBlockColors = enable; +} + +bool KCategorizedView::collapsibleBlocks() const +{ + return d->collapsibleBlocks; +} + +void KCategorizedView::setCollapsibleBlocks ( bool enable ) +{ + d->collapsibleBlocks = enable; +} + +QModelIndexList KCategorizedView::block ( const QString &category ) +{ + QModelIndexList res; + const Private::Block &block = d->blocks[category]; + if ( block.height == -1 ) + { + return res; + } + QModelIndex current = block.firstIndex; + const int first = current.row(); + for ( int i = 1; i <= block.items.count(); ++i ) + { + if ( current.isValid() ) + { + res << current; + } + current = d->proxyModel->index ( first + i, modelColumn(), rootIndex() ); + } + return res; +} + +QModelIndexList KCategorizedView::block ( const QModelIndex &representative ) +{ + return block ( representative.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString() ); +} + +QModelIndex KCategorizedView::indexAt ( const QPoint &point ) const +{ + if ( !d->isCategorized() ) + { + return QListView::indexAt ( point ); + } + + const int rowCount = d->proxyModel->rowCount(); + if ( !rowCount ) + { + return QModelIndex(); + } + + // Binary search that will try to spot if there is an index under point + int bottom = 0; + int top = rowCount - 1; + while ( bottom <= top ) + { + const int middle = ( bottom + top ) / 2; + const QModelIndex index = d->proxyModel->index ( middle, modelColumn(), rootIndex() ); + QRect rect = visualRect ( index ); + const int verticalOff = verticalOffset(); + int horizontalOff = horizontalOffset(); + if ( layoutDirection() == Qt::RightToLeft ) + { + horizontalOff *= -1; + } + rect.topLeft().ry() += verticalOff; + rect.topLeft().rx() += horizontalOff; + rect.bottomRight().ry() += verticalOff; + rect.bottomRight().rx() += horizontalOff; + if ( rect.contains ( point ) ) + { + if ( index.model()->flags ( index ) & Qt::ItemIsEnabled ) + { + return index; + } + return QModelIndex(); + } + bool directionCondition; + if ( layoutDirection() == Qt::LeftToRight ) + { + directionCondition = point.x() > rect.bottomRight().x(); + } + else + { + directionCondition = point.x() < rect.bottomLeft().x(); + } + if ( point.y() > rect.bottomRight().y() || + ( point.y() > rect.topLeft().y() && point.y() < rect.bottomRight().y() && directionCondition ) ) + { + bottom = middle + 1; + } + else + { + top = middle - 1; + } + } + return QModelIndex(); +} + +void KCategorizedView::reset() +{ + d->blocks.clear(); + QListView::reset(); +} + +void KCategorizedView::paintEvent ( QPaintEvent *event ) +{ + if ( !d->isCategorized() ) + { + QListView::paintEvent ( event ); + return; + } + + const QPair<QModelIndex, QModelIndex> intersecting = d->intersectingIndexesWithRect ( viewport()->rect().intersected ( event->rect() ) ); + + QPainter p ( viewport() ); + p.save(); + + Q_ASSERT ( selectionModel()->model() == d->proxyModel ); + + //BEGIN: draw categories + QHash<QString, Private::Block>::ConstIterator it ( d->blocks.constBegin() ); + while ( it != d->blocks.constEnd() ) + { + const Private::Block &block = *it; + const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + QStyleOptionViewItemV4 option ( viewOptions() ); + option.features |= d->alternatingBlockColors && block.alternate ? QStyleOptionViewItemV4::Alternate + : QStyleOptionViewItemV4::None; + option.state |= !d->collapsibleBlocks || !block.collapsed ? QStyle::State_Open + : QStyle::State_None; + const int height = d->categoryDrawer->categoryHeight ( categoryIndex, option ); + QPoint pos = d->blockPosition ( it.key() ); + pos.ry() -= height; + option.rect.setTopLeft ( pos ); + option.rect.setWidth ( d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin() ); + option.rect.setHeight ( height + d->blockHeight ( it.key() ) ); + option.rect = d->mapToViewport ( option.rect ); + if ( !option.rect.intersects ( viewport()->rect() ) ) + { + ++it; + continue; + } + d->categoryDrawer->drawCategory ( categoryIndex, d->proxyModel->sortRole(), option, &p ); + ++it; + } + //END: draw categories + + if ( intersecting.first.isValid() && intersecting.second.isValid() ) + { + //BEGIN: draw items + int i = intersecting.first.row(); + int indexToCheckIfBlockCollapsed = i; + QModelIndex categoryIndex; + QString category; + Private::Block *block = 0; + while ( i <= intersecting.second.row() ) + { + //BEGIN: first check if the block is collapsed. if so, we have to skip the item painting + if ( i == indexToCheckIfBlockCollapsed ) + { + categoryIndex = d->proxyModel->index ( i, d->proxyModel->sortColumn(), rootIndex() ); + category = categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); + block = &d->blocks[category]; + indexToCheckIfBlockCollapsed = block->firstIndex.row() + block->items.count(); + if ( block->collapsed ) + { + i = indexToCheckIfBlockCollapsed; + continue; + } + } + //END: first check if the block is collapsed. if so, we have to skip the item painting + + Q_ASSERT ( block ); + + const bool alternateItem = ( i - block->firstIndex.row() ) % 2; + + const QModelIndex index = d->proxyModel->index ( i, modelColumn(), rootIndex() ); + const Qt::ItemFlags flags = d->proxyModel->flags ( index ); + QStyleOptionViewItemV4 option ( viewOptions() ); + option.rect = visualRect ( index ); + option.widget = this; + option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText + : QStyleOptionViewItemV2::None; + option.features |= alternatingRowColors() && alternateItem ? QStyleOptionViewItemV4::Alternate + : QStyleOptionViewItemV4::None; + if ( flags & Qt::ItemIsSelectable ) + { + option.state |= selectionModel()->isSelected ( index ) ? QStyle::State_Selected + : QStyle::State_None; + } + else + { + option.state &= ~QStyle::State_Selected; + } + option.state |= ( index == currentIndex() ) ? QStyle::State_HasFocus + : QStyle::State_None; + if ( ! ( flags & Qt::ItemIsEnabled ) ) + { + option.state &= ~QStyle::State_Enabled; + } + else + { + option.state |= ( index == d->hoveredIndex ) ? QStyle::State_MouseOver + : QStyle::State_None; + } + + itemDelegate ( index )->paint ( &p, option, index ); + ++i; + } + //END: draw items + } + + //BEGIN: draw selection rect + if ( isSelectionRectVisible() && d->rubberBandRect.isValid() ) + { + QStyleOptionRubberBand opt; + opt.initFrom ( this ); + opt.shape = QRubberBand::Rectangle; + opt.opaque = false; + opt.rect = d->mapToViewport ( d->rubberBandRect ).intersected ( viewport()->rect().adjusted ( -16, -16, 16, 16 ) ); + p.save(); + style()->drawControl ( QStyle::CE_RubberBand, &opt, &p ); + p.restore(); + } + //END: draw selection rect + + p.restore(); +} + +void KCategorizedView::resizeEvent ( QResizeEvent *event ) +{ + d->regenerateAllElements(); + QListView::resizeEvent ( event ); +} + +void KCategorizedView::setSelection ( const QRect &rect, + QItemSelectionModel::SelectionFlags flags ) +{ + if ( !d->isCategorized() ) + { + QListView::setSelection ( rect, flags ); + return; + } + + if ( rect.topLeft() == rect.bottomRight() ) + { + const QModelIndex index = indexAt ( rect.topLeft() ); + selectionModel()->select ( index, flags ); + return; + } + + const QPair<QModelIndex, QModelIndex> intersecting = d->intersectingIndexesWithRect ( rect ); + + QItemSelection selection; + + //TODO: think of a faster implementation + QModelIndex firstIndex; + QModelIndex lastIndex; + for ( int i = intersecting.first.row(); i <= intersecting.second.row(); ++i ) + { + const QModelIndex index = d->proxyModel->index ( i, modelColumn(), rootIndex() ); + const bool visualRectIntersects = visualRect ( index ).intersects ( rect ); + if ( firstIndex.isValid() ) + { + if ( visualRectIntersects ) + { + lastIndex = index; + } + else + { + selection << QItemSelectionRange ( firstIndex, lastIndex ); + firstIndex = QModelIndex(); + } + } + else if ( visualRectIntersects ) + { + firstIndex = index; + lastIndex = index; + } + } + + if ( firstIndex.isValid() ) + { + selection << QItemSelectionRange ( firstIndex, lastIndex ); + } + + selectionModel()->select ( selection, flags ); +} + +void KCategorizedView::mouseMoveEvent ( QMouseEvent *event ) +{ + QListView::mouseMoveEvent ( event ); + d->hoveredIndex = indexAt ( event->pos() ); + const SelectionMode itemViewSelectionMode = selectionMode(); + if ( state() == DragSelectingState && isSelectionRectVisible() && itemViewSelectionMode != SingleSelection + && itemViewSelectionMode != NoSelection ) + { + QRect rect ( d->pressedPosition, event->pos() + QPoint ( horizontalOffset(), verticalOffset() ) ); + rect = rect.normalized(); + update ( rect.united ( d->rubberBandRect ) ); + d->rubberBandRect = rect; + } + QHash<QString, Private::Block>::ConstIterator it ( d->blocks.constBegin() ); + while ( it != d->blocks.constEnd() ) + { + const Private::Block &block = *it; + const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + QStyleOptionViewItemV4 option ( viewOptions() ); + const int height = d->categoryDrawer->categoryHeight ( categoryIndex, option ); + QPoint pos = d->blockPosition ( it.key() ); + pos.ry() -= height; + option.rect.setTopLeft ( pos ); + option.rect.setWidth ( d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin() ); + option.rect.setHeight ( height + d->blockHeight ( it.key() ) ); + option.rect = d->mapToViewport ( option.rect ); + const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); + if ( option.rect.contains ( mousePos ) ) + { + if ( d->categoryDrawer && d->hoveredBlock->height != -1 && *d->hoveredBlock != block ) + { + const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); + d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); + *d->hoveredBlock = block; + d->hoveredCategory = it.key(); + viewport()->update ( option.rect ); + } + else if ( d->hoveredBlock->height == -1 ) + { + *d->hoveredBlock = block; + d->hoveredCategory = it.key(); + } + else if ( d->categoryDrawer ) + { + d->categoryDrawer->mouseMoved ( categoryIndex, option.rect, event ); + } + viewport()->update ( option.rect ); + return; + } + ++it; + } + if ( d->categoryDrawer && d->hoveredBlock->height != -1 ) + { + const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); + d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + viewport()->update ( option.rect ); + } +} + +void KCategorizedView::mousePressEvent ( QMouseEvent *event ) +{ + if ( event->button() == Qt::LeftButton ) + { + d->pressedPosition = event->pos(); + d->pressedPosition.rx() += horizontalOffset(); + d->pressedPosition.ry() += verticalOffset(); + } + if ( !d->categoryDrawer ) + { + QListView::mousePressEvent ( event ); + return; + } + QHash<QString, Private::Block>::ConstIterator it ( d->blocks.constBegin() ); + while ( it != d->blocks.constEnd() ) + { + const Private::Block &block = *it; + const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); + const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); + if ( option.rect.contains ( mousePos ) ) + { + if ( d->categoryDrawer ) + { + d->categoryDrawer->mouseButtonPressed ( categoryIndex, option.rect, event ); + } + viewport()->update ( option.rect ); + if ( !event->isAccepted() ) + { + QListView::mousePressEvent ( event ); + } + return; + } + ++it; + } + QListView::mousePressEvent ( event ); +} + +void KCategorizedView::mouseReleaseEvent ( QMouseEvent *event ) +{ + d->pressedPosition = QPoint(); + d->rubberBandRect = QRect(); + QHash<QString, Private::Block>::ConstIterator it ( d->blocks.constBegin() ); + while ( it != d->blocks.constEnd() ) + { + const Private::Block &block = *it; + const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); + const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); + if ( option.rect.contains ( mousePos ) ) + { + if ( d->categoryDrawer ) + { + d->categoryDrawer->mouseButtonReleased ( categoryIndex, option.rect, event ); + } + viewport()->update ( option.rect ); + if ( !event->isAccepted() ) + { + QListView::mouseReleaseEvent ( event ); + } + return; + } + ++it; + } + QListView::mouseReleaseEvent ( event ); +} + +void KCategorizedView::leaveEvent ( QEvent *event ) +{ + QListView::leaveEvent ( event ); + if ( d->hoveredIndex.isValid() ) + { + viewport()->update ( visualRect ( d->hoveredIndex ) ); + d->hoveredIndex = QModelIndex(); + } + if ( d->categoryDrawer && d->hoveredBlock->height != -1 ) + { + const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); + const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); + d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + viewport()->update ( option.rect ); + } +} + +void KCategorizedView::startDrag ( Qt::DropActions supportedActions ) +{ + QListView::startDrag ( supportedActions ); +} + +void KCategorizedView::dragMoveEvent ( QDragMoveEvent *event ) +{ + QListView::dragMoveEvent ( event ); + d->hoveredIndex = indexAt ( event->pos() ); +} + +void KCategorizedView::dragEnterEvent ( QDragEnterEvent *event ) +{ + QListView::dragEnterEvent ( event ); +} + +void KCategorizedView::dragLeaveEvent ( QDragLeaveEvent *event ) +{ + QListView::dragLeaveEvent ( event ); +} + +void KCategorizedView::dropEvent ( QDropEvent *event ) +{ + QListView::dropEvent ( event ); +} + +//TODO: improve se we take into account collapsed blocks +//TODO: take into account when there is no grid and no uniformItemSizes +QModelIndex KCategorizedView::moveCursor ( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ) +{ + if ( !d->isCategorized() ) + { + return QListView::moveCursor ( cursorAction, modifiers ); + } + + const QModelIndex current = currentIndex(); + const QRect currentRect = visualRect ( current ); + if ( !current.isValid() ) + { + const int rowCount = d->proxyModel->rowCount ( rootIndex() ); + if ( !rowCount ) + { + return QModelIndex(); + } + return d->proxyModel->index ( 0, modelColumn(), rootIndex() ); + } + + switch ( cursorAction ) + { + case MoveLeft: + { + if ( !current.row() ) + { + return QModelIndex(); + } + const QModelIndex previous = d->proxyModel->index ( current.row() - 1, modelColumn(), rootIndex() ); + const QRect previousRect = visualRect ( previous ); + if ( previousRect.top() == currentRect.top() ) + { + return previous; + } + + return QModelIndex(); + } + case MoveRight: + { + if ( current.row() == d->proxyModel->rowCount() - 1 ) + { + return QModelIndex(); + } + const QModelIndex next = d->proxyModel->index ( current.row() + 1, modelColumn(), rootIndex() ); + const QRect nextRect = visualRect ( next ); + if ( nextRect.top() == currentRect.top() ) + { + return next; + } + + return QModelIndex(); + } + case MoveDown: + { + if ( d->hasGrid() || uniformItemSizes() || uniformItemWidths() ) + { + const QModelIndex current = currentIndex(); + const QSize itemSize = d->hasGrid() ? gridSize() + : sizeHintForIndex ( current ); + const Private::Block &block = d->blocks[d->categoryForIndex ( current )]; + //HACK: Why is the -2 needed? + const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 1 ); + const bool canMove = current.row() + maxItemsPerRow < block.firstIndex.row() + + block.items.count(); + + if ( canMove ) + { + return d->proxyModel->index ( current.row() + maxItemsPerRow, modelColumn(), rootIndex() ); + } + + const int currentRelativePos = ( current.row() - block.firstIndex.row() ) % maxItemsPerRow; + const QModelIndex nextIndex = d->proxyModel->index ( block.firstIndex.row() + block.items.count(), modelColumn(), rootIndex() ); + + if ( !nextIndex.isValid() ) + { + return QModelIndex(); + } + + const Private::Block &nextBlock = d->blocks[d->categoryForIndex ( nextIndex )]; + + if ( nextBlock.items.count() <= currentRelativePos ) + { + return QModelIndex(); + } + + if ( currentRelativePos < ( block.items.count() % maxItemsPerRow ) ) + { + return d->proxyModel->index ( nextBlock.firstIndex.row() + currentRelativePos, modelColumn(), rootIndex() ); + } + + return QModelIndex(); + } + } + case MoveUp: + { + if ( d->hasGrid() || uniformItemSizes() || uniformItemWidths() ) + { + const QModelIndex current = currentIndex(); + const QSize itemSize = d->hasGrid() ? gridSize() + : sizeHintForIndex ( current ); + const Private::Block &block = d->blocks[d->categoryForIndex ( current )]; + //HACK: Why is the -2 needed? + const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 1 ); + const bool canMove = current.row() - maxItemsPerRow >= block.firstIndex.row(); + + if ( canMove ) + { + return d->proxyModel->index ( current.row() - maxItemsPerRow, modelColumn(), rootIndex() ); + } + + const int currentRelativePos = ( current.row() - block.firstIndex.row() ) % maxItemsPerRow; + const QModelIndex prevIndex = d->proxyModel->index ( block.firstIndex.row() - 1, modelColumn(), rootIndex() ); + + if ( !prevIndex.isValid() ) + { + return QModelIndex(); + } + + const Private::Block &prevBlock = d->blocks[d->categoryForIndex ( prevIndex )]; + + if ( prevBlock.items.count() <= currentRelativePos ) + { + return QModelIndex(); + } + + const int remainder = prevBlock.items.count() % maxItemsPerRow; + if ( currentRelativePos < remainder ) + { + return d->proxyModel->index ( prevBlock.firstIndex.row() + prevBlock.items.count() - remainder + currentRelativePos, modelColumn(), rootIndex() ); + } + + return QModelIndex(); + } + } + default: + break; + } + + return QModelIndex(); +} + +void KCategorizedView::rowsAboutToBeRemoved ( const QModelIndex &parent, + int start, + int end ) +{ + if ( !d->isCategorized() ) + { + QListView::rowsAboutToBeRemoved ( parent, start, end ); + return; + } + + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + + if ( end - start + 1 == d->proxyModel->rowCount() ) + { + d->blocks.clear(); + QListView::rowsAboutToBeRemoved ( parent, start, end ); + return; + } + + // Removal feels a bit more complicated than insertion. Basically we can consider there are + // 3 different cases when going to remove items. (*) represents an item, Items between ([) and + // (]) are the ones which are marked for removal. + // + // - 1st case: + // ... * * * * * * [ * * * ... + // + // The items marked for removal are the last part of this category. No need to mark any item + // of this category as in quarantine, because no special offset will be pushed to items at + // the right because of any changes (since the removed items are those on the right most part + // of the category). + // + // - 2nd case: + // ... * * * * * * ] * * * ... + // + // The items marked for removal are the first part of this category. We have to mark as in + // quarantine all items in this category. Absolutely all. All items will have to be moved to + // the left (or moving up, because rows got a different offset). + // + // - 3rd case: + // ... * * [ * * * * ] * * ... + // + // The items marked for removal are in between of this category. We have to mark as in + // quarantine only those items that are at the right of the end of the removal interval, + // (starting on "]"). + // + // It hasn't been explicitly said, but when we remove, we have to mark all blocks that are + // located under the top most affected category as in quarantine (the block itself, as a whole), + // because such a change can force it to have a different offset (note that items themselves + // contain relative positions to the block, so marking the block as in quarantine is enough). + // + // Also note that removal implicitly means that we have to update correctly firstIndex of each + // block, and in general keep updated the internal information of elements. + + QStringList listOfCategoriesMarkedForRemoval; + + QString lastCategory; + int alreadyRemoved = 0; + for ( int i = start; i <= end; ++i ) + { + const QModelIndex index = d->proxyModel->index ( i, modelColumn(), parent ); + + Q_ASSERT ( index.isValid() ); + + const QString category = d->categoryForIndex ( index ); + + if ( lastCategory != category ) + { + lastCategory = category; + alreadyRemoved = 0; + } + + Private::Block &block = d->blocks[category]; + block.items.removeAt ( i - block.firstIndex.row() - alreadyRemoved ); + ++alreadyRemoved; + + if ( !block.items.count() ) + { + listOfCategoriesMarkedForRemoval << category; + } + + block.height = -1; + + viewport()->update(); + } + + //BEGIN: update the items that are in quarantine in affected categories + { + const QModelIndex lastIndex = d->proxyModel->index ( end, modelColumn(), parent ); + const QString category = d->categoryForIndex ( lastIndex ); + Private::Block &block = d->blocks[category]; + if ( block.items.count() && start <= block.firstIndex.row() && end >= block.firstIndex.row() ) + { + block.firstIndex = d->proxyModel->index ( end + 1, modelColumn(), parent ); + } + block.quarantineStart = block.firstIndex; + } + //END: update the items that are in quarantine in affected categories + + Q_FOREACH ( const QString &category, listOfCategoriesMarkedForRemoval ) + { + d->blocks.remove ( category ); + } + + //BEGIN: mark as in quarantine those categories that are under the affected ones + { + //BEGIN: order for marking as alternate those blocks that are alternate + QList<Private::Block> blockList = d->blocks.values(); + qSort ( blockList.begin(), blockList.end(), Private::Block::lessThan ); + QList<int> firstIndexesRows; + foreach ( const Private::Block &block, blockList ) + { + firstIndexesRows << block.firstIndex.row(); + } + //END: order for marking as alternate those blocks that are alternate + for ( QHash<QString, Private::Block>::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it ) + { + Private::Block &block = *it; + if ( block.firstIndex.row() > start ) + { + block.outOfQuarantine = false; + block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; + } + else if ( block.firstIndex.row() == start ) + { + block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; + } + } + } + //END: mark as in quarantine those categories that are under the affected ones + + QListView::rowsAboutToBeRemoved ( parent, start, end ); +} + +void KCategorizedView::updateGeometries() +{ + const int oldVerticalOffset = verticalOffset(); + const Qt::ScrollBarPolicy verticalP = verticalScrollBarPolicy(), horizontalP = horizontalScrollBarPolicy(); + + //BEGIN bugs 213068, 287847 ------------------------------------------------------------ + /* + * QListView::updateGeometries() has it's own opinion on whether the scrollbars should be visible (valid range) or not + * and triggers a (sometimes additionally timered) resize through ::layoutChildren() + * http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/itemviews/qlistview.cpp#line1499 + * (the comment above the main block isn't all accurate, layoutChldren is called regardless of the policy) + * + * As a result QListView and KCategorizedView occasionally started a race on the scrollbar visibility, effectively blocking the UI + * So we prevent QListView from having an own opinion on the scrollbar visibility by + * fixing it before calling the baseclass QListView::updateGeometries() + * + * Since the implicit show/hide by the followin range setting will cause further resizes if the policy is Qt::ScrollBarAsNeeded + * we keep it static until we're done, then restore the original value and ultimately change the scrollbar visibility ourself. + */ + if ( d->isCategorized() ) // important! - otherwise we'd pollute the setting if the view is initially not categorized + { + setVerticalScrollBarPolicy ( ( verticalP == Qt::ScrollBarAlwaysOn || verticalScrollBar()->isVisibleTo ( this ) ) ? + Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff ); + setHorizontalScrollBarPolicy ( ( horizontalP == Qt::ScrollBarAlwaysOn || horizontalScrollBar()->isVisibleTo ( this ) ) ? + Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff ); + } + //END bugs 213068, 287847 -------------------------------------------------------------- + + QListView::updateGeometries(); + + if ( !d->isCategorized() ) + { + return; + } + + const int rowCount = d->proxyModel->rowCount(); + if ( !rowCount ) + { + verticalScrollBar()->setRange ( 0, 0 ); + // unconditional, see function end todo + horizontalScrollBar()->setRange ( 0, 0 ); + return; + } + + const QModelIndex lastIndex = d->proxyModel->index ( rowCount - 1, modelColumn(), rootIndex() ); + Q_ASSERT ( lastIndex.isValid() ); + QRect lastItemRect = visualRect ( lastIndex ); + + if ( d->hasGrid() ) + { + lastItemRect.setSize ( lastItemRect.size().expandedTo ( gridSize() ) ); + } + else + { + if ( uniformItemSizes() ) + { + QSize itemSize = sizeHintForIndex ( lastIndex ); + itemSize.setHeight ( itemSize.height() + spacing() ); + lastItemRect.setSize ( itemSize ); + } + else + { + QSize itemSize = sizeHintForIndex ( lastIndex ); + const QString category = d->categoryForIndex ( lastIndex ); + itemSize.setHeight ( d->highestElementInLastRow ( d->blocks[category] ) + spacing() ); + lastItemRect.setSize ( itemSize ); + } + } + + const int bottomRange = lastItemRect.bottomRight().y() + verticalOffset() - viewport()->height(); + + if ( verticalScrollMode() == ScrollPerItem ) + { + verticalScrollBar()->setSingleStep ( lastItemRect.height() ); + const int rowsPerPage = qMax ( viewport()->height() / lastItemRect.height(), 1 ); + verticalScrollBar()->setPageStep ( rowsPerPage * lastItemRect.height() ); + } + + verticalScrollBar()->setRange ( 0, bottomRange ); + verticalScrollBar()->setValue ( oldVerticalOffset ); + + //TODO: also consider working with the horizontal scroll bar. since at this level I am not still + // supporting "top to bottom" flow, there is no real problem. If I support that someday + // (think how to draw categories), we would have to take care of the horizontal scroll bar too. + // In theory, as KCategorizedView has been designed, there is no need of horizontal scroll bar. + horizontalScrollBar()->setRange ( 0, 0 ); + + //BEGIN bugs 213068, 287847 ------------------------------------------------------------ + // restoring values from above ... + setVerticalScrollBarPolicy ( verticalP ); + setHorizontalScrollBarPolicy ( horizontalP ); + // ... and correct the visibility + bool validRange = verticalScrollBar()->maximum() != verticalScrollBar()->minimum(); + if ( verticalP == Qt::ScrollBarAsNeeded && ( verticalScrollBar()->isVisibleTo ( this ) != validRange ) ) + verticalScrollBar()->setVisible ( validRange ); + validRange = horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum(); + if ( horizontalP == Qt::ScrollBarAsNeeded && ( horizontalScrollBar()->isVisibleTo ( this ) != validRange ) ) + horizontalScrollBar()->setVisible ( validRange ); + //END bugs 213068, 287847 -------------------------------------------------------------- +} + +void KCategorizedView::currentChanged ( const QModelIndex ¤t, + const QModelIndex &previous ) +{ + QListView::currentChanged ( current, previous ); +} + +void KCategorizedView::dataChanged ( const QModelIndex &topLeft, + const QModelIndex &bottomRight ) +{ + QListView::dataChanged ( topLeft, bottomRight ); + if ( !d->isCategorized() ) + { + return; + } + + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + + //BEGIN: since the model changed data, we need to reconsider item sizes + int i = topLeft.row(); + int indexToCheck = i; + QModelIndex categoryIndex; + QString category; + Private::Block *block; + while ( i <= bottomRight.row() ) + { + const QModelIndex currIndex = d->proxyModel->index ( i, modelColumn(), rootIndex() ); + if ( i == indexToCheck ) + { + categoryIndex = d->proxyModel->index ( i, d->proxyModel->sortColumn(), rootIndex() ); + category = categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); + block = &d->blocks[category]; + block->quarantineStart = currIndex; + indexToCheck = block->firstIndex.row() + block->items.count(); + } + visualRect ( currIndex ); + ++i; + } + //END: since the model changed data, we need to reconsider item sizes +} + +void KCategorizedView::rowsInserted ( const QModelIndex &parent, + int start, + int end ) +{ + QListView::rowsInserted ( parent, start, end ); + if ( !d->isCategorized() ) + { + return; + } + + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + d->rowsInserted ( parent, start, end ); +} + +void KCategorizedView::slotLayoutChanged() +{ + if ( !d->isCategorized() ) + { + return; + } + + d->blocks.clear(); + *d->hoveredBlock = Private::Block(); + d->hoveredCategory = QString(); + if ( d->proxyModel->rowCount() ) + { + d->rowsInserted ( rootIndex(), 0, d->proxyModel->rowCount() - 1 ); + } +} +//END: Public part + +void KCategorizedView::slotCollapseOrExpandClicked ( QModelIndex idx ) +{ + d->_k_slotCollapseOrExpandClicked ( idx ); +} + + +#include "categorizedview.moc" diff --git a/depends/groupview/src/categorizedview_p.h b/depends/groupview/src/categorizedview_p.h new file mode 100644 index 00000000..13809312 --- /dev/null +++ b/depends/groupview/src/categorizedview_p.h @@ -0,0 +1,159 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCATEGORIZEDVIEW_P_H +#define KCATEGORIZEDVIEW_P_H + +class KCategorizedSortFilterProxyModel; +class KCategoryDrawer; +class KCategoryDrawerV2; +class KCategoryDrawerV3; + +/** + * @internal + */ +class KCategorizedView::Private +{ +public: + struct Block; + struct Item; + + Private(KCategorizedView *q); + ~Private(); + + /** + * @return whether this view has all required elements to be categorized. + */ + bool isCategorized() const; + + /** + * @return the block rect for the representative @p representative. + */ + QStyleOptionViewItemV4 blockRect(const QModelIndex &representative); + + /** + * Returns the first and last element that intersects with rect. + * + * @note see that here we cannot take out items between first and last (as we could + * do with the rubberband). + * + * Complexity: O(log(n)) where n is model()->rowCount(). + */ + QPair<QModelIndex, QModelIndex> intersectingIndexesWithRect(const QRect &rect) const; + + /** + * Returns the position of the block of @p category. + * + * Complexity: O(n) where n is the number of different categories when the block has been + * marked as in quarantine. O(1) the rest of the times (the vast majority). + */ + QPoint blockPosition(const QString &category); + + /** + * Returns the height of the block determined by @p category. + */ + int blockHeight(const QString &category); + + /** + * Returns the actual viewport width. + */ + int viewportWidth() const; + + /** + * Marks all elements as in quarantine. + * + * Complexity: O(n) where n is model()->rowCount(). + * + * @warning this is an expensive operation + */ + void regenerateAllElements(); + + /** + * Update internal information, and keep sync with the real information that the model contains. + */ + void rowsInserted(const QModelIndex &parent, int start, int end); + + /** + * Returns @p rect in viewport terms, taking in count horizontal and vertical offsets. + */ + QRect mapToViewport(const QRect &rect) const; + + /** + * Returns @p rect in absolute terms, converted from viewport position. + */ + QRect mapFromViewport(const QRect &rect) const; + + /** + * Returns the height of the highest element in last row. This is only applicable if there is + * no grid set and uniformItemSizes is false. + * + * @param block in which block are we searching. Necessary to stop the search if we hit the + * first item in this block. + */ + int highestElementInLastRow(const Block &block) const; + + /** + * Returns whether the view has a valid grid size. + */ + bool hasGrid() const; + + /** + * Returns the category for the given index. + */ + QString categoryForIndex(const QModelIndex &index) const; + + /** + * Updates the visual rect for item when flow is LeftToRight. + */ + void leftToRightVisualRect(const QModelIndex &index, Item &item, + const Block &block, const QPoint &blockPos) const; + + /** + * Updates the visual rect for item when flow is TopToBottom. + * @note we only support viewMode == ListMode in this case. + */ + void topToBottomVisualRect(const QModelIndex &index, Item &item, + const Block &block, const QPoint &blockPos) const; + + /** + * Called when expand or collapse has been clicked on the category drawer. + */ + void _k_slotCollapseOrExpandClicked(QModelIndex); + + KCategorizedView *q; + KCategorizedSortFilterProxyModel *proxyModel; + KCategoryDrawer *categoryDrawer; + int categorySpacing; + bool alternatingBlockColors; + bool collapsibleBlocks; + bool constantItemWidth; + + Block *hoveredBlock; + QString hoveredCategory; + QModelIndex hoveredIndex; + + QPoint pressedPosition; + QRect rubberBandRect; + + QHash<QString, Block> blocks; +}; + +#endif // KCATEGORIZEDVIEW_P_H + diff --git a/depends/groupview/src/categorydrawer.cpp b/depends/groupview/src/categorydrawer.cpp new file mode 100644 index 00000000..04903206 --- /dev/null +++ b/depends/groupview/src/categorydrawer.cpp @@ -0,0 +1,231 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "categorydrawer.h" + +#include <QPainter> +#include <QStyleOption> +#include <QApplication> + +#include <categorizedview.h> +#include <categorizedsortfilterproxymodel.h> + +#define HORIZONTAL_HINT 3 + +class KCategoryDrawer::Private +{ +public: + Private(KCategorizedView *view) + : view(view) + , leftMargin(0) + , rightMargin(0) + { + } + + ~Private() + { + } + int leftMargin; + int rightMargin; + KCategorizedView *view; +}; + +KCategoryDrawer::KCategoryDrawer(KCategorizedView *view) + : QObject(view) + , d(new Private(view)) +{ + setLeftMargin(2); + setRightMargin(2); +} + +KCategoryDrawer::~KCategoryDrawer() +{ + delete d; +} + + +void KCategoryDrawer::drawCategory(const QModelIndex &index, + int /*sortRole*/, + const QStyleOption &option, + QPainter *painter) const +{ + painter->setRenderHint(QPainter::Antialiasing); + + const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); + const QRect optRect = option.rect; + QFont font(QApplication::font()); + font.setBold(true); + const QFontMetrics fontMetrics = QFontMetrics(font); + + QColor outlineColor = option.palette.text().color(); + outlineColor.setAlphaF(0.35); + + //BEGIN: top left corner + { + painter->save(); + painter->setPen(outlineColor); + const QPointF topLeft(optRect.topLeft()); + QRectF arc(topLeft, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 1440, 1440); + painter->restore(); + } + //END: top left corner + + //BEGIN: left vertical line + { + QPoint start(optRect.topLeft()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topLeft()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: left vertical line + + //BEGIN: horizontal line + { + QPoint start(optRect.topLeft()); + start.rx() += 3; + QPoint horizontalGradTop(optRect.topLeft()); + horizontalGradTop.rx() += optRect.width() - 6; + painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); + } + //END: horizontal line + + //BEGIN: top right corner + { + painter->save(); + painter->setPen(outlineColor); + QPointF topRight(optRect.topRight()); + topRight.rx() -= 4; + QRectF arc(topRight, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 0, 1440); + painter->restore(); + } + //END: top right corner + + //BEGIN: right vertical line + { + QPoint start(optRect.topRight()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topRight()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: right vertical line + + //BEGIN: text + { + QRect textRect(option.rect); + textRect.setTop(textRect.top() + 7); + textRect.setLeft(textRect.left() + 7); + textRect.setHeight(fontMetrics.height()); + textRect.setRight(textRect.right() - 7); + + painter->save(); + painter->setFont(font); + QColor penColor(option.palette.text().color()); + penColor.setAlphaF(0.6); + painter->setPen(penColor); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category); + painter->restore(); + } + //END: text +} + +int KCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const +{ + Q_UNUSED(index); + Q_UNUSED(option) + + QFont font(QApplication::font()); + font.setBold(true); + QFontMetrics fontMetrics(font); + + const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ + + 11 /* top and bottom separation */; + return height; +} + +int KCategoryDrawer::leftMargin() const +{ + return d->leftMargin; +} + +void KCategoryDrawer::setLeftMargin(int leftMargin) +{ + d->leftMargin = leftMargin; +} + +int KCategoryDrawer::rightMargin() const +{ + return d->rightMargin; +} + +void KCategoryDrawer::setRightMargin(int rightMargin) +{ + d->rightMargin = rightMargin; +} + +KCategoryDrawer &KCategoryDrawer::operator=(const KCategoryDrawer &cd) +{ + d->leftMargin = cd.d->leftMargin; + d->rightMargin = cd.d->rightMargin; + d->view = cd.d->view; + return *this; +} + +KCategorizedView *KCategoryDrawer::view() const +{ + return d->view; +} + +void KCategoryDrawer::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event) +{ + event->ignore(); +} + +void KCategoryDrawer::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event) +{ + event->ignore(); +} + +void KCategoryDrawer::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event) +{ + event->ignore(); +} + +void KCategoryDrawer::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event) +{ + event->ignore(); +} + +void KCategoryDrawer::mouseLeft(const QModelIndex&, const QRect&) +{ +} + +#include "categorydrawer.moc" diff --git a/depends/launcher/CMakeLists.txt b/depends/launcher/CMakeLists.txt new file mode 100644 index 00000000..e5402ce7 --- /dev/null +++ b/depends/launcher/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8.6) +project(launcher Java) +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}") +find_package(Java 1.6 REQUIRED COMPONENTS Development) + + +include(UseJava) +set(CMAKE_JAVA_JAR_ENTRY_POINT MultiMCLauncher) +set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_TARGET_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/../../resources") + +set(SRC + MultiMCLauncher.java + org/simplericity/macify/eawt/Application.java + org/simplericity/macify/eawt/ApplicationAdapter.java + org/simplericity/macify/eawt/ApplicationEvent.java + org/simplericity/macify/eawt/ApplicationListener.java + org/simplericity/macify/eawt/DefaultApplication.java + net/minecraft/Launcher.java + MCFrame.java +) + +add_jar(MultiMCLauncher ${SRC})
\ No newline at end of file diff --git a/depends/launcher/MCFrame.java b/depends/launcher/MCFrame.java new file mode 100644 index 00000000..d6ebb240 --- /dev/null +++ b/depends/launcher/MCFrame.java @@ -0,0 +1,123 @@ +// +// Copyright 2012 MultiMC Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import net.minecraft.Launcher; +import java.applet.Applet; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.net.MalformedURLException; +import java.net.URL; +import java.io.IOException; +import java.io.File; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; + +public class MCFrame extends Frame implements WindowListener +{ + private Launcher appletWrap = null; + public MCFrame(String title) + { + super(title); + BufferedImage image = null; + try + { + image = ImageIO.read(new File("icon.png")); + setIconImage(image); + } + catch (IOException e) + { + e.printStackTrace(); + } + this.addWindowListener(this); + } + + public void start(Applet mcApplet, String user, String session, Dimension winSize, boolean maximize) + { + try + { + appletWrap = new Launcher(mcApplet, new URL("http://www.minecraft.net/game")); + } + catch (MalformedURLException ignored){} + + appletWrap.setParameter("username", user); + appletWrap.setParameter("sessionid", session); + appletWrap.setParameter("stand-alone", "true"); // Show the quit button. + mcApplet.setStub(appletWrap); + + this.add(appletWrap); + appletWrap.setPreferredSize(winSize); + this.pack(); + this.setLocationRelativeTo(null); + this.setResizable(true); + if (maximize) + this.setExtendedState(MAXIMIZED_BOTH); + + validate(); + appletWrap.init(); + appletWrap.start(); + setVisible(true); + } + + @Override + public void windowActivated(WindowEvent e) {} + + @Override + public void windowClosed(WindowEvent e) {} + + @Override + public void windowClosing(WindowEvent e) + { + new Thread() + { + public void run() + { + try + { + Thread.sleep(30000L); + } catch (InterruptedException localInterruptedException) + { + localInterruptedException.printStackTrace(); + } + System.out.println("FORCING EXIT!"); + System.exit(0); + } + } + .start(); + + if (appletWrap != null) + { + appletWrap.stop(); + appletWrap.destroy(); + } + // old minecraft versions can hang without this >_< + System.exit(0); + } + + @Override + public void windowDeactivated(WindowEvent e) {} + + @Override + public void windowDeiconified(WindowEvent e) {} + + @Override + public void windowIconified(WindowEvent e) {} + + @Override + public void windowOpened(WindowEvent e) {} +}
\ No newline at end of file diff --git a/depends/launcher/MultiMCLauncher.java b/depends/launcher/MultiMCLauncher.java new file mode 100644 index 00000000..09a019ce --- /dev/null +++ b/depends/launcher/MultiMCLauncher.java @@ -0,0 +1,331 @@ +// +// Copyright 2012 MultiMC Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import java.applet.Applet; +import java.awt.Dimension; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import org.simplericity.macify.eawt.Application; +import org.simplericity.macify.eawt.DefaultApplication; + +public class MultiMCLauncher +{ + /** + * @param args + * The arguments you want to launch Minecraft with. New path, + * Username, Session ID. + */ + public static void main(String[] args) + { + if (args.length < 3) + { + System.out.println("Not enough arguments."); + System.exit(-1); + } + + // Set the OSX application icon first, if we are on OSX. + Application application = new DefaultApplication(); + if(application.isMac()) + { + try + { + BufferedImage image = ImageIO.read(new File("icon.png")); + application.setApplicationIconImage(image); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + String userName = args[0]; + String sessionId = args[1]; + String windowtitle = args[2]; + String windowParams = args[3]; + String lwjgl = args[4]; + String cwd = System.getProperty("user.dir"); + + Dimension winSize = new Dimension(854, 480); + boolean maximize = false; + boolean compatMode = false; + + + String[] dimStrings = windowParams.split("x"); + + if (windowParams.equalsIgnoreCase("compatmode")) + { + compatMode = true; + } + else if (windowParams.equalsIgnoreCase("max")) + { + maximize = true; + } + else if (dimStrings.length == 2) + { + try + { + winSize = new Dimension(Integer.parseInt(dimStrings[0]), + Integer.parseInt(dimStrings[1])); + } + catch (NumberFormatException e) + { + System.out.println("Invalid Window size argument, " + + "using default."); + } + } + else + { + System.out.println("Invalid Window size argument, " + + "using default."); + } + + try + { + File binDir = new File(cwd, "bin"); + File lwjglDir; + if(lwjgl.equalsIgnoreCase("Mojang")) + lwjglDir = binDir; + else + lwjglDir = new File(lwjgl); + + System.out.println("Loading jars..."); + String[] lwjglJars = new String[] { + "lwjgl.jar", "lwjgl_util.jar", "jinput.jar" + }; + + URL[] urls = new URL[4]; + try + { + File f = new File(binDir, "minecraft.jar"); + urls[0] = f.toURI().toURL(); + System.out.println("Loading URL: " + urls[0].toString()); + + for (int i = 1; i < urls.length; i++) + { + File jar = new File(lwjglDir, lwjglJars[i-1]); + urls[i] = jar.toURI().toURL(); + System.out.println("Loading URL: " + urls[i].toString()); + } + } + catch (MalformedURLException e) + { + System.err.println("MalformedURLException, " + e.toString()); + System.exit(5); + } + + System.out.println("Loading natives..."); + String nativesDir = new File(lwjglDir, "natives").toString(); + + System.setProperty("org.lwjgl.librarypath", nativesDir); + System.setProperty("net.java.games.input.librarypath", nativesDir); + + URLClassLoader cl = + new URLClassLoader(urls, MultiMCLauncher.class.getClassLoader()); + + // Get the Minecraft Class. + Class<?> mc = null; + try + { + mc = cl.loadClass("net.minecraft.client.Minecraft"); + + Field f = getMCPathField(mc); + + if (f == null) + { + System.err.println("Could not find Minecraft path field. Launch failed."); + System.exit(-1); + } + + f.setAccessible(true); + f.set(null, new File(cwd)); + // And set it. + System.out.println("Fixed Minecraft Path: Field was " + f.toString()); + } + catch (ClassNotFoundException e) + { + System.err.println("Can't find main class. Searching..."); + + // Look for any class that looks like the main class. + File mcJar = new File(new File(cwd, "bin"), "minecraft.jar"); + ZipFile zip = null; + try + { + zip = new ZipFile(mcJar); + } catch (ZipException e1) + { + e1.printStackTrace(); + System.err.println("Search failed."); + System.exit(-1); + } catch (IOException e1) + { + e1.printStackTrace(); + System.err.println("Search failed."); + System.exit(-1); + } + + Enumeration<? extends ZipEntry> entries = zip.entries(); + ArrayList<String> classes = new ArrayList<String>(); + + while (entries.hasMoreElements()) + { + ZipEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) + { + String entryName = entry.getName().substring(0, entry.getName().lastIndexOf('.')); + entryName = entryName.replace('/', '.'); + System.out.println("Found class: " + entryName); + classes.add(entryName); + } + } + + for (String clsName : classes) + { + try + { + Class<?> cls = cl.loadClass(clsName); + if (!Runnable.class.isAssignableFrom(cls)) + { + continue; + } + else + { + System.out.println("Found class implementing runnable: " + + cls.getName()); + } + + if (getMCPathField(cls) == null) + { + continue; + } + else + { + System.out.println("Found class implementing runnable " + + "with mcpath field: " + cls.getName()); + } + + mc = cls; + break; + } + catch (ClassNotFoundException e1) + { + // Ignore + continue; + } + } + + if (mc == null) + { + System.err.println("Failed to find Minecraft main class."); + System.exit(-1); + } + else + { + System.out.println("Found main class: " + mc.getName()); + } + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + String[] mcArgs = new String[2]; + mcArgs[0] = userName; + mcArgs[1] = sessionId; + + if (compatMode) + { + System.out.println("Launching in compatibility mode..."); + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } + else + { + System.out.println("Launching with applet wrapper..."); + try + { + Class<?> MCAppletClass = cl.loadClass( + "net.minecraft.client.MinecraftApplet"); + Applet mcappl = (Applet) MCAppletClass.newInstance(); + MCFrame mcWindow = new MCFrame(windowtitle); + mcWindow.start(mcappl, userName, sessionId, winSize, maximize); + } catch (InstantiationException e) + { + System.out.println("Applet wrapper failed! Falling back " + + "to compatibility mode."); + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } + } + } catch (ClassNotFoundException e) + { + e.printStackTrace(); + System.exit(1); + } catch (IllegalArgumentException e) + { + e.printStackTrace(); + System.exit(2); + } catch (IllegalAccessException e) + { + e.printStackTrace(); + System.exit(2); + } catch (InvocationTargetException e) + { + e.printStackTrace(); + System.exit(3); + } catch (NoSuchMethodException e) + { + e.printStackTrace(); + System.exit(3); + } catch (SecurityException e) + { + e.printStackTrace(); + System.exit(4); + } + } + + public static Field getMCPathField(Class<?> mc) + { + Field[] fields = mc.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) + { + Field f = fields[i]; + if (f.getType() != File.class) + { + // Has to be File + continue; + } + if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC)) + { + // And Private Static. + continue; + } + return f; + } + return null; + } +} diff --git a/depends/launcher/UseJava.cmake b/depends/launcher/UseJava.cmake new file mode 100644 index 00000000..1a5ef107 --- /dev/null +++ b/depends/launcher/UseJava.cmake @@ -0,0 +1,881 @@ +# - Use Module for Java +# This file provides functions for Java. It is assumed that FindJava.cmake +# has already been loaded. See FindJava.cmake for information on how to +# load Java into your CMake project. +# +# add_jar(TARGET_NAME SRC1 SRC2 .. SRCN RCS1 RCS2 .. RCSN) +# +# This command creates a <TARGET_NAME>.jar. It compiles the given source +# files (SRC) and adds the given resource files (RCS) to the jar file. +# If only resource files are given then just a jar file is created. +# +# Additional instructions: +# To add compile flags to the target you can set these flags with +# the following variable: +# +# set(CMAKE_JAVA_COMPILE_FLAGS -nowarn) +# +# To add a path or a jar file to the class path you can do this +# with the CMAKE_JAVA_INCLUDE_PATH variable. +# +# set(CMAKE_JAVA_INCLUDE_PATH /usr/share/java/shibboleet.jar) +# +# To use a different output name for the target you can set it with: +# +# set(CMAKE_JAVA_TARGET_OUTPUT_NAME shibboleet.jar) +# add_jar(foobar foobar.java) +# +# To use a different output directory than CMAKE_CURRENT_BINARY_DIR +# you can set it with: +# +# set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/bin) +# +# To define an entry point in your jar you can set it with: +# +# set(CMAKE_JAVA_JAR_ENTRY_POINT com/examples/MyProject/Main) +# +# To add a VERSION to the target output name you can set it using +# CMAKE_JAVA_TARGET_VERSION. This will create a jar file with the name +# shibboleet-1.0.0.jar and will create a symlink shibboleet.jar +# pointing to the jar with the version information. +# +# set(CMAKE_JAVA_TARGET_VERSION 1.2.0) +# add_jar(shibboleet shibbotleet.java) +# +# If the target is a JNI library, utilize the following commands to +# create a JNI symbolic link: +# +# set(CMAKE_JNI_TARGET TRUE) +# set(CMAKE_JAVA_TARGET_VERSION 1.2.0) +# add_jar(shibboleet shibbotleet.java) +# install_jar(shibboleet ${LIB_INSTALL_DIR}/shibboleet) +# install_jni_symlink(shibboleet ${JAVA_LIB_INSTALL_DIR}) +# +# If a single target needs to produce more than one jar from its +# java source code, to prevent the accumulation of duplicate class +# files in subsequent jars, set/reset CMAKE_JAR_CLASSES_PREFIX prior +# to calling the add_jar() function: +# +# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/foo) +# add_jar(foo foo.java) +# +# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/bar) +# add_jar(bar bar.java) +# +# Target Properties: +# The add_jar() functions sets some target properties. You can get these +# properties with the +# get_property(TARGET <target_name> PROPERTY <propery_name>) +# command. +# +# INSTALL_FILES The files which should be installed. This is used by +# install_jar(). +# JNI_SYMLINK The JNI symlink which should be installed. +# This is used by install_jni_symlink(). +# JAR_FILE The location of the jar file so that you can include +# it. +# CLASS_DIR The directory where the class files can be found. For +# example to use them with javah. +# +# find_jar(<VAR> +# name | NAMES name1 [name2 ...] +# [PATHS path1 [path2 ... ENV var]] +# [VERSIONS version1 [version2]] +# [DOC "cache documentation string"] +# ) +# +# This command is used to find a full path to the named jar. A cache +# entry named by <VAR> is created to stor the result of this command. If +# the full path to a jar is found the result is stored in the variable +# and the search will not repeated unless the variable is cleared. If +# nothing is found, the result will be <VAR>-NOTFOUND, and the search +# will be attempted again next time find_jar is invoked with the same +# variable. +# The name of the full path to a file that is searched for is specified +# by the names listed after NAMES argument. Additional search locations +# can be specified after the PATHS argument. If you require special a +# version of a jar file you can specify it with the VERSIONS argument. +# The argument after DOC will be used for the documentation string in +# the cache. +# +# install_jar(TARGET_NAME DESTINATION) +# +# This command installs the TARGET_NAME files to the given DESTINATION. +# It should be called in the same scope as add_jar() or it will fail. +# +# install_jni_symlink(TARGET_NAME DESTINATION) +# +# This command installs the TARGET_NAME JNI symlinks to the given +# DESTINATION. It should be called in the same scope as add_jar() +# or it will fail. +# +# create_javadoc(<VAR> +# PACKAGES pkg1 [pkg2 ...] +# [SOURCEPATH <sourcepath>] +# [CLASSPATH <classpath>] +# [INSTALLPATH <install path>] +# [DOCTITLE "the documentation title"] +# [WINDOWTITLE "the title of the document"] +# [AUTHOR TRUE|FALSE] +# [USE TRUE|FALSE] +# [VERSION TRUE|FALSE] +# ) +# +# Create java documentation based on files or packages. For more +# details please read the javadoc manpage. +# +# There are two main signatures for create_javadoc. The first +# signature works with package names on a path with source files: +# +# Example: +# create_javadoc(my_example_doc +# PACKAGES com.exmaple.foo com.example.bar +# SOURCEPATH "${CMAKE_CURRENT_SOURCE_DIR}" +# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} +# WINDOWTITLE "My example" +# DOCTITLE "<h1>My example</h1>" +# AUTHOR TRUE +# USE TRUE +# VERSION TRUE +# ) +# +# The second signature for create_javadoc works on a given list of +# files. +# +# create_javadoc(<VAR> +# FILES file1 [file2 ...] +# [CLASSPATH <classpath>] +# [INSTALLPATH <install path>] +# [DOCTITLE "the documentation title"] +# [WINDOWTITLE "the title of the document"] +# [AUTHOR TRUE|FALSE] +# [USE TRUE|FALSE] +# [VERSION TRUE|FALSE] +# ) +# +# Example: +# create_javadoc(my_example_doc +# FILES ${example_SRCS} +# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} +# WINDOWTITLE "My example" +# DOCTITLE "<h1>My example</h1>" +# AUTHOR TRUE +# USE TRUE +# VERSION TRUE +# ) +# +# Both signatures share most of the options. These options are the +# same as what you can find in the javadoc manpage. Please look at +# the manpage for CLASSPATH, DOCTITLE, WINDOWTITLE, AUTHOR, USE and +# VERSION. +# +# The documentation will be by default installed to +# +# ${CMAKE_INSTALL_PREFIX}/share/javadoc/<VAR> +# +# if you don't set the INSTALLPATH. +# + +#============================================================================= +# Copyright 2010-2011 Andreas schneider <asn@redhat.com> +# Copyright 2010 Ben Boeckel <ben.boeckel@kitware.com> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +function (__java_copy_file src dest comment) + add_custom_command( + OUTPUT ${dest} + COMMAND cmake -E copy_if_different + ARGS ${src} + ${dest} + DEPENDS ${src} + COMMENT ${comment}) +endfunction (__java_copy_file src dest comment) + +# define helper scripts +set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake) +set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake) + +function(add_jar _TARGET_NAME) + set(_JAVA_SOURCE_FILES ${ARGN}) + + if (NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR) + set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif(NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR) + + if (CMAKE_JAVA_JAR_ENTRY_POINT) + set(_ENTRY_POINT_OPTION e) + set(_ENTRY_POINT_VALUE ${CMAKE_JAVA_JAR_ENTRY_POINT}) + endif (CMAKE_JAVA_JAR_ENTRY_POINT) + + if (LIBRARY_OUTPUT_PATH) + set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}) + else (LIBRARY_OUTPUT_PATH) + set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR}) + endif (LIBRARY_OUTPUT_PATH) + + set(CMAKE_JAVA_INCLUDE_PATH + ${CMAKE_JAVA_INCLUDE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_JAVA_OBJECT_OUTPUT_PATH} + ${CMAKE_JAVA_LIBRARY_OUTPUT_PATH} + ) + + if (WIN32 AND NOT CYGWIN AND NOT CMAKE_CROSSCOMPILING) + set(CMAKE_JAVA_INCLUDE_FLAG_SEP ";") + else () + set(CMAKE_JAVA_INCLUDE_FLAG_SEP ":") + endif() + + foreach (JAVA_INCLUDE_DIR ${CMAKE_JAVA_INCLUDE_PATH}) + set(CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_PATH_FINAL}${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}") + endforeach(JAVA_INCLUDE_DIR) + + set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_JAVA_TARGET_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir") + + set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar") + if (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION) + set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar") + set(_JAVA_TARGET_OUTPUT_LINK "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar") + elseif (CMAKE_JAVA_TARGET_VERSION) + set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar") + set(_JAVA_TARGET_OUTPUT_LINK "${_TARGET_NAME}.jar") + elseif (CMAKE_JAVA_TARGET_OUTPUT_NAME) + set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar") + endif (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION) + # reset + set(CMAKE_JAVA_TARGET_OUTPUT_NAME) + + set(_JAVA_CLASS_FILES) + set(_JAVA_COMPILE_FILES) + set(_JAVA_DEPENDS) + set(_JAVA_RESOURCE_FILES) + foreach(_JAVA_SOURCE_FILE ${_JAVA_SOURCE_FILES}) + get_filename_component(_JAVA_EXT ${_JAVA_SOURCE_FILE} EXT) + get_filename_component(_JAVA_FILE ${_JAVA_SOURCE_FILE} NAME_WE) + get_filename_component(_JAVA_PATH ${_JAVA_SOURCE_FILE} PATH) + get_filename_component(_JAVA_FULL ${_JAVA_SOURCE_FILE} ABSOLUTE) + + file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR} ${_JAVA_FULL}) + file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL}) + string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN) + string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN) + if (${_BIN_LEN} LESS ${_SRC_LEN}) + set(_JAVA_REL_PATH ${_JAVA_REL_BINARY_PATH}) + else (${_BIN_LEN} LESS ${_SRC_LEN}) + set(_JAVA_REL_PATH ${_JAVA_REL_SOURCE_PATH}) + endif (${_BIN_LEN} LESS ${_SRC_LEN}) + get_filename_component(_JAVA_REL_PATH ${_JAVA_REL_PATH} PATH) + + if (_JAVA_EXT MATCHES ".java") + list(APPEND _JAVA_COMPILE_FILES ${_JAVA_SOURCE_FILE}) + set(_JAVA_CLASS_FILE "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_REL_PATH}/${_JAVA_FILE}.class") + set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES} ${_JAVA_CLASS_FILE}) + + elseif (_JAVA_EXT MATCHES ".jar" + OR _JAVA_EXT MATCHES ".war" + OR _JAVA_EXT MATCHES ".ear" + OR _JAVA_EXT MATCHES ".sar") + list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_SOURCE_FILE}) + + elseif (_JAVA_EXT STREQUAL "") + list(APPEND CMAKE_JAVA_INCLUDE_PATH ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}} ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}_CLASSPATH}) + list(APPEND _JAVA_DEPENDS ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}}) + + else (_JAVA_EXT MATCHES ".java") + __java_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_JAVA_SOURCE_FILE} + ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_SOURCE_FILE} + "Copying ${_JAVA_SOURCE_FILE} to the build directory") + list(APPEND _JAVA_RESOURCE_FILES ${_JAVA_SOURCE_FILE}) + endif (_JAVA_EXT MATCHES ".java") + endforeach(_JAVA_SOURCE_FILE) + + # create an empty java_class_filelist + if (NOT EXISTS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist) + file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist "") + endif() + + if (_JAVA_COMPILE_FILES) + # Compile the java files and create a list of class files + add_custom_command( + # NOTE: this command generates an artificial dependency file + OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + COMMAND ${Java_JAVAC_EXECUTABLE} + ${CMAKE_JAVA_COMPILE_FLAGS} + -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}" + -d ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + ${_JAVA_COMPILE_FILES} + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + DEPENDS ${_JAVA_COMPILE_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building Java objects for ${_TARGET_NAME}.jar" + ) + add_custom_command( + OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist + COMMAND ${CMAKE_COMMAND} + -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH} + -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}" + -P ${_JAVA_CLASS_FILELIST_SCRIPT} + DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif (_JAVA_COMPILE_FILES) + + # create the jar file + set(_JAVA_JAR_OUTPUT_PATH + ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_NAME}) + if (CMAKE_JNI_TARGET) + add_custom_command( + OUTPUT ${_JAVA_JAR_OUTPUT_PATH} + COMMAND ${Java_JAR_EXECUTABLE} + -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE} + ${_JAVA_RESOURCE_FILES} @java_class_filelist + COMMAND ${CMAKE_COMMAND} + -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR} + -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME} + -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK} + -P ${_JAVA_SYMLINK_SCRIPT} + COMMAND ${CMAKE_COMMAND} + -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR} + -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_JAR_OUTPUT_PATH} + -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK} + -P ${_JAVA_SYMLINK_SCRIPT} + DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist + WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}" + ) + else () + add_custom_command( + OUTPUT ${_JAVA_JAR_OUTPUT_PATH} + COMMAND ${Java_JAR_EXECUTABLE} + -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE} + ${_JAVA_RESOURCE_FILES} @java_class_filelist + COMMAND ${CMAKE_COMMAND} + -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR} + -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME} + -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK} + -P ${_JAVA_SYMLINK_SCRIPT} + WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist + COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}" + ) + endif (CMAKE_JNI_TARGET) + + # Add the target and make sure we have the latest resource files. + add_custom_target(${_TARGET_NAME} ALL DEPENDS ${_JAVA_JAR_OUTPUT_PATH}) + + set_property( + TARGET + ${_TARGET_NAME} + PROPERTY + INSTALL_FILES + ${_JAVA_JAR_OUTPUT_PATH} + ) + + if (_JAVA_TARGET_OUTPUT_LINK) + set_property( + TARGET + ${_TARGET_NAME} + PROPERTY + INSTALL_FILES + ${_JAVA_JAR_OUTPUT_PATH} + ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK} + ) + + if (CMAKE_JNI_TARGET) + set_property( + TARGET + ${_TARGET_NAME} + PROPERTY + JNI_SYMLINK + ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK} + ) + endif (CMAKE_JNI_TARGET) + endif (_JAVA_TARGET_OUTPUT_LINK) + + set_property( + TARGET + ${_TARGET_NAME} + PROPERTY + JAR_FILE + ${_JAVA_JAR_OUTPUT_PATH} + ) + + set_property( + TARGET + ${_TARGET_NAME} + PROPERTY + CLASSDIR + ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + ) + +endfunction(add_jar) + +function(INSTALL_JAR _TARGET_NAME _DESTINATION) + get_property(__FILES + TARGET + ${_TARGET_NAME} + PROPERTY + INSTALL_FILES + ) + + if (__FILES) + install( + FILES + ${__FILES} + DESTINATION + ${_DESTINATION} + ) + else (__FILES) + message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.") + endif (__FILES) +endfunction(INSTALL_JAR _TARGET_NAME _DESTINATION) + +function(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION) + get_property(__SYMLINK + TARGET + ${_TARGET_NAME} + PROPERTY + JNI_SYMLINK + ) + + if (__SYMLINK) + install( + FILES + ${__SYMLINK} + DESTINATION + ${_DESTINATION} + ) + else (__SYMLINK) + message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.") + endif (__SYMLINK) +endfunction(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION) + +function (find_jar VARIABLE) + set(_jar_names) + set(_jar_files) + set(_jar_versions) + set(_jar_paths + /usr/share/java/ + /usr/local/share/java/ + ${Java_JAR_PATHS}) + set(_jar_doc "NOTSET") + + set(_state "name") + + foreach (arg ${ARGN}) + if (${_state} STREQUAL "name") + if (${arg} STREQUAL "VERSIONS") + set(_state "versions") + elseif (${arg} STREQUAL "NAMES") + set(_state "names") + elseif (${arg} STREQUAL "PATHS") + set(_state "paths") + elseif (${arg} STREQUAL "DOC") + set(_state "doc") + else (${arg} STREQUAL "NAMES") + set(_jar_names ${arg}) + if (_jar_doc STREQUAL "NOTSET") + set(_jar_doc "Finding ${arg} jar") + endif (_jar_doc STREQUAL "NOTSET") + endif (${arg} STREQUAL "VERSIONS") + elseif (${_state} STREQUAL "versions") + if (${arg} STREQUAL "NAMES") + set(_state "names") + elseif (${arg} STREQUAL "PATHS") + set(_state "paths") + elseif (${arg} STREQUAL "DOC") + set(_state "doc") + else (${arg} STREQUAL "NAMES") + set(_jar_versions ${_jar_versions} ${arg}) + endif (${arg} STREQUAL "NAMES") + elseif (${_state} STREQUAL "names") + if (${arg} STREQUAL "VERSIONS") + set(_state "versions") + elseif (${arg} STREQUAL "PATHS") + set(_state "paths") + elseif (${arg} STREQUAL "DOC") + set(_state "doc") + else (${arg} STREQUAL "VERSIONS") + set(_jar_names ${_jar_names} ${arg}) + if (_jar_doc STREQUAL "NOTSET") + set(_jar_doc "Finding ${arg} jar") + endif (_jar_doc STREQUAL "NOTSET") + endif (${arg} STREQUAL "VERSIONS") + elseif (${_state} STREQUAL "paths") + if (${arg} STREQUAL "VERSIONS") + set(_state "versions") + elseif (${arg} STREQUAL "NAMES") + set(_state "names") + elseif (${arg} STREQUAL "DOC") + set(_state "doc") + else (${arg} STREQUAL "VERSIONS") + set(_jar_paths ${_jar_paths} ${arg}) + endif (${arg} STREQUAL "VERSIONS") + elseif (${_state} STREQUAL "doc") + if (${arg} STREQUAL "VERSIONS") + set(_state "versions") + elseif (${arg} STREQUAL "NAMES") + set(_state "names") + elseif (${arg} STREQUAL "PATHS") + set(_state "paths") + else (${arg} STREQUAL "VERSIONS") + set(_jar_doc ${arg}) + endif (${arg} STREQUAL "VERSIONS") + endif (${_state} STREQUAL "name") + endforeach (arg ${ARGN}) + + if (NOT _jar_names) + message(FATAL_ERROR "find_jar: No name to search for given") + endif (NOT _jar_names) + + foreach (jar_name ${_jar_names}) + foreach (version ${_jar_versions}) + set(_jar_files ${_jar_files} ${jar_name}-${version}.jar) + endforeach (version ${_jar_versions}) + set(_jar_files ${_jar_files} ${jar_name}.jar) + endforeach (jar_name ${_jar_names}) + + find_file(${VARIABLE} + NAMES ${_jar_files} + PATHS ${_jar_paths} + DOC ${_jar_doc} + NO_DEFAULT_PATH) +endfunction (find_jar VARIABLE) + +function(create_javadoc _target) + set(_javadoc_packages) + set(_javadoc_files) + set(_javadoc_sourcepath) + set(_javadoc_classpath) + set(_javadoc_installpath "${CMAKE_INSTALL_PREFIX}/share/javadoc") + set(_javadoc_doctitle) + set(_javadoc_windowtitle) + set(_javadoc_author FALSE) + set(_javadoc_version FALSE) + set(_javadoc_use FALSE) + + set(_state "package") + + foreach (arg ${ARGN}) + if (${_state} STREQUAL "package") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_packages ${arg}) + set(_state "packages") + endif () + elseif (${_state} STREQUAL "packages") + if (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + list(APPEND _javadoc_packages ${arg}) + endif () + elseif (${_state} STREQUAL "files") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + list(APPEND _javadoc_files ${arg}) + endif () + elseif (${_state} STREQUAL "sourcepath") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + list(APPEND _javadoc_sourcepath ${arg}) + endif () + elseif (${_state} STREQUAL "classpath") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + list(APPEND _javadoc_classpath ${arg}) + endif () + elseif (${_state} STREQUAL "installpath") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_installpath ${arg}) + endif () + elseif (${_state} STREQUAL "doctitle") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_doctitle ${arg}) + endif () + elseif (${_state} STREQUAL "windowtitle") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_windowtitle ${arg}) + endif () + elseif (${_state} STREQUAL "author") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_author ${arg}) + endif () + elseif (${_state} STREQUAL "use") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_use ${arg}) + endif () + elseif (${_state} STREQUAL "version") + if (${arg} STREQUAL "PACKAGES") + set(_state "packages") + elseif (${arg} STREQUAL "FILES") + set(_state "files") + elseif (${arg} STREQUAL "SOURCEPATH") + set(_state "sourcepath") + elseif (${arg} STREQUAL "CLASSPATH") + set(_state "classpath") + elseif (${arg} STREQUAL "INSTALLPATH") + set(_state "installpath") + elseif (${arg} STREQUAL "DOCTITLE") + set(_state "doctitle") + elseif (${arg} STREQUAL "WINDOWTITLE") + set(_state "windowtitle") + elseif (${arg} STREQUAL "AUTHOR") + set(_state "author") + elseif (${arg} STREQUAL "USE") + set(_state "use") + elseif (${arg} STREQUAL "VERSION") + set(_state "version") + else () + set(_javadoc_version ${arg}) + endif () + endif (${_state} STREQUAL "package") + endforeach (arg ${ARGN}) + + set(_javadoc_builddir ${CMAKE_CURRENT_BINARY_DIR}/javadoc/${_target}) + set(_javadoc_options -d ${_javadoc_builddir}) + + if (_javadoc_sourcepath) + set(_start TRUE) + foreach(_path ${_javadoc_sourcepath}) + if (_start) + set(_sourcepath ${_path}) + set(_start FALSE) + else (_start) + set(_sourcepath ${_sourcepath}:${_path}) + endif (_start) + endforeach(_path ${_javadoc_sourcepath}) + set(_javadoc_options ${_javadoc_options} -sourcepath ${_sourcepath}) + endif (_javadoc_sourcepath) + + if (_javadoc_classpath) + set(_start TRUE) + foreach(_path ${_javadoc_classpath}) + if (_start) + set(_classpath ${_path}) + set(_start FALSE) + else (_start) + set(_classpath ${_classpath}:${_path}) + endif (_start) + endforeach(_path ${_javadoc_classpath}) + set(_javadoc_options ${_javadoc_options} -classpath "${_classpath}") + endif (_javadoc_classpath) + + if (_javadoc_doctitle) + set(_javadoc_options ${_javadoc_options} -doctitle '${_javadoc_doctitle}') + endif (_javadoc_doctitle) + + if (_javadoc_windowtitle) + set(_javadoc_options ${_javadoc_options} -windowtitle '${_javadoc_windowtitle}') + endif (_javadoc_windowtitle) + + if (_javadoc_author) + set(_javadoc_options ${_javadoc_options} -author) + endif (_javadoc_author) + + if (_javadoc_use) + set(_javadoc_options ${_javadoc_options} -use) + endif (_javadoc_use) + + if (_javadoc_version) + set(_javadoc_options ${_javadoc_options} -version) + endif (_javadoc_version) + + add_custom_target(${_target}_javadoc ALL + COMMAND ${Java_JAVADOC_EXECUTABLE} ${_javadoc_options} + ${_javadoc_files} + ${_javadoc_packages} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + install( + DIRECTORY ${_javadoc_builddir} + DESTINATION ${_javadoc_installpath} + ) +endfunction(create_javadoc) diff --git a/depends/launcher/UseJavaClassFilelist.cmake b/depends/launcher/UseJavaClassFilelist.cmake new file mode 100644 index 00000000..c842bf71 --- /dev/null +++ b/depends/launcher/UseJavaClassFilelist.cmake @@ -0,0 +1,52 @@ +# +# This script create a list of compiled Java class files to be added to a +# jar file. This avoids including cmake files which get created in the +# binary directory. +# + +#============================================================================= +# Copyright 2010-2011 Andreas schneider <asn@redhat.com> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +if (CMAKE_JAVA_CLASS_OUTPUT_PATH) + if (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}") + + set(_JAVA_GLOBBED_FILES) + if (CMAKE_JAR_CLASSES_PREFIX) + foreach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX}) + message(STATUS "JAR_CLASS_PREFIX: ${JAR_CLASS_PREFIX}") + + file(GLOB_RECURSE _JAVA_GLOBBED_TMP_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${JAR_CLASS_PREFIX}/*.class") + if (_JAVA_GLOBBED_TMP_FILES) + list(APPEND _JAVA_GLOBBED_FILES ${_JAVA_GLOBBED_TMP_FILES}) + endif (_JAVA_GLOBBED_TMP_FILES) + endforeach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX}) + else() + file(GLOB_RECURSE _JAVA_GLOBBED_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/*.class") + endif (CMAKE_JAR_CLASSES_PREFIX) + + set(_JAVA_CLASS_FILES) + # file(GLOB_RECURSE foo RELATIVE) is broken so we need this. + foreach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES}) + file(RELATIVE_PATH _JAVA_CLASS_FILE ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ${_JAVA_GLOBBED_FILE}) + set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES}${_JAVA_CLASS_FILE}\n) + endforeach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES}) + + # write to file + file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist ${_JAVA_CLASS_FILES}) + + else (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}") + message(SEND_ERROR "FATAL: Java class output path doesn't exist") + endif (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}") +else (CMAKE_JAVA_CLASS_OUTPUT_PATH) + message(SEND_ERROR "FATAL: Can't find CMAKE_JAVA_CLASS_OUTPUT_PATH") +endif (CMAKE_JAVA_CLASS_OUTPUT_PATH) diff --git a/depends/launcher/UseJavaSymlinks.cmake b/depends/launcher/UseJavaSymlinks.cmake new file mode 100644 index 00000000..c66ee1ea --- /dev/null +++ b/depends/launcher/UseJavaSymlinks.cmake @@ -0,0 +1,32 @@ +# +# Helper script for UseJava.cmake +# + +#============================================================================= +# Copyright 2010-2011 Andreas schneider <asn@redhat.com> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +if (UNIX AND _JAVA_TARGET_OUTPUT_LINK) + if (_JAVA_TARGET_OUTPUT_NAME) + find_program(LN_EXECUTABLE + NAMES + ln + ) + + execute_process( + COMMAND ${LN_EXECUTABLE} -sf "${_JAVA_TARGET_OUTPUT_NAME}" "${_JAVA_TARGET_OUTPUT_LINK}" + WORKING_DIRECTORY ${_JAVA_TARGET_DIR} + ) + else (_JAVA_TARGET_OUTPUT_NAME) + message(SEND_ERROR "FATAL: Can't find _JAVA_TARGET_OUTPUT_NAME") + endif (_JAVA_TARGET_OUTPUT_NAME) +endif (UNIX AND _JAVA_TARGET_OUTPUT_LINK) diff --git a/depends/launcher/net/minecraft/Launcher.java b/depends/launcher/net/minecraft/Launcher.java new file mode 100644 index 00000000..8cef35ad --- /dev/null +++ b/depends/launcher/net/minecraft/Launcher.java @@ -0,0 +1,154 @@ +// +// Copyright 2012 MultiMC Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package net.minecraft; + +import java.util.TreeMap; +import java.util.Map; +import java.net.URL; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.applet.Applet; +import java.applet.AppletStub; + +public class Launcher extends Applet implements AppletStub +{ + private Applet wrappedApplet; + private URL documentBase; + private boolean active = false; + private final Map<String, String> params; + + public Launcher(Applet applet, URL documentBase) + { + params = new TreeMap<String, String>(); + + this.setLayout(new BorderLayout()); + this.add(applet, "Center"); + this.wrappedApplet = applet; + this.documentBase = documentBase; + } + + public void setParameter(String name, String value) + { + params.put(name, value); + } + + public void replace(Applet applet) + { + this.wrappedApplet = applet; + + applet.setStub(this); + applet.setSize(getWidth(), getHeight()); + + this.setLayout(new BorderLayout()); + this.add(applet, "Center"); + + applet.init(); + active = true; + applet.start(); + validate(); + } + + @Override + public String getParameter(String name) + { + String param = params.get(name); + if (param != null) + return param; + try + { + return super.getParameter(name); + } catch (Exception ignore){} + return null; + } + + @Override + public boolean isActive() + { + return active; + } + + @Override + public void appletResize(int width, int height) + { + wrappedApplet.resize(width, height); + } + + @Override + public void resize(int width, int height) + { + wrappedApplet.resize(width, height); + } + + @Override + public void resize(Dimension d) + { + wrappedApplet.resize(d); + } + + @Override + public void init() + { + if (wrappedApplet != null) + { + wrappedApplet.init(); + } + } + + @Override + public void start() + { + wrappedApplet.start(); + active = true; + } + + @Override + public void stop() + { + wrappedApplet.stop(); + active = false; + } + + public void destroy() + { + wrappedApplet.destroy(); + } + + @Override + public URL getCodeBase() { + return wrappedApplet.getCodeBase(); + } + + @Override + public URL getDocumentBase() + { + return documentBase; + } + + @Override + public void setVisible(boolean b) + { + super.setVisible(b); + wrappedApplet.setVisible(b); + } + public void update(Graphics paramGraphics) + { + } + public void paint(Graphics paramGraphics) + { + } +}
\ No newline at end of file diff --git a/depends/launcher/org/simplericity/macify/eawt/Application.java b/depends/launcher/org/simplericity/macify/eawt/Application.java new file mode 100644 index 00000000..153bb9ee --- /dev/null +++ b/depends/launcher/org/simplericity/macify/eawt/Application.java @@ -0,0 +1,176 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * The Macify Library API interface provides integration with the OS X platform for Java Applications. + * The API includes a facade to the + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/index.html"> + * Apple Java Extensions API + * </a>. + * Additionally, it provides access to several useful methods in the Cocoa NSApplication API. + * + * The default implementation of this interface is {@link org.simplericity.macify.eawt.DefaultApplication}. + */ +public interface Application { + + static int REQUEST_USER_ATTENTION_TYPE_CRITICAL = 1 ; + static int REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL = 2 ; + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addAboutMenuItem()"> + * Apple's API + * </a>. + */ + void addAboutMenuItem(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addApplicationListener(com.apple.eawt.ApplicationListener)"> + * Apple's API + * </a>. + */ + void addApplicationListener(ApplicationListener applicationListener); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addPreferencesMenuItem()"> + * Apple's API + * </a>. + */ + void addPreferencesMenuItem(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledAboutMenu()"> + * Apple's API + * </a>. + */ + boolean getEnabledAboutMenu(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledPreferencesMenu()"> + * Apple's API + * </a>. + */ + boolean getEnabledPreferencesMenu(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#isAboutMenuItemPresent()"> + * Apple's API + * </a>. + */ + boolean isAboutMenuItemPresent(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#isPreferencesMenuItemPresent()"> + * Apple's API + * </a>. + */ + boolean isPreferencesMenuItemPresent(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removeAboutMenuItem()"> + * Apple's API + * </a>. + */ + void removeAboutMenuItem(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removeApplicationListener(com.apple.eawt.ApplicationListener)"> + * Apple's API + * </a>. + */ + void removeApplicationListener(ApplicationListener applicationListener); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removePreferencesMenuItem()"> + * Apple's API + * </a>. + */ + void removePreferencesMenuItem(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledAboutMenu()"> + * Apple's API + * </a>. + */ + void setEnabledAboutMenu(boolean enabled); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledPreferencesMenu()"> + * Apple's API + * </a>. + */ + void setEnabledPreferencesMenu(boolean enabled); + + /** + * See + * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getMouseLocationOnScreen()"> + * Apple's API + * </a>. + */ + Point getMouseLocationOnScreen(); + + /** + * See + * <a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/index.html#//apple_ref/doc/uid/TP40004004"> + * Apple's NSApplication Class Reference + * </a>. + * @param type on of {@link #REQUEST_USER_ATTENTION_TYPE_CRITICAL} or {@link #REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL}. + */ + int requestUserAttention(int type); + + /** + * See + * <a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/index.html#//apple_ref/doc/uid/TP40004004"> + * Apple's NSApplication Class Reference + * </a> + */ + void cancelUserAttentionRequest(int request); + + /** + * Update the application's icon image + * @param image + */ + void setApplicationIconImage(BufferedImage image); + + /** + * Get the application's icon image. + */ + BufferedImage getApplicationIconImage(); + + /** + * Determines whether the application is running on a Mac AND the Apple Extensions API classes are available. + * @return + */ + boolean isMac(); + + +} diff --git a/depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java new file mode 100644 index 00000000..e9c3db7d --- /dev/null +++ b/depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java @@ -0,0 +1,48 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class ApplicationAdapter implements ApplicationListener { + + public void handleQuit(ApplicationEvent event) { + + } + + public void handleAbout(ApplicationEvent event) { + + } + + public void handleOpenApplication(ApplicationEvent event) { + + } + + public void handleOpenFile(ApplicationEvent event) { + + } + + public void handlePreferences(ApplicationEvent event) { + + } + + public void handlePrintFile(ApplicationEvent event) { + + } + + public void handleReOpenApplication(ApplicationEvent event) { + + } +} diff --git a/depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java new file mode 100644 index 00000000..78420355 --- /dev/null +++ b/depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java @@ -0,0 +1,25 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface ApplicationEvent { + String getFilename(); + boolean isHandled(); + void setHandled(boolean handled); + Object getSource(); + String toString(); +} diff --git a/depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java new file mode 100644 index 00000000..a291bee4 --- /dev/null +++ b/depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java @@ -0,0 +1,27 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface ApplicationListener { + void handleAbout(ApplicationEvent event); + void handleOpenApplication(ApplicationEvent event); + void handleOpenFile(ApplicationEvent event); + void handlePreferences(ApplicationEvent event); + void handlePrintFile(ApplicationEvent event); + void handleQuit(ApplicationEvent event); + void handleReOpenApplication(ApplicationEvent event); +} diff --git a/depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java b/depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java new file mode 100644 index 00000000..5752a350 --- /dev/null +++ b/depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java @@ -0,0 +1,418 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.lang.reflect.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +/** + * Implements Application by calling the Mac OS X API through reflection. + * If this class is used on a non-OS X platform the operations will have no effect or they will simulate + * what the Apple API would do for those who manipulate state. ({@link #setEnabledAboutMenu(boolean)} etc.) + */ +@SuppressWarnings("unchecked") +public class DefaultApplication implements Application { + + private Object application; + private Class applicationListenerClass; + + Map listenerMap = Collections.synchronizedMap(new HashMap<Object, Object>()); + private boolean enabledAboutMenu = true; + private boolean enabledPreferencesMenu; + private boolean aboutMenuItemPresent = true; + private boolean preferencesMenuItemPresent; + private ClassLoader classLoader; + + public DefaultApplication() { + try { + final File file = new File("/System/Library/Java"); + if (file.exists()) { + ClassLoader scl = ClassLoader.getSystemClassLoader(); + Class clc = scl.getClass(); + if (URLClassLoader.class.isAssignableFrom(clc)) { + Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); + addUrl.setAccessible(true); + addUrl.invoke(scl, new Object[]{file.toURI().toURL()}); + } + } + + Class appClass = Class.forName("com.apple.eawt.Application"); + application = appClass.getMethod("getApplication", new Class[0]).invoke(null, new Object[0]); + applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); + } catch (ClassNotFoundException e) { + application = null; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + } + + public boolean isMac() { + return application != null; + } + + public void addAboutMenuItem() { + if (isMac()) { + callMethod(application, "addAboutMenuItem"); + } else { + this.aboutMenuItemPresent = true; + } + } + + public void addApplicationListener(ApplicationListener applicationListener) { + + if (!Modifier.isPublic(applicationListener.getClass().getModifiers())) { + throw new IllegalArgumentException("ApplicationListener must be a public class"); + } + if (isMac()) { + Object listener = Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[]{applicationListenerClass}, + new ApplicationListenerInvocationHandler(applicationListener)); + + callMethod(application, "addApplicationListener", new Class[]{applicationListenerClass}, new Object[]{listener}); + listenerMap.put(applicationListener, listener); + } else { + listenerMap.put(applicationListener, applicationListener); + } + } + + public void addPreferencesMenuItem() { + if (isMac()) { + callMethod("addPreferencesMenuItem"); + } else { + this.preferencesMenuItemPresent = true; + } + } + + public boolean getEnabledAboutMenu() { + if (isMac()) { + return callMethod("getEnabledAboutMenu").equals(Boolean.TRUE); + } else { + return enabledAboutMenu; + } + } + + public boolean getEnabledPreferencesMenu() { + if (isMac()) { + Object result = callMethod("getEnabledPreferencesMenu"); + return result.equals(Boolean.TRUE); + } else { + return enabledPreferencesMenu; + } + } + + public Point getMouseLocationOnScreen() { + if (isMac()) { + try { + Method method = application.getClass().getMethod("getMouseLocationOnScreen", new Class[0]); + return (Point) method.invoke(null, new Object[0]); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + return new Point(0, 0); + } + } + + public boolean isAboutMenuItemPresent() { + if (isMac()) { + return callMethod("isAboutMenuItemPresent").equals(Boolean.TRUE); + } else { + return aboutMenuItemPresent; + } + } + + public boolean isPreferencesMenuItemPresent() { + if (isMac()) { + return callMethod("isPreferencesMenuItemPresent").equals(Boolean.TRUE); + } else { + return this.preferencesMenuItemPresent; + } + } + + public void removeAboutMenuItem() { + if (isMac()) { + callMethod("removeAboutMenuItem"); + } else { + this.aboutMenuItemPresent = false; + } + } + + public synchronized void removeApplicationListener(ApplicationListener applicationListener) { + if (isMac()) { + Object listener = listenerMap.get(applicationListener); + callMethod(application, "removeApplicationListener", new Class[]{applicationListenerClass}, new Object[]{listener}); + + } + listenerMap.remove(applicationListener); + } + + public void removePreferencesMenuItem() { + if (isMac()) { + callMethod("removeAboutMenuItem"); + } else { + this.preferencesMenuItemPresent = false; + } + } + + public void setEnabledAboutMenu(boolean enabled) { + if (isMac()) { + callMethod(application, "setEnabledAboutMenu", new Class[]{Boolean.TYPE}, new Object[]{Boolean.valueOf(enabled)}); + } else { + this.enabledAboutMenu = enabled; + } + } + + public void setEnabledPreferencesMenu(boolean enabled) { + if (isMac()) { + callMethod(application, "setEnabledPreferencesMenu", new Class[]{Boolean.TYPE}, new Object[]{Boolean.valueOf(enabled)}); + } else { + this.enabledPreferencesMenu = enabled; + } + + } + + public int requestUserAttention(int type) { + if (type != REQUEST_USER_ATTENTION_TYPE_CRITICAL && type != REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL) { + throw new IllegalArgumentException("Requested user attention type is not allowed: " + type); + } + try { + Object application = getNSApplication(); + Field critical = application.getClass().getField("UserAttentionRequestCritical"); + Field informational = application.getClass().getField("UserAttentionRequestInformational"); + Field actual = type == REQUEST_USER_ATTENTION_TYPE_CRITICAL ? critical : informational; + + return ((Integer) application.getClass().getMethod("requestUserAttention", new Class[]{Integer.TYPE}).invoke(application, new Object[]{actual.get(null)})).intValue(); + + } catch (ClassNotFoundException e) { + return -1; + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + public void cancelUserAttentionRequest(int request) { + try { + Object application = getNSApplication(); + application.getClass().getMethod("cancelUserAttentionRequest", new Class[]{Integer.TYPE}).invoke(application, new Object[]{new Integer(request)}); + } catch (ClassNotFoundException e) { + // Nada + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private Object getNSApplication() throws ClassNotFoundException { + try { + Class applicationClass = Class.forName("com.apple.cocoa.application.NSApplication"); + return applicationClass.getMethod("sharedApplication", new Class[0]).invoke(null, new Object[0]); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setApplicationIconImage(BufferedImage image) { + if (isMac()) { + try { + Method setDockIconImage = application.getClass().getMethod("setDockIconImage", Image.class); + + try { + setDockIconImage.invoke(application, image); + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } + } catch (NoSuchMethodException mnfe) { + + + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", stream); + } catch (IOException e) { + throw new RuntimeException(e); + } + + try { + Class nsDataClass = Class.forName("com.apple.cocoa.foundation.NSData"); + Constructor constructor = nsDataClass.getConstructor(new Class[]{new byte[0].getClass()}); + + Object nsData = constructor.newInstance(new Object[]{stream.toByteArray()}); + + Class nsImageClass = Class.forName("com.apple.cocoa.application.NSImage"); + Object nsImage = nsImageClass.getConstructor(new Class[]{nsDataClass}).newInstance(new Object[]{nsData}); + + Object application = getNSApplication(); + + application.getClass().getMethod("setApplicationIconImage", new Class[]{nsImageClass}).invoke(application, new Object[]{nsImage}); + + } catch (ClassNotFoundException e) { + + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + + } + + } + } + + public BufferedImage getApplicationIconImage() { + if (isMac()) { + + try { + Method getDockIconImage = application.getClass().getMethod("getDockIconImage"); + try { + return (BufferedImage) getDockIconImage.invoke(application); + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } + } catch (NoSuchMethodException nsme) { + + try { + Class nsDataClass = Class.forName("com.apple.cocoa.foundation.NSData"); + Class nsImageClass = Class.forName("com.apple.cocoa.application.NSImage"); + Object application = getNSApplication(); + Object nsImage = application.getClass().getMethod("applicationIconImage", new Class[0]).invoke(application, new Object[0]); + + Object nsData = nsImageClass.getMethod("TIFFRepresentation", new Class[0]).invoke(nsImage, new Object[0]); + + Integer length = (Integer) nsDataClass.getMethod("length", new Class[0]).invoke(nsData, new Object[0]); + byte[] bytes = (byte[]) nsDataClass.getMethod("bytes", new Class[]{Integer.TYPE, Integer.TYPE}).invoke(nsData, new Object[]{Integer.valueOf(0), length}); + + BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes)); + return image; + + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + } + + return null; + } + + private Object callMethod(String methodname) { + return callMethod(application, methodname, new Class[0], new Object[0]); + } + + private Object callMethod(Object object, String methodname) { + return callMethod(object, methodname, new Class[0], new Object[0]); + } + + private Object callMethod(Object object, String methodname, Class[] classes, Object[] arguments) { + try { + if (classes == null) { + classes = new Class[arguments.length]; + for (int i = 0; i < classes.length; i++) { + classes[i] = arguments[i].getClass(); + + } + } + Method addListnerMethod = object.getClass().getMethod(methodname, classes); + return addListnerMethod.invoke(object, arguments); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + class ApplicationListenerInvocationHandler implements InvocationHandler { + private ApplicationListener applicationListener; + + ApplicationListenerInvocationHandler(ApplicationListener applicationListener) { + this.applicationListener = applicationListener; + } + + public Object invoke(Object object, Method appleMethod, Object[] objects) throws Throwable { + + ApplicationEvent event = createApplicationEvent(objects[0]); + try { + Method method = applicationListener.getClass().getMethod(appleMethod.getName(), new Class[]{ApplicationEvent.class}); + return method.invoke(applicationListener, new Object[]{event}); + } catch (NoSuchMethodException e) { + if (appleMethod.getName().equals("equals") && objects.length == 1) { + return Boolean.valueOf(object == objects[0]); + } + return null; + } + } + } + + private ApplicationEvent createApplicationEvent(final Object appleApplicationEvent) { + return (ApplicationEvent) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ApplicationEvent.class}, new InvocationHandler() { + public Object invoke(Object o, Method method, Object[] objects) throws Throwable { + return appleApplicationEvent.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(appleApplicationEvent, objects); + } + }); + } +} diff --git a/depends/patchlib/CMakeLists.txt b/depends/patchlib/CMakeLists.txt new file mode 100644 index 00000000..4130e08f --- /dev/null +++ b/depends/patchlib/CMakeLists.txt @@ -0,0 +1,14 @@ +project(patchlib C) + +set(SRCS +blocksort.c +huffman.c +crctable.c +randtable.c +compress.c +decompress.c +bzlib.c +bspatch.c +) + +add_library(patchlib STATIC ${SRCS}) diff --git a/depends/patchlib/LICENSE-bzip2 b/depends/patchlib/LICENSE-bzip2 new file mode 100644 index 00000000..cc614178 --- /dev/null +++ b/depends/patchlib/LICENSE-bzip2 @@ -0,0 +1,42 @@ + +-------------------------------------------------------------------------- + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2010 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@bzip.org +bzip2/libbzip2 version 1.0.6 of 6 September 2010 + +-------------------------------------------------------------------------- diff --git a/depends/patchlib/blocksort.c b/depends/patchlib/blocksort.c new file mode 100644 index 00000000..d63dbbf8 --- /dev/null +++ b/depends/patchlib/blocksort.c @@ -0,0 +1,1095 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ + diff --git a/depends/patchlib/bspatch.c b/depends/patchlib/bspatch.c new file mode 100644 index 00000000..e8469edc --- /dev/null +++ b/depends/patchlib/bspatch.c @@ -0,0 +1,303 @@ +/*- + * Copyright 2003-2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _MSC_VER + // bogus 'secure' nonsense + #define _CRT_SECURE_NO_WARNINGS + // bogus signed/unsigned mismatch + #pragma warning( disable: 4018) +#endif + +#ifdef _WIN32 + #include <sys/types.h> + #define ssize_t size_t +#else + #include <unistd.h> +#endif + +#if defined __APPLE__ && defined __MACH__ + typedef unsigned char u_char; +#endif + +#include "bzlib.h" +#include "bspatch.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> + +static off_t offtin(u_char *buf) +{ + off_t y; + + y=buf[7]&0x7F; + y=y*256;y+=buf[6]; + y=y*256;y+=buf[5]; + y=y*256;y+=buf[4]; + y=y*256;y+=buf[3]; + y=y*256;y+=buf[2]; + y=y*256;y+=buf[1]; + y=y*256;y+=buf[0]; + + if(buf[7]&0x80) y=-y; + + return y; +} + +int bspatch(const char * oldfile, const char * newfile, const char * patchfile) +{ + FILE * f, * cpf, * dpf, * epf; + BZFILE * cpfbz2, * dpfbz2, * epfbz2; + int cbz2err, dbz2err, ebz2err; + FILE * temp; + ssize_t oldsize,newsize; + ssize_t bzctrllen,bzdatalen; + unsigned char header[32],buf[8]; + unsigned char *old_contents; + unsigned char *new_contents; + off_t oldpos,newpos; + off_t ctrl[3]; + off_t lenread; + off_t i; + + /* Open patch file */ + if ((f = fopen(patchfile, "rb")) == NULL) + { + //err(1, "fopen(%s)", argv[3]); + return ERR_OTHER; + } + + /* + File format: + 0 8 "BSDIFF40" + 8 8 X + 16 8 Y + 24 8 sizeof(newfile) + 32 X bzip2(control block) + 32+X Y bzip2(diff block) + 32+X+Y ??? bzip2(extra block) + with control block a set of triples (x,y,z) meaning "add x bytes + from oldfile to x bytes from the diff block; copy y bytes from the + extra block; seek forwards in oldfile by z bytes". + */ + + /* Read header */ + if (fread(header, 1, 32, f) < 32) + { + if (feof(f)) + { + //errx(1, "Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + //err(1, "fread(%s)", argv[3]); + return ERR_OTHER; + } + + /* Check for appropriate magic */ + if (memcmp(header, "BSDIFF40", 8) != 0) + { + //errx(1, "Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Read lengths from header */ + bzctrllen=offtin(header+8); + bzdatalen=offtin(header+16); + newsize=offtin(header+24); + if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) + { + //errx(1,"Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Close patch file and re-open it via libbzip2 at the right places */ + if (fclose(f)) + { + //err(1, "fclose(%s)", argv[3]); + return ERR_OTHER; + } + if ((cpf = fopen(patchfile, "rb")) == NULL) + { + //err(1, "fopen(%s)", argv[3]); + return ERR_OTHER; + } + if (fseek(cpf, 32, SEEK_SET)) + { + // err(1, "fseeko(%s, %lld)", argv[3], (long long)32); + return ERR_OTHER; + } + if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) + { + //errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); + return ERR_OTHER; + } + if ((dpf = fopen(patchfile, "rb")) == NULL) + { + //err(1, "fopen(%s)", argv[3]); + return ERR_OTHER; + } + if (fseek(dpf, 32 + bzctrllen, SEEK_SET)) + { + //err(1, "fseeko(%s, %lld)", argv[3], (long long)(32 + bzctrllen)); + return ERR_OTHER; + } + if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) + { + //errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); + return ERR_OTHER; + } + if ((epf = fopen(patchfile, "rb")) == NULL) + { + //err(1, "fopen(%s)", argv[3]); + return ERR_OTHER; + } + if (fseek(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) + { + //err(1, "fseeko(%s, %lld)", argv[3], (long long)(32 + bzctrllen + bzdatalen)); + return ERR_OTHER; + } + if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) + { + //errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); + return ERR_OTHER; + } + + if((temp=fopen(oldfile,"rb")) == NULL) + { + return ERR_OTHER; + } + if((fseek(temp,0,SEEK_END))!=0) + { + return ERR_OTHER; + } + oldsize = ftell(temp); + if((old_contents=malloc(oldsize+1))==NULL) + { + return ERR_OTHER; + } + rewind(temp); + if(fread(old_contents,oldsize,1,temp)!=1) + { + return ERR_OTHER; + } + if(fclose(temp)==EOF) + { + return ERR_OTHER; + } + if((new_contents=malloc(newsize+1))==NULL) + { + //err(1,NULL); + return ERR_OTHER; + } + + oldpos=0;newpos=0; + while(newpos<newsize) + { + /* Read control data */ + for(i=0;i<=2;i++) { + lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); + if ((lenread < 8) || ((cbz2err != BZ_OK) && (cbz2err != BZ_STREAM_END))) + { + //errx(1, "Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + ctrl[i]=offtin(buf); + }; + + /* Sanity-check */ + if(newpos+ctrl[0]>newsize) + { + //errx(1,"Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Read diff string */ + lenread = BZ2_bzRead(&dbz2err, dpfbz2, new_contents + newpos, ctrl[0]); + if ((lenread < ctrl[0]) || ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) + { + //errx(1, "Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Add old data to diff string */ + for(i=0;i<ctrl[0];i++) + { + if((oldpos+i>=0) && (oldpos+i<oldsize)) + { + new_contents[newpos+i]+=old_contents[oldpos+i]; + } + } + + /* Adjust pointers */ + newpos+=ctrl[0]; + oldpos+=ctrl[0]; + + /* Sanity-check */ + if(newpos+ctrl[1]>newsize) + { + //errx(1,"Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Read extra string */ + lenread = BZ2_bzRead(&ebz2err, epfbz2, new_contents + newpos, ctrl[1]); + if ((lenread < ctrl[1]) || ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) + { + //errx(1, "Corrupt patch\n"); + return ERR_CORRUPT_PATCH; + } + + /* Adjust pointers */ + newpos+=ctrl[1]; + oldpos+=ctrl[2]; + }; + + /* Clean up the bzip2 reads */ + BZ2_bzReadClose(&cbz2err, cpfbz2); + BZ2_bzReadClose(&dbz2err, dpfbz2); + BZ2_bzReadClose(&ebz2err, epfbz2); + if (fclose(cpf) || fclose(dpf) || fclose(epf)) + { + //err(1, "fclose(%s)", argv[3]); + return ERR_OTHER; + } + + /* Write the new file */ + if( + ((temp=fopen(newfile,"wb"))==NULL) || + (fwrite(new_contents,newsize,1,temp)==0) || + (fclose(temp)==EOF) + ) + { + //err(1,"%s",argv[2]); + return ERR_OTHER; + } + + free(new_contents); + free(old_contents); + + return ERR_NONE; +} diff --git a/depends/patchlib/bspatch.h b/depends/patchlib/bspatch.h new file mode 100644 index 00000000..efb83ee5 --- /dev/null +++ b/depends/patchlib/bspatch.h @@ -0,0 +1,27 @@ +#ifndef _BSPATCH_H +#define _BSPATCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum BSPatchError +{ + ERR_CORRUPT_PATCH, + ERR_OTHER, + ERR_NONE, +}; + +/** + * patch oldfile by using patchfile and write the output to newfile. + * + * Returns ERR_NONE if successful + */ +int bspatch(const char * oldfile, const char * newfile, const char * patchfile); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/depends/patchlib/bzlib.c b/depends/patchlib/bzlib.c new file mode 100644 index 00000000..e2994b44 --- /dev/null +++ b/depends/patchlib/bzlib.c @@ -0,0 +1,1579 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#ifdef _MSC_VER + //'function': was declared deprecated + #define _CRT_SECURE_NO_DEPRECATE + #define _SCL_SECURE_NO_DEPRECATE + #pragma warning( disable: 4996 ) +#endif + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 10 December 2007.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + Int32 ro_blockSize100k = s->blockSize100k; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include <fcntl.h> +# include <io.h> +# define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/patchlib/bzlib.h b/depends/patchlib/bzlib.h new file mode 100644 index 00000000..7676f23a --- /dev/null +++ b/depends/patchlib/bzlib.h @@ -0,0 +1,283 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include <stdio.h> +#endif + +#ifdef _WIN32 +# include <windows.h> +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ + diff --git a/depends/patchlib/bzlib_private.h b/depends/patchlib/bzlib_private.h new file mode 100644 index 00000000..b134e220 --- /dev/null +++ b/depends/patchlib/bzlib_private.h @@ -0,0 +1,510 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include <stdlib.h> + +#ifndef BZ_NO_STDIO +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.6, 6-Sept-2010" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + diff --git a/depends/patchlib/compress.c b/depends/patchlib/compress.c new file mode 100644 index 00000000..caf76960 --- /dev/null +++ b/depends/patchlib/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/patchlib/crctable.c b/depends/patchlib/crctable.c new file mode 100644 index 00000000..1fea7e94 --- /dev/null +++ b/depends/patchlib/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/patchlib/decompress.c b/depends/patchlib/decompress.c new file mode 100644 index 00000000..311f5668 --- /dev/null +++ b/depends/patchlib/decompress.c @@ -0,0 +1,646 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + /* Check that N doesn't get too big, so that es doesn't + go negative. The maximum value that can be + RUNA/RUNB encoded is equal to the block size (post + the initial RLE), viz, 900k, so bounding N at 2 + million should guard against overflow without + rejecting any legitimate inputs. */ + if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR); + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + /* Check: unzftab entries in range. */ + for (i = 0; i <= 255; i++) { + if (s->unzftab[i] < 0 || s->unzftab[i] > nblock) + RETURN(BZ_DATA_ERROR); + } + /* Actually generate cftab. */ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + /* Check: cftab entries in range. */ + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + /* Check: cftab entries non-descending. */ + for (i = 1; i <= 256; i++) { + if (s->cftab[i-1] > s->cftab[i]) { + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/patchlib/huffman.c b/depends/patchlib/huffman.c new file mode 100644 index 00000000..2283fdbc --- /dev/null +++ b/depends/patchlib/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/patchlib/randtable.c b/depends/patchlib/randtable.c new file mode 100644 index 00000000..6d624599 --- /dev/null +++ b/depends/patchlib/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/depends/quazip/CMakeLists.txt b/depends/quazip/CMakeLists.txt new file mode 100644 index 00000000..b5a391e9 --- /dev/null +++ b/depends/quazip/CMakeLists.txt @@ -0,0 +1,28 @@ +project(quazip) + +# set all include directories for in and out of source builds +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${ZLIB_INCLUDE_DIRS} +) + +# include with QT_USE selected library parts +# INCLUDE(${QT_USE_FILE}) + +file(GLOB SRCS "*.c" "*.cpp") +file(GLOB PUBLIC_HEADERS "*.h") + +# Static link! +ADD_DEFINITIONS(-DQUAZIP_STATIC) + +#qt5_wrap_cpp(MOC_SRCS ${PUBLIC_HEADERS}) +#set(SRCS ${SRCS} ${MOC_SRCS}) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +add_library(quazip STATIC ${SRCS}) +target_link_libraries(quazip ${ZLIB_LIBRARIES}) + +#install(FILES ${PUBLIC_HEADERS} DESTINATION include/quazip) +#install(TARGETS quazip LIBRARY DESTINATION ${LIB_DESTINATION} ARCHIVE DESTINATION ${LIB_DESTINATION} RUNTIME DESTINATION ${LIB_DESTINATION}) diff --git a/depends/quazip/JlCompress.cpp b/depends/quazip/JlCompress.cpp new file mode 100644 index 00000000..69832140 --- /dev/null +++ b/depends/quazip/JlCompress.cpp @@ -0,0 +1,516 @@ +#include "JlCompress.h" +#include <QDebug> + +static bool copyData(QIODevice &inFile, QIODevice &outFile) +{ + while (!inFile.atEnd()) { + char buf[4096]; + qint64 readLen = inFile.read(buf, 4096); + if (readLen <= 0) + return false; + if (outFile.write(buf, readLen) != readLen) + return false; + } + return true; +} + +/**OK + * Comprime il file fileName, nell'oggetto zip, con il nome fileDest. + * + * La funzione fallisce se: + * * zip==NULL; + * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file; + * * non e possibile aprire il file d'origine; + * * non e possibile creare il file all'interno dell'oggetto zip; + * * si e rilevato un errore nella copia dei dati; + * * non e stato possibile chiudere il file all'interno dell'oggetto zip; + */ +bool JlCompress::compressFile(QuaZip* zip, QString fileName, QString fileDest) { + // zip: oggetto dove aggiungere il file + // fileName: nome del file reale + // fileDest: nome del file all'interno del file compresso + + // Controllo l'apertura dello zip + if (!zip) return false; + if (zip->getMode()!=QuaZip::mdCreate && + zip->getMode()!=QuaZip::mdAppend && + zip->getMode()!=QuaZip::mdAdd) return false; + + // Apro il file originale + QFile inFile; + inFile.setFileName(fileName); + if(!inFile.open(QIODevice::ReadOnly)) return false; + + // Apro il file risulato + QuaZipFile outFile(zip); + if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()))) return false; + + // Copio i dati + if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK) { + return false; + } + + // Chiudo i file + outFile.close(); + if (outFile.getZipError()!=UNZ_OK) return false; + inFile.close(); + + return true; +} + +/**OK + * Comprime la cartella dir nel file fileCompressed, se recursive e true allora + * comprime anche le sotto cartelle. I nomi dei file preceduti dal path creato + * togliendo il pat della cartella origDir al path della cartella dir. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato + * di creare. + * + * La funzione fallisce se: + * * zip==NULL; + * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file; + * * la cartella dir non esiste; + * * la compressione di una sotto cartella fallisce (1); + * * la compressione di un file fallisce; + * (1) La funzione si richiama in maniera ricorsiva per comprimere le sotto cartelle + * dunque gli errori di compressione di una sotto cartella sono gli stessi di questa + * funzione. + */ +bool JlCompress::compressSubDir(QuaZip* zip, QString dir, QString origDir, bool recursive) { + // zip: oggetto dove aggiungere il file + // dir: cartella reale corrente + // origDir: cartella reale originale + // (path(dir)-path(origDir)) = path interno all'oggetto zip + + // Controllo l'apertura dello zip + if (!zip) return false; + if (zip->getMode()!=QuaZip::mdCreate && + zip->getMode()!=QuaZip::mdAppend && + zip->getMode()!=QuaZip::mdAdd) return false; + + // Controllo la cartella + QDir directory(dir); + if (!directory.exists()) return false; + + // Se comprimo anche le sotto cartelle + if (recursive) { + // Per ogni sotto cartella + QFileInfoList files = directory.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot); + Q_FOREACH (QFileInfo file, files) { + // Comprimo la sotto cartella + if(!compressSubDir(zip,file.absoluteFilePath(),origDir,recursive)) return false; + } + } + + // Per ogni file nella cartella + QFileInfoList files = directory.entryInfoList(QDir::Files); + QDir origDirectory(origDir); + Q_FOREACH (QFileInfo file, files) { + // Se non e un file o e il file compresso che sto creando + if(!file.isFile()||file.absoluteFilePath()==zip->getZipName()) continue; + + // Creo il nome relativo da usare all'interno del file compresso + QString filename = origDirectory.relativeFilePath(file.absoluteFilePath()); + + // Comprimo il file + if (!compressFile(zip,file.absoluteFilePath(),filename)) return false; + } + + return true; +} + +/**OK + * Estrae il file fileName, contenuto nell'oggetto zip, con il nome fileDest. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato di estrarre. + * + * La funzione fallisce se: + * * zip==NULL; + * * l'oggetto zip e stato aperto in una modalita non compatibile con l'estrazione di file; + * * non e possibile aprire il file all'interno dell'oggetto zip; + * * non e possibile creare il file estratto; + * * si e rilevato un errore nella copia dei dati (1); + * * non e stato possibile chiudere il file all'interno dell'oggetto zip (1); + * + * (1): prima di uscire dalla funzione cancella il file estratto. + */ +bool JlCompress::extractFile(QuaZip* zip, QString fileName, QString fileDest) { + // zip: oggetto dove aggiungere il file + // filename: nome del file reale + // fileincompress: nome del file all'interno del file compresso + + // Controllo l'apertura dello zip + if (!zip) return false; + if (zip->getMode()!=QuaZip::mdUnzip) return false; + + // Apro il file compresso + if (!fileName.isEmpty()) + zip->setCurrentFile(fileName); + QuaZipFile inFile(zip); + if(!inFile.open(QIODevice::ReadOnly) || inFile.getZipError()!=UNZ_OK) return false; + + // Controllo esistenza cartella file risultato + QDir curDir; + if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) { + return false; + } + + if (QFileInfo(fileDest).isDir()) + return true; + + // Apro il file risultato + QFile outFile; + outFile.setFileName(fileDest); + if(!outFile.open(QIODevice::WriteOnly)) return false; + + // Copio i dati + if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK) { + outFile.close(); + removeFile(QStringList(fileDest)); + return false; + } + outFile.close(); + + // Chiudo i file + inFile.close(); + if (inFile.getZipError()!=UNZ_OK) { + removeFile(QStringList(fileDest)); + return false; + } + + return true; +} + +/** + * Rimuove i file il cui nome e specificato all'interno di listFile. + * Restituisce true se tutti i file sono stati cancellati correttamente, attenzione + * perche puo restituire false anche se alcuni file non esistevano e si e tentato + * di cancellarli. + */ +bool JlCompress::removeFile(QStringList listFile) { + bool ret = true; + // Per ogni file + for (int i=0; i<listFile.count(); i++) { + // Lo elimino + ret = ret && QFile::remove(listFile.at(i)); + } + return ret; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/**OK + * Comprime il file fileName nel file fileCompressed. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato + * di creare. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la compressione del file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +bool JlCompress::compressFile(QString fileCompressed, QString file) { + // Creo lo zip + QuaZip zip(fileCompressed); + QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); + if(!zip.open(QuaZip::mdCreate)) { + QFile::remove(fileCompressed); + return false; + } + + // Aggiungo il file + if (!compressFile(&zip,file,QFileInfo(file).fileName())) { + QFile::remove(fileCompressed); + return false; + } + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + QFile::remove(fileCompressed); + return false; + } + + return true; +} + +/**OK + * Comprime i file specificati in files nel file fileCompressed. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato + * di creare. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la compressione di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +bool JlCompress::compressFiles(QString fileCompressed, QStringList files) { + // Creo lo zip + QuaZip zip(fileCompressed); + QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); + if(!zip.open(QuaZip::mdCreate)) { + QFile::remove(fileCompressed); + return false; + } + + // Comprimo i file + QFileInfo info; + Q_FOREACH (QString file, files) { + info.setFile(file); + if (!info.exists() || !compressFile(&zip,file,info.fileName())) { + QFile::remove(fileCompressed); + return false; + } + } + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + QFile::remove(fileCompressed); + return false; + } + + return true; +} + +/**OK + * Comprime la cartella dir nel file fileCompressed, se recursive e true allora + * comprime anche le sotto cartelle. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato + * di creare. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la compressione di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +bool JlCompress::compressDir(QString fileCompressed, QString dir, bool recursive) { + // Creo lo zip + QuaZip zip(fileCompressed); + QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); + if(!zip.open(QuaZip::mdCreate)) { + QFile::remove(fileCompressed); + return false; + } + + // Aggiungo i file e le sotto cartelle + if (!compressSubDir(&zip,dir,dir,recursive)) { + QFile::remove(fileCompressed); + return false; + } + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + QFile::remove(fileCompressed); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/**OK + * Estrae il file fileName, contenuto nel file fileCompressed, con il nome fileDest. + * Se fileDest = "" allora il file viene estratto con lo stesso nome con cui e + * stato compresso. + * Se la funzione fallisce cancella il file che si e tentato di estrarre. + * Restituisce il nome assoluto del file estratto. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * l'estrazione del file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +QString JlCompress::extractFile(QString fileCompressed, QString fileName, QString fileDest) { + // Apro lo zip + QuaZip zip(fileCompressed); + if(!zip.open(QuaZip::mdUnzip)) { + return QString(); + } + + // Estraggo il file + if (fileDest.isEmpty()) + fileDest = fileName; + if (!extractFile(&zip,fileName,fileDest)) { + return QString(); + } + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + removeFile(QStringList(fileDest)); + return QString(); + } + return QFileInfo(fileDest).absoluteFilePath(); +} + +/**OK + * Estrae i file specificati in files, contenuti nel file fileCompressed, nella + * cartella dir. La struttura a cartelle del file compresso viene rispettata. + * Se dir = "" allora il file viene estratto nella cartella corrente. + * Se la funzione fallisce cancella i file che si e tentato di estrarre. + * Restituisce i nomi assoluti dei file estratti. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * l'estrazione di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +QStringList JlCompress::extractFiles(QString fileCompressed, QStringList files, QString dir) { + // Creo lo zip + QuaZip zip(fileCompressed); + if(!zip.open(QuaZip::mdUnzip)) { + return QStringList(); + } + + // Estraggo i file + QStringList extracted; + for (int i=0; i<files.count(); i++) { + QString absPath = QDir(dir).absoluteFilePath(files.at(i)); + if (!extractFile(&zip, files.at(i), absPath)) { + removeFile(extracted); + return QStringList(); + } + extracted.append(absPath); + } + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + removeFile(extracted); + return QStringList(); + } + + return extracted; +} + +QStringList JlCompress::extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions) +{ + QuaZip zip(fileCompressed); + if(!zip.open(QuaZip::mdUnzip)) + { + return QStringList(); + } + + QDir directory(dir); + QStringList extracted; + if (!zip.goToFirstFile()) + { + return QStringList(); + } + do + { + QString name = zip.getCurrentFileName(); + bool ok = true; + for(auto str: exceptions) + { + if(name.startsWith(str)) + { + ok = false; + break; + } + } + if(!ok) + continue; + QString absFilePath = directory.absoluteFilePath(name); + if (!JlCompress::extractFile(&zip, "", absFilePath)) + { + JlCompress::removeFile(extracted); + return QStringList(); + } + extracted.append(absFilePath); + } while (zip.goToNextFile()); + + zip.close(); + if(zip.getZipError()!=0) + { + JlCompress::removeFile(extracted); + return QStringList(); + } + + return extracted; +} + +/**OK + * Estrae il file fileCompressed nella cartella dir. + * Se dir = "" allora il file viene estratto nella cartella corrente. + * Se la funzione fallisce cancella i file che si e tentato di estrarre. + * Restituisce i nomi assoluti dei file estratti. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la compressione di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +QStringList JlCompress::extractDir(QString fileCompressed, QString dir) { + // Apro lo zip + QuaZip zip(fileCompressed); + if(!zip.open(QuaZip::mdUnzip)) { + return QStringList(); + } + + QDir directory(dir); + QStringList extracted; + if (!zip.goToFirstFile()) { + return QStringList(); + } + do { + QString name = zip.getCurrentFileName(); + QString absFilePath = directory.absoluteFilePath(name); + if (!extractFile(&zip, "", absFilePath)) { + removeFile(extracted); + return QStringList(); + } + extracted.append(absFilePath); + } while (zip.goToNextFile()); + + // Chiudo il file zip + zip.close(); + if(zip.getZipError()!=0) { + removeFile(extracted); + return QStringList(); + } + + return extracted; +} + +/**OK + * Restituisce la lista dei file resenti nel file compresso fileCompressed. + * Se la funzione fallisce, restituisce un elenco vuoto. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la richiesta di informazioni di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +QStringList JlCompress::getFileList(QString fileCompressed) { + // Apro lo zip + QuaZip* zip = new QuaZip(QFileInfo(fileCompressed).absoluteFilePath()); + if(!zip->open(QuaZip::mdUnzip)) { + delete zip; + return QStringList(); + } + + // Estraggo i nomi dei file + QStringList lst; + QuaZipFileInfo info; + for(bool more=zip->goToFirstFile(); more; more=zip->goToNextFile()) { + if(!zip->getCurrentFileInfo(&info)) { + delete zip; + return QStringList(); + } + lst << info.name; + //info.name.toLocal8Bit().constData() + } + + // Chiudo il file zip + zip->close(); + if(zip->getZipError()!=0) { + delete zip; + return QStringList(); + } + delete zip; + + return lst; +} + diff --git a/depends/quazip/JlCompress.h b/depends/quazip/JlCompress.h new file mode 100644 index 00000000..29d6191f --- /dev/null +++ b/depends/quazip/JlCompress.h @@ -0,0 +1,123 @@ +#ifndef JLCOMPRESSFOLDER_H_ +#define JLCOMPRESSFOLDER_H_ + +#include "quazip.h" +#include "quazipfile.h" +#include "quazipfileinfo.h" +#include <QString> +#include <QDir> +#include <QFileInfo> +#include <QFile> + +/// Utility class for typical operations. +/** + This class contains a number of useful static functions to perform + simple operations, such as mass ZIP packing or extraction. + */ +class QUAZIP_EXPORT JlCompress { +private: + /// Compress a single file. + /** + \param zip Opened zip to compress the file to. + \param fileName The full path to the source file. + \param fileDest The full name of the file inside the archive. + \return true if success, false otherwise. + */ + static bool compressFile(QuaZip* zip, QString fileName, QString fileDest); + /// Compress a subdirectory. + /** + \param parentZip Opened zip containing the parent directory. + \param dir The full path to the directory to pack. + \param parentDir The full path to the directory corresponding to + the root of the ZIP. + \param recursive Whether to pack sub-directories as well or only + files. + \return true if success, false otherwise. + */ + static bool compressSubDir(QuaZip* parentZip, QString dir, QString parentDir, bool recursive = true); + /// Extract a single file. + /** + \param zip The opened zip archive to extract from. + \param fileName The full name of the file to extract. + \param fileDest The full path to the destination file. + \return true if success, false otherwise. + */ + static bool extractFile(QuaZip* zip, QString fileName, QString fileDest); + /// Remove some files. + /** + \param listFile The list of files to remove. + \return true if success, false otherwise. + */ + static bool removeFile(QStringList listFile); + +public: + /// Compress a single file. + /** + \param fileCompressed The name of the archive. + \param file The file to compress. + \return true if success, false otherwise. + */ + static bool compressFile(QString fileCompressed, QString file); + /// Compress a list of files. + /** + \param fileCompressed The name of the archive. + \param files The file list to compress. + \return true if success, false otherwise. + */ + static bool compressFiles(QString fileCompressed, QStringList files); + /// Compress a whole directory. + /** + \param fileCompressed The name of the archive. + \param dir The directory to compress. + \param recursive Whether to pack the subdirectories as well, or + just regular files. + \return true if success, false otherwise. + */ + static bool compressDir(QString fileCompressed, QString dir = QString(), bool recursive = true); + +public: + /// Extract a single file. + /** + \param fileCompressed The name of the archive. + \param fileName The file to extract. + \param fileDest The destination file, assumed to be identical to + \a file if left empty. + \return The list of the full paths of the files extracted, empty on failure. + */ + static QString extractFile(QString fileCompressed, QString fileName, QString fileDest = QString()); + /// Extract a list of files. + /** + \param fileCompressed The name of the archive. + \param files The file list to extract. + \param dir The directory to put the files to, the current + directory if left empty. + \return The list of the full paths of the files extracted, empty on failure. + */ + static QStringList extractFiles(QString fileCompressed, QStringList files, QString dir = QString()); + /// Extract a whole archive. + /** + \param fileCompressed The name of the archive. + \param dir The directory to extract to, the current directory if + left empty. + \return The list of the full paths of the files extracted, empty on failure. + */ + static QStringList extractDir(QString fileCompressed, QString dir = QString()); + /// Extract a whole archive, with a list of exceptions (prefixes to ignore). + /** + \param fileCompressed The name of the archive. + \param dir The directory to extract to, the current directory if + left empty. + \param exceptions The list of exception prefixes + \return The list of the full paths of the files extracted, empty on failure. + */ + static QStringList extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions); + /// Get the file list. + /** + \return The list of the files in the archive, or, more precisely, the + list of the entries, including both files and directories if they + are present separately. + */ + static QStringList getFileList(QString fileCompressed); +}; + +#endif /* JLCOMPRESSFOLDER_H_ */ diff --git a/depends/quazip/crypt.h b/depends/quazip/crypt.h new file mode 100644 index 00000000..1d6da628 --- /dev/null +++ b/depends/quazip/crypt.h @@ -0,0 +1,135 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#include "quazip_global.h" + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab UNUSED) +{ + //(void) pcrc_32_tab; /* avoid "unused parameter" warning */ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize<RAND_HEAD_LEN) + return 0; + + /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the + * output of rand() to get less predictability, since rand() is + * often poorly implemented. + */ + if (++calls == 1) + { + srand((unsigned)(time(NULL) ^ ZCR_SEED2)); + } + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + c = (rand() >> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/depends/quazip/ioapi.h b/depends/quazip/ioapi.h new file mode 100644 index 00000000..f4c21809 --- /dev/null +++ b/depends/quazip/ioapi.h @@ -0,0 +1,77 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Modified by Sergey A. Tachenov to integrate with Qt. +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, voidpf file, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef uLong (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_qiodevice_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/depends/quazip/qioapi.cpp b/depends/quazip/qioapi.cpp new file mode 100644 index 00000000..f254c34d --- /dev/null +++ b/depends/quazip/qioapi.cpp @@ -0,0 +1,146 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Modified by Sergey A. Tachenov to integrate with Qt. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "zlib.h" +#include "ioapi.h" +#include "quazip_global.h" +#include <QIODevice> + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK qiodevice_open_file_func ( + voidpf opaque UNUSED, + voidpf file, + int mode) +{ + QIODevice *iodevice = reinterpret_cast<QIODevice*>(file); + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + iodevice->open(QIODevice::ReadOnly); + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + iodevice->open(QIODevice::ReadWrite); + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + iodevice->open(QIODevice::WriteOnly); + + if (iodevice->isOpen()) { + if (iodevice->isSequential()) { + iodevice->close(); + return NULL; + } else { + return iodevice; + } + } else + return NULL; +} + + +uLong ZCALLBACK qiodevice_read_file_func ( + voidpf opaque UNUSED, + voidpf stream, + void* buf, + uLong size) +{ + uLong ret; + ret = (uLong)((QIODevice*)stream)->read((char*)buf,size); + return ret; +} + + +uLong ZCALLBACK qiodevice_write_file_func ( + voidpf opaque UNUSED, + voidpf stream, + const void* buf, + uLong size) +{ + uLong ret; + ret = (uLong)((QIODevice*)stream)->write((char*)buf,size); + return ret; +} + +uLong ZCALLBACK qiodevice_tell_file_func ( + voidpf opaque UNUSED, + voidpf stream) +{ + uLong ret; + ret = ((QIODevice*)stream)->pos(); + return ret; +} + +int ZCALLBACK qiodevice_seek_file_func ( + voidpf opaque UNUSED, + voidpf stream, + uLong offset, + int origin) +{ + uLong qiodevice_seek_result=0; + int ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END : + qiodevice_seek_result = ((QIODevice*)stream)->size() - offset; + break; + case ZLIB_FILEFUNC_SEEK_SET : + qiodevice_seek_result = offset; + break; + default: return -1; + } + ret = !((QIODevice*)stream)->seek(qiodevice_seek_result); + return ret; +} + +int ZCALLBACK qiodevice_close_file_func ( + voidpf opaque UNUSED, + voidpf stream) +{ + ((QIODevice*)stream)->close(); + return 0; +} + +int ZCALLBACK qiodevice_error_file_func ( + voidpf opaque UNUSED, + voidpf stream) +{ + // can't check for error due to the QIODevice API limitation + return 0; +} + +void fill_qiodevice_filefunc ( + zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = qiodevice_open_file_func; + pzlib_filefunc_def->zread_file = qiodevice_read_file_func; + pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; + pzlib_filefunc_def->ztell_file = qiodevice_tell_file_func; + pzlib_filefunc_def->zseek_file = qiodevice_seek_file_func; + pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; + pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/depends/quazip/quaadler32.cpp b/depends/quazip/quaadler32.cpp new file mode 100644 index 00000000..097899f6 --- /dev/null +++ b/depends/quazip/quaadler32.cpp @@ -0,0 +1,28 @@ +#include "quaadler32.h" + +#include "zlib.h" + +QuaAdler32::QuaAdler32() +{ + reset(); +} + +quint32 QuaAdler32::calculate(const QByteArray &data) +{ + return adler32( adler32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() ); +} + +void QuaAdler32::reset() +{ + checksum = adler32(0L, Z_NULL, 0); +} + +void QuaAdler32::update(const QByteArray &buf) +{ + checksum = adler32( checksum, (const Bytef*)buf.data(), buf.size() ); +} + +quint32 QuaAdler32::value() +{ + return checksum; +} diff --git a/depends/quazip/quaadler32.h b/depends/quazip/quaadler32.h new file mode 100644 index 00000000..c5ac0532 --- /dev/null +++ b/depends/quazip/quaadler32.h @@ -0,0 +1,29 @@ +#ifndef QUAADLER32_H +#define QUAADLER32_H + +#include <QtCore/QByteArray> + +#include "quachecksum32.h" + +/// Adler32 checksum +/** \class QuaAdler32 quaadler32.h <quazip/quaadler32.h> + * This class wrappers the adler32 function with the QuaChecksum32 interface. + * See QuaChecksum32 for more info. + */ +class QUAZIP_EXPORT QuaAdler32 : public QuaChecksum32 +{ + +public: + QuaAdler32(); + + quint32 calculate(const QByteArray &data); + + void reset(); + void update(const QByteArray &buf); + quint32 value(); + +private: + quint32 checksum; +}; + +#endif //QUAADLER32_H diff --git a/depends/quazip/quachecksum32.h b/depends/quazip/quachecksum32.h new file mode 100644 index 00000000..773ec2a4 --- /dev/null +++ b/depends/quazip/quachecksum32.h @@ -0,0 +1,54 @@ +#ifndef QUACHECKSUM32_H +#define QUACHECKSUM32_H + +#include <QtCore/QByteArray> +#include "quazip_global.h" + +/// Checksum interface. +/** \class QuaChecksum32 quachecksum32.h <quazip/quachecksum32.h> + * This is an interface for 32 bit checksums. + * Classes implementing this interface can calcunate a certin + * checksum in a single step: + * \code + * QChecksum32 *crc32 = new QuaCrc32(); + * rasoult = crc32->calculate(data); + * \endcode + * or by streaming the data: + * \code + * QChecksum32 *crc32 = new QuaCrc32(); + * while(!fileA.atEnd()) + * crc32->update(fileA.read(bufSize)); + * resoultA = crc32->value(); + * crc32->reset(); + * while(!fileB.atEnd()) + * crc32->update(fileB.read(bufSize)); + * resoultB = crc32->value(); + * \endcode + */ +class QUAZIP_EXPORT QuaChecksum32 +{ + +public: + ///Calculates the checksum for data. + /** \a data source data + * \return data checksum + * + * This function has no efect on the value returned by value(). + */ + virtual quint32 calculate(const QByteArray &data) = 0; + + ///Resets the calculation on a checksun for a stream. + virtual void reset() = 0; + + ///Updates the calculated checksum for the stream + /** \a buf next portion of data from the stream + */ + virtual void update(const QByteArray &buf) = 0; + + ///Value of the checksum calculated for the stream passed throw update(). + /** \return checksum + */ + virtual quint32 value() = 0; +}; + +#endif //QUACHECKSUM32_H diff --git a/depends/quazip/quacrc32.cpp b/depends/quazip/quacrc32.cpp new file mode 100644 index 00000000..9381f24c --- /dev/null +++ b/depends/quazip/quacrc32.cpp @@ -0,0 +1,28 @@ +#include "quacrc32.h" + +#include "zlib.h" + +QuaCrc32::QuaCrc32() +{ + reset(); +} + +quint32 QuaCrc32::calculate(const QByteArray &data) +{ + return crc32( crc32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() ); +} + +void QuaCrc32::reset() +{ + checksum = crc32(0L, Z_NULL, 0); +} + +void QuaCrc32::update(const QByteArray &buf) +{ + checksum = crc32( checksum, (const Bytef*)buf.data(), buf.size() ); +} + +quint32 QuaCrc32::value() +{ + return checksum; +} diff --git a/depends/quazip/quacrc32.h b/depends/quazip/quacrc32.h new file mode 100644 index 00000000..4c86d566 --- /dev/null +++ b/depends/quazip/quacrc32.h @@ -0,0 +1,26 @@ +#ifndef QUACRC32_H +#define QUACRC32_H + +#include "quachecksum32.h" + +///CRC32 checksum +/** \class QuaCrc32 quacrc32.h <quazip/quacrc32.h> +* This class wrappers the crc32 function with the QuaChecksum32 interface. +* See QuaChecksum32 for more info. +*/ +class QUAZIP_EXPORT QuaCrc32 : public QuaChecksum32 { + +public: + QuaCrc32(); + + quint32 calculate(const QByteArray &data); + + void reset(); + void update(const QByteArray &buf); + quint32 value(); + +private: + quint32 checksum; +}; + +#endif //QUACRC32_H diff --git a/depends/quazip/quagzipfile.cpp b/depends/quazip/quagzipfile.cpp new file mode 100644 index 00000000..c1c70aad --- /dev/null +++ b/depends/quazip/quagzipfile.cpp @@ -0,0 +1,141 @@ +#include <QFile> + +#include "quagzipfile.h" + +class QuaGzipFilePrivate { + friend class QuaGzipFile; + QString fileName; + gzFile gzd; + inline QuaGzipFilePrivate(): gzd(NULL) {} + inline QuaGzipFilePrivate(const QString &fileName): + fileName(fileName), gzd(NULL) {} + template<typename FileId> bool open(FileId id, + QIODevice::OpenMode mode, QString &error); + gzFile open(int fd, const char *modeString); + gzFile open(const QString &name, const char *modeString); +}; + +gzFile QuaGzipFilePrivate::open(const QString &name, const char *modeString) +{ + return gzopen(QFile::encodeName(name).constData(), modeString); +} + +gzFile QuaGzipFilePrivate::open(int fd, const char *modeString) +{ + return gzdopen(fd, modeString); +} + +template<typename FileId> +bool QuaGzipFilePrivate::open(FileId id, QIODevice::OpenMode mode, + QString &error) +{ + char modeString[2]; + modeString[0] = modeString[1] = '\0'; + if ((mode & QIODevice::ReadOnly) != 0 + && (mode & QIODevice::WriteOnly) != 0) { + error = QuaGzipFile::trUtf8("Opening gzip for both reading" + " and writing is not supported"); + return false; + } else if ((mode & QIODevice::ReadOnly) != 0) { + modeString[0] = 'r'; + } else if ((mode & QIODevice::WriteOnly) != 0) { + modeString[0] = 'w'; + } else { + error = QuaGzipFile::trUtf8("You can open a gzip either for reading" + " or for writing. Which is it?"); + return false; + } + gzd = open(id, modeString); + if (gzd == NULL) { + error = QuaGzipFile::trUtf8("Could not gzopen() file"); + return false; + } + return true; +} + +QuaGzipFile::QuaGzipFile(): +d(new QuaGzipFilePrivate()) +{ +} + +QuaGzipFile::QuaGzipFile(QObject *parent): +QIODevice(parent), +d(new QuaGzipFilePrivate()) +{ +} + +QuaGzipFile::QuaGzipFile(const QString &fileName, QObject *parent): + QIODevice(parent), +d(new QuaGzipFilePrivate(fileName)) +{ +} + +QuaGzipFile::~QuaGzipFile() +{ + if (isOpen()) { + close(); + } + delete d; +} + +void QuaGzipFile::setFileName(const QString& fileName) +{ + d->fileName = fileName; +} + +QString QuaGzipFile::getFileName() const +{ + return d->fileName; +} + +bool QuaGzipFile::isSequential() const +{ + return true; +} + +bool QuaGzipFile::open(QIODevice::OpenMode mode) +{ + QString error; + if (!d->open(d->fileName, mode, error)) { + setErrorString(error); + return false; + } + return QIODevice::open(mode); +} + +bool QuaGzipFile::open(int fd, QIODevice::OpenMode mode) +{ + QString error; + if (!d->open(fd, mode, error)) { + setErrorString(error); + return false; + } + return QIODevice::open(mode); +} + +bool QuaGzipFile::flush() +{ + return gzflush(d->gzd, Z_SYNC_FLUSH) == Z_OK; +} + +void QuaGzipFile::close() +{ + QIODevice::close(); + gzclose(d->gzd); +} + +qint64 QuaGzipFile::readData(char *data, qint64 maxSize) +{ + return gzread(d->gzd, (voidp)data, (unsigned)maxSize); +} + +qint64 QuaGzipFile::writeData(const char *data, qint64 maxSize) +{ + if (maxSize == 0) + return 0; + int written = gzwrite(d->gzd, (voidp)data, (unsigned)maxSize); + if (written == 0) + return -1; + else + return written; +} diff --git a/depends/quazip/quagzipfile.h b/depends/quazip/quagzipfile.h new file mode 100644 index 00000000..211ceadb --- /dev/null +++ b/depends/quazip/quagzipfile.h @@ -0,0 +1,35 @@ +#ifndef QUAZIP_QUAGZIPFILE_H +#define QUAZIP_QUAGZIPFILE_H + +#include <QIODevice> +#include "quazip_global.h" + +#include <zlib.h> + +class QuaGzipFilePrivate; + +class QUAZIP_EXPORT QuaGzipFile: public QIODevice { + Q_OBJECT +public: + QuaGzipFile(); + QuaGzipFile(QObject *parent); + QuaGzipFile(const QString &fileName, QObject *parent = NULL); + virtual ~QuaGzipFile(); + void setFileName(const QString& fileName); + QString getFileName() const; + virtual bool isSequential() const; + virtual bool open(QIODevice::OpenMode mode); + virtual bool open(int fd, QIODevice::OpenMode mode); + virtual bool flush(); + virtual void close(); +protected: + virtual qint64 readData(char *data, qint64 maxSize); + virtual qint64 writeData(const char *data, qint64 maxSize); +private: + // not implemented by design to disable copy + QuaGzipFile(const QuaGzipFile &that); + QuaGzipFile& operator=(const QuaGzipFile &that); + QuaGzipFilePrivate *d; +}; + +#endif // QUAZIP_QUAGZIPFILE_H diff --git a/depends/quazip/quaziodevice.cpp b/depends/quazip/quaziodevice.cpp new file mode 100644 index 00000000..959ca0e8 --- /dev/null +++ b/depends/quazip/quaziodevice.cpp @@ -0,0 +1,283 @@ +#include "quaziodevice.h" + +#define QUAZIO_INBUFSIZE 4096 +#define QUAZIO_OUTBUFSIZE 4096 + +class QuaZIODevicePrivate { + friend class QuaZIODevice; + QuaZIODevicePrivate(QIODevice *io); + ~QuaZIODevicePrivate(); + QIODevice *io; + z_stream zins; + z_stream zouts; + char *inBuf; + int inBufPos; + int inBufSize; + char *outBuf; + int outBufPos; + int outBufSize; + bool zBufError; + int doFlush(QString &error); +}; + +QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): + io(io), + inBuf(NULL), + inBufPos(0), + inBufSize(0), + outBuf(NULL), + outBufPos(0), + outBufSize(0), + zBufError(false) +{ + zins.zalloc = (alloc_func) NULL; + zins.zfree = (free_func) NULL; + zins.opaque = NULL; + zouts.zalloc = (alloc_func) NULL; + zouts.zfree = (free_func) NULL; + zouts.opaque = NULL; + inBuf = new char[QUAZIO_INBUFSIZE]; + outBuf = new char[QUAZIO_OUTBUFSIZE]; +#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT + debug.setFileName("debug.out"); + debug.open(QIODevice::WriteOnly); +#endif +#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT + indebug.setFileName("debug.in"); + indebug.open(QIODevice::WriteOnly); +#endif +} + +QuaZIODevicePrivate::~QuaZIODevicePrivate() +{ +#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT + debug.close(); +#endif +#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT + indebug.close(); +#endif + if (inBuf != NULL) + delete[] inBuf; + if (outBuf != NULL) + delete[] outBuf; +} + +int QuaZIODevicePrivate::doFlush(QString &error) +{ + int flushed = 0; + while (outBufPos < outBufSize) { + int more = io->write(outBuf + outBufPos, outBufSize - outBufPos); + if (more == -1) { + error = io->errorString(); + return -1; + } + if (more == 0) + break; + outBufPos += more; + flushed += more; + } + if (outBufPos == outBufSize) { + outBufPos = outBufSize = 0; + } + return flushed; +} + +// #define QUAZIP_ZIODEVICE_DEBUG_OUTPUT +// #define QUAZIP_ZIODEVICE_DEBUG_INPUT +#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT +#include <QFile> +static QFile debug; +#endif +#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT +#include <QFile> +static QFile indebug; +#endif + +QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): + QIODevice(parent), + d(new QuaZIODevicePrivate(io)) +{ + connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); +} + +QuaZIODevice::~QuaZIODevice() +{ + if (isOpen()) + close(); + delete d; +} + +QIODevice *QuaZIODevice::getIoDevice() const +{ + return d->io; +} + +bool QuaZIODevice::open(QIODevice::OpenMode mode) +{ + if ((mode & QIODevice::ReadOnly) != 0) { + if (inflateInit(&d->zins) != Z_OK) { + setErrorString(d->zins.msg); + return false; + } + } + if ((mode & QIODevice::WriteOnly) != 0) { + if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { + setErrorString(d->zouts.msg); + return false; + } + } + return QIODevice::open(mode); +} + +void QuaZIODevice::close() +{ + if ((openMode() & QIODevice::ReadOnly) != 0) { + if (inflateEnd(&d->zins) != Z_OK) { + setErrorString(d->zins.msg); + } + } + if ((openMode() & QIODevice::WriteOnly) != 0) { + flush(); + if (deflateEnd(&d->zouts) != Z_OK) { + setErrorString(d->zouts.msg); + } + } + QIODevice::close(); +} + +qint64 QuaZIODevice::readData(char *data, qint64 maxSize) +{ + int read = 0; + while (read < maxSize) { + if (d->inBufPos == d->inBufSize) { + d->inBufPos = 0; + d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE); + if (d->inBufSize == -1) { + d->inBufSize = 0; + setErrorString(d->io->errorString()); + return -1; + } + if (d->inBufSize == 0) + break; + } + while (read < maxSize && d->inBufPos < d->inBufSize) { + d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos); + d->zins.avail_in = d->inBufSize - d->inBufPos; + d->zins.next_out = (Bytef *) (data + read); + d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB + int more = 0; + switch (inflate(&d->zins, Z_SYNC_FLUSH)) { + case Z_OK: + read = (char *) d->zins.next_out - data; + d->inBufPos = (char *) d->zins.next_in - d->inBuf; + break; + case Z_STREAM_END: + read = (char *) d->zins.next_out - data; + d->inBufPos = (char *) d->zins.next_in - d->inBuf; + return read; + case Z_BUF_ERROR: // this should never happen, but just in case + if (!d->zBufError) { + qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird", + d->zins.avail_in, d->zins.avail_out); + d->zBufError = true; + } + memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos); + d->inBufSize -= d->inBufPos; + d->inBufPos = 0; + more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize); + if (more == -1) { + setErrorString(d->io->errorString()); + return -1; + } + if (more == 0) + return read; + d->inBufSize += more; + break; + default: + setErrorString(QString::fromLocal8Bit(d->zins.msg)); + return -1; + } + } + } +#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT + indebug.write(data, read); +#endif + return read; +} + +qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize) +{ + int written = 0; + QString error; + if (d->doFlush(error) == -1) { + setErrorString(error); + return -1; + } + while (written < maxSize) { + // there is some data waiting in the output buffer + if (d->outBufPos < d->outBufSize) + return written; + d->zouts.next_in = (Bytef *) (data + written); + d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB + d->zouts.next_out = (Bytef *) d->outBuf; + d->zouts.avail_out = QUAZIO_OUTBUFSIZE; + switch (deflate(&d->zouts, Z_NO_FLUSH)) { + case Z_OK: + written = (char *) d->zouts.next_in - data; + d->outBufSize = (char *) d->zouts.next_out - d->outBuf; + break; + default: + setErrorString(QString::fromLocal8Bit(d->zouts.msg)); + return -1; + } + if (d->doFlush(error) == -1) { + setErrorString(error); + return -1; + } + } +#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT + debug.write(data, written); +#endif + return written; +} + +bool QuaZIODevice::flush() +{ + QString error; + if (d->doFlush(error) < 0) { + setErrorString(error); + return false; + } + // can't flush buffer, some data is still waiting + if (d->outBufPos < d->outBufSize) + return true; + Bytef c = 0; + d->zouts.next_in = &c; // fake input buffer + d->zouts.avail_in = 0; // of zero size + do { + d->zouts.next_out = (Bytef *) d->outBuf; + d->zouts.avail_out = QUAZIO_OUTBUFSIZE; + switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { + case Z_OK: + d->outBufSize = (char *) d->zouts.next_out - d->outBuf; + if (d->doFlush(error) < 0) { + setErrorString(error); + return false; + } + if (d->outBufPos < d->outBufSize) + return true; + break; + case Z_BUF_ERROR: // nothing to write? + return true; + default: + setErrorString(QString::fromLocal8Bit(d->zouts.msg)); + return false; + } + } while (d->zouts.avail_out == 0); + return true; +} + +bool QuaZIODevice::isSequential() const +{ + return true; +} diff --git a/depends/quazip/quaziodevice.h b/depends/quazip/quaziodevice.h new file mode 100644 index 00000000..b061cd16 --- /dev/null +++ b/depends/quazip/quaziodevice.h @@ -0,0 +1,27 @@ +#ifndef QUAZIP_QUAZIODEVICE_H +#define QUAZIP_QUAZIODEVICE_H + +#include <QIODevice> +#include "quazip_global.h" + +#include <zlib.h> + +class QuaZIODevicePrivate; + +class QUAZIP_EXPORT QuaZIODevice: public QIODevice { + Q_OBJECT +public: + QuaZIODevice(QIODevice *io, QObject *parent = NULL); + ~QuaZIODevice(); + virtual bool flush(); + virtual bool open(QIODevice::OpenMode); + virtual void close(); + QIODevice *getIoDevice() const; + virtual bool isSequential() const; +protected: + virtual qint64 readData(char *data, qint64 maxSize); + virtual qint64 writeData(const char *data, qint64 maxSize); +private: + QuaZIODevicePrivate *d; +}; +#endif // QUAZIP_QUAZIODEVICE_H diff --git a/depends/quazip/quazip.cpp b/depends/quazip/quazip.cpp new file mode 100644 index 00000000..b6fa92f0 --- /dev/null +++ b/depends/quazip/quazip.cpp @@ -0,0 +1,554 @@ +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include <QtCore/QFile> +#include <QtCore/QFlags> + +#include "quazip.h" + +/// All the internal stuff for the QuaZip class. +/** + \internal + + This class keeps all the private stuff for the QuaZip class so it can + be changed without breaking binary compatibility, according to the + Pimpl idiom. + */ +class QuaZipPrivate { + friend class QuaZip; + private: + /// The pointer to the corresponding QuaZip instance. + QuaZip *q; + /// The codec for file names. + QTextCodec *fileNameCodec; + /// The codec for comments. + QTextCodec *commentCodec; + /// The archive file name. + QString zipName; + /// The device to access the archive. + QIODevice *ioDevice; + /// The global comment. + QString comment; + /// The open mode. + QuaZip::Mode mode; + union { + /// The internal handle for UNZIP modes. + unzFile unzFile_f; + /// The internal handle for ZIP modes. + zipFile zipFile_f; + }; + /// Whether a current file is set. + bool hasCurrentFile_f; + /// The last error. + int zipError; + /// Whether \ref QuaZip::setDataDescriptorWritingEnabled() "the data descriptor writing mode" is enabled. + bool dataDescriptorWritingEnabled; + /// The constructor for the corresponding QuaZip constructor. + inline QuaZipPrivate(QuaZip *q): + q(q), + fileNameCodec(QTextCodec::codecForLocale()), + commentCodec(QTextCodec::codecForLocale()), + ioDevice(NULL), + mode(QuaZip::mdNotOpen), + hasCurrentFile_f(false), + zipError(UNZ_OK), + dataDescriptorWritingEnabled(true) {} + /// The constructor for the corresponding QuaZip constructor. + inline QuaZipPrivate(QuaZip *q, const QString &zipName): + q(q), + fileNameCodec(QTextCodec::codecForLocale()), + commentCodec(QTextCodec::codecForLocale()), + zipName(zipName), + ioDevice(NULL), + mode(QuaZip::mdNotOpen), + hasCurrentFile_f(false), + zipError(UNZ_OK), + dataDescriptorWritingEnabled(true) {} + /// The constructor for the corresponding QuaZip constructor. + inline QuaZipPrivate(QuaZip *q, QIODevice *ioDevice): + q(q), + fileNameCodec(QTextCodec::codecForLocale()), + commentCodec(QTextCodec::codecForLocale()), + ioDevice(ioDevice), + mode(QuaZip::mdNotOpen), + hasCurrentFile_f(false), + zipError(UNZ_OK), + dataDescriptorWritingEnabled(true) {} + /// Returns either a list of file names or a list of QuaZipFileInfo. + template<typename TFileInfo> + bool getFileInfoList(QList<TFileInfo> *result) const; +}; + +QuaZip::QuaZip(): + p(new QuaZipPrivate(this)) +{ +} + +QuaZip::QuaZip(const QString& zipName): + p(new QuaZipPrivate(this, zipName)) +{ +} + +QuaZip::QuaZip(QIODevice *ioDevice): + p(new QuaZipPrivate(this, ioDevice)) +{ +} + +QuaZip::~QuaZip() +{ + if(isOpen()) + close(); + delete p; +} + +bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi) +{ + p->zipError=UNZ_OK; + if(isOpen()) { + qWarning("QuaZip::open(): ZIP already opened"); + return false; + } + QIODevice *ioDevice = p->ioDevice; + if (ioDevice == NULL) { + if (p->zipName.isEmpty()) { + qWarning("QuaZip::open(): set either ZIP file name or IO device first"); + return false; + } else { + ioDevice = new QFile(p->zipName); + } + } + switch(mode) { + case mdUnzip: + p->unzFile_f=unzOpen2(ioDevice, ioApi); + if(p->unzFile_f!=NULL) { + p->mode=mode; + p->ioDevice = ioDevice; + return true; + } else { + p->zipError=UNZ_OPENERROR; + if (!p->zipName.isEmpty()) + delete ioDevice; + return false; + } + case mdCreate: + case mdAppend: + case mdAdd: + p->zipFile_f=zipOpen2(ioDevice, + mode==mdCreate?APPEND_STATUS_CREATE: + mode==mdAppend?APPEND_STATUS_CREATEAFTER: + APPEND_STATUS_ADDINZIP, + NULL, + ioApi); + if(p->zipFile_f!=NULL) { + p->mode=mode; + p->ioDevice = ioDevice; + return true; + } else { + p->zipError=UNZ_OPENERROR; + if (!p->zipName.isEmpty()) + delete ioDevice; + return false; + } + default: + qWarning("QuaZip::open(): unknown mode: %d", (int)mode); + if (!p->zipName.isEmpty()) + delete ioDevice; + return false; + break; + } +} + +void QuaZip::close() +{ + p->zipError=UNZ_OK; + switch(p->mode) { + case mdNotOpen: + qWarning("QuaZip::close(): ZIP is not open"); + return; + case mdUnzip: + p->zipError=unzClose(p->unzFile_f); + break; + case mdCreate: + case mdAppend: + case mdAdd: + p->zipError=zipClose(p->zipFile_f, + p->comment.isNull() ? NULL + : p->commentCodec->fromUnicode(p->comment).constData()); + break; + default: + qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode); + return; + } + // opened by name, need to delete the internal IO device + if (!p->zipName.isEmpty()) { + delete p->ioDevice; + p->ioDevice = NULL; + } + if(p->zipError==UNZ_OK) + p->mode=mdNotOpen; +} + +void QuaZip::setZipName(const QString& zipName) +{ + if(isOpen()) { + qWarning("QuaZip::setZipName(): ZIP is already open!"); + return; + } + p->zipName=zipName; + p->ioDevice = NULL; +} + +void QuaZip::setIoDevice(QIODevice *ioDevice) +{ + if(isOpen()) { + qWarning("QuaZip::setIoDevice(): ZIP is already open!"); + return; + } + p->ioDevice = ioDevice; + p->zipName = QString(); +} + +int QuaZip::getEntriesCount()const +{ + QuaZip *fakeThis=(QuaZip*)this; // non-const + fakeThis->p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode"); + return -1; + } + unz_global_info globalInfo; + if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK) + return p->zipError; + return (int)globalInfo.number_entry; +} + +QString QuaZip::getComment()const +{ + QuaZip *fakeThis=(QuaZip*)this; // non-const + fakeThis->p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode"); + return QString(); + } + unz_global_info globalInfo; + QByteArray comment; + if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK) + return QString(); + comment.resize(globalInfo.size_comment); + if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0) + return QString(); + fakeThis->p->zipError = UNZ_OK; + return p->commentCodec->toUnicode(comment); +} + +bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs) +{ + p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode"); + return false; + } + if(fileName.isEmpty()) { + p->hasCurrentFile_f=false; + return true; + } + // Unicode-aware reimplementation of the unzLocateFile function + if(p->unzFile_f==NULL) { + p->zipError=UNZ_PARAMERROR; + return false; + } + if(fileName.length()>MAX_FILE_NAME_LENGTH) { + p->zipError=UNZ_PARAMERROR; + return false; + } + bool sens = convertCaseSensitivity(cs) == Qt::CaseSensitive; + QString lower, current; + if(!sens) lower=fileName.toLower(); + p->hasCurrentFile_f=false; + for(bool more=goToFirstFile(); more; more=goToNextFile()) { + current=getCurrentFileName(); + if(current.isEmpty()) return false; + if(sens) { + if(current==fileName) break; + } else { + if(current.toLower()==lower) break; + } + } + return p->hasCurrentFile_f; +} + +bool QuaZip::goToFirstFile() +{ + p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); + return false; + } + p->zipError=unzGoToFirstFile(p->unzFile_f); + p->hasCurrentFile_f=p->zipError==UNZ_OK; + return p->hasCurrentFile_f; +} + +bool QuaZip::goToNextFile() +{ + p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); + return false; + } + p->zipError=unzGoToNextFile(p->unzFile_f); + p->hasCurrentFile_f=p->zipError==UNZ_OK; + if(p->zipError==UNZ_END_OF_LIST_OF_FILE) + p->zipError=UNZ_OK; + return p->hasCurrentFile_f; +} + +bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const +{ + QuaZip *fakeThis=(QuaZip*)this; // non-const + fakeThis->p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode"); + return false; + } + unz_file_info info_z; + QByteArray fileName; + QByteArray extra; + QByteArray comment; + if(info==NULL) return false; + if(!isOpen()||!hasCurrentFile()) return false; + if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK) + return false; + fileName.resize(info_z.size_filename); + extra.resize(info_z.size_file_extra); + comment.resize(info_z.size_file_comment); + if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL, + fileName.data(), fileName.size(), + extra.data(), extra.size(), + comment.data(), comment.size()))!=UNZ_OK) + return false; + info->versionCreated=info_z.version; + info->versionNeeded=info_z.version_needed; + info->flags=info_z.flag; + info->method=info_z.compression_method; + info->crc=info_z.crc; + info->compressedSize=info_z.compressed_size; + info->uncompressedSize=info_z.uncompressed_size; + info->diskNumberStart=info_z.disk_num_start; + info->internalAttr=info_z.internal_fa; + info->externalAttr=info_z.external_fa; + info->name=p->fileNameCodec->toUnicode(fileName); + info->comment=p->commentCodec->toUnicode(comment); + info->extra=extra; + info->dateTime=QDateTime( + QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday), + QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec)); + return true; +} + +QString QuaZip::getCurrentFileName()const +{ + QuaZip *fakeThis=(QuaZip*)this; // non-const + fakeThis->p->zipError=UNZ_OK; + if(p->mode!=mdUnzip) { + qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode"); + return QString(); + } + if(!isOpen()||!hasCurrentFile()) return QString(); + QByteArray fileName(MAX_FILE_NAME_LENGTH, 0); + if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL, fileName.data(), fileName.size(), + NULL, 0, NULL, 0))!=UNZ_OK) + return QString(); + return p->fileNameCodec->toUnicode(fileName.constData()); +} + +void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec) +{ + p->fileNameCodec=fileNameCodec; +} + +void QuaZip::setFileNameCodec(const char *fileNameCodecName) +{ + p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName); +} + +QTextCodec *QuaZip::getFileNameCodec()const +{ + return p->fileNameCodec; +} + +void QuaZip::setCommentCodec(QTextCodec *commentCodec) +{ + p->commentCodec=commentCodec; +} + +void QuaZip::setCommentCodec(const char *commentCodecName) +{ + p->commentCodec=QTextCodec::codecForName(commentCodecName); +} + +QTextCodec *QuaZip::getCommentCodec()const +{ + return p->commentCodec; +} + +QString QuaZip::getZipName() const +{ + return p->zipName; +} + +QIODevice *QuaZip::getIoDevice() const +{ + if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice + return NULL; + return p->ioDevice; +} + +QuaZip::Mode QuaZip::getMode()const +{ + return p->mode; +} + +bool QuaZip::isOpen()const +{ + return p->mode!=mdNotOpen; +} + +int QuaZip::getZipError() const +{ + return p->zipError; +} + +void QuaZip::setComment(const QString& comment) +{ + p->comment=comment; +} + +bool QuaZip::hasCurrentFile()const +{ + return p->hasCurrentFile_f; +} + +unzFile QuaZip::getUnzFile() +{ + return p->unzFile_f; +} + +zipFile QuaZip::getZipFile() +{ + return p->zipFile_f; +} + +void QuaZip::setDataDescriptorWritingEnabled(bool enabled) +{ + p->dataDescriptorWritingEnabled = enabled; +} + +bool QuaZip::isDataDescriptorWritingEnabled() const +{ + return p->dataDescriptorWritingEnabled; +} + +template<typename TFileInfo> +TFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok); + +template<> +QuaZipFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok) +{ + QuaZipFileInfo info; + *ok = zip->getCurrentFileInfo(&info); + return info; +} + +template<> +QString QuaZip_getFileInfo(QuaZip *zip, bool *ok) +{ + QString name = zip->getCurrentFileName(); + *ok = !name.isEmpty(); + return name; +} + +template<typename TFileInfo> +bool QuaZipPrivate::getFileInfoList(QList<TFileInfo> *result) const +{ + QuaZipPrivate *fakeThis=const_cast<QuaZipPrivate*>(this); + fakeThis->zipError=UNZ_OK; + if (mode!=QuaZip::mdUnzip) { + qWarning("QuaZip::getFileNameList/getFileInfoList(): " + "ZIP is not open in mdUnzip mode"); + return false; + } + QString currentFile; + if (q->hasCurrentFile()) { + currentFile = q->getCurrentFileName(); + } + if (q->goToFirstFile()) { + do { + bool ok; + result->append(QuaZip_getFileInfo<TFileInfo>(q, &ok)); + if (!ok) + return false; + } while (q->goToNextFile()); + } + if (zipError != UNZ_OK) + return false; + if (currentFile.isEmpty()) { + if (!q->goToFirstFile()) + return false; + } else { + if (!q->setCurrentFile(currentFile)) + return false; + } + return true; +} + +QStringList QuaZip::getFileNameList() const +{ + QStringList list; + if (p->getFileInfoList(&list)) + return list; + else + return QStringList(); +} + +QList<QuaZipFileInfo> QuaZip::getFileInfoList() const +{ + QList<QuaZipFileInfo> list; + if (p->getFileInfoList(&list)) + return list; + else + return QList<QuaZipFileInfo>(); +} + +Qt::CaseSensitivity QuaZip::convertCaseSensitivity(QuaZip::CaseSensitivity cs) +{ + if (cs == csDefault) { +#ifdef Q_WS_WIN + return Qt::CaseInsensitive; +#else + return Qt::CaseSensitive; +#endif + } else { + return cs == csSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + } +} diff --git a/depends/quazip/quazip.h b/depends/quazip/quazip.h new file mode 100644 index 00000000..a3ab8e52 --- /dev/null +++ b/depends/quazip/quazip.h @@ -0,0 +1,411 @@ +#ifndef QUA_ZIP_H +#define QUA_ZIP_H + +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include <QString> +#include <QStringList> +#include <QTextCodec> + +#include "zip.h" +#include "unzip.h" + +#include "quazip_global.h" +#include "quazipfileinfo.h" + +// just in case it will be defined in the later versions of the ZIP/UNZIP +#ifndef UNZ_OPENERROR +// define additional error code +#define UNZ_OPENERROR -1000 +#endif + +class QuaZipPrivate; + +/// ZIP archive. +/** \class QuaZip quazip.h <quazip/quazip.h> + * This class implements basic interface to the ZIP archive. It can be + * used to read table contents of the ZIP archive and retreiving + * information about the files inside it. + * + * You can also use this class to open files inside archive by passing + * pointer to the instance of this class to the constructor of the + * QuaZipFile class. But see QuaZipFile::QuaZipFile(QuaZip*, QObject*) + * for the possible pitfalls. + * + * This class is indended to provide interface to the ZIP subpackage of + * the ZIP/UNZIP package as well as to the UNZIP subpackage. But + * currently it supports only UNZIP. + * + * The use of this class is simple - just create instance using + * constructor, then set ZIP archive file name using setFile() function + * (if you did not passed the name to the constructor), then open() and + * then use different functions to work with it! Well, if you are + * paranoid, you may also wish to call close before destructing the + * instance, to check for errors on close. + * + * You may also use getUnzFile() and getZipFile() functions to get the + * ZIP archive handle and use it with ZIP/UNZIP package API directly. + * + * This class supports localized file names inside ZIP archive, but you + * have to set up proper codec with setCodec() function. By default, + * locale codec will be used, which is probably ok for UNIX systems, but + * will almost certainly fail with ZIP archives created in Windows. This + * is because Windows ZIP programs have strange habit of using DOS + * encoding for file names in ZIP archives. For example, ZIP archive + * with cyrillic names created in Windows will have file names in \c + * IBM866 encoding instead of \c WINDOWS-1251. I think that calling one + * function is not much trouble, but for true platform independency it + * would be nice to have some mechanism for file name encoding auto + * detection using locale information. Does anyone know a good way to do + * it? + **/ +class QUAZIP_EXPORT QuaZip { + friend class QuaZipPrivate; + public: + /// Useful constants. + enum Constants { + MAX_FILE_NAME_LENGTH=256 /**< Maximum file name length. Taken from + \c UNZ_MAXFILENAMEINZIP constant in + unzip.c. */ + }; + /// Open mode of the ZIP file. + enum Mode { + mdNotOpen, ///< ZIP file is not open. This is the initial mode. + mdUnzip, ///< ZIP file is open for reading files inside it. + mdCreate, ///< ZIP file was created with open() call. + mdAppend, /**< ZIP file was opened in append mode. This refers to + * \c APPEND_STATUS_CREATEAFTER mode in ZIP/UNZIP package + * and means that zip is appended to some existing file + * what is useful when that file contains + * self-extractor code. This is obviously \em not what + * you whant to use to add files to the existing ZIP + * archive. + **/ + mdAdd ///< ZIP file was opened for adding files in the archive. + }; + /// Case sensitivity for the file names. + /** This is what you specify when accessing files in the archive. + * Works perfectly fine with any characters thanks to Qt's great + * unicode support. This is different from ZIP/UNZIP API, where + * only US-ASCII characters was supported. + **/ + enum CaseSensitivity { + csDefault=0, ///< Default for platform. Case sensitive for UNIX, not for Windows. + csSensitive=1, ///< Case sensitive. + csInsensitive=2 ///< Case insensitive. + }; + static Qt::CaseSensitivity convertCaseSensitivity(CaseSensitivity); + private: + QuaZipPrivate *p; + // not (and will not be) implemented + QuaZip(const QuaZip& that); + // not (and will not be) implemented + QuaZip& operator=(const QuaZip& that); + public: + /// Constructs QuaZip object. + /** Call setName() before opening constructed object. */ + QuaZip(); + /// Constructs QuaZip object associated with ZIP file \a zipName. + QuaZip(const QString& zipName); + /// Constructs QuaZip object associated with ZIP file represented by \a ioDevice. + /** The IO device must be seekable, otherwise an error will occur when opening. */ + QuaZip(QIODevice *ioDevice); + /// Destroys QuaZip object. + /** Calls close() if necessary. */ + ~QuaZip(); + /// Opens ZIP file. + /** + * Argument \a mode specifies open mode of the ZIP archive. See Mode + * for details. Note that there is zipOpen2() function in the + * ZIP/UNZIP API which accepts \a globalcomment argument, but it + * does not use it anywhere, so this open() function does not have this + * argument. See setComment() if you need to set global comment. + * + * If the ZIP file is accessed via explicitly set QIODevice, then + * this device is opened in the necessary mode. If the device was + * already opened by some other means, then the behaviour is defined by + * the device implementation, but generally it is not a very good + * idea. For example, QFile will at least issue a warning. + * + * \return \c true if successful, \c false otherwise. + * + * \note ZIP/UNZIP API open calls do not return error code - they + * just return \c NULL indicating an error. But to make things + * easier, quazip.h header defines additional error code \c + * UNZ_ERROROPEN and getZipError() will return it if the open call + * of the ZIP/UNZIP API returns \c NULL. + * + * Argument \a ioApi specifies IO function set for ZIP/UNZIP + * package to use. See unzip.h, zip.h and ioapi.h for details. Note + * that IO API for QuaZip is different from the original package. + * The file path argument was changed to be of type \c voidpf, and + * QuaZip passes a QIODevice pointer there. This QIODevice is either + * set explicitly via setIoDevice() or the QuaZip(QIODevice*) + * constructor, or it is created internally when opening the archive + * by its file name. The default API (qioapi.cpp) just delegates + * everything to the QIODevice API. Not only this allows to use a + * QIODevice instead of file name, but also has a nice side effect + * of raising the file size limit from 2G to 4G. + * + * In short: just forget about the \a ioApi argument and you'll be + * fine. + **/ + bool open(Mode mode, zlib_filefunc_def *ioApi =NULL); + /// Closes ZIP file. + /** Call getZipError() to determine if the close was successful. The + * underlying QIODevice is also closed, regardless of whether it was + * set explicitly or not. */ + void close(); + /// Sets the codec used to encode/decode file names inside archive. + /** This is necessary to access files in the ZIP archive created + * under Windows with non-latin characters in file names. For + * example, file names with cyrillic letters will be in \c IBM866 + * encoding. + **/ + void setFileNameCodec(QTextCodec *fileNameCodec); + /// Sets the codec used to encode/decode file names inside archive. + /** \overload + * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName)); + **/ + void setFileNameCodec(const char *fileNameCodecName); + /// Returns the codec used to encode/decode comments inside archive. + QTextCodec* getFileNameCodec() const; + /// Sets the codec used to encode/decode comments inside archive. + /** This codec defaults to locale codec, which is probably ok. + **/ + void setCommentCodec(QTextCodec *commentCodec); + /// Sets the codec used to encode/decode comments inside archive. + /** \overload + * Equivalent to calling setCommentCodec(QTextCodec::codecForName(codecName)); + **/ + void setCommentCodec(const char *commentCodecName); + /// Returns the codec used to encode/decode comments inside archive. + QTextCodec* getCommentCodec() const; + /// Returns the name of the ZIP file. + /** Returns null string if no ZIP file name has been set, for + * example when the QuaZip instance is set up to use a QIODevice + * instead. + * \sa setZipName(), setIoDevice(), getIoDevice() + **/ + QString getZipName() const; + /// Sets the name of the ZIP file. + /** Does nothing if the ZIP file is open. + * + * Does not reset error code returned by getZipError(). + * \sa setIoDevice(), getIoDevice(), getZipName() + **/ + void setZipName(const QString& zipName); + /// Returns the device representing this ZIP file. + /** Returns null string if no device has been set explicitly, for + * example when opening a ZIP file by name. + * \sa setIoDevice(), getZipName(), setZipName() + **/ + QIODevice *getIoDevice() const; + /// Sets the device representing the ZIP file. + /** Does nothing if the ZIP file is open. + * + * Does not reset error code returned by getZipError(). + * \sa getIoDevice(), getZipName(), setZipName() + **/ + void setIoDevice(QIODevice *ioDevice); + /// Returns the mode in which ZIP file was opened. + Mode getMode() const; + /// Returns \c true if ZIP file is open, \c false otherwise. + bool isOpen() const; + /// Returns the error code of the last operation. + /** Returns \c UNZ_OK if the last operation was successful. + * + * Error code resets to \c UNZ_OK every time you call any function + * that accesses something inside ZIP archive, even if it is \c + * const (like getEntriesCount()). open() and close() calls reset + * error code too. See documentation for the specific functions for + * details on error detection. + **/ + int getZipError() const; + /// Returns number of the entries in the ZIP central directory. + /** Returns negative error code in the case of error. The same error + * code will be returned by subsequent getZipError() call. + **/ + int getEntriesCount() const; + /// Returns global comment in the ZIP file. + QString getComment() const; + /// Sets the global comment in the ZIP file. + /** The comment will be written to the archive on close operation. + * QuaZip makes a distinction between a null QByteArray() comment + * and an empty "" comment in the QuaZip::mdAdd mode. + * A null comment is the default and it means "don't change + * the comment". An empty comment removes the original comment. + * + * \sa open() + **/ + void setComment(const QString& comment); + /// Sets the current file to the first file in the archive. + /** Returns \c true on success, \c false otherwise. Call + * getZipError() to get the error code. + **/ + bool goToFirstFile(); + /// Sets the current file to the next file in the archive. + /** Returns \c true on success, \c false otherwise. Call + * getZipError() to determine if there was an error. + * + * Should be used only in QuaZip::mdUnzip mode. + * + * \note If the end of file was reached, getZipError() will return + * \c UNZ_OK instead of \c UNZ_END_OF_LIST_OF_FILE. This is to make + * things like this easier: + * \code + * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { + * // do something + * } + * if(zip.getZipError()==UNZ_OK) { + * // ok, there was no error + * } + * \endcode + **/ + bool goToNextFile(); + /// Sets current file by its name. + /** Returns \c true if successful, \c false otherwise. Argument \a + * cs specifies case sensitivity of the file name. Call + * getZipError() in the case of a failure to get error code. + * + * This is not a wrapper to unzLocateFile() function. That is + * because I had to implement locale-specific case-insensitive + * comparison. + * + * Here are the differences from the original implementation: + * + * - If the file was not found, error code is \c UNZ_OK, not \c + * UNZ_END_OF_LIST_OF_FILE (see also goToNextFile()). + * - If this function fails, it unsets the current file rather than + * resetting it back to what it was before the call. + * + * If \a fileName is null string then this function unsets the + * current file and return \c true. Note that you should close the + * file first if it is open! See + * QuaZipFile::QuaZipFile(QuaZip*,QObject*) for the details. + * + * Should be used only in QuaZip::mdUnzip mode. + * + * \sa setFileNameCodec(), CaseSensitivity + **/ + bool setCurrentFile(const QString& fileName, CaseSensitivity cs =csDefault); + /// Returns \c true if the current file has been set. + bool hasCurrentFile() const; + /// Retrieves information about the current file. + /** Fills the structure pointed by \a info. Returns \c true on + * success, \c false otherwise. In the latter case structure pointed + * by \a info remains untouched. If there was an error, + * getZipError() returns error code. + * + * Should be used only in QuaZip::mdUnzip mode. + * + * Does nothing and returns \c false in any of the following cases. + * - ZIP is not open; + * - ZIP does not have current file; + * - \a info is \c NULL; + * + * In all these cases getZipError() returns \c UNZ_OK since there + * is no ZIP/UNZIP API call. + **/ + bool getCurrentFileInfo(QuaZipFileInfo* info)const; + /// Returns the current file name. + /** Equivalent to calling getCurrentFileInfo() and then getting \c + * name field of the QuaZipFileInfo structure, but faster and more + * convenient. + * + * Should be used only in QuaZip::mdUnzip mode. + **/ + QString getCurrentFileName()const; + /// Returns \c unzFile handle. + /** You can use this handle to directly call UNZIP part of the + * ZIP/UNZIP package functions (see unzip.h). + * + * \warning When using the handle returned by this function, please + * keep in mind that QuaZip class is unable to detect any changes + * you make in the ZIP file state (e. g. changing current file, or + * closing the handle). So please do not do anything with this + * handle that is possible to do with the functions of this class. + * Or at least return the handle in the original state before + * calling some another function of this class (including implicit + * destructor calls and calls from the QuaZipFile objects that refer + * to this QuaZip instance!). So if you have changed the current + * file in the ZIP archive - then change it back or you may + * experience some strange behavior or even crashes. + **/ + unzFile getUnzFile(); + /// Returns \c zipFile handle. + /** You can use this handle to directly call ZIP part of the + * ZIP/UNZIP package functions (see zip.h). Warnings about the + * getUnzFile() function also apply to this function. + **/ + zipFile getZipFile(); + /// Changes the data descriptor writing mode. + /** + According to the ZIP format specification, a file inside archive + may have a data descriptor immediately following the file + data. This is reflected by a special flag in the local file header + and in the central directory. By default, QuaZIP sets this flag + and writes the data descriptor unless both method and level were + set to 0, in which case it operates in 1.0-compatible mode and + never writes data descriptors. + + By setting this flag to false, it is possible to disable data + descriptor writing, thus increasing compatibility with archive + readers that don't understand this feature of the ZIP file format. + + Setting this flag affects all the QuaZipFile instances that are + opened after this flag is set. + + The data descriptor writing mode is enabled by default. + + \param enabled If \c true, enable local descriptor writing, + disable it otherwise. + + \sa QuaZipFile::setDataDescriptorWritingEnabled() + */ + void setDataDescriptorWritingEnabled(bool enabled); + /// Returns the data descriptor default writing mode. + /** + \sa setDataDescriptorWritingEnabled() + */ + bool isDataDescriptorWritingEnabled() const; + /// Returns a list of files inside the archive. + /** + \return A list of file names or an empty list if there + was an error or if the archive is empty (call getZipError() to + figure out which). + \sa getFileInfoList() + */ + QStringList getFileNameList() const; + /// Returns information list about all files inside the archive. + /** + \return A list of QuaZipFileInfo objects or an empty list if there + was an error or if the archive is empty (call getZipError() to + figure out which). + \sa getFileNameList() + */ + QList<QuaZipFileInfo> getFileInfoList() const; +}; + +#endif diff --git a/depends/quazip/quazip_global.h b/depends/quazip/quazip_global.h new file mode 100644 index 00000000..d9d09ade --- /dev/null +++ b/depends/quazip/quazip_global.h @@ -0,0 +1,55 @@ +/** +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + */ + +#ifndef QUAZIP_GLOBAL_H +#define QUAZIP_GLOBAL_H + +#include <QtCore/qglobal.h> + +/** + This is automatically defined when building a static library, but when + including QuaZip sources directly into a project, QUAZIP_STATIC should + be defined explicitly to avoid possible troubles with unnecessary + importing/exporting. + */ +#ifdef QUAZIP_STATIC +#define QUAZIP_EXPORT +#else +/** + * When building a DLL with MSVC, QUAZIP_BUILD must be defined. + * qglobal.h takes care of defining Q_DECL_* correctly for msvc/gcc. + */ +#if defined(QUAZIP_BUILD) + #define QUAZIP_EXPORT Q_DECL_EXPORT +#else + #define QUAZIP_EXPORT Q_DECL_IMPORT +#endif +#endif // QUAZIP_STATIC + +#ifdef __GNUC__ +#define UNUSED __attribute__((__unused__)) +#else +#define UNUSED +#endif + +#endif // QUAZIP_GLOBAL_H diff --git a/depends/quazip/quazipdir.cpp b/depends/quazip/quazipdir.cpp new file mode 100644 index 00000000..02208894 --- /dev/null +++ b/depends/quazip/quazipdir.cpp @@ -0,0 +1,507 @@ +#include "quazipdir.h" + +#include <QSet> +#include <QSharedData> + +class QuaZipDirPrivate: public QSharedData { + friend class QuaZipDir; +private: + QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()): + zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault), + filter(QDir::NoFilter), sorting(QDir::NoSort) {} + QuaZip *zip; + QString dir; + QuaZip::CaseSensitivity caseSensitivity; + QDir::Filters filter; + QStringList nameFilters; + QDir::SortFlags sorting; + template<typename TFileInfoList> + bool entryInfoList(QStringList nameFilters, QDir::Filters filter, + QDir::SortFlags sort, TFileInfoList &result) const; + inline QString simplePath() const {return QDir::cleanPath(dir);} +}; + +QuaZipDir::QuaZipDir(const QuaZipDir &that): + d(that.d) +{ +} + +QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir): + d(new QuaZipDirPrivate(zip, dir)) +{ + if (d->dir.startsWith('/')) + d->dir = d->dir.mid(1); +} + +QuaZipDir::~QuaZipDir() +{ +} + +bool QuaZipDir::operator==(const QuaZipDir &that) +{ + return d->zip == that.d->zip && d->dir == that.d->dir; +} + +QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that) +{ + this->d = that.d; + return *this; +} + +QString QuaZipDir::operator[](int pos) const +{ + return entryList().at(pos); +} + +QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const +{ + return d->caseSensitivity; +} + +bool QuaZipDir::cd(const QString &directoryName) +{ + if (directoryName == "/") { + d->dir = ""; + return true; + } + QString dirName = directoryName; + if (dirName.endsWith('/')) + dirName.chop(1); + if (dirName.contains('/')) { + QuaZipDir dir(*this); + if (dirName.startsWith('/')) { +#ifdef QUAZIP_QUAZIPDIR_DEBUG + qDebug("QuaZipDir::cd(%s): going to /", + dirName.toUtf8().constData()); +#endif + if (!dir.cd("/")) + return false; + } + QStringList path = dirName.split('/', QString::SkipEmptyParts); + for (QStringList::const_iterator i = path.constBegin(); + i != path.end(); + ++i) { + const QString &step = *i; +#ifdef QUAZIP_QUAZIPDIR_DEBUG + qDebug("QuaZipDir::cd(%s): going to %s", + dirName.toUtf8().constData(), + step.toUtf8().constData()); +#endif + if (!dir.cd(step)) + return false; + } + d->dir = dir.path(); + return true; + } else { // no '/' + if (dirName == ".") { + return true; + } else if (dirName == "..") { + if (isRoot()) { + return false; + } else { + int slashPos = d->dir.lastIndexOf('/'); + if (slashPos == -1) { + d->dir = ""; + } else { + d->dir = d->dir.left(slashPos); + } + return true; + } + } else { // a simple subdirectory + if (exists(dirName)) { + if (isRoot()) + d->dir = dirName; + else + d->dir += "/" + dirName; + return true; + } else { + return false; + } + } + } +} + +bool QuaZipDir::cdUp() +{ + return cd(".."); +} + +uint QuaZipDir::count() const +{ + return entryList().count(); +} + +QString QuaZipDir::dirName() const +{ + return QDir(d->dir).dirName(); +} + +QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok, + const QString &relativeName, + bool isReal) +{ + QuaZipFileInfo info; + if (isReal) { + *ok = zip->getCurrentFileInfo(&info); + } else { + *ok = true; + info.compressedSize = 0; + info.crc = 0; + info.diskNumberStart = 0; + info.externalAttr = 0; + info.flags = 0; + info.internalAttr = 0; + info.method = 0; + info.uncompressedSize = 0; + info.versionCreated = info.versionNeeded = 0; + } + info.name = relativeName; + return info; +} + +template<typename TFileInfoList> +void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, TFileInfoList &to); + +template<> +void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QList<QuaZipFileInfo> &to) +{ + to = from; +} + +template<> +void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QStringList &to) +{ + to.clear(); + for (QList<QuaZipFileInfo>::const_iterator i = from.constBegin(); + i != from.constEnd(); + ++i) { + to.append(i->name); + } +} + +// utility class to restore the current file +class QuaZipDirRestoreCurrent { +public: + inline QuaZipDirRestoreCurrent(QuaZip *zip): + zip(zip), currentFile(zip->getCurrentFileName()) {} + inline ~QuaZipDirRestoreCurrent() + { + zip->setCurrentFile(currentFile); + } +private: + QuaZip *zip; + QString currentFile; +}; + +class QuaZipDirComparator +{ + private: + QDir::SortFlags sort; + static QString getExtension(const QString &name); + int compareStrings(const QString &string1, const QString &string2); + public: + inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {} + bool operator()(const QuaZipFileInfo &info1, const QuaZipFileInfo &info2); +}; + +QString QuaZipDirComparator::getExtension(const QString &name) +{ + if (name.endsWith('.') || name.indexOf('.', 1) == -1) { + return ""; + } else { + return name.mid(name.lastIndexOf('.') + 1); + } + +} + +int QuaZipDirComparator::compareStrings(const QString &string1, + const QString &string2) +{ + if (sort & QDir::LocaleAware) { + if (sort & QDir::IgnoreCase) { + return string1.toLower().localeAwareCompare(string2.toLower()); + } else { + return string1.localeAwareCompare(string2); + } + } else { + return string1.compare(string2, (sort & QDir::IgnoreCase) + ? Qt::CaseInsensitive : Qt::CaseSensitive); + } +} + +bool QuaZipDirComparator::operator()(const QuaZipFileInfo &info1, + const QuaZipFileInfo &info2) +{ + QDir::SortFlags order = sort + & (QDir::Name | QDir::Time | QDir::Size | QDir::Type); + if ((sort & QDir::DirsFirst) == QDir::DirsFirst + || (sort & QDir::DirsLast) == QDir::DirsLast) { + if (info1.name.endsWith('/') && !info2.name.endsWith('/')) + return (sort & QDir::DirsFirst) == QDir::DirsFirst; + else if (!info1.name.endsWith('/') && info2.name.endsWith('/')) + return (sort & QDir::DirsLast) == QDir::DirsLast; + } + bool result; + int extDiff; + switch (order) { + case QDir::Name: + result = compareStrings(info1.name, info2.name) < 0; + break; + case QDir::Type: + extDiff = compareStrings(getExtension(info1.name), + getExtension(info2.name)); + if (extDiff == 0) { + result = compareStrings(info1.name, info2.name) < 0; + } else { + result = extDiff < 0; + } + break; + case QDir::Size: + if (info1.uncompressedSize == info2.uncompressedSize) { + result = compareStrings(info1.name, info2.name) < 0; + } else { + result = info1.uncompressedSize < info2.uncompressedSize; + } + break; + case QDir::Time: + if (info1.dateTime == info2.dateTime) { + result = compareStrings(info1.name, info2.name) < 0; + } else { + result = info1.dateTime < info2.dateTime; + } + break; + default: + qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X", + static_cast<unsigned>(sort)); + return false; + } + return (sort & QDir::Reversed) ? !result : result; +} + +template<typename TFileInfoList> +bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, + QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const +{ + QString basePath = simplePath(); + if (!basePath.isEmpty()) + basePath += "/"; + int baseLength = basePath.length(); + result.clear(); + QuaZipDirRestoreCurrent saveCurrent(zip); + if (!zip->goToFirstFile()) { + return zip->getZipError() == UNZ_OK; + } + QDir::Filters fltr = filter; + if (fltr == QDir::NoFilter) + fltr = this->filter; + if (fltr == QDir::NoFilter) + fltr = QDir::AllEntries; + QStringList nmfltr = nameFilters; + if (nmfltr.isEmpty()) + nmfltr = this->nameFilters; + QSet<QString> dirsFound; + QList<QuaZipFileInfo> list; + do { + QString name = zip->getCurrentFileName(); + if (!name.startsWith(basePath)) + continue; + QString relativeName = name.mid(baseLength); + bool isDir = false; + bool isReal = true; + if (relativeName.contains('/')) { + int indexOfSlash = relativeName.indexOf('/'); + // something like "subdir/" + isReal = indexOfSlash == relativeName.length() - 1; + relativeName = relativeName.left(indexOfSlash + 1); + if (dirsFound.contains(relativeName)) + continue; + isDir = true; + } + dirsFound.insert(relativeName); + if ((fltr & QDir::Dirs) == 0 && isDir) + continue; + if ((fltr & QDir::Files) == 0 && !isDir) + continue; + if (!nmfltr.isEmpty() && QDir::match(nmfltr, relativeName)) + continue; + bool ok; + QuaZipFileInfo info = QuaZipDir_getFileInfo(zip, &ok, relativeName, + isReal); + if (!ok) { + return false; + } + list.append(info); + } while (zip->goToNextFile()); + QDir::SortFlags srt = sort; + if (srt == QDir::NoSort) + srt = sorting; +#ifdef QUAZIP_QUAZIPDIR_DEBUG + qDebug("QuaZipDirPrivate::entryInfoList(): before sort:"); + foreach (QuaZipFileInfo info, list) { + qDebug("%s\t%s", info.name.toUtf8().constData(), + info.dateTime.toString(Qt::ISODate).toUtf8().constData()); + } +#endif + if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) { + if (QuaZip::convertCaseSensitivity(caseSensitivity) + == Qt::CaseInsensitive) + srt |= QDir::IgnoreCase; + QuaZipDirComparator lessThan(srt); + qSort(list.begin(), list.end(), lessThan); + } + QuaZipDir_convertInfoList(list, result); + return true; +} + +QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters, + QDir::Filters filters, QDir::SortFlags sort) const +{ + QList<QuaZipFileInfo> result; + if (d->entryInfoList(nameFilters, filters, sort, result)) + return result; + else + return QList<QuaZipFileInfo>(); +} + +QList<QuaZipFileInfo> QuaZipDir::entryInfoList(QDir::Filters filters, + QDir::SortFlags sort) const +{ + return entryInfoList(QStringList(), filters, sort); +} + +QStringList QuaZipDir::entryList(const QStringList &nameFilters, + QDir::Filters filters, QDir::SortFlags sort) const +{ + QStringList result; + if (d->entryInfoList(nameFilters, filters, sort, result)) + return result; + else + return QStringList(); +} + +QStringList QuaZipDir::entryList(QDir::Filters filters, + QDir::SortFlags sort) const +{ + return entryList(QStringList(), filters, sort); +} + +bool QuaZipDir::exists(const QString &filePath) const +{ + if (filePath == "/") + return true; + QString fileName = filePath; + if (fileName.endsWith('/')) + fileName.chop(1); + if (fileName.contains('/')) { + QFileInfo fileInfo(fileName); +#ifdef QUAZIP_QUAZIPDIR_DEBUG + qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, " + "fileInfo.path()=%s", fileName.toUtf8().constData(), + fileInfo.fileName().toUtf8().constData(), + fileInfo.path().toUtf8().constData()); +#endif + QuaZipDir dir(*this); + return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName()); + } else { + if (fileName == "..") { + return !isRoot(); + } else if (fileName == ".") { + return true; + } else { + QStringList entries = entryList(QDir::AllEntries, QDir::NoSort); +#ifdef QUAZIP_QUAZIPDIR_DEBUG + qDebug("QuaZipDir::exists(): looking for %s", + fileName.toUtf8().constData()); + for (QStringList::const_iterator i = entries.constBegin(); + i != entries.constEnd(); + ++i) { + qDebug("QuaZipDir::exists(): entry: %s", + i->toUtf8().constData()); + } +#endif + Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity( + d->caseSensitivity); + if (filePath.endsWith('/')) { + return entries.contains(filePath, cs); + } else { + return entries.contains(fileName, cs) + || entries.contains(fileName + "/", cs); + } + } + } +} + +bool QuaZipDir::exists() const +{ + QDir thisDir(d->dir); + return QuaZipDir(d->zip, thisDir.filePath("..")).exists(thisDir.dirName()); +} + +QString QuaZipDir::filePath(const QString &fileName) const +{ + return QDir(d->dir).filePath(fileName); +} + +QDir::Filters QuaZipDir::filter() +{ + return d->filter; +} + +bool QuaZipDir::isRoot() const +{ + return d->simplePath().isEmpty(); +} + +QStringList QuaZipDir::nameFilters() const +{ + return d->nameFilters; +} + +QString QuaZipDir::path() const +{ + return d->dir; +} + +QString QuaZipDir::relativeFilePath(const QString &fileName) const +{ + return QDir(d->dir).relativeFilePath(fileName); +} + +void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity) +{ + d->caseSensitivity = caseSensitivity; +} + +void QuaZipDir::setFilter(QDir::Filters filters) +{ + d->filter = filters; +} + +void QuaZipDir::setNameFilters(const QStringList &nameFilters) +{ + d->nameFilters = nameFilters; +} + +void QuaZipDir::setPath(const QString &path) +{ + QString newDir = path; + if (newDir == "/") { + d->dir = ""; + } else { + if (newDir.endsWith('/')) + newDir.chop(1); + if (newDir.startsWith('/')) + newDir = newDir.mid(1); + d->dir = newDir; + } +} + +void QuaZipDir::setSorting(QDir::SortFlags sort) +{ + d->sorting = sort; +} + +QDir::SortFlags QuaZipDir::sorting() const +{ + return d->sorting; +} diff --git a/depends/quazip/quazipdir.h b/depends/quazip/quazipdir.h new file mode 100644 index 00000000..e2d70bc8 --- /dev/null +++ b/depends/quazip/quazipdir.h @@ -0,0 +1,171 @@ +#ifndef QUAZIP_QUAZIPDIR_H +#define QUAZIP_QUAZIPDIR_H + +class QuaZipDirPrivate; + +#include "quazip.h" +#include "quazipfileinfo.h" +#include <QDir> +#include <QList> +#include <QSharedDataPointer> + +/// Provides ZIP archive navigation. +/** +* This class is modelled after QDir, and is designed to provide similar +* features for ZIP archives. +* +* The only significant difference from QDir is that the root path is not +* '/', but an empty string since that's how the file paths are stored in +* the archive. However, QuaZipDir understands the paths starting with +* '/'. It is important in a few places: +* +* - In the cd() function. +* - In the constructor. +* - In the exists() function. +* +* Note that since ZIP uses '/' on all platforms, the '\' separator is +* not supported. +*/ +class QUAZIP_EXPORT QuaZipDir { +private: + QSharedDataPointer<QuaZipDirPrivate> d; +public: + /// The copy constructor. + QuaZipDir(const QuaZipDir &that); + /// Constructs a QuaZipDir instance pointing to the specified directory. + /** + If \a dir is not specified, points to the root of the archive. + The same happens if the \a dir is "/". + */ + QuaZipDir(QuaZip *zip, const QString &dir = QString()); + /// Destructor. + ~QuaZipDir(); + /// The assignment operator. + bool operator==(const QuaZipDir &that); + /// operator!= + /** + \return \c true if either this and \a that use different QuaZip + instances or if they point to different directories. + */ + inline bool operator!=(const QuaZipDir &that) {return !operator==(that);} + /// operator== + /** + \return \c true if both this and \a that use the same QuaZip + instance and point to the same directory. + */ + QuaZipDir& operator=(const QuaZipDir &that); + /// Returns the name of the entry at the specified position. + QString operator[](int pos) const; + /// Returns the current case sensitivity mode. + QuaZip::CaseSensitivity caseSensitivity() const; + /// Changes the 'current' directory. + /** + * If the path starts with '/', it is interpreted as an absolute + * path from the root of the archive. Otherwise, it is interpreted + * as a path relative to the current directory as was set by the + * previous cd() or the constructor. + * + * Note that the subsequent path() call will not return a path + * starting with '/' in all cases. + */ + bool cd(const QString &dirName); + /// Goes up. + bool cdUp(); + /// Returns the number of entries in the directory. + uint count() const; + /// Returns the current directory name. + /** + The name doesn't include the path. + */ + QString dirName() const; + /// Returns the list of the entries in the directory. + /** + \param nameFilters The list of file patterns to list, uses the same + syntax as QDir. + \param filters The entry type filters, only Files and Dirs are + accepted. + \param sort Sorting mode (not supported yet). + */ + QList<QuaZipFileInfo> entryInfoList(const QStringList &nameFilters, + QDir::Filters filters = QDir::NoFilter, + QDir::SortFlags sort = QDir::NoSort) const; + /// Returns the list of the entries in the directory. + /** + \overload + + The same as entryInfoList(QStringList(), filters, sort). + */ + QList<QuaZipFileInfo> entryInfoList(QDir::Filters filters = QDir::NoFilter, + QDir::SortFlags sort = QDir::NoSort) const; + /// Returns the list of the entry names in the directory. + /** + The same as entryInfoList(nameFilters, filters, sort), but only + returns entry names. + */ + QStringList entryList(const QStringList &nameFilters, + QDir::Filters filters = QDir::NoFilter, + QDir::SortFlags sort = QDir::NoSort) const; + /// Returns the list of the entry names in the directory. + /** + \overload + + The same as entryList(QStringList(), filters, sort). + */ + QStringList entryList(QDir::Filters filters = QDir::NoFilter, + QDir::SortFlags sort = QDir::NoSort) const; + /// Returns \c true if the entry with the specified name exists. + /** + The ".." is considered to exist if the current directory + is not root. The "." and "/" are considered to + always exist. Paths starting with "/" are relative to + the archive root, other paths are relative to the current dir. + */ + bool exists(const QString &fileName) const; + /// Return \c true if the directory pointed by this QuaZipDir exists. + bool exists() const; + /// Returns the full path to the specified file. + /** + Doesn't check if the file actually exists. + */ + QString filePath(const QString &fileName) const; + /// Returns the default filter. + QDir::Filters filter(); + /// Returns if the QuaZipDir points to the root of the archive. + /** + Not that the root path is the empty string, not '/'. + */ + bool isRoot() const; + /// Return the default name filter. + QStringList nameFilters() const; + /// Returns the path to the current dir. + /** + The path never starts with '/', and the root path is an empty + string. + */ + QString path() const; + /// Returns the path to the specified file relative to the current dir. + QString relativeFilePath(const QString &fileName) const; + /// Sets the default case sensitivity mode. + void setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity); + /// Sets the default filter. + void setFilter(QDir::Filters filters); + /// Sets the default name filter. + void setNameFilters(const QStringList &nameFilters); + /// Goes to the specified path. + /** + The difference from cd() is that this function never checks if the + path actually exists and doesn't use relative paths, so it's + possible to go to the root directory with setPath(""). + + Note that this function still chops the trailing and/or leading + '/' and treats a single '/' as the root path (path() will still + return an empty string). + */ + void setPath(const QString &path); + /// Sets the default sorting mode. + void setSorting(QDir::SortFlags sort); + /// Returns the default sorting mode. + QDir::SortFlags sorting() const; +}; + +#endif // QUAZIP_QUAZIPDIR_H diff --git a/depends/quazip/quazipfile.cpp b/depends/quazip/quazipfile.cpp new file mode 100644 index 00000000..323f815e --- /dev/null +++ b/depends/quazip/quazipfile.cpp @@ -0,0 +1,488 @@ +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include "quazipfile.h" + +using namespace std; + +/// The implementation class for QuaZip. +/** +\internal + +This class contains all the private stuff for the QuaZipFile class, thus +allowing to preserve binary compatibility between releases, the +technique known as the Pimpl (private implementation) idiom. +*/ +class QuaZipFilePrivate { + friend class QuaZipFile; + private: + /// The pointer to the associated QuaZipFile instance. + QuaZipFile *q; + /// The QuaZip object to work with. + QuaZip *zip; + /// The file name. + QString fileName; + /// Case sensitivity mode. + QuaZip::CaseSensitivity caseSensitivity; + /// Whether this file is opened in the raw mode. + bool raw; + /// Write position to keep track of. + /** + QIODevice::pos() is broken for non-seekable devices, so we need + our own position. + */ + qint64 writePos; + /// Uncompressed size to write along with a raw file. + ulong uncompressedSize; + /// CRC to write along with a raw file. + quint32 crc; + /// Whether \ref zip points to an internal QuaZip instance. + /** + This is true if the archive was opened by name, rather than by + supplying an existing QuaZip instance. + */ + bool internal; + /// The last error. + int zipError; + /// Resets \ref zipError. + inline void resetZipError() const {setZipError(UNZ_OK);} + /// Sets the zip error. + /** + This function is marked as const although it changes one field. + This allows to call it from const functions that don't change + anything by themselves. + */ + void setZipError(int zipError) const; + /// The constructor for the corresponding QuaZipFile constructor. + inline QuaZipFilePrivate(QuaZipFile *q): + q(q), zip(NULL), internal(true), zipError(UNZ_OK) {} + /// The constructor for the corresponding QuaZipFile constructor. + inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName): + q(q), internal(true), zipError(UNZ_OK) + { + zip=new QuaZip(zipName); + } + /// The constructor for the corresponding QuaZipFile constructor. + inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName, + QuaZip::CaseSensitivity cs): + q(q), internal(true), zipError(UNZ_OK) + { + zip=new QuaZip(zipName); + this->fileName=fileName; + if (this->fileName.startsWith('/')) + this->fileName = this->fileName.mid(1); + this->caseSensitivity=cs; + } + /// The constructor for the QuaZipFile constructor accepting a file name. + inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip): + q(q), zip(zip), internal(false), zipError(UNZ_OK) {} + /// The destructor. + inline ~QuaZipFilePrivate() + { + if (internal) + delete zip; + } +}; + +QuaZipFile::QuaZipFile(): + p(new QuaZipFilePrivate(this)) +{ +} + +QuaZipFile::QuaZipFile(QObject *parent): + QIODevice(parent), + p(new QuaZipFilePrivate(this)) +{ +} + +QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): + QIODevice(parent), + p(new QuaZipFilePrivate(this, zipName)) +{ +} + +QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, + QuaZip::CaseSensitivity cs, QObject *parent): + QIODevice(parent), + p(new QuaZipFilePrivate(this, zipName, fileName, cs)) +{ +} + +QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): + QIODevice(parent), + p(new QuaZipFilePrivate(this, zip)) +{ +} + +QuaZipFile::~QuaZipFile() +{ + if (isOpen()) + close(); + delete p; +} + +QString QuaZipFile::getZipName() const +{ + return p->zip==NULL ? QString() : p->zip->getZipName(); +} + +QuaZip *QuaZipFile::getZip() const +{ + return p->internal ? NULL : p->zip; +} + +QString QuaZipFile::getActualFileName()const +{ + p->setZipError(UNZ_OK); + if (p->zip == NULL || (openMode() & WriteOnly)) + return QString(); + QString name=p->zip->getCurrentFileName(); + if(name.isNull()) + p->setZipError(p->zip->getZipError()); + return name; +} + +void QuaZipFile::setZipName(const QString& zipName) +{ + if(isOpen()) { + qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); + return; + } + if(p->zip!=NULL && p->internal) + delete p->zip; + p->zip=new QuaZip(zipName); + p->internal=true; +} + +void QuaZipFile::setZip(QuaZip *zip) +{ + if(isOpen()) { + qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); + return; + } + if(p->zip!=NULL && p->internal) + delete p->zip; + p->zip=zip; + p->fileName=QString(); + p->internal=false; +} + +void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) +{ + if(p->zip==NULL) { + qWarning("QuaZipFile::setFileName(): call setZipName() first"); + return; + } + if(!p->internal) { + qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); + return; + } + if(isOpen()) { + qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); + return; + } + p->fileName=fileName; + if (p->fileName.startsWith('/')) + p->fileName = p->fileName.mid(1); + p->caseSensitivity=cs; +} + +void QuaZipFilePrivate::setZipError(int zipError) const +{ + QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const + fakeThis->zipError=zipError; + if(zipError==UNZ_OK) + q->setErrorString(QString()); + else + q->setErrorString(q->tr("ZIP/UNZIP API error %1").arg(zipError)); +} + +bool QuaZipFile::open(OpenMode mode) +{ + return open(mode, NULL); +} + +bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) +{ + p->resetZipError(); + if(isOpen()) { + qWarning("QuaZipFile::open(): already opened"); + return false; + } + if(mode&Unbuffered) { + qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); + return false; + } + if((mode&ReadOnly)&&!(mode&WriteOnly)) { + if(p->internal) { + if(!p->zip->open(QuaZip::mdUnzip)) { + p->setZipError(p->zip->getZipError()); + return false; + } + if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) { + p->setZipError(p->zip->getZipError()); + p->zip->close(); + return false; + } + } else { + if(p->zip==NULL) { + qWarning("QuaZipFile::open(): zip is NULL"); + return false; + } + if(p->zip->getMode()!=QuaZip::mdUnzip) { + qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", + (int)mode, (int)p->zip->getMode()); + return false; + } + if(!p->zip->hasCurrentFile()) { + qWarning("QuaZipFile::open(): zip does not have current file"); + return false; + } + } + p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password)); + if(p->zipError==UNZ_OK) { + setOpenMode(mode); + p->raw=raw; + return true; + } else + return false; + } + qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); + return false; +} + +bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, + const char *password, quint32 crc, + int method, int level, bool raw, + int windowBits, int memLevel, int strategy) +{ + zip_fileinfo info_z; + p->resetZipError(); + if(isOpen()) { + qWarning("QuaZipFile::open(): already opened"); + return false; + } + if((mode&WriteOnly)&&!(mode&ReadOnly)) { + if(p->internal) { + qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); + return false; + } + if(p->zip==NULL) { + qWarning("QuaZipFile::open(): zip is NULL"); + return false; + } + if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) { + qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", + (int)mode, (int)p->zip->getMode()); + return false; + } + info_z.tmz_date.tm_year=info.dateTime.date().year(); + info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; + info_z.tmz_date.tm_mday=info.dateTime.date().day(); + info_z.tmz_date.tm_hour=info.dateTime.time().hour(); + info_z.tmz_date.tm_min=info.dateTime.time().minute(); + info_z.tmz_date.tm_sec=info.dateTime.time().second(); + info_z.dosDate = 0; + info_z.internal_fa=(uLong)info.internalAttr; + info_z.external_fa=(uLong)info.externalAttr; + if (!p->zip->isDataDescriptorWritingEnabled()) + zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); + p->setZipError(zipOpenNewFileInZip3(p->zip->getZipFile(), + p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, + info.extraLocal.constData(), info.extraLocal.length(), + info.extraGlobal.constData(), info.extraGlobal.length(), + p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), + method, level, (int)raw, + windowBits, memLevel, strategy, + password, (uLong)crc)); + if(p->zipError==UNZ_OK) { + p->writePos=0; + setOpenMode(mode); + p->raw=raw; + if(raw) { + p->crc=crc; + p->uncompressedSize=info.uncompressedSize; + } + return true; + } else + return false; + } + qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); + return false; +} + +bool QuaZipFile::isSequential()const +{ + return true; +} + +qint64 QuaZipFile::pos()const +{ + if(p->zip==NULL) { + qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); + return -1; + } + if(!isOpen()) { + qWarning("QuaZipFile::pos(): file is not open"); + return -1; + } + if(openMode()&ReadOnly) + // QIODevice::pos() is broken for sequential devices, + // but thankfully bytesAvailable() returns the number of + // bytes buffered, so we know how far ahead we are. + return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable(); + else + return p->writePos; +} + +bool QuaZipFile::atEnd()const +{ + if(p->zip==NULL) { + qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); + return false; + } + if(!isOpen()) { + qWarning("QuaZipFile::atEnd(): file is not open"); + return false; + } + if(openMode()&ReadOnly) + // the same problem as with pos() + return QIODevice::bytesAvailable() == 0 + && unzeof(p->zip->getUnzFile())==1; + else + return true; +} + +qint64 QuaZipFile::size()const +{ + if(!isOpen()) { + qWarning("QuaZipFile::atEnd(): file is not open"); + return -1; + } + if(openMode()&ReadOnly) + return p->raw?csize():usize(); + else + return p->writePos; +} + +qint64 QuaZipFile::csize()const +{ + unz_file_info info_z; + p->setZipError(UNZ_OK); + if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; + p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); + if(p->zipError!=UNZ_OK) + return -1; + return info_z.compressed_size; +} + +qint64 QuaZipFile::usize()const +{ + unz_file_info info_z; + p->setZipError(UNZ_OK); + if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; + p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); + if(p->zipError!=UNZ_OK) + return -1; + return info_z.uncompressed_size; +} + +bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) +{ + if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false; + p->zip->getCurrentFileInfo(info); + p->setZipError(p->zip->getZipError()); + return p->zipError==UNZ_OK; +} + +void QuaZipFile::close() +{ + p->resetZipError(); + if(p->zip==NULL||!p->zip->isOpen()) return; + if(!isOpen()) { + qWarning("QuaZipFile::close(): file isn't open"); + return; + } + if(openMode()&ReadOnly) + p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile())); + else if(openMode()&WriteOnly) + if(isRaw()) p->setZipError(zipCloseFileInZipRaw(p->zip->getZipFile(), p->uncompressedSize, p->crc)); + else p->setZipError(zipCloseFileInZip(p->zip->getZipFile())); + else { + qWarning("Wrong open mode: %d", (int)openMode()); + return; + } + if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); + else return; + if(p->internal) { + p->zip->close(); + p->setZipError(p->zip->getZipError()); + } +} + +qint64 QuaZipFile::readData(char *data, qint64 maxSize) +{ + p->setZipError(UNZ_OK); + qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize); + if (bytesRead < 0) { + p->setZipError((int) bytesRead); + return -1; + } + return bytesRead; +} + +qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) +{ + p->setZipError(ZIP_OK); + p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize)); + if(p->zipError!=ZIP_OK) return -1; + else { + p->writePos+=maxSize; + return maxSize; + } +} + +QString QuaZipFile::getFileName() const +{ + return p->fileName; +} + +QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const +{ + return p->caseSensitivity; +} + +bool QuaZipFile::isRaw() const +{ + return p->raw; +} + +int QuaZipFile::getZipError() const +{ + return p->zipError; +} + +qint64 QuaZipFile::bytesAvailable() const +{ + return size() - pos(); +} diff --git a/depends/quazip/quazipfile.h b/depends/quazip/quazipfile.h new file mode 100644 index 00000000..f6cc41a6 --- /dev/null +++ b/depends/quazip/quazipfile.h @@ -0,0 +1,442 @@ +#ifndef QUA_ZIPFILE_H +#define QUA_ZIPFILE_H + +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include <QIODevice> + +#include "quazip_global.h" +#include "quazip.h" +#include "quazipnewinfo.h" + +class QuaZipFilePrivate; + +/// A file inside ZIP archive. +/** \class QuaZipFile quazipfile.h <quazip/quazipfile.h> + * This is the most interesting class. Not only it provides C++ + * interface to the ZIP/UNZIP package, but also integrates it with Qt by + * subclassing QIODevice. This makes possible to access files inside ZIP + * archive using QTextStream or QDataStream, for example. Actually, this + * is the main purpose of the whole QuaZIP library. + * + * You can either use existing QuaZip instance to create instance of + * this class or pass ZIP archive file name to this class, in which case + * it will create internal QuaZip object. See constructors' descriptions + * for details. Writing is only possible with the existing instance. + * + * Note that due to the underlying library's limitation it is not + * possible to use multiple QuaZipFile instances to open several files + * in the same archive at the same time. If you need to write to + * multiple files in parallel, then you should write to temporary files + * first, then pack them all at once when you have finished writing. If + * you need to read multiple files inside the same archive in parallel, + * you should extract them all into a temporary directory first. + * + * \section quazipfile-sequential Sequential or random-access? + * + * At the first thought, QuaZipFile has fixed size, the start and the + * end and should be therefore considered random-access device. But + * there is one major obstacle to making it random-access: ZIP/UNZIP API + * does not support seek() operation and the only way to implement it is + * through reopening the file and re-reading to the required position, + * but this is prohibitively slow. + * + * Therefore, QuaZipFile is considered to be a sequential device. This + * has advantage of availability of the ungetChar() operation (QIODevice + * does not implement it properly for non-sequential devices unless they + * support seek()). Disadvantage is a somewhat strange behaviour of the + * size() and pos() functions. This should be kept in mind while using + * this class. + * + **/ +class QUAZIP_EXPORT QuaZipFile: public QIODevice { + friend class QuaZipFilePrivate; + Q_OBJECT + private: + QuaZipFilePrivate *p; + // these are not supported nor implemented + QuaZipFile(const QuaZipFile& that); + QuaZipFile& operator=(const QuaZipFile& that); + protected: + /// Implementation of the QIODevice::readData(). + qint64 readData(char *data, qint64 maxSize); + /// Implementation of the QIODevice::writeData(). + qint64 writeData(const char *data, qint64 maxSize); + public: + /// Constructs a QuaZipFile instance. + /** You should use setZipName() and setFileName() or setZip() before + * trying to call open() on the constructed object. + **/ + QuaZipFile(); + /// Constructs a QuaZipFile instance. + /** \a parent argument specifies this object's parent object. + * + * You should use setZipName() and setFileName() or setZip() before + * trying to call open() on the constructed object. + **/ + QuaZipFile(QObject *parent); + /// Constructs a QuaZipFile instance. + /** \a parent argument specifies this object's parent object and \a + * zipName specifies ZIP archive file name. + * + * You should use setFileName() before trying to call open() on the + * constructed object. + * + * QuaZipFile constructed by this constructor can be used for read + * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. + **/ + QuaZipFile(const QString& zipName, QObject *parent =NULL); + /// Constructs a QuaZipFile instance. + /** \a parent argument specifies this object's parent object, \a + * zipName specifies ZIP archive file name and \a fileName and \a cs + * specify a name of the file to open inside archive. + * + * QuaZipFile constructed by this constructor can be used for read + * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. + * + * \sa QuaZip::setCurrentFile() + **/ + QuaZipFile(const QString& zipName, const QString& fileName, + QuaZip::CaseSensitivity cs =QuaZip::csDefault, QObject *parent =NULL); + /// Constructs a QuaZipFile instance. + /** \a parent argument specifies this object's parent object. + * + * \a zip is the pointer to the existing QuaZip object. This + * QuaZipFile object then can be used to read current file in the + * \a zip or to write to the file inside it. + * + * \warning Using this constructor for reading current file can be + * tricky. Let's take the following example: + * \code + * QuaZip zip("archive.zip"); + * zip.open(QuaZip::mdUnzip); + * zip.setCurrentFile("file-in-archive"); + * QuaZipFile file(&zip); + * file.open(QIODevice::ReadOnly); + * // ok, now we can read from the file + * file.read(somewhere, some); + * zip.setCurrentFile("another-file-in-archive"); // oops... + * QuaZipFile anotherFile(&zip); + * anotherFile.open(QIODevice::ReadOnly); + * anotherFile.read(somewhere, some); // this is still ok... + * file.read(somewhere, some); // and this is NOT + * \endcode + * So, what exactly happens here? When we change current file in the + * \c zip archive, \c file that references it becomes invalid + * (actually, as far as I understand ZIP/UNZIP sources, it becomes + * closed, but QuaZipFile has no means to detect it). + * + * Summary: do not close \c zip object or change its current file as + * long as QuaZipFile is open. Even better - use another constructors + * which create internal QuaZip instances, one per object, and + * therefore do not cause unnecessary trouble. This constructor may + * be useful, though, if you already have a QuaZip instance and do + * not want to access several files at once. Good example: + * \code + * QuaZip zip("archive.zip"); + * zip.open(QuaZip::mdUnzip); + * // first, we need some information about archive itself + * QByteArray comment=zip.getComment(); + * // and now we are going to access files inside it + * QuaZipFile file(&zip); + * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { + * file.open(QIODevice::ReadOnly); + * // do something cool with file here + * file.close(); // do not forget to close! + * } + * zip.close(); + * \endcode + **/ + QuaZipFile(QuaZip *zip, QObject *parent =NULL); + /// Destroys a QuaZipFile instance. + /** Closes file if open, destructs internal QuaZip object (if it + * exists and \em is internal, of course). + **/ + virtual ~QuaZipFile(); + /// Returns the ZIP archive file name. + /** If this object was created by passing QuaZip pointer to the + * constructor, this function will return that QuaZip's file name + * (or null string if that object does not have file name yet). + * + * Otherwise, returns associated ZIP archive file name or null + * string if there are no name set yet. + * + * \sa setZipName() getFileName() + **/ + QString getZipName()const; + /// Returns a pointer to the associated QuaZip object. + /** Returns \c NULL if there is no associated QuaZip or it is + * internal (so you will not mess with it). + **/ + QuaZip* getZip()const; + /// Returns file name. + /** This function returns file name you passed to this object either + * by using + * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) + * or by calling setFileName(). Real name of the file may differ in + * case if you used case-insensitivity. + * + * Returns null string if there is no file name set yet. This is the + * case when this QuaZipFile operates on the existing QuaZip object + * (constructor QuaZipFile(QuaZip*,QObject*) or setZip() was used). + * + * \sa getActualFileName + **/ + QString getFileName() const; + /// Returns case sensitivity of the file name. + /** This function returns case sensitivity argument you passed to + * this object either by using + * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) + * or by calling setFileName(). + * + * Returns unpredictable value if getFileName() returns null string + * (this is the case when you did not used setFileName() or + * constructor above). + * + * \sa getFileName + **/ + QuaZip::CaseSensitivity getCaseSensitivity() const; + /// Returns the actual file name in the archive. + /** This is \em not a ZIP archive file name, but a name of file inside + * archive. It is not necessary the same name that you have passed + * to the + * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*), + * setFileName() or QuaZip::setCurrentFile() - this is the real file + * name inside archive, so it may differ in case if the file name + * search was case-insensitive. + * + * Equivalent to calling getCurrentFileName() on the associated + * QuaZip object. Returns null string if there is no associated + * QuaZip object or if it does not have a current file yet. And this + * is the case if you called setFileName() but did not open the + * file yet. So this is perfectly fine: + * \code + * QuaZipFile file("somezip.zip"); + * file.setFileName("somefile"); + * QString name=file.getName(); // name=="somefile" + * QString actual=file.getActualFileName(); // actual is null string + * file.open(QIODevice::ReadOnly); + * QString actual=file.getActualFileName(); // actual can be "SoMeFiLe" on Windows + * \endcode + * + * \sa getZipName(), getFileName(), QuaZip::CaseSensitivity + **/ + QString getActualFileName()const; + /// Sets the ZIP archive file name. + /** Automatically creates internal QuaZip object and destroys + * previously created internal QuaZip object, if any. + * + * Will do nothing if this file is already open. You must close() it + * first. + **/ + void setZipName(const QString& zipName); + /// Returns \c true if the file was opened in raw mode. + /** If the file is not open, the returned value is undefined. + * + * \sa open(OpenMode,int*,int*,bool,const char*) + **/ + bool isRaw() const; + /// Binds to the existing QuaZip instance. + /** This function destroys internal QuaZip object, if any, and makes + * this QuaZipFile to use current file in the \a zip object for any + * further operations. See QuaZipFile(QuaZip*,QObject*) for the + * possible pitfalls. + * + * Will do nothing if the file is currently open. You must close() + * it first. + **/ + void setZip(QuaZip *zip); + /// Sets the file name. + /** Will do nothing if at least one of the following conditions is + * met: + * - ZIP name has not been set yet (getZipName() returns null + * string). + * - This QuaZipFile is associated with external QuaZip. In this + * case you should call that QuaZip's setCurrentFile() function + * instead! + * - File is already open so setting the name is meaningless. + * + * \sa QuaZip::setCurrentFile + **/ + void setFileName(const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault); + /// Opens a file for reading. + /** Returns \c true on success, \c false otherwise. + * Call getZipError() to get error code. + * + * \note Since ZIP/UNZIP API provides buffered reading only, + * QuaZipFile does not support unbuffered reading. So do not pass + * QIODevice::Unbuffered flag in \a mode, or open will fail. + **/ + virtual bool open(OpenMode mode); + /// Opens a file for reading. + /** \overload + * Argument \a password specifies a password to decrypt the file. If + * it is NULL then this function behaves just like open(OpenMode). + **/ + inline bool open(OpenMode mode, const char *password) + {return open(mode, NULL, NULL, false, password);} + /// Opens a file for reading. + /** \overload + * Argument \a password specifies a password to decrypt the file. + * + * An integers pointed by \a method and \a level will receive codes + * of the compression method and level used. See unzip.h. + * + * If raw is \c true then no decompression is performed. + * + * \a method should not be \c NULL. \a level can be \c NULL if you + * don't want to know the compression level. + **/ + bool open(OpenMode mode, int *method, int *level, bool raw, const char *password =NULL); + /// Opens a file for writing. + /** \a info argument specifies information about file. It should at + * least specify a correct file name. Also, it is a good idea to + * specify correct timestamp (by default, current time will be + * used). See QuaZipNewInfo. + * + * The \a password argument specifies the password for crypting. Pass NULL + * if you don't need any crypting. The \a crc argument was supposed + * to be used for crypting too, but then it turned out that it's + * false information, so you need to set it to 0 unless you want to + * use the raw mode (see below). + * + * Arguments \a method and \a level specify compression method and + * level. The only method supported is Z_DEFLATED, but you may also + * specify 0 for no compression. If all of the files in the archive + * use both method 0 and either level 0 is explicitly specified or + * data descriptor writing is disabled with + * QuaZip::setDataDescriptorWritingEnabled(), then the + * resulting archive is supposed to be compatible with the 1.0 ZIP + * format version, should you need that. Except for this, \a level + * has no other effects with method 0. + * + * If \a raw is \c true, no compression is performed. In this case, + * \a crc and uncompressedSize field of the \a info are required. + * + * Arguments \a windowBits, \a memLevel, \a strategy provide zlib + * algorithms tuning. See deflateInit2() in zlib. + **/ + bool open(OpenMode mode, const QuaZipNewInfo& info, + const char *password =NULL, quint32 crc =0, + int method =Z_DEFLATED, int level =Z_DEFAULT_COMPRESSION, bool raw =false, + int windowBits =-MAX_WBITS, int memLevel =DEF_MEM_LEVEL, int strategy =Z_DEFAULT_STRATEGY); + /// Returns \c true, but \ref quazipfile-sequential "beware"! + virtual bool isSequential()const; + /// Returns current position in the file. + /** Implementation of the QIODevice::pos(). When reading, this + * function is a wrapper to the ZIP/UNZIP unztell(), therefore it is + * unable to keep track of the ungetChar() calls (which is + * non-virtual and therefore is dangerous to reimplement). So if you + * are using ungetChar() feature of the QIODevice, this function + * reports incorrect value until you get back characters which you + * ungot. + * + * When writing, pos() returns number of bytes already written + * (uncompressed unless you use raw mode). + * + * \note Although + * \ref quazipfile-sequential "QuaZipFile is a sequential device" + * and therefore pos() should always return zero, it does not, + * because it would be misguiding. Keep this in mind. + * + * This function returns -1 if the file or archive is not open. + * + * Error code returned by getZipError() is not affected by this + * function call. + **/ + virtual qint64 pos()const; + /// Returns \c true if the end of file was reached. + /** This function returns \c false in the case of error. This means + * that you called this function on either not open file, or a file + * in the not open archive or even on a QuaZipFile instance that + * does not even have QuaZip instance associated. Do not do that + * because there is no means to determine whether \c false is + * returned because of error or because end of file was reached. + * Well, on the other side you may interpret \c false return value + * as "there is no file open to check for end of file and there is + * no end of file therefore". + * + * When writing, this function always returns \c true (because you + * are always writing to the end of file). + * + * Error code returned by getZipError() is not affected by this + * function call. + **/ + virtual bool atEnd()const; + /// Returns file size. + /** This function returns csize() if the file is open for reading in + * raw mode, usize() if it is open for reading in normal mode and + * pos() if it is open for writing. + * + * Returns -1 on error, call getZipError() to get error code. + * + * \note This function returns file size despite that + * \ref quazipfile-sequential "QuaZipFile is considered to be sequential device", + * for which size() should return bytesAvailable() instead. But its + * name would be very misguiding otherwise, so just keep in mind + * this inconsistence. + **/ + virtual qint64 size()const; + /// Returns compressed file size. + /** Equivalent to calling getFileInfo() and then getting + * compressedSize field, but more convenient and faster. + * + * File must be open for reading before calling this function. + * + * Returns -1 on error, call getZipError() to get error code. + **/ + qint64 csize()const; + /// Returns uncompressed file size. + /** Equivalent to calling getFileInfo() and then getting + * uncompressedSize field, but more convenient and faster. See + * getFileInfo() for a warning. + * + * File must be open for reading before calling this function. + * + * Returns -1 on error, call getZipError() to get error code. + **/ + qint64 usize()const; + /// Gets information about current file. + /** This function does the same thing as calling + * QuaZip::getCurrentFileInfo() on the associated QuaZip object, + * but you can not call getCurrentFileInfo() if the associated + * QuaZip is internal (because you do not have access to it), while + * you still can call this function in that case. + * + * File must be open for reading before calling this function. + * + * Returns \c false in the case of an error. + **/ + bool getFileInfo(QuaZipFileInfo *info); + /// Closes the file. + /** Call getZipError() to determine if the close was successful. + **/ + virtual void close(); + /// Returns the error code returned by the last ZIP/UNZIP API call. + int getZipError() const; + /// Returns the number of bytes available for reading. + virtual qint64 bytesAvailable() const; +}; + +#endif diff --git a/depends/quazip/quazipfileinfo.h b/depends/quazip/quazipfileinfo.h new file mode 100644 index 00000000..99540229 --- /dev/null +++ b/depends/quazip/quazipfileinfo.h @@ -0,0 +1,66 @@ +#ifndef QUA_ZIPFILEINFO_H +#define QUA_ZIPFILEINFO_H + +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include <QByteArray> +#include <QDateTime> + +#include "quazip_global.h" + +/// Information about a file inside archive. +/** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to + * fill this structure. */ +struct QUAZIP_EXPORT QuaZipFileInfo { + /// File name. + QString name; + /// Version created by. + quint16 versionCreated; + /// Version needed to extract. + quint16 versionNeeded; + /// General purpose flags. + quint16 flags; + /// Compression method. + quint16 method; + /// Last modification date and time. + QDateTime dateTime; + /// CRC. + quint32 crc; + /// Compressed file size. + quint32 compressedSize; + /// Uncompressed file size. + quint32 uncompressedSize; + /// Disk number start. + quint16 diskNumberStart; + /// Internal file attributes. + quint16 internalAttr; + /// External file attributes. + quint32 externalAttr; + /// Comment. + QString comment; + /// Extra field. + QByteArray extra; +}; + +#endif diff --git a/depends/quazip/quazipnewinfo.cpp b/depends/quazip/quazipnewinfo.cpp new file mode 100644 index 00000000..ed57e09f --- /dev/null +++ b/depends/quazip/quazipnewinfo.cpp @@ -0,0 +1,51 @@ +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. +*/ + +#include <QFileInfo> + +#include "quazipnewinfo.h" + + +QuaZipNewInfo::QuaZipNewInfo(const QString& name): + name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0) +{ +} + +QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file): + name(name), internalAttr(0), externalAttr(0) +{ + QFileInfo info(file); + QDateTime lm = info.lastModified(); + if (!info.exists()) + dateTime = QDateTime::currentDateTime(); + else + dateTime = lm; +} + +void QuaZipNewInfo::setFileDateTime(const QString& file) +{ + QFileInfo info(file); + QDateTime lm = info.lastModified(); + if (info.exists()) + dateTime = lm; +} diff --git a/depends/quazip/quazipnewinfo.h b/depends/quazip/quazipnewinfo.h new file mode 100644 index 00000000..62159ea7 --- /dev/null +++ b/depends/quazip/quazipnewinfo.h @@ -0,0 +1,102 @@ +#ifndef QUA_ZIPNEWINFO_H +#define QUA_ZIPNEWINFO_H + +/* +Copyright (C) 2005-2011 Sergey A. Tachenov + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See COPYING file for the full LGPL text. + +Original ZIP package is copyrighted by Gilles Vollant, see +quazip/(un)zip.h files for details, basically it's zlib license. + **/ + +#include <QDateTime> +#include <QString> + +#include "quazip_global.h" + +/// Information about a file to be created. +/** This structure holds information about a file to be created inside + * ZIP archive. At least name should be set to something correct before + * passing this structure to + * QuaZipFile::open(OpenMode,const QuaZipNewInfo&,int,int,bool). + **/ +struct QUAZIP_EXPORT QuaZipNewInfo { + /// File name. + /** This field holds file name inside archive, including path relative + * to archive root. + **/ + QString name; + /// File timestamp. + /** This is the last file modification date and time. Will be stored + * in the archive central directory. It is a good practice to set it + * to the source file timestamp instead of archive creating time. Use + * setFileDateTime() or QuaZipNewInfo(const QString&, const QString&). + **/ + QDateTime dateTime; + /// File internal attributes. + quint16 internalAttr; + /// File external attributes. + quint32 externalAttr; + /// File comment. + /** Will be encoded using QuaZip::getCommentCodec(). + **/ + QString comment; + /// File local extra field. + QByteArray extraLocal; + /// File global extra field. + QByteArray extraGlobal; + /// Uncompressed file size. + /** This is only needed if you are using raw file zipping mode, i. e. + * adding precompressed file in the zip archive. + **/ + ulong uncompressedSize; + /// Constructs QuaZipNewInfo instance. + /** Initializes name with \a name, dateTime with current date and + * time. Attributes are initialized with zeros, comment and extra + * field with null values. + **/ + QuaZipNewInfo(const QString& name); + /// Constructs QuaZipNewInfo instance. + /** Initializes name with \a name and dateTime with timestamp of the + * file named \a file. If the \a file does not exists or its timestamp + * is inaccessible (e. g. you do not have read permission for the + * directory file in), uses current date and time. Attributes are + * initialized with zeros, comment and extra field with null values. + * + * \sa setFileDateTime() + **/ + QuaZipNewInfo(const QString& name, const QString& file); + /// Sets the file timestamp from the existing file. + /** Use this function to set the file timestamp from the existing + * file. Use it like this: + * \code + * QuaZipFile zipFile(&zip); + * QFile file("file-to-add"); + * file.open(QIODevice::ReadOnly); + * QuaZipNewInfo info("file-name-in-archive"); + * info.setFileDateTime("file-to-add"); // take the timestamp from file + * zipFile.open(QIODevice::WriteOnly, info); + * \endcode + * + * This function does not change dateTime if some error occured (e. g. + * file is inaccessible). + **/ + void setFileDateTime(const QString& file); +}; + +#endif diff --git a/depends/quazip/unzip.c b/depends/quazip/unzip.c new file mode 100644 index 00000000..6e115ae6 --- /dev/null +++ b/depends/quazip/unzip.c @@ -0,0 +1,1603 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info + + Modified by Sergey A. Tachenov to integrate with Qt. +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1<c2) + return -1; + if (c1>c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (file, pzlib_filefunc_def) + voidpf file; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_qiodevice_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + file, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pos<us.offset_central_dir+us.size_central_dir) && + (err==UNZ_OK)) + err=UNZ_BADZIPFILE; + + if (err!=UNZ_OK) + { + ZCLOSE(us.z_filefunc, us.filestream); + return NULL; + } + + us.byte_before_the_zipfile = central_pos - + (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + us.encrypted = 0; + + + s=(unz_s*)ALLOC(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + +extern unzFile ZEXPORT unzOpen (file) + voidpf file; +{ + return unzOpen2(file, NULL); +} + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose (file) + unzFile file; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + uLong uSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + uSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename<fileNameBufferSize) + { + *(szFileName+file_info.size_filename)='\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename>0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + uSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extra<extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (uSeek!=0) { + if (ZSEEK(s->z_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + uSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + uSeek += file_info.size_file_extra - uSizeRead; + } + else + uSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment<commentBufferSize) + { + *(szComment+file_info.size_file_comment)='\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (uSeek!=0) { + if (ZSEEK(s->z_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + uSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + uSeek+=file_info.size_file_comment - uSizeRead; + } + else + uSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) + uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;i<uReadThis;i++) + pfile_in_zip_read_info->read_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;i<uDoCopy;i++) + *(pfile_in_zip_read_info->stream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/depends/quazip/unzip.h b/depends/quazip/unzip.h new file mode 100644 index 00000000..33c9dc1a --- /dev/null +++ b/depends/quazip/unzip.h @@ -0,0 +1,356 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Modified by Sergey A. Tachenov to integrate with Qt. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((voidpf file)); +/* + Open a Zip file. path contain whatever zopen_file from the IO API + accepts. For Qt implementation it is a pointer to QIODevice, for + fopen() implementation it's a file name. + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((voidpf file, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/depends/quazip/zip.c b/depends/quazip/zip.c new file mode 100644 index 00000000..bf8c0a10 --- /dev/null +++ b/depends/quazip/zip.c @@ -0,0 +1,1281 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.01e, February 12th, 2005 + + 27 Dec 2004 Rolf Kalbermatter + Modification to zipOpen2 to support globalComment retrieval. + + Copyright (C) 1998-2005 Gilles Vollant + + Read zip.h for more info + + Modified by Sergey A. Tachenov to integrate with Qt. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "zlib.h" +#include "zip.h" +#include "quazip_global.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x031e) /* best for standard pkware crypt */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] = + " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define DESCRIPTORHEADERMAGIC (0x08074b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + uLong pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralheader; /* size of the central header for cur file */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile_info; + +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile_info ci; /* info on the file curretly writing */ + + uLong begin_pos; /* position of the beginning of the zipfile */ + uLong add_position_when_writting_offset; + uLong number_entry; +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + unsigned flags; +} zip_internal; + + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(ldi) + linkedlist_datablock_internal* ldi; +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(ll) + linkedlist_data* ll; +{ + ll->first_block = ll->last_block = NULL; +} + +#if 0 // unused +local void free_linkedlist(ll) + linkedlist_data* ll; +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} +#endif + +local int add_data_in_datablock(ll,buf,len) + linkedlist_data* ll; + const void* buf; + uLong len; +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;i<copy_this;i++) + *(to_copy+i)=*(from_copy+i); + + ldi->filled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 or 4 (byte, short or long) +*/ + +local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte)); +local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong x; + int nbByte; +{ + unsigned char buf[4]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); +local void ziplocal_putValue_inmemory (dest, x, nbByte) + void* dest; + uLong x; + int nbByte; +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) + const tm_zip* ptm; + uLong dosDate UNUSED; +{ + uLong year = (uLong)ptm->tm_year; + if (year>1980) + year-=1980; + else if (year>80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int ziplocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int ziplocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int ziplocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong ziplocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT zipOpen2 (file, append, globalcomment, pzlib_filefunc_def) + voidpf file; + int append; + zipcharpc* globalcomment; + zlib_filefunc_def* pzlib_filefunc_def; +{ + zip_internal ziinit; + zip_internal* zi; + int err=ZIP_OK; + + + if (pzlib_filefunc_def==NULL) + fill_qiodevice_filefunc(&ziinit.z_filefunc); + else + ziinit.z_filefunc = *pzlib_filefunc_def; + + ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) + (ziinit.z_filefunc.opaque, + file, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + ziinit.flags = ZIP_WRITE_DATA_DESCRIPTOR; + init_linkedlist(&(ziinit.central_dir)); + + + zi = (zip_internal*)ALLOC(sizeof(zip_internal)); + if (zi==NULL) + { + ZCLOSE(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory */ + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry; + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong size_comment; + + central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); + if (central_pos==0) + err=ZIP_ERRNO; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* zipfile global comment length */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((central_pos<offset_central_dir+size_central_dir) && + (err==ZIP_OK)) + err=ZIP_BADZIPFILE; + + if (err!=ZIP_OK) + { + ZCLOSE(ziinit.z_filefunc, ziinit.filestream); + TRYFREE(zi); + return NULL; + } + + if (size_comment>0) + { + ziinit.globalcomment = ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); + ziinit.globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - + (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + { + uLong size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + uLong read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir,buf_read, + (uLong)read_this); + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + ziinit.begin_pos = byte_before_the_zipfile; + ziinit.number_entry = number_entry_CD; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen (file, append) + voidpf file; + int append; +{ + return zipOpen2(file,append,NULL,NULL); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; + int windowBits; + int memLevel; + int strategy; + const char* password; + uLong crcForCrypting; +{ + zip_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + uLong version_to_extract; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (method == 0 + && (level == 0 || (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) == 0)) + { + version_to_extract = 10; + } + else + { + version_to_extract = 20; + } + + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); + } + + zi->ci.flag = 0; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + { + zi->ci.flag |= 1; + } + if (version_to_extract >= 20 + && (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) != 0) + zi->ci.flag |= 8; + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + + size_extrafield_global + size_comment; + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); + + ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); + ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)version_to_extract,2); + ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); + + for (i=0;i<size_filename;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;i<size_extrafield_global;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;i<size_comment;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* write the local header */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)version_to_extract,2); + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); + + if ((err==ZIP_OK) && (size_filename>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + + if ((err==ZIP_OK) && (size_extrafield_local>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) + !=size_extrafield_local) + err = ZIP_ERRNO; + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, + Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = 1; + } +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + crcForCrypting = (uLong)zi->ci.dosDate << 16; // ATTANTION! Without this row, you don't unpack your password protected archive in other app. + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; +{ + return zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; +{ + return zipOpenNewFileInZip2 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0); +} + +local int zipFlushWriteBuffer(zi) + zip_internal* zi; +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;i<zi->ci.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, + zi->ci.buffered_data[i],t); +#endif + } + if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) + !=zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + zi->ci.pos_in_buffered_data = 0; + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (file, buf, len) + zipFile file; + const void* buf; + unsigned len; +{ + zip_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.stream.next_in = (void*)buf; + zi->ci.stream.avail_in = len; + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + for (i=0;i<copy_this;i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) + zipFile file; + uLong uncompressed_size; + uLong crc32; +{ + zip_internal* zi; + uLong compressed_size; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + err=deflateEnd(&zi->ci.stream); + zi->ci.stream_initialised = 0; + } + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = (uLong)zi->ci.stream.total_in; + } + compressed_size = (uLong)zi->ci.stream.total_out; +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20, + compressed_size,4); /*compr size*/ + if (zi->ci.stream.data_type == Z_ASCII) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + ziplocal_putValue_inmemory(zi->ci.central_header+24, + uncompressed_size,4); /*uncompr size*/ + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, + (uLong)zi->ci.size_centralheader); + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + uLong cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (ZSEEK(zi->z_filefunc,zi->filestream, + zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + + if (ZSEEK(zi->z_filefunc,zi->filestream, + cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if ((zi->ci.flag & 8) != 0) { + /* Write local Descriptor after file data */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)DESCRIPTORHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (file) + zipFile file; +{ + return zipCloseFileInZipRaw (file,0,0); +} + +extern int ZEXPORT zipClose (file, global_comment) + zipFile file; + const char* global_comment; +{ + zip_internal* zi; + int err = 0; + uLong size_centraldir = 0; + uLong centraldir_pos_inzip; + uInt size_global_comment; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + if (global_comment==NULL) + size_global_comment = 0; + else + size_global_comment = (uInt)strlen(global_comment); + + centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + ldi->data,ldi->filled_in_this_block) + !=ldi->filled_in_this_block ) + err = ZIP_ERRNO; + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_datablock(zi->central_dir.first_block); + + if (err==ZIP_OK) /* Magic End */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* size of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the + starting disk number */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, + (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + + if (err==ZIP_OK) /* zipfile comment length */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if ((err==ZIP_OK) && (size_global_comment>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + global_comment,size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + + if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipSetFlags(zipFile file, unsigned flags) +{ + zip_internal* zi; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + zi->flags |= flags; + return ZIP_OK; +} + +extern int ZEXPORT zipClearFlags(zipFile file, unsigned flags) +{ + zip_internal* zi; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + zi->flags &= ~flags; + return ZIP_OK; +} diff --git a/depends/quazip/zip.h b/depends/quazip/zip.h new file mode 100644 index 00000000..269ec2da --- /dev/null +++ b/depends/quazip/zip.h @@ -0,0 +1,245 @@ +/* zip.h -- IO for compress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow creates .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + For uncompress .zip file, look at unzip.h + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.html for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Modified by Sergey A. Tachenov to integrate with Qt. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _zip_H +#define _zip_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#define ZIP_WRITE_DATA_DESCRIPTOR 0x8u + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((voidpf file, int append)); +/* + Create a zipfile. + file is whatever the IO API accepts. For Qt IO API it's a pointer to + QIODevice. For fopen() IO API it's a file name (const char*). + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((voidpf file, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCtypting)); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCtypting : crc of file to compress (needed for crypting) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); +/* + Close the current file in the zipfile, for fiel opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + +/* + Added by Sergey A. Tachenov to tweak zipping behaviour. + */ +extern int ZEXPORT zipSetFlags(zipFile file, unsigned flags); +extern int ZEXPORT zipClearFlags(zipFile file, unsigned flags); + +#ifdef __cplusplus +} +#endif + +#endif /* _zip_H */ diff --git a/depends/settings/CMakeLists.txt b/depends/settings/CMakeLists.txt new file mode 100644 index 00000000..e5aae0b7 --- /dev/null +++ b/depends/settings/CMakeLists.txt @@ -0,0 +1,51 @@ +project(libSettings) + +# Find Qt +find_package(Qt5Core REQUIRED) + +# Include Qt headers. +include_directories(${Qt5Base_INCLUDE_DIRS}) +include_directories(${Qt5Network_INCLUDE_DIRS}) + +SET(LIBSETTINGS_HEADERS +include/libsettings_config.h + +include/inifile.h + +include/settingsobject.h +include/setting.h +include/overridesetting.h + +include/basicsettingsobject.h +include/inisettingsobject.h + +include/keyring.h +) + +SET(LIBSETTINGS_HEADERS_PRIVATE +src/stubkeyring.h +) + +SET(LIBSETTINGS_SOURCES +src/inifile.cpp + +src/settingsobject.cpp +src/setting.cpp +src/overridesetting.cpp + +src/basicsettingsobject.cpp +src/inisettingsobject.cpp + +src/keyring.cpp +src/stubkeyring.cpp +) + +# Set the include dir path. +SET(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) +include_directories(${LIBSETTINGS_INCLUDE_DIR}) + +add_definitions(-DLIBSETTINGS_LIBRARY) + +add_library(libSettings SHARED ${LIBSETTINGS_SOURCES} ${LIBSETTINGS_HEADERS} ${LIBSETTINGS_HEADERS_PRIVATE}) +qt5_use_modules(libSettings Core) +target_link_libraries(libSettings) diff --git a/depends/settings/include/basicsettingsobject.h b/depends/settings/include/basicsettingsobject.h new file mode 100644 index 00000000..b7e5851d --- /dev/null +++ b/depends/settings/include/basicsettingsobject.h @@ -0,0 +1,44 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASICSETTINGSOBJECT_H +#define BASICSETTINGSOBJECT_H + +#include <QObject> +#include <QSettings> + +#include "settingsobject.h" + +#include "libsettings_config.h" + +/*! + * \brief A settings object that stores its settings in a QSettings object. + */ +class LIBSETTINGS_EXPORT BasicSettingsObject : public SettingsObject +{ + Q_OBJECT +public: + explicit BasicSettingsObject(QObject *parent = 0); + +protected slots: + virtual void changeSetting(const Setting &setting, QVariant value); + +protected: + virtual QVariant retrieveValue(const Setting &setting); + + QSettings config; +}; + +#endif // BASICSETTINGSOBJECT_H diff --git a/depends/settings/include/inifile.h b/depends/settings/include/inifile.h new file mode 100644 index 00000000..94467832 --- /dev/null +++ b/depends/settings/include/inifile.h @@ -0,0 +1,38 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INIFILE_H +#define INIFILE_H + +#include <QMap> +#include <QString> +#include <QVariant> + +#include "libsettings_config.h" + +// Sectionless INI parser (for instance config files) +class LIBSETTINGS_EXPORT INIFile : public QMap<QString, QVariant> +{ +public: + explicit INIFile(); + + bool loadFile(QString fileName); + bool saveFile(QString fileName); + + QVariant get(QString key, QVariant def) const; + void set(QString key, QVariant val); +}; + +#endif // INIFILE_H diff --git a/depends/settings/include/inisettingsobject.h b/depends/settings/include/inisettingsobject.h new file mode 100644 index 00000000..03d6fe05 --- /dev/null +++ b/depends/settings/include/inisettingsobject.h @@ -0,0 +1,60 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INISETTINGSOBJECT_H +#define INISETTINGSOBJECT_H + +#include <QObject> + +#include "inifile.h" + +#include "settingsobject.h" + +#include "libutil_config.h" + +/*! + * \brief A settings object that stores its settings in an INIFile. + */ +class LIBSETTINGS_EXPORT INISettingsObject : public SettingsObject +{ + Q_OBJECT +public: + explicit INISettingsObject(const QString &path, QObject *parent = 0); + + /*! + * \brief Gets the path to the INI file. + * \return The path to the INI file. + */ + virtual QString filePath() const { return m_filePath; } + + /*! + * \brief Sets the path to the INI file and reloads it. + * \param filePath The INI file's new path. + */ + virtual void setFilePath(const QString &filePath); + +protected slots: + virtual void changeSetting(const Setting &setting, QVariant value); + virtual void resetSetting ( const Setting& setting ); + +protected: + virtual QVariant retrieveValue(const Setting &setting); + + INIFile m_ini; + + QString m_filePath; +}; + +#endif // INISETTINGSOBJECT_H diff --git a/depends/settings/include/keyring.h b/depends/settings/include/keyring.h new file mode 100644 index 00000000..299b14b0 --- /dev/null +++ b/depends/settings/include/keyring.h @@ -0,0 +1,92 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KEYRING_H +#define KEYRING_H + +#include <QString> + +#include "libsettings_config.h" + +/** + * @file libsettings/include/keyring.h + * Access to System Keyrings + */ + +/** + * @brief The Keyring class + * the System Keyring/Keychain/Wallet/Vault/etc + */ +class LIBSETTINGS_EXPORT Keyring +{ +public: + /** + * @brief the System Keyring instance + * @return the Keyring instance + */ + static Keyring *instance(); + + /** + * @brief store a password in the Keyring + * @param service the service name + * @param username the account name + * @param password the password to store + * @return success + */ + virtual bool storePassword(QString service, QString username, QString password) = 0; + + /** + * @brief get a password from the Keyring + * @param service the service name + * @param username the account name + * @return the password (success=!isNull()) + */ + virtual QString getPassword(QString service, QString username) = 0; + + /** + * @brief lookup a password + * @param service the service name + * @param username the account name + * @return wether the password is available + */ + virtual bool hasPassword(QString service, QString username) = 0; + + /** + * @brief get a list of all stored accounts. + * @param service the service name + * @return + */ + virtual QStringList getStoredAccounts(QString service) = 0; + + /** + * @brief Remove the specified account from storage + * @param service the service name + * @param username the account name + * @return + */ + virtual void removeStoredAccount(QString service, QString username) = 0; + +protected: + /// fall back to StubKeyring if false + virtual bool isValid() { return false; } + +private: + static Keyring *m_instance; + static void destroy(); +}; + +#endif // KEYRING_H diff --git a/depends/settings/include/libsettings_config.h b/depends/settings/include/libsettings_config.h new file mode 100644 index 00000000..dc8e6228 --- /dev/null +++ b/depends/settings/include/libsettings_config.h @@ -0,0 +1,27 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBINSTANCE_CONFIG_H +#define LIBINSTANCE_CONFIG_H + +#include <QtCore/QtGlobal> + +#ifdef LIBSETTINGS_LIBRARY +# define LIBSETTINGS_EXPORT Q_DECL_EXPORT +#else +# define LIBSETTINGS_EXPORT Q_DECL_IMPORT +#endif + +#endif // LIBINSTANCE_CONFIG_H diff --git a/depends/settings/include/overridesetting.h b/depends/settings/include/overridesetting.h new file mode 100644 index 00000000..58bb6d40 --- /dev/null +++ b/depends/settings/include/overridesetting.h @@ -0,0 +1,43 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVERRIDESETTING_H +#define OVERRIDESETTING_H + +#include <QObject> + +#include "setting.h" + +#include "libsettings_config.h" + +/*! + * \brief A setting that 'overrides another.' + * This means that the setting's default value will be the value of another setting. + * The other setting can be (and usually is) a part of a different SettingsObject + * than this one. + */ +class LIBSETTINGS_EXPORT OverrideSetting : public Setting +{ + Q_OBJECT +public: + explicit OverrideSetting(const QString &name, Setting *other, QObject *parent = 0); + + virtual QVariant defValue() const; + +protected: + Setting *m_other; +}; + +#endif // OVERRIDESETTING_H diff --git a/depends/settings/include/setting.h b/depends/settings/include/setting.h new file mode 100644 index 00000000..a161ab50 --- /dev/null +++ b/depends/settings/include/setting.h @@ -0,0 +1,114 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SETTING_H +#define SETTING_H + +#include <QObject> +#include <QVariant> + +#include "libsettings_config.h" + +class SettingsObject; + +/*! + * + */ +class LIBSETTINGS_EXPORT Setting : public QObject +{ + Q_OBJECT +public: + /*! + * \brief Constructs a new Setting object with the given parent. + * \param parent The Setting's parent object. + */ + explicit Setting(QString id, QVariant defVal = QVariant(), QObject *parent = 0); + + /*! + * \brief Gets this setting's ID. + * This is used to refer to the setting within the application. + * \warning Changing the ID while the setting is registered with a SettingsObject results in undefined behavior. + * \return The ID of the setting. + */ + virtual QString id() const { return m_id; } + + /*! + * \brief Gets this setting's config file key. + * This is used to store the setting's value in the config file. It is usually + * the same as the setting's ID, but it can be different. + * \return The setting's config file key. + */ + virtual QString configKey() const { return id(); } + + /*! + * \brief Gets this setting's value as a QVariant. + * This is done by calling the SettingsObject's retrieveValue() function. + * If this Setting doesn't have a SettingsObject, this returns an invalid QVariant. + * \return QVariant containing this setting's value. + * \sa value() + */ + virtual QVariant get() const; + + /*! + * \brief Gets this setting's actual value (I.E. not as a QVariant). + * This function is just shorthand for get().value<T>() + * \return The setting's actual value. + */ + template<typename T> + inline T value() const { return get().value<T>(); } + + + /*! + * \brief Gets this setting's default value. + * \return The default value of this setting. + */ + virtual QVariant defValue() const; + +signals: + /*! + * \brief Signal emitted when this Setting object's value changes. + * \param setting A reference to the Setting that changed. + * \param value This Setting object's new value. + */ + void settingChanged(const Setting &setting, QVariant value); + + /*! + * \brief Signal emitted when this Setting object's value resets to default. + * \param setting A reference to the Setting that changed. + */ + void settingReset(const Setting &setting); + +public slots: + /*! + * \brief Changes the setting's value. + * This is done by emitting the settingChanged() signal which will then be + * handled by the SettingsObject object and cause the setting to change. + * \param value The new value. + */ + virtual void set(QVariant value); + + /*! + * \brief Reset the setting to default + * This is done by emitting the settingReset() signal which will then be + * handled by the SettingsObject object and cause the setting to change. + * \param value The new value. + */ + virtual void reset(); +protected: + QString m_id; + QVariant m_defVal; +}; + +#endif // SETTING_H diff --git a/depends/settings/include/settingsobject.h b/depends/settings/include/settingsobject.h new file mode 100644 index 00000000..a2f03699 --- /dev/null +++ b/depends/settings/include/settingsobject.h @@ -0,0 +1,192 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SETTINGSOBJECT_H +#define SETTINGSOBJECT_H + +#include <QObject> +#include <QMap> + +#include "libsettings_config.h" + +class Setting; + +/*! + * \brief The SettingsObject handles communicating settings between the application and a settings file. + * The class keeps a list of Setting objects. Each Setting object represents one + * of the application's settings. These Setting objects are registered with + * a SettingsObject and can be managed similarly to the way a list works. + * + * \author Andrew Okin + * \date 2/22/2013 + * + * \sa Setting + */ +class LIBSETTINGS_EXPORT SettingsObject : public QObject +{ + Q_OBJECT +public: + explicit SettingsObject(QObject *parent = 0); + + /*! + * \brief Registers the given setting with this SettingsObject and connects the necessary signals. + * This will fail if there is already a setting with the same ID as + * the one that is being registered. + * \note Registering a setting object causes the SettingsObject to take ownership + * of the object. This means that setting's parent will be set to the object + * it was registered with. Because the object it was registered with has taken + * ownership, it becomes responsible for managing that setting object's memory. + * \warning Do \b not delete the setting after registering it. + * \param setting A pointer to the setting that will be registered. + * \return True if successful. False if registry failed. + */ + virtual bool registerSetting(Setting *setting); + + /*! + * \brief Unregisters the given setting from this SettingsObject and disconnects its signals. + * \note This does not delete the setting. Furthermore, when the setting is + * unregistered, the SettingsObject drops ownership of the setting. This means + * that if you unregister a setting, its parent is set to null and you become + * responsible for freeing its memory. + * \param setting The setting to unregister. + */ + virtual void unregisterSetting(Setting *setting); + + + /*! + * \brief Gets the setting with the given ID. + * \param id The ID of the setting to get. + * \return A pointer to the setting with the given ID. + * Returns null if there is no setting with the given ID. + * \sa operator []() + */ + virtual Setting *getSetting(const QString &id) const; + + /*! + * \brief Same as getSetting() + * \param id The ID of the setting to get. + * \return A pointer to the setting with the given ID. + * \sa getSetting() + */ + inline Setting *operator [](const QString &id) { return getSetting(id); } + + + /*! + * \brief Gets the value of the setting with the given ID. + * \param id The ID of the setting to get. + * \return The setting's value as a QVariant. + * If no setting with the given ID exists, returns an invalid QVariant. + */ + virtual QVariant get(const QString &id) const; + + /*! + * \brief Sets the value of the setting with the given ID. + * If no setting with the given ID exists, returns false and logs to qDebug + * \param id The ID of the setting to change. + * \param value The new value of the setting. + * \return True if successful, false if it failed. + */ + virtual bool set(const QString &id, QVariant value); + + /*! + * \brief Reverts the setting with the given ID to default. + * \param id The ID of the setting to reset. + */ + virtual void reset(const QString &id) const; + + /*! + * \brief Gets a QList with pointers to all of the registered settings. + * The order of the entries in the list is undefined. + * \return A QList with pointers to all registered settings. + */ + virtual QList<Setting *> getSettings(); + + /*! + * \brief Checks if this SettingsObject contains a setting with the given ID. + * \param id The ID to check for. + * \return True if the SettingsObject has a setting with the given ID. + */ + virtual bool contains(const QString &id); + +signals: + /*! + * \brief Signal emitted when one of this SettingsObject object's settings changes. + * This is usually just connected directly to each Setting object's + * settingChanged() signals. + * \param setting A reference to the Setting object that changed. + * \param value The Setting object's new value. + */ + void settingChanged(const Setting &setting, QVariant value); + + /*! + * \brief Signal emitted when one of this SettingsObject object's settings resets. + * This is usually just connected directly to each Setting object's + * settingReset() signals. + * \param setting A reference to the Setting object that changed. + */ + void settingReset(const Setting &setting); + +protected slots: + /*! + * \brief Changes a setting. + * This slot is usually connected to each Setting object's + * settingChanged() signal. The signal is emitted, causing this slot + * to update the setting's value in the config file. + * \param setting A reference to the Setting object that changed. + * \param value The setting's new value. + */ + virtual void changeSetting(const Setting &setting, QVariant value) = 0; + + /*! + * \brief Resets a setting. + * This slot is usually connected to each Setting object's + * settingReset() signal. The signal is emitted, causing this slot + * to update the setting's value in the config file. + * \param setting A reference to the Setting object that changed. + */ + virtual void resetSetting(const Setting &setting) = 0; + +protected: + /*! + * \brief Connects the necessary signals to the given Setting. + * \param setting The setting to connect. + */ + virtual void connectSignals(const Setting &setting); + + /*! + * \brief Disconnects signals from the given Setting. + * \param setting The setting to disconnect. + */ + virtual void disconnectSignals(const Setting &setting); + + /*! + * \brief Function used by Setting objects to get their values from the SettingsObject. + * \param setting The + * \return + */ + virtual QVariant retrieveValue(const Setting &setting) = 0; + + friend class Setting; + +private: + QMap<QString, Setting *> m_settings; +}; + +/*! + * \brief A global settings object. + */ +LIBSETTINGS_EXPORT extern SettingsObject *globalSettings; + +#endif // SETTINGSOBJECT_H diff --git a/depends/settings/src/basicsettingsobject.cpp b/depends/settings/src/basicsettingsobject.cpp new file mode 100644 index 00000000..484928c8 --- /dev/null +++ b/depends/settings/src/basicsettingsobject.cpp @@ -0,0 +1,46 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/basicsettingsobject.h" +#include "include/setting.h" + +BasicSettingsObject::BasicSettingsObject(QObject *parent) : + SettingsObject(parent) +{ + +} + +void BasicSettingsObject::changeSetting(const Setting &setting, QVariant value) +{ + if (contains(setting.id())) + { + if(value.isValid()) + config.setValue(setting.configKey(), value); + else + config.remove(setting.configKey()); + } +} + +QVariant BasicSettingsObject::retrieveValue(const Setting &setting) +{ + if (contains(setting.id())) + { + return config.value(setting.configKey()); + } + else + { + return QVariant(); + } +} diff --git a/depends/settings/src/inifile.cpp b/depends/settings/src/inifile.cpp new file mode 100644 index 00000000..43545a4a --- /dev/null +++ b/depends/settings/src/inifile.cpp @@ -0,0 +1,86 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/inifile.h" + +#include <QFile> +#include <QTextStream> +#include <QStringList> + +INIFile::INIFile() +{ + +} + +bool INIFile::saveFile(QString fileName) +{ + // TODO Handle errors. + QFile file(fileName); + file.open(QIODevice::WriteOnly); + QTextStream out(&file); + + for (Iterator iter = begin(); iter != end(); iter++) + { + out << iter.key() << "=" << iter.value().toString() << "\n"; + } + + return true; +} + +bool INIFile::loadFile(QString fileName) +{ + // TODO Handle errors. + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream in(&file); + + QStringList lines = in.readAll().split('\n'); + for (int i = 0; i < lines.count(); i++) + { + QString & lineRaw = lines[i]; + // Ignore comments. + QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed(); + + int eqPos = line.indexOf('='); + if(eqPos == -1) + continue; + QString key = line.left(eqPos).trimmed(); + QString valueStr = line.right(line.length() - eqPos - 1).trimmed(); + + QVariant value(valueStr); + /* + QString dbg = key; + dbg += " = "; + dbg += valueStr; + qDebug(dbg.toLocal8Bit()); + */ + this->operator [](key) = value; + } + + return true; +} + +QVariant INIFile::get(QString key, QVariant def) const +{ + if (!this->contains(key)) + return def; + else + return this->operator [](key); +} + +void INIFile::set(QString key, QVariant val) +{ + this->operator [](key) = val; +} diff --git a/depends/settings/src/inisettingsobject.cpp b/depends/settings/src/inisettingsobject.cpp new file mode 100644 index 00000000..854421b6 --- /dev/null +++ b/depends/settings/src/inisettingsobject.cpp @@ -0,0 +1,62 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/inisettingsobject.h" +#include "include/setting.h" + +INISettingsObject::INISettingsObject(const QString &path, QObject *parent) : + SettingsObject(parent) +{ + m_filePath = path; + m_ini.loadFile(path); +} + +void INISettingsObject::setFilePath(const QString &filePath) +{ + m_filePath = filePath; +} + +void INISettingsObject::changeSetting(const Setting &setting, QVariant value) +{ + if (contains(setting.id())) + { + if(value.isValid()) + m_ini.set(setting.configKey(), value); + else + m_ini.remove(setting.configKey()); + m_ini.saveFile(m_filePath); + } +} + +void INISettingsObject::resetSetting ( const Setting& setting ) +{ + if (contains(setting.id())) + { + m_ini.remove(setting.configKey()); + m_ini.saveFile(m_filePath); + } +} + +QVariant INISettingsObject::retrieveValue(const Setting &setting) +{ + if (contains(setting.id())) + { + return m_ini.get(setting.configKey(), QVariant()); + } + else + { + return QVariant(); + } +} diff --git a/depends/settings/src/keyring.cpp b/depends/settings/src/keyring.cpp new file mode 100644 index 00000000..9eaba684 --- /dev/null +++ b/depends/settings/src/keyring.cpp @@ -0,0 +1,63 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/keyring.h" + +#include "osutils.h" + +#include "stubkeyring.h" + +// system specific keyrings +/*#if defined(OSX) +class OSXKeychain; +#define KEYRING OSXKeychain +#elif defined(LINUX) +class XDGKeyring; +#define KEYRING XDGKeyring +#elif defined(WINDOWS) +class Win32Keystore; +#define KEYRING Win32Keystore +#else +#pragma message Keyrings are not supported on your os. Falling back to the insecure StubKeyring +#endif*/ + +Keyring *Keyring::instance() +{ + if (m_instance == nullptr) + { +#ifdef KEYRING + m_instance = new KEYRING(); + if (!m_instance->isValid()) + { + qWarning("Could not create SystemKeyring! falling back to StubKeyring."); + m_instance = new StubKeyring(); + } +#else + qWarning("Keyrings are not supported on your OS. Fallback StubKeyring is insecure!"); + m_instance = new StubKeyring(); +#endif + atexit(Keyring::destroy); + } + return m_instance; +} + +void Keyring::destroy() +{ + delete m_instance; +} + +Keyring *Keyring::m_instance; diff --git a/depends/settings/src/overridesetting.cpp b/depends/settings/src/overridesetting.cpp new file mode 100644 index 00000000..eafb298f --- /dev/null +++ b/depends/settings/src/overridesetting.cpp @@ -0,0 +1,30 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/overridesetting.h" + +OverrideSetting::OverrideSetting(const QString &name, Setting *other, QObject *parent) : + Setting(name, QVariant(), parent) +{ + m_other = other; +} + +QVariant OverrideSetting::defValue() const +{ + if (m_other) + return m_other->get(); + else + return QVariant(); +} diff --git a/depends/settings/src/setting.cpp b/depends/settings/src/setting.cpp new file mode 100644 index 00000000..8e60af06 --- /dev/null +++ b/depends/settings/src/setting.cpp @@ -0,0 +1,54 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/setting.h" +#include "include/settingsobject.h" + +Setting::Setting(QString id, QVariant defVal, QObject *parent) : + QObject(parent), m_id(id), m_defVal(defVal) +{ + +} + +QVariant Setting::get() const +{ + SettingsObject *sbase = qobject_cast<SettingsObject *>(parent()); + if (!sbase) + { + return defValue(); + } + else + { + QVariant test = sbase->retrieveValue(*this); + if(!test.isValid()) + return defValue(); + return test; + } +} + +QVariant Setting::defValue() const +{ + return m_defVal; +} + +void Setting::set(QVariant value) +{ + emit settingChanged(*this, value); +} + +void Setting::reset() +{ + emit settingReset(*this); +} diff --git a/depends/settings/src/settingsobject.cpp b/depends/settings/src/settingsobject.cpp new file mode 100644 index 00000000..bf7b8825 --- /dev/null +++ b/depends/settings/src/settingsobject.cpp @@ -0,0 +1,144 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/settingsobject.h" +#include "include/setting.h" + +#include <QVariant> + +SettingsObject *globalSettings; + +SettingsObject::SettingsObject(QObject *parent) : + QObject(parent) +{ + +} + +bool SettingsObject::registerSetting(Setting *setting) +{ + // Check if setting is null or we already have a setting with the same ID. + if (!setting) + { + qDebug(QString("Failed to register setting. Setting is null."). + arg(setting->id()).toUtf8()); + return false; // Fail + } + + if (contains(setting->id())) + { + qDebug(QString("Failed to register setting %1. ID already exists."). + arg(setting->id()).toUtf8()); + return false; // Fail + } + + m_settings.insert(setting->id(), setting); + setting->setParent(this); // Take ownership. + + // Connect signals. + connectSignals(*setting); + + // qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8()); + return true; +} + +void SettingsObject::unregisterSetting(Setting *setting) +{ + if (!setting || !m_settings.contains(setting->id())) + return; // We can't unregister something that's not registered. + + m_settings.remove(setting->id()); + + // Disconnect signals. + disconnectSignals(*setting); + + setting->setParent(NULL); // Drop ownership. +} + + +Setting *SettingsObject::getSetting(const QString &id) const +{ + // Make sure there is a setting with the given ID. + if (!m_settings.contains(id)) + return NULL; + + return m_settings[id]; +} + +QVariant SettingsObject::get(const QString &id) const +{ + Setting *setting = getSetting(id); + return (setting ? setting->get() : QVariant()); +} + +bool SettingsObject::set(const QString &id, QVariant value) +{ + Setting *setting = getSetting(id); + if (!setting) + { + qDebug(QString("Error changing setting %1. Setting doesn't exist."). + arg(id).toUtf8()); + return false; + } + else + { + setting->set(value); + return true; + } +} + +void SettingsObject::reset(const QString &id) const +{ + Setting *setting = getSetting(id); + if(setting) + setting->reset(); +} + + +QList<Setting *> SettingsObject::getSettings() +{ + return m_settings.values(); +} + +bool SettingsObject::contains(const QString &id) +{ + return m_settings.contains(id); +} + + +void SettingsObject::connectSignals(const Setting &setting) +{ + connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)), + SLOT(changeSetting(const Setting &, QVariant))); + connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)), + SIGNAL(settingChanged(const Setting &, QVariant))); + + connect(&setting, SIGNAL(settingReset(Setting)), + SLOT(resetSetting(const Setting &))); + connect(&setting, SIGNAL(settingReset(Setting)), + SIGNAL(settingReset(const Setting &))); +} + +void SettingsObject::disconnectSignals(const Setting &setting) +{ + setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), + this, SLOT(changeSetting(const Setting &, QVariant))); + setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), + this, SIGNAL(settingChanged(const Setting &, QVariant))); + + setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), + this, SLOT(resetSetting(const Setting &, QVariant))); + setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), + this, SIGNAL(settingReset(const Setting &, QVariant))); +} diff --git a/depends/settings/src/stubkeyring.cpp b/depends/settings/src/stubkeyring.cpp new file mode 100644 index 00000000..cf814d2f --- /dev/null +++ b/depends/settings/src/stubkeyring.cpp @@ -0,0 +1,104 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stubkeyring.h" + +#include <QStringList> + +// Scrambling +// this is NOT SAFE, but it's not plain either. +int scrambler = 0x9586309; + +QString scramble(QString in_) +{ + QByteArray in = in_.toUtf8(); + QByteArray out; + for (int i = 0; i<in.length(); i++) + out.append(in.at(i) ^ scrambler); + return QString::fromUtf8(out); +} + +inline QString base64(QString in) +{ + return QString(in.toUtf8().toBase64()); +} +inline QString unbase64(QString in) +{ + return QString::fromUtf8(QByteArray::fromBase64(in.toLatin1())); +} + +inline QString scramble64(QString in) +{ + return base64(scramble(in)); +} +inline QString unscramble64(QString in) +{ + return scramble(unbase64(in)); +} + +// StubKeyring implementation +inline QString generateKey(QString service, QString username) +{ + return QString("%1/%2").arg(base64(service)).arg(scramble64(username)); +} + +bool StubKeyring::storePassword(QString service, QString username, QString password) +{ + m_settings.setValue(generateKey(service, username), scramble64(password)); + return true; +} + +QString StubKeyring::getPassword(QString service, QString username) +{ + QString key = generateKey(service, username); + if (!m_settings.contains(key)) + return QString(); + return unscramble64(m_settings.value(key).toString()); +} + +bool StubKeyring::hasPassword(QString service, QString username) +{ + return m_settings.contains(generateKey(service, username)); +} + +QStringList StubKeyring::getStoredAccounts(QString service) +{ + service = base64(service).append('/'); + QStringList out; + QStringList in(m_settings.allKeys()); + QStringListIterator it(in); + while(it.hasNext()) + { + QString c = it.next(); + if (c.startsWith(service)) + out << unscramble64(c.mid(service.length())); + } + return out; +} + +void StubKeyring::removeStoredAccount ( QString service, QString username ) +{ + QString key = generateKey(service, username); + m_settings.remove(key); +} + +//FIXME: this needs tweaking/changes for user account level storage +StubKeyring::StubKeyring() : +// m_settings(QSettings::UserScope, "Orochimarufan", "Keyring") + m_settings("keyring.cfg", QSettings::IniFormat) +{ +} diff --git a/depends/settings/src/stubkeyring.h b/depends/settings/src/stubkeyring.h new file mode 100644 index 00000000..45791c85 --- /dev/null +++ b/depends/settings/src/stubkeyring.h @@ -0,0 +1,41 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STUBKEYRING_H +#define STUBKEYRING_H + +#include "include/keyring.h" + +#include <QSettings> + +class StubKeyring : public Keyring +{ +public: + virtual bool storePassword(QString service, QString username, QString password); + virtual QString getPassword(QString service, QString username); + virtual bool hasPassword(QString service, QString username); + virtual QStringList getStoredAccounts(QString service); + virtual void removeStoredAccount(QString service, QString username); +private: + friend class Keyring; + explicit StubKeyring(); + virtual bool isValid() { return true; } + + QSettings m_settings; +}; + +#endif // STUBKEYRING_H diff --git a/depends/util/CMakeLists.txt b/depends/util/CMakeLists.txt new file mode 100644 index 00000000..7affb5ea --- /dev/null +++ b/depends/util/CMakeLists.txt @@ -0,0 +1,51 @@ +project(libUtil) + +######## Set compiler flags ######## +IF(APPLE) + # assume clang 4.1.0+, add C++0x/C++11 stuff + message(STATUS "Using APPLE CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++") +ELSEIF(UNIX) + # assume GCC, add C++0x/C++11 stuff + MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +ELSEIF(MINGW) + MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") +ENDIF() + + +# Find Qt +find_package(Qt5Core REQUIRED) + +# Include Qt headers. +include_directories(${Qt5Base_INCLUDE_DIRS}) +# include_directories(${Qt5Network_INCLUDE_DIRS}) + +SET(LIBUTIL_HEADERS +include/libutil_config.h + +include/apputils.h + +include/pathutils.h +include/osutils.h +include/userutils.h +include/cmdutils.h +) + +SET(LIBUTIL_SOURCES +src/pathutils.cpp +src/osutils.cpp +src/userutils.cpp +src/cmdutils.cpp +) + +# Set the include dir path. +SET(LIBUTIL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) + +add_definitions(-DLIBUTIL_LIBRARY) + +add_library(libUtil SHARED ${LIBUTIL_SOURCES} ${LIBUTIL_HEADERS}) +# qt5_use_modules(libUtil Core Network) +qt5_use_modules(libUtil Core) +target_link_libraries(libUtil) diff --git a/depends/util/include/apputils.h b/depends/util/include/apputils.h new file mode 100644 index 00000000..a64adc50 --- /dev/null +++ b/depends/util/include/apputils.h @@ -0,0 +1,21 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APPUTILS_H +#define APPUTILS_H + +#define STR_VAL(val) # val + +#endif // APPUTILS_H diff --git a/depends/util/include/cmdutils.h b/depends/util/include/cmdutils.h new file mode 100644 index 00000000..a6379397 --- /dev/null +++ b/depends/util/include/cmdutils.h @@ -0,0 +1,259 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CMDUTILS_H +#define CMDUTILS_H + +#include <exception> + +#include <QString> +#include <QVariant> +#include <QHash> +#include <QStringList> + +#include "libutil_config.h" + +/** + * @file libutil/include/cmdutils.h + * @brief commandline parsing and processing utilities + */ + +namespace Util { +namespace Commandline { + +/** + * @brief split a string into argv items like a shell would do + * @param args the argument string + * @return a QStringList containing all arguments + */ +LIBUTIL_EXPORT QStringList splitArgs(QString args); + +/** + * @brief The FlagStyle enum + * Specifies how flags are decorated + */ + +namespace FlagStyle +{ +enum LIBUTIL_EXPORT Enum +{ + GNU, /**< --option and -o (GNU Style) */ + Unix, /**< -option and -o (Unix Style) */ + Windows, /**< /option and /o (Windows Style) */ +#ifdef Q_OS_WIN32 + Default = Windows +#else + Default = GNU +#endif +}; +} + +/** + * @brief The ArgumentStyle enum + */ +namespace ArgumentStyle +{ +enum LIBUTIL_EXPORT Enum +{ + Space, /**< --option=value */ + Equals, /**< --option value */ + SpaceAndEquals, /**< --option[= ]value */ +#ifdef Q_OS_WIN32 + Default = Equals +#else + Default = SpaceAndEquals +#endif +}; +} + +/** + * @brief The ParsingError class + */ +class LIBUTIL_EXPORT ParsingError : public std::exception +{ +public: + ParsingError(const QString &what); + ParsingError(const ParsingError &e); + ~ParsingError() throw() {} + const char *what() const throw(); + QString qwhat() const; +private: + QString m_what; +}; + +/** + * @brief The Parser class + */ +class LIBUTIL_EXPORT Parser +{ +public: + /** + * @brief Parser constructor + * @param flagStyle the FlagStyle to use in this Parser + * @param argStyle the ArgumentStyle to use in this Parser + */ + Parser(FlagStyle::Enum flagStyle = FlagStyle::Default, + ArgumentStyle::Enum argStyle = ArgumentStyle::Default); + + /** + * @brief set the flag style + * @param style + */ + void setFlagStyle(FlagStyle::Enum style); + + /** + * @brief get the flag style + * @return + */ + FlagStyle::Enum flagStyle(); + + /** + * @brief set the argument style + * @param style + */ + void setArgumentStyle(ArgumentStyle::Enum style); + + /** + * @brief get the argument style + * @return + */ + ArgumentStyle::Enum argumentStyle(); + + /** + * @brief define a boolean switch + * @param name the parameter name + * @param def the default value + */ + void addSwitch(QString name, bool def = false); + + /** + * @brief define an option that takes an additional argument + * @param name the parameter name + * @param def the default value + */ + void addOption(QString name, QVariant def = QVariant()); + + /** + * @brief define a positional argument + * @param name the parameter name + * @param required wether this argument is required + * @param def the default value + */ + void addArgument(QString name, bool required = true, QVariant def = QVariant()); + + /** + * @brief adds a flag to an existing parameter + * @param name the (existing) parameter name + * @param flag the flag character + * @see addSwitch addArgument addOption + * Note: any one parameter can only have one flag + */ + void addShortOpt(QString name, QChar flag); + + /** + * @brief adds documentation to a Parameter + * @param name the parameter name + * @param metavar a string to be displayed as placeholder for the value + * @param doc a QString containing the documentation + * Note: on positional arguments, metavar replaces the name as displayed. + * on options , metavar replaces the value placeholder + */ + void addDocumentation(QString name, QString doc, QString metavar = QString()); + + /** + * @brief generate a help message + * @param progName the program name to use in the help message + * @param helpIndent how much the parameter documentation should be indented + * @param flagsInUsage whether we should use flags instead of options in the usage + * @return a help message + */ + QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true); + + /** + * @brief generate a short usage message + * @param progName the program name to use in the usage message + * @param useFlags whether we should use flags instead of options + * @return a usage message + */ + QString compileUsage(QString progName, bool useFlags = true); + + /** + * @brief parse + * @param argv a QStringList containing the program ARGV + * @return a QHash mapping argument names to their values + */ + QHash<QString, QVariant> parse(QStringList argv); + + /** + * @brief clear all definitions + */ + void clear(); + + ~Parser(); + +private: + FlagStyle::Enum m_flagStyle; + ArgumentStyle::Enum m_argStyle; + + enum OptionType + { + otSwitch, + otOption + }; + + // Important: the common part MUST BE COMMON ON ALL THREE structs + struct CommonDef { + QString name; + QString doc; + QString metavar; + QVariant def; + }; + + struct OptionDef { + // common + QString name; + QString doc; + QString metavar; + QVariant def; + // option + OptionType type; + QChar flag; + }; + + struct PositionalDef { + // common + QString name; + QString doc; + QString metavar; + QVariant def; + // positional + bool required; + }; + + QHash<QString, OptionDef *> m_options; + QHash<QChar, OptionDef *> m_flags; + QHash<QString, CommonDef *> m_params; + QList<PositionalDef *> m_positionals; + QList<OptionDef *> m_optionList; + + void getPrefix(QString &opt, QString &flag); +}; + +} +} + +#endif // CMDUTILS_H diff --git a/depends/util/include/libutil_config.h b/depends/util/include/libutil_config.h new file mode 100644 index 00000000..4bf111e6 --- /dev/null +++ b/depends/util/include/libutil_config.h @@ -0,0 +1,27 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBUTIL_CONFIG_H +#define LIBUTIL_CONFIG_H + +#include <QtCore/QtGlobal> + +#ifdef LIBUTIL_LIBRARY +# define LIBUTIL_EXPORT Q_DECL_EXPORT +#else +# define LIBUTIL_EXPORT Q_DECL_IMPORT +#endif + +#endif // LIBUTIL_CONFIG_H diff --git a/depends/util/include/osutils.h b/depends/util/include/osutils.h new file mode 100644 index 00000000..c5d4bb61 --- /dev/null +++ b/depends/util/include/osutils.h @@ -0,0 +1,29 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OSUTILS_H +#define OSUTILS_H + +#include <QString> + +#if defined _WIN32 | defined _WIN64 +#define WINDOWS 1 +#elif __APPLE__ & __MACH__ +#define OSX 1 +#elif __linux__ +#define LINUX 1 +#endif + +#endif // OSUTILS_H diff --git a/depends/util/include/pathutils.h b/depends/util/include/pathutils.h new file mode 100644 index 00000000..d4f41da3 --- /dev/null +++ b/depends/util/include/pathutils.h @@ -0,0 +1,37 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PATHUTILS_H +#define PATHUTILS_H + +#include <QString> + +#include "libutil_config.h" + +LIBUTIL_EXPORT QString PathCombine(QString path1, QString path2); +LIBUTIL_EXPORT QString PathCombine(QString path1, QString path2, QString path3); + +LIBUTIL_EXPORT QString AbsolutePath(QString path); + +LIBUTIL_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-'); + +LIBUTIL_EXPORT QString DirNameFromString(QString string, QString inDir = "."); + +LIBUTIL_EXPORT bool ensurePathExists(QString filenamepath); + +LIBUTIL_EXPORT bool copyPath(QString src, QString dst); + + +#endif // PATHUTILS_H diff --git a/depends/util/include/siglist.h b/depends/util/include/siglist.h new file mode 100644 index 00000000..24b1a889 --- /dev/null +++ b/depends/util/include/siglist.h @@ -0,0 +1,129 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIGLIST_H +#define SIGLIST_H + +#include <QObject> +#include <QList> + +// A QList that allows emitting signals when the list changes. +// Since QObject doesn't support templates, to use this class with a +// certain type, you should create a class deriving from SigList<T> and then +// call the DEFINE_SIGLIST_SIGNALS(T) and SETUP_SIGLIST_SIGNALS(T) macros. +template <typename T> +class SigList : public QList<T> +{ +public: + explicit SigList() : QList<T>() {} + + virtual void append(const T &value); + virtual void append(const QList<T> &other); + + virtual void clear(); + + virtual void erase(typename QList<T>::iterator pos); + virtual void erase(typename QList<T>::iterator first, typename QList<T>::iterator last); + + virtual void insert(int i, const T &t); + virtual void insert(typename QList<T>::iterator before, const T &t); + + virtual void move(int from, int to); + + virtual void pop_back() { takeLast(); } + virtual void pop_front() { takeFirst(); } + + virtual void push_back(const T &t) { append(t); } + virtual void push_front(const T &t) { prepend(t); } + + virtual void prepend(const T &t); + + virtual int removeAll(const T &t); + virtual bool removeOne(const T &t); + + virtual void removeAt(int i) { takeAt(i); } + virtual void removeFirst() { takeFirst(); } + virtual void removeLast() { takeLast(); } + + virtual void swap(QList<T> &other); + virtual void swap(int i, int j); + + virtual T takeAt(int i); + virtual T takeFirst(); + virtual T takeLast(); + + virtual QList<T> &operator +=(const QList<T> &other) { append(other); return *this; } + virtual QList<T> &operator +=(const T &value) { append(value); return *this; } + virtual QList<T> &operator <<(const QList<T> &other) { append(other); return *this; } + virtual QList<T> &operator <<(const T &value) { append(value); return *this; } + + virtual QList<T> &operator =(const QList<T> &other); + +protected: + // Signal emitted after an item is added to the list. + // Contains a reference to item and the item's new index. + virtual void onItemAdded(const T &item, int index) = 0; + + // Signal emitted after multiple items are added to the list at once. + // The items parameter is a const reference to a QList of the items that + // were added. + // The firstIndex parameter is the new index of the first item added. + virtual void onItemsAdded(const QList<T> &items, int firstIndex) = 0; + + // Signal emitted after an item is removed to the list. + // Contains a reference to the item and the item's old index. + virtual void onItemRemoved(const T &item, int index) = 0; + + // Signal emitted after multiple items are removed from the list at once. + // The items parameter is a const reference to a QList of the items that + // were added. + // The firstIndex parameter is the new index of the first item added. + virtual void onItemsRemoved(const QList<T> &items, int firstIndex) = 0; + + // Signal emitted after an item is moved to another index. + // Contains the item, the old index, and the new index. + virtual void onItemMoved(const T &item, int oldIndex, int newIndex) = 0; + + // Signal emitted after an operation that changes the whole list occurs. + // This signal should be treated as if all data in the entire list was cleared + // and new data added in its place. + virtual void onInvalidated() = 0; +}; + +// Defines the signals for a SigList +#define DEFINE_SIGLIST_SIGNALS(TYPE) \ + Q_SIGNAL void itemAdded(TYPE const &item, int index);\ + Q_SIGNAL void itemsAdded(const QList<TYPE> &items, int firstIndex);\ + Q_SIGNAL void itemRemoved(TYPE const &item, int index);\ + Q_SIGNAL void itemsRemoved(const QList<TYPE> &items, int firstIndex);\ + Q_SIGNAL void itemMoved(TYPE const &item, int oldIndex, int newIndex);\ + Q_SIGNAL void invalidated(); + +// Overrides the onItem* functions and causes them to emit their corresponding +// signals. +#define SETUP_SIGLIST_SIGNALS(TYPE) \ + virtual void onItemAdded(TYPE const &item, int index)\ + { emit itemAdded(item, index); }\ + virtual void onItemsAdded(const QList<TYPE> &items, int firstIndex)\ + { emit itemsAdded(items, firstIndex); }\ + virtual void onItemRemoved(TYPE const &item, int index)\ + { emit itemRemoved(item, index); }\ + virtual void onItemsRemoved(const QList<TYPE> &items, int firstIndex)\ + { emit itemsRemoved(items, firstIndex); }\ + virtual void onItemMoved(TYPE const &item, int oldIndex, int newIndex)\ + { emit itemMoved(item, oldIndex, newIndex); }\ + virtual void onInvalidated() { emit invalidated(); } + +#endif // SIGLIST_H diff --git a/depends/util/include/siglist_impl.h b/depends/util/include/siglist_impl.h new file mode 100644 index 00000000..5cdc632a --- /dev/null +++ b/depends/util/include/siglist_impl.h @@ -0,0 +1,156 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "siglist.h" + +template <typename T> +void SigList<T>::append(const T &value) +{ + QList<T>::append(value); + onItemAdded(value, QList<T>::length() - 1); +} + +template <typename T> +void SigList<T>::prepend(const T &value) +{ + QList<T>::prepend(value); + onItemAdded(value, 0); +} + +template <typename T> +void SigList<T>::append(const QList<T> &other) +{ + int index = QList<T>::length(); + QList<T>::append(other); + onItemsAdded(other, index); +} + +template <typename T> +void SigList<T>::clear() +{ + QList<T>::clear(); + onInvalidated(); +} + +template <typename T> +void SigList<T>::erase(typename QList<T>::iterator pos) +{ + T value = *pos; + int index = QList<T>::indexOf(*pos); + QList<T>::erase(pos); + onItemRemoved(value, index); +} + +template <typename T> +void SigList<T>::erase(typename QList<T>::iterator first, typename QList<T>::iterator last) +{ + QList<T> removedValues; + int firstIndex = QList<T>::indexOf(*first); + + for (auto iter = first; iter < last; iter++) + { + removedValues << *iter; + QList<T>::erase(iter); + } + + onItemsRemoved(removedValues, firstIndex); +} + +template <typename T> +void SigList<T>::insert(int i, const T &t) +{ + QList<T>::insert(i, t); + onItemAdded(t, i); +} + +template <typename T> +void SigList<T>::insert(typename QList<T>::iterator before, const T &t) +{ + QList<T>::insert(before, t); + onItemAdded(t, QList<T>::indexOf(t)); +} + +template <typename T> +void SigList<T>::move(int from, int to) +{ + const T &item = QList<T>::at(from); + QList<T>::move(from, to); + onItemMoved(item, from, to); +} + +template <typename T> +int SigList<T>::removeAll(const T &t) +{ + int retVal = QList<T>::removeAll(t); + onInvalidated(); + return retVal; +} + +template <typename T> +bool SigList<T>::removeOne(const T &t) +{ + int index = QList<T>::indexOf(t); + if (QList<T>::removeOne(t)) + { + onItemRemoved(t, index); + return true; + } + return false; +} + +template <typename T> +void SigList<T>::swap(QList<T> &other) +{ + QList<T>::swap(other); + onInvalidated(); +} + +template <typename T> +void SigList<T>::swap(int i, int j) +{ + const T &item1 = QList<T>::at(i); + const T &item2 = QList<T>::at(j); + QList<T>::swap(i, j); + onItemMoved(item1, i, j); + onItemMoved(item2, j, i); +} + +template <typename T> +T SigList<T>::takeAt(int i) +{ + T val = QList<T>::takeAt(i); + onItemRemoved(val, i); + return val; +} + +template <typename T> +T SigList<T>::takeFirst() +{ + return takeAt(0); +} + +template <typename T> +T SigList<T>::takeLast() +{ + return takeAt(QList<T>::length() - 1); +} + +template <typename T> +QList<T> &SigList<T>::operator =(const QList<T> &other) +{ + QList<T>::operator =(other); + onInvalidated(); + return *this; +} diff --git a/depends/util/include/userutils.h b/depends/util/include/userutils.h new file mode 100644 index 00000000..4f2760b1 --- /dev/null +++ b/depends/util/include/userutils.h @@ -0,0 +1,19 @@ +#ifndef USERUTILS_H +#define USERUTILS_H + +#include <QString> + +#include "libutil_config.h" + +namespace Util +{ +// Get the Directory representing the User's Desktop +LIBUTIL_EXPORT QString getDesktopDir(); + +// Create a shortcut at *location*, pointing to *dest* called with the arguments *args* +// call it *name* and assign it the icon *icon* +// return true if operation succeeded +LIBUTIL_EXPORT bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation); +} + +#endif // USERUTILS_H diff --git a/depends/util/src/cmdutils.cpp b/depends/util/src/cmdutils.cpp new file mode 100644 index 00000000..80ba719d --- /dev/null +++ b/depends/util/src/cmdutils.cpp @@ -0,0 +1,484 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/cmdutils.h" + +/** + * @file libutil/src/cmdutils.cpp + */ + +namespace Util { +namespace Commandline { + +// commandline splitter +QStringList splitArgs(QString args) +{ + QStringList argv; + QString current; + bool escape = false; + QChar inquotes; + for (int i=0; i<args.length(); i++) + { + QChar cchar = args.at(i); + + // \ escaped + if (escape) + { + current += cchar; + escape = false; + // in "quotes" + } + else if (!inquotes.isNull()) + { + if (cchar == 0x5C) + escape = true; + else if (cchar == inquotes) + inquotes = 0; + else + current += cchar; + // otherwise + } + else + { + if (cchar == 0x20) + { + if (!current.isEmpty()) + { + argv << current; + current.clear(); + } + } + else if (cchar == 0x22 || cchar == 0x27) + inquotes = cchar; + else + current += cchar; + } + } + if (!current.isEmpty()) + argv << current; + return argv; +} + + +Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle) +{ + m_flagStyle = flagStyle; + m_argStyle = argStyle; +} + +// styles setter/getter +void Parser::setArgumentStyle(ArgumentStyle::Enum style) +{ + m_argStyle = style; +} +ArgumentStyle::Enum Parser::argumentStyle() +{ + return m_argStyle; +} + +void Parser::setFlagStyle(FlagStyle::Enum style) +{ + m_flagStyle = style; +} +FlagStyle::Enum Parser::flagStyle() +{ + return m_flagStyle; +} + +// setup methods +void Parser::addSwitch(QString name, bool def) +{ + if (m_params.contains(name)) + throw "Name not unique"; + + OptionDef *param = new OptionDef; + param->type = otSwitch; + param->name = name; + param->metavar = QString("<%1>").arg(name); + param->def = def; + + m_options[name] = param; + m_params[name] = (CommonDef *)param; + m_optionList.append(param); +} + +void Parser::addOption(QString name, QVariant def) +{ + if (m_params.contains(name)) + throw "Name not unique"; + + OptionDef *param = new OptionDef; + param->type = otOption; + param->name = name; + param->metavar = QString("<%1>").arg(name); + param->def = def; + + m_options[name] = param; + m_params[name] = (CommonDef *)param; + m_optionList.append(param); +} + +void Parser::addArgument(QString name, bool required, QVariant def) +{ + if (m_params.contains(name)) + throw "Name not unique"; + + PositionalDef *param = new PositionalDef; + param->name = name; + param->def = def; + param->required = required; + param->metavar = name; + + m_positionals.append(param); + m_params[name] = (CommonDef *)param; +} + +void Parser::addDocumentation(QString name, QString doc, QString metavar) +{ + if (!m_params.contains(name)) + throw "Name does not exist"; + + CommonDef *param = m_params[name]; + param->doc = doc; + if (!metavar.isNull()) + param->metavar = metavar; +} + +void Parser::addShortOpt(QString name, QChar flag) +{ + if (!m_params.contains(name)) + throw "Name does not exist"; + if (!m_options.contains(name)) + throw "Name is not an Option or Swtich"; + + OptionDef *param = m_options[name]; + m_flags[flag] = param; + param->flag = flag; +} + +// help methods +QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags) +{ + QStringList help; + help << compileUsage(progName, useFlags) << "\r\n"; + + // positionals + if (!m_positionals.isEmpty()) + { + help << "\r\n"; + help << "Positional arguments:\r\n"; + QListIterator<PositionalDef *> it2(m_positionals); + while(it2.hasNext()) + { + PositionalDef *param = it2.next(); + help << " " << param->metavar; + help << " " << QString(helpIndent - param->metavar.length() - 1, ' '); + help << param->doc << "\r\n"; + } + } + + // Options + if (!m_optionList.isEmpty()) + { + help << "\r\n"; + QString optPrefix, flagPrefix; + getPrefix(optPrefix, flagPrefix); + + help << "Options & Switches:\r\n"; + QListIterator<OptionDef *> it(m_optionList); + while(it.hasNext()) + { + OptionDef *option = it.next(); + help << " "; + int nameLength = optPrefix.length() + option->name.length(); + if (!option->flag.isNull()) + { + nameLength += 3 + flagPrefix.length(); + help << flagPrefix << option->flag << ", "; + } + help << optPrefix << option->name; + if (option->type == otOption) + { + QString arg = QString("%1%2").arg(((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar); + nameLength += arg.length(); + help << arg; + } + help << " " << QString(helpIndent - nameLength - 1, ' '); + help << option->doc << "\r\n"; + } + } + + return help.join(""); +} + +QString Parser::compileUsage(QString progName, bool useFlags) +{ + QStringList usage; + usage << "Usage: " << progName; + + QString optPrefix, flagPrefix; + getPrefix(optPrefix, flagPrefix); + + // options + QListIterator<OptionDef *> it(m_optionList); + while(it.hasNext()) + { + OptionDef *option = it.next(); + usage << " ["; + if (!option->flag.isNull() && useFlags) + usage << flagPrefix << option->flag; + else + usage << optPrefix << option->name; + if (option->type == otOption) + usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar; + usage << "]"; + } + + // arguments + QListIterator<PositionalDef *> it2(m_positionals); + while(it2.hasNext()) + { + PositionalDef *param = it2.next(); + usage << " " << (param->required ? "<" : "["); + usage << param->metavar; + usage << (param->required ? ">" : "]"); + } + + return usage.join(""); +} + +// parsing +QHash<QString, QVariant> Parser::parse(QStringList argv) +{ + QHash<QString, QVariant> map; + + QStringListIterator it(argv); + QString programName = it.next(); + + QString optionPrefix; + QString flagPrefix; + QListIterator<PositionalDef *> positionals(m_positionals); + QStringList expecting; + + getPrefix(optionPrefix, flagPrefix); + + while (it.hasNext()) + { + QString arg = it.next(); + + if (!expecting.isEmpty()) + // we were expecting an argument + { + QString name = expecting.first(); + + if (map.contains(name)) + throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix)); + + map[name] = QVariant(arg); + + expecting.removeFirst(); + continue; + } + + if (arg.startsWith(optionPrefix)) + // we have an option + { + //qDebug("Found option %s", qPrintable(arg)); + + QString name = arg.mid(optionPrefix.length()); + QString equals; + + if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && name.contains("=")) + { + int i = name.indexOf("="); + equals = name.mid(i+1); + name = name.left(i); + } + + if (m_options.contains(name)) + { + if (map.contains(name)) + throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix)); + + OptionDef *option = m_options[name]; + if (option->type == otSwitch) + map[name] = true; + else //if (option->type == otOption) + { + if (m_argStyle == ArgumentStyle::Space) + expecting.append(name); + else if (!equals.isNull()) + map[name] = equals; + else if (m_argStyle == ArgumentStyle::SpaceAndEquals) + expecting.append(name); + else + throw ParsingError(QString("Option %2%1 reqires an argument.").arg(name, optionPrefix)); + } + + continue; + } + + throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix)); + } + + if (arg.startsWith(flagPrefix)) + // we have (a) flag(s) + { + //qDebug("Found flags %s", qPrintable(arg)); + + QString flags = arg.mid(flagPrefix.length()); + QString equals; + + if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && flags.contains("=")) + { + int i = flags.indexOf("="); + equals = flags.mid(i+1); + flags = flags.left(i); + } + + for (int i = 0; i < flags.length(); i++) + { + QChar flag = flags.at(i); + + if (!m_flags.contains(flag)) + throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix)); + + OptionDef *option = m_flags[flag]; + + if (map.contains(option->name)) + throw ParsingError(QString("Option %2%1 was given multiple times").arg(option->name, optionPrefix)); + + if (option->type == otSwitch) + map[option->name] = true; + else //if (option->type == otOption) + { + if (m_argStyle == ArgumentStyle::Space) + expecting.append(option->name); + else if (!equals.isNull()) + if (i == flags.length()-1) + map[option->name] = equals; + else + throw ParsingError(QString("Flag %4%2 of Argument-requiring Option %1 not last flag in %4%3").arg(option->name, flag, flags, flagPrefix)); + else if (m_argStyle == ArgumentStyle::SpaceAndEquals) + expecting.append(option->name); + else + throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)").arg(option->name, flag, flagPrefix)); + } + } + + continue; + } + + // must be a positional argument + if (!positionals.hasNext()) + throw ParsingError(QString("Don't know what to do with '%1'").arg(arg)); + + PositionalDef *param = positionals.next(); + + map[param->name] = arg; + } + + // check if we're missing something + if (!expecting.isEmpty()) + throw ParsingError(QString("Was still expecting arguments for %2%1").arg(expecting.join(QString(", ")+optionPrefix), optionPrefix)); + + while (positionals.hasNext()) + { + PositionalDef *param = positionals.next(); + if (param->required) + throw ParsingError(QString("Missing required positional argument '%1'").arg(param->name)); + else + map[param->name] = param->def; + } + + // fill out gaps + QListIterator<OptionDef *> iter(m_optionList); + while (iter.hasNext()) + { + OptionDef *option = iter.next(); + if (!map.contains(option->name)) + map[option->name] = option->def; + } + + return map; +} + +//clear defs +void Parser::clear() +{ + m_flags.clear(); + m_params.clear(); + m_options.clear(); + + QMutableListIterator<OptionDef *> it(m_optionList); + while(it.hasNext()) + { + OptionDef *option = it.next(); + it.remove(); + delete option; + } + + QMutableListIterator<PositionalDef *> it2(m_positionals); + while(it2.hasNext()) + { + PositionalDef *arg = it2.next(); + it2.remove(); + delete arg; + } +} + +//Destructor +Parser::~Parser() +{ + clear(); +} + +//getPrefix +void Parser::getPrefix(QString &opt, QString &flag) +{ + if (m_flagStyle == FlagStyle::Windows) + opt = flag = "/"; + else if (m_flagStyle == FlagStyle::Unix) + opt = flag = "-"; + //else if (m_flagStyle == FlagStyle::GNU) + else { + opt = "--"; + flag = "-"; + } +} + +// ParsingError +ParsingError::ParsingError(const QString &what) +{ + m_what = what; +} +ParsingError::ParsingError(const ParsingError &e) +{ + m_what = e.m_what; +} + +const char *ParsingError::what() const throw() +{ + return m_what.toLocal8Bit().constData(); +} +QString ParsingError::qwhat() const +{ + return m_what; +} + +} +} diff --git a/depends/util/src/osutils.cpp b/depends/util/src/osutils.cpp new file mode 100644 index 00000000..9a85d1e5 --- /dev/null +++ b/depends/util/src/osutils.cpp @@ -0,0 +1,19 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/osutils.h" + +#include <QUrl> +#include <QFileInfo> diff --git a/depends/util/src/pathutils.cpp b/depends/util/src/pathutils.cpp new file mode 100644 index 00000000..97287840 --- /dev/null +++ b/depends/util/src/pathutils.cpp @@ -0,0 +1,94 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/pathutils.h" + +#include <QFileInfo> +#include <QDir> + +QString PathCombine(QString path1, QString path2) +{ + if (!path1.endsWith('/')) + return path1.append('/').append(path2); + else + return path1.append(path2); +} + +QString PathCombine(QString path1, QString path2, QString path3) +{ + return PathCombine(PathCombine(path1, path2), path3); +} + +QString AbsolutePath(QString path) +{ + return QFileInfo(path).absolutePath(); +} + +QString badFilenameChars = "\"\\/?<>:*|!"; + +QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) +{ + for (int i = 0; i < string.length(); i++) + { + if (badFilenameChars.contains(string[i])) + { + string[i] = replaceWith; + } + } + return string; +} + +QString DirNameFromString(QString string, QString inDir) +{ + int num = 0; + QString dirName = RemoveInvalidFilenameChars(string, '-'); + while (QFileInfo(PathCombine(inDir, dirName)).exists()) + { + num++; + dirName = RemoveInvalidFilenameChars(dirName, '-') + QString::number(num); + + // If it's over 9000 + if (num > 9000) + return ""; + } + return dirName; +} + +bool ensurePathExists(QString filenamepath) +{ + QFileInfo a ( filenamepath ); + QDir dir; + return (dir.mkpath ( a.path() )); +} + +bool copyPath(QString src, QString dst) +{ + QDir dir(src); + if (!dir.exists()) + return false; + + foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + { + QString dst_path = dst + QDir::separator() + d; + dir.mkpath(dst_path); + copyPath(src+ QDir::separator() + d, dst_path); + } + + foreach (QString f, dir.entryList(QDir::Files)) + { + QFile::copy(src + QDir::separator() + f, dst + QDir::separator() + f); + } + return true; +} diff --git a/depends/util/src/userutils.cpp b/depends/util/src/userutils.cpp new file mode 100644 index 00000000..b70841ed --- /dev/null +++ b/depends/util/src/userutils.cpp @@ -0,0 +1,123 @@ +#include "include/userutils.h" + +#include <QStandardPaths> +#include <QFile> +#include <QTextStream> + +#include "include/osutils.h" +#include "include/pathutils.h" + +// Win32 crap +#if WINDOWS + +#include <windows.h> +#include <winnls.h> +#include <shobjidl.h> +#include <objbase.h> +#include <objidl.h> +#include <shlguid.h> +#include <shlobj.h> + +bool called_coinit = false; + +HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args) +{ + HRESULT hres; + + if (!called_coinit) + { + hres = CoInitialize(NULL); + called_coinit = true; + + if (!SUCCEEDED(hres)) + { + qWarning("Failed to initialize COM. Error 0x%08X", hres); + return hres; + } + } + + + IShellLink* link; + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&link); + + if (SUCCEEDED(hres)) + { + IPersistFile* persistFile; + + link->SetPath(targetPath); + link->SetArguments(args); + + hres = link->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile); + if (SUCCEEDED(hres)) + { + WCHAR wstr[MAX_PATH]; + + MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH); + + hres = persistFile->Save(wstr, TRUE); + persistFile->Release(); + } + link->Release(); + } + return hres; +} + +#endif + +QString Util::getDesktopDir() +{ + return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +} + +// Cross-platform Shortcut creation +bool Util::createShortCut(QString location, QString dest, QStringList args, QString name, QString icon) +{ +#if LINUX + location = PathCombine(location, name + ".desktop"); + qDebug("location: %s", qPrintable(location)); + + QFile f(location); + f.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream stream(&f); + + QString argstring; + if (!args.empty()) + argstring = " '" + args.join("' '") + "'"; + + stream << "[Desktop Entry]" << "\n"; + stream << "Type=Application" << "\n"; + stream << "TryExec=" << dest.toLocal8Bit() << "\n"; + stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; + stream << "Name=" << name.toLocal8Bit() << "\n"; + stream << "Icon=" << icon.toLocal8Bit() << "\n"; + + stream.flush(); + f.close(); + + f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); + + return true; +#elif WINDOWS + // TODO: Fix +// QFile file(PathCombine(location, name + ".lnk")); +// WCHAR *file_w; +// WCHAR *dest_w; +// WCHAR *args_w; +// file.fileName().toWCharArray(file_w); +// dest.toWCharArray(dest_w); + +// QString argStr; +// for (int i = 0; i < args.count(); i++) +// { +// argStr.append(args[i]); +// argStr.append(" "); +// } +// argStr.toWCharArray(args_w); + +// return SUCCEEDED(CreateLink(file_w, dest_w, args_w)); + return false; +#else + qWarning("Desktop Shortcuts not supported on your platform!"); + return false; +#endif +} |