/// 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; }
// 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; }
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; } }
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()); } } }