1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2020, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17/* Handles group files */
18
19#pragma once
20
21#include <CStdFile.h>
22#include <StdBuf.h>
23#include <StdCompiler.h>
24
25// C4Group-Rewind-warning:
26// The current C4Group-implementation cannot handle random file access very well,
27// because all files are written within a single zlib-stream.
28// For every out-of-order-file accessed a group-rewind must be performed, and every
29// single file up to the accessed file unpacked. As a workaround, all C4Groups are
30// packed in a file order matching the reading order of the engine.
31// If the reading order doesn't match the packing order, and a rewind has to be performed,
32// a warning is issued in Debug-builds of the engine. But since some components require
33// random access because they are loaded on-demand at runtime (e.g. global sounds), the
34// warning may be temp disabled for those files using C4GRP_DISABLE_REWINDWARN and
35// C4GRP_ENABLE_REWINDWARN. A ref counter keeps track of nested calls to those functions.
36//
37// If you add any new components to scenario or definition files, remember to adjust the
38// sort order lists in C4Components.h accordingly, and enforce a reading order for that
39// component.
40//
41// Maybe some day, someone will write a C4Group-implementation that is probably capable of
42// random access...
43#ifndef NDEBUG
44extern int iC4GroupRewindFilePtrNoWarn;
45#define C4GRP_DISABLE_REWINDWARN ++iC4GroupRewindFilePtrNoWarn;
46#define C4GRP_ENABLE_REWINDWARN --iC4GroupRewindFilePtrNoWarn;
47#else
48#define C4GRP_DISABLE_REWINDWARN ;
49#define C4GRP_ENABLE_REWINDWARN ;
50#endif
51
52const int C4GroupFileVer1 = 1, C4GroupFileVer2 = 2;
53
54const int C4GroupMaxMaker = 30,
55 C4GroupMaxPassword = 30,
56 C4GroupMaxError = 100;
57
58#define C4GroupFileID "RedWolf Design GrpFolder"
59
60void C4Group_SetMaker(const char *szMaker);
61void C4Group_SetTempPath(const char *szPath);
62const char *C4Group_GetTempPath();
63void C4Group_SetSortList(const char **ppSortList);
64void C4Group_SetProcessCallback(bool(*fnCallback)(const char *, int));
65bool C4Group_IsGroup(const char *szFilename);
66bool C4Group_CopyItem(const char *szSource, const char *szTarget, bool fNoSort = false, bool fResetAttributes = false);
67bool C4Group_MoveItem(const char *szSource, const char *szTarget, bool fNoSort = false);
68bool C4Group_DeleteItem(const char *szItem, bool fRecycle = false);
69bool C4Group_PackDirectoryTo(const char *szFilename, const char *szFilenameTo, bool overwriteTarget = false);
70bool C4Group_PackDirectory(const char *szFilename);
71bool C4Group_UnpackDirectory(const char *szFilename);
72bool C4Group_ExplodeDirectory(const char *szFilename);
73bool C4Group_ReadFile(const char *szFilename, char **pData, size_t *iSize);
74bool C4Group_GetFileCRC(const char *szFilename, uint32_t *pCRC32);
75bool C4Group_GetFileContentsCRC(const char *szFilename, uint32_t *pCRC32);
76bool C4Group_GetFileSHA1(const char *szFilename, uint8_t *pSHA1);
77
78bool EraseItemSafe(const char *szFilename);
79
80extern const char *C4CFN_FLS[];
81
82extern time_t C4Group_AssumeTimeOffset;
83
84#pragma pack (push, 1)
85
86class C4GroupHeader
87{
88public:
89 char id[24 + 4]{};
90 int32_t Ver1{}, Ver2{};
91 int32_t Entries{};
92 char Maker[C4GroupMaxMaker + 2]{};
93 char Password[C4GroupMaxPassword + 2]{};
94 int32_t Creation{}, Original{};
95 uint8_t fbuf[92]{};
96
97public:
98 void Init();
99};
100
101const char C4GECS_None = 0,
102 C4GECS_Old = 1,
103 C4GECS_New = 2;
104
105class C4GroupEntryCore
106{
107public:
108 char FileName[260]{};
109 int32_t Packed{}, ChildGroup{};
110 int32_t Size{}, __Unused{}, Offset{};
111 uint32_t Time{};
112 char HasCRC{};
113 unsigned int CRC{};
114 char Executable{};
115 uint8_t fbuf[26]{};
116};
117
118#pragma pack (pop)
119
120const int C4GRES_InGroup = 0,
121 C4GRES_OnDisk = 1,
122 C4GRES_InMemory = 2,
123 C4GRES_Deleted = 3;
124
125class C4GroupEntry : public C4GroupEntryCore
126{
127public:
128 ~C4GroupEntry();
129
130public:
131 char DiskPath[_MAX_PATH + 1]{};
132 int Status{};
133 bool DeleteOnDisk{};
134 bool HoldBuffer{};
135 bool BufferIsStdbuf{};
136 bool NoSort{};
137 uint8_t *bpMemBuf{};
138 C4GroupEntry *Next{};
139
140public:
141 void Set(const DirectoryIterator &iter, const char *szPath);
142};
143
144const int GRPF_Inactive = 0,
145 GRPF_File = 1,
146 GRPF_Folder = 2;
147
148class C4Group
149{
150public:
151 enum class OpenFlags
152 {
153 None = 0,
154 Overwrite = 1 << 0
155 };
156
157public:
158 C4Group();
159 ~C4Group();
160
161protected:
162 int Status;
163 char FileName[_MAX_PATH + 1];
164 // Parent status
165 C4Group *Mother;
166 bool ExclusiveChild;
167 // File & Folder
168 C4GroupEntry *SearchPtr;
169 CStdFile StdFile;
170 size_t iCurrFileSize; // size of last accessed file
171 // File only
172 size_t FilePtr;
173 int MotherOffset;
174 int EntryOffset;
175 bool Modified;
176 C4GroupHeader Head;
177 C4GroupEntry *FirstEntry;
178 // Folder only
179 DirectoryIterator FolderSearch;
180 C4GroupEntry FolderSearchEntry;
181 C4GroupEntry LastFolderSearchEntry;
182
183 bool StdOutput;
184 bool(*fnProcessCallback)(const char *, int);
185 char ErrorString[C4GroupMaxError + 1];
186 bool MadeOriginal;
187
188 bool NoSort; // If this flag is set, all entries will be marked NoSort in AddEntry
189
190public:
191 bool Open(const char *szGroupName, bool fCreate = false, OpenFlags flags = OpenFlags::None);
192 bool Close();
193 bool Save(bool fReOpen);
194 bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive = false);
195 bool OpenChild(const char *strEntry);
196 bool OpenMother();
197 bool Add(const char *szFiles);
198 bool Add(const char *szFile, const char *szAddAs);
199 bool Add(const char *szName, void *pBuffer, size_t iSize, bool fChild = false, bool fHoldBuffer = false, time_t iTime = 0, bool fExecutable = false);
200 bool Add(const char *szName, StdBuf &pBuffer, bool fChild = false, bool fHoldBuffer = false, time_t iTime = 0, bool fExecutable = false);
201 bool Add(const char *szName, StdStrBuf &pBuffer, bool fChild = false, bool fHoldBuffer = false, time_t iTime = 0, bool fExecutable = false);
202 bool Merge(const char *szFolders);
203 bool Move(const char *szFiles);
204 bool Move(const char *szFile, const char *szAddAs);
205 bool Extract(const char *szFiles, const char *szExtractTo = nullptr, const char *szExclude = nullptr);
206 bool ExtractEntry(const char *szFilename, const char *szExtractTo = nullptr);
207 bool Delete(const char *szFiles, bool fRecursive = false);
208 bool DeleteEntry(const char *szFilename, bool fRecycle = false);
209 bool Rename(const char *szFile, const char *szNewName);
210 bool Sort(const char *szSortList);
211 bool SortByList(const char **ppSortList, const char *szFilename = nullptr);
212 bool View(const char *szFiles);
213 bool GetOriginal();
214 bool AccessEntry(const char *szWildCard,
215 size_t *iSize = nullptr, char *sFileName = nullptr,
216 bool *fChild = nullptr);
217 bool AccessNextEntry(const char *szWildCard,
218 size_t *iSize = nullptr, char *sFileName = nullptr,
219 bool *fChild = nullptr);
220 bool LoadEntry(const char *szEntryName, char **lpbpBuf,
221 size_t *ipSize = nullptr, int iAppendZeros = 0);
222 bool LoadEntry(const char *szEntryName, StdBuf &Buf);
223 bool LoadEntryString(const char *szEntryName, StdStrBuf &Buf);
224 bool FindEntry(const char *szWildCard,
225 char *sFileName = nullptr,
226 size_t *iSize = nullptr,
227 bool *fChild = nullptr);
228 bool FindNextEntry(const char *szWildCard,
229 char *sFileName = nullptr,
230 size_t *iSize = nullptr,
231 bool *fChild = nullptr,
232 bool fStartAtFilename = false);
233 bool Read(void *pBuffer, size_t iSize);
234 bool Advance(size_t iOffset);
235 void SetMaker(const char *szMaker);
236 void SetStdOutput(bool fStatus);
237 void MakeOriginal(bool fOriginal);
238 void ResetSearch();
239 const char *GetError();
240 const char *GetMaker();
241 const char *GetPassword();
242 const char *GetName();
243 StdStrBuf GetFullName() const;
244 int EntryCount(const char *szWildCard = nullptr);
245 int EntrySize(const char *szWildCard = nullptr);
246 size_t AccessedEntrySize() { return iCurrFileSize; } // retrieve size of last accessed entry
247 uint32_t EntryTime(const char *szFilename);
248 unsigned int EntryCRC32(const char *szWildCard = nullptr);
249 int32_t GetCreation();
250 int GetStatus();
251 inline bool IsOpen() { return Status != GRPF_Inactive; }
252 C4Group *GetMother();
253 inline bool IsPacked() { return Status == GRPF_File; }
254 inline bool HasPackedMother() { if (!Mother) return false; return Mother->IsPacked(); }
255 inline bool SetNoSort(bool fNoSort) { NoSort = fNoSort; return true; }
256#ifndef NDEBUG
257 void PrintInternals(const char *szIndent = nullptr);
258#endif
259
260protected:
261 void Init();
262 void Default();
263 void Clear();
264 bool EnsureChildFilePtr(C4Group *pChild);
265 bool CloseExclusiveMother();
266 bool Error(const char *szStatus);
267 bool OpenReal(const char *szGroupName);
268 bool OpenRealGrpFile();
269 bool SetFilePtr(size_t iOffset);
270 bool RewindFilePtr();
271 bool AdvanceFilePtr(size_t iOffset, C4Group *pByChild = nullptr);
272 bool AddEntry(int status,
273 bool childgroup,
274 const char *fname,
275 size_t size,
276 time_t time,
277 char cCRC,
278 unsigned int iCRC,
279 const char *entryname = nullptr,
280 uint8_t *membuf = nullptr,
281 bool fDeleteOnDisk = false,
282 bool fHoldBuffer = false,
283 bool fExecutable = false,
284 bool fBufferIsStdbuf = false);
285 bool AddEntryOnDisk(const char *szFilename, const char *szAddAs = nullptr, bool fMove = false);
286 bool SetFilePtr2Entry(const char *szName, C4Group *pByChild = nullptr);
287 bool AppendEntry2StdFile(C4GroupEntry *centry, CStdFile &stdfile);
288 C4GroupEntry *GetEntry(const char *szName);
289 C4GroupEntry *SearchNextEntry(const char *szName);
290 C4GroupEntry *GetNextFolderEntry();
291 bool CalcCRC32(C4GroupEntry *pEntry);
292};
293
294inline C4Group::OpenFlags operator|(const C4Group::OpenFlags first, const C4Group::OpenFlags second) noexcept
295{
296 return static_cast<C4Group::OpenFlags>(std::to_underlying(value: first) | std::to_underlying(value: second));
297}
298
299inline C4Group::OpenFlags operator&(const C4Group::OpenFlags first, const C4Group::OpenFlags second) noexcept
300{
301 return static_cast<C4Group::OpenFlags>(std::to_underlying(value: first) & std::to_underlying(value: second));
302}
303