Пример #1
0
/// Returns a pointer to a newly created suitable subclass of CArchiveBase
CArchiveBase* CArchiveFactory::OpenArchive(const std::string& fileName, const std::string& type)
{
	std::string ext = type;
	if (type.empty()) {
		ext = filesystem.GetExtension(fileName);
	}

	std::string fn = filesystem.LocateFile(fileName);

	CArchiveBase* ret = NULL;

	if (ext == "sd7") {
		ret = new CArchive7Zip(fn);
	} else if (ext == "sdz") {
		ret = new CArchiveZip(fn);
	} else if (ext == "sdd") {
		ret = new CArchiveDir(fn);
	} else if (ext == "sdp") {
		ret = new CArchivePool(fn);
	}

	if (ret && ret->IsOpen()) {
		return ret;
	}

	delete ret;
	return NULL;
}
Пример #2
0
/**
 * Get CRC of the data in the specified archive.
 * Returns 0 if file could not be opened.
 */
unsigned int CArchiveScanner::GetCRC(const std::string& arcName)
{
	CRC crc;
	CArchiveBase* ar;
	std::list<std::string> files;

	//! Try to open an archive
	ar = CArchiveFactory::OpenArchive(arcName);
	if (!ar) {
		return 0; // It wasn't an archive
	}

	//! Load ignore list.
	IFileFilter* ignore = CreateIgnoreFilter(ar);

	for (unsigned fid = 0; fid != ar->NumFiles(); ++fid) {
		std::string name;
		int size;
		ar->FileInfo(fid, name, size);

		if (ignore->Match(name)) {
			continue;
		}

		StringToLowerInPlace(name); //! case insensitive hash
		files.push_back(name);
	}

	files.sort();

	//! Add all files in sorted order
	for (std::list<std::string>::iterator i = files.begin(); i != files.end(); ++i) {
		const unsigned int nameCRC = CRC().Update(i->data(), i->size()).GetDigest();
		const unsigned fid = ar->FindFile(*i);
		const unsigned int dataCRC = ar->GetCrc32(fid);
		crc.Update(nameCRC);
		crc.Update(dataCRC);
	}

	delete ignore;
	delete ar;

	unsigned int digest = crc.GetDigest();

	//! A value of 0 is used to indicate no crc.. so never return that
	//! Shouldn't happen all that often
	if (digest == 0) {
		return 4711;
	} else {
		return digest;
	}
}
Пример #3
0
/** Get CRC of the data in the specified archive.
    Returns 0 if file could not be opened. */
unsigned int CArchiveScanner::GetCRC(const string& arcName)
{
    CRC crc;
    CArchiveBase* ar;
    std::list<string> files;

    // Try to open an archive
    ar = CArchiveFactory::OpenArchive(arcName);
    if (!ar) {
        return 0; // It wasn't an archive
    }

    // Load ignore list.
    IFileFilter* ignore = CreateIgnoreFilter(ar);

    string name;
    int size;
    // Sort all file paths for deterministic behaviour
    for (int cur = 0; (cur = ar->FindFiles(cur, &name, &size)); /* no-op */) {
        if (ignore->Match(name)) {
            continue;
        }
        const string lower = StringToLower(name); // case insensitive hash
        files.push_back(lower);
    }
    files.sort();

    // Add all files in sorted order
    for (std::list<string>::iterator i = files.begin(); i != files.end(); i++ ) {
        const unsigned int nameCRC = CRC().Update(i->data(), i->size()).GetDigest();
        const unsigned int dataCRC = ar->GetCrc32(*i);
        crc.Update(nameCRC);
        crc.Update(dataCRC);
    }

    delete ignore;
    delete ar;

    unsigned int digest = crc.GetDigest();

    // A value of 0 is used to indicate no crc.. so never return that
    // Shouldn't happen all that often
    if (digest == 0) {
        return 4711;
    } else {
        return digest;
    }
}
Пример #4
0
// Returns a pointer to a newly created suitable subclass of CArchiveBase
CArchiveBase* CArchiveFactory::OpenArchive(const std::string& fileName,
                                           const std::string& type)
{
	std::string ext = type;
	if (type.empty()) {
		ext = filesystem.GetExtension(fileName);
	}

	     if (ext == "sd7") { ext = "7z";  }
	else if (ext == "sdz") { ext = "zip"; }
	else if (ext == "sdd") { ext = "dir"; }
	else if (ext == "sdp") { ext = "pool"; }
	else if ((ext == "ccx") || (ext == "hpi") || (ext == "ufo") ||
	         (ext == "gp3") || (ext == "gp4") || (ext == "swx")) {
		ext = "hpi";
	}
	

	std::string fn = filesystem.LocateFile(fileName);

	CArchiveBase* ret = NULL;

	if (ext == "7z") {
		ret = new CArchive7Zip(fn);
	} else if (ext == "zip") {
		ret = new CArchiveZip(fn);
	} else if (ext == "dir") {
		ret = new CArchiveDir(fn);
	} else if (ext == "pool") {
		ret = new CArchivePool(fn);

	} else if (ext == "hpi") {
		ret = new CArchiveHPI(fn);
	}

	if (ret && ret->IsOpen()) {
		return ret;
	}

	delete ret;
	return NULL;
}
// Returns a pointer to a newly created suitable subclass of CArchiveBase
CArchiveBase* CArchiveFactory::OpenArchive(const std::string& fileName)
{
	std::string ext = StringToLower(filesystem.GetExtension(fileName));
	std::string fn = filesystem.LocateFile(fileName);

	CArchiveBase* ret = NULL;

	if (ext == "sd7")
		ret = SAFE_NEW CArchive7Zip(fn);
	else if (ext == "sdz")
		ret = SAFE_NEW CArchiveZip(fn);
	else if (ext == "sdd")
		ret = SAFE_NEW CArchiveDir(fn);
	else if ((ext == "ccx") || (ext == "hpi") || (ext == "ufo") || (ext == "gp3") || (ext == "gp4") || (ext == "swx"))
		ret = SAFE_NEW CArchiveHPI(fn);

	if (ret && ret->IsOpen())
		return ret;

	delete ret;
	return NULL;
}
// Returns a pointer to a newly created suitable subclass of CArchiveBase
CArchiveBase* CArchiveFactory::OpenArchive(const std::string& fileName)
{
	std::string ext = StringToLower(filesystem.GetExtension(fileName));

	std::vector<std::string> filenames = filesystem.GetNativeFilenames(fileName);
	for (std::vector<std::string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
		CArchiveBase* ret = NULL;

		if (ext == "sd7")
			ret = new CArchive7Zip(*it);
		else if (ext == "sdz")
			ret = new CArchiveZip(*it);
		else if (ext == "sdd")
			ret = new CArchiveDir(*it);
		else if ((ext == "ccx") || (ext == "hpi") || (ext == "ufo") || (ext == "gp3") || (ext == "gp4") || (ext == "swx"))
			ret = new CArchiveHPI(*it);

		if (ret && ret->IsOpen())
			return ret;

		delete ret;
	}
	return NULL;
}
// Returns a pointer to a newly created suitable subclass of CArchiveBase
CArchiveBase* CArchiveFactory::OpenArchive(const string& fileName)
{
	string ext = fileName.substr(fileName.find_last_of('.') + 1);
	transform(ext.begin(), ext.end(), ext.begin(), (int (*)(int))tolower);

	CArchiveBase* ret = NULL;

	if (ext == "sd7")
		ret = new CArchive7ZipDll(fileName);
	if (ext == "sdz")
		ret = new CArchiveZip(fileName);
	else if ((ext == "ccx") || (ext == "hpi") || (ext == "ufo") || (ext == "gp3") || (ext == "gp4") || (ext == "swx"))
		ret = new CArchiveHPI(fileName);

	if (!ret)
		return NULL;

	if (!ret->IsOpen()) {
		delete ret;
		return NULL;
	}

	return ret;
}
Пример #8
0
void CArchiveScanner::ScanArchive(const std::string& fullName, bool doChecksum)
{
	struct stat info;
	stat(fullName.c_str(), &info);

	const std::string fn    = filesystem.GetFilename(fullName);
	const std::string fpath = filesystem.GetDirectory(fullName);
	const std::string lcfn  = StringToLower(fn);

	//! Determine whether this archive has earlier be found to be broken
	std::map<std::string, BrokenArchive>::iterator bai = brokenArchives.find(lcfn);
	if (bai != brokenArchives.end()) {
		if ((unsigned)info.st_mtime == bai->second.modified && fpath == bai->second.path) {
			bai->second.updated = true;
			return;
		}
	}

	//! Determine whether to rely on the cached info or not
	bool cached = false;

	std::map<std::string, ArchiveInfo>::iterator aii = archiveInfo.find(lcfn);
	if (aii != archiveInfo.end()) {

		//! This archive may have been obsoleted, do not process it if so
		if (aii->second.replaced.length() > 0) {
			return;
		}

		if ((unsigned)info.st_mtime == aii->second.modified && fpath == aii->second.path) {
			cached = true;
			aii->second.updated = true;
		}

		//! If we are here, we could have invalid info in the cache
		//! Force a reread if it's a directory archive, as st_mtime only
		//! reflects changes to the directory itself, not the contents.
		if (!cached) {
			archiveInfo.erase(aii);
		}
	}

	//! Time to parse the info we are interested in
	if (cached) {
		//! If cached is true, aii will point to the archive
		if (doChecksum && (aii->second.checksum == 0))
			aii->second.checksum = GetCRC(fullName);
	} else {
		CArchiveBase* ar = CArchiveFactory::OpenArchive(fullName);
		if (!ar || !ar->IsOpen()) {
			logOutput.Print("Unable to open archive: %s", fullName.c_str());
			return;
		}

		ArchiveInfo ai;
		std::string error = "";

		std::string mapfile;
		bool hasModinfo = ar->FileExists("modinfo.lua");
		bool hasMapinfo = ar->FileExists("mapinfo.lua");
		
		//! check for smf/sm3 and if the uncompression of important files is too costy
		for (unsigned fid = 0; fid != ar->NumFiles(); ++fid)
		{
			std::string name;
			int size;
			ar->FileInfo(fid, name, size);
			const std::string lowerName = StringToLower(name);
			const std::string ext = filesystem.GetExtension(lowerName);

			if ((ext == "smf") || (ext == "sm3")) {
				mapfile = name;
			}

			const unsigned char metaFileClass = GetMetaFileClass(lowerName);
			if ((metaFileClass != 0) && !(ar->HasLowReadingCost(fid))) {
				//! is a meta-file and not cheap to read
				if (metaFileClass == 1) {
					//! 1st class
					error = "Unpacking/reading cost for meta file " + name
							+ " is too high, please repack the archive (make sure to use a non-solid algorithm, if applicable)";
					break;
				} else if (metaFileClass == 2) {
					//! 2nd class
					logOutput.Print(LOG_ARCHIVESCANNER,
							"Warning: Archive %s: The cost for reading a 2nd class meta-file is too high: %s",
							fullName.c_str(), name.c_str());
				}
			}
		}

		if (!error.empty()) {
			//! we already have an error, no further evaluation required
		} if (hasMapinfo || !mapfile.empty()) {
			//! it is a map
			if (hasMapinfo) {
				ScanArchiveLua(ar, "mapinfo.lua", ai, error);
			} else if (hasModinfo) {
				//! backwards-compat for modinfo.lua in maps
				ScanArchiveLua(ar, "modinfo.lua", ai, error);
			}
			if (ai.archiveData.GetName().empty()) {
				//! FIXME The name will never be empty, if version is set (see HACK in ArchiveData)
				ai.archiveData.SetInfoItemValueString("name", filesystem.GetBasename(mapfile));
			}
			if (ai.archiveData.GetMapFile().empty()) {
				ai.archiveData.SetInfoItemValueString("mapfile", mapfile);
			}
			AddDependency(ai.archiveData.GetDependencies(), "Map Helper v1");
			ai.archiveData.SetInfoItemValueInteger("modType", modtype::map);

			logOutput.Print(LOG_ARCHIVESCANNER, "Found new map: %s", ai.archiveData.GetName().c_str());
		} else if (hasModinfo) {
			//! it is a mod
			ScanArchiveLua(ar, "modinfo.lua", ai, error);
			if (ai.archiveData.GetModType() == modtype::primary) {
				AddDependency(ai.archiveData.GetDependencies(), "Spring content v1");
			}

			logOutput.Print(LOG_ARCHIVESCANNER, "Found new game: %s", ai.archiveData.GetName().c_str());
		} else {
			//! neither a map nor a mod: error
			error = "missing modinfo.lua/mapinfo.lua";
		}

		delete ar;

		if (!error.empty()) {
			//! for some reason, the archive is marked as broken
			logOutput.Print("Failed to scan %s (%s)", fullName.c_str(), error.c_str());

			//! record it as broken, so we don't need to look inside everytime
			BrokenArchive ba;
			ba.path = fpath;
			ba.modified = info.st_mtime;
			ba.updated = true;
			ba.problem = error;
			brokenArchives[lcfn] = ba;
			return;
		}

		ai.path = fpath;
		ai.modified = info.st_mtime;
		ai.origName = fn;
		ai.updated = true;

		//! Optionally calculate a checksum for the file
		//! To prevent reading all files in all directory (.sdd) archives
		//! every time this function is called, directory archive checksums
		//! are calculated on the fly.
		if (doChecksum) {
			ai.checksum = GetCRC(fullName);
		} else {
			ai.checksum = 0;
		}

		archiveInfo[lcfn] = ai;
	}
}
Пример #9
0
void CArchiveScanner::ScanArchive(const std::string& fullName, bool doChecksum)
{
	struct stat info;
	stat(fullName.c_str(), &info);

	const std::string fn    = filesystem.GetFilename(fullName);
	const std::string fpath = filesystem.GetDirectory(fullName);
	const std::string lcfn  = StringToLower(fn);

	// Determine whether this archive has earlier be found to be broken
	std::map<std::string, BrokenArchive>::iterator bai = brokenArchives.find(lcfn);
	if (bai != brokenArchives.end()) {
		if ((unsigned)info.st_mtime == bai->second.modified && fpath == bai->second.path) {
			bai->second.updated = true;
			return;
		}
	}

	// Determine whether to rely on the cached info or not
	bool cached = false;

	std::map<std::string, ArchiveInfo>::iterator aii = archiveInfo.find(lcfn);
	if (aii != archiveInfo.end()) {

		// This archive may have been obsoleted, do not process it if so
		if (aii->second.replaced.length() > 0) {
			return;
		}

		if ((unsigned)info.st_mtime == aii->second.modified && fpath == aii->second.path) {
			cached = true;
			aii->second.updated = true;
		}

		// If we are here, we could have invalid info in the cache
		// Force a reread if it's a directory archive, as st_mtime only
		// reflects changes to the directory itself, not the contents.
		if (!cached) {
			archiveInfo.erase(aii);
		}
	}

	// Time to parse the info we are interested in
	if (cached) {
		// If cached is true, aii will point to the archive
		if (doChecksum && (aii->second.checksum == 0))
			aii->second.checksum = GetCRC(fullName);
	} else {
		CArchiveBase* ar = CArchiveFactory::OpenArchive(fullName);
		if (ar && ar->IsOpen()) {
			ArchiveInfo ai;

			std::string mapfile;
			bool hasModinfo = false;
			bool hasMapinfo = false;
			for (unsigned fid = 0; fid != ar->NumFiles(); ++fid)
			{
				std::string name;
				int size;
				ar->FileInfo(fid, name, size);

				const std::string lowerName = StringToLower(name);
				const std::string ext = filesystem.GetExtension(lowerName);

				if ((ext == "smf") || (ext == "sm3")) {
					mapfile = name;
				} else if (lowerName == "modinfo.lua") {
					hasModinfo = true;
				} else if (lowerName == "mapinfo.lua") {
					hasMapinfo = true;
				}
			}

			if (hasMapinfo || !mapfile.empty()) {
				// it is a map
				if (hasMapinfo) {
					ScanArchiveLua(ar, "mapinfo.lua", ai);
				} else if (hasModinfo) {
					// backwards-compat for modinfo.lua in maps
					ScanArchiveLua(ar, "modinfo.lua", ai);
				}
				if (ai.archiveData.name.empty()) {
					ai.archiveData.name = filesystem.GetBasename(mapfile);
				}
				if (ai.archiveData.mapfile.empty()) {
					ai.archiveData.mapfile = mapfile;
				}
				AddDependency(ai.archiveData.dependencies, "Map Helper v1");
				ai.archiveData.modType = modtype::map;
				
			} else if (hasModinfo) {
				// it is a mod
				ScanArchiveLua(ar, "modinfo.lua", ai);
				if (ai.archiveData.modType == modtype::primary)
					AddDependency(ai.archiveData.dependencies, "Spring content v1");
			} else {
				// neither a map nor a mod: error
				logOutput.Print(LOG_ARCHIVESCANNER, "Failed to scan %s (missing modinfo.lua/mapinfo.lua)", fullName.c_str());
				delete ar;

				// record it as broken, so we don't need to look inside everytime
				BrokenArchive ba;
				ba.path = fpath;
				ba.modified = info.st_mtime;
				ba.updated = true;
				brokenArchives[lcfn] = ba;
				return;
			}

			ai.path = fpath;
			ai.modified = info.st_mtime;
			ai.origName = fn;
			ai.updated = true;

			delete ar;

			// Optionally calculate a checksum for the file
			// To prevent reading all files in all directory (.sdd) archives
			// every time this function is called, directory archive checksums
			// are calculated on the fly.
			if (doChecksum) {
				ai.checksum = GetCRC(fullName);
			} else {
				ai.checksum = 0;
			}

			archiveInfo[lcfn] = ai;
		} else {
			logOutput.Print(LOG_ARCHIVESCANNER, "Unable to open archive: %s", fullName.c_str());
		}
	}
}
void CArchiveScanner::Scan(const string& curPath, bool checksum)
{
	InitCrcTable();
	isDirty = true;

	const int flags = (FileSystem::INCLUDE_DIRS | FileSystem::RECURSE);
	std::vector<std::string> found = filesystem.FindFiles(curPath, "*", flags);

	for (std::vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
		string fullName = *it;

		// Strip
		const char lastFullChar = fullName[fullName.size() - 1];
		if ((lastFullChar == '/') || (lastFullChar == '\\')) {
			fullName = fullName.substr(0, fullName.size() - 1);
		}

		const string fn    = filesystem.GetFilename(fullName);
		const string fpath = filesystem.GetDirectory(fullName);
		const string lcfn    = StringToLower(fn);
		const string lcfpath = StringToLower(fpath);

		// Exclude archivefiles found inside directory archives (.sdd)
		if (lcfpath.find(".sdd") != string::npos) {
			continue;
		}

		// Exclude archivefiles found inside hidden directories
		if ((lcfpath.find("/hidden/")   != string::npos) ||
		    (lcfpath.find("\\hidden\\") != string::npos)) {
			continue;
		}

		// Is this an archive we should look into?
		if (CArchiveFactory::IsArchive(fullName)) {
			struct stat info;

			stat(fullName.c_str(), &info);

			// Determine whether to rely on the cached info or not
			bool cached = false;

			map<string, ArchiveInfo>::iterator aii = archiveInfo.find(lcfn);
			if (aii != archiveInfo.end()) {

				// This archive may have been obsoleted, do not process it if so
				if (aii->second.replaced.length() > 0)
					continue;

				/*
					For truely correct updating of .sdd archives, this code should
					be enabled. Unfortunately it has as side effect that all files
					in all .sdd's always need to be stat()'ed, which really slows
					down program startup.

					An update can be forced anyway by removing ArchiveCacheV*.txt
					or renaming the archive.
				*/

				/*if (S_ISDIR(info.st_mode)) {
					struct stat info2;
					std::vector<std::string> sddfiles = filesystem.FindFiles(fpath, "*", FileSystem::RECURSE | FileSystem::INCLUDE_DIRS);
					for (std::vector<std::string>::iterator sddit = found.begin(); sddit != found.end(); ++sddit) {
						stat(sddit->c_str(), &info2);
						if (info.st_mtime < info2.st_mtime) {
							info.st_mtime = info2.st_mtime;
						}
					}
				}*/

				if ((unsigned)info.st_mtime == aii->second.modified && fpath == aii->second.path) {
					cached = true;
					aii->second.updated = true;
				}

				// If we are here, we could have invalid info in the cache
				// Force a reread if it's a directory archive, as st_mtime only
				// reflects changes to the directory itself, not the contents.
				if (!cached) {
					archiveInfo.erase(aii);
				}
			}

			// Time to parse the info we are interested in
			if (!cached) {

				//printf("scanning archive: %s\n", fullName.c_str());

				CArchiveBase* ar = CArchiveFactory::OpenArchive(fullName);
				if (ar) {
					int cur;
					string name;
					int size;
					ArchiveInfo ai;

					cur = ar->FindFiles(0, &name, &size);
					while (cur != 0) {
						//printf("found %s %d\n", name.c_str(), size);

						string ext = StringToLower(name.substr(name.find_last_of('.') + 1));

						// only accept new format maps
						if (ext == "smf" || ext == "sm3") {
							MapData md;
							if (name.find_last_of('\\') == string::npos && name.find_last_of('/') == string::npos) {
								md.name = name;
								md.virtualPath = "/";
							}
							else {
								if (name.find_last_of('\\') == string::npos) {
									md.name = name.substr(name.find_last_of('/') + 1);
									md.virtualPath = name.substr(0, name.find_last_of('/') + 1);	// include the backslash
								} else {
									md.name = name.substr(name.find_last_of('\\') + 1);
									md.virtualPath = name.substr(0, name.find_last_of('\\') + 1);	// include the backslash
								}
								//md.name = md.name.substr(0, md.name.find_last_of('.'));
							}
							ai.mapData.push_back(md);
						}

						if (name == "modinfo.tdf") {
							int fh = ar->OpenFile(name);
							if (fh) {
								int fsize = ar->FileSize(fh);

								char* buf = SAFE_NEW char[fsize];
								ar->ReadFile(fh, buf, fsize);
								ar->CloseFile(fh);
								try {
									TdfParser p( buf, fsize );
									ai.modData = GetModData(&p, "mod");
								} catch (const TdfParser::parse_error&) {
									// Silently ignore mods with parse errors
								}
								delete [] buf;
							}

						}

						cur = ar->FindFiles(cur, &name, &size);
					}

					ai.path = fpath;
					ai.modified = info.st_mtime;
					ai.origName = fn;
					ai.updated = true;

					delete ar;

					// Optionally calculate a checksum for the file
					// To prevent reading all files in all directory (.sdd) archives every time this function
					// is called, directory archive checksums are calculated on the fly.
					if (checksum) {
						ai.checksum = GetCRC(fullName);
					}
					else {
						ai.checksum = 0;
					}

					archiveInfo[lcfn] = ai;
				}
			}
			else {
				// If cached is true, aii will point to the archive
				if ((checksum) && (aii->second.checksum == 0)) {
void CArchiveScanner::Scan(const string& curPath, bool checksum)
{
	isDirty = true;

	std::vector<std::string> found = filesystem.FindFiles(curPath, "*", FileSystem::RECURSE | FileSystem::INCLUDE_DIRS);
	struct stat info;
	for (std::vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
		stat(it->c_str(),&info);

		string fullName = *it;
		string fn = filesystem.GetFilename(fullName);
		string fpath = filesystem.GetDirectory(fullName);
		string lcfn = StringToLower(fn);
		string lcfpath = StringToLower(fpath);

		// Exclude archivefiles found inside directory (.sdd) archives.
		string::size_type sdd = lcfpath.find(".sdd");
		if (sdd != string::npos)
			continue;

		// Is this an archive we should look into?
		if (CArchiveFactory::IsArchive(fullName)) {

			// Determine whether to rely on the cached info or not
			bool cached = false;

			map<string, ArchiveInfo>::iterator aii = archiveInfo.find(lcfn);
			if (aii != archiveInfo.end()) {

				// This archive may have been obsoleted, do not process it if so
				if (aii->second.replaced.length() > 0)
					continue;

				if (S_ISDIR(info.st_mode)) {
					struct stat info2;
					std::vector<std::string> sddfiles = filesystem.FindFiles(fpath, "*", FileSystem::RECURSE | FileSystem::INCLUDE_DIRS);
					for (std::vector<std::string>::iterator sddit = found.begin(); sddit != found.end(); ++sddit) {
						stat(sddit->c_str(), &info2);
						if (info.st_mtime < info2.st_mtime) {
							info.st_mtime = info2.st_mtime;
						}
					}
				}

				if (info.st_mtime == aii->second.modified && fpath == aii->second.path) {
					cached = true;
					aii->second.updated = true;
				}

				// If we are here, we could have invalid info in the cache
				// Force a reread if it's a directory archive, as st_mtime only
				// reflects changes to the directory itself, not the contents.
				if (!cached) {
					archiveInfo.erase(aii);
				}
			}

			// Time to parse the info we are interested in
			if (!cached) {
				CArchiveBase* ar = CArchiveFactory::OpenArchive(fullName);
				if (ar) {
					int cur;
					string name;
					int size;
					ArchiveInfo ai;

					cur = ar->FindFiles(0, &name, &size);
					while (cur != 0) {
						//printf("found %s %d\n", name.c_str(), size);

						string ext = StringToLower(name.substr(name.find_last_of('.') + 1));

						// only accept new format maps
						if (ext == "smf" || ext == "sm3") {
							MapData md;
							if (name.find_last_of('\\') == string::npos && name.find_last_of('/') == string::npos) {
								md.name = name;
								md.virtualPath = "/";
							}
							else {
								if (name.find_last_of('\\') == string::npos) {
									md.name = name.substr(name.find_last_of('/') + 1);
									md.virtualPath = name.substr(0, name.find_last_of('/') + 1);	// include the backslash
								} else {
									md.name = name.substr(name.find_last_of('\\') + 1);
									md.virtualPath = name.substr(0, name.find_last_of('\\') + 1);	// include the backslash
								}
								//md.name = md.name.substr(0, md.name.find_last_of('.'));
							}
							ai.mapData.push_back(md);
						}

						if (name == "modinfo.tdf") {
							int fh = ar->OpenFile(name);
							if (fh) {
								int fsize = ar->FileSize(fh);

								void* buf = malloc(fsize);
								ar->ReadFile(fh, buf, fsize);
								ar->CloseFile(fh);
								try {
									TdfParser p( reinterpret_cast<char*>(buf), fsize );
									ai.modData = GetModData(&p, "mod");
								} catch (const TdfParser::parse_error& e) {
									// Silently ignore mods with parse errors
								}
								free(buf);
							}

						}

						cur = ar->FindFiles(cur, &name, &size);
					}

					ai.path = fpath;
					ai.modified = info.st_mtime;
					ai.origName = fn;
					ai.updated = true;

					delete ar;

					// Optionally calculate a checksum for the file
					// To prevent reading all files in all directory (.sdd) archives every time this function
					// is called, directory archive checksums are calculated on the fly.
					if (checksum) {
						ai.checksum = GetCRC(fullName);
					}
					else {
						ai.checksum = 0;
					}

					archiveInfo[lcfn] = ai;
				}
			}
			else {
				// If cached is true, aii will point to the archive
				if ((checksum) && (aii->second.checksum == 0)) {
					aii->second.checksum = GetCRC(fullName);
				}
			}
		}
	}

	// Now we'll have to parse the replaces-stuff found in the mods
	for (map<string, ArchiveInfo>::iterator aii = archiveInfo.begin(); aii != archiveInfo.end(); ++aii) {
		for (vector<string>::iterator i = aii->second.modData.replaces.begin(); i != aii->second.modData.replaces.end(); ++i) {

			string lcname = StringToLower(*i);

			map<string, ArchiveInfo>::iterator ar = archiveInfo.find(lcname);

			// If it's not there, we will create a new entry
			if (ar == archiveInfo.end()) {
				ArchiveInfo tmp;
				archiveInfo[lcname] = tmp;
				ar = archiveInfo.find(lcname);
			}

			// Overwrite the info for this archive with a replaced pointer
			ar->second.path = "";
			ar->second.origName = lcname;
			ar->second.modified = 1;
			ar->second.mapData.clear();
			ar->second.modData.name = "";
			ar->second.modData.replaces.clear();
			ar->second.updated = true;
			ar->second.replaced = aii->first;
		}
	}
}
Пример #12
0
void CArchiveScanner::ScanArchive(const string& fullName, bool doChecksum)
{
    struct stat info;

    stat(fullName.c_str(), &info);

    const string fn    = filesystem.GetFilename(fullName);
    const string fpath = filesystem.GetDirectory(fullName);
    const string lcfn    = StringToLower(fn);
    const string lcfpath = StringToLower(fpath);

    // Determine whether to rely on the cached info or not
    bool cached = false;

    std::map<string, ArchiveInfo>::iterator aii = archiveInfo.find(lcfn);
    if (aii != archiveInfo.end()) {

        // This archive may have been obsoleted, do not process it if so
        if (aii->second.replaced.length() > 0)
            return;

        if ((unsigned)info.st_mtime == aii->second.modified && fpath == aii->second.path) {
            cached = true;
            aii->second.updated = true;
        }

        // If we are here, we could have invalid info in the cache
        // Force a reread if it's a directory archive, as st_mtime only
        // reflects changes to the directory itself, not the contents.
        if (!cached) {
            archiveInfo.erase(aii);
        }
    }

    // Time to parse the info we are interested in
    if (cached)
    {
        // If cached is true, aii will point to the archive
        if (doChecksum && (aii->second.checksum == 0))
            aii->second.checksum = GetCRC(fullName);
    }
    else
    {
        CArchiveBase* ar = CArchiveFactory::OpenArchive(fullName);
        if (ar)
        {
            ArchiveInfo ai;

            string name;
            int size;

            std::string mapfile;
            bool hasModinfo = false;
            bool hasMapinfo = false;
            for (int cur = 0; (cur = ar->FindFiles(cur, &name, &size)); /* no-op */)
            {
                const string lowerName = StringToLower(name);
                const string ext = lowerName.substr(lowerName.find_last_of('.') + 1);

                if ((ext == "smf") || (ext == "sm3"))
                {
                    mapfile = name;
                }
                else if (lowerName == "modinfo.lua")
                {
                    hasModinfo = true;
                }
                else if (lowerName == "mapinfo.lua")
                {
                    hasMapinfo = true;
                }
            }

            if (hasMapinfo || !mapfile.empty())
            {   // its a map
                if (hasMapinfo)
                {
                    ScanArchiveLua(ar, "mapinfo.lua", ai);
                }
                else if (hasModinfo) // backwards-compat for modinfo.lua in maps
                {
                    ScanArchiveLua(ar, "modinfo.lua", ai);
                }
                if (ai.archiveData.name.empty())
                    ai.archiveData.name = filesystem.GetFilename(mapfile);
                if (ai.archiveData.mapfile.empty())
                    ai.archiveData.mapfile = mapfile;
                AddDependency(ai.archiveData.dependencies, "maphelper.sdz");
                ai.archiveData.modType = modtype::map;

            }
            else if (hasModinfo)
            {   // mod
                ScanArchiveLua(ar, "modinfo.lua", ai);
                if (ai.archiveData.modType == modtype::primary)
                    AddDependency(ai.archiveData.dependencies, "Spring content v1");
            }
            else
            {   // error
                LogObject() << "Failed to read archive, files missing: " << fullName;
                delete ar;
                return;
            }

            ai.path = fpath;
            ai.modified = info.st_mtime;
            ai.origName = fn;
            ai.updated = true;

            delete ar;

            // Optionally calculate a checksum for the file
            // To prevent reading all files in all directory (.sdd) archives
            // every time this function is called, directory archive checksums
            // are calculated on the fly.
            if (doChecksum) {
                ai.checksum = GetCRC(fullName);
            } else {
                ai.checksum = 0;
            }

            archiveInfo[lcfn] = ai;
        }
    }
}