A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2238 lines 54 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com> 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21 22#include "sgf.h" 23#include "sgf_storage.h" 24#include "types.h" 25#include "goban.h" 26#include "board.h" 27#include "display.h" 28#include "game.h" 29#include "util.h" 30 31int sgf_fd = -1; 32int unhandled_fd = -1; 33 34int tree_head = -1; 35int current_node = -1; 36int start_node = -1; 37 38bool header_marked = false; 39 40static int add_child_variation (int *variation_number); 41 42static int get_move_from_node (int handle); 43 44static bool is_important_node (int handle); 45static bool goto_next_important_node (bool forward); 46static bool retreat_node (void); 47static bool advance_node (void); 48 49static bool do_add_stones (void); 50static void setup_handicap_helper (unsigned short pos); 51 52static int undo_node_helper (void); 53 54static void set_one_mark (unsigned short pos, enum prop_type_t type); 55static void set_label_mark (unsigned short pos, char to_set); 56 57bool 58play_move_sgf (unsigned short pos, unsigned char color) 59{ 60 int handle; 61 int prop_handle; 62 int temp; 63 int temp2; 64 union prop_data_t temp_data; 65 int saved = current_node; 66 67 if ((color != BLACK && color != WHITE) || 68 (!on_board (pos) && pos != PASS_POS)) 69 { 70 return false; 71 } 72 73 74 /* go to the node before the next important node (move/add 75 stone/variation) this is the right place to look for children, add 76 variations, whatever. (if there is no next, we're already at the 77 right place) */ 78 79 if (get_node (current_node)->next >= 0) 80 { 81 current_node = get_node (current_node)->next; 82 83 /* true means forward */ 84 if (goto_next_important_node (true)) 85 { 86 current_node = get_node (current_node)->prev; 87 } 88 } 89 90 91 if ((temp = get_matching_child_sgf (pos, color)) >= 0) 92 { 93 /* don't have to do anything to set up temp as the right variation 94 number */ 95 96 } 97 else 98 { 99 /* now either there were no children, or none matched the one we 100 want so we have to add a new one */ 101 102 /* first test if it's legal. we don't do this above because SGF 103 files are allowed to have illegal moves in them, and it seems 104 to make sense to allow traversing those variations without 105 making the user change to a different play_mode */ 106 107 bool suicide_allowed = false; 108 109 if (rb->strcmp (header.ruleset, "NZ") == 0 || 110 rb->strcmp (header.ruleset, "GOE") == 0) 111 { 112 suicide_allowed = true; 113 } 114 115 if (play_mode != MODE_FORCE_PLAY && 116 !legal_move_board (pos, color, suicide_allowed)) 117 { 118 return false; 119 } 120 121 handle = add_child_sgf (NULL); 122 123 if (handle < 0) 124 { 125 current_node = saved; 126 return false; 127 } 128 129 union prop_data_t temp_prop_data; 130 temp_prop_data.position = pos; 131 132 prop_handle = add_prop_sgf (handle, 133 color == BLACK ? PROP_BLACK_MOVE : 134 PROP_WHITE_MOVE, temp_prop_data); 135 136 if (prop_handle < 0) 137 { 138 /* TODO: add code to completely remove the child which we 139 added, and then uncomment the following line. probably 140 doens't matter much since we're out of memory, but 141 whatever 142 free_storage_sgf(handle); */ 143 rb->splash (2 * HZ, 144 "Out of memory led to invalid state. Please exit."); 145 current_node = saved; 146 return false; 147 } 148 149 set_game_modified(); 150 151 temp = get_matching_child_sgf (pos, color); 152 } 153 154 /* now, one way or another temp has been set to the child variation 155 number that we should follow, so all we need to do is "choose" it 156 and redo_node_sgf */ 157 158 current_node = get_node (current_node)->next; 159 temp_data.number = temp; 160 161 temp2 = add_or_set_prop_sgf (current_node, 162 PROP_VARIATION_CHOICE, temp_data); 163 /* free up a superfluous prop */ 164 if (temp == 0) 165 { 166 delete_prop_handle_sgf (current_node, temp2); 167 } 168 169 current_node = saved; 170 return redo_node_sgf (); 171} 172 173bool 174add_mark_sgf (unsigned short pos, enum prop_type_t type) 175{ 176 union prop_data_t temp_data; 177 int temp_handle; 178 enum prop_type_t original_type; 179 180 if (!on_board (pos) || current_node < 0) 181 { 182 return false; 183 } 184 185 if (type == PROP_CIRCLE || 186 type == PROP_SQUARE || 187 type == PROP_TRIANGLE || 188 type == PROP_MARK || type == PROP_DIM || type == PROP_SELECTED) 189 { 190 temp_data.position = pos; 191 192 if ((temp_handle = get_prop_pos_sgf (type, temp_data)) >= 0) 193 { 194 original_type = get_prop (temp_handle)->type; 195 delete_prop_handle_sgf (current_node, temp_handle); 196 197 if (type == original_type) 198 { 199 set_one_mark (pos, PROP_INVALID); 200 return true; 201 } 202 } 203 204 add_prop_sgf (current_node, type, temp_data); 205 set_one_mark (pos, type); 206 207 return true; 208 } 209 else if (type == PROP_LABEL) 210 { 211#define MIN_LABEL 'a' 212#define MAX_LABEL 'f' 213 int temp_prop_handle = get_node (current_node)->props; 214 215 while (temp_prop_handle >= 0) 216 { 217 struct prop_t *temp_prop = get_prop (temp_prop_handle); 218 219 if (temp_prop->type == PROP_LABEL && 220 temp_prop->data.position == pos) 221 { 222 char to_set = temp_prop->data.label_extra; 223 ++to_set; 224 if (to_set > MAX_LABEL) 225 { 226 delete_prop_handle_sgf (current_node, temp_prop_handle); 227 set_one_mark (pos, PROP_INVALID); 228 return true; 229 } 230 231 temp_prop->data.label_extra = to_set; 232 set_label_mark (pos, to_set); 233 return true; 234 } 235 236 temp_prop_handle = temp_prop->next; 237 } 238 239 temp_data.label_extra = MIN_LABEL; 240 temp_data.position = pos; 241 242 add_prop_sgf (current_node, type, temp_data); 243 set_label_mark (pos, MIN_LABEL); 244 return true; 245 } 246 else 247 { 248 return false; 249 } 250} 251 252bool 253add_stone_sgf (unsigned short pos, unsigned char color) 254{ 255 int handle; 256 int prop_handle; 257 int saved = current_node; 258 union prop_data_t temp_data; 259 enum prop_type_t temp_type; 260 int var_number; 261 int temp; 262 263 if (!on_board (pos)) 264 { 265 return false; 266 } 267 268 if (color == get_point_board (pos)) 269 { 270 return false; 271 } 272 273 if ((!is_important_node (current_node) || 274 (current_node == start_node && get_move_sgf () < 0)) || 275 (get_prop_sgf (current_node, PROP_ADD_BLACK, NULL) >= 0 || 276 get_prop_sgf (current_node, PROP_ADD_WHITE, NULL) >= 0 || 277 get_prop_sgf (current_node, PROP_ADD_EMPTY, NULL) >= 0) || 278 get_node (current_node)->props < 0) 279 { 280 281 if (color == BLACK) 282 { 283 temp_type = PROP_ADD_BLACK; 284 } 285 else if (color == WHITE) 286 { 287 temp_type = PROP_ADD_WHITE; 288 } 289 else 290 { 291 temp_type = PROP_ADD_EMPTY; 292 } 293 294 temp_data.position = pos; 295 296 handle = get_prop_pos_sgf (temp_type, temp_data); 297 298 /* we have to always delete the old one and conditionally create a 299 new one (instead of trying to reuse the old one by changing 300 the type of it) because if we don't, our invariant with 301 respect to like-properties being grouped together in the 302 property list can easily be violated */ 303 if (handle >= 0) 304 { 305 temp_data.stone_extra = get_prop (handle)->data.stone_extra; 306 delete_prop_handle_sgf (current_node, handle); 307 } 308 else 309 { 310 temp_data.stone_extra = 0; 311 if (get_point_board (pos) == EMPTY) 312 { 313 temp_data.stone_extra |= FLAG_ORIG_EMPTY; 314 } 315 else if (get_point_board (pos) == BLACK) 316 { 317 temp_data.stone_extra |= FLAG_ORIG_BLACK; 318 } 319 /* else do nothing */ 320 } 321 322 /* now we've saved the information about what the board was 323 originally like, we can do the actual set */ 324 325 set_point_board (pos, color); 326 327 /* test if what we currently did just returned the board back to 328 its original for this position. if so, we DON'T create a new 329 PROP_ADD_*, because it's not needed (we already deleted the old 330 one, so in that case we just return) */ 331 if (((temp_data.stone_extra & FLAG_ORIG_EMPTY) && color == EMPTY) || 332 (!(temp_data.stone_extra & FLAG_ORIG_EMPTY) && 333 (((temp_data.stone_extra & FLAG_ORIG_BLACK) && color == BLACK) || 334 (!(temp_data.stone_extra & FLAG_ORIG_BLACK) && color == WHITE)))) 335 { 336 /* do nothing, set back to original */ 337 } 338 else 339 { 340 /* we're not set back to original, so add a prop for it */ 341 add_prop_sgf (current_node, temp_type, temp_data); 342 } 343 344 set_game_modified(); 345 346 return true; 347 } 348 else 349 { 350 /* we have to make a child variation and add stones in it */ 351 352 /* go to the node before the next important node (move/add 353 stone/variation) this is the right place to look for children, 354 add variations, whatever. (if there is no next, we're already 355 at the right place) */ 356 357 if (get_node (current_node)->next >= 0) 358 { 359 current_node = get_node (current_node)->next; 360 361 /* true means forward */ 362 if (goto_next_important_node (true)) 363 { 364 current_node = get_node (current_node)->prev; 365 } 366 } 367 368 handle = add_child_sgf (&var_number); 369 370 if (handle < 0) 371 { 372 rb->splash (2 * HZ, "Out of memory!"); 373 return false; 374 } 375 376 temp_data.position = pos; 377 378 if (color == BLACK) 379 { 380 temp_type = PROP_ADD_BLACK; 381 } 382 else if (color == WHITE) 383 { 384 temp_type = PROP_ADD_WHITE; 385 } 386 else 387 { 388 temp_type = PROP_ADD_EMPTY; 389 } 390 391 prop_handle = add_prop_sgf (handle, temp_type, temp_data); 392 393 if (prop_handle < 0) 394 { 395 /* TODO: add code to completely remove the child which we 396 added, and then uncomment the following line. probably 397 doens't matter much since we're out of memory, but 398 whatever 399 free_storage_sgf(handle); */ 400 rb->splash (2 * HZ, "Out of memory!"); 401 return false; 402 } 403 404 set_game_modified(); 405 406 /* now, "choose" the variation that we just added */ 407 408 current_node = get_node (current_node)->next; 409 temp_data.number = var_number; 410 411 temp = add_or_set_prop_sgf (current_node, 412 PROP_VARIATION_CHOICE, temp_data); 413 414 /* free up a superfluous prop */ 415 if (var_number == 0) 416 { 417 delete_prop_handle_sgf (current_node, temp); 418 } 419 420 current_node = saved; 421 422 /* and follow to our choice, returning since we already did the 423 work */ 424 return redo_node_sgf (); 425 } 426 427 return false; 428} 429 430bool 431undo_node_sgf (void) 432{ 433 int result = undo_node_helper (); 434 435 /* if we undid a ko threat, we need to figure out what the ko_pos is 436 there's no simple way to do this except to undo one /more/ move, 437 and then redo back to this location. (we could store it, but this 438 isn't that bad) Note: this doesn't need to recurse because we don't 439 care what previous move's ko positions were (since the tree is 440 already set in stone basically, it wouldn't change anything). */ 441 if (result == 1) 442 { 443 int backward_move_num = move_num - 1; 444 int saved_current = current_node; 445 446 while (move_num > backward_move_num) 447 { 448 result = undo_node_helper (); 449 450 if (result < 0) 451 { 452 DEBUGF 453 ("couldn't undo to previous move in ko threat handling!\n"); 454 return false; 455 } 456 } 457 458 /* now we're backed up to the previous move before our destination 459 so, let's go forward again until we get to the node we were at 460 */ 461 462 while (current_node != saved_current) 463 { 464 if (!redo_node_sgf ()) 465 { 466 DEBUGF 467 ("redoing to correct node failed on ko threat handling!\n"); 468 return false; 469 } 470 } 471 } 472 else if (result < 0) 473 { 474 DEBUGF ("initial undo failed!\n"); 475 return false; 476 } 477 478 set_all_marks_sgf (); 479 480 /* if there is a move in this node, move the screen so that it is 481 visible */ 482 int handle = get_move_sgf (); 483 if (handle >= 0) 484 { 485 move_display (get_prop (handle)->data.position); 486 } 487 488 return true; 489} 490 491static int 492undo_node_helper (void) 493{ 494 bool ko_threat_move = false; 495 496 if (current_node == start_node) 497 { 498 /* refuse to undo the initial SGF node, which is tree_head if 499 handicap == 0 or 1. If handicap >= 2, start_node is the node 500 with the handicap crap and added moves on it. don't let the 501 user undo past that */ 502 DEBUGF ("not undoing start_node\n"); 503 return -1; 504 } 505 506 struct prop_t *temp_move = get_prop (get_move_sgf ()); 507 508 if (temp_move) 509 { 510 int undone_caps = 0; 511 int undone_suicides = 0; 512 unsigned short move_pos = temp_move->data.position; 513 unsigned char move_color = temp_move->type == PROP_BLACK_MOVE ? BLACK : 514 WHITE; 515 516 unsigned short flags = temp_move->data.stone_extra; 517 518 if (move_pos != PASS_POS) 519 { 520 521 if (flags & FLAG_N_CAP) 522 { 523 undone_caps += flood_fill_board (NORTH (move_pos), 524 OTHER (move_color)); 525 } 526 if (flags & FLAG_S_CAP) 527 { 528 undone_caps += flood_fill_board (SOUTH (move_pos), 529 OTHER (move_color)); 530 } 531 if (flags & FLAG_E_CAP) 532 { 533 undone_caps += flood_fill_board (EAST (move_pos), 534 OTHER (move_color)); 535 } 536 if (flags & FLAG_W_CAP) 537 { 538 undone_caps += flood_fill_board (WEST (move_pos), 539 OTHER (move_color)); 540 } 541 542 if (flags & FLAG_SELF_CAP) 543 { 544 undone_suicides += flood_fill_board (move_pos, move_color); 545 } 546 547 if (flags & FLAG_ORIG_EMPTY) 548 { 549 set_point_board (move_pos, EMPTY); 550 } 551 else if (flags & FLAG_ORIG_BLACK) 552 { 553 set_point_board (move_pos, BLACK); 554 } 555 else 556 { 557 set_point_board (move_pos, WHITE); 558 } 559 } 560 561 if (move_color == BLACK) 562 { 563 black_captures -= undone_caps; 564 white_captures -= undone_suicides; 565 } 566 else 567 { 568 white_captures -= undone_caps; 569 black_captures -= undone_suicides; 570 } 571 572 if (flags & FLAG_KO_THREAT) 573 { 574 ko_threat_move = true; 575 } 576 577 --move_num; 578 current_player = OTHER (current_player); 579 } 580 else 581 { 582 /* test for added stones! */ 583 struct prop_t *temp_prop; 584 585 temp_prop = get_prop (get_node (current_node)->props); 586 587 while (temp_prop) 588 { 589 if ((temp_prop->type == PROP_ADD_BLACK || 590 temp_prop->type == PROP_ADD_WHITE || 591 temp_prop->type == PROP_ADD_EMPTY) && 592 on_board (temp_prop->data.position)) 593 { 594 if (temp_prop->data.stone_extra & FLAG_ORIG_EMPTY) 595 { 596 set_point_board (temp_prop->data.position, EMPTY); 597 } 598 else if (temp_prop->data.stone_extra & FLAG_ORIG_BLACK) 599 { 600 set_point_board (temp_prop->data.position, BLACK); 601 } 602 else 603 { 604 set_point_board (temp_prop->data.position, WHITE); 605 } 606 } 607 608 temp_prop = get_prop (temp_prop->next); 609 } 610 } 611 612 if (!retreat_node ()) 613 { 614 return -1; 615 } 616 617 if (ko_threat_move) 618 { 619 return 1; 620 } 621 622 return 0; 623} 624 625bool 626redo_node_sgf (void) 627{ 628 if (!advance_node ()) 629 { 630 return false; 631 } 632 633 set_all_marks_sgf (); 634 635 int temp_move = get_move_sgf (); 636 if (temp_move >= 0) 637 { 638 struct prop_t *move_prop = get_prop (temp_move); 639 unsigned short pos = move_prop->data.position; 640 unsigned char color = 641 move_prop->type == PROP_BLACK_MOVE ? BLACK : WHITE; 642 643 if (color != current_player) 644 { 645 DEBUGF ("redo_node_sgf: wrong color!\n"); 646 } 647 648 /* zero out the undo information and set the ko threat flag to the 649 correct value */ 650 651 move_prop->data.stone_extra = 0; 652 653 if (ko_pos != INVALID_POS) 654 { 655 move_prop->data.stone_extra |= FLAG_KO_THREAT; 656 } 657 658 ko_pos = INVALID_POS; 659 660 if (pos == PASS_POS) 661 { 662 rb->splashf (HZ / 2, "%s Passes", 663 color == BLACK ? "Black" : "White"); 664 } 665 else 666 { 667 int n_cap, s_cap, e_cap, w_cap, self_cap; 668 669 n_cap = s_cap = e_cap = w_cap = self_cap = 0; 670 671 if (get_point_board (pos) == EMPTY) 672 { 673 move_prop->data.stone_extra |= FLAG_ORIG_EMPTY; 674 } 675 else if (get_point_board (pos) == BLACK) 676 { 677 move_prop->data.stone_extra |= FLAG_ORIG_BLACK; 678 } 679 /* else do nothing */ 680 681 set_point_board (pos, color); 682 683 /* do captures on the 4 cardinal directions, if the opponent 684 stones are breathless */ 685 if (get_point_board (NORTH (pos)) == OTHER (color) && 686 get_liberties_board (NORTH (pos)) == 0) 687 { 688 n_cap = flood_fill_board (NORTH (pos), EMPTY); 689 move_prop->data.stone_extra |= FLAG_N_CAP; 690 } 691 if (get_point_board (SOUTH (pos)) == OTHER (color) && 692 get_liberties_board (SOUTH (pos)) == 0) 693 { 694 s_cap = flood_fill_board (SOUTH (pos), EMPTY); 695 move_prop->data.stone_extra |= FLAG_S_CAP; 696 } 697 if (get_point_board (EAST (pos)) == OTHER (color) && 698 get_liberties_board (EAST (pos)) == 0) 699 { 700 e_cap = flood_fill_board (EAST (pos), EMPTY); 701 move_prop->data.stone_extra |= FLAG_E_CAP; 702 } 703 if (get_point_board (WEST (pos)) == OTHER (color) && 704 get_liberties_board (WEST (pos)) == 0) 705 { 706 w_cap = flood_fill_board (WEST (pos), EMPTY); 707 move_prop->data.stone_extra |= FLAG_W_CAP; 708 } 709 710 /* then check for suicide */ 711 if (get_liberties_board (pos) == 0) 712 { 713 self_cap = flood_fill_board (pos, EMPTY); 714 move_prop->data.stone_extra |= FLAG_SELF_CAP; 715 } 716 717 718 /* now check for a ko, with the following requirements: 1) we 719 captured one opponent stone 2) we placed one stone (not 720 connected to a larger group) 3) we have one liberty */ 721 722 if (!self_cap && 723 (n_cap + s_cap + e_cap + w_cap == 1) && 724 get_liberties_board (pos) == 1 && 725 get_point_board (NORTH (pos)) != color && 726 get_point_board (SOUTH (pos)) != color && 727 get_point_board (EAST (pos)) != color && 728 get_point_board (WEST (pos)) != color) 729 { 730 /* We passed all tests, so there is a ko to set. The 731 ko_pos is our single liberty location */ 732 733 if (get_point_board (NORTH (pos)) == EMPTY) 734 { 735 ko_pos = NORTH (pos); 736 } 737 else if (get_point_board (SOUTH (pos)) == EMPTY) 738 { 739 ko_pos = SOUTH (pos); 740 } 741 else if (get_point_board (EAST (pos)) == EMPTY) 742 { 743 ko_pos = EAST (pos); 744 } 745 else 746 { 747 ko_pos = WEST (pos); 748 } 749 } 750 751 if (color == BLACK) 752 { 753 black_captures += n_cap + s_cap + e_cap + w_cap; 754 white_captures += self_cap; 755 } 756 else 757 { 758 white_captures += n_cap + s_cap + e_cap + w_cap; 759 black_captures += self_cap; 760 } 761 762 /* this will move the cursor near this move if it was off the 763 screen */ 764 move_display (pos); 765 } 766 767 ++move_num; 768 current_player = OTHER (color); 769 770 goto redo_node_sgf_succeeded; 771 } 772 else if (do_add_stones ()) 773 { 774 goto redo_node_sgf_succeeded; 775 } 776 777 return false; 778 char comment_buffer[512]; 779 780redo_node_sgf_succeeded: 781#if !defined(GBN_TEST) 782 if (auto_show_comments && 783 read_comment_sgf (comment_buffer, sizeof (comment_buffer))) 784 { 785 unsigned int i; 786 for (i = 0; i < sizeof (comment_buffer); ++i) 787 { 788 /* newlines display badly in rb->splash, so replace them 789 * with spaces 790 */ 791 if (comment_buffer[i] == '\n' || 792 comment_buffer[i] == '\r') 793 { 794 comment_buffer[i] = ' '; 795 } 796 else if (comment_buffer[i] == '\0') 797 { 798 break; 799 } 800 } 801 draw_screen_display(); 802 rb->splash(HZ / 3, comment_buffer); 803 rb->button_clear_queue(); 804 rb->action_userabort(TIMEOUT_BLOCK); 805 } 806#else 807 (void) comment_buffer; 808#endif 809 810 return true; 811} 812 813int 814mark_child_variations_sgf (void) 815{ 816 int result; 817 int saved = current_node; 818 struct node_t *node = get_node (current_node); 819 820 int move_handle; 821 822 if (!node) 823 { 824 return 0; 825 } 826 827 current_node = node->next; 828 goto_next_important_node (true); 829 830 result = num_variations_sgf (); 831 832 if (result > 1) 833 { 834 int i; 835 int branch_node = current_node; 836 for (i = 0; i < result; ++i) 837 { 838 go_to_variation_sgf (i); 839 goto_next_important_node (true); 840 841 move_handle = get_move_sgf (); 842 843 if (move_handle >= 0) 844 { 845 set_one_mark (get_prop (move_handle)->data.position, 846 get_prop (move_handle)->type); 847 } 848 849 current_node = branch_node; 850 } 851 } 852 853 current_node = saved; 854 855 return result; 856} 857 858void 859set_all_marks_sgf (void) 860{ 861 struct prop_t *prop = get_prop (get_node (current_node)->props); 862 863 while (prop) 864 { 865 if (prop->type == PROP_LABEL) 866 { 867 set_label_mark (prop->data.position, prop->data.label_extra); 868 } 869 else if (prop->type == PROP_COMMENT) 870 { 871 set_comment_display (true); 872 } 873 else 874 { 875 set_one_mark (prop->data.position, prop->type); 876 } 877 prop = get_prop (prop->next); 878 } 879} 880 881static void 882set_one_mark (unsigned short pos, enum prop_type_t type) 883{ 884 switch (type) 885 { 886 case PROP_CIRCLE: 887 set_mark_display (pos, 'c'); 888 break; 889 case PROP_SQUARE: 890 set_mark_display (pos, 's'); 891 break; 892 case PROP_TRIANGLE: 893 set_mark_display (pos, 't'); 894 break; 895 case PROP_MARK: 896 set_mark_display (pos, 'm'); 897 break; 898 case PROP_DIM: 899 set_mark_display (pos, 'd'); 900 break; 901 case PROP_SELECTED: 902 set_mark_display (pos, 'S'); 903 break; 904 case PROP_BLACK_MOVE: 905 set_mark_display (pos, 'b'); 906 break; 907 case PROP_WHITE_MOVE: 908 set_mark_display (pos, 'w'); 909 break; 910 case PROP_INVALID: 911 set_mark_display (pos, ' '); 912 default: 913 break; 914 } 915} 916 917static void 918set_label_mark (unsigned short pos, char to_set) 919{ 920 set_mark_display (pos, to_set | (1 << 7)); 921} 922 923static bool 924do_add_stones (void) 925{ 926 bool ret_val = false; 927 struct prop_t *temp_prop; 928 int temp_handle; 929 930 if (current_node < 0) 931 { 932 return false; 933 } 934 935 temp_handle = get_node (current_node)->props; 936 temp_prop = get_prop (temp_handle); 937 938 while (temp_prop) 939 { 940 if (temp_prop->type == PROP_ADD_BLACK || 941 temp_prop->type == PROP_ADD_WHITE || 942 temp_prop->type == PROP_ADD_EMPTY) 943 { 944 945 temp_prop->data.stone_extra = 0; 946 947 /* TODO: we could delete do-nothing PROP_ADD_*s here */ 948 949 if (get_point_board (temp_prop->data.position) == EMPTY) 950 { 951 temp_prop->data.stone_extra |= FLAG_ORIG_EMPTY; 952 } 953 else if (get_point_board (temp_prop->data.position) == BLACK) 954 { 955 temp_prop->data.stone_extra |= FLAG_ORIG_BLACK; 956 } 957 /* else, do nothing */ 958 959 if (temp_prop->type == PROP_ADD_BLACK || 960 temp_prop->type == PROP_ADD_WHITE) 961 { 962 set_point_board (temp_prop->data.position, 963 temp_prop->type == PROP_ADD_BLACK ? BLACK : 964 WHITE); 965 ret_val = true; 966 } 967 else 968 { 969 set_point_board (temp_prop->data.position, EMPTY); 970 971 ret_val = true; 972 } 973 } 974 975 temp_handle = temp_prop->next; 976 temp_prop = get_prop (temp_handle); 977 } 978 979 return ret_val; 980} 981 982int 983add_child_sgf (int *variation_number) 984{ 985 int node_handle; 986 struct node_t *node; 987 struct node_t *current = get_node (current_node); 988 989 if (current->next < 0) 990 { 991 node_handle = alloc_storage_sgf (); 992 node = get_node (node_handle); 993 994 if (node_handle < 0 || !current) 995 { 996 return NO_NODE; 997 } 998 999 node->prev = current_node; 1000 node->next = NO_NODE; 1001 node->props = NO_PROP; 1002 1003 current->next = node_handle; 1004 1005 if (variation_number) 1006 { 1007 *variation_number = 0; 1008 } 1009 1010 return node_handle; 1011 } 1012 else 1013 { 1014 return add_child_variation (variation_number); 1015 } 1016} 1017 1018int 1019add_prop_sgf (int node, enum prop_type_t type, union prop_data_t data) 1020{ 1021 int new_prop; 1022 int temp_prop_handle; 1023 1024 if (node < 0) 1025 { 1026 return NO_PROP; 1027 } 1028 1029 new_prop = alloc_storage_sgf (); 1030 1031 if (new_prop < 0) 1032 { 1033 return NO_PROP; 1034 } 1035 1036 get_prop (new_prop)->type = type; 1037 get_prop (new_prop)->data = data; 1038 1039 /* check if the new_prop goes at the start */ 1040 if (get_node (node)->props == NO_PROP || 1041 (type == PROP_VARIATION && 1042 get_prop (get_node (node)->props)->type != PROP_VARIATION)) 1043 { 1044 1045 if (get_node (node)->props >= 0) 1046 { 1047 get_prop (new_prop)->next = get_node (node)->props; 1048 } 1049 else 1050 { 1051 get_prop (new_prop)->next = NO_PROP; 1052 } 1053 1054 get_node (node)->props = new_prop; 1055 return new_prop; 1056 } 1057 1058 temp_prop_handle = get_node (node)->props; 1059 1060 while (1) 1061 { 1062 if (get_prop (temp_prop_handle)->next < 0 || 1063 (get_prop (temp_prop_handle)->type == type && 1064 get_prop (get_prop (temp_prop_handle)->next)->type != type)) 1065 { 1066 /* new_prop goes after the current one either because we're at 1067 the end of the props list, or because we're adding a prop 1068 after the ones of its same type */ 1069 get_prop (new_prop)->next = get_prop (temp_prop_handle)->next; 1070 get_prop (temp_prop_handle)->next = new_prop; 1071 1072 return new_prop; 1073 } 1074 1075 temp_prop_handle = get_prop (temp_prop_handle)->next; 1076 } 1077} 1078 1079int 1080get_prop_pos_sgf (enum prop_type_t type, union prop_data_t data) 1081{ 1082 int temp_prop_handle; 1083 struct prop_t *prop; 1084 1085 if (current_node < 0) 1086 { 1087 return -1; 1088 } 1089 1090 temp_prop_handle = get_node (current_node)->props; 1091 1092 while (temp_prop_handle >= 0) 1093 { 1094 prop = get_prop (temp_prop_handle); 1095 1096 if ((type == PROP_ADD_BLACK || 1097 type == PROP_ADD_WHITE || 1098 type == PROP_ADD_EMPTY) 1099 && 1100 (prop->type == PROP_ADD_BLACK || 1101 prop->type == PROP_ADD_WHITE || 1102 prop->type == PROP_ADD_EMPTY) 1103 && (prop->data.position == data.position)) 1104 { 1105 return temp_prop_handle; 1106 } 1107 1108 if ((type == PROP_CIRCLE || 1109 type == PROP_SQUARE || 1110 type == PROP_TRIANGLE || 1111 type == PROP_MARK || 1112 type == PROP_DIM || 1113 type == PROP_SELECTED) && 1114 (prop->type == PROP_CIRCLE || 1115 prop->type == PROP_SQUARE || 1116 prop->type == PROP_TRIANGLE || 1117 prop->type == PROP_MARK || 1118 prop->type == PROP_DIM || 1119 prop->type == PROP_SELECTED) && 1120 (prop->data.position == data.position)) 1121 { 1122 return temp_prop_handle; 1123 } 1124 1125 temp_prop_handle = get_prop (temp_prop_handle)->next; 1126 } 1127 1128 return -1; 1129} 1130 1131int 1132add_or_set_prop_sgf (int node, enum prop_type_t type, union prop_data_t data) 1133{ 1134 int temp_prop_handle; 1135 1136 if (node < 0) 1137 { 1138 return NO_PROP; 1139 } 1140 1141 temp_prop_handle = get_prop_sgf (node, type, NULL); 1142 1143 if (temp_prop_handle >= 0) 1144 { 1145 get_prop (temp_prop_handle)->data = data; 1146 return temp_prop_handle; 1147 } 1148 1149 /* there was no prop to set, so we need to add one */ 1150 return add_prop_sgf (node, type, data); 1151} 1152 1153int 1154get_prop_sgf (int node, enum prop_type_t type, int *previous_prop) 1155{ 1156 int previous_handle = NO_PROP; 1157 int current_handle; 1158 1159 if (node < 0) 1160 { 1161 return NO_PROP; 1162 } 1163 1164 if (get_node (node)->props < 0) 1165 { 1166 return NO_PROP; 1167 } 1168 1169 current_handle = get_node (node)->props; 1170 1171 while (current_handle >= 0) 1172 { 1173 if (get_prop (current_handle)->type == type || type == PROP_ANY) 1174 { 1175 if (previous_prop) 1176 { 1177 *previous_prop = previous_handle; 1178 } 1179 1180 return current_handle; 1181 } 1182 else 1183 { 1184 previous_handle = current_handle; 1185 current_handle = get_prop (current_handle)->next; 1186 } 1187 } 1188 1189 return NO_PROP; 1190} 1191 1192bool 1193delete_prop_sgf (int node, enum prop_type_t type) 1194{ 1195 if (node < 0) 1196 { 1197 return false; 1198 } 1199 1200 return delete_prop_handle_sgf (node, get_prop_sgf (node, type, NULL)); 1201} 1202 1203bool 1204delete_prop_handle_sgf (int node, int prop) 1205{ 1206 int previous; 1207 1208 if (prop < 0 || node < 0 || get_node (node)->props < 0) 1209 { 1210 return false; 1211 } 1212 1213 if (get_node (node)->props == prop) 1214 { 1215 get_node (node)->props = get_prop (get_node (node)->props)->next; 1216 free_storage_sgf (prop); 1217 return true; 1218 } 1219 1220 previous = get_node (node)->props; 1221 1222 while (get_prop (previous)->next != prop && get_prop (previous)->next >= 0) 1223 { 1224 previous = get_prop (previous)->next; 1225 } 1226 1227 if (get_prop (previous)->next < 0) 1228 { 1229 return false; 1230 } 1231 else 1232 { 1233 get_prop (previous)->next = get_prop (get_prop (previous)->next)->next; 1234 free_storage_sgf (prop); 1235 return true; 1236 } 1237} 1238 1239int 1240read_comment_sgf (char *buffer, size_t buffer_size) 1241{ 1242 size_t bytes_read = 0; 1243 1244 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL); 1245 1246 if (prop_handle < 0) 1247 { 1248 return 0; 1249 } 1250 1251 if (unhandled_fd < 0) 1252 { 1253 DEBUGF ("unhandled file is closed?!\n"); 1254 return 0; 1255 } 1256 1257 if (rb->lseek (unhandled_fd, get_prop (prop_handle)->data.number, 1258 SEEK_SET) < 0) 1259 { 1260 DEBUGF ("couldn't seek in unhandled_fd\n"); 1261 return -1; 1262 } 1263 1264 if (!(read_char_no_whitespace (unhandled_fd) == 'C') || 1265 !(read_char_no_whitespace (unhandled_fd) == '[')) 1266 { 1267 DEBUGF ("comment prop points to incorrect place in unhandled_fd!!\n"); 1268 return -1; 1269 } 1270 1271 /* make output a string, the lazy way */ 1272 rb->memset (buffer, 0, buffer_size); 1273 ++bytes_read; 1274 1275 bool done = false; 1276 bool escaped = false; 1277 1278 while ((buffer_size > bytes_read) && !done) 1279 { 1280 int temp = read_char (unhandled_fd); 1281 1282 switch (temp) 1283 { 1284 case ']': 1285 if (!escaped) 1286 { 1287 done = true; 1288 break; 1289 } 1290 *buffer = temp; 1291 ++buffer; 1292 ++bytes_read; 1293 escaped = false; 1294 break; 1295 1296 case -1: 1297 DEBUGF ("encountered end of file before end of comment!\n"); 1298 done = true; 1299 break; 1300 1301 case '\\': 1302 escaped = !escaped; 1303 if (escaped) 1304 { 1305 break; 1306 } 1307 /* Intentional fallthrough */ 1308 default: 1309 *buffer = temp; 1310 ++buffer; 1311 ++bytes_read; 1312 escaped = false; 1313 break; 1314 } 1315 } 1316 1317 return bytes_read; 1318} 1319 1320int 1321write_comment_sgf (char *string) 1322{ 1323 char *orig_string = string; 1324 1325 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL); 1326 1327 int start_of_comment = -1; 1328 bool overwriting = false; 1329 1330 int bytes_written = 0; 1331 1332 set_game_modified(); 1333 1334 if (unhandled_fd < 0) 1335 { 1336 DEBUGF ("unhandled file is closed?!\n"); 1337 return 0; 1338 } 1339 1340 if (prop_handle >= 0) 1341 { 1342 if ((start_of_comment = rb->lseek (unhandled_fd, 1343 get_prop (prop_handle)->data.number, 1344 SEEK_SET)) < 0) 1345 { 1346 DEBUGF ("couldn't seek in unhandled_fd\n"); 1347 return -1; 1348 } 1349 else 1350 { 1351 overwriting = true; 1352 } 1353 } 1354 else 1355 { 1356 overwriting = false; 1357 } 1358 1359 start_of_write_wcs: 1360 1361 if (overwriting) 1362 { 1363 if (!(read_char_no_whitespace (unhandled_fd) == 'C') || 1364 !(read_char_no_whitespace (unhandled_fd) == '[')) 1365 { 1366 DEBUGF ("non-comment while overwriting!!\n"); 1367 return -1; 1368 } 1369 } 1370 else 1371 { 1372 start_of_comment = rb->lseek (unhandled_fd, 0, SEEK_END); 1373 1374 if (start_of_comment < 0) 1375 { 1376 DEBUGF ("error seeking to end in write_comment_sgf\n"); 1377 return -1; 1378 } 1379 1380 write_char (unhandled_fd, 'C'); 1381 write_char (unhandled_fd, '['); 1382 } 1383 1384 1385 bool overwrite_escaped = false; 1386 1387 while (*string) 1388 { 1389 if (overwriting) 1390 { 1391 int orig_char = peek_char (unhandled_fd); 1392 1393 switch (orig_char) 1394 { 1395 case '\\': 1396 overwrite_escaped = !overwrite_escaped; 1397 break; 1398 1399 case ']': 1400 if (overwrite_escaped) 1401 { 1402 overwrite_escaped = false; 1403 break; 1404 } 1405 /* otherwise, fall through */ 1406 1407 case -1: 1408 1409 /* we reached the end of the part we can put our comment 1410 in, but there's more comment to write, so we should 1411 start again, this time making a new comment (the old 1412 becomes wasted space in unhandled_fd, but it doesn't 1413 really hurt anything except extra space on disk */ 1414 1415 overwriting = false; 1416 string = orig_string; 1417 bytes_written = 0; 1418 goto start_of_write_wcs; 1419 break; 1420 1421 default: 1422 overwrite_escaped = false; 1423 break; 1424 } 1425 } 1426 1427 switch (*string) 1428 { 1429 case '\\': 1430 case ']': 1431 write_char (unhandled_fd, '\\'); 1432 1433 /* fall through */ 1434 1435 default: 1436 write_char (unhandled_fd, *string); 1437 break; 1438 } 1439 1440 ++string; 1441 ++bytes_written; 1442 } 1443 1444 /* finish out the record */ 1445 write_char (unhandled_fd, ']'); 1446 write_char (unhandled_fd, ';'); 1447 1448 /* and put the reference into the unhandled_fd into the comment prop */ 1449 union prop_data_t temp_data; 1450 temp_data.number = start_of_comment; 1451 1452 add_or_set_prop_sgf (current_node, PROP_COMMENT, temp_data); 1453 set_comment_display (true); 1454 return bytes_written; 1455} 1456 1457bool 1458has_more_nodes_sgf (void) 1459{ 1460 int saved = current_node; 1461 bool ret_val = false; 1462 1463 if (current_node < 0) 1464 { 1465 return false; 1466 } 1467 1468 current_node = get_node (current_node)->next; 1469 1470 if (current_node >= 0) 1471 { 1472 /* returns true if it finds an important node */ 1473 ret_val = goto_next_important_node (true); 1474 } 1475 1476 current_node = saved; 1477 return ret_val; 1478} 1479 1480/* logic is different here because the first node in a tree is a valid 1481 place to go */ 1482bool 1483has_prev_nodes_sgf (void) 1484{ 1485 if (current_node < 0) 1486 { 1487 return false; 1488 } 1489 1490 return current_node != start_node; 1491} 1492 1493int 1494get_move_sgf (void) 1495{ 1496 int result = -1; 1497 int saved_current = current_node; 1498 1499 goto_next_important_node (true); 1500 1501 result = get_move_from_node (current_node); 1502 1503 current_node = saved_current; 1504 return result; 1505} 1506 1507static int 1508get_move_from_node (int handle) 1509{ 1510 int prop_handle; 1511 1512 if (handle < 0) 1513 { 1514 return -2; 1515 } 1516 1517 prop_handle = get_node (handle)->props; 1518 1519 1520 while (prop_handle >= 0) 1521 { 1522 if (get_prop (prop_handle)->type == PROP_BLACK_MOVE || 1523 get_prop (prop_handle)->type == PROP_WHITE_MOVE) 1524 { 1525 return prop_handle; 1526 } 1527 1528 prop_handle = get_prop (prop_handle)->next; 1529 } 1530 1531 return -1; 1532} 1533 1534static bool 1535retreat_node (void) 1536{ 1537 int result = get_node (current_node)->prev; 1538 1539 if (current_node == start_node) 1540 { 1541 return false; 1542 } 1543 1544 if (result < 0) 1545 { 1546 return false; 1547 } 1548 else 1549 { 1550 clear_marks_display (); 1551 1552 current_node = result; 1553 1554 /* go backwards to the next important node (move/add 1555 stone/variation/etc.) */ 1556 goto_next_important_node (false); 1557 return true; 1558 } 1559} 1560 1561static bool 1562advance_node (void) 1563{ 1564 int result = get_node (current_node)->next; 1565 1566 if (result < 0) 1567 { 1568 return false; 1569 } 1570 else 1571 { 1572 clear_marks_display (); 1573 1574 current_node = result; 1575 1576 /* go forward to the next move/add stone/variation/etc. node */ 1577 goto_next_important_node (true); 1578 result = get_prop_sgf (current_node, PROP_VARIATION_CHOICE, NULL); 1579 1580 if (result >= 0) 1581 { 1582 go_to_variation_sgf (get_prop (result)->data.number); 1583 } 1584 1585 return true; 1586 } 1587} 1588 1589int 1590num_variations_sgf (void) 1591{ 1592 int result = 1; 1593 struct prop_t *temp_prop; 1594 struct node_t *temp_node = get_node (current_node); 1595 1596 if (temp_node == 0) 1597 { 1598 return 0; 1599 } 1600 1601 if (temp_node->prev >= 0) 1602 { 1603 temp_node = get_node (get_node (temp_node->prev)->next); 1604 } 1605 1606 temp_prop = get_prop (temp_node->props); 1607 1608 while (temp_prop) 1609 { 1610 if (temp_prop->type == PROP_VARIATION) 1611 { 1612 ++result; 1613 } 1614 else 1615 { 1616 /* variations are at the beginning of the prop list */ 1617 break; 1618 } 1619 1620 temp_prop = get_prop (temp_prop->next); 1621 } 1622 1623 return result; 1624} 1625 1626bool 1627go_to_variation_sgf (unsigned int num) 1628{ 1629 int saved = current_node; 1630 struct node_t *temp_node = get_node (current_node); 1631 struct prop_t *temp_prop; 1632 1633 if (!temp_node) 1634 { 1635 return false; 1636 } 1637 1638 temp_node = get_node (temp_node->prev); 1639 1640 if (!temp_node) 1641 { 1642 return false; 1643 } 1644 1645 temp_node = get_node (current_node = temp_node->next); 1646 1647 if (!temp_node) 1648 { 1649 current_node = saved; 1650 return false; 1651 } 1652 1653 temp_prop = get_prop (temp_node->props); 1654 1655 while (num) 1656 { 1657 if (!temp_prop || temp_prop->type != PROP_VARIATION) 1658 { 1659 current_node = saved; 1660 return false; 1661 } 1662 1663 if (num == 1) 1664 { 1665 current_node = temp_prop->data.node; 1666 break; 1667 } 1668 1669 temp_prop = get_prop (temp_prop->next); 1670 --num; 1671 } 1672 1673 return true; 1674} 1675 1676int 1677get_matching_child_sgf (unsigned short pos, unsigned char color) 1678{ 1679 struct node_t *temp_node; 1680 struct prop_t *temp_prop; 1681 int variation_count = 0; 1682 int saved; 1683 1684 /* set true later in a loop if we want a prop in the first variation 1685 which means that we shouldn't check that branch for children */ 1686 bool dont_check_first_var_children = false; 1687 1688 temp_node = get_node (current_node); 1689 1690 if (!temp_node) 1691 { 1692 return -3; 1693 } 1694 1695 temp_node = get_node (temp_node->next); 1696 1697 if (!temp_node) 1698 { 1699 return -2; 1700 } 1701 1702 temp_prop = get_prop (temp_node->props); 1703 1704 while (temp_prop) 1705 { 1706 if (temp_prop->type == PROP_VARIATION) 1707 { 1708 ++variation_count; 1709 saved = current_node; 1710 current_node = temp_prop->data.node; 1711 1712 struct prop_t *temp_move = get_prop (get_move_sgf ()); 1713 1714 current_node = saved; 1715 1716 if (temp_move && 1717 temp_move->data.position == pos && 1718 ((color == BLACK && temp_move->type == PROP_BLACK_MOVE) || 1719 (color == WHITE && temp_move->type == PROP_WHITE_MOVE))) 1720 { 1721 return variation_count; 1722 } 1723 } 1724 else if ((temp_prop->type == PROP_BLACK_MOVE && color == BLACK) || 1725 (temp_prop->type == PROP_WHITE_MOVE && color == WHITE)) 1726 { 1727 if (temp_prop->data.position == pos) 1728 { 1729 return 0; 1730 } 1731 else 1732 { 1733 return -4; 1734 } 1735 } 1736 else if (temp_prop->type == PROP_ADD_WHITE || 1737 temp_prop->type == PROP_ADD_BLACK || 1738 temp_prop->type == PROP_ADD_EMPTY) 1739 { 1740 dont_check_first_var_children = true; 1741 } 1742 1743 temp_prop = get_prop (temp_prop->next); 1744 } 1745 1746 if (dont_check_first_var_children) 1747 { 1748 return -1; 1749 } 1750 else 1751 { 1752 saved = current_node; 1753 current_node = temp_node->next; 1754 1755 struct prop_t *temp_move = get_prop (get_move_sgf ()); 1756 if (temp_move && 1757 pos == temp_move->data.position && 1758 color == (temp_move->type == PROP_BLACK_MOVE ? BLACK : WHITE)) 1759 { 1760 current_node = saved; 1761 return 0; 1762 } 1763 1764 current_node = saved; 1765 return -1; 1766 } 1767} 1768 1769int 1770next_variation_sgf (void) 1771{ 1772 int saved = current_node; 1773 int saved_start = start_node; 1774 union prop_data_t temp_data; 1775 int prop_handle; 1776 int num_vars = 0; 1777 1778 if (current_node < 0 || get_node (current_node)->prev < 0) 1779 { 1780 return -1; 1781 } 1782 1783 start_node = NO_NODE; 1784 1785 1786 1787 if (num_variations_sgf () < 2) 1788 { 1789 1790 current_node = saved; 1791 start_node = saved_start; 1792 return -2; 1793 } 1794 1795 /* now we're at a branch node which we should go to the next variation 1796 (we were at the "chosen" one, so go to the next one after that, 1797 (mod the number of variations)) */ 1798 1799 int chosen = 0; 1800 int branch_node = get_node (get_node (current_node)->prev)->next; 1801 1802 prop_handle = get_prop_sgf (branch_node, PROP_VARIATION_CHOICE, NULL); 1803 1804 if (prop_handle >= 0) 1805 { 1806 chosen = get_prop (prop_handle)->data.number; 1807 } 1808 1809 ++chosen; 1810 1811 if (chosen >= (num_vars = num_variations_sgf ())) 1812 { 1813 chosen = 0; 1814 } 1815 1816 temp_data.number = chosen; 1817 add_or_set_prop_sgf (branch_node, PROP_VARIATION_CHOICE, temp_data); 1818 1819 if (!undo_node_sgf ()) 1820 { 1821 current_node = saved; 1822 start_node = saved_start; 1823 return -3; 1824 } 1825 1826 if (redo_node_sgf ()) 1827 { 1828 start_node = saved_start; 1829 return chosen + 1; 1830 } 1831 else 1832 { 1833 current_node = saved; 1834 start_node = saved_start; 1835 return -4; 1836 } 1837} 1838 1839int 1840add_child_variation (int *variation_number) 1841{ 1842 struct node_t *temp_node = get_node (current_node); 1843 struct prop_t *temp_prop; 1844 int temp_prop_handle; 1845 int new_node = alloc_storage_sgf (); 1846 int new_prop = alloc_storage_sgf (); 1847 int temp_variation_number; 1848 1849 if (new_node < 0 || new_prop < 0) 1850 { 1851 if (new_node >= 0) 1852 { 1853 free_storage_sgf (new_node); 1854 } 1855 if (new_prop >= 0) 1856 { 1857 free_storage_sgf (new_prop); 1858 } 1859 1860 return NO_NODE; 1861 } 1862 1863 if (!temp_node) 1864 { 1865 free_storage_sgf (new_node); 1866 free_storage_sgf (new_prop); 1867 1868 return NO_NODE; 1869 } 1870 1871 temp_node = get_node (temp_node->next); 1872 1873 if (!temp_node) 1874 { 1875 free_storage_sgf (new_node); 1876 free_storage_sgf (new_prop); 1877 1878 return NO_NODE; 1879 } 1880 1881 get_node (new_node)->prev = current_node; 1882 get_node (new_node)->next = NO_NODE; 1883 get_node (new_node)->props = NO_PROP; 1884 1885 get_prop (new_prop)->type = PROP_VARIATION; 1886 get_prop (new_prop)->next = NO_PROP; 1887 get_prop (new_prop)->data.node = new_node; 1888 1889 temp_prop_handle = temp_node->props; 1890 1891 if (temp_prop_handle < 0) 1892 { 1893 temp_node->props = new_prop; 1894 1895 if (variation_number) 1896 { 1897 *variation_number = 1; 1898 } 1899 1900 return new_node; 1901 } 1902 1903 if (get_prop (temp_prop_handle)->type != PROP_VARIATION) 1904 { 1905 get_prop (new_prop)->next = temp_node->props; 1906 temp_node->props = new_prop; 1907 1908 if (variation_number) 1909 { 1910 *variation_number = 1; 1911 } 1912 1913 return new_node; 1914 } 1915 1916 /* the lowest it can be, since 1 isn't it */ 1917 temp_variation_number = 2; 1918 1919 while (1) 1920 { 1921 temp_prop = get_prop (temp_prop_handle); 1922 if (temp_prop->next < 0 || 1923 get_prop (temp_prop->next)->type != PROP_VARIATION) 1924 { 1925 get_prop (new_prop)->next = temp_prop->next; 1926 temp_prop->next = new_prop; 1927 1928 if (variation_number) 1929 { 1930 *variation_number = temp_variation_number; 1931 } 1932 1933 return new_node; 1934 } 1935 1936 ++temp_variation_number; 1937 temp_prop_handle = temp_prop->next; 1938 } 1939} 1940 1941static bool 1942is_important_node (int handle) 1943{ 1944 struct prop_t *temp_prop; 1945 1946 if (handle < 0) 1947 { 1948 return false; 1949 } 1950 1951 if (handle == start_node) 1952 { 1953 return true; 1954 } 1955 1956 if (get_node (handle)->prev < 0) 1957 { 1958 return true; 1959 } 1960 1961 temp_prop = get_prop (get_node (handle)->props); 1962 1963 while (temp_prop) 1964 { 1965 if (temp_prop->type == PROP_BLACK_MOVE || 1966 temp_prop->type == PROP_WHITE_MOVE || 1967 temp_prop->type == PROP_ADD_BLACK || 1968 temp_prop->type == PROP_ADD_WHITE || 1969 temp_prop->type == PROP_ADD_EMPTY || 1970 temp_prop->type == PROP_VARIATION) 1971 { 1972 return true; 1973 } 1974 1975 temp_prop = get_prop (temp_prop->next); 1976 } 1977 1978 return false; 1979} 1980 1981static bool 1982goto_next_important_node (bool forward) 1983{ 1984 int temp_node = current_node; 1985 int last_good = temp_node; 1986 1987 while (temp_node >= 0 && !is_important_node (temp_node)) 1988 { 1989 last_good = temp_node; 1990 1991 temp_node = forward ? get_node (temp_node)->next : 1992 get_node (temp_node)->prev; 1993 1994 } 1995 1996 if (temp_node < 0) 1997 { 1998 current_node = last_good; 1999 } 2000 else 2001 { 2002 current_node = temp_node; 2003 } 2004 2005 if (temp_node < 0) 2006 { 2007 return false; 2008 } 2009 else 2010 { 2011 return true; 2012 } 2013} 2014 2015bool 2016is_handled_sgf (enum prop_type_t type) 2017{ 2018 if (type == PROP_BLACK_MOVE || 2019 type == PROP_WHITE_MOVE || 2020 type == PROP_ADD_BLACK || 2021 type == PROP_ADD_WHITE || 2022 type == PROP_ADD_EMPTY || 2023 type == PROP_CIRCLE || type == PROP_SQUARE || type == PROP_TRIANGLE || 2024#if 0 2025 /* these marks are stupid and nobody uses them. if we could find 2026 a good way to draw them we could do them anyway, but no reason 2027 to unless it's easy */ 2028 type == PROP_DIM || type == PROP_SELECTED || 2029#endif 2030 type == PROP_COMMENT || 2031 type == PROP_MARK || 2032 type == PROP_LABEL || 2033 type == PROP_GAME || 2034 type == PROP_FILE_FORMAT || 2035 type == PROP_APPLICATION || 2036 type == PROP_CHARSET || 2037 type == PROP_SIZE || 2038 type == PROP_KOMI || 2039 type == PROP_BLACK_NAME || 2040 type == PROP_WHITE_NAME || 2041 type == PROP_BLACK_RANK || 2042 type == PROP_WHITE_RANK || 2043 type == PROP_BLACK_TEAM || 2044 type == PROP_WHITE_TEAM || 2045 type == PROP_DATE || 2046 type == PROP_ROUND || 2047 type == PROP_EVENT || 2048 type == PROP_PLACE || 2049 type == PROP_OVERTIME || 2050 type == PROP_RESULT || 2051 type == PROP_TIME_LIMIT || 2052 type == PROP_RULESET || 2053 type == PROP_HANDICAP || type == PROP_VARIATION_TYPE) 2054 { 2055 return true; 2056 } 2057 2058 return false; 2059} 2060 2061void 2062setup_handicap_sgf (void) 2063{ 2064 union prop_data_t temp_data; 2065 2066 if (header.handicap <= 1) 2067 { 2068 return; 2069 } 2070 2071 current_node = start_node; 2072 2073 temp_data.number = header.handicap; 2074 add_prop_sgf (current_node, PROP_HANDICAP, temp_data); 2075 2076 /* now, add the actual stones */ 2077 2078 if ((board_width != 19 && board_width != 13 && board_width != 9) || 2079 board_width != board_height || header.handicap > 9) 2080 { 2081 rb->splashf (5 * HZ, 2082 "Use the 'Add Black' tool to add %d handicap stones!", 2083 header.handicap); 2084 return; 2085 } 2086 2087 int handicaps_to_place = header.handicap; 2088 2089 int low_coord = 0, mid_coord = 0, high_coord = 0; 2090 2091 if (board_width == 19) 2092 { 2093 low_coord = 3; 2094 mid_coord = 9; 2095 high_coord = 15; 2096 } 2097 else if (board_width == 13) 2098 { 2099 low_coord = 3; 2100 mid_coord = 6; 2101 high_coord = 9; 2102 } 2103 else if (board_width == 9) 2104 { 2105 low_coord = 2; 2106 mid_coord = 4; 2107 high_coord = 6; 2108 } 2109 2110 /* first four go in the corners */ 2111 handicaps_to_place -= 2; 2112 setup_handicap_helper (POS (high_coord, low_coord)); 2113 setup_handicap_helper (POS (low_coord, high_coord)); 2114 2115 if (!handicaps_to_place) 2116 { 2117 goto done_adding_stones; 2118 } 2119 2120 --handicaps_to_place; 2121 setup_handicap_helper (POS (high_coord, high_coord)); 2122 2123 if (!handicaps_to_place) 2124 { 2125 goto done_adding_stones; 2126 } 2127 2128 --handicaps_to_place; 2129 setup_handicap_helper (POS (low_coord, low_coord)); 2130 2131 if (!handicaps_to_place) 2132 { 2133 goto done_adding_stones; 2134 } 2135 2136 /* now done with first four, if only one left it goes in the center */ 2137 if (handicaps_to_place == 1) 2138 { 2139 --handicaps_to_place; 2140 setup_handicap_helper (POS (mid_coord, mid_coord)); 2141 } 2142 else 2143 { 2144 handicaps_to_place -= 2; 2145 setup_handicap_helper (POS (high_coord, mid_coord)); 2146 setup_handicap_helper (POS (low_coord, mid_coord)); 2147 } 2148 2149 if (!handicaps_to_place) 2150 { 2151 goto done_adding_stones; 2152 } 2153 2154 /* done with first 6 */ 2155 2156 if (handicaps_to_place == 1) 2157 { 2158 --handicaps_to_place; 2159 setup_handicap_helper (POS (mid_coord, mid_coord)); 2160 } 2161 else 2162 { 2163 handicaps_to_place -= 2; 2164 setup_handicap_helper (POS (mid_coord, high_coord)); 2165 setup_handicap_helper (POS (mid_coord, low_coord)); 2166 } 2167 2168 if (!handicaps_to_place) 2169 { 2170 goto done_adding_stones; 2171 } 2172 2173 /* done with first eight, there can only be the tengen remaining */ 2174 2175 setup_handicap_helper (POS (mid_coord, mid_coord)); 2176 2177 done_adding_stones: 2178 goto_handicap_start_sgf (); 2179 return; 2180} 2181 2182static void 2183setup_handicap_helper (unsigned short pos) 2184{ 2185 union prop_data_t temp_data; 2186 2187 temp_data.position = pos; 2188 2189 add_prop_sgf (current_node, PROP_ADD_BLACK, temp_data); 2190} 2191 2192void 2193goto_handicap_start_sgf (void) 2194{ 2195 if (start_node != tree_head) 2196 { 2197 current_node = get_node (start_node)->prev; 2198 redo_node_sgf (); 2199 } 2200} 2201 2202bool 2203post_game_setup_sgf (void) 2204{ 2205 int temp_handle = alloc_storage_sgf (); 2206 int saved = current_node; 2207 2208 if (temp_handle < 0) 2209 { 2210 return false; 2211 } 2212 2213 union prop_data_t temp_data; 2214 temp_data.number = 0; /* meaningless */ 2215 2216 if (!header_marked) 2217 { 2218 add_prop_sgf (tree_head, PROP_ROOT_PROPS, temp_data); 2219 header_marked = true; 2220 } 2221 2222 get_node (temp_handle)->next = current_node; 2223 get_node (temp_handle)->prev = NO_NODE; 2224 get_node (temp_handle)->props = NO_PROP; 2225 2226 current_node = temp_handle; 2227 2228 redo_node_sgf (); 2229 2230 if (current_node == temp_handle) 2231 { 2232 current_node = saved; 2233 } 2234 2235 free_storage_sgf (temp_handle); 2236 2237 return true; 2238}