A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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}