bool SGFParser::parse(const QString &fileName, const QString &/*filter*/) { 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; /* // 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); }
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); }
// Return false: corrupt sgf, true: sgf okay. result = 0 when property not found bool SGFParser::parseProperty(const QString &toParse, const QString &prop, QString &result) { int pos, strLength=toParse.length(); result = ""; pos = toParse.indexOf(prop+"["); if (pos == -1) return true; pos += 2; if (toParse[pos] != '[') return corruptSgf(pos); while (toParse[++pos] != ']' && pos < strLength) result.append(toParse[pos]); if (pos > strLength) return corruptSgf(pos); return true; }
bool SGFParser::doParse(const QString &toParseStr) { if (toParseStr.isNull() || toParseStr.isEmpty()) { qWarning("Failed loading from file. Is it empty?"); return false; } QString tmp; if(!loadedfromfile) { /* This bit of ugliness is because sgfs are used to duplicate boards as well * as load from file FIXME */ parseProperty(toParseStr, "CA", tmp); //codec if (!tmp.isEmpty()) readCodec = QTextCodec::codecForName(tmp.toLatin1().constData()); } const MyString *toParse = NULL; //////TODO if (static_cast<Codec>(setting->readIntEntry("CODEC")) == codecNone) ///////// toParse = new MySimpleString(toParseStr); /////// else toParse = new MyString(toParseStr); Q_CHECK_PTR(toParse); int pos = 0, posVarBegin = 0, posVarEnd = 0, posNode = 0, moves = 0, i, x=-1, y=-1; int a_offset = QChar::fromLatin1('a').unicode() - 1 ; unsigned int pointer = 0, strLength = toParse->length(); bool black = true, setup = false, old_label = false; isRoot = true; bool remember_root; QString unknownProperty; State state; MarkType markType; QString moveStr, commentStr; Position *position; MoveNum *moveNum; QStack<Move*> stack; QStack<MoveNum*> movesStack; /* FIXME toRemove, et., al., appears unused Remove it */ QStack<Position*> toRemove; /* ////TODO stack.setAutoDelete(false); movesStack.setAutoDelete(true); toRemove.setAutoDelete(true); */ // Initialises the tree with board size parseProperty(toParseStr, "SZ", tmp); // Tree *tree = new Tree(tmp.isEmpty() ? 19 : tmp.toInt()) ;// boardHandler->getTree(); state = stateVarBegin; bool cancel = false; //FIXME abort does nothing!! // qDebug("File length = %d", strLength); tree->setLoadingSGF(true); QString sss=""; do { posVarBegin = toParse->find('(', pointer); posVarEnd = toParse->find(')', pointer); posNode = toParse->find(';', pointer); pos = minPos(posVarBegin, posVarEnd, posNode); // 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(); Q_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); // tree->removeStone(position->x, position->y); // qDebug("Removing %d %d from stoneHandler.", position->x, position->y); } moves = x; 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(); // qDebug("############### Before creating move ####################"); // qDebug(toParse->Str.toLatin1().constData()); //tree->createMoveSGF(); /* This does happen, why??? FIXME */ // qDebug("############### ####################"); // qDebug(toParse->Str.toLatin1().constData()); // qDebug("############### After creating move ####################"); unknownProperty = QString(); #ifdef FIXME //why is this a warning? this happens on loading a file with time info if (tree->getCurrent()->getTimeinfo()) qWarning("*** Timeinfo set !!!!"); #endif //FIXME //tree->getCurrent()->setTimeinfo(false); } else isRoot = false; Property prop; pos ++; do { uint tmppos=0; pos = toParse->next_nonspace (pos); if ((tmppos = toParse->isProperty("B",pos))) { prop = moveBlack; pos = tmppos; black = true; } else if ((tmppos = toParse->isProperty("W",pos))) { prop = moveWhite; pos = tmppos; black = false; } else if ((tmppos = toParse->isProperty("N",pos))) { prop = nodeName; pos = tmppos; } else if ((tmppos = toParse->isProperty("AB",pos))) { prop = editBlack; pos = tmppos; setup = true; black = true; } else if ((tmppos = toParse->isProperty("AW",pos))) { prop = editWhite; pos = tmppos; setup = true; black = false; } else if ((tmppos = toParse->isProperty("AE",pos))) { prop = editErase; pos = tmppos; setup = true; } else if ((tmppos = toParse->isProperty("TR",pos))) { prop = editMark; markType = markTriangle; pos = tmppos; } else if ((tmppos = toParse->isProperty("CR",pos))) { prop = editMark; markType = markCircle; pos = tmppos; } else if ((tmppos = toParse->isProperty("SQ",pos))) { prop = editMark; markType = markSquare; pos = tmppos; } else if ((tmppos = toParse->isProperty("MA",pos))) { prop = editMark; markType = markCross; pos = tmppos; } // old definition else if ((tmppos = toParse->isProperty("M",pos))) { prop = editMark; markType = markCross; pos = tmppos; } else if ((tmppos = toParse->isProperty("LB",pos))) { prop = editMark; markType = markText; pos = tmppos; old_label = false; } // Added old L property. This is not SGF4, but many files contain this tag. else if ((tmppos = toParse->isProperty("L",pos))) { prop = editMark; markType = markText; pos = tmppos; old_label = true; } else if ((tmppos = toParse->isProperty("C",pos))) { prop = comment; pos = tmppos; } else if ((tmppos = toParse->isProperty("TB",pos))) { prop = editMark; markType = markTerrBlack; pos = tmppos; black = true; } else if ((tmppos = toParse->isProperty("TW",pos))) { prop = editMark; markType = markTerrWhite; pos = tmppos; black = false; } else if ((tmppos = toParse->isProperty("BL",pos))) { prop = timeLeft; pos = tmppos; black = true; } else if ((tmppos = toParse->isProperty("WL",pos))) { prop = timeLeft; pos = tmppos; black = false; } else if ((tmppos = toParse->isProperty("OB",pos))) { prop = openMoves; pos = tmppos; black = true; } else if ((tmppos = toParse->isProperty("OW",pos))) { prop = openMoves; pos = tmppos; black = false; } else if ((tmppos = toParse->isProperty("PL",pos))) { prop = nextMove; pos = tmppos; } else if ((tmppos = toParse->isProperty("RG",pos))) { prop = unknownProp; pos = tmppos; setup = true; } // Empty node else if (toParse->at(pos) == ';' || toParse->at(pos) == '(' || toParse->at(pos) == ')') { qDebug("Found empty node at %d", pos); while (toParse->at(pos).isSpace()) pos++; continue; } else { // handle like comment prop = unknownProp; pos = toParse->next_nonspace (pos); //qDebug("SGF: next nonspace (1st):" + QString(toParse->at(pos)) + QString(toParse->at(pos+1)) + QString(toParse->at(pos+2))); } //qDebug("Start do loop : FOUND PROP %d, pos at %d now", prop, pos); //qDebug(toParse->getStr()); //causes crash // Next is one or more '[xx]'. // Only one in a move property, several in a setup propery do { if (toParse->at(pos) != '[' && prop != unknownProp) { delete toParse; return corruptSgf(pos); } // Empty type if (toParse->at(pos+1) == ']') { // CGoban stores pass as 'B[]' or 'W[]' if (prop == moveBlack || prop == moveWhite) { tree->doPass(true); // Remember this move for later, to remove from the matrix. position = new Position; position->x = x; position->y = y; toRemove.push(position); moves ++; } pos += 2; continue; } switch (prop) { case moveBlack: case moveWhite: // rare case: root contains move or placed stone: if (remember_root) { qDebug("root contains stone -> node created"); /* Something is screwy here, inconsistencies * in the way SGF's are treated. Like the below: * the whole point of "remember_root", FIXME*/ tree->addEmptyMove(); isRoot = false; unknownProperty = QString(); #ifdef FIXME //why is this a warning? if (tree->getCurrent()->getTimeinfo()) qWarning("*** Timeinfo set (2)!!!!"); #endif //FIXME //tree->getCurrent()->setTimeinfo(false); } case editBlack: case editWhite: case editErase: { x = toParse->at(pos+1).unicode() - a_offset ;// - 'a';// + 1; y = toParse->at(pos+2).unicode() - a_offset ; //- 'a' + 1; int x1, y1; bool compressed_list; // check for compressed lists if (toParse->at(pos+3) == ':') { x1 = toParse->at(pos+4).unicode() -a_offset;// - 'a' + 1; y1 = toParse->at(pos+5).unicode() -a_offset;// - 'a' + 1; compressed_list = true; } else { x1 = x; y1 = y; compressed_list = false; } /* * TODO Do we nned this when the tree is created from file ? * boardHandler->setModeSGF(setup || compressed_list ? modeEdit : modeNormal); */ int i, j; for (i = x; i <= x1; i++) for (j = y; j <= y1; j++) { if (prop == editErase) { tree->addStoneToCurrentMove(stoneErase, i, j); } else { if(setup) { if ((!remember_root) && (stack.top() == tree->getCurrent())) tree->addEmptyMove(); //if this is first in branch we need to add an empty move tree->addStoneToCurrentMove(black ? stoneBlack : stoneWhite, i, j); } else { Move *result = tree->getCurrent()->makeMove(black ? stoneBlack : stoneWhite, i, j); if (result) tree->setCurrent(result); } } // tree->getCurrent()->getMatrix()->debug(); //qDebug("ADDING MOVE %s %d/%d", black?"B":"W", x, y); // Remember this move for later, to remove from the matrix. position = new Position; position->x = i; position->y = j; toRemove.push(position); moves ++; } if (compressed_list) // Advance pos by 7 pos += 7; else // Advance pos by 4 pos += 4; break; } case nodeName: { commentStr = QString(); bool skip = false; while (toParse->at(++pos) != ']') { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Nodename string ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Nodename string ended immediately"); } // white spaces if (toParse->at(pos) == '\\') { while (toParse->at(pos+1).isSpace() && static_cast<unsigned int>(pos) < strLength-2) pos++; if (toParse->at(pos).isSpace()) pos++; // case: "../<cr><lf>]" if (toParse->at(pos) == ']') { pos--; skip = true; } } // escaped chars: '\', ']', ':' if (!(toParse->at(pos) == '\\' && (toParse->at(pos+1) == ']' || toParse->at(pos+1) == '\\' || toParse->at(pos+1) == ':')) && !skip && // no formatting !(toParse->at(pos) == '\n') && !(toParse->at(pos) == '\r')) commentStr.append(toParse->at(pos)); } //qDebug("Node name read: %s", commentStr.toLatin1().constData()); if (!commentStr.isEmpty()) // add comment; skip 'C[]' tree->getCurrent()->setNodeName(commentStr); pos++; break; } case comment: { commentStr = QString(); bool skip = false; while (toParse->at(++pos) != ']' || (toParse->at(pos-1) == '\\' && toParse->at(pos) == ']')) { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Comment string ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Comment string ended immediately"); } // white spaces if (toParse->at(pos) == '\\') { while (toParse->at(pos+1).isSpace() && static_cast<unsigned int>(pos) < strLength-2) pos++; if (toParse->at(pos).isSpace()) pos++; // case: "../<cr><lf>]" if (toParse->at(pos) == ']') { pos--; skip = true; } } // escaped chars: '\', ']', ':' if (!(toParse->at(pos) == '\\' && (toParse->at(pos+1) == ']' || toParse->at(pos+1) == '\\' || toParse->at(pos+1) == ':')) && !skip) commentStr.append(toParse->at(pos)); } //qDebug("Comment read: %s", commentStr.toLatin1().constData()); if (!commentStr.isEmpty()) { // add comment; skip 'C[]' if(readCodec) tree->getCurrent()->setComment(readCodec->toUnicode(commentStr.toLatin1().constData())); else tree->getCurrent()->setComment(commentStr.toLatin1().constData()); } pos ++; break; } case unknownProp: { // skip if property is known anyway bool skip = false; // save correct property name (or p.n. + '[') commentStr = QString(toParse->at(pos)); commentStr += toParse->at(tmppos = toParse->next_nonspace (pos + 1)); pos = tmppos; // check if it's really necessary to hold properties // maybe they are handled at another position if (commentStr == "WR" || commentStr == "BR" || commentStr == "PW" || commentStr == "PB" || commentStr == "SZ" || commentStr == "KM" || commentStr == "HA" || commentStr == "RE" || commentStr == "DT" || commentStr == "PC" || commentStr == "CP" || commentStr == "GN" || commentStr == "OT" || commentStr == "TM" || // now: general options commentStr == "GM" || commentStr == "ST" || commentStr == "AP" || commentStr == "FF") { skip = true; } sss= toParse->at(pos); while (toParse->at(++pos) != ']' || (toParse->at(pos-1) == '\\' && toParse->at(pos) == ']')) { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Unknown property ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Unknown property ended immediately"); } sss= toParse->at(pos); if (!skip) commentStr.append(toParse->at(pos)); } if (!skip) commentStr.append("]"); // qDebug("Comment read: %s", commentStr.latin1()); if ((!commentStr.isEmpty()) && (!skip)) { // cumulate unknown properties; skip empty property 'XZ[]' unknownProperty += commentStr; tree->getCurrent()->setUnknownProperty(unknownProperty); } pos ++; sss= toParse->at(pos); break; } case editMark: // set moveStr for increment labels of old 'L[]' property moveStr = "A"; while (toParse->at(pos) == '[' && static_cast<unsigned int>(pos) < strLength) { x = toParse->at(pos+1).unicode() -a_offset;// - 'a' + 1; y = toParse->at(pos+2).unicode() -a_offset;// - 'a' + 1; // qDebug("MARK: %d at %d/%d", markType, x, y); pos += 3; // 'LB' property? Then we need to get the text if (markType == markText && !old_label) { if (toParse->at(pos) != ':') { delete toParse; return corruptSgf(pos); } moveStr = ""; while (toParse->at(++pos) != ']' && static_cast<unsigned int>(pos) < strLength) moveStr.append(toParse->at(pos)); // qDebug("LB TEXT = %s", moveStr.latin1()); // It might me a number mark? bool check = false; moveStr.toInt(&check); // Try to convert to Integer // treat integers as characters... check = false; if (check) tree->getCurrent()->getMatrix()-> insertMark(x, y, markNumber); // Worked, its a number else tree->getCurrent()->getMatrix()-> insertMark(x, y, markType); // Nope, its a letter tree->getCurrent()->getMatrix()-> setMarkText(x, y, moveStr); /*else //fastload { if (check) // Number tree->getCurrent()->insertFastLoadMark(x, y, markNumber); else // Text tree->getCurrent()->insertFastLoadMark(x, y, markType, moveStr); }*/ } else { int x1, y1; bool compressed_list; // check for compressed lists if (toParse->at(pos) == ':') { x1 = toParse->at(pos+1).unicode() -a_offset;// - 'a' + 1; y1 = toParse->at(pos+2).unicode() -a_offset;// - 'a' + 1; compressed_list = true; } else { x1 = x; y1 = y; compressed_list = false; } int i, j; for (i = x; i <= x1; i++) for (j = y; j <= y1; j++) { tree->getCurrent()->getMatrix()->insertMark(i, j, markType); //else //fastload // tree->getCurrent()->insertFastLoadMark(i, j, markType); // auto increment for old property 'L' if (old_label) { tree->getCurrent()->getMatrix()-> setMarkText(x, y, moveStr); QChar c1 = moveStr[0]; if (c1 == 'Z') moveStr = QString("a"); else moveStr = c1.unicode() + 1; } } // new_node = false; if (compressed_list) // Advance pos by 3 pos += 3; if((markType == markTerrWhite || markType == markTerrBlack) && !tree->getCurrent()->isTerritoryMarked()) tree->getCurrent()->setTerritoryMarked(); } //old_label = false; pos ++; while (toParse->at(pos).isSpace()) pos++; } break; case openMoves: { QString tmp_mv; while (toParse->at(++pos) != ']') tmp_mv += toParse->at(pos); tree->getCurrent()->setOpenMoves(tmp_mv.toInt()); pos++; if (!tree->getCurrent()->getTimeinfo()) { tree->getCurrent()->setTimeinfo(true); tree->getCurrent()->setTimeLeft(0); } break; } case timeLeft: { QString tmp_mv; while (toParse->at(++pos) != ']') tmp_mv += toParse->at(pos); tree->getCurrent()->setTimeLeft(tmp_mv.toFloat()); pos++; if (!tree->getCurrent()->getTimeinfo()) { tree->getCurrent()->setTimeinfo(true); tree->getCurrent()->setOpenMoves(0); } break; } case nextMove: if (toParse->at(++pos) == 'W') tree->getCurrent()->setPLinfo(stoneWhite); else if (toParse->at(pos) == 'B') tree->getCurrent()->setPLinfo(stoneBlack); pos += 2; break; default: break; } while (toParse->at(pos).isSpace()) pos++; sss= toParse->at(pos); } while (setup && toParse->at(pos) == '['); // tree->getCurrent()->getMatrix()->debug(); // qDebug("end do loop"); // qDebug(toParse->getStr()); while (toParse->at(pos).isSpace()) pos++; } while (toParse->at(pos) != ';' && toParse->at(pos) != '(' && toParse->at(pos) != ')' && static_cast<unsigned int>(pos) < strLength); // Advance pointer pointer = pos; break; default: delete toParse; return corruptSgf(pointer); } } while (pointer < strLength && pos >= 0); tree->setLoadingSGF(false); delete toParse; return !cancel; }
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; } // Added old L property. This is not SGF4, but many files contain this tag. else if (toParse->at(pos) == 'L' && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = editMark; markType = markText; pos = tmppos; old_label = true; } else if (toParse->at(pos) == 'C' && toParse->at(tmppos = toParse->next_nonspace (pos + 1)) == '[') { prop = comment; pos = tmppos; } else if (toParse->find("TB", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markTerrBlack; pos = tmppos; black = true; } else if (toParse->find("TW", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = editMark; markType = markTerrWhite; pos = tmppos; black = false; } else if (toParse->find("BL", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = timeLeft; pos = tmppos; black = true; } else if (toParse->find("WL", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = timeLeft; pos = tmppos; black = false; } else if (toParse->find("OB", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = openMoves; pos = tmppos; black = true; } else if (toParse->find("OW", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = openMoves; pos = tmppos; black = false; } else if (toParse->find("PL", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = nextMove; pos = tmppos; } else if (toParse->find("RG", pos) == pos && toParse->at(tmppos = toParse->next_nonspace (pos + 2)) == '[') { prop = unknownProp; pos = tmppos; setup = true; } // Empty node else if (toParse->at(pos) == ';' || toParse->at(pos) == '(' || toParse->at(pos) == ')') { qDebug("Found empty node at %d", pos); while (toParse->at(pos).isSpace()) pos++; continue; } else { // handle like comment prop = unknownProp; pos = toParse->next_nonspace (pos); //qDebug("SGF: next nonspace (1st):" + QString(toParse->at(pos)) + QString(toParse->at(pos+1)) + QString(toParse->at(pos+2))); } // qDebug("FOUND PROP %d, pos at %d now", prop, pos); // Next is one or more '[xx]'. // Only one in a move property, several in a setup propery do { if (toParse->at(pos) != '[' && prop != unknownProp) { delete toParse; return corruptSgf(pos); } // Empty type if (toParse->at(pos+1) == ']') { // CGoban stores pass as 'B[]' or 'W[]' if (prop == moveBlack || prop == moveWhite) { boardHandler->doPass(true); // Remember this move for later, to remove from the matrix. position = new Position; position->x = x; position->y = y; toRemove.push(position); moves ++; } pos += 2; continue; } switch (prop) { case moveBlack: case moveWhite: // rare case: root contains move or placed stone: if (remember_root) { qDebug("root contains stone -> node created"); boardHandler->createMoveSGF(); unknownProperty = QString(); isRoot = false; if (tree->getCurrent()->getTimeinfo()) qWarning("*** Timeinfo set !!!!"); //tree->getCurrent()->setTimeinfo(false); } case editBlack: case editWhite: case editErase: { x = toParse->at(pos+1) - 'a' + 1; y = toParse->at(pos+2) - 'a' + 1; int x1, y1; bool compressed_list; // check for compressed lists if (toParse->at(pos+3) == ':') { x1 = toParse->at(pos+4) - 'a' + 1; y1 = toParse->at(pos+5) - 'a' + 1; compressed_list = true; } else { x1 = x; y1 = y; compressed_list = false; } boardHandler->setModeSGF(setup || compressed_list ? modeEdit : modeNormal); int i, j; for (i = x; i <= x1; i++) for (j = y; j <= y1; j++) { if (i == 20 && j == 20) boardHandler->doPass(true); else if (prop == editErase) { if (!fastLoad) boardHandler->removeStone(i, j, true, false); else { tree->getCurrent()->setX(0); tree->getCurrent()->setY(0); tree->getCurrent()->setColor(stoneNone); } } else { if (!fastLoad) boardHandler->addStoneSGF(black ? stoneBlack : stoneWhite, i, j, new_node); else { tree->getCurrent()->setX(i); tree->getCurrent()->setY(j); tree->getCurrent()->setColor(black? stoneBlack : stoneWhite); } } // tree->getCurrent()->getMatrix()->debug(); // qDebug("ADDING MOVE %s %d/%d", black?"B":"W", x, y); // Remember this move for later, to remove from the matrix. position = new Position; position->x = i; position->y = j; toRemove.push(position); moves ++; } new_node = false; if (compressed_list) // Advance pos by 7 pos += 7; else // Advance pos by 4 pos += 4; break; } case nodeName: { commentStr = QString(); bool skip = false; while (toParse->at(++pos) != ']') { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Nodename string ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Nodename string ended immediately"); } // white spaces if (toParse->at(pos) == '\\') { while (toParse->at(pos+1).isSpace() && static_cast<unsigned int>(pos) < strLength-2) pos++; if (toParse->at(pos).isSpace()) pos++; // case: "../<cr><lf>]" if (toParse->at(pos) == ']') { pos--; skip = true; } } // escaped chars: '\', ']', ':' if (!(toParse->at(pos) == '\\' && (toParse->at(pos+1) == ']' || toParse->at(pos+1) == '\\' || toParse->at(pos+1) == ':')) && !skip && // no formatting !(toParse->at(pos) == '\n') && !(toParse->at(pos) == '\r')) commentStr.append(toParse->at(pos)); } // qDebug("Comment read: %s", commentStr.latin1()); if (commentStr) // add comment; skip 'C[]' tree->getCurrent()->setNodeName(commentStr); pos++; break; } case comment: { commentStr = QString(); bool skip = false; while (toParse->at(++pos) != ']' || (toParse->at(pos-1) == '\\' && toParse->at(pos) == ']')) { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Comment string ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Comment string ended immediately"); } // white spaces if (toParse->at(pos) == '\\') { while (toParse->at(pos+1).isSpace() && static_cast<unsigned int>(pos) < strLength-2) pos++; if (toParse->at(pos).isSpace()) pos++; // case: "../<cr><lf>]" if (toParse->at(pos) == ']') { pos--; skip = true; } } // escaped chars: '\', ']', ':' if (!(toParse->at(pos) == '\\' && (toParse->at(pos+1) == ']' || toParse->at(pos+1) == '\\' || toParse->at(pos+1) == ':')) && !skip) commentStr.append(toParse->at(pos)); } // qDebug("Comment read: %s", commentStr.latin1()); if (commentStr) { // add comment; skip 'C[]' tree->getCurrent()->setComment(commentStr); } pos ++; break; } case unknownProp: { // skip if property is known anyway bool skip = false; // save correct property name (or p.n. + '[') commentStr = toParse->at(pos); commentStr += toParse->at(tmppos = toParse->next_nonspace (pos + 1)); pos = tmppos; // check if it's really necessary to hold properties // maybe they are handled at another position if (commentStr == "WR" || commentStr == "BR" || commentStr == "PW" || commentStr == "PB" || commentStr == "SZ" || commentStr == "KM" || commentStr == "HA" || commentStr == "RE" || commentStr == "DT" || commentStr == "PC" || commentStr == "CP" || commentStr == "GN" || commentStr == "OT" || commentStr == "TM" || // now: general options commentStr == "GM" || commentStr == "ST" || commentStr == "AP" || commentStr == "FF") { skip = true; } sss= toParse->at(pos); while (toParse->at(++pos) != ']' || (toParse->at(pos-1) == '\\' && toParse->at(pos) == ']')) { if (static_cast<unsigned int>(pos) > strLength-1) { qDebug("SGF: Unknown property ended immediately"); delete toParse; return corruptSgf(pos, "SGF: Unknown property ended immediately"); } sss= toParse->at(pos); if (!skip) commentStr.append(toParse->at(pos)); } if (!skip) commentStr.append("]"); // qDebug("Comment read: %s", commentStr.latin1()); if (commentStr && !skip) { // cumulate unknown properties; skip empty property 'XZ[]' unknownProperty += commentStr; tree->getCurrent()->setUnknownProperty(unknownProperty); } pos ++; sss= toParse->at(pos); break; } case editMark: // set moveStr for increment labels of old 'L[]' property moveStr = "A"; while (toParse->at(pos) == '[' && static_cast<unsigned int>(pos) < strLength) { x = toParse->at(pos+1) - 'a' + 1; y = toParse->at(pos+2) - 'a' + 1; // qDebug("MARK: %d at %d/%d", markType, x, y); pos += 3; // 'LB' property? Then we need to get the text if (markType == markText && !old_label) { if (toParse->at(pos) != ':') { delete toParse; return corruptSgf(pos); } moveStr = ""; while (toParse->at(++pos) != ']' && static_cast<unsigned int>(pos) < strLength) moveStr.append(toParse->at(pos)); // qDebug("LB TEXT = %s", moveStr.latin1()); // It might me a number mark? bool check = false; moveStr.toInt(&check); // Try to convert to Integer // treat integers as characters... check = false; if (!fastLoad) { if (check) tree->getCurrent()->getMatrix()-> insertMark(x, y, markNumber); // Worked, its a number else tree->getCurrent()->getMatrix()-> insertMark(x, y, markType); // Nope, its a letter tree->getCurrent()->getMatrix()-> setMarkText(x, y, moveStr); } else { if (check) // Number tree->getCurrent()->insertFastLoadMark(x, y, markNumber); else // Text tree->getCurrent()->insertFastLoadMark(x, y, markType, moveStr); } } else { int x1, y1; bool compressed_list; // check for compressed lists if (toParse->at(pos) == ':') { x1 = toParse->at(pos+1) - 'a' + 1; y1 = toParse->at(pos+2) - 'a' + 1; compressed_list = true; } else { x1 = x; y1 = y; compressed_list = false; } // boardHandler->setModeSGF(setup || compressed_list ? modeEdit : modeNormal); int i, j; for (i = x; i <= x1; i++) for (j = y; j <= y1; j++) { if (!fastLoad) tree->getCurrent()->getMatrix()->insertMark(i, j, markType); else tree->getCurrent()->insertFastLoadMark(i, j, markType); // auto increment for old property 'L' if (old_label) { tree->getCurrent()->getMatrix()-> setMarkText(x, y, moveStr); QChar c1 = moveStr[0]; if (c1 == 'Z') moveStr = QString("a"); else moveStr = c1.unicode() + 1; } } // new_node = false; if (compressed_list) // Advance pos by 3 pos += 3; } //old_label = false; pos ++; while (toParse->at(pos).isSpace()) pos++; } break; case openMoves: { QString tmp_mv; while (toParse->at(++pos) != ']') tmp_mv += toParse->at(pos); tree->getCurrent()->setOpenMoves(tmp_mv.toInt()); pos++; if (!tree->getCurrent()->getTimeinfo()) { tree->getCurrent()->setTimeinfo(true); tree->getCurrent()->setTimeLeft(0); } break; } case timeLeft: { QString tmp_mv; while (toParse->at(++pos) != ']') tmp_mv += toParse->at(pos); tree->getCurrent()->setTimeLeft(tmp_mv.toFloat()); pos++; if (!tree->getCurrent()->getTimeinfo()) { tree->getCurrent()->setTimeinfo(true); tree->getCurrent()->setOpenMoves(0); } break; } case nextMove: if (toParse->at(++pos) == 'W') tree->getCurrent()->setPLinfo(stoneWhite); else if (toParse->at(pos) == 'B') tree->getCurrent()->setPLinfo(stoneBlack); pos += 2; break; default: break; } while (toParse->at(pos).isSpace()) pos++; sss= toParse->at(pos); } while (setup && toParse->at(pos) == '['); //tree->getCurrent()->getMatrix()->debug(); while (toParse->at(pos).isSpace()) pos++; } while (toParse->at(pos) != ';' && toParse->at(pos) != '(' && toParse->at(pos) != ')' && static_cast<unsigned int>(pos) < strLength); // Advance pointer pointer = pos; break; default: delete toParse; return corruptSgf(pointer); } } while (pointer < strLength && pos >= 0); progress.setProgress(strLength); delete toParse; return !cancel; }
// Called from clipboard SGF import bool SGFParser::parseString(const QString &toParse) { if (toParse.isNull() || toParse.isEmpty() || !initGame(toParse, NULL)) return corruptSgf(); return doParse(toParse); }