1106 lines
29 KiB
C++
1106 lines
29 KiB
C++
// 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 "stdpch.h"
|
|
#include "nel/misc/path.h"
|
|
#include "nel/misc/command.h"
|
|
#include "nel/misc/algo.h"
|
|
#include "nel/misc/sheet_id.h"
|
|
#include "game_share/file_description_container.h"
|
|
#include "game_share/persistent_data.h"
|
|
#include "game_share/singleton_registry.h"
|
|
#include "handy_commands.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Very handy utility routines for the handy utility commands
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void readFileList(const NLMISC::CSString& fileListName, CFileDescriptionContainer& result)
|
|
{
|
|
// read the file list from disk (the result will be empty if the file list didn't exist)
|
|
NLMISC::CSString fileList;
|
|
fileList.readFromFile(fileListName);
|
|
|
|
// split the file list text block into lines
|
|
NLMISC::CVectorSString files;
|
|
fileList.splitLines(files);
|
|
|
|
// iterate over the lies in the input file
|
|
for(uint32 i=0;i<files.size();++i)
|
|
{
|
|
// clean lines up, stripping spaces and quotes
|
|
NLMISC::CSString theFile= files[i].strip().unquoteIfQuoted();
|
|
|
|
// skip empty lines and comments
|
|
if (theFile.empty() || theFile.left(1)=="#")
|
|
continue;
|
|
|
|
// add the file name to the result
|
|
result.addFile(theFile);
|
|
}
|
|
}
|
|
|
|
static void addToFdc(const NLMISC::CSString& filespec, CFileDescriptionContainer& result)
|
|
{
|
|
if (filespec.left(1)=="@")
|
|
{
|
|
readFileList(filespec.leftCrop(1),result);
|
|
}
|
|
else
|
|
{
|
|
result.addFileSpec(filespec);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handy utility commands - pdr
|
|
//-----------------------------------------------------------------------------
|
|
// pdrBin2xml <input file name> <output file name>
|
|
// pdrXml2bin <input file name> <output file name>
|
|
// pdr2xml <input file spec>
|
|
// pdr2bin <input file spec>
|
|
// pdr2txt <input file spec>
|
|
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdrBin2xml,"convert a binary pdr file to xml","<input file name> <output file name>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
log.displayNL("Converting to XML : %s => %s",args[0].c_str(),args[1].c_str());
|
|
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
pdr.readFromBinFile(args[0]);
|
|
pdr.writeToTxtFile(args[1]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdrXml2bin,"convert a text pdr file to binary","<input file name> <output file name>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
log.displayNL("Converting to Binary : %s => %s",args[0].c_str(),args[1].c_str());
|
|
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
pdr.readFromTxtFile(args[0]);
|
|
pdr.writeToBinFile(args[1]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdr2xml,"convert one or more sets of pdr files to xml format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()<1)
|
|
return false;
|
|
|
|
for (uint32 i=0;i<args.size();++i)
|
|
{
|
|
CFileDescriptionContainer fdc;
|
|
addToFdc(args[i],fdc);
|
|
|
|
DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
|
|
|
|
for (uint32 j=0;j<fdc.size();++j)
|
|
{
|
|
const CFileDescription& fd=fdc[j];
|
|
NLMISC::CSString outputFileName=
|
|
(fd.FileName.right(4).toLower()==".bin" || fd.FileName.right(4).toLower()==".txt")?
|
|
fd.FileName.rightCrop(4)+".xml": fd.FileName+".xml";
|
|
|
|
log.displayNL("Converting to XML : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
pdr.readFromFile(fd.FileName);
|
|
pdr.writeToFile(outputFileName);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdr2bin,"convert one or more sets of pdr files to binary format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()<1)
|
|
return false;
|
|
|
|
for (uint32 i=0;i<args.size();++i)
|
|
{
|
|
CFileDescriptionContainer fdc;
|
|
addToFdc(args[i],fdc);
|
|
|
|
DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
|
|
|
|
for (uint32 j=0;j<fdc.size();++j)
|
|
{
|
|
const CFileDescription& fd=fdc[j];
|
|
NLMISC::CSString outputFileName=
|
|
(fd.FileName.right(4).toLower()==".xml" || fd.FileName.right(4).toLower()==".txt")?
|
|
fd.FileName.rightCrop(4)+".bin": fd.FileName+".bin";
|
|
|
|
log.displayNL("Converting to Binary : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
pdr.readFromFile(fd.FileName);
|
|
pdr.writeToFile(outputFileName);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdr2txt,"convert one or more sets of pdr files to txt (lines) format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()<1)
|
|
return false;
|
|
|
|
for (uint32 i=0;i<args.size();++i)
|
|
{
|
|
CFileDescriptionContainer fdc;
|
|
addToFdc(args[i],fdc);
|
|
|
|
DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
|
|
|
|
for (uint32 j=0;j<fdc.size();++j)
|
|
{
|
|
const CFileDescription& fd=fdc[j];
|
|
NLMISC::CSString outputFileName=
|
|
(fd.FileName.right(4).toLower()==".bin" || fd.FileName.right(4).toLower()==".xml")?
|
|
fd.FileName.rightCrop(4)+".txt": fd.FileName+".txt";
|
|
|
|
log.displayNL("Converting to TXT : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
pdr.readFromFile(fd.FileName);
|
|
pdr.writeToFile(outputFileName);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdrFileCompare,"Compare 2 pdr files","<first file> <second file>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
CPersistentDataRecord pdr0;
|
|
pdr0.readFromFile(args[0]);
|
|
|
|
CPersistentDataRecord pdr1;
|
|
pdr1.readFromFile(args[1]);
|
|
|
|
log.displayNL("%s : %s / %s", (pdr0==pdr1)?"Files MATCH":"Files DON'T match", args[0].c_str(), args[1].c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,pdrInfo,"Extract info from pdr file(s)","<input file spec>|'@'<listfile> [<input file spec>[...]] [<output fil name>.csv]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
NLMISC::CSString outputFileName;
|
|
NLMISC::CSString csvTxt;
|
|
uint32 numFileSpecs=(uint32)args.size();
|
|
|
|
if (numFileSpecs>0 && NLMISC::CSString(args.back()).right(4)==".csv")
|
|
{
|
|
outputFileName= args.back();
|
|
--numFileSpecs;
|
|
log.displayNL("Extracting PDR info to write to csv file: %s",outputFileName.quoteIfNotQuoted().c_str());
|
|
csvTxt= "FileName,"+ CPersistentDataRecord::getCSVHeaderLine()+"\n";
|
|
}
|
|
else
|
|
{
|
|
log.displayNL("Extracting PDR info (No output file specified so no csv file will be written)",outputFileName.quoteIfNotQuoted().c_str());
|
|
}
|
|
|
|
if (numFileSpecs<1)
|
|
return false;
|
|
|
|
for (uint32 i=0;i<numFileSpecs;++i)
|
|
{
|
|
CFileDescriptionContainer fdc;
|
|
{
|
|
H_AUTO(pdrInfo_BuildFDC)
|
|
addToFdc(args[i],fdc);
|
|
}
|
|
|
|
DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
|
|
|
|
for (uint32 j=0;j<fdc.size();++j)
|
|
{
|
|
H_AUTO(pdrInfo_treatFile)
|
|
static CPersistentDataRecord pdr;
|
|
pdr.clear();
|
|
|
|
const CFileDescription& fd=fdc[j];
|
|
log.display("- %s: ", fd.FileName.quoteIfNotQuoted().c_str());
|
|
|
|
{
|
|
H_AUTO(pdrInfo_readFromFile)
|
|
pdr.readFromFile(fd.FileName);
|
|
}
|
|
|
|
log.displayNL("%s", pdr.getInfo().c_str());
|
|
|
|
if (!outputFileName.empty())
|
|
{
|
|
csvTxt+=NLMISC::CFile::getFilenameWithoutExtension(fd.FileName)+","+pdr.getInfoAsCSV()+"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!outputFileName.empty())
|
|
{
|
|
log.displayNL("Writing file: %s",outputFileName.quoteIfNotQuoted().c_str());
|
|
csvTxt.writeToFile(outputFileName);
|
|
}
|
|
log.displayNL("Info extraction from PDRs finished");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handy utility commands - file compare
|
|
//-----------------------------------------------------------------------------
|
|
// quickFileCompare <file0> <file1>
|
|
// thoroughFileCompare <file0> <file1> [<max mem footprint>]
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,quickFileCompare,"compare 2 files (by comparing timestamp and size)","<file0> <file1>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
nlinfo("comparing files ...");
|
|
bool result= NLMISC::CFile::quickFileCompare(args[0], args[1]);
|
|
nlinfo("- %s",result?"Same":"Different");
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,thoroughFileCompare,"compare 2 files (by comparing data)","<file0> <file1> [<max mem footprint>]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2 && args.size()!=3)
|
|
return false;
|
|
|
|
bool result;
|
|
nlinfo("comparing files ...");
|
|
|
|
if (args.size()==3)
|
|
{
|
|
uint32 size;
|
|
NLMISC::fromString(args[2], size);
|
|
if (size<2)
|
|
{
|
|
nlwarning("The third parameter must be a value >= 2 : The following value is not valid: %s",args[2].c_str());
|
|
return true;
|
|
}
|
|
result= NLMISC::CFile::thoroughFileCompare(args[0], args[1], size);
|
|
}
|
|
else
|
|
{
|
|
result= NLMISC::CFile::thoroughFileCompare(args[0], args[1]);
|
|
}
|
|
|
|
nlinfo("- %s",result?"Same":"Different");
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handy utility commands - file and directory management
|
|
//-----------------------------------------------------------------------------
|
|
// cd [<path>]
|
|
// md <path>
|
|
// copyFile <src> <dest>
|
|
// del <fileName>
|
|
// dir [<wildcard>]
|
|
// deltree <directory>
|
|
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,cd,"change directory or display current working directory","[<path>]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=0 && args.size()!=1)
|
|
return false;
|
|
|
|
if (args.size()==1)
|
|
{
|
|
NLMISC::CPath::setCurrentPath(args[0].c_str());
|
|
}
|
|
|
|
nlinfo("Current directory: %s",NLMISC::CPath::getCurrentPath().c_str());
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,md,"create a new directory (or directory tree)","<path>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
NLMISC::CFile::createDirectoryTree(args[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,copyFile,"copy a file","<src> <dest>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
NLMISC::CFile::copyFile(args[1],args[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,del,"delete a file","<fileName>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
NLMISC::CFile::deleteFile(args[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,dir,"list files in the current directory","[<wildcard>]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1 && args.size()!=0)
|
|
return false;
|
|
|
|
std::string wildcard="*";
|
|
if (args.size()==1)
|
|
wildcard=args[0];
|
|
|
|
std::vector<std::string> directories;
|
|
NLMISC::CPath::getPathContent(".",false,true,false,directories);
|
|
for (uint32 i=(uint32)directories.size();i--;)
|
|
{
|
|
if (!NLMISC::testWildCard(directories[i],wildcard))
|
|
{
|
|
directories[i]=directories.back();
|
|
directories.pop_back();
|
|
}
|
|
}
|
|
std::sort(directories.begin(),directories.end());
|
|
for (uint32 i=0;i<directories.size();++i)
|
|
{
|
|
nlinfo("%s/",directories[i].c_str());
|
|
}
|
|
|
|
std::vector<std::string> files;
|
|
NLMISC::CPath::getPathContent(".",false,false,true,files);
|
|
for (uint32 i=(uint32)files.size();i--;)
|
|
{
|
|
if (!NLMISC::testWildCard(files[i],wildcard))
|
|
{
|
|
files[i]=files.back();
|
|
files.pop_back();
|
|
}
|
|
}
|
|
std::sort(files.begin(),files.end());
|
|
for (uint32 i=0;i<files.size();++i)
|
|
{
|
|
nlinfo("%-40s %10d",files[i].c_str(),NLMISC::CFile::getFileSize(files[i]));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,deltree,"delete all files matching given spec, recursively","<fileSpec>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
CFileDescriptionContainer tempFiles;
|
|
tempFiles.addFileSpec(args[0],true);
|
|
for (uint32 i=0;i<tempFiles.size();++i)
|
|
{
|
|
NLMISC::CFile::deleteFile(tempFiles[i].FileName);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handy utility commands - file viewing
|
|
//-----------------------------------------------------------------------------
|
|
// viewTxtFile <file_name> [<first_line=1>[ <num_lines=100>]]
|
|
// viewBinFile <file_name> [<first_offset=0>[ <length=65536>]]
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,viewTxtFile,"view a text file segment","<file_name> [<first_line=1>[ <num_lines=100>]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
// deal with command line arguments
|
|
uint32 firstLine=1;
|
|
uint32 count=100;
|
|
switch (args.size())
|
|
{
|
|
case 3:
|
|
NLMISC::fromString(args[2], count);
|
|
if (count<1||args[2]!=NLMISC::toString("%u",count))
|
|
return false;
|
|
|
|
case 2:
|
|
NLMISC::fromString(args[1], firstLine);
|
|
if (firstLine<1||args[1]!=NLMISC::toString("%u",firstLine))
|
|
return false;
|
|
|
|
case 1:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// read the new file
|
|
NLMISC::CSString fileBody;
|
|
fileBody.readFromFile(args[0]);
|
|
if (fileBody.empty())
|
|
{
|
|
nlwarning("File not found or file empty: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// if we have a utf16 file then convert to utf8
|
|
if (fileBody.size()>=2 && ((fileBody[0]==char(0xff) && fileBody[1]==char(0xfe)) || (fileBody[0]==char(0xfe) && fileBody[1]==char(0xff))) )
|
|
{
|
|
nlinfo("Displaying unicode UTF16 text:");
|
|
ucstring ucs;
|
|
ucs.resize((fileBody.size()-2)/2);
|
|
memcpy((char*)&ucs[0],(char*)&fileBody[0],fileBody.size()-2);
|
|
fileBody=ucs.toUtf8();
|
|
}
|
|
|
|
// split the new file into lines
|
|
NLMISC::CVectorSString lines;
|
|
fileBody.splitLines(lines);
|
|
|
|
// display the lines
|
|
nlinfo("Listing lines %u to %u of %u for file: %s",firstLine,std::min(uint32(firstLine+count-1),(uint32)lines.size()),lines.size(),args[0].c_str());
|
|
for (uint32 i=0;i<count && i+firstLine<=lines.size();++i)
|
|
nlinfo("%6i %s",i+firstLine,lines[i+firstLine-1].c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,viewBinFile,"view a binary file segment","<file_name> [<first_offset=0>[ <length=65536>]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
// deal with command line arguments
|
|
uint32 start=0;
|
|
uint32 count=65536;
|
|
switch (args.size())
|
|
{
|
|
case 3:
|
|
NLMISC::fromString(args[2], count);
|
|
if (count<1||args[2]!=NLMISC::toString("%u",count))
|
|
return false;
|
|
|
|
case 2:
|
|
NLMISC::fromString(args[1], start);
|
|
if (args[1]!=NLMISC::toString("%u",start))
|
|
return false;
|
|
|
|
case 1:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// read the new file and convert to lines
|
|
NLMISC::CSString fileBody;
|
|
fileBody.readFromFile(args[0]);
|
|
if (fileBody.empty())
|
|
{
|
|
nlwarning("File not found or file empty: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// ensure start is valid
|
|
if (start>=fileBody.size())
|
|
{
|
|
nlwarning("first_offset (%u) beyond end of file (%u): %s",start,fileBody.size(),args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// clamp the value of 'count'
|
|
if (start+count>fileBody.size())
|
|
{
|
|
count= (uint32)fileBody.size()-start;
|
|
}
|
|
|
|
// display the data
|
|
uint32 entriesPerLine=20;
|
|
nlinfo("Dumping offset %u to %u of %u for file: %s",start,start+count,fileBody.size(),args[0].c_str());
|
|
for (uint32 i=0;i<(count+15)/entriesPerLine;++i)
|
|
{
|
|
NLMISC::CSString s;
|
|
|
|
// generate the address
|
|
s= NLMISC::toString("%10u ",start+entriesPerLine*i);
|
|
|
|
// generate the hex text
|
|
uint32 j=0;
|
|
for (;j<entriesPerLine && entriesPerLine*i+j<count;++j)
|
|
s+=NLMISC::toString("%02X ",(uint8)fileBody[start+entriesPerLine*i+j]);
|
|
|
|
// pad out to the start of the ASCII text
|
|
for (;j<entriesPerLine;++j)
|
|
s+=" ";
|
|
s+=" ";
|
|
|
|
// generate the ASCII text
|
|
for (j=0;j<entriesPerLine && entriesPerLine*i+j<count;++j)
|
|
s+=NLMISC::CSString::isPrintable(fileBody[start+entriesPerLine*i+j])?fileBody[start+entriesPerLine*i+j]:'.';
|
|
|
|
// display the line
|
|
log.displayNL("%s",s.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handy utility commands - text editing
|
|
//-----------------------------------------------------------------------------
|
|
// txtEditAppend <text>
|
|
// txtEditCopy <start_line> <end_line> <insert_location>
|
|
// txtEditDeleteLines <first_line>[ <last_line>]
|
|
// txtEditErase
|
|
// txtEditInsert <line_number> <text>
|
|
// txtEditList [<first_line=1>[ <num_lines=100>]]
|
|
// txtEditMergeFile <insert_location> <file_name> [<first_line> <last_line>]
|
|
// txtEditNew <fileName>
|
|
// txtEditRead <fileName>
|
|
// txtEditSet <line_number> <text>
|
|
// txtEditToFile <file name>
|
|
// txtEditUnload
|
|
// txtEditWrite
|
|
|
|
static NLMISC::CSString TxtEditFileName;
|
|
static NLMISC::CVectorSString TxtEditLines;
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditUnload,"unload the text file in ram","")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=0)
|
|
return false;
|
|
|
|
TxtEditFileName.clear();
|
|
TxtEditLines.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditRead,"load a text file into ram","<fileName>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
// make sure there is no file currently loaded
|
|
if (!TxtEditFileName.empty())
|
|
{
|
|
nlwarning("unable to open file '%s' because file '%s' is already open",args[0].c_str(),TxtEditFileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
// check that the file exists
|
|
if (!NLMISC::CFile::fileExists(args[0]))
|
|
{
|
|
nlwarning("File Not Found: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// read the file and split into lines
|
|
NLMISC::CSString fileBody;
|
|
fileBody.readFromFile(args[0]);
|
|
fileBody.splitLines(TxtEditLines);
|
|
|
|
// finish up
|
|
TxtEditFileName=args[0];
|
|
nlinfo("Loaded %u lines from file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditMergeFile,"load a text file into ram","<insert_location> <file_name> [<first_line> <last_line>]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded - nothing to save");
|
|
return true;
|
|
}
|
|
|
|
// make sure there's a valid arg count
|
|
switch (args.size())
|
|
{
|
|
case 4:
|
|
case 2:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// get the insert location and make sure it's valid
|
|
uint32 insertPosition;
|
|
NLMISC::fromString(args[0], insertPosition);
|
|
if (insertPosition<1|| args[0]!=NLMISC::toString("%u",insertPosition)) return false;
|
|
if (insertPosition>TxtEditLines.size()+1) { nlwarning("Invalid insert position"); return false; }
|
|
|
|
// read the new file and convert to lines
|
|
NLMISC::CVectorSString newLines;
|
|
NLMISC::CSString fileBody;
|
|
fileBody.readFromFile(args[1]);
|
|
fileBody.splitLines(newLines);
|
|
if (fileBody.empty()) { nlwarning("File not found or file empty: %s",args[1].c_str()); return false; }
|
|
|
|
if (args.size()==2)
|
|
{
|
|
// we have to merge in the whole file...
|
|
TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),newLines.begin(),newLines.end());
|
|
nlinfo("Merged in %u lines from file :%s",newLines.size(),args[1].c_str());
|
|
}
|
|
else
|
|
{
|
|
// we only want part of the new file
|
|
|
|
// determine the first and last lines to extract from the new file
|
|
uint32 firstLine, lastLine;
|
|
NLMISC::fromString(args[2], firstLine);
|
|
if (firstLine<1|| args[2]!=NLMISC::toString("%u",firstLine)) return false;
|
|
NLMISC::fromString(args[3], lastLine);
|
|
if (lastLine<1|| args[3]!=NLMISC::toString("%u",lastLine)) return false;
|
|
|
|
// make sure the line numbers are valid
|
|
if (firstLine==0||firstLine>lastLine||lastLine>newLines.size())
|
|
{
|
|
nlwarning("invalid line number argument or first line is beyond last line");
|
|
return false;
|
|
}
|
|
|
|
// do the merge
|
|
NLMISC::CVectorSString linesToMerge(newLines.begin()+(firstLine-1),newLines.begin()+lastLine);
|
|
TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),linesToMerge.begin(),linesToMerge.end());
|
|
nlinfo("Merged in %u lines from file :%s",linesToMerge.size(),args[1].c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditNew,"prepare to edit a new textfile","<fileName>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
// make sure there is no file currently loaded
|
|
if (!TxtEditFileName.empty())
|
|
{
|
|
nlwarning("unable to craete new file '%s' because file '%s' is already open",args[0].c_str(),TxtEditFileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
// check that the file doesn't exist
|
|
if (NLMISC::CFile::fileExists(args[0]))
|
|
{
|
|
nlwarning("File Already Exists: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// finish up
|
|
TxtEditLines.clear();
|
|
TxtEditFileName=args[0];
|
|
nlinfo("Created new empty buffer for file :%s",TxtEditFileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditWrite,"save a modified text file","")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=0)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded - nothing to save");
|
|
return true;
|
|
}
|
|
|
|
// join the lines and write to file
|
|
nlinfo("Writing %u lines to file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
|
|
NLMISC::CSString fileBody;
|
|
fileBody.join(TxtEditLines,'\n');
|
|
fileBody.writeToFile(TxtEditFileName);
|
|
|
|
// finish up
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditToFile,"save a loaded text file","<file name>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded - nothing to save");
|
|
return true;
|
|
}
|
|
|
|
// check that the file doesn't exist
|
|
if (NLMISC::CFile::fileExists(args[0]))
|
|
{
|
|
nlwarning("File Already Exists: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// set the new file name
|
|
TxtEditFileName=args[0];
|
|
|
|
// join the lines and write to file
|
|
nlinfo("Writing %u lines to file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
|
|
NLMISC::CSString fileBody;
|
|
fileBody.join(TxtEditLines,'\n');
|
|
fileBody.writeToFile(TxtEditFileName);
|
|
|
|
// finish up
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditList,"list the lines in a loaded textfile","[<first_line=1>[ <num_lines=100>]]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// deal with command line arguments
|
|
uint32 firstLine=1;
|
|
uint32 count=100;
|
|
switch (args.size())
|
|
{
|
|
case 2:
|
|
NLMISC::fromString(args[1], count);
|
|
if (count<1||args[1]!=NLMISC::toString("%u",count))
|
|
return false;
|
|
|
|
case 1:
|
|
NLMISC::fromString(args[0], firstLine);
|
|
if (firstLine<1||args[0]!=NLMISC::toString("%u",firstLine))
|
|
return false;
|
|
|
|
case 0:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// display the lines
|
|
nlinfo("Listing lines %u to %u of %u for file: %s",firstLine,std::min(uint32(firstLine+count-1),(uint32)TxtEditLines.size()),TxtEditLines.size(),TxtEditFileName.c_str());
|
|
for (uint32 i=0;i<count && i+firstLine<=TxtEditLines.size();++i)
|
|
nlinfo("%6i %s",i+firstLine,TxtEditLines[i+firstLine-1].c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditDeleteLines,"delete one or more lines in a loaded textfile","<first_line>[ <last_line>]")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// deal with command line arguments
|
|
uint32 firstLine=0;
|
|
uint32 lastLine=0;
|
|
switch (args.size())
|
|
{
|
|
case 2:
|
|
NLMISC::fromString(args[1], lastLine);
|
|
if (lastLine<1||args[1]!=NLMISC::toString("%u",lastLine))
|
|
return false;
|
|
|
|
case 1:
|
|
NLMISC::fromString(args[0], firstLine);
|
|
if (firstLine<1||args[0]!=NLMISC::toString("%u",firstLine))
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (lastLine==0)
|
|
lastLine= firstLine;
|
|
|
|
if (lastLine<firstLine)
|
|
{
|
|
nlwarning("last_line (%u) must be >= first_line (%u)",lastLine,firstLine);
|
|
return false;
|
|
}
|
|
|
|
// ensure that the lines to be deleted fall within the file
|
|
if (lastLine>TxtEditLines.size())
|
|
{
|
|
nlwarning("last_line (%u) must be <= num lines (%u) in file: %s",lastLine,TxtEditLines.size(),args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// delete the lines
|
|
nlinfo("Deleting lines %u to %u of %u for file: %s",firstLine,lastLine,TxtEditLines.size(),args[0].c_str());
|
|
TxtEditLines.erase(TxtEditLines.begin()+firstLine-1,TxtEditLines.begin()+lastLine);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditErase,"erase all of the current contents of the currently loaded file","")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=0)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// do the work...
|
|
TxtEditLines.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditAppend,"append a line of text to a loaded textfile","<text>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=1)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
TxtEditLines.push_back(args[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditCopy,"duplicate a segment of the text file and insert it at the given location","<start_line> <end_line> <insert_location>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=3)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// extract numeric values for args and verify theri validity
|
|
uint32 firstLine, lastLine, insertPosition;
|
|
NLMISC::fromString(args[0], firstLine);
|
|
if (firstLine<1|| args[0]!=NLMISC::toString("%u",firstLine)) return false;
|
|
NLMISC::fromString(args[1], lastLine);
|
|
if (lastLine<1|| args[1]!=NLMISC::toString("%u",lastLine)) return false;
|
|
NLMISC::fromString(args[2], insertPosition);
|
|
if (insertPosition<1|| args[2]!=NLMISC::toString("%u",insertPosition)) return false;
|
|
|
|
// make sure the line numbers are valid
|
|
if (firstLine>lastLine||lastLine>TxtEditLines.size()||insertPosition>TxtEditLines.size()+1)
|
|
{
|
|
nlwarning("invalid line number argument or first line is beyond last line");
|
|
return false;
|
|
}
|
|
|
|
// duplicate the lines in question and insert them
|
|
NLMISC::CVectorSString newLines(TxtEditLines.begin()+firstLine-1,TxtEditLines.begin()+lastLine);
|
|
TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),newLines.begin(),newLines.end());
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditInsert,"insert a line into a loaded text file","<line_number> <text>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// extract the line number
|
|
uint32 lineNumber;
|
|
NLMISC::fromString(args[0], lineNumber);
|
|
if (lineNumber<1||args[0]!=NLMISC::toString("%u",lineNumber))
|
|
return false;
|
|
|
|
// deal with special case of insert at end of buffer
|
|
if (lineNumber==TxtEditLines.size()+1)
|
|
{
|
|
TxtEditLines.push_back(args[1]);
|
|
return true;
|
|
}
|
|
|
|
// make sure the line number falls within the file
|
|
if (lineNumber>TxtEditLines.size())
|
|
{
|
|
nlwarning("line number (%u) must be less than or equal to num lines in file (%u)",lineNumber,TxtEditLines.size());
|
|
return false;
|
|
}
|
|
|
|
TxtEditLines.insert(TxtEditLines.begin()+(lineNumber-1),args[1]);
|
|
return true;
|
|
}
|
|
|
|
NLMISC_CATEGORISED_COMMAND(utils,txtEditSet,"change a line in a loaded text file","<line_number> <text>")
|
|
{
|
|
NLMISC::CNLLogOverride logOverride(&log);
|
|
|
|
if (args.size()!=2)
|
|
return false;
|
|
|
|
// make sure there is a file currently loaded
|
|
if (TxtEditFileName.empty())
|
|
{
|
|
nlwarning("no text file currently loaded");
|
|
return true;
|
|
}
|
|
|
|
// extract the line number
|
|
uint32 lineNumber;
|
|
NLMISC::fromString(args[0], lineNumber);
|
|
if (lineNumber<1||args[0]!=NLMISC::toString("%u",lineNumber))
|
|
return false;
|
|
|
|
// make sure the line number falls within the file
|
|
if (lineNumber>TxtEditLines.size())
|
|
{
|
|
nlwarning("line number (%u) must be less than or equal to num lines in file (%u)",lineNumber,TxtEditLines.size());
|
|
return false;
|
|
}
|
|
|
|
TxtEditLines[lineNumber-1]= args[1];
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// init the sheetid.bin file for use by PDR
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CForSheetId: public IServiceSingleton
|
|
{
|
|
public:
|
|
void init()
|
|
{
|
|
// NLMISC::CSheetId::init(false);
|
|
}
|
|
};
|
|
|
|
static CForSheetId ForSheetId;
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// Fake variable to force a link to the handy_commands module
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
CHandyCommandIncluderClass::CHandyCommandIncluderClass()
|
|
{
|
|
static bool firstTime= true;
|
|
|
|
if (!firstTime)
|
|
return;
|
|
|
|
// nldebug("Activating Handy Commands");
|
|
firstTime= false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|