A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 858 lines 23 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 "goban.h" 23#include "sgf_parse.h" 24#include "sgf.h" 25#include "sgf_storage.h" 26#include "util.h" 27#include "board.h" 28#include "game.h" 29 30static void handle_prop_value (enum prop_type_t type); 31static int read_prop_value (char *buffer, size_t buffer_size); 32static void do_range (enum prop_type_t type, unsigned short ul, 33 unsigned short br); 34static void parse_prop (void); 35static void parse_node (void); 36static enum prop_type_t parse_prop_type (void); 37 38static unsigned short sgf_to_pos (char *buffer); 39 40bool 41parse_sgf (const char *filename) 42{ 43 int saved = current_node; 44 45 /* for parsing */ 46 int first_handle = 0; /* first node in the branch */ 47 int file_position = 0; 48 49 int temp; 50 51 close_file (&sgf_fd); 52 53 sgf_fd = rb->open (filename, O_RDONLY); 54 55 if (sgf_fd < 0) 56 { 57 return false; 58 } 59 60 current_node = start_node; 61 62 if (current_node < 0) 63 { 64 current_node = saved; 65 return false; 66 } 67 68 empty_stack (&parse_stack); 69 70 /* seek to the first '(' */ 71 while (peek_char_no_whitespace (sgf_fd) != '(') 72 { 73 if (read_char_no_whitespace (sgf_fd) == -1) 74 { 75 DEBUGF ("end of file or error before we found a '('\n"); 76 current_node = saved; 77 return false; 78 } 79 } 80 81 push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR)); 82 push_int_stack (&parse_stack, current_node); 83 84 while (pop_int_stack (&parse_stack, &first_handle) && 85 pop_int_stack (&parse_stack, &file_position)) 86 { 87 /* DEBUGF("poped off %d\n", file_position); */ 88 89 rb->yield (); 90 91 current_node = first_handle; 92 93 if (file_position == -1) 94 { 95 temp = read_char_no_whitespace (sgf_fd); 96 if (temp != '(') 97 { 98 /* we're here because there may have been a sibling after 99 another gametree that was handled, but there's no '(', 100 so there wasnt' a sibling, so just go on to any more 101 gametrees in the stack */ 102 continue; 103 } 104 else 105 { 106 /* there may be more siblings after we process this one */ 107 push_int_stack (&parse_stack, -1); 108 push_int_stack (&parse_stack, first_handle); 109 } 110 } 111 else 112 { 113 /* check for a sibling after we finish with this node */ 114 push_int_stack (&parse_stack, -1); 115 push_int_stack (&parse_stack, first_handle); 116 117 rb->lseek (sgf_fd, file_position, SEEK_SET); 118 119 120 /* we're at the start of a gametree here, right at the '(' */ 121 temp = read_char_no_whitespace (sgf_fd); 122 123 if (temp != '(') 124 { 125 DEBUGF ("start of gametree doesn't have a '('!\n"); 126 current_node = saved; 127 return false; 128 } 129 } 130 131 while (1) 132 { 133 temp = peek_char_no_whitespace (sgf_fd); 134 /* DEBUGF("||| %d, %c\n", absolute_position(), (char) temp); */ 135 136 if (temp == ';') 137 { 138 /* fill the tree_head node before moving on */ 139 if (current_node != tree_head || 140 get_node (current_node)->props >= 0) 141 { 142 int temp = add_child_sgf (NULL); 143 144 if (temp >= 0) 145 { 146 current_node = temp; 147 } 148 else 149 { 150 rb->splash (2 * HZ, "Out of memory while parsing!"); 151 return false; 152 } 153 } 154 155 156 read_char_no_whitespace (sgf_fd); 157 parse_node (); 158 } 159 else if (temp == ')') 160 { 161 /* finished this gametree */ 162 163 /* we want to end one past the ')', so eat it up: */ 164 read_char_no_whitespace (sgf_fd); 165 break; 166 } 167 else if (temp == '(') 168 { 169 /* 170 DEBUGF ("adding %d\n", (int) rb->lseek (sgf_fd, 0, 171 SEEK_CUR)); */ 172 push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR)); 173 push_int_stack (&parse_stack, current_node); 174 175 break; 176 } 177 else if (temp == -1) 178 { 179 break; 180 } 181 else 182 { 183 DEBUGF ("extra characters found while parsing: %c\n", temp); 184 /* skip the extras i guess */ 185 read_char_no_whitespace (sgf_fd); 186 } 187 } 188 } 189 190 current_node = get_node (tree_head)->next; 191 while (current_node >= 0 && get_node (current_node)->props < 0) 192 { 193 temp = current_node; /* to be freed later */ 194 195 /* update the ->prev pointed on all branches of the next node */ 196 current_node = get_node (current_node)->next; 197 /* DEBUGF("trying to set prev for branch %d\n", current_node); */ 198 if (current_node >= 0) 199 { 200 get_node (current_node)->prev = tree_head; 201 202 struct prop_t *loop_prop = 203 get_prop (get_node (current_node)->props); 204 205 while (loop_prop != 0) 206 { 207 if (loop_prop->type == PROP_VARIATION) 208 { 209 get_node (loop_prop->data.number)->prev = tree_head; 210 } 211 else 212 { 213 /* all of the variations have to be up front, so we 214 can quit here */ 215 break; 216 } 217 loop_prop = get_prop (loop_prop->next); 218 } 219 } 220 221 /* update the tree head */ 222 get_node (tree_head)->next = get_node (temp)->next; 223 /* DEBUGF("freeing %d %d %d\n", temp, start_node, saved); */ 224 if (start_node == temp || saved == temp) 225 { 226 start_node = saved = tree_head; 227 } 228 free_storage_sgf (temp); 229 230 current_node = get_node (tree_head)->next; 231 } 232 233 current_node = saved; 234 235 236 /* DEBUGF("got past!\n"); */ 237 close_file (&sgf_fd); 238 return true; 239} 240 241 242static void 243parse_node (void) 244{ 245 int temp; 246 247 while (1) 248 { 249 temp = peek_char_no_whitespace (sgf_fd); 250 251 if (temp == -1 || temp == ')' || temp == '(' || temp == ';') 252 { 253 return; 254 } 255 else 256 { 257 parse_prop (); 258 } 259 } 260} 261 262 263 264int start_of_prop = 0; 265static void 266parse_prop (void) 267{ 268 enum prop_type_t temp_type = PROP_INVALID; 269 int temp; 270 271 272 while (1) 273 { 274 temp = peek_char_no_whitespace (sgf_fd); 275 276 if (temp == -1 || temp == ')' || temp == '(' || temp == ';') 277 { 278 return; 279 } 280 else if (temp == '[') 281 { 282 handle_prop_value (temp_type); 283 } 284 else 285 { 286 start_of_prop = rb->lseek (sgf_fd, 0, SEEK_CUR); 287 temp_type = parse_prop_type (); 288 } 289 } 290} 291 292static enum prop_type_t 293parse_prop_type (void) 294{ 295 char buffer[3]; 296 int pos = 0; 297 int temp; 298 299 rb->memset (buffer, 0, sizeof (buffer)); 300 301 while (1) 302 { 303 temp = peek_char_no_whitespace (sgf_fd); 304 305 if (temp == ';' || temp == '[' || temp == '(' || 306 temp == -1 || temp == ')') 307 { 308 if (pos == 1 || pos == 2) 309 { 310 break; 311 } 312 else 313 { 314 return PROP_INVALID; 315 } 316 } 317 else if (temp >= 'A' && temp <= 'Z') 318 { 319 buffer[pos++] = temp; 320 321 if (pos == 2) 322 { 323 read_char_no_whitespace (sgf_fd); 324 break; 325 } 326 } 327 328 temp = read_char_no_whitespace (sgf_fd); 329 } 330 331 /* check if we're still reading a prop name, in which case we fail 332 (but first we want to eat up the rest of the prop name) */ 333 bool failed = false; 334 while (peek_char_no_whitespace (sgf_fd) != ';' && 335 peek_char_no_whitespace (sgf_fd) != '[' && 336 peek_char_no_whitespace (sgf_fd) != '(' && 337 peek_char_no_whitespace (sgf_fd) != '}' && 338 peek_char_no_whitespace (sgf_fd) != -1) 339 { 340 failed = true; 341 read_char_no_whitespace (sgf_fd); 342 } 343 344 if (failed) 345 { 346 return PROP_INVALID; 347 } 348 349 int i; 350 for (i = 0; i < PROP_NAMES_SIZE; ++i) 351 { 352 if (rb->strcmp (buffer, prop_names[i]) == 0) 353 { 354 return (enum prop_type_t) i; 355 } 356 } 357 return PROP_INVALID; 358} 359 360static int 361read_prop_value (char *buffer, size_t buffer_size) 362{ 363 bool escaped = false; 364 int temp; 365 int bytes_read = 0; 366 367 /* make it a string, the lazy way */ 368 rb->memset (buffer, 0, buffer_size); 369 --buffer_size; 370 371 if (peek_char (sgf_fd) == '[') 372 { 373 read_char (sgf_fd); 374 } 375 376 while (1) 377 { 378 temp = read_char (sgf_fd); 379 if (temp == ']' && !escaped) 380 { 381 return bytes_read; 382 } 383 else if (temp == '\\') 384 { 385 if (escaped) 386 { 387 if (buffer && buffer_size) 388 { 389 *(buffer++) = temp; 390 ++bytes_read; 391 --buffer_size; 392 } 393 } 394 escaped = !escaped; 395 } 396 else if (temp == -1) 397 { 398 return bytes_read; 399 } 400 else 401 { 402 escaped = false; 403 if (buffer && buffer_size) 404 { 405 *(buffer++) = temp; 406 ++bytes_read; 407 --buffer_size; 408 } 409 } 410 } 411} 412 413static void 414handle_prop_value (enum prop_type_t type) 415{ 416 /* max size of generically supported prop values is 6, which is 5 for 417 a point range ab:cd and one for the \0 418 419 (this buffer is only used for them, things such as white and black 420 player names are stored in different buffers) */ 421 422 /* make it a little bigger for other random crap, like reading in time 423 */ 424#define PROP_HANDLER_BUFFER_SIZE 16 425 426 char real_buffer[PROP_HANDLER_BUFFER_SIZE]; 427 char *buffer = real_buffer; 428 429 int temp; 430 union prop_data_t temp_data; 431 bool in_prop_value = false; 432 bool escaped = false; 433 bool done = false; 434 int temp_width, temp_height; 435 unsigned short temp_pos_ul, temp_pos_br; 436 int temp_size; 437 char *temp_buffer; 438 bool got_value; 439 440 /* special extra handling for root properties, set a marker telling us 441 the right place to spit the values out in output_sgf */ 442 if (type == PROP_GAME || 443 type == PROP_APPLICATION || 444 type == PROP_CHARSET || 445 type == PROP_SIZE || 446 type == PROP_FILE_FORMAT || type == PROP_VARIATION_TYPE) 447 { 448 header_marked = true; 449 450 temp_data.number = 0; /* meaningless */ 451 452 /* don't add more than one, so just set it if we found one already 453 */ 454 add_or_set_prop_sgf (current_node, PROP_ROOT_PROPS, temp_data); 455 } 456 457 458 if (!is_handled_sgf (type) || type == PROP_COMMENT) 459 { 460 /* DEBUGF("unhandled prop %d\n", (int) type); */ 461 rb->lseek (sgf_fd, start_of_prop, SEEK_SET); 462 463 temp_data.number = rb->lseek (unhandled_fd, 0, SEEK_CUR); 464 /* absolute_position(&unhandled_prop_list); */ 465 466 add_prop_sgf (current_node, 467 type == PROP_COMMENT ? PROP_COMMENT : 468 PROP_GENERIC_UNHANDLED, temp_data); 469 470 got_value = false; 471 while (!done) 472 { 473 temp = peek_char (sgf_fd); 474 475 switch (temp) 476 { 477 case -1: 478 done = true; 479 break; 480 481 case '\\': 482 if (got_value && !in_prop_value) 483 { 484 done = true; 485 } 486 escaped = !escaped; 487 break; 488 case '[': 489 escaped = false; 490 in_prop_value = true; 491 got_value = true; 492 break; 493 case ']': 494 if (!escaped) 495 { 496 in_prop_value = false; 497 } 498 escaped = false; 499 break; 500 case ')': 501 case '(': 502 case ';': 503 if (!in_prop_value) 504 { 505 done = true; 506 } 507 escaped = false; 508 break; 509 default: 510 if (got_value && !in_prop_value) 511 { 512 if (!is_whitespace (temp)) 513 { 514 done = true; 515 } 516 } 517 escaped = false; 518 break; 519 }; 520 521 if (done) 522 { 523 write_char (unhandled_fd, ';'); 524 } 525 else 526 { 527 /* don't write out-of-prop whitespace */ 528 if (in_prop_value || !is_whitespace (temp)) 529 { 530 write_char (unhandled_fd, (char) temp); 531 } 532 533 read_char (sgf_fd); 534 } 535 } 536 537 538 return; 539 } 540 else if (type == PROP_BLACK_MOVE || type == PROP_WHITE_MOVE) 541 { 542 /* DEBUGF("move prop %d\n", (int) type); */ 543 544 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 545 546 temp_data.position = INVALID_POS; 547 548 /* empty is apparently acceptable as a pass */ 549 if (temp == 0) 550 { 551 temp_data.position = PASS_POS; 552 } 553 else if (temp == 2) 554 { 555 temp_data.position = sgf_to_pos (buffer); 556 } 557 else 558 { 559 DEBUGF ("invalid move position read in, of wrong size!\n"); 560 } 561 562 563 if (temp_data.position != INVALID_POS) 564 { 565 add_prop_sgf (current_node, type, temp_data); 566 } 567 568 return; 569 } 570 else if (type == PROP_ADD_BLACK || 571 type == PROP_ADD_WHITE || 572 type == PROP_ADD_EMPTY || 573 type == PROP_CIRCLE || 574 type == PROP_SQUARE || 575 type == PROP_TRIANGLE || 576 type == PROP_DIM || type == PROP_MARK || type == PROP_SELECTED) 577 { 578 /* DEBUGF("add prop %d\n", (int) type); */ 579 580 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 581 if (temp == 2) 582 { 583 temp_data.position = sgf_to_pos (buffer); 584 585 if (temp_data.position != INVALID_POS && 586 temp_data.position != PASS_POS) 587 { 588 add_prop_sgf (current_node, type, temp_data); 589 } 590 } 591 else if (temp == 5) 592 { 593 /* example: "ab:cd", two positions separated by a colon */ 594 temp_pos_ul = sgf_to_pos (buffer); 595 temp_pos_br = sgf_to_pos (&(buffer[3])); 596 597 if (!on_board (temp_pos_ul) || !on_board (temp_pos_br) || 598 buffer[2] != ':') 599 { 600 DEBUGF ("invalid range value!\n"); 601 } 602 603 do_range (type, temp_pos_ul, temp_pos_br); 604 } 605 else 606 { 607 DEBUGF ("invalid position or range read in. wrong size!\n"); 608 } 609 return; 610 } 611 else if (type == PROP_LABEL) 612 { 613 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 614 615 if (temp < 4 || buffer[2] != ':') 616 { 617 DEBUGF ("invalid LaBel property '%s'", buffer); 618 } 619 temp_data.position = sgf_to_pos (buffer); 620 621 if (!on_board (temp_data.position)) 622 { 623 DEBUGF ("LaBel set on invalid position!\n"); 624 } 625 626 temp_data.label_extra = buffer[3]; 627 628 add_prop_sgf (current_node, type, temp_data); 629 return; 630 } 631 else if (type == PROP_GAME) 632 { 633 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 634 if (temp != 1 || buffer[0] != '1') 635 { 636 rb->splash (2 * HZ, "This isn't a Go SGF!! Parsing stopped."); 637 DEBUGF ("incorrect game type loaded!\n"); 638 639 close_file (&sgf_fd); 640 } 641 } 642 else if (type == PROP_FILE_FORMAT) 643 { 644 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 645 if (temp != 1 || (buffer[0] != '3' && buffer[0] != '4')) 646 { 647 rb->splash (2 * HZ, "Wrong SGF file version! Parsing stopped."); 648 DEBUGF ("can't handle file format %c\n", buffer[0]); 649 650 close_file (&sgf_fd); 651 } 652 } 653 else if (type == PROP_APPLICATION || 654 type == PROP_CHARSET || type == PROP_VARIATION_TYPE) 655 { 656 /* we don't care. on output we'll write our own values for these */ 657 read_prop_value (NULL, 0); 658 } 659 else if (type == PROP_SIZE) 660 { 661 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 662 if (temp == 0) 663 { 664 rb->splash (HZ, "Invalid board size specified in file."); 665 } 666 else 667 { 668 temp_width = rb->atoi (buffer); 669 while (*buffer != ':' && *buffer != '\0') 670 { 671 ++buffer; 672 } 673 674 if (*buffer != '\0') 675 { 676 ++buffer; 677 temp_height = rb->atoi (buffer); 678 } 679 else 680 { 681 temp_height = temp_width; 682 } 683 684 685 if (!set_size_board (temp_width, temp_height)) 686 { 687 rb->splashf (HZ, 688 "Board too big/small! (%dx%d) Stopping parse.", 689 temp_width, temp_height); 690 close_file (&sgf_fd); 691 } 692 else 693 { 694 clear_board (); 695 } 696 } 697 } 698 else if (type == PROP_KOMI) 699 { 700 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 701 702 if (temp == 0) 703 { 704 header.komi = 0; 705 DEBUGF ("invalid komi specification. setting to zero\n"); 706 } 707 else 708 { 709 header.komi = rb->atoi (buffer) << 1; 710 while (*buffer != '.' && *buffer != ',' && *buffer != '\0') 711 { 712 ++buffer; 713 } 714 715 if (*buffer != '\0') 716 { 717 ++buffer; 718 719 if (*buffer == 0) 720 { 721 /* do nothing */ 722 } 723 else if (*buffer >= '1' && *buffer <= '9') 724 { 725 header.komi += 1; 726 } 727 else 728 { 729 if (*buffer != '0') 730 { 731 DEBUGF ("extra characters after komi value!\n"); 732 } 733 } 734 } 735 } 736 } 737 else if (type == PROP_BLACK_NAME || 738 type == PROP_WHITE_NAME || 739 type == PROP_BLACK_RANK || 740 type == PROP_WHITE_RANK || 741 type == PROP_BLACK_TEAM || 742 type == PROP_WHITE_TEAM || 743 type == PROP_DATE || 744 type == PROP_ROUND || 745 type == PROP_EVENT || 746 type == PROP_PLACE || 747 type == PROP_OVERTIME || 748 type == PROP_RESULT || type == PROP_RULESET) 749 { 750 if (!get_header_string_and_size 751 (&header, type, &temp_buffer, &temp_size)) 752 { 753 rb->splash (5 * HZ, 754 "Error getting header string. Report this."); 755 } 756 else 757 { 758 temp = read_prop_value (temp_buffer, temp_size - 1); 759#if 0 760 DEBUGF ("read %d bytes into header for type: %d\n", temp, type); 761 DEBUGF ("data: %s\n", temp_buffer); 762#endif 763 } 764 } 765 else if (type == PROP_TIME_LIMIT) 766 { 767 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 768 header.time_limit = rb->atoi (buffer); 769 DEBUGF ("setting time: %d (%s)\n", header.time_limit, buffer); 770 } 771 else if (type == PROP_HANDICAP) 772 { 773 temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE); 774 if (start_node == tree_head) 775 { 776 if (rb->atoi (buffer) >= 2) 777 { 778 start_node = current_node; 779 temp_data.number = header.handicap = rb->atoi (buffer); 780 add_prop_sgf (current_node, type, temp_data); 781 DEBUGF ("setting handicap: %d\n", header.handicap); 782 } 783 else 784 { 785 DEBUGF ("invalid HAndicap prop. ignoring\n"); 786 } 787 } 788 else 789 { 790 rb->splash (HZ, "extraneous HAndicap prop present in file!\n"); 791 } 792 } 793 else 794 { 795 DEBUGF ("UNHANDLED PROP TYPE!!!\n"); 796 rb->splash (3 * HZ, 797 "A SGF prop was not dealt with. Please report this"); 798 read_prop_value (NULL, 0); 799 } 800} 801 802 803 804/* upper-left and bottom right */ 805static void 806do_range (enum prop_type_t type, unsigned short ul, unsigned short br) 807{ 808 /* this code is overly general and accepts ranges even if ul and br 809 aren't the required corners it's easier doing that that failing if 810 the input is bad */ 811 812 bool x_reverse = false; 813 bool y_reverse = false; 814 union prop_data_t temp_data; 815 816 if (I (br) < I (ul)) 817 { 818 x_reverse = true; 819 } 820 821 if (J (br) < J (ul)) 822 { 823 y_reverse = true; 824 } 825 826 int x, y; 827 for (x = I (ul); 828 x_reverse ? (x >= I (br)) : (x <= I (br)); x_reverse ? --x : ++x) 829 { 830 for (y = J (ul); 831 y_reverse ? (y >= J (br)) : (y <= J (br)); y_reverse ? --y : ++y) 832 { 833 temp_data.position = POS (x, y); 834 835 DEBUGF ("adding %d %d for range (type %d)\n", 836 I (temp_data.position), J (temp_data.position), type); 837 add_prop_sgf (current_node, type, temp_data); 838 } 839 } 840} 841 842 843 844static unsigned short 845sgf_to_pos (char *buffer) 846{ 847 if (buffer[0] == 't' && buffer[1] == 't') 848 { 849 return PASS_POS; 850 } 851 else if (buffer[0] < 'a' || buffer[0] > 'z' || 852 buffer[1] < 'a' || buffer[1] > 'z') 853 { 854 return INVALID_POS; 855 } 856 return POS (buffer[0] - 'a', buffer[1] - 'a'); 857} 858