📄 sgfparser.cpp
字号:
/** sgfparser.cpp*/#include "qgo.h"#include "sgfparser.h"#include "boardhandler.h"#include "board.h"#include "move.h"#include "tree.h"#include "stonehandler.h"#include "matrix.h"#include "globals.h"#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <qfile.h>#include <qtextstream.h>#include <qprogressdialog.h>#include "xmlparser.h"#include "parserdefs.h"#include <qmessagebox.h>#include <qbig5codec.h>#include <qeucjpcodec.h> #include <qjiscodec.h> #include <qsjiscodec.h>#include <qgbkcodec.h>#include <qeuckrcodec.h>#include <qtsciicodec.h>#include <qstrlist.h>#include <qregexp.h>#include <qptrstack.h>#define STR_OFFSET 2000/* #define DEBUG_CODEC */// Note: This class is a DIRTY UGLY HACK.// It probably does all the stuff books tell you never ever to do.// But it speeds up sgf reading of large files (Kogo) significantly.class MyString{public: MyString() { } MyString(const QString &src) { Str = src; strLength = src.length(); } virtual ~MyString() { } virtual const QChar at(uint i) const { return Str.at(i); } virtual uint next_nonspace (uint i) const { // ignore lower characters, too while (at(i).isSpace() || // == ' ' || at(i) == '\n' || at(i) == '\t' || at(i) >= 'a' && at(i) <= 'z') i++; return i; } virtual int find(const char *c, unsigned int index) const { if (index >= strLength) return -1; // Offset. Hope that is enough. TODO Long comments check? unsigned int l = index+STR_OFFSET<strLength ? index+STR_OFFSET : strLength, cl = strlen(c), i, found; do { found = i = 0; do { if (Str.at(index+i) != c[i]) break; found ++; if (found == cl) return index; } while (i++ < cl); } while (index++ < l); if (index == l) return -1; return index; } virtual int find(char c, unsigned int index) const { if (index >= strLength) return -1; // Offset. Hope that is enough. TODO Long comments check? unsigned int l = index+STR_OFFSET<strLength ? index+STR_OFFSET : strLength; while (Str.at(index) != c && index++ < l); if (index == l) return -1; return index; } virtual unsigned int length() const { return strLength; } unsigned int strLength; private: QString Str;};class MySimpleString : public MyString{public: MySimpleString(const QString &src) { Str = src.latin1(); strLength = src.length(); } virtual ~MySimpleString() { } const QChar at(uint i) const { return Str[i]; } int find(const char *c, unsigned int index) const { if (index >= strLength) return -1; // Offset. Hope that is enough. TODO Long comments check? unsigned int l = index+STR_OFFSET<strLength ? index+STR_OFFSET : strLength, cl = strlen(c), i, found; do { found = i = 0; do { if (Str[index+i] != c[i]) break; found ++; if (found == cl) return index; } while (i++ < cl); } while (index++ < l); if (index == l) return -1; return index; } int find(char c, unsigned int index) const { if (index >= strLength) return -1; // Offset. Hope that is enough. TODO Long comments check? unsigned int l = index+STR_OFFSET<strLength ? index+STR_OFFSET : strLength; while (Str[index] != c && index++ < l); if (index == l) return -1; return index; } private: const char* Str; };// End dirty ugly hack. :*)SGFParser::SGFParser(BoardHandler *bh): boardHandler(bh){ CHECK_PTR(boardHandler); stream = NULL; xmlParser = NULL;}SGFParser::~SGFParser(){ delete xmlParser;}bool SGFParser::parse(const QString &fileName, const QString &filter, bool fastLoad){ if (fileName.isNull() || fileName.isEmpty()) { qWarning("No filename given!"); return false; } CHECK_PTR(boardHandler); QString toParse = loadFile(fileName); if (toParse.isNull() || toParse.isEmpty()) return false; // Convert old sgf/mgt format into new// if (toParse.find("White[") != -1) // Do a quick test if this is necassary.// convertOldSgf(toParse); // Check for filter, if given if (!filter.isNull()) { // XML if (filter == Board::tr("XML")) { // Init xmlparser if not yet done if (xmlParser == NULL) xmlParser = new XMLParser(boardHandler); xmlParser->parse(fileName); return true; } } if (!initGame(toParse, fileName)) return corruptSgf(); return doParse(toParse, fastLoad);}// Called from clipboard SGF importbool SGFParser::parseString(const QString &toParse){ if (toParse.isNull() || toParse.isEmpty() || !initGame(toParse, NULL)) return corruptSgf(); return doParse(toParse);}QString SGFParser::loadFile(const QString &fileName){ qDebug("Trying to load file <%s>", fileName.latin1()); QFile file(fileName); if (!file.exists()) { QMessageBox::warning(0, PACKAGE, Board::tr("Could not find file:") + " " + fileName); return NULL; } if (!file.open(IO_ReadOnly)) { QMessageBox::warning(0, PACKAGE, Board::tr("Could not open file:") + " " + fileName); return NULL; } QTextStream txt(&file); if (!initStream(&txt)) { QMessageBox::critical(0, PACKAGE, Board::tr("Invalid text encoding given. Please check preferences!")); return NULL; } QString toParse; // Read file in string toParse while (!txt.eof()) toParse.append(txt.readLine() + "\n"); file.close();#ifdef DEBUG_CODEC QMessageBox::information(0, "READING", toParse);#endif return toParse;}bool SGFParser::initStream(QTextStream *stream){ QTextCodec *codec = NULL; stream->setEncoding(QTextStream::Locale);#ifdef DEBUG_CODEC QMessageBox::information(0, "LOCALE", QTextCodec::locale());#endif switch (static_cast<Codec>(setting->readIntEntry("CODEC"))) { case codecNone:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "NONE");#endif break; case codecBig5:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "Big5");#endif#ifndef Q_WS_WIN codec = new QBig5Codec();#endif break; case codecEucJP:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "EUC JP");#endif codec = new QEucJpCodec(); break; case codecJIS:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "JIS");#endif codec = new QJisCodec(); break; case codecSJIS:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "Shift JIS");#endif codec = new QSjisCodec(); break; case codecEucKr:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "EUC KR");#endif codec = new QEucKrCodec(); break; case codecGBK:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "GBK");#endif codec = new QGbkCodec(); break; case codecTscii:#ifdef DEBUG_CODEC QMessageBox::information(0, "CODEC", "TSCII");#endif codec = new QTsciiCodec(); break; default: return false; } if (codec != NULL && stream != NULL) stream->setCodec(codec); return true;}bool SGFParser::doParse(const QString &toParseStr, bool fastLoad){ if (toParseStr.isNull() || toParseStr.isEmpty()) { qWarning("Failed loading from file. Is it empty?"); return false; } const MyString *toParse = NULL; if (static_cast<Codec>(setting->readIntEntry("CODEC")) == codecNone) toParse = new MySimpleString(toParseStr); else toParse = new MyString(toParseStr); CHECK_PTR(toParse); int pos = 0, posVarBegin = 0, posVarEnd = 0, posNode = 0, moves = 0, i, x=-1, y=-1; unsigned int pointer = 0, strLength = toParse->length(); bool black = true, setup = false, old_label = false, new_node = false; isRoot = true; bool remember_root; QString unknownProperty; State state; MarkType markType; QString moveStr, commentStr; Position *position; MoveNum *moveNum; QPtrStack<Move> stack; QPtrStack<MoveNum> movesStack; QPtrStack<Position> toRemove; stack.setAutoDelete(FALSE); movesStack.setAutoDelete(TRUE); toRemove.setAutoDelete(TRUE); Tree *tree = boardHandler->getTree(); state = stateVarBegin; bool cancel = false; int progressCounter = 0; QProgressDialog progress(Board::tr("Reading sgf file..."), Board::tr("Abort"), strLength, boardHandler->board, "progress", true); // qDebug("File length = %d", strLength); progress.setProgress(0); QString sss=""; do { if (!(++progressCounter%10)) { progress.setProgress(pointer); if (progress.wasCancelled()) { cancel = true; break; } } // qDebug("POINTER = %d: %c", pointer, toParse->Str[pointer]); posVarBegin = toParse->find('(', pointer); posVarEnd = toParse->find(')', pointer); posNode = toParse->find(';', pointer); pos = minPos(posVarBegin, posVarEnd, posNode); // qDebug("VarBegin %d, VarEnd %d, Move %d, MINPOS %d", posVarBegin, posVarEnd, posNode, pos); // qDebug("State before switch = %d", state); // Switch states // Node -> VarEnd if (state == stateNode && pos == posVarEnd) state = stateVarEnd; // Node -> VarBegin if (state == stateNode && pos == posVarBegin) state = stateVarBegin; // VarBegin -> Node else if (state == stateVarBegin && pos == posNode) state = stateNode; // VarEnd -> VarBegin else if (state == stateVarEnd && pos == posVarBegin) state = stateVarBegin; // qDebug("State after switch = %d", state); // Do the work switch (state) { case stateVarBegin: if (pos != posVarBegin) { delete toParse; return corruptSgf(pos); } // qDebug("Var BEGIN at %d, moves = %d", pos, moves); stack.push(tree->getCurrent()); moveNum = new MoveNum; moveNum->n = moves; movesStack.push(moveNum); pointer = pos + 1; break; case stateVarEnd: if (pos != posVarEnd) { delete toParse; return corruptSgf(pos); } // qDebug("VAR END"); if (!movesStack.isEmpty() && !stack.isEmpty()) { Move *m = stack.pop(); CHECK_PTR(m); x = movesStack.pop()->n; // qDebug("Var END at %d, moves = %d, moves from stack = %d", pos, moves, x); for (i=moves; i > x; i--) { position = toRemove.pop(); if (position == NULL) continue; boardHandler->getStoneHandler()->removeStone(position->x, position->y); // qDebug("Removing %d %d from stoneHandler.", position->x, position->y); } moves = x; if (!fastLoad) boardHandler->getStoneHandler()->updateAll(m->getMatrix(), false); tree->setCurrent(m); } pointer = pos + 1; break; case stateNode: if (pos != posNode) { delete toParse; return corruptSgf(pos); } // qDebug("Node at %d", pos); commentStr = QString(); setup = false; markType = markNone; // Create empty node remember_root = isRoot; if (!isRoot) { boardHandler->createMoveSGF(); unknownProperty = QString();if (tree->getCurrent()->getTimeinfo()) qWarning("*** Timeinfo set !!!!"); //tree->getCurrent()->setTimeinfo(false); } else isRoot = false; new_node = true; Property prop; pos ++; do { uint tmppos=0; pos = toParse->next_nonspace (pos); // qDebug("READING PROPERTY AT %d: %c", pos, toParse->at(pos)); // if (toParse->find("B[", pos) == pos) if (toParse->at(pos) == 'B' && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = moveBlack; pos = tmppos; black = true; } // else if (toParse->find("W[", pos) == pos) else if (toParse->at(pos) == 'W' && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = moveWhite; pos = tmppos; black = false; } else if (toParse->at(pos) == 'N' && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = nodeName; pos = tmppos; } else if (toParse->find("AB", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editBlack; pos = tmppos; setup = true; black = true; } else if (toParse->find("AW", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editWhite; pos = tmppos; setup = true; black = false; } else if (toParse->find("AE", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editErase; pos = tmppos; setup = true; } else if (toParse->find("TR", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markTriangle; pos = tmppos; } else if (toParse->find("CR", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markCircle; pos = tmppos; } else if (toParse->find("SQ", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markSquare; pos = tmppos; } else if (toParse->find("MA", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markCross; pos = tmppos; } // old definition else if (toParse->find("M", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = editMark; markType = markCross; pos = tmppos; } else if (toParse->find("LB", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markText; pos = tmppos; old_label = false; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -