File: PakFile.h

package info (click to toggle)
jazz2-native 3.5.0-3
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 16,912 kB
  • sloc: cpp: 172,557; xml: 113; python: 36; makefile: 5; sh: 2
file content (204 lines) | stat: -rw-r--r-- 5,781 bytes parent folder | download
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);
	};

}}