Added ai command setEquipment
[ryzomcore.git] / ryzom / server / src / server_share / handy_commands.cpp
blobe2f06a45eb9641a30b02078f8f61552f0ad4bdba
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //-----------------------------------------------------------------------------
18 // includes
19 //-----------------------------------------------------------------------------
20 #include "stdpch.h"
21 #include "nel/misc/path.h"
22 #include "nel/misc/command.h"
23 #include "nel/misc/algo.h"
24 #include "nel/misc/sheet_id.h"
25 #include "game_share/file_description_container.h"
26 #include "game_share/persistent_data.h"
27 #include "game_share/singleton_registry.h"
28 #include "handy_commands.h"
31 //-----------------------------------------------------------------------------
32 // Very handy utility routines for the handy utility commands
33 //-----------------------------------------------------------------------------
35 static void readFileList(const NLMISC::CSString& fileListName, CFileDescriptionContainer& result)
37 // read the file list from disk (the result will be empty if the file list didn't exist)
38 NLMISC::CSString fileList;
39 fileList.readFromFile(fileListName);
41 // split the file list text block into lines
42 NLMISC::CVectorSString files;
43 fileList.splitLines(files);
45 // iterate over the lies in the input file
46 for(uint32 i=0;i<files.size();++i)
48 // clean lines up, stripping spaces and quotes
49 NLMISC::CSString theFile= files[i].strip().unquoteIfQuoted();
51 // skip empty lines and comments
52 if (theFile.empty() || theFile.left(1)=="#")
53 continue;
55 // add the file name to the result
56 result.addFile(theFile);
60 static void addToFdc(const NLMISC::CSString& filespec, CFileDescriptionContainer& result)
62 if (filespec.left(1)=="@")
64 readFileList(filespec.leftCrop(1),result);
66 else
68 result.addFileSpec(filespec);
73 //-----------------------------------------------------------------------------
74 // Handy utility commands - pdr
75 //-----------------------------------------------------------------------------
76 // pdrBin2xml <input file name> <output file name>
77 // pdrXml2bin <input file name> <output file name>
78 // pdr2xml <input file spec>
79 // pdr2bin <input file spec>
80 // pdr2txt <input file spec>
83 NLMISC_CATEGORISED_COMMAND(utils,pdrBin2xml,"convert a binary pdr file to xml","<input file name> <output file name>")
85 NLMISC::CNLLogOverride logOverride(&log);
87 if (args.size()!=2)
88 return false;
90 log.displayNL("Converting to XML : %s => %s",args[0].c_str(),args[1].c_str());
92 static CPersistentDataRecord pdr;
93 pdr.clear();
94 pdr.readFromBinFile(args[0]);
95 pdr.writeToTxtFile(args[1]);
97 return true;
100 NLMISC_CATEGORISED_COMMAND(utils,pdrXml2bin,"convert a text pdr file to binary","<input file name> <output file name>")
102 NLMISC::CNLLogOverride logOverride(&log);
104 if (args.size()!=2)
105 return false;
107 log.displayNL("Converting to Binary : %s => %s",args[0].c_str(),args[1].c_str());
109 static CPersistentDataRecord pdr;
110 pdr.clear();
111 pdr.readFromTxtFile(args[0]);
112 pdr.writeToBinFile(args[1]);
114 return true;
117 NLMISC_CATEGORISED_COMMAND(utils,pdr2xml,"convert one or more sets of pdr files to xml format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
119 NLMISC::CNLLogOverride logOverride(&log);
121 if (args.size()<1)
122 return false;
124 for (uint32 i=0;i<args.size();++i)
126 CFileDescriptionContainer fdc;
127 addToFdc(args[i],fdc);
129 DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
131 for (uint32 j=0;j<fdc.size();++j)
133 const CFileDescription& fd=fdc[j];
134 NLMISC::CSString outputFileName=
135 (fd.FileName.right(4).toLower()==".bin" || fd.FileName.right(4).toLower()==".txt")?
136 fd.FileName.rightCrop(4)+".xml": fd.FileName+".xml";
138 log.displayNL("Converting to XML : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
139 static CPersistentDataRecord pdr;
140 pdr.clear();
141 pdr.readFromFile(fd.FileName);
142 pdr.writeToFile(outputFileName);
146 return true;
149 NLMISC_CATEGORISED_COMMAND(utils,pdr2bin,"convert one or more sets of pdr files to binary format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
151 NLMISC::CNLLogOverride logOverride(&log);
153 if (args.size()<1)
154 return false;
156 for (uint32 i=0;i<args.size();++i)
158 CFileDescriptionContainer fdc;
159 addToFdc(args[i],fdc);
161 DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
163 for (uint32 j=0;j<fdc.size();++j)
165 const CFileDescription& fd=fdc[j];
166 NLMISC::CSString outputFileName=
167 (fd.FileName.right(4).toLower()==".xml" || fd.FileName.right(4).toLower()==".txt")?
168 fd.FileName.rightCrop(4)+".bin": fd.FileName+".bin";
170 log.displayNL("Converting to Binary : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
171 static CPersistentDataRecord pdr;
172 pdr.clear();
173 pdr.readFromFile(fd.FileName);
174 pdr.writeToFile(outputFileName);
178 return true;
181 NLMISC_CATEGORISED_COMMAND(utils,pdr2txt,"convert one or more sets of pdr files to txt (lines) format","<input file spec>|'@'<listfile> [<input file spec>[...]]")
183 NLMISC::CNLLogOverride logOverride(&log);
185 if (args.size()<1)
186 return false;
188 for (uint32 i=0;i<args.size();++i)
190 CFileDescriptionContainer fdc;
191 addToFdc(args[i],fdc);
193 DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
195 for (uint32 j=0;j<fdc.size();++j)
197 const CFileDescription& fd=fdc[j];
198 NLMISC::CSString outputFileName=
199 (fd.FileName.right(4).toLower()==".bin" || fd.FileName.right(4).toLower()==".xml")?
200 fd.FileName.rightCrop(4)+".txt": fd.FileName+".txt";
202 log.displayNL("Converting to TXT : %s => %s",fd.FileName.c_str(),outputFileName.c_str());
203 static CPersistentDataRecord pdr;
204 pdr.clear();
205 pdr.readFromFile(fd.FileName);
206 pdr.writeToFile(outputFileName);
210 return true;
213 NLMISC_CATEGORISED_COMMAND(utils,pdrFileCompare,"Compare 2 pdr files","<first file> <second file>")
215 NLMISC::CNLLogOverride logOverride(&log);
217 if (args.size()!=2)
218 return false;
220 CPersistentDataRecord pdr0;
221 pdr0.readFromFile(args[0]);
223 CPersistentDataRecord pdr1;
224 pdr1.readFromFile(args[1]);
226 log.displayNL("%s : %s / %s", (pdr0==pdr1)?"Files MATCH":"Files DON'T match", args[0].c_str(), args[1].c_str());
228 return true;
231 NLMISC_CATEGORISED_COMMAND(utils,pdrInfo,"Extract info from pdr file(s)","<input file spec>|'@'<listfile> [<input file spec>[...]] [<output fil name>.csv]")
233 NLMISC::CNLLogOverride logOverride(&log);
235 NLMISC::CSString outputFileName;
236 NLMISC::CSString csvTxt;
237 uint32 numFileSpecs=(uint32)args.size();
239 if (numFileSpecs>0 && NLMISC::CSString(args.back()).right(4)==".csv")
241 outputFileName= args.back();
242 --numFileSpecs;
243 log.displayNL("Extracting PDR info to write to csv file: %s",outputFileName.quoteIfNotQuoted().c_str());
244 csvTxt= "FileName,"+ CPersistentDataRecord::getCSVHeaderLine()+"\n";
246 else
248 log.displayNL("Extracting PDR info (No output file specified so no csv file will be written)",outputFileName.quoteIfNotQuoted().c_str());
251 if (numFileSpecs<1)
252 return false;
254 for (uint32 i=0;i<numFileSpecs;++i)
256 CFileDescriptionContainer fdc;
258 H_AUTO(pdrInfo_BuildFDC)
259 addToFdc(args[i],fdc);
262 DROP_IF(fdc.empty(),"No files found that match file spec: "<<args[i],continue);
264 for (uint32 j=0;j<fdc.size();++j)
266 H_AUTO(pdrInfo_treatFile)
267 static CPersistentDataRecord pdr;
268 pdr.clear();
270 const CFileDescription& fd=fdc[j];
271 log.display("- %s: ", fd.FileName.quoteIfNotQuoted().c_str());
274 H_AUTO(pdrInfo_readFromFile)
275 pdr.readFromFile(fd.FileName);
278 log.displayNL("%s", pdr.getInfo().c_str());
280 if (!outputFileName.empty())
282 csvTxt+=NLMISC::CFile::getFilenameWithoutExtension(fd.FileName)+","+pdr.getInfoAsCSV()+"\n";
287 if (!outputFileName.empty())
289 log.displayNL("Writing file: %s",outputFileName.quoteIfNotQuoted().c_str());
290 csvTxt.writeToFile(outputFileName);
292 log.displayNL("Info extraction from PDRs finished");
294 return true;
298 //-----------------------------------------------------------------------------
299 // Handy utility commands - file compare
300 //-----------------------------------------------------------------------------
301 // quickFileCompare <file0> <file1>
302 // thoroughFileCompare <file0> <file1> [<max mem footprint>]
304 NLMISC_CATEGORISED_COMMAND(utils,quickFileCompare,"compare 2 files (by comparing timestamp and size)","<file0> <file1>")
306 NLMISC::CNLLogOverride logOverride(&log);
308 if (args.size()!=2)
309 return false;
311 nlinfo("comparing files ...");
312 bool result= NLMISC::CFile::quickFileCompare(args[0], args[1]);
313 nlinfo("- %s",result?"Same":"Different");
315 return true;
318 NLMISC_CATEGORISED_COMMAND(utils,thoroughFileCompare,"compare 2 files (by comparing data)","<file0> <file1> [<max mem footprint>]")
320 NLMISC::CNLLogOverride logOverride(&log);
322 if (args.size()!=2 && args.size()!=3)
323 return false;
325 bool result;
326 nlinfo("comparing files ...");
328 if (args.size()==3)
330 uint32 size;
331 NLMISC::fromString(args[2], size);
332 if (size<2)
334 nlwarning("The third parameter must be a value >= 2 : The following value is not valid: %s",args[2].c_str());
335 return true;
337 result= NLMISC::CFile::thoroughFileCompare(args[0], args[1], size);
339 else
341 result= NLMISC::CFile::thoroughFileCompare(args[0], args[1]);
344 nlinfo("- %s",result?"Same":"Different");
345 return true;
349 //-----------------------------------------------------------------------------
350 // Handy utility commands - file and directory management
351 //-----------------------------------------------------------------------------
352 // cd [<path>]
353 // md <path>
354 // copyFile <src> <dest>
355 // del <fileName>
356 // dir [<wildcard>]
357 // deltree <directory>
360 NLMISC_CATEGORISED_COMMAND(utils,cd,"change directory or display current working directory","[<path>]")
362 NLMISC::CNLLogOverride logOverride(&log);
364 if (args.size()!=0 && args.size()!=1)
365 return false;
367 if (args.size()==1)
369 NLMISC::CPath::setCurrentPath(args[0].c_str());
372 nlinfo("Current directory: %s",NLMISC::CPath::getCurrentPath().c_str());
373 return true;
376 NLMISC_CATEGORISED_COMMAND(utils,md,"create a new directory (or directory tree)","<path>")
378 NLMISC::CNLLogOverride logOverride(&log);
380 if (args.size()!=1)
381 return false;
383 NLMISC::CFile::createDirectoryTree(args[0]);
385 return true;
388 NLMISC_CATEGORISED_COMMAND(utils,copyFile,"copy a file","<src> <dest>")
390 NLMISC::CNLLogOverride logOverride(&log);
392 if (args.size()!=2)
393 return false;
395 NLMISC::CFile::copyFile(args[1],args[0]);
397 return true;
400 NLMISC_CATEGORISED_COMMAND(utils,del,"delete a file","<fileName>")
402 NLMISC::CNLLogOverride logOverride(&log);
404 if (args.size()!=1)
405 return false;
407 NLMISC::CFile::deleteFile(args[0]);
409 return true;
412 NLMISC_CATEGORISED_COMMAND(utils,dir,"list files in the current directory","[<wildcard>]")
414 NLMISC::CNLLogOverride logOverride(&log);
416 if (args.size()!=1 && args.size()!=0)
417 return false;
419 std::string wildcard="*";
420 if (args.size()==1)
421 wildcard=args[0];
423 std::vector<std::string> directories;
424 NLMISC::CPath::getPathContent(".",false,true,false,directories);
425 for (uint32 i=(uint32)directories.size();i--;)
427 if (!NLMISC::testWildCard(directories[i],wildcard))
429 directories[i]=directories.back();
430 directories.pop_back();
433 std::sort(directories.begin(),directories.end());
434 for (uint32 i=0;i<directories.size();++i)
436 nlinfo("%s/",directories[i].c_str());
439 std::vector<std::string> files;
440 NLMISC::CPath::getPathContent(".",false,false,true,files);
441 for (uint32 i=(uint32)files.size();i--;)
443 if (!NLMISC::testWildCard(files[i],wildcard))
445 files[i]=files.back();
446 files.pop_back();
449 std::sort(files.begin(),files.end());
450 for (uint32 i=0;i<files.size();++i)
452 nlinfo("%-40s %10d",files[i].c_str(),NLMISC::CFile::getFileSize(files[i]));
455 return true;
458 NLMISC_CATEGORISED_COMMAND(utils,deltree,"delete all files matching given spec, recursively","<fileSpec>")
460 NLMISC::CNLLogOverride logOverride(&log);
462 if (args.size()!=1)
463 return false;
465 CFileDescriptionContainer tempFiles;
466 tempFiles.addFileSpec(args[0],true);
467 for (uint32 i=0;i<tempFiles.size();++i)
469 NLMISC::CFile::deleteFile(tempFiles[i].FileName);
472 return true;
476 //-----------------------------------------------------------------------------
477 // Handy utility commands - file viewing
478 //-----------------------------------------------------------------------------
479 // viewTxtFile <file_name> [<first_line=1>[ <num_lines=100>]]
480 // viewBinFile <file_name> [<first_offset=0>[ <length=65536>]]
482 NLMISC_CATEGORISED_COMMAND(utils,viewTxtFile,"view a text file segment","<file_name> [<first_line=1>[ <num_lines=100>]]")
484 NLMISC::CNLLogOverride logOverride(&log);
486 // deal with command line arguments
487 uint32 firstLine=1;
488 uint32 count=100;
489 switch (args.size())
491 case 3:
492 NLMISC::fromString(args[2], count);
493 if (count<1||args[2]!=NLMISC::toString("%u",count))
494 return false;
496 case 2:
497 NLMISC::fromString(args[1], firstLine);
498 if (firstLine<1||args[1]!=NLMISC::toString("%u",firstLine))
499 return false;
501 case 1:
502 break;
503 default:
504 return false;
507 // read the new file
508 NLMISC::CSString fileBody;
509 fileBody.readFromFile(args[0]);
510 if (fileBody.empty())
512 nlwarning("File not found or file empty: %s",args[0].c_str());
513 return false;
516 // if we have a utf16 file then convert to utf8
517 if (fileBody.size()>=2 && ((fileBody[0]==char(0xff) && fileBody[1]==char(0xfe)) || (fileBody[0]==char(0xfe) && fileBody[1]==char(0xff))) )
519 nlinfo("Displaying unicode UTF16 text:");
520 ucstring ucs;
521 ucs.resize((fileBody.size()-2)/2);
522 memcpy((char*)&ucs[0],(char*)&fileBody[0],fileBody.size()-2);
523 fileBody=ucs.toUtf8();
526 // split the new file into lines
527 NLMISC::CVectorSString lines;
528 fileBody.splitLines(lines);
530 // display the lines
531 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());
532 for (uint32 i=0;i<count && i+firstLine<=lines.size();++i)
533 nlinfo("%6i %s",i+firstLine,lines[i+firstLine-1].c_str());
535 return true;
538 NLMISC_CATEGORISED_COMMAND(utils,viewBinFile,"view a binary file segment","<file_name> [<first_offset=0>[ <length=65536>]]")
540 NLMISC::CNLLogOverride logOverride(&log);
542 // deal with command line arguments
543 uint32 start=0;
544 uint32 count=65536;
545 switch (args.size())
547 case 3:
548 NLMISC::fromString(args[2], count);
549 if (count<1||args[2]!=NLMISC::toString("%u",count))
550 return false;
552 case 2:
553 NLMISC::fromString(args[1], start);
554 if (args[1]!=NLMISC::toString("%u",start))
555 return false;
557 case 1:
558 break;
559 default:
560 return false;
563 // read the new file and convert to lines
564 NLMISC::CSString fileBody;
565 fileBody.readFromFile(args[0]);
566 if (fileBody.empty())
568 nlwarning("File not found or file empty: %s",args[0].c_str());
569 return false;
572 // ensure start is valid
573 if (start>=fileBody.size())
575 nlwarning("first_offset (%u) beyond end of file (%u): %s",start,fileBody.size(),args[0].c_str());
576 return false;
579 // clamp the value of 'count'
580 if (start+count>fileBody.size())
582 count= (uint32)fileBody.size()-start;
585 // display the data
586 uint32 entriesPerLine=20;
587 nlinfo("Dumping offset %u to %u of %u for file: %s",start,start+count,fileBody.size(),args[0].c_str());
588 for (uint32 i=0;i<(count+15)/entriesPerLine;++i)
590 NLMISC::CSString s;
592 // generate the address
593 s= NLMISC::toString("%10u ",start+entriesPerLine*i);
595 // generate the hex text
596 uint32 j=0;
597 for (;j<entriesPerLine && entriesPerLine*i+j<count;++j)
598 s+=NLMISC::toString("%02X ",(uint8)fileBody[start+entriesPerLine*i+j]);
600 // pad out to the start of the ASCII text
601 for (;j<entriesPerLine;++j)
602 s+=" ";
603 s+=" ";
605 // generate the ASCII text
606 for (j=0;j<entriesPerLine && entriesPerLine*i+j<count;++j)
607 s+=NLMISC::CSString::isPrintable(fileBody[start+entriesPerLine*i+j])?fileBody[start+entriesPerLine*i+j]:'.';
609 // display the line
610 log.displayNL("%s",s.c_str());
613 return true;
616 //-----------------------------------------------------------------------------
617 // Handy utility commands - text editing
618 //-----------------------------------------------------------------------------
619 // txtEditAppend <text>
620 // txtEditCopy <start_line> <end_line> <insert_location>
621 // txtEditDeleteLines <first_line>[ <last_line>]
622 // txtEditErase
623 // txtEditInsert <line_number> <text>
624 // txtEditList [<first_line=1>[ <num_lines=100>]]
625 // txtEditMergeFile <insert_location> <file_name> [<first_line> <last_line>]
626 // txtEditNew <fileName>
627 // txtEditRead <fileName>
628 // txtEditSet <line_number> <text>
629 // txtEditToFile <file name>
630 // txtEditUnload
631 // txtEditWrite
633 static NLMISC::CSString TxtEditFileName;
634 static NLMISC::CVectorSString TxtEditLines;
636 NLMISC_CATEGORISED_COMMAND(utils,txtEditUnload,"unload the text file in ram","")
638 NLMISC::CNLLogOverride logOverride(&log);
640 if (args.size()!=0)
641 return false;
643 TxtEditFileName.clear();
644 TxtEditLines.clear();
646 return true;
649 NLMISC_CATEGORISED_COMMAND(utils,txtEditRead,"load a text file into ram","<fileName>")
651 NLMISC::CNLLogOverride logOverride(&log);
653 if (args.size()!=1)
654 return false;
656 // make sure there is no file currently loaded
657 if (!TxtEditFileName.empty())
659 nlwarning("unable to open file '%s' because file '%s' is already open",args[0].c_str(),TxtEditFileName.c_str());
660 return true;
663 // check that the file exists
664 if (!NLMISC::CFile::fileExists(args[0]))
666 nlwarning("File Not Found: %s",args[0].c_str());
667 return false;
670 // read the file and split into lines
671 NLMISC::CSString fileBody;
672 fileBody.readFromFile(args[0]);
673 fileBody.splitLines(TxtEditLines);
675 // finish up
676 TxtEditFileName=args[0];
677 nlinfo("Loaded %u lines from file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
678 return true;
681 NLMISC_CATEGORISED_COMMAND(utils,txtEditMergeFile,"load a text file into ram","<insert_location> <file_name> [<first_line> <last_line>]")
683 NLMISC::CNLLogOverride logOverride(&log);
685 // make sure there is a file currently loaded
686 if (TxtEditFileName.empty())
688 nlwarning("no text file currently loaded - nothing to save");
689 return true;
692 // make sure there's a valid arg count
693 switch (args.size())
695 case 4:
696 case 2:
697 break;
698 default:
699 return false;
702 // get the insert location and make sure it's valid
703 uint32 insertPosition;
704 NLMISC::fromString(args[0], insertPosition);
705 if (insertPosition<1|| args[0]!=NLMISC::toString("%u",insertPosition)) return false;
706 if (insertPosition>TxtEditLines.size()+1) { nlwarning("Invalid insert position"); return false; }
708 // read the new file and convert to lines
709 NLMISC::CVectorSString newLines;
710 NLMISC::CSString fileBody;
711 fileBody.readFromFile(args[1]);
712 fileBody.splitLines(newLines);
713 if (fileBody.empty()) { nlwarning("File not found or file empty: %s",args[1].c_str()); return false; }
715 if (args.size()==2)
717 // we have to merge in the whole file...
718 TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),newLines.begin(),newLines.end());
719 nlinfo("Merged in %u lines from file :%s",newLines.size(),args[1].c_str());
721 else
723 // we only want part of the new file
725 // determine the first and last lines to extract from the new file
726 uint32 firstLine, lastLine;
727 NLMISC::fromString(args[2], firstLine);
728 if (firstLine<1|| args[2]!=NLMISC::toString("%u",firstLine)) return false;
729 NLMISC::fromString(args[3], lastLine);
730 if (lastLine<1|| args[3]!=NLMISC::toString("%u",lastLine)) return false;
732 // make sure the line numbers are valid
733 if (firstLine==0||firstLine>lastLine||lastLine>newLines.size())
735 nlwarning("invalid line number argument or first line is beyond last line");
736 return false;
739 // do the merge
740 NLMISC::CVectorSString linesToMerge(newLines.begin()+(firstLine-1),newLines.begin()+lastLine);
741 TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),linesToMerge.begin(),linesToMerge.end());
742 nlinfo("Merged in %u lines from file :%s",linesToMerge.size(),args[1].c_str());
745 return true;
748 NLMISC_CATEGORISED_COMMAND(utils,txtEditNew,"prepare to edit a new textfile","<fileName>")
750 NLMISC::CNLLogOverride logOverride(&log);
752 if (args.size()!=1)
753 return false;
755 // make sure there is no file currently loaded
756 if (!TxtEditFileName.empty())
758 nlwarning("unable to craete new file '%s' because file '%s' is already open",args[0].c_str(),TxtEditFileName.c_str());
759 return true;
762 // check that the file doesn't exist
763 if (NLMISC::CFile::fileExists(args[0]))
765 nlwarning("File Already Exists: %s",args[0].c_str());
766 return false;
769 // finish up
770 TxtEditLines.clear();
771 TxtEditFileName=args[0];
772 nlinfo("Created new empty buffer for file :%s",TxtEditFileName.c_str());
773 return true;
776 NLMISC_CATEGORISED_COMMAND(utils,txtEditWrite,"save a modified text file","")
778 NLMISC::CNLLogOverride logOverride(&log);
780 if (args.size()!=0)
781 return false;
783 // make sure there is a file currently loaded
784 if (TxtEditFileName.empty())
786 nlwarning("no text file currently loaded - nothing to save");
787 return true;
790 // join the lines and write to file
791 nlinfo("Writing %u lines to file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
792 NLMISC::CSString fileBody;
793 fileBody.join(TxtEditLines,'\n');
794 fileBody.writeToFile(TxtEditFileName);
796 // finish up
797 return true;
800 NLMISC_CATEGORISED_COMMAND(utils,txtEditToFile,"save a loaded text file","<file name>")
802 NLMISC::CNLLogOverride logOverride(&log);
804 if (args.size()!=1)
805 return false;
807 // make sure there is a file currently loaded
808 if (TxtEditFileName.empty())
810 nlwarning("no text file currently loaded - nothing to save");
811 return true;
814 // check that the file doesn't exist
815 if (NLMISC::CFile::fileExists(args[0]))
817 nlwarning("File Already Exists: %s",args[0].c_str());
818 return false;
821 // set the new file name
822 TxtEditFileName=args[0];
824 // join the lines and write to file
825 nlinfo("Writing %u lines to file :%s",TxtEditLines.size(),TxtEditFileName.c_str());
826 NLMISC::CSString fileBody;
827 fileBody.join(TxtEditLines,'\n');
828 fileBody.writeToFile(TxtEditFileName);
830 // finish up
831 return true;
834 NLMISC_CATEGORISED_COMMAND(utils,txtEditList,"list the lines in a loaded textfile","[<first_line=1>[ <num_lines=100>]]")
836 NLMISC::CNLLogOverride logOverride(&log);
838 // make sure there is a file currently loaded
839 if (TxtEditFileName.empty())
841 nlwarning("no text file currently loaded");
842 return true;
845 // deal with command line arguments
846 uint32 firstLine=1;
847 uint32 count=100;
848 switch (args.size())
850 case 2:
851 NLMISC::fromString(args[1], count);
852 if (count<1||args[1]!=NLMISC::toString("%u",count))
853 return false;
855 case 1:
856 NLMISC::fromString(args[0], firstLine);
857 if (firstLine<1||args[0]!=NLMISC::toString("%u",firstLine))
858 return false;
860 case 0:
861 break;
862 default:
863 return false;
866 // display the lines
867 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());
868 for (uint32 i=0;i<count && i+firstLine<=TxtEditLines.size();++i)
869 nlinfo("%6i %s",i+firstLine,TxtEditLines[i+firstLine-1].c_str());
871 return true;
874 NLMISC_CATEGORISED_COMMAND(utils,txtEditDeleteLines,"delete one or more lines in a loaded textfile","<first_line>[ <last_line>]")
876 NLMISC::CNLLogOverride logOverride(&log);
878 // make sure there is a file currently loaded
879 if (TxtEditFileName.empty())
881 nlwarning("no text file currently loaded");
882 return true;
885 // deal with command line arguments
886 uint32 firstLine=0;
887 uint32 lastLine=0;
888 switch (args.size())
890 case 2:
891 NLMISC::fromString(args[1], lastLine);
892 if (lastLine<1||args[1]!=NLMISC::toString("%u",lastLine))
893 return false;
895 case 1:
896 NLMISC::fromString(args[0], firstLine);
897 if (firstLine<1||args[0]!=NLMISC::toString("%u",firstLine))
898 return false;
899 break;
901 default:
902 return false;
905 if (lastLine==0)
906 lastLine= firstLine;
908 if (lastLine<firstLine)
910 nlwarning("last_line (%u) must be >= first_line (%u)",lastLine,firstLine);
911 return false;
914 // ensure that the lines to be deleted fall within the file
915 if (lastLine>TxtEditLines.size())
917 nlwarning("last_line (%u) must be <= num lines (%u) in file: %s",lastLine,TxtEditLines.size(),args[0].c_str());
918 return false;
921 // delete the lines
922 nlinfo("Deleting lines %u to %u of %u for file: %s",firstLine,lastLine,TxtEditLines.size(),args[0].c_str());
923 TxtEditLines.erase(TxtEditLines.begin()+firstLine-1,TxtEditLines.begin()+lastLine);
925 return true;
928 NLMISC_CATEGORISED_COMMAND(utils,txtEditErase,"erase all of the current contents of the currently loaded file","")
930 NLMISC::CNLLogOverride logOverride(&log);
932 if (args.size()!=0)
933 return false;
935 // make sure there is a file currently loaded
936 if (TxtEditFileName.empty())
938 nlwarning("no text file currently loaded");
939 return true;
942 // do the work...
943 TxtEditLines.clear();
945 return true;
948 NLMISC_CATEGORISED_COMMAND(utils,txtEditAppend,"append a line of text to a loaded textfile","<text>")
950 NLMISC::CNLLogOverride logOverride(&log);
952 if (args.size()!=1)
953 return false;
955 // make sure there is a file currently loaded
956 if (TxtEditFileName.empty())
958 nlwarning("no text file currently loaded");
959 return true;
962 TxtEditLines.push_back(args[0]);
964 return true;
967 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>")
969 NLMISC::CNLLogOverride logOverride(&log);
971 if (args.size()!=3)
972 return false;
974 // make sure there is a file currently loaded
975 if (TxtEditFileName.empty())
977 nlwarning("no text file currently loaded");
978 return true;
981 // extract numeric values for args and verify theri validity
982 uint32 firstLine, lastLine, insertPosition;
983 NLMISC::fromString(args[0], firstLine);
984 if (firstLine<1|| args[0]!=NLMISC::toString("%u",firstLine)) return false;
985 NLMISC::fromString(args[1], lastLine);
986 if (lastLine<1|| args[1]!=NLMISC::toString("%u",lastLine)) return false;
987 NLMISC::fromString(args[2], insertPosition);
988 if (insertPosition<1|| args[2]!=NLMISC::toString("%u",insertPosition)) return false;
990 // make sure the line numbers are valid
991 if (firstLine>lastLine||lastLine>TxtEditLines.size()||insertPosition>TxtEditLines.size()+1)
993 nlwarning("invalid line number argument or first line is beyond last line");
994 return false;
997 // duplicate the lines in question and insert them
998 NLMISC::CVectorSString newLines(TxtEditLines.begin()+firstLine-1,TxtEditLines.begin()+lastLine);
999 TxtEditLines.insert(TxtEditLines.begin()+(insertPosition-1),newLines.begin(),newLines.end());
1001 return true;
1004 NLMISC_CATEGORISED_COMMAND(utils,txtEditInsert,"insert a line into a loaded text file","<line_number> <text>")
1006 NLMISC::CNLLogOverride logOverride(&log);
1008 if (args.size()!=2)
1009 return false;
1011 // make sure there is a file currently loaded
1012 if (TxtEditFileName.empty())
1014 nlwarning("no text file currently loaded");
1015 return true;
1018 // extract the line number
1019 uint32 lineNumber;
1020 NLMISC::fromString(args[0], lineNumber);
1021 if (lineNumber<1||args[0]!=NLMISC::toString("%u",lineNumber))
1022 return false;
1024 // deal with special case of insert at end of buffer
1025 if (lineNumber==TxtEditLines.size()+1)
1027 TxtEditLines.push_back(args[1]);
1028 return true;
1031 // make sure the line number falls within the file
1032 if (lineNumber>TxtEditLines.size())
1034 nlwarning("line number (%u) must be less than or equal to num lines in file (%u)",lineNumber,TxtEditLines.size());
1035 return false;
1038 TxtEditLines.insert(TxtEditLines.begin()+(lineNumber-1),args[1]);
1039 return true;
1042 NLMISC_CATEGORISED_COMMAND(utils,txtEditSet,"change a line in a loaded text file","<line_number> <text>")
1044 NLMISC::CNLLogOverride logOverride(&log);
1046 if (args.size()!=2)
1047 return false;
1049 // make sure there is a file currently loaded
1050 if (TxtEditFileName.empty())
1052 nlwarning("no text file currently loaded");
1053 return true;
1056 // extract the line number
1057 uint32 lineNumber;
1058 NLMISC::fromString(args[0], lineNumber);
1059 if (lineNumber<1||args[0]!=NLMISC::toString("%u",lineNumber))
1060 return false;
1062 // make sure the line number falls within the file
1063 if (lineNumber>TxtEditLines.size())
1065 nlwarning("line number (%u) must be less than or equal to num lines in file (%u)",lineNumber,TxtEditLines.size());
1066 return false;
1069 TxtEditLines[lineNumber-1]= args[1];
1070 return true;
1074 //-----------------------------------------------------------------------------
1075 // init the sheetid.bin file for use by PDR
1076 //-----------------------------------------------------------------------------
1078 class CForSheetId: public IServiceSingleton
1080 public:
1081 void init()
1083 // NLMISC::CSheetId::init(false);
1087 static CForSheetId ForSheetId;
1090 //-------------------------------------------------------------------------------------------------
1091 // Fake variable to force a link to the handy_commands module
1092 //-------------------------------------------------------------------------------------------------
1094 CHandyCommandIncluderClass::CHandyCommandIncluderClass()
1096 static bool firstTime= true;
1098 if (!firstTime)
1099 return;
1101 // nldebug("Activating Handy Commands");
1102 firstTime= false;
1106 //-----------------------------------------------------------------------------