"Fossies" - the Fresh Open Source Software Archive

Member "ansifilter-2.18/src/codegenerator.cpp" (30 Jan 2021, 38264 Bytes) of package /linux/privat/ansifilter-2.18.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "codegenerator.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.17_vs_2.18.

    1 /***************************************************************************
    2                       codegenerator.cpp  -  description
    3                              -------------------
    4     copyright            : (C) 2007-2021 by Andre Simon
    5     email                : a.simon@mailbox.org
    6  ***************************************************************************/
    7 
    8 
    9 /*
   10 This file is part of ANSIFilter.
   11 
   12 ANSIFilter is free software: you can redistribute it and/or modify
   13 it under the terms of the GNU General Public License as published by
   14 the Free Software Foundation, either version 3 of the License, or
   15 (at your option) any later version.
   16 
   17 ANSIFilter is distributed in the hope that it will be useful,
   18 but WITHOUT ANY WARRANTY; without even the implied warranty of
   19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   20 GNU General Public License for more details.
   21 
   22 You should have received a copy of the GNU General Public License
   23 along with ANSIFilter.  If not, see <http://www.gnu.org/licenses/>.
   24 */
   25 
   26 #include "codegenerator.h"
   27 
   28 #ifdef WIN32
   29 #include <windows.h>
   30 #else
   31 #include <unistd.h>
   32 #endif
   33 
   34 #include <cstdlib>
   35 #include <cstring>
   36 #include <fstream>
   37 #include "version.h"
   38 
   39 #include "pangogenerator.h"
   40 #include "htmlgenerator.h"
   41 #include "rtfgenerator.h"
   42 #include "plaintextgenerator.h"
   43 #include "texgenerator.h"
   44 #include "latexgenerator.h"
   45 #include "bbcodegenerator.h"
   46 #include "svggenerator.h"
   47 
   48 namespace ansifilter
   49 {
   50 
   51 CodeGenerator * CodeGenerator::getInstance(OutputType type)
   52 {
   53     CodeGenerator* generator=NULL;
   54     switch (type) {
   55     case TEXT:
   56         generator = new PlaintextGenerator();
   57         break;
   58     case HTML:
   59         generator = new HtmlGenerator();
   60         break;
   61     case PANGO:
   62         generator = new PangoGenerator();
   63         break;
   64     case LATEX:
   65         generator = new LaTeXGenerator();
   66         break;
   67     case TEX:
   68         generator = new TeXGenerator();
   69         break;
   70     case RTF:
   71         generator = new RtfGenerator();
   72         break;
   73     case BBCODE:
   74         generator = new BBCodeGenerator();
   75         break;
   76     case SVG:
   77         generator = new SVGGenerator();
   78         break;
   79 
   80     default:
   81         break;
   82     }
   83     return generator;
   84 }
   85 
   86 CodeGenerator::CodeGenerator(ansifilter::OutputType type)
   87     :in(NULL),
   88      out(NULL),
   89      tagIsOpen(false),
   90      encoding("none"),
   91      docTitle("Source file"),
   92      fragmentOutput(false),
   93      font("Courier New"),
   94      fontSize("10pt"),
   95 
   96      lineNumberWidth ( 5 ),
   97      lineNumber ( 0 ),
   98      showLineNumbers ( false ),
   99 
  100      numberWrappedLines ( true ), //TODO add option
  101      numberCurrentLine(false),
  102      addAnchors(false),
  103      applyDynStyles(false),
  104      omitVersionInfo(false),
  105      parseCP437(false),
  106      parseAsciiBin(false),
  107      parseAsciiTundra(false),
  108 
  109      outputType(type),
  110      ignoreFormatting(false),
  111      readAfterEOF(false),
  112      omitTrailingCR(false),
  113      ignClearSeq(false),
  114      ignCSISeq(false),
  115      termBuffer(NULL),
  116      curX(0),
  117      curY(0),
  118      memX(0),
  119      memY(0),
  120      maxY(0),
  121      asciiArtWidth(80),
  122      asciiArtHeight(150),
  123      lineWrapLen(0)
  124 {
  125     elementStyle.setFgColour(rgb2html(workingPalette[0]));
  126 }
  127 
  128 CodeGenerator::~CodeGenerator()
  129 {}
  130 
  131 
  132 void CodeGenerator::setShowLineNumbers(bool flag)
  133 {
  134     showLineNumbers=flag;
  135 }
  136 
  137 void CodeGenerator::setFragmentCode(bool flag)
  138 {
  139     fragmentOutput=flag;
  140 }
  141 
  142 void CodeGenerator::setWrapNoNumbers(bool flag)
  143 {
  144     numberWrappedLines = flag;
  145 }
  146 
  147 void CodeGenerator::setParseCodePage437(bool flag){
  148     parseCP437 = flag;
  149 }
  150 
  151 void CodeGenerator::setParseAsciiBin(bool flag){
  152     parseAsciiBin = flag;
  153 }
  154 
  155 void CodeGenerator::setParseAsciiTundra(bool flag){
  156     parseAsciiTundra = flag;
  157 }
  158 
  159 void CodeGenerator::setIgnoreClearSeq(bool flag) {
  160     ignClearSeq = flag;
  161 }
  162 
  163 void CodeGenerator::setIgnoreCSISeq(bool flag) {
  164     ignCSISeq = flag;
  165 }
  166 
  167 void CodeGenerator::setAsciiArtSize(int width, int height){
  168     if (width>0) asciiArtWidth = width;
  169     if (height>0) asciiArtHeight = height;
  170 }
  171 
  172 bool CodeGenerator::getFragmentCode()
  173 {
  174     return fragmentOutput;
  175 }
  176 
  177 void CodeGenerator::setFont(const string& s)
  178 {
  179     font = s;
  180 }
  181 
  182 void CodeGenerator::setFontSize(const string& s)
  183 {
  184     fontSize = s ;
  185 }
  186 
  187 void CodeGenerator::setTitle(const string & title)
  188 {
  189     if (!title.empty())
  190         docTitle= title;
  191     std::size_t found = docTitle.rfind("/");
  192     if (found!=std::string::npos){
  193         docTitle = docTitle.substr(found+1);
  194     }
  195 }
  196 
  197 string CodeGenerator::getTitle()
  198 {
  199     return docTitle;
  200 }
  201 
  202 void CodeGenerator::setEncoding(const string& encodingName)
  203 {
  204     encoding = encodingName;
  205 }
  206 
  207 void CodeGenerator::setStyleSheet(const std::string& path)
  208 {
  209     styleSheetPath=path;
  210 }
  211 
  212 void CodeGenerator::setPreformatting ( WrapMode lineWrappingStyle,
  213                                        unsigned int lineLength
  214                                      )
  215 {
  216 
  217     lineWrapLen = lineLength;
  218 }
  219 
  220 
  221 void CodeGenerator::setApplyDynStyles(bool flag) {
  222     applyDynStyles = flag;
  223 }
  224 
  225 void CodeGenerator::setSVGSize ( const string& w, const string& h )
  226 {
  227     width=w;
  228     height=h;
  229 }
  230 
  231 ParseError CodeGenerator::generateFile (const string &inFileName,
  232                                         const string &outFileName)
  233 {
  234 
  235     ParseError error=PARSE_OK;
  236 
  237     in = (inFileName.empty()? &cin :new ifstream (inFileName.c_str() , std::ios::binary));
  238 
  239 
  240     if (!in->fail() && error==PARSE_OK) {
  241         out = (outFileName.empty()? &cout :new ofstream (outFileName.c_str()));
  242         if ( out->fail()) {
  243             error=BAD_OUTPUT;
  244         }
  245     }
  246 
  247     if ( in->fail()) {
  248         error=BAD_INPUT;
  249     }
  250     if (error==PARSE_OK) {
  251         if (! fragmentOutput) {
  252             *out << getHeader();
  253         }
  254 
  255         printBody();
  256 
  257         if (! fragmentOutput) {
  258             *out << getFooter();
  259         }
  260     }
  261 
  262     if (!outFileName.empty()) {
  263         delete out;
  264         out=NULL;
  265     }
  266     if (!inFileName.empty()) {
  267         delete in;
  268         in=NULL;
  269     }
  270     return error;
  271 }
  272 
  273 string CodeGenerator::generateString(const string &input)
  274 {
  275     in = new istringstream (input);
  276     out = new ostringstream ();
  277 
  278     if ( in->fail() || out->fail()) {
  279         return "";
  280     }
  281 
  282     if (! fragmentOutput) {
  283         *out << getHeader();
  284     }
  285 
  286     printBody();
  287 
  288     if (! fragmentOutput) {
  289         *out << getFooter();
  290     }
  291 
  292     string result = static_cast<ostringstream*>(out)->str();
  293 
  294     delete out;
  295     out=NULL;
  296     delete in;
  297     in=NULL;
  298 
  299     return result;
  300 }
  301 
  302 string CodeGenerator::generateStringFromFile(const string &inFileName)
  303 {
  304 
  305     in = new ifstream (inFileName.c_str() , std::ios::binary);
  306     out = new ostringstream ();
  307 
  308     if ( in->fail() || out->fail()) {
  309         return "";
  310     }
  311 
  312     if (! fragmentOutput) {
  313         *out << getHeader();
  314     }
  315 
  316     printBody();
  317 
  318     if (! fragmentOutput) {
  319         *out << getFooter();
  320     }
  321 
  322     string result = static_cast<ostringstream*>(out)->str();
  323 
  324     delete out;
  325     out=NULL;
  326     delete in;
  327     in=NULL;
  328 
  329     return result;
  330 }
  331 
  332 ParseError CodeGenerator::generateFileFromString (const string &sourceStr,
  333         const string &outFileName,
  334         const string &title)
  335 {
  336     ParseError error=PARSE_OK;
  337 
  338     in = new istringstream (sourceStr);
  339     if (!in->fail()) {
  340         out = (outFileName.empty()? &cout :new ofstream (outFileName.c_str()));
  341         if ( out->fail()) {
  342             error=BAD_OUTPUT;
  343         }
  344     }
  345 
  346     if ( in->fail()) {
  347         error=BAD_INPUT;
  348     }
  349 
  350     if (error==PARSE_OK) {
  351 
  352         if (! fragmentOutput) {
  353             *out << getHeader();
  354         }
  355 
  356         printBody();
  357 
  358         if (! fragmentOutput) {
  359             *out << getFooter();
  360         }
  361     }
  362 
  363     if (!outFileName.empty()) {
  364         delete out;
  365         out=NULL;
  366     }
  367 
  368     delete in;
  369     in=NULL;
  370 
  371     return error;
  372 }
  373 
  374 
  375 bool CodeGenerator::printDynamicStyleFile ( const string &outPath )
  376 {
  377     return true;
  378 }
  379 
  380 /*
  381 
  382  ESC[nL       Inserts n blank lines at cursor line.   (NANSI)
  383  ESC[nM       Deletes n lines including cursor line.  (NANSI)
  384  ESC[n@       Inserts n blank chars at cursor.        (NANSI)
  385  ESC[nP       Deletes n chars including cursor char.  (NANSI)
  386  ESC[n;ny     Output char translate                   (NANSI)
  387  */
  388 
  389 bool CodeGenerator::parseSGRParameters(const string& line, size_t begin, size_t end)
  390 {
  391     if (line.empty() || begin==end) { // fix empty grep --color ending sequence
  392       elementStyle.setReset(true);
  393       return true;
  394     }
  395 
  396     int ansiCode=0;
  397     int colorCode=0;
  398     unsigned char colorValues[3]= {0};
  399 
  400     string codes=line.substr(begin, end-begin);
  401     vector<string> codeVector = StringTools::splitString(codes, ';');
  402 
  403     vector<string>::iterator itVectorData = codeVector.begin();
  404     while( itVectorData != codeVector.end()) {
  405         StringTools::str2num<int>(ansiCode, *(itVectorData), std::dec);
  406         elementStyle.setReset(false);
  407 
  408         switch (ansiCode) {
  409         case 0:
  410             elementStyle.setReset(true);
  411             break;
  412         case 1:
  413             elementStyle.setBold(true);
  414             elementStyle.setFgColour(rgb2html(workingPalette[8]));
  415             break;
  416         case 2: //Faint
  417             break;
  418 
  419         case 3:
  420             elementStyle.setItalic(true);
  421             break;
  422 
  423         case 5: //Blink
  424         case 6: //Blink fast
  425             elementStyle.setBlink(true);
  426             break;
  427 
  428         case 7:
  429             elementStyle.imageMode(true);
  430             break;
  431 
  432         case 8:
  433             elementStyle.setConceal(true);
  434             break;
  435 
  436         case 4:// Underline Single
  437         case 21: // Underline double
  438             elementStyle.setUnderline(true);
  439             break;
  440 
  441         case 22:
  442             elementStyle.setBold(false);
  443             break;
  444 
  445         case 24:
  446             elementStyle.setUnderline(false);
  447             break;
  448 
  449         case 25:
  450             elementStyle.setBlink(false);
  451             break;
  452 
  453         case 27:
  454             elementStyle.imageMode(false);
  455             break;
  456 
  457         case 28:
  458             elementStyle.setConceal(false);
  459             break;
  460 
  461         case 30:
  462         case 31:
  463         case 32:
  464         case 33:
  465         case 34:
  466         case 35:
  467         case 36:
  468         case 37:
  469             if (elementStyle.isBold()){
  470               elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-30+8]));
  471             } else
  472               elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-30]));
  473             break;
  474 
  475         case 38: // xterm 256 foreground color mode \033[38;5;<color>
  476 
  477             itVectorData++;
  478             if (itVectorData == codeVector.end()) break;
  479 
  480             if (*(itVectorData)=="5") {
  481                 itVectorData++;
  482                 if (itVectorData == codeVector.end()) break;
  483 
  484                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  485                 xterm2rgb((unsigned char)colorCode, colorValues);
  486                 elementStyle.setFgColour(rgb2html(colorValues));
  487             } else if (*(itVectorData)=="2") {
  488 
  489                 itVectorData++;
  490                 if (itVectorData == codeVector.end()) break;
  491                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  492                 colorValues[0] = colorCode & 0xff;
  493                 itVectorData++;
  494 
  495                 if (itVectorData == codeVector.end()) break;
  496                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  497                 colorValues[1] = colorCode & 0xff;
  498 
  499                 itVectorData++;
  500                 if (itVectorData == codeVector.end()) break;
  501                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  502                 colorValues[2] = colorCode & 0xff;
  503 
  504                 elementStyle.setFgColour(rgb2html(colorValues));
  505             }
  506             break;
  507 
  508         case 39:
  509           elementStyle.setReset(true);
  510             break;
  511 
  512         case 40:
  513         case 41:
  514         case 42:
  515         case 43:
  516         case 44:
  517         case 45:
  518         case 46:
  519         case 47:
  520             elementStyle.setBgColour(rgb2html(workingPalette[ansiCode-40]));
  521             break;
  522 
  523         case 48:  // xterm 256 background color mode \033[48;5;<color>
  524 
  525             itVectorData++;
  526             if (itVectorData == codeVector.end()) break;
  527 
  528             if(*(itVectorData)=="5") {
  529 
  530                 itVectorData++;
  531                 if (itVectorData == codeVector.end()) break;
  532 
  533                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  534                 xterm2rgb((unsigned char)colorCode, colorValues);
  535                 elementStyle.setBgColour(rgb2html(colorValues));
  536             } else if (*(itVectorData)=="2") {
  537 
  538                 itVectorData++;
  539                 if (itVectorData == codeVector.end()) break;
  540                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  541                 colorValues[0] = colorCode & 0xff;
  542                 itVectorData++;
  543 
  544                 if (itVectorData == codeVector.end()) break;
  545                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  546                 colorValues[1] = colorCode & 0xff;
  547 
  548                 itVectorData++;
  549                 if (itVectorData == codeVector.end()) break;
  550                 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
  551                 colorValues[2] = colorCode & 0xff;
  552 
  553                 elementStyle.setBgColour(rgb2html(colorValues));
  554             }
  555 
  556             break;
  557 
  558         case 49:
  559           elementStyle.setReset(true);
  560             break;
  561 
  562         /*aixterm codes*/
  563         case 90:
  564         case 91:
  565         case 92:
  566         case 93:
  567         case 94:
  568         case 95:
  569         case 96:
  570         case 97:
  571             elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-90+8]));
  572             break;
  573 
  574         case 100:
  575         case 101:
  576         case 102:
  577         case 103:
  578         case 104:
  579         case 105:
  580         case 106:
  581         case 107:
  582             elementStyle.setBgColour(rgb2html(workingPalette[ansiCode-100+8]));
  583             break;
  584         }
  585 
  586         // Set RTF color index
  587         // 8 default colours followed by bright colors see rtfgenerator.cpp
  588         if (ansiCode>=30 and ansiCode <=37)
  589           elementStyle.setFgColourID(ansiCode-30 + (elementStyle.isBold()? 8 : 0) );
  590         else if (ansiCode>=90 and ansiCode <98)
  591           elementStyle.setFgColourID(ansiCode-90+8);
  592 
  593         else if (ansiCode>=40 and ansiCode <=47)
  594           elementStyle.setBgColourID(ansiCode-40 /*+ elementStyle.isBold()? 8 : 0 */);
  595         else if (ansiCode>=100 and ansiCode <108)
  596           elementStyle.setBgColourID(ansiCode-100+8);
  597 
  598         if (itVectorData != codeVector.end()) itVectorData++;
  599     }
  600 
  601     return true;
  602 }
  603 
  604 
  605 void CodeGenerator::parseCodePage437Seq(string line, size_t begin, size_t end){
  606 
  607   string codes=line.substr(begin, end-begin);
  608   vector<string> codeVector = StringTools::splitString(codes, ',');
  609 
  610   if (line[end]=='H'){
  611     codeVector = StringTools::splitString(codes, ';');
  612 
  613     curX = curY = 0;
  614     if (codeVector.size()==1) {
  615       curY = atoi(codeVector[0].c_str());
  616     } else  if (codeVector.size()==2){
  617      curY = atoi(codeVector[0].c_str());
  618      curX = atoi(codeVector[1].c_str());
  619     }
  620 
  621     if (maxY<curY && curY<asciiArtHeight) maxY=curY;
  622   }
  623 
  624   if (line[end]=='A'){
  625     if (codeVector.size()==1){
  626       curY -= atoi(codeVector[0].c_str());
  627      } else {
  628       curY--;
  629     }
  630   }
  631 
  632   if (line[end]=='B'){
  633     if (codeVector.size()==1){
  634       curY += atoi(codeVector[0].c_str());
  635     } else {
  636       curY++;
  637     }
  638     if (maxY<curY && curY<asciiArtHeight) maxY=curY;
  639   }
  640 
  641   if (line[end]=='C'){
  642 
  643     if (codeVector.size()==1){
  644       curX += atoi(codeVector[0].c_str());
  645     } else {
  646       curX++;
  647     }
  648 
  649     //handle coloumn overflow
  650     if (curX>asciiArtWidth && curY<asciiArtHeight){
  651       curX-=asciiArtWidth;
  652       curY++;
  653       if (maxY<curY && curY<asciiArtHeight) maxY=curY;
  654     }
  655   }
  656 
  657   if (line[end]=='D'){
  658     if (codeVector.size()==1){
  659       curX -= atoi(codeVector[0].c_str());
  660      } else {
  661       curX--;
  662     }
  663     if (curX<0) curX=0;
  664   }
  665 
  666   if (line[end]=='J'){
  667  //   std::cerr<<"J!!!\n";
  668     /*
  669     if (codeVector.size()==1 && codeVector[0]=="2"){
  670       for (int i=0;i<100*100;i++) *termBuffer[i]->c =0;
  671     }
  672     */
  673   }
  674 
  675   if (line[end]=='K'){
  676   //  std::cerr<<"K!!!\n";
  677     //    for (int i=curX;i<asciiArtWidth;i++) termBuffer[curY*asciiArtWidth + i]->c =0;
  678 
  679   }
  680 
  681   if (line[end]=='s'){
  682     memX = curX;
  683     memY = curY;
  684     memStyle = elementStyle;
  685   }
  686   if (line[end]=='u'){
  687     curX = memX;
  688     curY = memY;
  689     elementStyle=memStyle;
  690   }
  691 
  692 }
  693 
  694 void CodeGenerator::insertLineNumber ()
  695 {
  696     if ( showLineNumbers && !parseCP437) {
  697 
  698         ostringstream lnum;
  699         lnum << setw ( 5 ) << right;
  700         if( numberCurrentLine ) {
  701             *out << getCloseTag();
  702             lnum << lineNumber;
  703             *out <<lnum.str()<<spacer;
  704             *out << getOpenTag();
  705         } else {
  706             *out << lnum.str(); //for indentation
  707         }
  708     }
  709 }
  710 
  711 void CodeGenerator::printTermBuffer() {
  712 
  713     for (int y=0;y<=maxY;y++) {
  714 
  715         for (int x=0;x<asciiArtWidth;x++) {
  716             if (termBuffer[x + y* asciiArtWidth].c=='\r') {
  717                 break;
  718             }
  719             elementStyle = termBuffer[x + y* asciiArtWidth].style;
  720 
  721             //full block
  722             if (termBuffer[x + y* asciiArtWidth].c == 0xdb){
  723                 elementStyle.setBgColour(elementStyle.getFgColour());
  724             }
  725 
  726             if (!elementStyle.isReset()) {
  727                 *out <<getOpenTag();
  728             }
  729 
  730             *out << maskCP437Character(termBuffer[x + y* asciiArtWidth].c);
  731 
  732             if (!elementStyle.isReset()) {
  733                 *out <<getCloseTag();
  734             }
  735         }
  736     *out<<newLineTag;
  737   }
  738   out->flush();
  739   delete [] termBuffer;
  740 }
  741 
  742 
  743 void CodeGenerator::parseBinFile(){
  744   char buffer [2] = {0};
  745   int cur=0;
  746   int next=0;
  747   int count=0;
  748   allocateTermBuffer();
  749   while (in->read (buffer, 2)){
  750 
  751     cur = buffer[0]&0xff;
  752     next = buffer[1]&0xff;
  753 
  754     int colBg = (next & 240) >> 4;
  755     int colFg = (next & 15);
  756 
  757     if (colBg > 8)
  758     {
  759       colBg -= 8;
  760     }
  761 
  762     elementStyle.setFgColour(rgb2html(workingPalette[colFg]));
  763     elementStyle.setBgColour(rgb2html(workingPalette[colBg]));
  764 
  765     //FIXME:
  766     elementStyle.setBold(cur >= 0x20 && cur <= 0x7a);
  767 
  768     if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
  769       termBuffer[curX + curY*asciiArtWidth].c = cur;
  770       termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
  771       curX++;
  772     }
  773     if (count % asciiArtWidth == 0 ) {
  774       curY++;
  775       if (maxY<curY && curY<asciiArtHeight) maxY=curY;
  776       curX=0;
  777     }
  778     count+=2;
  779   }
  780 }
  781 
  782 // the XBIN decoding function is based on AnsiLove:
  783 // https://github.com/ansilove/
  784 void CodeGenerator::parseXBinFile(){
  785 
  786     char header [11] = {0};
  787     char palette [48] = {0};
  788 
  789     if (in->read(header, 11)){
  790 
  791       asciiArtWidth = 0xff & ((header[ 6 ] << 8) + header[ 5 ]);
  792       asciiArtHeight = 0xff & ((header[ 8 ] << 8) + header[ 7 ]);
  793       int fontSize = header[ 9 ];
  794       int flags = header[ 10 ];
  795 /*
  796       std::cerr<<"XBIN width:"<<asciiArtWidth<<"\n";
  797       std::cerr<<"XBIN height:"<<asciiArtHeight<<"\n";
  798       std::cerr<<"XBIN fontsize:"<<fontSize<<"\n";
  799       std::cerr<<"XBIN flags:"<<flags<<"\n";
  800 */
  801       allocateTermBuffer();
  802 
  803       if( (flags & 1) == 1 && in->read(palette, 48)) {
  804         int loop;
  805         int index;
  806 
  807         //override default palette
  808         //TODO allow user to override this with a map?
  809         for (loop = 0; loop < 16; loop++)
  810         {
  811           index = loop * 3;
  812           workingPalette[loop][0] = palette[index] << 2 | palette[index] >> 4;
  813           workingPalette[loop][1] = palette[index+1] << 2 | palette[index+1] >> 4;
  814           workingPalette[loop][2] = palette[index+2] << 2 | palette[index+2] >> 4;
  815         }
  816       }
  817 
  818       // skip font
  819       if( (flags & 2) == 2 ) {
  820         int numchars = ( flags & 0x10 ? 512 : 256 );
  821         in->seekg ( fontSize * numchars, ios::cur);
  822       }
  823 
  824       // decode image
  825       if( (flags & 4) == 4) {
  826         int c=0;
  827         while( in && curY < asciiArtHeight)
  828         {
  829           c = in->get();
  830           int compression = c & 0xC0;
  831           int cnt = ( c & 0x3F ) + 1;
  832 
  833           int cur = -1;
  834           int attr = -1;
  835 
  836           while( cnt-- ) {
  837             // none
  838             if( compression == 0 ) {
  839               cur = in->get();
  840               attr = in->get();
  841             }
  842             // char
  843             else if ( compression == 0x40 ) {
  844               if( cur == -1 ) {
  845                 cur = in->get();
  846               }
  847               attr = in->get();
  848             }
  849             // attr
  850             else if ( compression == 0x80 ) {
  851               if( attr == -1 ) {
  852                 attr = in->get();
  853               }
  854               cur = in->get();
  855             }
  856             // both
  857             else {
  858               if( cur == -1 ) {
  859                 cur = in->get();
  860               }
  861               if( attr == -1 ) {
  862                 attr = in->get();
  863               }
  864             }
  865 
  866             int colBg = (attr & 240) >> 4;
  867             int colFg = (attr & 15);
  868 
  869             if (colBg > 8)
  870             {
  871               colBg -= 8;
  872             }
  873 
  874             elementStyle.setFgColour(rgb2html(workingPalette[colFg]));
  875             elementStyle.setBgColour(rgb2html(workingPalette[colBg]));
  876 
  877             //FIXME:
  878             elementStyle.setBold(cur >= 0x20 && cur <= 0x7a);
  879 
  880             if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
  881               termBuffer[curX + curY*asciiArtWidth].c = cur;
  882               termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
  883               curX++;
  884             }
  885 
  886             if (curX == asciiArtWidth)
  887             {
  888               curX = 0;
  889               curY++;
  890               if (maxY<curY && curY<asciiArtHeight) maxY=curY;
  891             }
  892           }
  893         }
  894     } else {
  895       //flat BIN
  896       parseBinFile();
  897     }
  898   }
  899 }
  900 
  901 // the TND decoding function is based on AnsiLove:
  902 // https://github.com/ansilove/
  903 void CodeGenerator::parseTundraFile(){
  904     char buffer  [10] = {0};
  905     bool isTundra = false;
  906     if ( in->read (buffer, 9) ) {
  907         isTundra = string(buffer)=="\x18TUNDRA24";
  908     }
  909 
  910     if (!isTundra) {
  911         std::cerr<<"not a Tundra file\n";
  912         return;
  913     }
  914 
  915     asciiArtWidth = 80;
  916 
  917     allocateTermBuffer();
  918 
  919     int fg_red=0, fg_green=0, fg_blue=0;
  920     int bg_red=0, bg_green=0, bg_blue=0;
  921     while (in->read (buffer, 1)){
  922 
  923         if (curX >= asciiArtWidth){
  924             curX = 0;
  925             curY ++;
  926         }
  927 
  928         int cur = buffer[0]&0xff;
  929 
  930         if (cur==1) {
  931             if (!in->read (buffer, 8)) goto GENTLE_EXIT;
  932 
  933             curY = ((buffer[0] << 24)& 0xff) + ((buffer[1] << 16)& 0xff) + ((buffer[2] << 8)& 0xff) + (buffer[3]& 0xff);
  934             curX = ((buffer[4] << 24)& 0xff) + ((buffer[5] << 16)& 0xff) + ((buffer[6] << 8)& 0xff) + (buffer[7]& 0xff);
  935         }
  936 
  937         if (cur == 2)
  938         {
  939             if (!in->read (buffer, 5) ) goto GENTLE_EXIT;
  940 
  941             fg_red=buffer[2];
  942             fg_green=buffer[3];
  943             fg_blue=buffer[4];
  944             cur= buffer[0];
  945         }
  946 
  947         if (cur == 4)
  948         {
  949             if (!in->read (buffer, 5) ) goto GENTLE_EXIT;
  950             bg_red=buffer[2];
  951             bg_green=buffer[3];
  952             bg_blue=buffer[4];
  953             cur= buffer[0];
  954         }
  955 
  956         if (cur==6)
  957         {
  958             if (!in->read (buffer, 10) ) goto GENTLE_EXIT;
  959 
  960             fg_red=buffer[2];
  961             fg_green=buffer[3];
  962             fg_blue=buffer[4];
  963 
  964             bg_red=buffer[6];
  965             bg_green=buffer[7];
  966             bg_blue=buffer[8];
  967 
  968             cur = buffer[0];
  969         }
  970 
  971         if (cur !=1 && cur !=2 && cur !=4 && cur !=6)
  972         {
  973             elementStyle.setFgColour(rgb2html( fg_red&0xff, fg_green&0xff, fg_blue&0xff));
  974             elementStyle.setBgColour(rgb2html( bg_red&0xff, bg_green&0xff, bg_blue&0xff ));
  975 
  976             termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
  977 
  978             termBuffer[curX + curY*asciiArtWidth].c  = cur &0xff;
  979             curX++;
  980         }
  981   }
  982 
  983 GENTLE_EXIT:
  984 
  985   maxY =  curY<asciiArtHeight ?  curY : asciiArtHeight;
  986 }
  987 
  988 void CodeGenerator::allocateTermBuffer(){
  989 
  990   if (termBuffer) delete [] termBuffer;
  991 
  992   termBuffer = new TDChar[asciiArtWidth*asciiArtHeight];
  993   for (int i=0; i<asciiArtWidth*asciiArtHeight; i++){
  994     termBuffer[i].c=0;
  995   }
  996 }
  997 
  998 bool CodeGenerator::streamIsXBIN() {
  999   if (in==&cin) return false;
 1000 
 1001   bool isXBIN = false;
 1002   char head[5] = {0};
 1003   if (in->read (head, sizeof head -1) ) {
 1004     isXBIN = string(head)=="XBIN";
 1005   }
 1006   in->clear();
 1007   in->seekg (0, ios::beg);
 1008   return isXBIN;
 1009 }
 1010 
 1011 
 1012 bool CodeGenerator::streamIsTundra() {
 1013   if (in==&cin) return false;
 1014 
 1015   bool isTND = false;
 1016   char head[10] = {0};
 1017 
 1018   if (in->read (head, sizeof head -1) ) {
 1019     isTND = string(head)=="\x18TUNDRA24";
 1020   }
 1021   in->clear();
 1022   in->seekg (0, ios::beg);
 1023   return isTND;
 1024 }
 1025 ////////////////////////////////////////////////////////////////////////////
 1026 
 1027 void CodeGenerator::processInput()
 1028 {
 1029   int cur=0;
 1030   int next=0;
 1031 
 1032   if (parseCP437 || parseAsciiBin || parseAsciiTundra){
 1033     elementStyle.setReset(false);
 1034   }
 1035 
 1036   // deal with BIN/XBIN without file watching, reformatting and line numbering distractions
 1037   if (parseAsciiBin){
 1038 
 1039     if (streamIsXBIN())
 1040       parseXBinFile();
 1041     else
 1042       parseBinFile();
 1043 
 1044     printTermBuffer();
 1045     return;
 1046   }
 1047 
 1048   if (parseAsciiTundra && streamIsTundra()){
 1049     parseTundraFile();
 1050 
 1051     printTermBuffer();
 1052     return;
 1053   }
 1054 
 1055 
 1056   // handle normal text files
 1057   if (readAfterEOF && in!=&cin) {
 1058     in->seekg (0, ios::end);
 1059     // output the last few lines or the complete file if not too big
 1060     if (in->tellg()>51200) {
 1061       in->seekg (-512, ios::end);
 1062       // output complete lines, ignore cur line fragment
 1063       in->ignore(512, '\n');
 1064     } else {
 1065       in->seekg (0, ios::beg); // output complete file
 1066     }
 1067   }
 1068 
 1069   if (streamIsXBIN()) {
 1070    *out<<"Please apply --art-bin option for XBIN files.\n";
 1071    return;
 1072   }
 1073 
 1074   if (streamIsTundra()) {
 1075    *out<<"Please apply --art-tundra option for TND files.\n";
 1076    return;
 1077   }
 1078 
 1079   string line;
 1080   size_t i=0;
 1081   size_t plainTxtCnt=0;
 1082   bool tagOpen=false;
 1083   bool isGrepOutput=false;
 1084   bool isKSeq=false;
 1085 
 1086   bool omitNewLine=false;
 1087   lineNumber=0;
 1088 
 1089   if (parseCP437){
 1090     allocateTermBuffer();
 1091   }
 1092 
 1093   while (true) {
 1094 
 1095     bool eof=false;
 1096 
 1097     eof=!getline(*in, line);
 1098 
 1099     if( !omitNewLine )
 1100         ++lineNumber;
 1101 
 1102     numberCurrentLine = true;
 1103 
 1104     if (eof) {
 1105       // imitate tail bahaviour, continue to read after EOF
 1106       if (readAfterEOF) {
 1107         out->flush();
 1108         in->clear();
 1109         #ifdef WIN32
 1110         Sleep(250);
 1111         #else
 1112         sleep(1);
 1113         #endif
 1114       } else {
 1115         if (!parseCP437 && !omitTrailingCR)
 1116             printNewLine(outputType!=TEXT);
 1117         break;
 1118       }
 1119     } else {
 1120 
 1121       if (!omitNewLine && !parseCP437 && lineNumber>1)
 1122           printNewLine();
 1123 
 1124       if (!omitNewLine )
 1125           insertLineNumber();
 1126 
 1127       omitNewLine = false;
 1128 
 1129       i=0;
 1130       plainTxtCnt=0;
 1131       size_t seqEnd=string::npos;
 1132 
 1133       while (i <line.length() ) {
 1134         // CSI ?
 1135         cur = line[i]&0xff;
 1136 
 1137         if (parseCP437){
 1138 
 1139           if (cur==0x1b && line.length() - i > 2){
 1140             next = line[i+1]&0xff;
 1141             if (next==0x5b) {
 1142               i+=2;
 1143               seqEnd = i;
 1144               //find sequence end
 1145               while (   seqEnd<line.length()
 1146                 && (line[seqEnd]<0x40 || line[seqEnd]>0x7e )) {
 1147                 ++seqEnd;
 1148                 }
 1149 
 1150                 if ( line[seqEnd]=='m' ) {
 1151                   parseSGRParameters(line, i, seqEnd);
 1152                 } else {
 1153                   parseCodePage437Seq(line, i, seqEnd);
 1154                 }
 1155                 i=seqEnd+1;
 1156             }
 1157             else {
 1158                 ++i;
 1159             }
 1160           } else  if (cur==0x1a && line.length() - i > 6){
 1161             // skip SAUCE info section
 1162             while (line[i]==0x1a || !line[i]) ++i;
 1163             if (line.substr(i, 5)=="SAUCE"){
 1164               break;
 1165             }
 1166           } else {
 1167             if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
 1168               termBuffer[curX + curY*asciiArtWidth].c = line[i];
 1169               termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
 1170               curX++;
 1171             }
 1172 
 1173             if (curX==asciiArtWidth || line[i]=='\r' ) {
 1174               curY++;
 1175               if (maxY<curY && curY<asciiArtHeight) maxY=curY;
 1176               curX=0;
 1177               if (line[i]=='\r') i=line.length();
 1178             }
 1179             ++i;
 1180           }
 1181         } else {
 1182 
 1183           if ( (cur&0xff)==0x0d && i<line.length()-1) {
 1184             plainTxtCnt-=i;
 1185 
 1186             lineBuf.seekp(showLineNumbers ? 0 : 0, ios::beg);
 1187             lineBuf<<getOpenTag();
 1188           }
 1189             // wrap line
 1190           if (lineWrapLen && plainTxtCnt && plainTxtCnt % lineWrapLen==0) {
 1191               ++lineNumber;
 1192               printNewLine();
 1193               insertLineNumber();
 1194               plainTxtCnt=0;
 1195            }
 1196 
 1197           if ( line.length() - i > 2 && (line[i+1]&0xff)==0x08) i++;
 1198           if ( cur==0x07) {
 1199               ++lineNumber;
 1200               printNewLine();
 1201               insertLineNumber();
 1202         }
 1203 
 1204           if ( cur==0x1b || (!ignCSISeq && ( cur==0x9b || cur==0xc2)) ) {
 1205 
 1206             if (line.length() - i > 2){
 1207               next = line[i+1]&0xff;
 1208 
 1209               //move index behind CSI
 1210               if ( (cur==0x1b && next==0x5b) || ( cur==0xc2 && next==0x9b) ) {
 1211                   ++i;
 1212               } else {
 1213                   // restore a unicode sequence if the two digit CSI is not matched
 1214                   // ansiweather -l Berlin,DE | ansifilter -T
 1215                   if (cur==0xc2 || cur==0x1b) {
 1216                       lineBuf << maskCharacter(cur);
 1217                       ++plainTxtCnt;
 1218                 }
 1219 
 1220               }
 1221 
 1222               // http://linuxcommand.org/lc3_adv_tput.php
 1223               // http://ascii-table.com/ansi-escape-sequences-vt-100.php
 1224               if (next==0x28){ // ( -> maybe need to handle more codes here
 1225                   if (line[i+2]==0x42) { // B
 1226                       elementStyle.setReset(false);
 1227                       i+=2;
 1228                 }
 1229               }
 1230 
 1231             // https://iterm2.com/documentation-escape-codes.html
 1232             if (next==0x5d) {
 1233 
 1234                 if (line[i+2]=='8') {
 1235 
 1236                     size_t uriBegin = line.find(';', i+4);
 1237                     seqEnd = line.find("\x1b]8;;\x07", i);
 1238                     size_t uriDelim = line.find(0x07, uriBegin+1);
 1239 
 1240                     if (uriBegin != string::npos && seqEnd != string::npos){
 1241                         string uri = line.substr(uriBegin+1, uriDelim - uriBegin - 1 );
 1242                         string txt = line.substr(uriDelim+1, seqEnd - uriDelim - 1);
 1243                         lineBuf << getHyperlink(uri, txt);
 1244                         i=seqEnd+4;
 1245                     }
 1246                 }
 1247                 ++i;
 1248             }
 1249 
 1250               if (i<line.size()) ++i;
 1251 
 1252               if (line[i-1]==0x5b || (line[i-1]&0xff)==0x9b){
 1253                 seqEnd=i;
 1254                 //find sequence end
 1255                 while (   seqEnd<line.length()
 1256                   && (line[seqEnd]<0x40 || line[seqEnd]>0x7e )) {
 1257                     ++seqEnd;
 1258                   }
 1259 
 1260                   if (   line[seqEnd]=='m' && !ignoreFormatting ) {
 1261                     if (!elementStyle.isReset()) {
 1262                       lineBuf << getCloseTag();
 1263                       tagOpen=false;
 1264                     }
 1265                     parseSGRParameters(line, i, seqEnd);
 1266                     if (!elementStyle.isReset()) {
 1267                       lineBuf << getOpenTag();
 1268                       tagOpen=true;
 1269                     }
 1270                   }
 1271 
 1272                   // fix K sequences (iterm2/grep)
 1273                   isKSeq =  line[seqEnd]=='K' && !ignClearSeq ;
 1274                   isGrepOutput = isKSeq && isascii(line[seqEnd+1]) && line[seqEnd+1] !=13 && line[seqEnd+1] != 27;
 1275 
 1276                   if (   line[seqEnd]=='s' || line[seqEnd]=='u'
 1277                     || (isKSeq && !isGrepOutput) ){
 1278                       i=line.length()+1;
 1279                       omitNewLine = isKSeq; // \n may follow K
 1280                       //FIXME std::cerr << "K CASE!\n";
 1281                   }
 1282                   else {
 1283                       i = 1 + ((seqEnd!=line.length())?seqEnd:i);
 1284                   }
 1285               } else {
 1286                 cur= line[i-1]&0xff;
 1287                 next = line[i]&0xff;
 1288 
 1289                 //ignore content of two and single byte sequences (no CSI)
 1290                 if (cur==0x1b && (  next==0x50 || next==0x5d || next==0x58
 1291                   || next==0x5e||next==0x5f ) ) // DECSC seq
 1292                 {
 1293                   seqEnd=i;
 1294                   //find string end
 1295                   while ( seqEnd<line.length()
 1296                       && (line[seqEnd]&0xff)!=0x9e
 1297                       && line[seqEnd]!=0x07
 1298                       && (line[seqEnd]&0xff)!=0x3b ) {
 1299                         ++seqEnd;
 1300                     }
 1301 
 1302                     if (line[seqEnd+1]=='A')
 1303                         seqEnd++;
 1304 
 1305                     i=seqEnd+1;
 1306                 } else if (cur==0x1b && (
 1307                    next==0x37 || next==0x38
 1308 
 1309                 ) ) // DECSC seq
 1310                 {
 1311                     if (line[seqEnd+1]==0x1b)
 1312                         ++i;
 1313                 }
 1314               }
 1315             } else {
 1316                 ++i;
 1317             }
 1318           } else if (!ignCSISeq && (cur==0x90 || cur==0x9d || cur==0x98 || cur==0x9e ||cur==0x9f)) {
 1319             seqEnd=i;
 1320             //find string end
 1321             while (   seqEnd<line.length() && (line[seqEnd]&0xff)!=0x9e
 1322               && line[seqEnd]!=0x07 ) {
 1323               ++seqEnd;
 1324               }
 1325               // handle false positives in unicode sequences
 1326               // TODO fix set terminal title CSI (testansi.py)
 1327               if (seqEnd<line.length() ) {
 1328                   i=seqEnd+1;
 1329               } else {
 1330                 lineBuf << maskCharacter(line[i]);
 1331                 ++i;
 1332                 ++plainTxtCnt;
 1333               }
 1334           } else {
 1335             // output printable character
 1336             lineBuf << maskCharacter(line[i]);
 1337             ++i;
 1338             ++plainTxtCnt;
 1339           }
 1340         }
 1341       }
 1342     }
 1343   } // while (true)
 1344 
 1345   if (tagOpen) {
 1346     *out <<getCloseTag();
 1347   }
 1348 
 1349   if (parseCP437){
 1350     printTermBuffer();
 1351   }
 1352   out->flush();
 1353 }
 1354 
 1355 
 1356 void CodeGenerator::printNewLine(bool eof) {
 1357 
 1358     string lineStr(lineBuf.str());
 1359     if (eof) {
 1360         lineStr = lineStr.substr(0, lineBuf.tellp());
 1361     }
 1362     *out << lineStr;
 1363     *out << newLineTag;
 1364 
 1365     lineBuf.clear();
 1366     lineBuf.str(std::string());
 1367 }
 1368 
 1369 
 1370 /* the following functions are based on Wolfgang Frischs xterm256 converter utility:
 1371    http://frexx.de/xterm-256-notes/
 1372 */
 1373 void CodeGenerator::xterm2rgb(unsigned char color, unsigned char* rgb)
 1374 {
 1375     // 16 basic colors
 1376     if(color<16) {
 1377         rgb[0] = workingPalette[color][0];
 1378         rgb[1] = workingPalette[color][1];
 1379         rgb[2] = workingPalette[color][2];
 1380     }
 1381 
 1382     // color cube color
 1383     if(color>=16 && color<=232) {
 1384         color-=16;
 1385         rgb[0] = valuerange[(color/36)%6];
 1386         rgb[1] = valuerange[(color/6)%6];
 1387         rgb[2] = valuerange[color%6];
 1388     }
 1389 
 1390     // gray tone
 1391     if(color>232) {
 1392         rgb[0]=rgb[1]=rgb[2] = 8+(color-232)*0x0a;
 1393     }
 1394 }
 1395 
 1396 string CodeGenerator::rgb2html(unsigned char* rgb){
 1397   char colorString[10]= {0};
 1398   sprintf(colorString, "#%02x%02x%02x", rgb[0], rgb[1], rgb[2]);
 1399   return string(colorString);
 1400 }
 1401 
 1402 string CodeGenerator::rgb2html(int r, int g, int b){
 1403   char colorString[10]= {0};
 1404   sprintf(colorString, "#%02x%02x%02x", r, g, b);
 1405   return string(colorString);
 1406 }
 1407 
 1408 const unsigned char CodeGenerator::valuerange[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };
 1409 
 1410 unsigned char CodeGenerator::defaultPalette[16][3] = {
 1411     { 0x00, 0x00, 0x00 }, // 0 ColorBlack
 1412     { 0xCD, 0x00, 0x00 }, // 1 ColorRed
 1413     { 0x00, 0xCD, 0x00 }, // 2 ColorGreen
 1414     { 0xCD, 0xCD, 0x00 }, // 3 ColorYellow
 1415     { 0x00, 0x00, 0xEE }, // 4 ColorBlue
 1416     { 0xCD, 0x00, 0xCD }, // 5 ColorMagenta
 1417     { 0x00, 0xCD, 0xCD }, // 6 ColorCyan
 1418     { 0xE5, 0xE5, 0xE5 }, // 7 ColorGray
 1419     { 0x7F, 0x7F, 0x7F }, // 8 ColorDarkGray
 1420     { 0xFF, 0x00, 0x00 }, // 9 ColorBrightRed
 1421     { 0x00, 0xFF, 0x00 }, // 10 ColorBrightGreen
 1422     { 0xFF, 0xFF, 0x00 }, // 11 ColorBrightYellow
 1423     { 0x5C, 0x5C, 0xFF }, // 12 ColorBrightBlue
 1424     { 0xFF, 0x00, 0xFF }, // 13 ColorBrightMagenta
 1425     { 0x00, 0xFF, 0xFF }, // 14 ColorBrightCyan
 1426     { 0xFF, 0xFF, 0xFF }  // 15 ColorBrightWhite
 1427 };
 1428 
 1429 unsigned char CodeGenerator::workingPalette[16][3] = {
 1430     { 0x00, 0x00, 0x00 }, // 0 ColorBlack
 1431     { 0xCD, 0x00, 0x00 }, // 1 ColorRed
 1432     { 0x00, 0xCD, 0x00 }, // 2 ColorGreen
 1433     { 0xCD, 0xCD, 0x00 }, // 3 ColorYellow
 1434     { 0x00, 0x00, 0xEE }, // 4 ColorBlue
 1435     { 0xCD, 0x00, 0xCD }, // 5 ColorMagenta
 1436     { 0x00, 0xCD, 0xCD }, // 6 ColorCyan
 1437     { 0xE5, 0xE5, 0xE5 }, // 7 ColorGray
 1438     { 0x7F, 0x7F, 0x7F }, // 8 ColorDarkGray
 1439     { 0xFF, 0x00, 0x00 }, // 9 ColorBrightRed
 1440     { 0x00, 0xFF, 0x00 }, // 10 ColorBrightGreen
 1441     { 0xFF, 0xFF, 0x00 }, // 11 ColorBrightYellow
 1442     { 0x5C, 0x5C, 0xFF }, // 12 ColorBrightBlue
 1443     { 0xFF, 0x00, 0xFF }, // 13 ColorBrightMagenta
 1444     { 0x00, 0xFF, 0xFF }, // 14 ColorBrightCyan
 1445     { 0xFF, 0xFF, 0xFF }  // 15 ColorBrightWhite
 1446 };
 1447 
 1448 bool CodeGenerator::setColorMap(const string& mapPath){
 1449 
 1450   //restore default colors
 1451   if (mapPath.length()==0){
 1452    memcpy(workingPalette, defaultPalette, sizeof defaultPalette);
 1453    return true;
 1454   }
 1455 
 1456   ifstream mapFile ( mapPath.c_str() );
 1457   if ( mapFile ) {
 1458 
 1459       string line;
 1460 
 1461       unsigned int idx=0;
 1462       char sep='\0';
 1463 
 1464       string colorCode;
 1465       while ( getline ( mapFile, line ) ) {
 1466         stringstream s(line);
 1467 
 1468         s>>idx;
 1469         if (idx>15) return false;
 1470 
 1471         s>>sep;
 1472         if (sep!='=') return false;
 1473 
 1474         s>>colorCode;
 1475         if (colorCode.size()>=7 && colorCode[0]=='#' ) {
 1476           workingPalette[idx][0] = (char)std::strtol(colorCode.substr ( 1, 2 ).c_str(), NULL, 16);
 1477           workingPalette[idx][1] = (char)std::strtol(colorCode.substr ( 3, 2 ).c_str(), NULL, 16);
 1478           workingPalette[idx][2] = (char)std::strtol(colorCode.substr ( 5, 2 ).c_str(), NULL, 16);
 1479         } else {
 1480           return false;
 1481         }
 1482       }
 1483       mapFile.close();
 1484   } else {
 1485       return false;
 1486   }
 1487   return true;
 1488 }
 1489 
 1490 }