// Ryzom - MMORPG Framework
// 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 .
//-----------------------------------------------------------------------------
// 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
// pdrXml2bin
// pdr2xml
// pdr2bin
// pdr2txt
NLMISC_CATEGORISED_COMMAND(utils,pdrBin2xml,"convert a binary pdr file to xml"," ")
{
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"," ")
{
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"," |'@' [ [...]]")
{
NLMISC::CNLLogOverride logOverride(&log);
if (args.size()<1)
return false;
for (uint32 i=0;i %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"," |'@' [ [...]]")
{
NLMISC::CNLLogOverride logOverride(&log);
if (args.size()<1)
return false;
for (uint32 i=0;i %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"," |'@' [ [...]]")
{
NLMISC::CNLLogOverride logOverride(&log);
if (args.size()<1)
return false;
for (uint32 i=0;i %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"," ")
{
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)"," |'@' [ [...]] [.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
// thoroughFileCompare []
NLMISC_CATEGORISED_COMMAND(utils,quickFileCompare,"compare 2 files (by comparing timestamp and size)"," ")
{
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)"," []")
{
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 []
// md
// copyFile
// del
// dir []
// deltree
NLMISC_CATEGORISED_COMMAND(utils,cd,"change directory or display current working directory","[]")
{
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)","")
{
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"," ")
{
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","")
{
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","[]")
{
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 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 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")
{
NLMISC::CNLLogOverride logOverride(&log);
if (args.size()!=1)
return false;
CFileDescriptionContainer tempFiles;
tempFiles.addFileSpec(args[0],true);
for (uint32 i=0;i [[ ]]
// viewBinFile [[ ]]
NLMISC_CATEGORISED_COMMAND(utils,viewTxtFile,"view a text file segment"," [[ ]]")
{
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 [[ ]]")
{
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
// txtEditCopy
// txtEditDeleteLines [ ]
// txtEditErase
// txtEditInsert
// txtEditList [[ ]]
// txtEditMergeFile [ ]
// txtEditNew
// txtEditRead
// txtEditSet
// txtEditToFile
// 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","")
{
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"," [ ]")
{
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","")
{
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","")
{
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","[[ ]]")
{
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[ ]")
{
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= 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","")
{
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"," ")
{
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"," ")
{
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"," ")
{
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;
}
//-----------------------------------------------------------------------------