summaryrefslogtreecommitdiffstats
path: root/src/cppc4group.hpp
blob: d80ffdfb9f5a56b65ec74665990dfd0646311726 (plain)
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
205
206
207
208
209
210
211
212
213
214
215
216
217
#pragma once

// this is the main and only include file for cppc4group, the C++-wrapper for cc4group
// because cc4group is already object-oriented, this is just a simple wrapper and almost all methods work exactly the same, just with C++-types
// although, when dealing with raw memory (because cc4group can't know the type of the stored data) also users of this wrapper have to deal with some void*
// because most methods behave exactly the same, please look at cc4group.h for their description (they are named exactly the same with a few exceptions)
// in case their are real differences, they will be noted in this header also
// until now, all except one example only use cc4group's C-API, but as a C++-programmer you should have no problems reading them anyway
// one general difference to the C-API that is not mentioned at the individual methods is that functions actually returning data return an std::optional instead of a bool...
// ...the optional will be populated normally and empty in error cases

#include <memory>
#include <vector>
#include <optional>
#include <cstdio>
#include <functional>
#include <type_traits>

class CppC4Group {
	// all C-related stuff is hidden from this header so it doesn't land in the precious C++-only code this might be used in...
	// ...and pollute the root-namespace with various types that are better contained in this class instead
	struct Private;

	std::unique_ptr<Private> p;

	// this is only needed for internal purposes
	CppC4Group(std::unique_ptr<Private>&& p);

public:
	// this struct is used for the getEntryData-method to return them in a uniform way (instead of using reference- or pointer-arguments)
	struct Data {
		// this will contain the information cc4group.getEntryData stores in its data argument
		const void* data;
		// and the same for size
		size_t size;

		// there should be no reason to construct one of these as API user, but it is needed internally
		Data();
		Data(const void* const data, const size_t size);
	};

	// this struct contains the same things as the CC4Group_EntryInfo struct of the C-version
	// member descriptions also apply equally here
	struct EntryInfo {
		std::string fileName;
		int32_t modified;
		std::string author;
		size_t size;
		size_t totalSize;
		bool executable;
		bool directory;
		bool official;
	};

	// this struct is used in custom tmp memory strategies
	// instead of using additional pointer arguments to return the cleanup stuff everything is returned in a single one of this by the tmp memory strategy function
	struct TmpMemory {
		using TmpMemoryCleanupCallback = std::function<bool(void* memory, void* arg)>;

		// memory needs to hold the readily allocated memory
		// it corresponds to the pointer that needs to be returned by C tmp memory strategies
		void* memory;

		// cleanup is a fancy std::function that will be called when the memory is not needed anymore
		// it receives the memory pointer stored above and the custom argument stored right below as arguments
		TmpMemoryCleanupCallback cleanup;

		// this can be set to arbitrary data needed by the cleanup callback to properly cleanup all used resources
		void* arg;

		// this is just a plain constructor setting each member accordingly
		TmpMemory(void* const memory, const TmpMemoryCleanupCallback& cleanup, void* const arg);
	};

public:
	// the C++ custom tmp memory strategy only receives the needed size as argument and must return an optional containing the information described above (TmpMemory)
	// or an empty optional on failure
	using TmpMemoryCallback = std::optional<TmpMemory>(*)(size_t size);

	// the C++ memory management strategies work exactly the same way as in C, but with std::function instead
	class MemoryManagement {
	public:
		struct MemoryManagementStrategy_t;
		using MemoryManagementStrategy = MemoryManagementStrategy_t*;

		using Start = std::function<void*(void* memory, size_t size)>;
		using End = std::function<void(void* memory)>;

	private:
		struct Private;
		Private* p;

	public:
		MemoryManagement(Start&& start, End&& end);
		MemoryManagement(MemoryManagementStrategy strategy);

		~MemoryManagement();

		MemoryManagementStrategy operator()() const;

		// this may be used as start function if it need not do anything but return the original pointer
		static constexpr auto NoOpStart = [](void* memory, size_t) { return memory; };

		static const MemoryManagement Take;
		static const MemoryManagement Copy;
		static const MemoryManagement Reference;
	};

	// these are equivalent to their C counterparts
	using ReadCallback = bool(*)(const void** const data, size_t* const size, void* const arg);
	using SetupCallback = bool(*)(void* const arg);
	using WriteCallback = bool(*)(const void* const data, size_t const size, void* const arg);

	// but a more practical variant here
	using WarningCallback = void(*)(const std::string& message);

	// these enums are just mapped to their C-counterparts internally
	enum class TmpMemoryStrategy {
		Memory,
		File,
		Auto
	};

	enum class AllowedEntryTypes {
		File = 0x1,
		Directory = 0x2,
		All = File | Directory
	};

	// use this to set one of the predefined strategies
	static void setTmpMemoryStrategy(const TmpMemoryStrategy strategy);

	// and this for your own custom strategy
	static void setTmpMemoryStrategy(const TmpMemoryCallback callback);

	static void setWarningCallback(const WarningCallback callback);

public:
	// the constructor will automatically construct an internal CC4Group, so no new-equivalent method is needed
	// may throw std::bad_alloc if the allocation and construction of the internal CC4Group object fails
	CppC4Group(const bool lazy = true);

	// the move constructor is needed to move the returned group out of the optional from openAsChild
	CppC4Group(CppC4Group&& other);

	// the destructor will automatically delete the internal CC4Group, so also no extra method needed
	~CppC4Group();

	bool create();
	bool openExisting(const std::string& path);
	bool openFd(const int fd);
	bool openFilePointer(FILE* file);
	bool openMemory(const void* const data, const size_t size, const MemoryManagement& management = MemoryManagement::Reference);
	bool openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement& management = MemoryManagement::Take, SetupCallback initCallback = nullptr, SetupCallback deinitCallback = nullptr);

	// save actually maps to both cc4group.save and cc4group.saveParent, thanks to default arguments (yes, thats the reason why they are separate in the C-API)
	bool save(const bool parent = false);
	// saveAs actually maps to both cc4group.saveAs and cc4group.saveAsOverwrite, as above
	bool saveAs(const std::string& path, const bool overwrite = false);
	bool saveToFd(const int fd);
	bool saveToFilePointer(FILE* file);
	bool saveWithWriteCallback(const WriteCallback callback, void* const arg = nullptr, size_t bufferSize = 0);

	bool extractAll(const std::string& path);

	// as with save, maps either to cc4group.extractSingle or cc4group.extractSingleRename
	bool extractSingle(const std::string& entryPath, const std::string& targetPath, const bool rename = true);

	std::optional<Data> getEntryData(const std::string& path);
	std::string getErrorMessage();
	int32_t getErrorCode();
	std::string getErrorMethod();
	std::string getErrorCauser();

	bool setMaker(const std::string& maker, const std::string& path = "", const bool recursive = false);
	bool setCreation(const int32_t creation, const std::string& path = "", const bool recursive = false);
	bool setOfficial(const bool official, const std::string& path = "", const bool recursive = false);
	bool setExecutable(const bool executable, const std::string& path);

	std::optional<EntryInfo> getEntryInfo(const std::string& path = "", const bool lazy = false);
	std::optional<std::vector<EntryInfo>> getEntryInfos(const std::string& path = "", const bool lazy = false);

	bool deleteEntry(const std::string& path, const bool recursive = false);
	bool renameEntry(const std::string& oldPath, const std::string& newPath);

	bool addFromDisk(const std::string& path, const std::string& targetPath, const AllowedEntryTypes allowedEntryTypes = AllowedEntryTypes::All);

	bool createDirectory(const std::string& path);

	bool createFile(const std::string& path);

	bool setEntryData(const std::string& path, const void* const data = nullptr, const size_t size = 0, const MemoryManagement& management = MemoryManagement::Copy);

	// to get the child group out of the optional in case of success, construct a new CppC4Group with the move constructor: CppC4Group child{std::move(*optionalChild)};
	std::optional<CppC4Group> openAsChild(const std::string& path);
	bool isChild() const;

	std::string getName() const;
	std::string getFullName() const;
};

inline bool operator&(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs)
{
	using T = std::underlying_type_t <CppC4Group::AllowedEntryTypes>;
	return static_cast<T>(lhs) & static_cast<T>(rhs);
}

inline CppC4Group::AllowedEntryTypes operator|(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs)
{
	using T = std::underlying_type_t <CppC4Group::AllowedEntryTypes>;
	return static_cast<CppC4Group::AllowedEntryTypes>(static_cast<T>(lhs) | static_cast<T>(rhs));
}

inline CppC4Group::AllowedEntryTypes& operator|=(CppC4Group::AllowedEntryTypes& lhs, CppC4Group::AllowedEntryTypes rhs)
{
	return lhs = lhs | rhs;
}