// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //----------------------------------------------------------------------------- // includes //----------------------------------------------------------------------------- #include "nel/misc/variable.h" #include "nel/misc/path.h" #include "game_share/file_description_container.h" #include "game_share/utils.h" #include "char_scan_script.h" //----------------------------------------------------------------------------- // Namespaces //----------------------------------------------------------------------------- using namespace std; using namespace NLMISC; //----------------------------------------------------------------------------- // Variables //----------------------------------------------------------------------------- CVariable<string> ScriptDirectory("variables", "ScriptDirectory", "Directory containing script files", string("./"), 0, true); CVariable<string> OutputDirectory("variables", "OutputDirectory", "Directory containing output files", string("./"), 0, true); //------------------------------------------------------------------------------------------------- // methods CCharScanScript //------------------------------------------------------------------------------------------------- bool CCharScanScript::addScriptFile(const std::string& fileName) { std::string fullFileName=NLMISC::CPath::standardizePath(ScriptDirectory)+fileName; // make sure the file exists if (!NLMISC::CFile::fileExists(fullFileName)) { nlwarning("script file not found: %s",fullFileName.c_str()); return false; } // make sure the file hasn't already been included previously for (uint32 i=0;i<_ScriptFiles.size();++i) { if (_ScriptFiles[i].getFileName()==fullFileName) { nlwarning("attempt to include script file '%s' more than once",fullFileName.c_str()); return true; } } // add & parse the new file return vectAppend(_ScriptFiles).parseFile(fullFileName); } void CCharScanScript::applyToJob(CCharacterScanJob& job) { // iterate backwards over the script files in order to apply the most important files last for (uint32 i=(uint32)_ScriptFiles.size();i--;) { _ScriptFiles[i].applyToJob(job); } } //------------------------------------------------------------------------------------------------- // methods CCharScanScriptFile //------------------------------------------------------------------------------------------------- bool CCharScanScriptFile::parseFile(const std::string& fileName, CCharScanScript* container) { _FileName= fileName; // read the content of the input file bool result; NLMISC::CSString fileContent; result=fileContent.readFromFile(fileName); if (result==false) { nlwarning("Failed to read script file: %s",fileName.c_str()); return false; } // split the file into lines and execute them one by one NLMISC::CVectorSString lines; fileContent.splitLines(lines); for (uint32 i=0;i<lines.size();++i) { // strip comments and leading and trailing blanks CSString theLine= lines[i].replace("//","\xff").splitTo('\xff').strip(); if (theLine.empty()) continue; CCharScanScriptCommandRegistry::getInstance()->execute(*this,theLine,container); } return true; } bool CCharScanScriptFile::applyToJob(CCharacterScanJob& job) { bool result=true; // apply the file names CFileDescriptionContainer fdc; for (uint32 i=0;i<_InputFiles.size();++i) { fdc.addFileSpec(_InputFiles[i]); } job.addFiles(fdc); // apply the filters for (uint32 i=0;i<_Filters.size();++i) { ICharFilter* filter= CCharFilterFactory::getInstance()->build(_Filters[i]); if (filter==NULL) { nlwarning("Failed to build filter description from line: %s",_Filters[i].c_str()); result=false; continue; } job.addFilter(filter); } // apply the info extractors for (uint32 i=0;i<_InfoExtractors.size();++i) { ICharInfoExtractor* infoExtractor= CCharInfoExtractorFactory::getInstance()->build(_InfoExtractors[i]); if (infoExtractor==NULL) { nlwarning("Failed to build filter description from line: %s",_InfoExtractors[i].c_str()); result=false; continue; } job.addInfoExtractor(infoExtractor); } // apply the output path job.setOutputPath(_OutputPath); return result; } const std::string& CCharScanScriptFile::getFileName() const { return _FileName; } const std::string& CCharScanScriptFile::getDescription() const { return _Description; } bool CCharScanScriptFile::setDescription(const std::string& description) { _Description= description; return true; } bool CCharScanScriptFile::setOutputPath(const std::string& path) { _OutputPath= path; return true; } bool CCharScanScriptFile::addFilter(const std::string& rawArgs) { _Filters.push_back(rawArgs); return true; } bool CCharScanScriptFile::addInfoExtractor(const std::string& rawArgs) { _InfoExtractors.push_back(rawArgs); return true; } bool CCharScanScriptFile::addInputFiles(const std::string& rawArgs) { _InputFiles.push_back(rawArgs); return true; } //------------------------------------------------------------------------------------------------- // methods CCharScanScriptCommandRegistry //------------------------------------------------------------------------------------------------- CCharScanScriptCommandRegistry* CCharScanScriptCommandRegistry::getInstance() { static CCharScanScriptCommandRegistry* ptr=NULL; if (ptr==NULL) ptr= new CCharScanScriptCommandRegistry; return ptr; } void CCharScanScriptCommandRegistry::registerScriptCommand(NLMISC::CSmartPtr<ICharScanScriptCommand> scriptCommand) { // ensure that we don't have a name conflict with an existing script command for (uint32 i=0;i<_ScriptCommands.size();++i) { nlassert(scriptCommand->getName()!=_ScriptCommands[i]->getName()); } // add the new script command _ScriptCommands.push_back(scriptCommand); } void CCharScanScriptCommandRegistry::displayScriptCommands(NLMISC::CLog* log) { uint32 longestName=4; // iterate over the script commands to determine the length of the longest name for (uint32 i=0;i<_ScriptCommands.size();++i) { std::string s= _ScriptCommands[i]->getName(); if (s.size()>longestName) longestName=(uint32)s.size(); } // iterate over the script commands displaying names and description for (uint32 i=0;i<_ScriptCommands.size();++i) { log->displayNL("%-*s %s",longestName,_ScriptCommands[i]->getName(),_ScriptCommands[i]->getDescription()); } } bool CCharScanScriptCommandRegistry::execute(CCharScanScriptFile& scriptFile,const CSString& commandLine,CCharScanScript* container) { // split the command line into its constituent parts CSString theCommand= commandLine.firstWordConst(); CSString theRawArgs= commandLine.tailFromFirstWord().strip(); CVectorSString theArgs; theRawArgs.splitByOneOfSeparators(" \t",theArgs,false,false,false,false,true); // try to locate and execute the given command for (uint32 i=0;i<_ScriptCommands.size();++i) { if (theCommand==_ScriptCommands[i]->getName()) { return _ScriptCommands[i]->execute(scriptFile,theArgs,theRawArgs,commandLine,container); } } // we failed to find the command so bomb nlwarning("Unknown script command '%s' in line: %s",theCommand.c_str(),commandLine.c_str()); return false; } //----------------------------------------------------------------------------- // CHAR_SCAN_SCRIPT_COMMAND: instances //----------------------------------------------------------------------------- CHAR_SCAN_SCRIPT_COMMAND(description,"<description>","Set the description phrase for the script - displayed by the listScripts command") { if (rawArgs.strip().empty()) return false; return scriptFile.setDescription(rawArgs); } CHAR_SCAN_SCRIPT_COMMAND(include,"<include_file_name>","Include another script file") { if (args.size()!=1) return false; if (container==NULL) return true; return container->addScriptFile(args[0]); } CHAR_SCAN_SCRIPT_COMMAND(inputFiles,"[<path>/]<file_spec>","Add a set of files to be parsed") { if (rawArgs.strip().empty()) return false; return scriptFile.addInputFiles(rawArgs); } CHAR_SCAN_SCRIPT_COMMAND(filter,"<name> [<args>]","Add a filter to limit the set criteria to determine which files' content to reflect in output") { if (rawArgs.strip().empty()) return false; return scriptFile.addFilter(rawArgs); } CHAR_SCAN_SCRIPT_COMMAND(infoExtactor,"<name> [<args>]","Add an info extractor") { if (rawArgs.strip().empty()) return false; return scriptFile.addInfoExtractor(rawArgs); } CHAR_SCAN_SCRIPT_COMMAND(outputPath,"<path>","Set the directory to which the output will be written") { if (args.size()!=1) return false; return scriptFile.setOutputPath(args[0]); } //-----------------------------------------------------------------------------