File: ArchiveScanner.h

package info (click to toggle)
spring 106.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 55,260 kB
  • sloc: cpp: 543,946; ansic: 44,800; python: 12,575; java: 12,201; awk: 5,889; sh: 1,796; asm: 1,546; xml: 655; perl: 405; php: 211; objc: 194; makefile: 76; sed: 2
file content (254 lines) | stat: -rw-r--r-- 9,989 bytes parent folder | download | duplicates (3)
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */

#ifndef _ARCHIVE_SCANNER_H
#define _ARCHIVE_SCANNER_H

#include <cstring> // memset
#include <string>
#include <deque>
#include <vector>

#include "System/Info.h"
#include "System/Sync/SHA512.hpp"
#include "System/UnorderedMap.hpp"

class IArchive;
class IFileFilter;
class LuaTable;

/*
 * This class searches through a given directory and its sub-directories looking
 * for archive files.
 * When it finds one, it figures out what kind of archive it is (i.e. if it is a
 * map or a mod currently). This information is cached, so that only modified
 * archives are actually opened. The information can then be retreived by the
 * mod and map selectors.
 *
 * The archive namespace is global, so it is not allowed to have an archive with
 * the same name in more than one folder.
 */

namespace modtype
{
	enum {
		hidden = 0,
		primary = 1,
		reserved = 2,
		map = 3,
		base = 4,
		menu = 5
	};
}

class CArchiveScanner
{
public:
	class ArchiveData
	{
	public:
		ArchiveData() {}
		ArchiveData(const LuaTable& archiveTable, bool fromCache);

		/*
		 * These methods are only here for convenience and compile time checks.
		 * These are all the properties used engine internally.
		 * With these methods, we prevent spreading key strings through the
		 * code, which could provoke runtime bugs when edited wrong.
		 * There may well be other info-times supplied by the archive.
		 */
		std::string GetName() const { return GetInfoValueString("name_pure"); }          /// ex:  Original Total Annihilation
		std::string GetNameVersioned() const { return GetInfoValueString("name"); }      /// ex:  Original Total Annihilation v2.3
		std::string GetShortName() const { return GetInfoValueString("shortName"); }     /// ex:  OTA
		std::string GetVersion() const { return GetInfoValueString("version"); }         /// ex:  v2.3
		std::string GetMutator() const { return GetInfoValueString("mutator"); }         /// ex:  deployment
		std::string GetGame() const { return GetInfoValueString("game"); }               /// ex:  Total Annihilation
		std::string GetShortGame() const { return GetInfoValueString("shortGame"); }     /// ex:  TA
		std::string GetDescription() const { return GetInfoValueString("description"); } /// ex:  Little units blowing up other little units
		std::string GetMapFile() const { return GetInfoValueString("mapFile"); }         /// in case its a map, store location of smf file
		int GetModType() const { return GetInfoValueInteger("modType"); }                /// 0=hidden, 1=primary, (2=unused), 3=map, 4=base, 5=menu
		bool GetOnlyLocal() const { return GetInfoValueBool("onlyLocal"); }              /// if true spring will not listen for incoming connections

		const std::vector< std::pair<std::string, InfoItem> >& GetInfo() const { return infoItems; }
		std::vector<InfoItem> GetInfoItems() const;

		const std::vector<std::string>& GetDependencies() const { return dependencies; }
		std::vector<std::string>& GetDependencies() { return dependencies; }

		const std::vector<std::string>& GetReplaces() const { return replaces; }
		std::vector<std::string>& GetReplaces() { return replaces; }

		void SetInfoItemValueString(const std::string& key, const std::string& value);
		void SetInfoItemValueInteger(const std::string& key, int value);
		void SetInfoItemValueFloat(const std::string& key, float value);
		void SetInfoItemValueBool(const std::string& key, bool value);

		bool IsValid(std::string& error) const;
		bool IsEmpty() const { return infoItems.empty(); }

		bool IsMap() const { const int mt = GetModType(); return (mt == modtype::map); }
		bool IsGame() const { const int mt = GetModType(); return (mt == modtype::hidden || mt == modtype::primary); }
		bool IsBase() const { const int mt = GetModType(); return (mt == modtype::base); }
		bool IsMenu() const { const int mt = GetModType(); return (mt == modtype::menu); }

		static bool IsReservedKey(const std::string& keyLower);
		static std::string GetKeyDescription(const std::string& keyLower);

	private:
		InfoValueType GetInfoValueType(const std::string& key) const;
		std::string GetInfoValueString(const std::string& key) const;
		int GetInfoValueInteger(const std::string& key) const;
		float GetInfoValueFloat(const std::string& key) const;
		bool GetInfoValueBool(const std::string& key) const;

		const InfoItem* GetInfoItem(const std::string& key) const;

		// NB: may invalidate existing references
		InfoItem& GetAddInfoItem(const std::string& key);

		std::vector< std::pair<std::string, InfoItem> > infoItems;

		std::vector<std::string> dependencies; /// Archives we depend on
		std::vector<std::string> replaces;     /// This archive obsoletes these archives
	};

	CArchiveScanner();
	~CArchiveScanner();

public:
	const std::string& GetFilepath() const { return cachefile; }

	static const char* GetMapHelperContentName() { return "Map Helper v1"; }
	static const char* GetSpringBaseContentName() { return "Spring content v1"; }
	static uint32_t GetNumScannedArchives();

	std::vector<std::string> GetMaps() const;
	std::vector<ArchiveData> GetPrimaryMods() const;
	std::vector<ArchiveData> GetAllMods() const;
	std::vector<ArchiveData> GetAllArchives() const;

	std::vector<std::string> GetAllArchivesUsedBy(const std::string& rootArchive) const;

public:
	/// checksum of the given archive (without dependencies)
	sha512::raw_digest GetArchiveSingleChecksumBytes(const std::string& name);
	/// calculate checksum of the given archive and all its dependencies
	sha512::raw_digest GetArchiveCompleteChecksumBytes(const std::string& name);

	/// first 4 bytes of single checksum (TODO: get rid of this in unitsync)
	uint32_t GetArchiveSingleChecksum(const std::string& name) { return *reinterpret_cast<const uint32_t*>(&GetArchiveSingleChecksumBytes(name)[0]); }
	/// first 4 bytes of complete checksum (TODO: get rid of this in ExternalAI)
	uint32_t GetArchiveCompleteChecksum(const std::string& name) { return *reinterpret_cast<const uint32_t*>(&GetArchiveCompleteChecksumBytes(name)[0]); }

	/// like GetArchiveCompleteChecksum, throws exception if mismatch
	void CheckArchive(const std::string& name, const sha512::raw_digest& serverChecksum, sha512::raw_digest& clientChecksum);
	void ScanArchive(const std::string& fullName, bool checksum = false);
	void ScanAllDirs();
	void Clear();
	void Reload();

	std::string ArchiveFromName(const std::string& versionedName) const;
	std::string NameFromArchive(const std::string& archiveName) const;
	std::string GameHumanNameFromArchive(const std::string& archiveName) const;
	std::string  MapHumanNameFromArchive(const std::string& archiveName) const;
	std::string GetArchivePath(const std::string& archiveName) const;
	std::string MapNameToMapFile(const std::string& versionedMapName) const;
	ArchiveData GetArchiveData(const std::string& versionedName) const;
	ArchiveData GetArchiveDataByArchive(const std::string& archive) const;


private:
	struct ArchiveInfo {
		ArchiveInfo() {
			memset(checksum, 0, sizeof(checksum));
		}

		std::string path;             // FileSystem::GetDirectory(origName)
		std::string origName;         // non-lowercased name
		std::string replaced;         // if not empty, use this archive instead
		std::string archiveDataPath;  // path to {mod,map}info.lua for .sdd's

		ArchiveData archiveData;

		uint32_t modified = 0;
		uint32_t modifiedArchiveData = 0;
		uint8_t checksum[sha512::SHA_LEN];

		bool updated = false;
		bool hashed = false;
	};
	struct BrokenArchive {
		std::string name;         // lower-case
		std::string path;         // FileSystem::GetDirectory(origName)
		std::string problem;

		uint32_t modified = 0;
		bool updated = false;
	};

private:
	ArchiveInfo& GetAddArchiveInfo(const std::string& lcfn);
	BrokenArchive& GetAddBrokenArchive(const std::string& lcfn);

	void ScanDirs(const std::vector<std::string>& dirs);
	void ScanDir(const std::string& curPath, std::deque<std::string>& foundArchives);

	/// scan mapinfo / modinfo lua files
	bool ScanArchiveLua(IArchive* ar, const std::string& fileName, ArchiveInfo& ai, std::string& err);

	/**
	 * scan archive for map file
	 * @return file name if found, empty string if not
	 */
	std::string SearchMapFile(const IArchive* ar, std::string& error);


	void ReadCacheData(const std::string& filename);
	void WriteCacheData(const std::string& filename);

	IFileFilter* CreateIgnoreFilter(IArchive* ar);

	/**
	 * Get hash of the data in the specified archive.
	 * Returns false if file could not be opened.
	 */
	bool GetArchiveChecksum(const std::string& filename, ArchiveInfo& archiveInfo);

	bool CheckCachedData(const std::string& fullName, unsigned& modified, bool doChecksum);

	/**
	 * Returns a value > 0 if the file is rated as a meta-file.
	 * First class means, it is essential for the archive, and will be read by
	 * pretty much every software that scans through archives.
	 * Second class means, it is not essential for the archive, but it may be
	 * read by certain tools that generate spezialized indices while scanning
	 * through archives.
	 * Examples:
	 * "objects3d/ddm.s3o" -> 0
	 * "ModInfo.lua" -> 1
	 * "maps/dsd.smf" -> 1
	 * "LuaAI.lua" -> 2
	 * "sides/arm.bmp" -> 2
	 * @param filePath file path, relative to archive-root
	 * @return 0 if the file is not a meta-file,
	 *         1 if the file is a first class meta-file,
	 *         2 if the file is a second class meta-file
	 */
	static int GetMetaFileClass(const std::string& filePath);
	static bool CheckCompression(const IArchive* ar, const std::string& fullName, std::string& error);

private:
	spring::unordered_map<std::string, size_t> archiveInfosIndex;
	spring::unordered_map<std::string, size_t> brokenArchivesIndex;

	std::vector<ArchiveInfo> archiveInfos;
	std::vector<BrokenArchive> brokenArchives;

	std::string cachefile;

	bool isDirty = false;
	bool isInScan = false;
};

extern CArchiveScanner* archiveScanner;

#endif // _ARCHIVE_SCANNER_H