Merged PR 945937: Merge psmith to feature
This commit is contained in:
Родитель
0a54ba1b46
Коммит
56712c5d65
|
@ -19,9 +19,9 @@
|
|||
7B5F64B61F7F824B009387FB /* StreamBase.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 7B5F64A61F7F824B009387FB /* StreamBase.hpp */; };
|
||||
7B5F64B71F7F824B009387FB /* xPlatAppx.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 7B5F64A71F7F824B009387FB /* xPlatAppx.hpp */; };
|
||||
7B5F64B81F7F824B009387FB /* ZipFileStream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 7B5F64A81F7F824B009387FB /* ZipFileStream.hpp */; };
|
||||
7B5F64B91F7F824B009387FB /* ZipStream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 7B5F64A91F7F824B009387FB /* ZipStream.hpp */; };
|
||||
7B5F64BA1F7F824B009387FB /* xPlatAppx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B5F64AB1F7F824B009387FB /* xPlatAppx.cpp */; };
|
||||
7B5F64BB1F7F824B009387FB /* ZipStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B5F64AC1F7F824B009387FB /* ZipStream.cpp */; };
|
||||
EE2E65D71F833E2D0070D4E4 /* ZipObject.hpp in Headers */ = {isa = PBXBuildFile; fileRef = EE2E65D61F833E2D0070D4E4 /* ZipObject.hpp */; };
|
||||
EE2E65D91F833E460070D4E4 /* ZipObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EE2E65D81F833E460070D4E4 /* ZipObject.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -38,9 +38,9 @@
|
|||
7B5F64A61F7F824B009387FB /* StreamBase.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StreamBase.hpp; sourceTree = "<group>"; };
|
||||
7B5F64A71F7F824B009387FB /* xPlatAppx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = xPlatAppx.hpp; sourceTree = "<group>"; };
|
||||
7B5F64A81F7F824B009387FB /* ZipFileStream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZipFileStream.hpp; sourceTree = "<group>"; };
|
||||
7B5F64A91F7F824B009387FB /* ZipStream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZipStream.hpp; sourceTree = "<group>"; };
|
||||
7B5F64AB1F7F824B009387FB /* xPlatAppx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xPlatAppx.cpp; sourceTree = "<group>"; };
|
||||
7B5F64AC1F7F824B009387FB /* ZipStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZipStream.cpp; sourceTree = "<group>"; };
|
||||
EE2E65D61F833E2D0070D4E4 /* ZipObject.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZipObject.hpp; sourceTree = "<group>"; };
|
||||
EE2E65D81F833E460070D4E4 /* ZipObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZipObject.cpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -82,6 +82,7 @@
|
|||
7B5F649C1F7F824B009387FB /* inc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EE2E65D61F833E2D0070D4E4 /* ZipObject.hpp */,
|
||||
7B5F649D1F7F824B009387FB /* Base64Stream.hpp */,
|
||||
7B5F649E1F7F824B009387FB /* CRC32Stream.hpp */,
|
||||
7B5F649F1F7F824B009387FB /* Exceptions.hpp */,
|
||||
|
@ -94,7 +95,6 @@
|
|||
7B5F64A61F7F824B009387FB /* StreamBase.hpp */,
|
||||
7B5F64A71F7F824B009387FB /* xPlatAppx.hpp */,
|
||||
7B5F64A81F7F824B009387FB /* ZipFileStream.hpp */,
|
||||
7B5F64A91F7F824B009387FB /* ZipStream.hpp */,
|
||||
);
|
||||
name = inc;
|
||||
path = ../../src/inc;
|
||||
|
@ -103,8 +103,8 @@
|
|||
7B5F64AA1F7F824B009387FB /* xPlatAppx */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EE2E65D81F833E460070D4E4 /* ZipObject.cpp */,
|
||||
7B5F64AB1F7F824B009387FB /* xPlatAppx.cpp */,
|
||||
7B5F64AC1F7F824B009387FB /* ZipStream.cpp */,
|
||||
);
|
||||
name = xPlatAppx;
|
||||
path = ../../src/xPlatAppx;
|
||||
|
@ -120,8 +120,8 @@
|
|||
7B5F64B51F7F824B009387FB /* SHA256Stream.hpp in Headers */,
|
||||
7B5F64B01F7F824B009387FB /* FileStream.hpp in Headers */,
|
||||
7B5F64AF1F7F824B009387FB /* Exceptions.hpp in Headers */,
|
||||
EE2E65D71F833E2D0070D4E4 /* ZipObject.hpp in Headers */,
|
||||
7B5F64B71F7F824B009387FB /* xPlatAppx.hpp in Headers */,
|
||||
7B5F64B91F7F824B009387FB /* ZipStream.hpp in Headers */,
|
||||
7B5F64AE1F7F824B009387FB /* CRC32Stream.hpp in Headers */,
|
||||
7B5F64B21F7F824B009387FB /* OffsetStream.hpp in Headers */,
|
||||
7B5F64B81F7F824B009387FB /* ZipFileStream.hpp in Headers */,
|
||||
|
@ -190,8 +190,8 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
EE2E65D91F833E460070D4E4 /* ZipObject.cpp in Sources */,
|
||||
7B5F64BA1F7F824B009387FB /* xPlatAppx.cpp in Sources */,
|
||||
7B5F64BB1F7F824B009387FB /* ZipStream.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -290,6 +290,7 @@
|
|||
7B5F64961F7F807B009387FB /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
|
@ -303,6 +304,7 @@
|
|||
7B5F64971F7F807B009387FB /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
|
|
|
@ -4,144 +4,117 @@
|
|||
#include <vector>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <initializer_list>
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
namespace Meta {
|
||||
|
||||
// A uniary type with Read, Write, Validate, and Size
|
||||
class Object
|
||||
// Represents a heterogeneous collection of types that can be operated on as a compile-time vector
|
||||
template <typename... Types>
|
||||
class TypeList
|
||||
{
|
||||
std::tuple<Types...> fields;
|
||||
static constexpr std::size_t last_index{ std::tuple_size<std::tuple<Types...>>::value };
|
||||
|
||||
public:
|
||||
Object(void* value) : v(value) {}
|
||||
virtual ~Object() { }
|
||||
template<std::size_t index = 0, typename FuncT>
|
||||
inline typename std::enable_if<index == last_index, void>::type for_each(FuncT)
|
||||
{ }
|
||||
|
||||
virtual void Write(StreamBase* stream) = 0;
|
||||
virtual void Read(StreamBase* stream) = 0;
|
||||
virtual void Validate() = 0;
|
||||
virtual size_t Size() = 0;
|
||||
|
||||
template <class T>
|
||||
static T* GetValue(Object* o)
|
||||
template<std::size_t index = 0, typename FuncT>
|
||||
inline typename std::enable_if<index < last_index, void>::type for_each(FuncT f)
|
||||
{
|
||||
return reinterpret_cast<T*>(o->v);
|
||||
f(Field<index>());
|
||||
for_each<index + 1, FuncT>(f);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void SetValue(Object* o, T& value)
|
||||
{
|
||||
*reinterpret_cast<T*>(o->v) = value;
|
||||
}
|
||||
|
||||
protected:
|
||||
void* value() { return v; }
|
||||
void* v = nullptr;
|
||||
template <size_t index>
|
||||
inline auto& Field() { return std::get<index>(fields); }
|
||||
};
|
||||
|
||||
// Aggregates a collection of objects
|
||||
// validation is handled incrementally via Read
|
||||
// size is summation of size of all fields.
|
||||
class StructuredObject : public Object
|
||||
// Defines set of operations (size, write, read) for a serializable/deserializable type.
|
||||
template <class... Types>
|
||||
class StructuredObject : public TypeList<Types...>
|
||||
{
|
||||
public:
|
||||
StructuredObject(std::initializer_list<std::shared_ptr<Object>> list) : Object(&fields), fields(list) { }
|
||||
|
||||
virtual void Write(StreamBase* stream) override
|
||||
{
|
||||
std::for_each(fields.begin(), fields.end(), [&](auto field) { field->Write(stream); });
|
||||
}
|
||||
|
||||
virtual void Read(StreamBase* stream) override
|
||||
{
|
||||
std::for_each(fields.begin(), fields.end(), [&](auto field)
|
||||
{
|
||||
field->Read(stream);
|
||||
field->Validate();
|
||||
});
|
||||
}
|
||||
|
||||
virtual void Validate() override {}
|
||||
|
||||
virtual size_t Size() override
|
||||
size_t Size()
|
||||
{
|
||||
size_t result = 0;
|
||||
std::for_each(fields.begin(), fields.end(), [&](auto field) { result += field->Size(); });
|
||||
this->for_each([&](auto& item)
|
||||
{
|
||||
result += item.Size();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Object* Field(size_t index) { return fields[index].get(); }
|
||||
void Write(StreamBase* stream)
|
||||
{
|
||||
offset = stream->Ftell();
|
||||
this->for_each([&](auto& item)
|
||||
{
|
||||
item.Write(stream);
|
||||
});
|
||||
}
|
||||
|
||||
void Read(StreamBase* stream)
|
||||
{
|
||||
offset = stream->Ftell();
|
||||
this->for_each([&](auto& item)
|
||||
{
|
||||
item.Read(stream);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void Validate() { }
|
||||
|
||||
protected:
|
||||
std::vector<std::shared_ptr<Object>> fields;
|
||||
std::uint64_t offset = 0; // For debugging purposes!
|
||||
};
|
||||
|
||||
// base type for serializable fields
|
||||
// base type for individual serializable/deserializable fields
|
||||
template <class T>
|
||||
class FieldBase : public Object
|
||||
class FieldBase
|
||||
{
|
||||
public:
|
||||
using Lambda = std::function<void(T& v)>;
|
||||
|
||||
FieldBase(Lambda validator) : Object(&value), validate(validator) {}
|
||||
|
||||
virtual T& GetValue() { return value; }
|
||||
virtual void SetValue(T& v) { value = v; }
|
||||
|
||||
virtual void Write(StreamBase* stream) override
|
||||
FieldBase()
|
||||
{
|
||||
validation = [](T&){}; // empty validation by-default.
|
||||
}
|
||||
|
||||
virtual void Write(StreamBase* stream)
|
||||
{
|
||||
offset = stream->Ftell();
|
||||
StreamBase::Write<T>(stream, &value);
|
||||
}
|
||||
|
||||
virtual void Read(StreamBase* stream) override
|
||||
virtual void Read(StreamBase* stream)
|
||||
{
|
||||
offset = stream->Ftell();
|
||||
StreamBase::Read<T>(stream, &value);
|
||||
Validate();
|
||||
}
|
||||
|
||||
void Validate() override { validate(GetValue()); }
|
||||
virtual void Validate() { validation(this->value); }
|
||||
virtual size_t Size() { return sizeof(T); }
|
||||
|
||||
virtual size_t Size() override { return sizeof(T); }
|
||||
|
||||
protected:
|
||||
std::uint64_t offset = 0; // For debugging purposes!
|
||||
T value;
|
||||
Lambda validate;
|
||||
T value;
|
||||
Lambda validation;
|
||||
};
|
||||
|
||||
// 2 byte field
|
||||
class Field2Bytes : public FieldBase<std::uint16_t>
|
||||
{
|
||||
public:
|
||||
Field2Bytes(Lambda&& validator) : FieldBase<std::uint16_t>(validator) {}
|
||||
};
|
||||
|
||||
// 4 byte field
|
||||
class Field4Bytes : public FieldBase<std::uint32_t>
|
||||
{
|
||||
public:
|
||||
Field4Bytes(Lambda&& validator) : FieldBase<std::uint32_t>(validator) {}
|
||||
};
|
||||
|
||||
// 8 byte field
|
||||
class Field8Bytes : public FieldBase<std::uint64_t>
|
||||
{
|
||||
public:
|
||||
Field8Bytes(Lambda&& validator) : FieldBase<std::uint64_t>(validator) {}
|
||||
};
|
||||
class Field2Bytes : public FieldBase<std::uint16_t> { };
|
||||
class Field4Bytes : public FieldBase<std::uint32_t> { };
|
||||
class Field8Bytes : public FieldBase<std::uint64_t> { };
|
||||
|
||||
// variable length field.
|
||||
class FieldNBytes : public Object
|
||||
class FieldNBytes : public FieldBase<std::vector<std::uint8_t>>
|
||||
{
|
||||
public:
|
||||
using Lambda = std::function<void(std::vector<std::uint8_t>& v)>;
|
||||
FieldNBytes(Lambda validator) : Object(&value), validate(validator) {}
|
||||
|
||||
size_t Size() override { return value.size(); }
|
||||
virtual size_t Size() override { return value.size(); }
|
||||
virtual void Validate() override { }
|
||||
|
||||
virtual void Write(StreamBase* stream) override
|
||||
{
|
||||
|
@ -153,12 +126,6 @@ namespace xPlat {
|
|||
stream->Read(Size(), value.data());
|
||||
Validate();
|
||||
}
|
||||
|
||||
void Validate() override { validate(value); }
|
||||
|
||||
protected:
|
||||
std::vector<std::uint8_t> value;
|
||||
Lambda validate;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,14 +34,14 @@ namespace xPlat {
|
|||
template <class T>
|
||||
static void Read(StreamBase* stream, T* value)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value, "specified value type must be both trivial and standard-layout");
|
||||
//static_assert(std::is_pod<T>::value, "specified value type must be both trivial and standard-layout");
|
||||
stream->Read(sizeof(T), reinterpret_cast<std::uint8_t*>(const_cast<T*>(value)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void Write(StreamBase* stream, T* value)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value, "specified value type must be both trivial and standard-layout");
|
||||
//static_assert(std::is_pod<T>::value, "specified value type must be both trivial and standard-layout");
|
||||
stream->Write(sizeof(T), reinterpret_cast<std::uint8_t*>(const_cast<T*>(value)));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,30 @@ namespace xPlat {
|
|||
{
|
||||
public:
|
||||
// TODO: define what streams to pass in on the .ctor
|
||||
ZipFileStream(std::string&& fileName) : fileName(std::move(fileName))
|
||||
ZipFileStream(
|
||||
std::string&& fileName,
|
||||
std::uint32_t offset,
|
||||
std::uint32_t compressedSize,
|
||||
std::uint32_t uncompressedSize,
|
||||
bool isCompressed
|
||||
) :
|
||||
m_fileName(std::move(fileName)),
|
||||
m_offset(offset),
|
||||
m_compressedSize(compressedSize),
|
||||
m_uncompressedSize(uncompressedSize),
|
||||
m_isCompressed(isCompressed)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string fileName;
|
||||
std::string m_fileName;
|
||||
|
||||
// TODO: change to uint64_t when adding 4+GB support
|
||||
std::uint32_t m_offset;
|
||||
std::uint32_t m_compressedSize;
|
||||
std::uint32_t m_uncompressedSize;
|
||||
|
||||
bool m_isCompressed = false;
|
||||
std::uint64_t m_relativePosition = 0;
|
||||
};
|
||||
}
|
|
@ -23,296 +23,34 @@ namespace xPlat {
|
|||
InvalidZip64CentralDirectoryRecord = 5,
|
||||
InvalidCentralDirectoryHeader = 6,
|
||||
HiddenDataBetweenLastCDHandEoCD = 7,
|
||||
InvalidLocalFileHeader = 8,
|
||||
};
|
||||
|
||||
ZipException(std::string message, Error error) : ExceptionBase(ExceptionBase::Facility::ZIP), reason(message)
|
||||
ZipException(std::string message, Error error) : reason(message), ExceptionBase(ExceptionBase::Facility::ZIP)
|
||||
{
|
||||
SetLastError(static_cast<std::uint32_t>(error));
|
||||
}
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
enum class ZipVersions : std::uint16_t
|
||||
{
|
||||
Zip32DefaultVersion = 20,
|
||||
Zip64FormatExtension = 45,
|
||||
};
|
||||
|
||||
// from ZIP file format specification detailed in AppNote.txt
|
||||
enum class Signatures : std::uint32_t
|
||||
{
|
||||
LocalFileHeader = 0x04034b50,
|
||||
DataDescriptor = 0x08074b50,
|
||||
CentralFileHeader = 0x02014b50,
|
||||
Zip64EndOfCD = 0x06064b50,
|
||||
Zip64EndOfCDLocator = 0x07064b50,
|
||||
EndOfCentralDirectory = 0x06054b50,
|
||||
};
|
||||
|
||||
enum class CompressionType : std::uint16_t
|
||||
{
|
||||
Store = 0,
|
||||
Deflate = 8,
|
||||
};
|
||||
|
||||
// Hat tip to the people at Facebook. Timestamp for files in ZIP archive
|
||||
// format held constant to make pack/unpack deterministic
|
||||
enum class MagicNumbers : std::uint16_t
|
||||
{
|
||||
FileTime = 0x6B60, // kudos to those know this
|
||||
FileDate = 0xA2B1, // :)
|
||||
};
|
||||
|
||||
enum class GeneralPurposeBitFlags : std::uint16_t
|
||||
{
|
||||
UNSUPPORTED_0 = 0x0001, // Bit 0: If set, indicates that the file is encrypted.
|
||||
|
||||
Deflate_MaxCompress = 0x0002, // Maximum compression (-exx/-ex), otherwise, normal compression (-en)
|
||||
Deflate_FastCompress = 0x0004, // Fast (-ef), if Max+Fast then SuperFast (-es) compression
|
||||
|
||||
CRC32_SizesZero = 0x0008, // the field's crc-32 compressed and uncompressed sizes = 0 in the local header
|
||||
// the correct values are put in the data descriptor immediately following the
|
||||
// compressed data.
|
||||
EnhancedDeflate = 0x0010,
|
||||
CompressedPatchedData = 0x0020,
|
||||
UNSUPPORTED_6 = 0x0040, // Strong encryption.
|
||||
|
||||
UnUsed_7 = 0x0080, // currently unused
|
||||
UnUsed_8 = 0x0100, // currently unused
|
||||
UnUsed_9 = 0x0200, // currently unused
|
||||
UnUsed_10 = 0x0400, // currently unused
|
||||
|
||||
EncodingMustUseUTF8 = 0x0800, // Language encoding flag (EFS). File name and comments fields MUST be encoded UTF-8
|
||||
|
||||
UNSUPPORTED_12 = 0x1000, // Reserved by PKWARE for enhanced compression
|
||||
UNSUPPORTED_13 = 0x2000, // Set when encrypting the Central Directory
|
||||
UNSUPPORTED_14 = 0x4000, // Reserved by PKWARE
|
||||
UNSUPPORTED_15 = 0x8000, // Reserved by PKWARE
|
||||
};
|
||||
|
||||
inline constexpr GeneralPurposeBitFlags operator &(GeneralPurposeBitFlags a, GeneralPurposeBitFlags b)
|
||||
{ return static_cast<GeneralPurposeBitFlags>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
inline constexpr GeneralPurposeBitFlags operator |(GeneralPurposeBitFlags a, GeneralPurposeBitFlags b)
|
||||
{ return static_cast<GeneralPurposeBitFlags>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
// if any of these are set, then fail.
|
||||
constexpr static const GeneralPurposeBitFlags UnsupportedFlagsMask =
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_0 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_6 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_12 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_13 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_14 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_15;
|
||||
|
||||
class CentralDirectoryFileHeader : public Meta::StructuredObject
|
||||
{
|
||||
/* TODO: Implement large file support.
|
||||
This type currently represents a "zip32" Central Directory Header. We need to refactor this into 3 types:
|
||||
a "header" type that reads all the way down to field 7 (CRC-32) as-is, but that replaces fields 8 & 9 with a
|
||||
new meta object type (ambiguous?) whose implementation is determined by the version needed to extract (field 2)'s
|
||||
value.
|
||||
|
||||
This type would replace its existing compressed & uncompressed sizes properties (encapsulated in fields 8 & 9)
|
||||
with a 64-bit value version of those methods:
|
||||
std::uint64_t GetCompressedSize() { ...
|
||||
std::uint64_t GetUncompressedSize() { ...
|
||||
void SetCompressedSize (std::uint64_t...
|
||||
void SetUncompressedSize(std::uint64_t...
|
||||
|
||||
The underlying implementation of these methods would validate the resulting in/out values and pass the correct
|
||||
static_casted value to the new meta object type (ambiguous?) which would then hold the correct value, as well
|
||||
as handle the correct sizes w.r.t. (de)serialization.
|
||||
|
||||
As-is I don't believe that we "need" this for now, so keeping the implementation "simpler" is probably the correct
|
||||
answer for now.
|
||||
*/
|
||||
public:
|
||||
virtual ~CentralDirectoryFileHeader() {}
|
||||
|
||||
CentralDirectoryFileHeader(StreamBase* s) : Meta::StructuredObject(
|
||||
{
|
||||
// 0 - central file header signature 4 bytes(0x02014b50)
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::CentralFileHeader))
|
||||
{ throw ZipException("signature mismatch", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
// 1 - version made by 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if (v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
{ throw ZipException("unsupported version made by", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
// 2 - version needed to extract 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion)) &&
|
||||
(v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
)
|
||||
{ throw ZipException("unsupported version needed to extract", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
// 3 - general purpose bit flag 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if ((v & static_cast<std::uint16_t>(UnsupportedFlagsMask)) != 0)
|
||||
{ throw ZipException("unsupported flag(s) specified", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
// 4 - compression method 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(CompressionType::Store)) &&
|
||||
(v != static_cast<std::uint16_t>(CompressionType::Deflate))
|
||||
)
|
||||
{ throw ZipException("unsupported compression method", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
// 5 - last mod file time 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 6 - last mod file date 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 7 - crc - 32 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([&](std::uint32_t& v) {}),
|
||||
// 8 - compressed size 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {}),
|
||||
// 9 - uncompressed size 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {}),
|
||||
//10 - file name length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
{ if (v > std::numeric_limits<std::uint16_t>::max())
|
||||
{ throw ZipException("file name exceeds max size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(17))->resize(v, 0);
|
||||
}),
|
||||
//11 - extra field length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
{ if (v > std::numeric_limits<std::uint16_t>::max())
|
||||
{ throw ZipException("file name exceeds max size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(18))->resize(v, 0);
|
||||
}),
|
||||
//12 - file comment length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
{ if (v > std::numeric_limits<std::uint16_t>::max())
|
||||
{ throw ZipException("file comment exceeds max size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(19))->resize(v, 0);
|
||||
}),
|
||||
//13 - disk number start 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported disk number start", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
//14 - internal file attributes 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported internal file attributes", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
//15 - external file attributes 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
{ if (v != 0)
|
||||
{ //throw ZipException("unsupported external file attributes", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
//16 - relative offset of local header 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([&](std::uint32_t& v)
|
||||
{ if (v >= m_stream->Ftell())
|
||||
{ throw ZipException("invalid relative offset", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
}),
|
||||
//17 - file name(variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data) {}),
|
||||
//18 - extra field(variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data) {}),
|
||||
//19 - file comment(variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data) {})
|
||||
}), m_stream(s)
|
||||
{/*constructor*/
|
||||
SetSignature(static_cast<std::uint32_t>(Signatures::CentralFileHeader));
|
||||
SetVersionMadeBy(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
|
||||
// only set to Zip64FormatExtension iff required!
|
||||
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion));
|
||||
SetLastModFileDate(static_cast<std::uint16_t>(MagicNumbers::FileDate));
|
||||
SetLastModFileTime(static_cast<std::uint16_t>(MagicNumbers::FileTime));
|
||||
SetExtraFieldLength(0);
|
||||
SetFileCommentLength(0);
|
||||
SetDiskNumberStart(0);
|
||||
SetInternalFileAttributes(0);
|
||||
SetExternalFileAttributes(0);
|
||||
}
|
||||
|
||||
std::uint16_t GetVersionNeededToExtract() { return *Meta::Object::GetValue<std::uint16_t>(Field(2)); }
|
||||
|
||||
GeneralPurposeBitFlags GetGeneralPurposeBitFlag() { return static_cast<GeneralPurposeBitFlags>(*Meta::Object::GetValue<std::uint16_t>(Field(3))); }
|
||||
void SetGeneralPurposeBitFlag(std::uint16_t value) { Meta::Object::SetValue(Field(3), value); }
|
||||
|
||||
std::uint16_t GetCompressionMethod() { return *Meta::Object::GetValue<std::uint16_t>(Field(4)); }
|
||||
void SetCompressionMethod(std::uint16_t value) { Meta::Object::SetValue(Field(4), value); }
|
||||
|
||||
std::uint32_t GetCrc32() { return *Meta::Object::GetValue<std::uint32_t>(Field(7)); }
|
||||
void SetCrc(std::uint32_t value) { Meta::Object::SetValue(Field(7), value); }
|
||||
|
||||
std::uint32_t GetCompressedSize() { return *Meta::Object::GetValue<std::uint32_t>(Field(8)); }
|
||||
void SetCompressedSize(std::uint32_t value) { Meta::Object::SetValue(Field(8), value); }
|
||||
|
||||
std::uint32_t GetUncompressedSize() { return *Meta::Object::GetValue<std::uint32_t>(Field(9)); }
|
||||
void SetUncompressedSize(std::uint32_t value) { Meta::Object::SetValue(Field(9), value); }
|
||||
|
||||
std::uint32_t GetRelativeOffsetOfLocalHeader() { return *Meta::Object::GetValue<std::uint32_t>(Field(16)); }
|
||||
void SetRelativeOffsetOfLocalHeader(std::uint32_t value) { Meta::Object::SetValue(Field(16), value); }
|
||||
|
||||
std::string GetFileName()
|
||||
{
|
||||
auto data = Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(17));
|
||||
return std::string(data->begin(), data->end());
|
||||
}
|
||||
|
||||
void SetFileName(std::string name)
|
||||
{
|
||||
auto data = Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(17));
|
||||
data->resize(name.size());
|
||||
data->assign(name.begin(), name.end());
|
||||
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
|
||||
}
|
||||
|
||||
private:
|
||||
void SetSignature(std::uint32_t value) { Meta::Object::SetValue(Field(0), value); }
|
||||
void SetVersionMadeBy(std::uint16_t value) { Meta::Object::SetValue(Field(1), value); }
|
||||
void SetVersionNeededToExtract(std::uint16_t value) { Meta::Object::SetValue(Field(2), value); }
|
||||
|
||||
void SetLastModFileTime(std::uint16_t value) { Meta::Object::SetValue(Field(5), value); }
|
||||
void SetLastModFileDate(std::uint16_t value) { Meta::Object::SetValue(Field(6), value); }
|
||||
|
||||
void SetFileNameLength(std::uint16_t value) { Meta::Object::SetValue(Field(10), value); }
|
||||
void SetExtraFieldLength(std::uint16_t value) { Meta::Object::SetValue(Field(11), value); }
|
||||
void SetFileCommentLength(std::uint16_t value) { Meta::Object::SetValue(Field(12), value); }
|
||||
void SetDiskNumberStart(std::uint16_t value) { Meta::Object::SetValue(Field(13), value); }
|
||||
void SetInternalFileAttributes(std::uint16_t value) { Meta::Object::SetValue(Field(14), value); }
|
||||
void SetExternalFileAttributes(std::uint16_t value) { Meta::Object::SetValue(Field(15), value); }
|
||||
|
||||
StreamBase* m_stream = nullptr;
|
||||
};//class CentralDirectoryFileHeader
|
||||
// forward declarations
|
||||
class CentralDirectoryFileHeader;
|
||||
class LocalFileHeader;
|
||||
|
||||
// This represents a raw stream over a.zip file.
|
||||
class ZipObject
|
||||
{
|
||||
public:
|
||||
ZipObject(StreamPtr&& stream) : m_stream(std::move(stream)) { }
|
||||
|
||||
void Read();
|
||||
ZipObject(StreamBase* stream);
|
||||
|
||||
std::vector<std::string> GetFileNames();
|
||||
|
||||
protected:
|
||||
StreamPtr m_stream;
|
||||
std::map<std::string, std::shared_ptr<ZipFileStream>> m_streams;
|
||||
std::map<std::string, std::shared_ptr<CentralDirectoryFileHeader>> m_centralDirectory;
|
||||
|
||||
// TODO: change to uint64_t when adding full zip64 support
|
||||
//std::map<std::uint32_t, std::shared_ptr<LocalFileHeader>> fileRepository;
|
||||
std::map<std::uint32_t, std::shared_ptr<LocalFileHeader>> m_fileRepository;
|
||||
|
||||
};//class ZipObject
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,87 +33,83 @@ namespace xPlat {
|
|||
[Zip64EndOfCentralDirectoryLocator]
|
||||
[EndCentralDirectoryRecord]
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class LocalFileHeader : public Meta::StructuredObject
|
||||
enum class ZipVersions : std::uint16_t
|
||||
{
|
||||
public:
|
||||
std::uint16_t GetFileNameLength() { return *Meta::Object::GetValue<std::uint16_t>(Field(9)); }
|
||||
void SetFileNameLength(std::uint16_t value) { Meta::Object::SetValue(Field(9), value); }
|
||||
std::uint16_t GetExtraFieldLength() { return *Meta::Object::GetValue<std::uint16_t>(Field(10)); }
|
||||
void SetExtraFieldLength(std::uint16_t value) { Meta::Object::SetValue(Field(10), value); }
|
||||
std::uint32_t GetCompressedSize() { return *Meta::Object::GetValue<std::uint32_t>(Field(7)); }
|
||||
void SetCompressedSize(std::uint32_t value) { Meta::Object::SetValue(Field(7), value); }
|
||||
std::uint32_t GetUncompressedSize() { return *Meta::Object::GetValue<std::uint32_t>(Field(8)); }
|
||||
void SetUncompressedSize(std::uint32_t value) { Meta::Object::SetValue(Field(8), value); }
|
||||
Zip32DefaultVersion = 20,
|
||||
Zip64FormatExtension = 45,
|
||||
};
|
||||
|
||||
std::string GetFileName() {
|
||||
auto data = *Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(11));
|
||||
return std::string(data.begin(), data.end());
|
||||
}
|
||||
// from ZIP file format specification detailed in AppNote.txt
|
||||
enum class Signatures : std::uint32_t
|
||||
{
|
||||
LocalFileHeader = 0x04034b50,
|
||||
DataDescriptor = 0x08074b50,
|
||||
CentralFileHeader = 0x02014b50,
|
||||
Zip64EndOfCD = 0x06064b50,
|
||||
Zip64EndOfCDLocator = 0x07064b50,
|
||||
EndOfCentralDirectory = 0x06054b50,
|
||||
};
|
||||
|
||||
void SetFileName(std::string name)
|
||||
{
|
||||
auto data = *Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(11));
|
||||
data.resize(name.size());
|
||||
data.assign(name.begin(), name.end());
|
||||
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
|
||||
}
|
||||
enum class CompressionType : std::uint16_t
|
||||
{
|
||||
Store = 0,
|
||||
Deflate = 8,
|
||||
};
|
||||
|
||||
LocalFileHeader() : Meta::StructuredObject(
|
||||
{
|
||||
// 0 - local file header signature 4 bytes(0x04034b50)
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
{
|
||||
if (v != static_cast<std::uint32_t>(Signatures::LocalFileHeader))
|
||||
{
|
||||
throw ZipException("file header does not match signature", ZipException::Error::InvalidHeader);
|
||||
}
|
||||
}),
|
||||
// 1 - version needed to extract 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 2 - general purpose bit flag 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 3 - compression method 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 4 - last mod file time 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 5 - last mod file date 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v) {}),
|
||||
// 6 - crc - 32 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {}),
|
||||
// 7 - compressed size 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {}),
|
||||
// 8 - uncompressed size 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {}),
|
||||
// 9 - file name length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
{
|
||||
if (GetFileNameLength() > std::numeric_limits<std::uint16_t>::max())
|
||||
{
|
||||
throw ZipException("file name field exceeds max size", ZipException::Error::FieldOutOfRange);
|
||||
}
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(11))->resize(GetFileNameLength(), 0);
|
||||
}),
|
||||
// 10- extra field length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
{
|
||||
if (GetExtraFieldLength() > std::numeric_limits<std::uint16_t>::max())
|
||||
{
|
||||
throw ZipException("extra field exceeds max size", ZipException::Error::FieldOutOfRange);
|
||||
}
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(12))->resize(GetExtraFieldLength(), 0);
|
||||
}),
|
||||
// 11- file name (variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data) {}),
|
||||
// 12- extra field (variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data) {})
|
||||
})
|
||||
{/*constructor*/
|
||||
}
|
||||
}; //class LocalFileHeader
|
||||
// Hat tip to the people at Facebook. Timestamp for files in ZIP archive
|
||||
// format held constant to make pack/unpack deterministic
|
||||
enum class MagicNumbers : std::uint16_t
|
||||
{
|
||||
FileTime = 0x6B60, // kudos to those know this
|
||||
FileDate = 0xA2B1, // :)
|
||||
};
|
||||
|
||||
enum class GeneralPurposeBitFlags : std::uint16_t
|
||||
{
|
||||
UNSUPPORTED_0 = 0x0001, // Bit 0: If set, indicates that the file is encrypted.
|
||||
|
||||
Deflate_MaxCompress = 0x0002, // Maximum compression (-exx/-ex), otherwise, normal compression (-en)
|
||||
Deflate_FastCompress = 0x0004, // Fast (-ef), if Max+Fast then SuperFast (-es) compression
|
||||
|
||||
GeneralPurposeBit = 0x0008, // the field's crc-32 compressed and uncompressed sizes = 0 in the local header
|
||||
// the correct values are put in the data descriptor immediately following the
|
||||
// compressed data.
|
||||
EnhancedDeflate = 0x0010,
|
||||
CompressedPatchedData = 0x0020,
|
||||
UNSUPPORTED_6 = 0x0040, // Strong encryption.
|
||||
UnUsed_7 = 0x0080, // currently unused
|
||||
UnUsed_8 = 0x0100, // currently unused
|
||||
UnUsed_9 = 0x0200, // currently unused
|
||||
UnUsed_10 = 0x0400, // currently unused
|
||||
|
||||
EncodingMustUseUTF8 = 0x0800, // Language encoding flag (EFS). File name and comments fields MUST be encoded UTF-8
|
||||
|
||||
UNSUPPORTED_12 = 0x1000, // Reserved by PKWARE for enhanced compression
|
||||
UNSUPPORTED_13 = 0x2000, // Set when encrypting the Central Directory
|
||||
UNSUPPORTED_14 = 0x4000, // Reserved by PKWARE
|
||||
UNSUPPORTED_15 = 0x8000, // Reserved by PKWARE
|
||||
};
|
||||
|
||||
inline constexpr GeneralPurposeBitFlags operator &(GeneralPurposeBitFlags a, GeneralPurposeBitFlags b)
|
||||
{
|
||||
return static_cast<GeneralPurposeBitFlags>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
inline constexpr GeneralPurposeBitFlags operator |(GeneralPurposeBitFlags a, GeneralPurposeBitFlags b)
|
||||
{
|
||||
return static_cast<GeneralPurposeBitFlags>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
// if any of these are set, then fail.
|
||||
constexpr static const GeneralPurposeBitFlags UnsupportedFlagsMask =
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_0 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_6 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_12 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_13 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_14 |
|
||||
GeneralPurposeBitFlags::UNSUPPORTED_15;
|
||||
|
||||
/*
|
||||
class DataDescriptor : public Meta::StructuredObject
|
||||
{
|
||||
public:
|
||||
|
@ -136,23 +132,377 @@ namespace xPlat {
|
|||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v) {})
|
||||
|
||||
})
|
||||
{/*constructor*/
|
||||
{
|
||||
}
|
||||
};//class DataDescriptor
|
||||
*/
|
||||
|
||||
class Zip64EndOfCentralDirectoryRecord : public Meta::StructuredObject
|
||||
/* TODO: Implement large file support.
|
||||
This type currently represents a "zip32" Central Directory Header. We need to create a new field type (offset)
|
||||
to replace the type for fields 8 & 9 whose implementation is determined by the version needed to extract (field 2)'s
|
||||
value.
|
||||
|
||||
This type would replace its existing compressed & uncompressed sizes properties (encapsulated in fields 8 & 9)
|
||||
with a 64-bit value version of those methods:
|
||||
std::uint64_t GetCompressedSize() { ...
|
||||
std::uint64_t GetUncompressedSize() { ...
|
||||
void SetCompressedSize (std::uint64_t...
|
||||
void SetUncompressedSize(std::uint64_t...
|
||||
|
||||
The underlying implementation of these methods would validate the resulting in/out values and pass the correct
|
||||
static_casted value to the new meta object type (offset) which would then hold the correct value, as well
|
||||
as handle the correct sizes w.r.t. (de)serialization.
|
||||
|
||||
As-is I don't believe that we "need" this for now, so keeping the implementation "simpler" is probably the correct
|
||||
answer for now.
|
||||
*/
|
||||
class CentralDirectoryFileHeader : public Meta::StructuredObject<
|
||||
Meta::Field4Bytes, // 0 - central file header signature 4 bytes(0x02014b50)
|
||||
Meta::Field2Bytes, // 1 - version made by 2 bytes
|
||||
Meta::Field2Bytes, // 2 - version needed to extract 2 bytes
|
||||
Meta::Field2Bytes, // 3 - general purpose bit flag 2 bytes
|
||||
Meta::Field2Bytes, // 4 - compression method 2 bytes
|
||||
Meta::Field2Bytes, // 5 - last mod file time 2 bytes
|
||||
Meta::Field2Bytes, // 6 - last mod file date 2 bytes
|
||||
Meta::Field4Bytes, // 7 - crc - 32 4 bytes
|
||||
Meta::Field4Bytes, // 8 - compressed size 4 bytes
|
||||
Meta::Field4Bytes, // 9 - uncompressed size 4 bytes
|
||||
Meta::Field2Bytes, //10 - file name length 2 bytes
|
||||
Meta::Field2Bytes, //11 - extra field length 2 bytes
|
||||
Meta::Field2Bytes, //12 - file comment length 2 bytes
|
||||
Meta::Field2Bytes, //13 - disk number start 2 bytes
|
||||
Meta::Field2Bytes, //14 - internal file attributes 2 bytes
|
||||
Meta::Field4Bytes, //15 - external file attributes 4 bytes
|
||||
Meta::Field4Bytes, //16 - relative offset of local header 4 bytes
|
||||
Meta::FieldNBytes, //17 - file name(variable size)
|
||||
Meta::FieldNBytes, //18 - extra field(variable size)
|
||||
Meta::FieldNBytes //19 - file comment(variable size)
|
||||
>
|
||||
{
|
||||
public:
|
||||
Zip64EndOfCentralDirectoryRecord(StreamBase* s) : Meta::StructuredObject(
|
||||
CentralDirectoryFileHeader(StreamBase* s) : m_stream(s)
|
||||
{
|
||||
// 0 - central file header signature 4 bytes(0x02014b50)
|
||||
Field<0>().validation = [](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::CentralFileHeader))
|
||||
{ throw ZipException("signature mismatch", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
// 1 - version made by 2 bytes
|
||||
Field<1>().validation = [](std::uint16_t& v)
|
||||
{ if (v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
{ throw ZipException("unsupported version made by", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
// 2 - version needed to extract 2 bytes
|
||||
Field<2>().validation = [](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion)) &&
|
||||
(v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
)
|
||||
{ throw ZipException("unsupported version needed to extract", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
// 3 - general purpose bit flag 2 bytes
|
||||
Field<3>().validation = [](std::uint16_t& v)
|
||||
{ if ((v & static_cast<std::uint16_t>(UnsupportedFlagsMask)) != 0)
|
||||
{ throw ZipException("unsupported flag(s) specified", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
// 4 - compression method 2 bytes
|
||||
Field<4>().validation = [](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(CompressionType::Store)) &&
|
||||
(v != static_cast<std::uint16_t>(CompressionType::Deflate))
|
||||
)
|
||||
{ throw ZipException("unsupported compression method", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
// 5 - last mod file time 2 bytes
|
||||
// 6 - last mod file date 2 bytes
|
||||
// 7 - crc - 32 4 bytes
|
||||
// 8 - compressed size 4 bytes
|
||||
// 9 - uncompressed size 4 bytes
|
||||
//10 - file name length 2 bytes
|
||||
Field<10>().validation = [&](std::uint16_t& v)
|
||||
{ if (v == 0)
|
||||
{ throw ZipException("unsupported file name size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
Field<17>().value.resize(v,0);
|
||||
};
|
||||
//11 - extra field length 2 bytes
|
||||
Field<11>().validation = [&](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported extra field size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
Field<18>().value.resize(v,0);
|
||||
};
|
||||
//12 - file comment length 2 bytes
|
||||
Field<12>().validation = [&](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported file comment size", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
//13 - disk number start 2 bytes
|
||||
Field<13>().validation = [](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported disk number start", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
//14 - internal file attributes 2 bytes
|
||||
Field<14>().validation = [](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported internal file attributes", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
//15 - external file attributes 4 bytes
|
||||
//16 - relative offset of local header 4 bytes
|
||||
Field<16>().validation = [&](std::uint32_t& v)
|
||||
{ if (v >= m_stream->Ftell())
|
||||
{ throw ZipException("invalid relative offset", ZipException::Error::InvalidCentralDirectoryHeader);
|
||||
}
|
||||
};
|
||||
//17 - file name(variable size)
|
||||
//18 - extra field(variable size)
|
||||
//19 - file comment(variable size)
|
||||
|
||||
SetSignature(static_cast<std::uint32_t>(Signatures::CentralFileHeader));
|
||||
|
||||
SetVersionMadeBy(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
|
||||
// only set to Zip64FormatExtension iff required!
|
||||
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion));
|
||||
SetLastModFileDate(static_cast<std::uint16_t>(MagicNumbers::FileDate));
|
||||
SetLastModFileTime(static_cast<std::uint16_t>(MagicNumbers::FileTime));
|
||||
SetExtraFieldLength(0);
|
||||
SetFileCommentLength(0);
|
||||
SetDiskNumberStart(0);
|
||||
SetInternalFileAttributes(0);
|
||||
SetExternalFileAttributes(0);
|
||||
}
|
||||
|
||||
bool IsGeneralPurposeBitSet()
|
||||
{
|
||||
return ((GetGeneralPurposeBitFlags() & GeneralPurposeBitFlags::GeneralPurposeBit) == GeneralPurposeBitFlags::GeneralPurposeBit);
|
||||
}
|
||||
|
||||
std::uint16_t GetVersionNeededToExtract() { return Field<2>().value; }
|
||||
|
||||
GeneralPurposeBitFlags GetGeneralPurposeBitFlags() { return static_cast<GeneralPurposeBitFlags>(Field<3>().value); }
|
||||
void SetGeneralPurposeBitFlags(std::uint16_t value) { Field<3>().value = value; }
|
||||
|
||||
std::uint16_t GetCompressionMethod() { return Field<4>().value; }
|
||||
void SetCompressionMethod(std::uint16_t value) { Field<4>().value= value; }
|
||||
|
||||
std::uint32_t GetCrc32() { return Field<7>().value; }
|
||||
void SetCrc(std::uint32_t value) { Field<7>().value = value; }
|
||||
|
||||
std::uint32_t GetCompressedSize() { return Field<8>().value; }
|
||||
void SetCompressedSize(std::uint32_t value) { Field<8>().value = value; }
|
||||
|
||||
std::uint32_t GetUncompressedSize() { return Field<9>().value; }
|
||||
void SetUncompressedSize(std::uint32_t value) { Field<9>().value = value; }
|
||||
|
||||
std::uint32_t GetRelativeOffsetOfLocalHeader() { return Field<16>().value; }
|
||||
void SetRelativeOffsetOfLocalHeader(std::uint32_t value) { Field<16>().value = value; }
|
||||
|
||||
std::string GetFileName()
|
||||
{
|
||||
auto data = Field<17>().value;
|
||||
return std::string(data.begin(), data.end());
|
||||
}
|
||||
|
||||
void SetFileName(std::string name)
|
||||
{
|
||||
auto data = Field<17>().value;
|
||||
data.resize(name.size());
|
||||
data.assign(name.begin(), name.end());
|
||||
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
|
||||
}
|
||||
|
||||
private:
|
||||
void SetSignature(std::uint32_t value) { Field<0>().value = value; }
|
||||
void SetVersionMadeBy(std::uint16_t value) { Field<1>().value = value; }
|
||||
void SetVersionNeededToExtract(std::uint16_t value) { Field<2>().value = value; }
|
||||
|
||||
void SetLastModFileTime(std::uint16_t value) { Field<5>().value = value; }
|
||||
void SetLastModFileDate(std::uint16_t value) { Field<6>().value = value; }
|
||||
|
||||
void SetFileNameLength(std::uint16_t value) { Field<10>().value = value; }
|
||||
void SetExtraFieldLength(std::uint16_t value) { Field<11>().value = value; }
|
||||
void SetFileCommentLength(std::uint16_t value) { Field<12>().value = value; }
|
||||
void SetDiskNumberStart(std::uint16_t value) { Field<13>().value = value; }
|
||||
void SetInternalFileAttributes(std::uint16_t value) { Field<14>().value = value; }
|
||||
void SetExternalFileAttributes(std::uint16_t value) { Field<15>().value = value; }
|
||||
|
||||
StreamBase* m_stream = nullptr;
|
||||
};//class CentralDirectoryFileHeader
|
||||
|
||||
class LocalFileHeader : public Meta::StructuredObject<
|
||||
Meta::Field4Bytes, // 0 - local file header signature 4 bytes(0x04034b50)
|
||||
Meta::Field2Bytes, // 1 - version needed to extract 2 bytes
|
||||
Meta::Field2Bytes, // 2 - general purpose bit flag 2 bytes
|
||||
Meta::Field2Bytes, // 3 - compression method 2 bytes
|
||||
Meta::Field2Bytes, // 4 - last mod file time 2 bytes
|
||||
Meta::Field2Bytes, // 5 - last mod file date 2 bytes
|
||||
Meta::Field4Bytes, // 6 - crc - 32 4 bytes
|
||||
Meta::Field4Bytes, // 7 - compressed size 4 bytes
|
||||
Meta::Field4Bytes, // 8 - uncompressed size 4 bytes
|
||||
Meta::Field2Bytes, // 9 - file name length 2 bytes
|
||||
Meta::Field2Bytes, // 10- extra field length 2 bytes
|
||||
Meta::FieldNBytes, // 11- file name (variable size)
|
||||
Meta::FieldNBytes // 12- extra field (variable size)
|
||||
>
|
||||
{
|
||||
public:
|
||||
LocalFileHeader(std::shared_ptr<CentralDirectoryFileHeader> directoryEntry) : m_directoryEntry(directoryEntry)
|
||||
{
|
||||
// 0 - local file header signature 4 bytes(0x04034b50)
|
||||
Field<0>().validation = [](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::LocalFileHeader))
|
||||
{ throw ZipException("file header does not match signature", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 1 - version needed to extract 2 bytes
|
||||
Field<1>().validation = [](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion)) &&
|
||||
(v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
)
|
||||
{ throw ZipException("unsupported version needed to extract", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 2 - general purpose bit flag 2 bytes
|
||||
Field<2>().validation = [&](std::uint16_t& v)
|
||||
{ if ((v & static_cast<std::uint16_t>(UnsupportedFlagsMask)) != 0)
|
||||
{ throw ZipException("unsupported flag(s) specified", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
if (IsGeneralPurposeBitSet() != m_directoryEntry->IsGeneralPurposeBitSet())
|
||||
{ throw ZipException("inconsistent general purpose bits specified", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 3 - compression method 2 bytes
|
||||
Field<3>().validation = [](std::uint16_t& v)
|
||||
{ if ((v != static_cast<std::uint16_t>(CompressionType::Store)) &&
|
||||
(v != static_cast<std::uint16_t>(CompressionType::Deflate))
|
||||
)
|
||||
{ throw ZipException("unsupported compression method", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 4 - last mod file time 2 bytes
|
||||
// 5 - last mod file date 2 bytes
|
||||
// 6 - crc - 32 4 bytes
|
||||
Field<6>().validation = [&](std::uint32_t& v)
|
||||
{ if (IsGeneralPurposeBitSet() && (v != 0))
|
||||
{ throw ZipException("Invalid CRC", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 7 - compressed size 4 bytes
|
||||
Field<7>().validation = [&](std::uint32_t& v)
|
||||
{ if (IsGeneralPurposeBitSet() && (v != 0))
|
||||
{ throw ZipException("Invalid compressed size", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 8 - uncompressed size 4 bytes
|
||||
Field<8>().validation = [&](std::uint32_t& v)
|
||||
{ if (IsGeneralPurposeBitSet() && (v != 0))
|
||||
{ throw ZipException("Invalid uncompressed size", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
};
|
||||
// 9 - file name length 2 bytes
|
||||
Field<9>().validation = [&](std::uint16_t& v)
|
||||
{ if (v == 0)
|
||||
{ throw ZipException("unsupported file name size", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
Field<11>().value.resize(GetFileNameLength(), 0);
|
||||
};
|
||||
// 10- extra field length 2 bytes
|
||||
Field<10>().validation = [&](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported extra field size", ZipException::Error::InvalidLocalFileHeader);
|
||||
}
|
||||
Field<12>().value.resize(GetExtraFieldLength(), 0);
|
||||
};
|
||||
// 11- file name (variable size)
|
||||
// 12- extra field (variable size)
|
||||
}
|
||||
|
||||
bool IsGeneralPurposeBitSet()
|
||||
{
|
||||
return ((GetGeneralPurposeBitFlags() & GeneralPurposeBitFlags::GeneralPurposeBit) == GeneralPurposeBitFlags::GeneralPurposeBit);
|
||||
}
|
||||
|
||||
GeneralPurposeBitFlags GetGeneralPurposeBitFlags() { return static_cast<GeneralPurposeBitFlags>(Field<2>().value); }
|
||||
|
||||
CompressionType GetCompressionType() { return static_cast<CompressionType>(Field<3>().value); }
|
||||
|
||||
std::uint32_t GetCompressedSize()
|
||||
{
|
||||
if (IsGeneralPurposeBitSet())
|
||||
{
|
||||
return m_directoryEntry->GetCompressedSize();
|
||||
}
|
||||
return Field<7>().value;
|
||||
}
|
||||
|
||||
std::uint32_t GetUncompressedSize()
|
||||
{
|
||||
if (IsGeneralPurposeBitSet())
|
||||
{
|
||||
return m_directoryEntry->GetUncompressedSize();
|
||||
}
|
||||
return Field<8>().value;
|
||||
}
|
||||
|
||||
std::uint16_t GetFileNameLength() { return Field<9>().value; }
|
||||
std::uint16_t GetExtraFieldLength() { return Field<10>().value; }
|
||||
|
||||
void SetGeneralPurposeBitFlag(std::uint16_t value) { Field<2>().value = value; }
|
||||
void SetCompressedSize(std::uint32_t value) { Field<7>().value = value; }
|
||||
void SetUncompressedSize(std::uint32_t value) { Field<8>().value = value; }
|
||||
void SetFileNameLength(std::uint16_t value) { Field<9>().value = value; }
|
||||
void SetExtraFieldLength(std::uint16_t value) { Field<10>().value = value; }
|
||||
|
||||
std::string GetFileName()
|
||||
{
|
||||
auto data = Field<11>().value;
|
||||
return std::string(data.begin(), data.end());
|
||||
}
|
||||
|
||||
void SetFileName(std::string name)
|
||||
{
|
||||
auto data = Field<11>().value;
|
||||
data.resize(name.size());
|
||||
data.assign(name.begin(), name.end());
|
||||
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
|
||||
}
|
||||
protected:
|
||||
|
||||
std::shared_ptr<CentralDirectoryFileHeader> m_directoryEntry = nullptr;
|
||||
}; //class LocalFileHeader
|
||||
|
||||
class Zip64EndOfCentralDirectoryRecord : public Meta::StructuredObject<
|
||||
Meta::Field4Bytes, // 0 - zip64 end of central dir signature 4 bytes(0x06064b50)
|
||||
Meta::Field8Bytes, // 1 - size of zip64 end of central directory record 8 bytes
|
||||
Meta::Field2Bytes, // 2 - version made by 2 bytes
|
||||
Meta::Field2Bytes, // 3 - version needed to extract 2 bytes
|
||||
Meta::Field4Bytes, // 4 - number of this disk 4 bytes
|
||||
Meta::Field4Bytes, // 5 - number of the disk with the start of the central directory 4 bytes
|
||||
Meta::Field8Bytes, // 6 - total number of entries in the central directory on this disk 8 bytes
|
||||
Meta::Field8Bytes, // 7 - total number of entries in the central directory 8 bytes
|
||||
Meta::Field8Bytes, // 8 - size of the central directory 8 bytes
|
||||
Meta::Field8Bytes, // 9 - offset of start of central directory with respect to the
|
||||
// starting disk number 8 bytes
|
||||
Meta::FieldNBytes //10 - zip64 extensible data sector (variable size)
|
||||
>
|
||||
{
|
||||
public:
|
||||
Zip64EndOfCentralDirectoryRecord(StreamBase* s) : m_stream(s)
|
||||
{
|
||||
// 0 - zip64 end of central dir signature 4 bytes(0x06064b50)
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<0>().validation = [](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::Zip64EndOfCD))
|
||||
{ throw ZipException("end of zip64 central directory does not match signature", ZipException::Error::InvalidHeader);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 1 - size of zip64 end of central directory record 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([&](std::uint64_t& v)
|
||||
Field<1>().validation = [&](std::uint64_t& v)
|
||||
{ //4.3.14.1 The value stored into the "size of zip64 end of central
|
||||
// directory record" should be the size of the remaining
|
||||
// record and should not include the leading 12 bytes.
|
||||
|
@ -160,211 +510,226 @@ namespace xPlat {
|
|||
if (v != size)
|
||||
{ throw ZipException("invalid size of zip64 EOCD", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 2 - version made by 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<2>().validation = [](std::uint16_t& v)
|
||||
{ if (v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
{ throw ZipException("invalid zip64 EOCD version made by", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 3 - version needed to extract 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<3>().validation = [](std::uint16_t& v)
|
||||
{ if (v != static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))
|
||||
{ throw ZipException("invalid zip64 EOCD version to extract", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 4 - number of this disk 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<4>().validation = [](std::uint32_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("invalid disk number", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 5 - number of the disk with the start of the central directory 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<5>().validation = [](std::uint32_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("invalid disk index", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 6 - total number of entries in the central directory on this disk 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([](std::uint64_t& v)
|
||||
Field<6>().validation = [](std::uint64_t& v)
|
||||
{ if (v == 0)
|
||||
{ throw ZipException("invalid number of entries", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 7 - total number of entries in the central directory 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([&](std::uint64_t& v)
|
||||
Field<7>().validation = [&](std::uint64_t& v)
|
||||
{ if (v != this->GetTotalNumberOfEntries())
|
||||
{ throw ZipException("invalid total number of entries", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 8 - size of the central directory 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([&](std::uint64_t& v)
|
||||
Field<8>().validation = [&](std::uint64_t& v)
|
||||
{ // TODO: tighten up this validation
|
||||
if ((v == 0) ||
|
||||
(v >= m_stream->Ftell()))
|
||||
{ throw ZipException("invalid size of central directory", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 9 - offset of start of central directory with respect to the starting disk number 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([&](std::uint64_t& v)
|
||||
Field<9>().validation = [&](std::uint64_t& v)
|
||||
{ // TODO: tighten up this validation
|
||||
if ((v == 0) ||
|
||||
(v >= m_stream->Ftell()))
|
||||
{ throw ZipException("invalid start of central directory", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
//10 - zip64 extensible data sector(variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data)
|
||||
Field<10>().validation = [](std::vector<std::uint8_t>& data)
|
||||
{ if (data.size() != 0)
|
||||
{ throw ZipException("unsupported extensible data", ZipException::Error::InvalidZip64CentralDirectoryRecord);
|
||||
}
|
||||
})
|
||||
}), m_stream(s)
|
||||
{
|
||||
};
|
||||
|
||||
SetSignature(static_cast<std::uint32_t>(Signatures::Zip64EndOfCD));
|
||||
SetGetSizeOfZip64CDRecord(this->Size() - 12);
|
||||
SetVersionMadeBy(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
|
||||
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
|
||||
SetNumberOfThisDisk(0);
|
||||
SetTotalNumberOfEntries(0);
|
||||
Meta::Object::GetValue<std::vector<std::uint8_t>>(Field(10))->resize(0);
|
||||
}/*constructor*/
|
||||
|
||||
std::uint64_t GetTotalNumberOfEntries()
|
||||
{
|
||||
return *Meta::Object::GetValue<std::uint64_t>(Field(6));
|
||||
Field<10>().value.resize(0);
|
||||
}
|
||||
|
||||
std::uint64_t GetTotalNumberOfEntries() { return Field<6>().value; }
|
||||
|
||||
void SetTotalNumberOfEntries(std::uint64_t value)
|
||||
{
|
||||
Meta::Object::SetValue(Field(6), value);
|
||||
Meta::Object::SetValue(Field(7), value);
|
||||
Field<6>().value = value;
|
||||
Field<7>().value = value;
|
||||
}
|
||||
|
||||
std::uint64_t GetSizeOfCD() { return *Meta::Object::GetValue<std::uint64_t>(Field(8)); }
|
||||
void SetSizeOfCD(std::uint64_t value) { Meta::Object::SetValue(Field(8), value); }
|
||||
std::uint64_t GetOffsetfStartOfCD() { return *Meta::Object::GetValue<std::uint64_t>(Field(9)); }
|
||||
void SetOffsetfStartOfCD(std::uint64_t value) { Meta::Object::SetValue(Field(9), value); }
|
||||
std::uint64_t GetSizeOfCD() { return Field<8>().value; }
|
||||
void SetSizeOfCD(std::uint64_t value) { Field<8>().value = value; }
|
||||
std::uint64_t GetOffsetfStartOfCD() { return Field<9>().value; }
|
||||
void SetOffsetfStartOfCD(std::uint64_t value) { Field<9>().value = value; }
|
||||
|
||||
private:
|
||||
void SetSignature(std::uint32_t value) { Meta::Object::SetValue(Field(0), value); }
|
||||
void SetGetSizeOfZip64CDRecord(std::uint64_t value) { Meta::Object::SetValue(Field(1), value); }
|
||||
void SetVersionMadeBy(std::uint16_t value) { Meta::Object::SetValue(Field(2), value); }
|
||||
void SetVersionNeededToExtract(std::uint16_t value) { Meta::Object::SetValue(Field(3), value); }
|
||||
void SetNumberOfThisDisk(std::uint32_t value) { Meta::Object::SetValue(Field(4), value); }
|
||||
void SetSignature(std::uint32_t value) { Field<0>().value = value; }
|
||||
void SetGetSizeOfZip64CDRecord(std::uint64_t value) { Field<1>().value = value; }
|
||||
void SetVersionMadeBy(std::uint16_t value) { Field<2>().value = value; }
|
||||
void SetVersionNeededToExtract(std::uint16_t value) { Field<3>().value = value; }
|
||||
void SetNumberOfThisDisk(std::uint32_t value) { Field<4>().value = value; }
|
||||
|
||||
StreamBase* m_stream = nullptr;
|
||||
}; //class Zip64EndOfCentralDirectoryRecord
|
||||
|
||||
class Zip64EndOfCentralDirectoryLocator : public Meta::StructuredObject
|
||||
class Zip64EndOfCentralDirectoryLocator : public Meta::StructuredObject<
|
||||
Meta::Field4Bytes, // 0 - zip64 end of central dir locator signature 4 bytes(0x07064b50)
|
||||
Meta::Field4Bytes, // 1 - number of the disk with the start of the zip64
|
||||
// end of central directory 4 bytes
|
||||
Meta::Field8Bytes, // 2 - relative offset of the zip64 end of central
|
||||
// directory record 8 bytes
|
||||
Meta::Field4Bytes // 3 - total number of disks 4 bytes
|
||||
>
|
||||
{
|
||||
public:
|
||||
Zip64EndOfCentralDirectoryLocator(StreamBase* s) : Meta::StructuredObject(
|
||||
Zip64EndOfCentralDirectoryLocator(StreamBase* s) : m_stream(s)
|
||||
{
|
||||
// 0 - zip64 end of central dir locator signature 4 bytes(0x07064b50)
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<0>().validation = [](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::Zip64EndOfCDLocator))
|
||||
{ throw ZipException("end of central directory locator does not match signature", ZipException::Error::InvalidHeader);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 1 - number of the disk with the start of the zip64 end of central directory 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<1>().validation = [](std::uint32_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("Invalid disk number", ZipException::Error::InvalidZip64CentralDirectoryLocator);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 2 - relative offset of the zip64 end of central directory record 8 bytes
|
||||
std::make_shared<Meta::Field8Bytes>([&](std::uint64_t& v)
|
||||
Field<2>().validation = [&](std::uint64_t& v)
|
||||
{ if ((v == 0) ||
|
||||
(v >= m_stream->Ftell()))
|
||||
{ throw ZipException("Invalid relative offset", ZipException::Error::InvalidZip64CentralDirectoryLocator);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 3 - total number of disks 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<3>().validation = [](std::uint32_t& v)
|
||||
{ if (v != 1)
|
||||
{ throw ZipException("Invalid total number of disks", ZipException::Error::InvalidZip64CentralDirectoryLocator);
|
||||
}
|
||||
})
|
||||
}), m_stream(s)
|
||||
{/*constructor*/
|
||||
};
|
||||
|
||||
SetSignature(static_cast<std::uint32_t>(Signatures::Zip64EndOfCDLocator));
|
||||
SetNumberOfDisk(0);
|
||||
SetTotalNumberOfDisks(1);
|
||||
}
|
||||
|
||||
std::uint64_t GetRelativeOffset() { return *Meta::Object::GetValue<std::uint64_t>(Field(2)); }
|
||||
void SetRelativeOffset(std::uint64_t value) { Meta::Object::SetValue(Field(2), value); }
|
||||
std::uint64_t GetRelativeOffset() { return Field<2>().value; }
|
||||
void SetRelativeOffset(std::uint64_t value) { Field<2>().value = value; }
|
||||
|
||||
private:
|
||||
void SetSignature(std::uint32_t value) { Meta::Object::SetValue(Field(0), value); }
|
||||
void SetNumberOfDisk(std::uint32_t value) { Meta::Object::SetValue(Field(1), value); }
|
||||
void SetTotalNumberOfDisks(std::uint32_t value) { Meta::Object::SetValue(Field(3), value); }
|
||||
void SetSignature(std::uint32_t value) { Field<0>().value = value; }
|
||||
void SetNumberOfDisk(std::uint32_t value) { Field<1>().value = value; }
|
||||
void SetTotalNumberOfDisks(std::uint32_t value) { Field<3>().value = value; }
|
||||
|
||||
StreamBase* m_stream = nullptr;
|
||||
}; //class Zip64EndOfCentralDirectoryLocator
|
||||
|
||||
class EndCentralDirectoryRecord : public Meta::StructuredObject
|
||||
class EndCentralDirectoryRecord : public Meta::StructuredObject<
|
||||
Meta::Field4Bytes, // 0 - end of central dir signature 4 bytes (0x06054b50)
|
||||
Meta::Field2Bytes, // 1 - number of this disk 2 bytes
|
||||
Meta::Field2Bytes, // 2 - number of the disk with the start of the
|
||||
// central directory 2 bytes
|
||||
Meta::Field2Bytes, // 3 - total number of entries in the central
|
||||
// directory on this disk 2 bytes
|
||||
Meta::Field2Bytes, // 4 - total number of entries in the central
|
||||
// directory 2 bytes
|
||||
Meta::Field4Bytes, // 5 - size of the central directory 4 bytes
|
||||
Meta::Field4Bytes, // 6 - offset of start of central directory with
|
||||
// respect to the starting disk number 4 bytes
|
||||
Meta::Field2Bytes, // 7 - .ZIP file comment length 2 bytes
|
||||
Meta::FieldNBytes // 8 - .ZIP file comment (variable size)
|
||||
>
|
||||
{
|
||||
public:
|
||||
EndCentralDirectoryRecord() : Meta::StructuredObject(
|
||||
EndCentralDirectoryRecord()
|
||||
{
|
||||
// 0 - end of central dir signature 4 bytes (0x06054b50)
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<0>().validation = [](std::uint32_t& v)
|
||||
{ if (v != static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory))
|
||||
{ throw ZipException("invalid signiture", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 1 - number of this disk 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<1>().validation = [](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported disk number", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 2 - number of the disk with the start of the central directory 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<2>().validation = [](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("unsupported EoCDR disk number", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 3 - total number of entries in the central directory on this disk 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<3>().validation = [](std::uint16_t& v)
|
||||
{ if (v != std::numeric_limits<std::uint16_t>::max())
|
||||
{ throw ZipException("unsupported total number of entries on this disk", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 4 - total number of entries in the central directory 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([](std::uint16_t& v)
|
||||
Field<4>().validation = [](std::uint16_t& v)
|
||||
{ if (v != std::numeric_limits<std::uint16_t>::max())
|
||||
{ throw ZipException("unsupported total number of entries", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 5 - size of the central directory 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<5>().validation = [](std::uint32_t& v)
|
||||
{ if (v != std::numeric_limits<std::uint32_t>::max())
|
||||
{ throw ZipException("unsupported size of central directory", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 6 - offset of start of central directory with respect to the starting disk number 4 bytes
|
||||
std::make_shared<Meta::Field4Bytes>([](std::uint32_t& v)
|
||||
Field<6>().validation = [](std::uint32_t& v)
|
||||
{ if (v != std::numeric_limits<std::uint32_t>::max())
|
||||
{ throw ZipException("unsupported offset of start of central directory", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 7 - .ZIP file comment length 2 bytes
|
||||
std::make_shared<Meta::Field2Bytes>([&](std::uint16_t& v)
|
||||
Field<7>().validation = [&](std::uint16_t& v)
|
||||
{ if (v != 0)
|
||||
{ throw ZipException("Zip comment unsupported", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
}),
|
||||
};
|
||||
// 8 - .ZIP file comment (variable size)
|
||||
std::make_shared<Meta::FieldNBytes>([](std::vector<std::uint8_t>& data)
|
||||
Field<8>().validation = [](std::vector<std::uint8_t>& data)
|
||||
{ if (data.size() != 0)
|
||||
{ throw ZipException("Zip comment unsupported", ZipException::Error::InvalidEndOfCentralDirectoryRecord);
|
||||
}
|
||||
})
|
||||
})
|
||||
{/*constructor*/
|
||||
};
|
||||
|
||||
SetSignature(static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory));
|
||||
SetNumberOfDisk(0);
|
||||
SetDiskStart(0);
|
||||
|
@ -378,45 +743,71 @@ namespace xPlat {
|
|||
}
|
||||
|
||||
private:
|
||||
void SetSignature(std::uint32_t value) { Meta::Object::SetValue(Field(0), value); }
|
||||
void SetNumberOfDisk(std::uint16_t value) { Meta::Object::SetValue(Field(1), value); }
|
||||
void SetDiskStart(std::uint16_t value) { Meta::Object::SetValue(Field(2), value); }
|
||||
void SetTotalNumberOfEntries(std::uint16_t value) { Meta::Object::SetValue(Field(3), value); }
|
||||
void SetTotalEntriesInCentralDirectory(std::uint16_t value) { Meta::Object::SetValue(Field(4), value); }
|
||||
void SetSizeOfCentralDirectory(std::uint32_t value) { Meta::Object::SetValue(Field(5), value); }
|
||||
void SetOffsetOfCentralDirectory(std::uint32_t value) { Meta::Object::SetValue(Field(6), value); }
|
||||
void SetCommentLength(std::uint16_t value) { Meta::Object::SetValue(Field(7), value); }
|
||||
void SetSignature(std::uint32_t value) { Field<0>().value = value; }
|
||||
void SetNumberOfDisk(std::uint16_t value) { Field<1>().value = value; }
|
||||
void SetDiskStart(std::uint16_t value) { Field<2>().value = value; }
|
||||
void SetTotalNumberOfEntries(std::uint16_t value) { Field<3>().value = value; }
|
||||
void SetTotalEntriesInCentralDirectory(std::uint16_t value) { Field<4>().value = value; }
|
||||
void SetSizeOfCentralDirectory(std::uint32_t value) { Field<5>().value = value; }
|
||||
void SetOffsetOfCentralDirectory(std::uint32_t value) { Field<6>().value = value; }
|
||||
void SetCommentLength(std::uint16_t value) { Field<7>().value = value; }
|
||||
|
||||
};//class EndOfCentralDirectoryRecord
|
||||
|
||||
void ZipObject::Read()
|
||||
ZipObject::ZipObject(StreamBase* stream)
|
||||
{
|
||||
// Confirm that the file IS the correct format
|
||||
EndCentralDirectoryRecord endCentralDirectoryRecord;
|
||||
m_stream->Seek(-1 * endCentralDirectoryRecord.Size(), StreamBase::Reference::END);
|
||||
endCentralDirectoryRecord.Read(m_stream.get());
|
||||
stream->Seek(-1 * endCentralDirectoryRecord.Size(), StreamBase::Reference::END);
|
||||
endCentralDirectoryRecord.Read(stream);
|
||||
|
||||
// find where the zip central directory exists.
|
||||
Zip64EndOfCentralDirectoryLocator zip64Locator(m_stream.get());
|
||||
m_stream->Seek(-1*(endCentralDirectoryRecord.Size() + zip64Locator.Size()), StreamBase::Reference::END);
|
||||
zip64Locator.Read(m_stream.get());
|
||||
Zip64EndOfCentralDirectoryLocator zip64Locator(stream);
|
||||
stream->Seek(-1*(endCentralDirectoryRecord.Size() + zip64Locator.Size()), StreamBase::Reference::END);
|
||||
zip64Locator.Read(stream);
|
||||
|
||||
// now read the zip central directory
|
||||
Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectory(m_stream.get());
|
||||
m_stream->Seek(zip64Locator.GetRelativeOffset(), StreamBase::Reference::START);
|
||||
zip64EndOfCentralDirectory.Read(m_stream.get());
|
||||
m_stream->Seek(zip64EndOfCentralDirectory.GetOffsetfStartOfCD(), StreamBase::Reference::START);
|
||||
Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectory(stream);
|
||||
stream->Seek(zip64Locator.GetRelativeOffset(), StreamBase::Reference::START);
|
||||
zip64EndOfCentralDirectory.Read(stream);
|
||||
|
||||
// read the zip central directory
|
||||
stream->Seek(zip64EndOfCentralDirectory.GetOffsetfStartOfCD(), StreamBase::Reference::START);
|
||||
for (std::uint32_t index = 0; index < zip64EndOfCentralDirectory.GetTotalNumberOfEntries(); index++)
|
||||
{
|
||||
auto centralFileHeader = std::make_shared<CentralDirectoryFileHeader>(m_stream.get());
|
||||
centralFileHeader->Read(m_stream.get());
|
||||
auto centralFileHeader = std::make_shared<CentralDirectoryFileHeader>(stream);
|
||||
centralFileHeader->Read(stream);
|
||||
// TODO: ensure that there are no collisions on name!
|
||||
m_centralDirectory.insert(std::make_pair(centralFileHeader->GetFileName(), centralFileHeader));
|
||||
}
|
||||
|
||||
// We should have no data between the end of the last central directory header and the start of the EoCD
|
||||
if (m_stream->Ftell() != zip64Locator.GetRelativeOffset())
|
||||
if (stream->Ftell() != zip64Locator.GetRelativeOffset())
|
||||
{ throw ZipException("hidden data unsupported", ZipException::Error::HiddenDataBetweenLastCDHandEoCD);
|
||||
}
|
||||
|
||||
// read the file repository
|
||||
for (auto centralFileHeader : m_centralDirectory)
|
||||
{
|
||||
stream->Seek(centralFileHeader.second->GetRelativeOffsetOfLocalHeader(), xPlat::StreamBase::Reference::START);
|
||||
auto localFileHeader = std::make_shared<LocalFileHeader>(centralFileHeader.second);
|
||||
localFileHeader->Read(stream);
|
||||
m_fileRepository.insert(std::make_pair(
|
||||
centralFileHeader.second->GetRelativeOffsetOfLocalHeader(),
|
||||
localFileHeader));
|
||||
|
||||
auto zipFileStream = std::make_shared<ZipFileStream>(
|
||||
centralFileHeader.second->GetFileName(),
|
||||
centralFileHeader.second->GetRelativeOffsetOfLocalHeader() + localFileHeader->Size(),
|
||||
localFileHeader->GetCompressedSize(),
|
||||
localFileHeader->GetUncompressedSize(),
|
||||
localFileHeader->GetCompressionType() == CompressionType::Deflate
|
||||
);
|
||||
|
||||
m_streams.insert(std::make_pair(
|
||||
centralFileHeader.second->GetFileName(),
|
||||
zipFileStream));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ZipObject::GetFileNames()
|
||||
|
@ -429,4 +820,4 @@ namespace xPlat {
|
|||
return result;
|
||||
}
|
||||
|
||||
} // namespace xPlat
|
||||
} // namespace xPlat
|
||||
|
|
|
@ -53,10 +53,11 @@ XPLATAPPX_API unsigned int UnpackAppx(char* source, char* destination)
|
|||
{
|
||||
return ResultOf(source, destination, [&]() {
|
||||
std::string appxFileName(source);
|
||||
xPlat::ZipObject zip(std::make_unique<xPlat::FileStream>(
|
||||
auto rawFile = std::make_unique<xPlat::FileStream>(
|
||||
std::move(appxFileName),
|
||||
xPlat::FileStream::Mode::READ));
|
||||
zip.Read();
|
||||
xPlat::FileStream::Mode::READ);
|
||||
|
||||
xPlat::ZipObject zip(rawFile.get());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче