bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) { Common::File stream; bool foundHeader = false; if (node.getName().matchString("*.zip", true) && !node.isDirectory()) { Common::Archive *zipArchive = Common::makeZipArchive(node); if (zipArchive && zipArchive->hasFile("THEMERC")) { // Open THEMERC from the ZIP file. stream.open("THEMERC", *zipArchive); } // Delete the ZIP archive again. Note: This only works because // stream.open() only uses ZipArchive::createReadStreamForMember, // and that in turn happens to read all the data for a given // archive member into a memory block. So there will be no dangling // reference to zipArchive anywhere. This could change if we // ever modify ZipArchive::createReadStreamForMember. delete zipArchive; } else if (node.isDirectory()) { Common::FSNode headerfile = node.getChild("THEMERC"); if (!headerfile.exists() || !headerfile.isReadable() || headerfile.isDirectory()) return false; stream.open(headerfile); } if (stream.isOpen()) { Common::String stxHeader = stream.readLine(); foundHeader = themeConfigParseHeader(stxHeader, themeName); } return foundHeader; }
bool Ps2SaveFileManager::removeSavefile(const Common::String &filename) { Common::FSNode savePath(ConfMan.get("savepath")); // TODO: is this fast? Common::FSNode file; if (!savePath.exists() || !savePath.isDirectory()) return false; if (_getDev(savePath) == MC_DEV) { // if (strncmp(savePath.getPath().c_str(), "mc0:", 4) == 0) { char path[32], temp[32]; strcpy(temp, filename.c_str()); // mcSplit(temp, game, ext); char *game = strdup(strtok(temp, ".")); char *ext = strdup(strtok(NULL, "*")); sprintf(path, "mc0:ScummVM/%s", game); // per game path mcCheck(path); sprintf(path, "mc0:ScummVM/%s/%s.sav", game, ext); file = Common::FSNode(path); free(game); free(ext); } else { file = savePath.getChild(filename); } if (!file.exists() || file.isDirectory()) return false; fio.remove(file.getPath().c_str()); return true; }
bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) { if (path == "" || path == "/") { if (ConfMan.hasKey("rootpath", "cloud")) addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root")); addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games")); return true; } if (HandlerUtils::hasForbiddenCombinations(path)) return false; Common::String prefixToRemove = "", prefixToAdd = ""; if (!transformPath(path, prefixToRemove, prefixToAdd)) return false; Common::FSNode node = Common::FSNode(path); if (path == "/") node = node.getParent(); // absolute root if (!HandlerUtils::permittedPath(node.getPath())) return false; if (!node.isDirectory()) return false; // list directory Common::FSList _nodeContent; if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files _nodeContent.clear(); else Common::sort(_nodeContent.begin(), _nodeContent.end()); // add parent directory link { Common::String filePath = path; if (filePath.hasPrefix(prefixToRemove)) filePath.erase(0, prefixToRemove.size()); if (filePath == "" || filePath == "/" || filePath == "\\") filePath = "/"; else filePath = parentPath(prefixToAdd + filePath); addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, _("Parent directory")); } // fill the content for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) { Common::String name = i->getDisplayName(); if (i->isDirectory()) name += "/"; Common::String filePath = i->getPath(); if (filePath.hasPrefix(prefixToRemove)) filePath.erase(0, prefixToRemove.size()); filePath = prefixToAdd + filePath; addItem(content, itemTemplate, detectType(i->isDirectory(), name), filePath, name); } return true; }
void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) { clearError(); if (!dir.exists()) { setError(Common::kPathDoesNotExist, "The savepath '"+dir.getPath()+"' does not exist"); } else if (!dir.isDirectory()) { setError(Common::kPathNotDirectory, "The savepath '"+dir.getPath()+"' is not a directory"); } }
void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth) { if (!node.exists() || !node.isReadable() || !node.isDirectory()) return; ThemeDescriptor td; // Check whether we point to a valid theme directory. if (themeConfigUsable(node, td.name)) { td.filename = node.getPath(); td.id = node.getName(); list.push_back(td); // A theme directory should never contain any other themes // thus we just return to the caller here. return; } Common::FSList fileList; // Check all files. We need this to find all themes inside ZIP archives. if (!node.getChildren(fileList, Common::FSNode::kListFilesOnly)) return; for (Common::FSList::iterator i = fileList.begin(); i != fileList.end(); ++i) { // We will only process zip files for now if (!i->getPath().matchString("*.zip", true)) continue; td.name.clear(); if (themeConfigUsable(*i, td.name)) { td.filename = i->getPath(); td.id = i->getName(); // If the name of the node object also contains // the ".zip" suffix, we will strip it. if (td.id.matchString("*.zip", true)) { for (int j = 0; j < 4; ++j) td.id.deleteLastChar(); } list.push_back(td); } } fileList.clear(); // Check if we exceeded the given recursion depth if (depth - 1 == -1) return; // As next step we will search all subdirectories if (!node.getChildren(fileList, Common::FSNode::kListDirectoriesOnly)) return; for (Common::FSList::iterator i = fileList.begin(); i != fileList.end(); ++i) listUsableThemes(*i, list, depth == -1 ? - 1 : depth - 1); }
bool diskFileExists(const Common::String &filename) { // Try directly from SearchMan first Common::ArchiveMemberList files; SearchMan.listMatchingMembers(files, filename); for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { if ((*it)->getName() == filename) { return true; } } // File wasn't found in SearchMan, try to parse the path as a relative path. Common::FSNode searchNode = getNodeForRelativePath(filename); if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { return true; } return false; }
// Parse a relative path in the game-folder, and if it exists, return a FSNode to it. static Common::FSNode getNodeForRelativePath(const Common::String &filename) { // The filename can be an explicit path, thus we need to chop it up, expecting the path the game // specifies to follow the Windows-convention of folder\subfolder\file (absolute paths should not happen) // Absolute path: These should have been handled in openDiskFile. if (filename.contains(':')) { // So just return an invalid node. return Common::FSNode(); } // Relative path: if (filename.contains('\\')) { Common::StringTokenizer path(filename, "\\"); // Start traversing relative to the game-data-dir const Common::FSNode gameDataDir(ConfMan.get("path")); Common::FSNode curNode = gameDataDir; // Parse all path-elements while (!path.empty()) { // Get the next path-component by slicing on '\\' Common::String pathPart = path.nextToken(); // Get the next FSNode in the chain, if it exists as a child from the previous. curNode = curNode.getChild(pathPart); if (!curNode.isReadable()) { // Return an invalid FSNode. return Common::FSNode(); } // Following the comments in common/fs.h, anything not a directory is a file. if (!curNode.isDirectory()) { if (!path.empty()) { error("Relative path %s reached a file before the end of the path", filename.c_str()); } return curNode; } } } // Return an invalid FSNode to mark that we didn't find the requested file. return Common::FSNode(); }
Common::SeekableReadStream *openDiskFile(const Common::String &filename) { uint32 prefixSize = 0; Common::SeekableReadStream *file = NULL; Common::String fixedFilename = filename; // Absolute path: TODO: Add specific fallbacks here. if (filename.contains(':')) { if (filename.hasPrefix("c:\\windows\\fonts\\")) { // East Side Story refers to "c:\windows\fonts\framd.ttf" fixedFilename = filename.c_str() + 17; } else { error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str()); } } // Try directly from SearchMan first Common::ArchiveMemberList files; SearchMan.listMatchingMembers(files, fixedFilename); for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { if ((*it)->getName() == filename) { file = (*it)->createReadStream(); break; } } // File wasn't found in SearchMan, try to parse the path as a relative path. if (!file) { Common::FSNode searchNode = getNodeForRelativePath(filename); if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { file = searchNode.createReadStream(); } } if (file) { uint32 magic1, magic2; magic1 = file->readUint32LE(); magic2 = file->readUint32LE(); bool compressed = false; if (magic1 == DCGF_MAGIC && magic2 == COMPRESSED_FILE_MAGIC) { compressed = true; } if (compressed) { uint32 dataOffset, compSize, uncompSize; dataOffset = file->readUint32LE(); compSize = file->readUint32LE(); uncompSize = file->readUint32LE(); byte *compBuffer = new byte[compSize]; if (!compBuffer) { error("Error allocating memory for compressed file '%s'", filename.c_str()); delete file; return NULL; } byte *data = new byte[uncompSize]; if (!data) { error("Error allocating buffer for file '%s'", filename.c_str()); delete[] compBuffer; delete file; return NULL; } file->seek(dataOffset + prefixSize, SEEK_SET); file->read(compBuffer, compSize); if (Common::uncompress(data, (unsigned long *)&uncompSize, compBuffer, compSize) != true) { error("Error uncompressing file '%s'", filename.c_str()); delete[] compBuffer; delete file; return NULL; } delete[] compBuffer; delete file; return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES); } else { file->seek(0, SEEK_SET); return file; } return file; } return NULL; }
// The following function tries to detect the language for COMI and DIG static Common::Language detectLanguage(const Common::FSList &fslist, byte id) { // First try to detect Chinese translation Common::FSNode fontFile; if (searchFSNode(fslist, "chinese_gb16x12.fnt", fontFile)) { debug(0, "Chinese detected"); return Common::ZH_CNA; } // Now try to detect COMI and Dig by language files if (id != GID_CMI && id != GID_DIG) return Common::UNK_LANG; // Check for LANGUAGE.BND (Dig) resp. LANGUAGE.TAB (CMI). // These are usually inside the "RESOURCE" subdirectory. // If found, we match based on the file size (should we // ever determine that this is insufficient, we can still // switch to MD5 based detection). const char *filename = (id == GID_CMI) ? "LANGUAGE.TAB" : "LANGUAGE.BND"; Common::File tmp; Common::FSNode langFile; if (searchFSNode(fslist, filename, langFile)) tmp.open(langFile); if (!tmp.isOpen()) { // try loading in RESOURCE sub dir... Common::FSNode resDir; Common::FSList tmpList; if (searchFSNode(fslist, "RESOURCE", resDir) && resDir.isDirectory() && resDir.getChildren(tmpList, Common::FSNode::kListFilesOnly) && searchFSNode(tmpList, filename, langFile)) { tmp.open(langFile); } } if (tmp.isOpen()) { uint size = tmp.size(); if (id == GID_CMI) { switch (size) { case 439080: // 2daf3db71d23d99d19fc9a544fcf6431 return Common::EN_ANY; case 322602: // caba99f4f5a0b69963e5a4d69e6f90af return Common::ZH_TWN; case 493252: // 5d59594b24f3f1332e7d7e17455ed533 return Common::DE_DEU; case 461746: // 35bbe0e4d573b318b7b2092c331fd1fa return Common::FR_FRA; case 443439: // 4689d013f67aabd7c35f4fd7c4b4ad69 return Common::IT_ITA; case 398613: // d1f5750d142d34c4c8f1f330a1278709 return Common::KO_KOR; case 440586: // 5a1d0f4fa00917bdbfe035a72a6bba9d return Common::PT_BRA; case 454457: // 0e5f450ec474a30254c0e36291fb4ebd case 394083: // ad684ca14c2b4bf4c21a81c1dbed49bc return Common::RU_RUS; case 449787: // 64f3fe479d45b52902cf88145c41d172 return Common::ES_ESP; } } else { // The DIG switch (size) { case 248627: // 1fd585ac849d57305878c77b2f6c74ff return Common::DE_DEU; case 257460: // 04cf6a6ba6f57e517bc40eb81862cfb0 return Common::FR_FRA; case 231402: // 93d13fcede954c78e65435592182a4db return Common::IT_ITA; case 228772: // 5d9ad90d3a88ea012d25d61791895ebe return Common::PT_BRA; case 229884: // d890074bc15c6135868403e73c5f4f36 return Common::ES_ESP; case 223107: // 64f3fe479d45b52902cf88145c41d172 return Common::JA_JPN; case 180730: // 424fdd60822722cdc75356d921dad9bf return Common::ZH_TWN; } } } return Common::UNK_LANG; }