1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
#pragma once
#include "../Common.h"
#include "../Containers/Array.h"
#include "../Containers/String.h"
#include "FileStream.h"
#include "FileSystem.h"
#include <memory>
namespace Death { namespace IO {
//###==##====#=====--==~--~=~- --- -- - - - -
/** @brief Preferred file compression, used in @ref PakWriter::AddFile() */
enum class PakPreferredCompression
{
None, /**< None */
Deflate, /**< Deflate */
Lz4, /**< LZ4 */
Zstd, /**< Zstandard */
Lzma2Compressed /**< LZMA2 - Not implemented */
};
/**
@brief Provides read-only access to contents of `.pak` file
*/
class PakFile
{
friend class PakWriter;
public:
explicit PakFile(Containers::StringView path);
PakFile(const PakFile&) = delete;
PakFile& operator=(const PakFile&) = delete;
explicit operator bool() const {
return IsValid();
}
/** @brief Returns mount point for the container */
Containers::StringView GetMountPoint() const;
/** @brief Returns path of the `.pak` file */
Containers::StringView GetPath() const;
bool IsValid() const;
/** @brief Returns `true` if the specified path is a file */
bool FileExists(Containers::StringView path);
/** @overload */
bool FileExists(std::uint64_t hashedPath);
/** @brief Returns `true` if the specified path is a directory */
bool DirectoryExists(Containers::StringView path);
/** @brief Opens a file stream */
std::unique_ptr<Stream> OpenFile(Containers::StringView path, std::int32_t bufferSize = FileStream::DefaultBufferSize);
/** @overload */
std::unique_ptr<Stream> OpenFile(std::uint64_t hashedPath, std::int32_t bufferSize = FileStream::DefaultBufferSize);
/** @brief Handles directory traversal, should be used as iterator */
class Directory
{
public:
#ifndef DOXYGEN_GENERATING_OUTPUT
class Proxy
{
friend class Directory;
public:
Containers::StringView operator*() const & noexcept;
private:
explicit Proxy(Containers::StringView path);
Containers::String _path;
};
// Iterator defines
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
//using reference = const Containers::StringView&;
using value_type = Containers::StringView;
#endif
Directory() noexcept;
Directory(PakFile& pakFile, Containers::StringView path, FileSystem::EnumerationOptions options = FileSystem::EnumerationOptions::None);
~Directory();
Directory(const Directory& other);
Directory& operator=(const Directory& other);
Directory(Directory&& other) noexcept;
Directory& operator=(Directory&& other) noexcept;
Containers::StringView operator*() const& noexcept;
Directory& operator++();
Proxy operator++(int) {
Proxy p{**this};
++*this;
return p;
}
bool operator==(const Directory& rhs) const;
bool operator!=(const Directory& rhs) const;
Directory begin() noexcept {
return *this;
}
Directory end() noexcept {
return Directory();
}
private:
class Impl;
std::shared_ptr<Impl> _impl;
};
private:
enum class ItemFlags : std::uint32_t {
None = 0,
Directory = 0x01,
Link = 0x02, // Not implemented
Encrypted = 0x10, // Not implemented
CompressionFlags = 0xF00,
};
DEATH_PRIVATE_ENUM_FLAGS(ItemFlags);
#ifndef DOXYGEN_GENERATING_OUTPUT
// Doxygen 1.12.0 outputs also private structs/unions even if it shouldn't
struct Item {
Containers::String Name;
ItemFlags Flags;
std::uint64_t Offset;
std::uint32_t UncompressedSize;
std::uint32_t Size;
Containers::Array<Item> ChildItems;
};
#endif
static constexpr std::uint32_t CompressionFlagsShift = 8;
Containers::String _path;
Containers::String _mountPoint;
Containers::Array<Item> _rootItems;
bool _useHashIndex;
void ConstructsItemsFromIndex(Stream& s, Item* parentItem, bool deflateCompressed, bool useRelativeOffsets, std::uint32_t depth);
Containers::Array<Item>* ReadIndexFromStream(Stream& s, Item* parentItem, bool useRelativeOffsets, std::int64_t indexStartPosition);
DEATH_NEVER_INLINE Containers::Array<Item>* ReadIndexFromStreamDeflateCompressed(Stream& s, Item* parentItem, bool useRelativeOffsets, std::int64_t indexStartPosition);
Item* FindItem(Containers::StringView path);
Item* FindItemByHash(std::uint64_t hashedPath);
static DEATH_ALWAYS_INLINE bool HasCompressedSize(ItemFlags itemFlags);
};
/**
@brief Allows to create a `.pak` file
*/
class PakWriter
{
public:
explicit PakWriter(Containers::StringView path, bool useHashIndex = false, bool useCompressedIndex = false, bool useRelativeOffsets = false, bool append = false);
~PakWriter();
PakWriter(const PakWriter&) = delete;
PakWriter& operator=(const PakWriter&) = delete;
explicit operator bool() const {
return IsValid();
}
bool IsValid() const;
/** @brief Adds a file to the `.pak` container */
bool AddFile(Stream& stream, Containers::StringView path, PakPreferredCompression preferredCompression = PakPreferredCompression::None);
/** @brief Writes file index and finalizes the `.pak` containers */
void Finalize();
/** @brief Returns optional mount point for the container */
Containers::StringView GetMountPoint() const;
/** @brief Sets optional mount point for the container */
void SetMountPoint(Containers::String value);
private:
Containers::String _mountPoint;
std::unique_ptr<FileStream> _outputStream;
Containers::Array<PakFile::Item> _rootItems;
bool _finalized;
bool _alreadyExisted;
bool _useHashIndex;
bool _useCompressedIndex;
bool _useRelativeOffsets;
PakFile::Item* FindOrCreateParentItem(Containers::StringView& path);
void WriteItemDescription(Stream& s, PakFile::Item& item, std::int64_t indexStartPosition);
};
}}
|