1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Handles group files */
19 
20 #ifndef INC_C4Group
21 #define INC_C4Group
22 
23 #ifdef HAVE_IO_H
24 #include <io.h>
25 #endif
26 #include "c4group/CStdFile.h"
27 
28 // C4Group-Rewind-warning:
29 // The current C4Group-implementation cannot handle random file access very well,
30 // because all files are written within a single zlib-stream.
31 // For every out-of-order-file accessed a group-rewind must be performed, and every
32 // single file up to the accessed file unpacked. As a workaround, all C4Groups are
33 // packed in a file order matching the reading order of the engine.
34 // If the reading order doesn't match the packing order, and a rewind has to be performed,
35 // a warning is issued in Debug-builds of the engine. But since some components require
36 // random access because they are loaded on-demand at runtime (e.g. global sounds), the
37 // warning may be temp disabled for those files using C4GRP_DISABLE_REWINDWARN and
38 // C4GRP_ENABLE_REWINDWARN. A ref counter keeps track of nested calls to those functions.
39 //
40 // If you add any new components to scenario or definition files, remember to adjust the
41 // sort order lists in C4Components.h accordingly, and enforce a reading order for that
42 // component.
43 //
44 // Maybe some day, someone will write a C4Group-implementation that is probably capable of
45 // random access...
46 #ifdef _DEBUG
47 extern int iC4GroupRewindFilePtrNoWarn;
48 #define C4GRP_DISABLE_REWINDWARN ++iC4GroupRewindFilePtrNoWarn;
49 #define C4GRP_ENABLE_REWINDWARN --iC4GroupRewindFilePtrNoWarn;
50 #else
51 #define C4GRP_DISABLE_REWINDWARN ;
52 #define C4GRP_ENABLE_REWINDWARN ;
53 #endif
54 
55 const int C4GroupFileVer1=1, C4GroupFileVer2=2;
56 
57 const int C4GroupMaxError = 100;
58 
59 const int32_t C4GroupSwapThreshold = 10 * 1024 * 1024;
60 
61 #define C4GroupFileID "RedWolf Design GrpFolder"
62 
63 bool C4Group_TestIgnore(const char *szFilename);
64 void C4Group_SetTempPath(const char *szPath);
65 const char* C4Group_GetTempPath();
66 void C4Group_SetSortList(const char **ppSortList);
67 void C4Group_SetProcessCallback(bool (*fnCallback)(const char *, int));
68 bool C4Group_IsGroup(const char *szFilename);
69 bool C4Group_CopyItem(const char *szSource, const char *szTarget, bool fNoSort=false, bool fResetAttributes=false);
70 bool C4Group_MoveItem(const char *szSource, const char *szTarget, bool fNoSort=false);
71 bool C4Group_DeleteItem(const char *szItem, bool fRecycle=false);
72 bool C4Group_PackDirectoryTo(const char *szFilename, const char *szFilenameTo);
73 bool C4Group_PackDirectory(const char *szFilename);
74 bool C4Group_UnpackDirectory(const char *szFilename);
75 bool C4Group_ExplodeDirectory(const char *szFilename);
76 bool C4Group_ReadFile(const char *szFilename, char **pData, size_t *iSize);
77 
78 extern const char *C4CFN_FLS[];
79 
80 #pragma pack (push, 1)
81 
82 struct C4GroupHeader
83 {
84 	char id[24+4] = C4GroupFileID;
85 	int Ver1 = C4GroupFileVer1;
86 	int Ver2 = C4GroupFileVer2;
87 	int Entries = 0;
88 	char reserved[164] = { 0 };
89 };
90 
91 struct C4GroupEntryCore
92 {
93 	char FileName[260] = { 0 };
94 	int32_t Packed = 0, ChildGroup = 0;
95 	int32_t Size = 0, reserved1 = 0, Offset = 0;
96 	int32_t reserved2 = 0;
97 	char reserved3 = '\0';
98 	unsigned int reserved4 = 0;
99 	char Executable = '\0';
100 	BYTE fbuf[26] = { 0 };
101 };
102 
103 #pragma pack (pop)
104 
105 class C4GroupEntry: public C4GroupEntryCore
106 {
107 public:
108 	~C4GroupEntry();
109 
110 	enum EntryStatus
111 	{
112 		C4GRES_InGroup,
113 		C4GRES_OnDisk,
114 		C4GRES_InMemory,
115 		C4GRES_Deleted
116 	};
117 
118 public:
119 	char DiskPath[_MAX_PATH + 1] = { 0 };
120 	EntryStatus Status = C4GRES_InGroup;
121 	bool DeleteOnDisk = false;
122 	bool HoldBuffer = false;
123 	bool BufferIsStdbuf = false;
124 	bool NoSort = false;
125 	BYTE *bpMemBuf = nullptr;
126 	C4GroupEntry *Next = nullptr;
127 public:
128 	void Set(const DirectoryIterator & iter, const char * szPath);
129 };
130 
131 class C4Group : public CStdStream
132 {
133 	struct P;
134 	std::unique_ptr<P> p;
135 public:
136 	C4Group();
137 	~C4Group() override;
138 	C4Group(C4Group &&) = default;
139 	C4Group &operator=(C4Group &&) = default;
140 
141 protected:
142 	// C4Update requires these to be available by a subclass (C4GroupEx)
143 	C4GroupHeader Head;
144 	C4GroupEntry *GetEntry(const char *szName);
145 	void Clear();
146 
147 public:
148 	bool Open(const char *szGroupName, bool fCreate=false);
149 	bool Close();
150 	bool Save(bool fReOpen);
151 	bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false);
152 	bool OpenChild(const char* strEntry);
153 	bool OpenMother();
154 	bool Add(const char *szFile, const char *szAddAs);
155 	bool Add(const char *szName, void *pBuffer, int iSize, bool fChild = false, bool fHoldBuffer = false, bool fExecutable = false);
156 	bool Add(const char *szName, StdBuf &pBuffer, bool fChild = false, bool fHoldBuffer = false, bool fExecutable = false);
157 	bool Add(const char *szName, StdStrBuf &pBuffer, bool fChild = false, bool fHoldBuffer = false, bool fExecutable = false);
158 	bool Merge(const char *szFolders);
159 	bool Move(const char *szFile, const char *szAddAs);
160 	bool Extract(const char *szFiles, const char *szExtractTo=nullptr, const char *szExclude=nullptr);
161 	bool ExtractEntry(const char *szFilename, const char *szExtractTo=nullptr);
162 	bool Delete(const char *szFiles, bool fRecursive = false);
163 	bool DeleteEntry(const char *szFilename, bool fRecycle=false);
164 	bool Rename(const char *szFile, const char *szNewName);
165 	bool Sort(const char *szSortList);
166 	bool SortByList(const char **ppSortList, const char *szFilename=nullptr);
167 	bool AccessEntry(const char *szWildCard,
168 	                 size_t *iSize=nullptr, char *sFileName=nullptr,
169 	                 bool NeedsToBeAGroup = false);
170 	bool AccessNextEntry(const char *szWildCard,
171 	                     size_t *iSize=nullptr, char *sFileName=nullptr,
172 	                     bool fStartAtFilename=false);
173 	bool LoadEntry(const char *szEntryName, char **lpbpBuf,
174 	               size_t *ipSize=nullptr, int iAppendZeros=0);
175 	bool LoadEntry(const char *szEntryName, StdBuf * Buf);
LoadEntry(const StdStrBuf & name,StdBuf * Buf)176 	bool LoadEntry(const StdStrBuf & name, StdBuf * Buf) { return LoadEntry(name.getData(), Buf); }
177 	bool LoadEntryString(const char *szEntryName, StdStrBuf * Buf);
LoadEntryString(const StdStrBuf & name,StdStrBuf * Buf)178 	bool LoadEntryString(const StdStrBuf & name, StdStrBuf * Buf) { return LoadEntryString(name.getData(), Buf); }
179 	bool FindEntry(const char *szWildCard,
180 	               StdStrBuf *sFileName=nullptr,
181 	               size_t *iSize=nullptr);
FindEntry(const char * szWildCard,char * sFileName)182 	bool FindEntry(const char *szWildCard,
183 	               char *sFileName)
184 	{
185 		StdStrBuf name;
186 		bool r = FindEntry(szWildCard, &name);
187 		if(sFileName) SCopy(name.getData(),sFileName);
188 		return r;
189 	}
190 	bool FindNextEntry(const char *szWildCard,
191 	                   StdStrBuf *sFileName=nullptr,
192 	                   size_t *iSize=nullptr,
193 	                   bool fStartAtFilename=false);
194 	bool FindNextEntry(const char *szWildCard,
195 	                   char *sFileName,
196 	                   size_t *iSize=nullptr,
197 	                   bool fStartAtFilename=false)
198 	{
199 		StdStrBuf name(fStartAtFilename ? sFileName : "");
200 		bool r = FindNextEntry(szWildCard, &name, iSize, fStartAtFilename);
201 		if (r && sFileName) SCopy(name.getData(),sFileName);
202 		return r;
203 	}
204 	bool Read(void *pBuffer, size_t iSize) override;
205 	bool Advance(int iOffset) override;
206 	void SetStdOutput(bool fStatus);
207 	void ResetSearch(bool reload_contents=false); // reset search pointer so calls to FindNextEntry find first entry again. if reload_contents is set, the file list for directories is also refreshed.
208 	const char *GetError();
209 	const char *GetName() const;
210 	StdStrBuf GetFullName() const;
211 	int EntryCount(const char *szWildCard=nullptr);
212 	size_t EntrySize(const char *szWildCard=nullptr);
213 	size_t AccessedEntrySize() const override; // retrieve size of last accessed entry
214 	unsigned int EntryCRC32(const char *szWildCard=nullptr);
215 	bool IsOpen() const;
216 	C4Group *GetMother();
217 	bool IsPacked() const;
218 	bool HasPackedMother() const;
219 	bool SetNoSort(bool fNoSort);
220 	int PreCacheEntries(const char *szSearchPattern, bool cache_previous=false); // pre-load entries to memory. return number of loaded entries.
221 
222 	const C4GroupHeader &GetHeader() const;
223 	const C4GroupEntry *GetFirstEntry() const;
224 
225 private:
226 	void Init();
227 	bool EnsureChildFilePtr(C4Group *pChild);
228 	bool CloseExclusiveMother();
229 	bool Error(const char *szStatus);
230 	bool OpenReal(const char *szGroupName);
231 	bool OpenRealGrpFile();
232 	bool SetFilePtr(int iOffset);
233 	bool RewindFilePtr();
234 	bool AdvanceFilePtr(int iOffset);
235 	bool AddEntry(C4GroupEntry::EntryStatus status,
236 	              bool childgroup,
237 	              const char *fname,
238 	              long size,
239 	              const char *entryname = nullptr,
240 	              BYTE *membuf = nullptr,
241 	              bool fDeleteOnDisk = false,
242 	              bool fHoldBuffer = false,
243 	              bool fExecutable = false,
244 	              bool fBufferIsStdbuf = false);
245 	bool AddEntryOnDisk(const char *szFilename, const char *szAddAs=nullptr, bool fMove=false);
246 	bool SetFilePtr2Entry(const char *szName, bool NeedsToBeAGroup = false);
247 	bool AppendEntry2StdFile(C4GroupEntry *centry, CStdFile &stdfile);
248 	C4GroupEntry *SearchNextEntry(const char *szName);
249 	C4GroupEntry *GetNextFolderEntry();
250 	uint32_t CalcCRC32(C4GroupEntry *pEntry);
251 	void PreCacheEntry(C4GroupEntry * p);
252 };
253 
254 #endif
255