summaryrefslogtreecommitdiff
path: root/apps/plugins/goban/sgf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/goban/sgf.c')
-rw-r--r--apps/plugins/goban/sgf.c2237
1 files changed, 2237 insertions, 0 deletions
diff --git a/apps/plugins/goban/sgf.c b/apps/plugins/goban/sgf.c
new file mode 100644
index 0000000000..ad6e4a4e05
--- /dev/null
+++ b/apps/plugins/goban/sgf.c
@@ -0,0 +1,2237 @@
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 {
793 comment_buffer[i] = ' ';
794 }
795 else if (comment_buffer[i] == '\0')
796 {
797 break;
798 }
799 }
800 draw_screen_display();
801 rb->splash(HZ / 3, comment_buffer);
802 rb->button_clear_queue();
803 rb->action_userabort(TIMEOUT_BLOCK);
804 }
805#else
806 (void) comment_buffer;
807#endif
808
809 return true;
810}
811
812int
813mark_child_variations_sgf (void)
814{
815 int result;
816 int saved = current_node;
817 struct node_t *node = get_node (current_node);
818
819 int move_handle;
820
821 if (!node)
822 {
823 return 0;
824 }
825
826 current_node = node->next;
827 goto_next_important_node (true);
828
829 result = num_variations_sgf ();
830
831 if (result > 1)
832 {
833 int i;
834 int branch_node = current_node;
835 for (i = 0; i < result; ++i)
836 {
837 go_to_variation_sgf (i);
838 goto_next_important_node (true);
839
840 move_handle = get_move_sgf ();
841
842 if (move_handle >= 0)
843 {
844 set_one_mark (get_prop (move_handle)->data.position,
845 get_prop (move_handle)->type);
846 }
847
848 current_node = branch_node;
849 }
850 }
851
852 current_node = saved;
853
854 return result;
855}
856
857void
858set_all_marks_sgf (void)
859{
860 struct prop_t *prop = get_prop (get_node (current_node)->props);
861
862 while (prop)
863 {
864 if (prop->type == PROP_LABEL)
865 {
866 set_label_mark (prop->data.position, prop->data.label_extra);
867 }
868 else if (prop->type == PROP_COMMENT)
869 {
870 set_comment_display (true);
871 }
872 else
873 {
874 set_one_mark (prop->data.position, prop->type);
875 }
876 prop = get_prop (prop->next);
877 }
878}
879
880static void
881set_one_mark (unsigned short pos, enum prop_type_t type)
882{
883 switch (type)
884 {
885 case PROP_CIRCLE:
886 set_mark_display (pos, 'c');
887 break;
888 case PROP_SQUARE:
889 set_mark_display (pos, 's');
890 break;
891 case PROP_TRIANGLE:
892 set_mark_display (pos, 't');
893 break;
894 case PROP_MARK:
895 set_mark_display (pos, 'm');
896 break;
897 case PROP_DIM:
898 set_mark_display (pos, 'd');
899 break;
900 case PROP_SELECTED:
901 set_mark_display (pos, 'S');
902 break;
903 case PROP_BLACK_MOVE:
904 set_mark_display (pos, 'b');
905 break;
906 case PROP_WHITE_MOVE:
907 set_mark_display (pos, 'w');
908 break;
909 case PROP_INVALID:
910 set_mark_display (pos, ' ');
911 default:
912 break;
913 }
914}
915
916static void
917set_label_mark (unsigned short pos, char to_set)
918{
919 set_mark_display (pos, to_set | (1 << 7));
920}
921
922static bool
923do_add_stones (void)
924{
925 bool ret_val = false;
926 struct prop_t *temp_prop;
927 int temp_handle;
928
929 if (current_node < 0)
930 {
931 return false;
932 }
933
934 temp_handle = get_node (current_node)->props;
935 temp_prop = get_prop (temp_handle);
936
937 while (temp_prop)
938 {
939 if (temp_prop->type == PROP_ADD_BLACK ||
940 temp_prop->type == PROP_ADD_WHITE ||
941 temp_prop->type == PROP_ADD_EMPTY)
942 {
943
944 temp_prop->data.stone_extra = 0;
945
946 /* TODO: we could delete do-nothing PROP_ADD_*s here */
947
948 if (get_point_board (temp_prop->data.position) == EMPTY)
949 {
950 temp_prop->data.stone_extra |= FLAG_ORIG_EMPTY;
951 }
952 else if (get_point_board (temp_prop->data.position) == BLACK)
953 {
954 temp_prop->data.stone_extra |= FLAG_ORIG_BLACK;
955 }
956 /* else, do nothing */
957
958 if (temp_prop->type == PROP_ADD_BLACK ||
959 temp_prop->type == PROP_ADD_WHITE)
960 {
961 set_point_board (temp_prop->data.position,
962 temp_prop->type == PROP_ADD_BLACK ? BLACK :
963 WHITE);
964 ret_val = true;
965 }
966 else
967 {
968 set_point_board (temp_prop->data.position, EMPTY);
969
970 ret_val = true;
971 }
972 }
973
974 temp_handle = temp_prop->next;
975 temp_prop = get_prop (temp_handle);
976 }
977
978 return ret_val;
979}
980
981int
982add_child_sgf (int *variation_number)
983{
984 int node_handle;
985 struct node_t *node;
986 struct node_t *current = get_node (current_node);
987
988 if (current->next < 0)
989 {
990 node_handle = alloc_storage_sgf ();
991 node = get_node (node_handle);
992
993 if (node_handle < 0 || !current)
994 {
995 return NO_NODE;
996 }
997
998 node->prev = current_node;
999 node->next = NO_NODE;
1000 node->props = NO_PROP;
1001
1002 current->next = node_handle;
1003
1004 if (variation_number)
1005 {
1006 *variation_number = 0;
1007 }
1008
1009 return node_handle;
1010 }
1011 else
1012 {
1013 return add_child_variation (variation_number);
1014 }
1015}
1016
1017int
1018add_prop_sgf (int node, enum prop_type_t type, union prop_data_t data)
1019{
1020 int new_prop;
1021 int temp_prop_handle;
1022
1023 if (node < 0)
1024 {
1025 return NO_PROP;
1026 }
1027
1028 new_prop = alloc_storage_sgf ();
1029
1030 if (new_prop < 0)
1031 {
1032 return NO_PROP;
1033 }
1034
1035 get_prop (new_prop)->type = type;
1036 get_prop (new_prop)->data = data;
1037
1038 /* check if the new_prop goes at the start */
1039 if (get_node (node)->props == NO_PROP ||
1040 (type == PROP_VARIATION &&
1041 get_prop (get_node (node)->props)->type != PROP_VARIATION))
1042 {
1043
1044 if (get_node (node)->props >= 0)
1045 {
1046 get_prop (new_prop)->next = get_node (node)->props;
1047 }
1048 else
1049 {
1050 get_prop (new_prop)->next = NO_PROP;
1051 }
1052
1053 get_node (node)->props = new_prop;
1054 return new_prop;
1055 }
1056
1057 temp_prop_handle = get_node (node)->props;
1058
1059 while (1)
1060 {
1061 if (get_prop (temp_prop_handle)->next < 0 ||
1062 (get_prop (temp_prop_handle)->type == type &&
1063 get_prop (get_prop (temp_prop_handle)->next)->type != type))
1064 {
1065 /* new_prop goes after the current one either because we're at
1066 the end of the props list, or because we're adding a prop
1067 after the ones of its same type */
1068 get_prop (new_prop)->next = get_prop (temp_prop_handle)->next;
1069 get_prop (temp_prop_handle)->next = new_prop;
1070
1071 return new_prop;
1072 }
1073
1074 temp_prop_handle = get_prop (temp_prop_handle)->next;
1075 }
1076}
1077
1078int
1079get_prop_pos_sgf (enum prop_type_t type, union prop_data_t data)
1080{
1081 int temp_prop_handle;
1082 struct prop_t *prop;
1083
1084 if (current_node < 0)
1085 {
1086 return -1;
1087 }
1088
1089 temp_prop_handle = get_node (current_node)->props;
1090
1091 while (temp_prop_handle >= 0)
1092 {
1093 prop = get_prop (temp_prop_handle);
1094
1095 if ((type == PROP_ADD_BLACK ||
1096 type == PROP_ADD_WHITE ||
1097 type == PROP_ADD_EMPTY)
1098 &&
1099 (prop->type == PROP_ADD_BLACK ||
1100 prop->type == PROP_ADD_WHITE ||
1101 prop->type == PROP_ADD_EMPTY)
1102 && (prop->data.position == data.position))
1103 {
1104 return temp_prop_handle;
1105 }
1106
1107 if ((type == PROP_CIRCLE ||
1108 type == PROP_SQUARE ||
1109 type == PROP_TRIANGLE ||
1110 type == PROP_MARK ||
1111 type == PROP_DIM ||
1112 type == PROP_SELECTED) &&
1113 (prop->type == PROP_CIRCLE ||
1114 prop->type == PROP_SQUARE ||
1115 prop->type == PROP_TRIANGLE ||
1116 prop->type == PROP_MARK ||
1117 prop->type == PROP_DIM ||
1118 prop->type == PROP_SELECTED) &&
1119 (prop->data.position == data.position))
1120 {
1121 return temp_prop_handle;
1122 }
1123
1124 temp_prop_handle = get_prop (temp_prop_handle)->next;
1125 }
1126
1127 return -1;
1128}
1129
1130int
1131add_or_set_prop_sgf (int node, enum prop_type_t type, union prop_data_t data)
1132{
1133 int temp_prop_handle;
1134
1135 if (node < 0)
1136 {
1137 return NO_PROP;
1138 }
1139
1140 temp_prop_handle = get_prop_sgf (node, type, NULL);
1141
1142 if (temp_prop_handle >= 0)
1143 {
1144 get_prop (temp_prop_handle)->data = data;
1145 return temp_prop_handle;
1146 }
1147
1148 /* there was no prop to set, so we need to add one */
1149 return add_prop_sgf (node, type, data);
1150}
1151
1152int
1153get_prop_sgf (int node, enum prop_type_t type, int *previous_prop)
1154{
1155 int previous_handle = NO_PROP;
1156 int current_handle;
1157
1158 if (node < 0)
1159 {
1160 return NO_PROP;
1161 }
1162
1163 if (get_node (node)->props < 0)
1164 {
1165 return NO_PROP;
1166 }
1167
1168 current_handle = get_node (node)->props;
1169
1170 while (current_handle >= 0)
1171 {
1172 if (get_prop (current_handle)->type == type || type == PROP_ANY)
1173 {
1174 if (previous_prop)
1175 {
1176 *previous_prop = previous_handle;
1177 }
1178
1179 return current_handle;
1180 }
1181 else
1182 {
1183 previous_handle = current_handle;
1184 current_handle = get_prop (current_handle)->next;
1185 }
1186 }
1187
1188 return NO_PROP;
1189}
1190
1191bool
1192delete_prop_sgf (int node, enum prop_type_t type)
1193{
1194 if (node < 0)
1195 {
1196 return false;
1197 }
1198
1199 return delete_prop_handle_sgf (node, get_prop_sgf (node, type, NULL));
1200}
1201
1202bool
1203delete_prop_handle_sgf (int node, int prop)
1204{
1205 int previous;
1206
1207 if (prop < 0 || node < 0 || get_node (node)->props < 0)
1208 {
1209 return false;
1210 }
1211
1212 if (get_node (node)->props == prop)
1213 {
1214 get_node (node)->props = get_prop (get_node (node)->props)->next;
1215 free_storage_sgf (prop);
1216 return true;
1217 }
1218
1219 previous = get_node (node)->props;
1220
1221 while (get_prop (previous)->next != prop && get_prop (previous)->next >= 0)
1222 {
1223 previous = get_prop (previous)->next;
1224 }
1225
1226 if (get_prop (previous)->next < 0)
1227 {
1228 return false;
1229 }
1230 else
1231 {
1232 get_prop (previous)->next = get_prop (get_prop (previous)->next)->next;
1233 free_storage_sgf (prop);
1234 return true;
1235 }
1236}
1237
1238int
1239read_comment_sgf (char *buffer, size_t buffer_size)
1240{
1241 size_t bytes_read = 0;
1242
1243 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL);
1244
1245 if (prop_handle < 0)
1246 {
1247 return 0;
1248 }
1249
1250 if (unhandled_fd < 0)
1251 {
1252 DEBUGF ("unhandled file is closed?!\n");
1253 return 0;
1254 }
1255
1256 if (rb->lseek (unhandled_fd, get_prop (prop_handle)->data.number,
1257 SEEK_SET) < 0)
1258 {
1259 DEBUGF ("couldn't seek in unhandled_fd\n");
1260 return -1;
1261 }
1262
1263 if (!read_char_no_whitespace (unhandled_fd) == 'C' ||
1264 !read_char_no_whitespace (unhandled_fd) == '[')
1265 {
1266 DEBUGF ("comment prop points to incorrect place in unhandled_fd!!\n");
1267 return -1;
1268 }
1269
1270 /* make output a string, the lazy way */
1271 rb->memset (buffer, 0, buffer_size);
1272 ++bytes_read;
1273
1274 bool done = false;
1275 bool escaped = false;
1276
1277 while ((buffer_size > bytes_read) && !done)
1278 {
1279 int temp = read_char (unhandled_fd);
1280
1281 switch (temp)
1282 {
1283 case ']':
1284 if (!escaped)
1285 {
1286 done = true;
1287 break;
1288 }
1289 *buffer = temp;
1290 ++buffer;
1291 ++bytes_read;
1292 escaped = false;
1293 break;
1294
1295 case -1:
1296 DEBUGF ("encountered end of file before end of comment!\n");
1297 done = true;
1298 break;
1299
1300 case '\\':
1301 escaped = !escaped;
1302 if (escaped)
1303 {
1304 break;
1305 }
1306
1307 default:
1308 *buffer = temp;
1309 ++buffer;
1310 ++bytes_read;
1311 escaped = false;
1312 break;
1313 }
1314 }
1315
1316 return bytes_read;
1317}
1318
1319int
1320write_comment_sgf (char *string)
1321{
1322 char *orig_string = string;
1323
1324 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL);
1325
1326 int start_of_comment = -1;
1327 bool overwriting = false;
1328
1329 int bytes_written = 0;
1330
1331 set_game_modified();
1332
1333 if (unhandled_fd < 0)
1334 {
1335 DEBUGF ("unhandled file is closed?!\n");
1336 return 0;
1337 }
1338
1339 if (prop_handle >= 0)
1340 {
1341 if ((start_of_comment = rb->lseek (unhandled_fd,
1342 get_prop (prop_handle)->data.number,
1343 SEEK_SET)) < 0)
1344 {
1345 DEBUGF ("couldn't seek in unhandled_fd\n");
1346 return -1;
1347 }
1348 else
1349 {
1350 overwriting = true;
1351 }
1352 }
1353 else
1354 {
1355 overwriting = false;
1356 }
1357
1358 start_of_write_wcs:
1359
1360 if (overwriting)
1361 {
1362 if (!read_char_no_whitespace (unhandled_fd) == 'C' ||
1363 !read_char_no_whitespace (unhandled_fd) == '[')
1364 {
1365 DEBUGF ("non-comment while overwriting!!\n");
1366 return -1;
1367 }
1368 }
1369 else
1370 {
1371 start_of_comment = rb->lseek (unhandled_fd, 0, SEEK_END);
1372
1373 if (start_of_comment < 0)
1374 {
1375 DEBUGF ("error seeking to end in write_comment_sgf\n");
1376 return -1;
1377 }
1378
1379 write_char (unhandled_fd, 'C');
1380 write_char (unhandled_fd, '[');
1381 }
1382
1383
1384 bool overwrite_escaped = false;
1385
1386 while (*string)
1387 {
1388 if (overwriting)
1389 {
1390 int orig_char = peek_char (unhandled_fd);
1391
1392 switch (orig_char)
1393 {
1394 case '\\':
1395 overwrite_escaped = !overwrite_escaped;
1396 break;
1397
1398 case ']':
1399 if (overwrite_escaped)
1400 {
1401 overwrite_escaped = false;
1402 break;
1403 }
1404 /* otherwise, fall through */
1405
1406 case -1:
1407
1408 /* we reached the end of the part we can put our comment
1409 in, but there's more comment to write, so we should
1410 start again, this time making a new comment (the old
1411 becomes wasted space in unhandled_fd, but it doesn't
1412 really hurt anything except extra space on disk */
1413
1414 overwriting = false;
1415 string = orig_string;
1416 bytes_written = 0;
1417 goto start_of_write_wcs;
1418 break;
1419
1420 default:
1421 overwrite_escaped = false;
1422 break;
1423 }
1424 }
1425
1426 switch (*string)
1427 {
1428 case '\\':
1429 case ']':
1430 write_char (unhandled_fd, '\\');
1431
1432 /* fall through */
1433
1434 default:
1435 write_char (unhandled_fd, *string);
1436 break;
1437 }
1438
1439 ++string;
1440 ++bytes_written;
1441 }
1442
1443 /* finish out the record */
1444 write_char (unhandled_fd, ']');
1445 write_char (unhandled_fd, ';');
1446
1447 /* and put the reference into the unhandled_fd into the comment prop */
1448 union prop_data_t temp_data;
1449 temp_data.number = start_of_comment;
1450
1451 add_or_set_prop_sgf (current_node, PROP_COMMENT, temp_data);
1452 set_comment_display (true);
1453 return bytes_written;
1454}
1455
1456bool
1457has_more_nodes_sgf (void)
1458{
1459 int saved = current_node;
1460 bool ret_val = false;
1461
1462 if (current_node < 0)
1463 {
1464 return false;
1465 }
1466
1467 current_node = get_node (current_node)->next;
1468
1469 if (current_node >= 0)
1470 {
1471 /* returns true if it finds an important node */
1472 ret_val = goto_next_important_node (true);
1473 }
1474
1475 current_node = saved;
1476 return ret_val;
1477}
1478
1479/* logic is different here because the first node in a tree is a valid
1480 place to go */
1481bool
1482has_prev_nodes_sgf (void)
1483{
1484 if (current_node < 0)
1485 {
1486 return false;
1487 }
1488
1489 return current_node != start_node;
1490}
1491
1492int
1493get_move_sgf (void)
1494{
1495 int result = -1;
1496 int saved_current = current_node;
1497
1498 goto_next_important_node (true);
1499
1500 result = get_move_from_node (current_node);
1501
1502 current_node = saved_current;
1503 return result;
1504}
1505
1506static int
1507get_move_from_node (int handle)
1508{
1509 int prop_handle;
1510
1511 if (handle < 0)
1512 {
1513 return -2;
1514 }
1515
1516 prop_handle = get_node (handle)->props;
1517
1518
1519 while (prop_handle >= 0)
1520 {
1521 if (get_prop (prop_handle)->type == PROP_BLACK_MOVE ||
1522 get_prop (prop_handle)->type == PROP_WHITE_MOVE)
1523 {
1524 return prop_handle;
1525 }
1526
1527 prop_handle = get_prop (prop_handle)->next;
1528 }
1529
1530 return -1;
1531}
1532
1533static bool
1534retreat_node (void)
1535{
1536 int result = get_node (current_node)->prev;
1537
1538 if (current_node == start_node)
1539 {
1540 return false;
1541 }
1542
1543 if (result < 0)
1544 {
1545 return false;
1546 }
1547 else
1548 {
1549 clear_marks_display ();
1550
1551 current_node = result;
1552
1553 /* go backwards to the next important node (move/add
1554 stone/variation/etc.) */
1555 goto_next_important_node (false);
1556 return true;
1557 }
1558}
1559
1560static bool
1561advance_node (void)
1562{
1563 int result = get_node (current_node)->next;
1564
1565 if (result < 0)
1566 {
1567 return false;
1568 }
1569 else
1570 {
1571 clear_marks_display ();
1572
1573 current_node = result;
1574
1575 /* go forward to the next move/add stone/variation/etc. node */
1576 goto_next_important_node (true);
1577 result = get_prop_sgf (current_node, PROP_VARIATION_CHOICE, NULL);
1578
1579 if (result >= 0)
1580 {
1581 go_to_variation_sgf (get_prop (result)->data.number);
1582 }
1583
1584 return true;
1585 }
1586}
1587
1588int
1589num_variations_sgf (void)
1590{
1591 int result = 1;
1592 struct prop_t *temp_prop;
1593 struct node_t *temp_node = get_node (current_node);
1594
1595 if (temp_node == 0)
1596 {
1597 return 0;
1598 }
1599
1600 if (temp_node->prev >= 0)
1601 {
1602 temp_node = get_node (get_node (temp_node->prev)->next);
1603 }
1604
1605 temp_prop = get_prop (temp_node->props);
1606
1607 while (temp_prop)
1608 {
1609 if (temp_prop->type == PROP_VARIATION)
1610 {
1611 ++result;
1612 }
1613 else
1614 {
1615 /* variations are at the beginning of the prop list */
1616 break;
1617 }
1618
1619 temp_prop = get_prop (temp_prop->next);
1620 }
1621
1622 return result;
1623}
1624
1625bool
1626go_to_variation_sgf (unsigned int num)
1627{
1628 int saved = current_node;
1629 struct node_t *temp_node = get_node (current_node);
1630 struct prop_t *temp_prop;
1631
1632 if (!temp_node)
1633 {
1634 return false;
1635 }
1636
1637 temp_node = get_node (temp_node->prev);
1638
1639 if (!temp_node)
1640 {
1641 return false;
1642 }
1643
1644 temp_node = get_node (current_node = temp_node->next);
1645
1646 if (!temp_node)
1647 {
1648 current_node = saved;
1649 return false;
1650 }
1651
1652 temp_prop = get_prop (temp_node->props);
1653
1654 while (num)
1655 {
1656 if (!temp_prop || temp_prop->type != PROP_VARIATION)
1657 {
1658 current_node = saved;
1659 return false;
1660 }
1661
1662 if (num == 1)
1663 {
1664 current_node = temp_prop->data.node;
1665 break;
1666 }
1667
1668 temp_prop = get_prop (temp_prop->next);
1669 --num;
1670 }
1671
1672 return true;
1673}
1674
1675int
1676get_matching_child_sgf (unsigned short pos, unsigned char color)
1677{
1678 struct node_t *temp_node;
1679 struct prop_t *temp_prop;
1680 int variation_count = 0;
1681 int saved;
1682
1683 /* set true later in a loop if we want a prop in the first variation
1684 which means that we shouldn't check that branch for children */
1685 bool dont_check_first_var_children = false;
1686
1687 temp_node = get_node (current_node);
1688
1689 if (!temp_node)
1690 {
1691 return -3;
1692 }
1693
1694 temp_node = get_node (temp_node->next);
1695
1696 if (!temp_node)
1697 {
1698 return -2;
1699 }
1700
1701 temp_prop = get_prop (temp_node->props);
1702
1703 while (temp_prop)
1704 {
1705 if (temp_prop->type == PROP_VARIATION)
1706 {
1707 ++variation_count;
1708 saved = current_node;
1709 current_node = temp_prop->data.node;
1710
1711 struct prop_t *temp_move = get_prop (get_move_sgf ());
1712
1713 current_node = saved;
1714
1715 if (temp_move &&
1716 temp_move->data.position == pos &&
1717 ((color == BLACK && temp_move->type == PROP_BLACK_MOVE) ||
1718 (color == WHITE && temp_move->type == PROP_WHITE_MOVE)))
1719 {
1720 return variation_count;
1721 }
1722 }
1723 else if ((temp_prop->type == PROP_BLACK_MOVE && color == BLACK) ||
1724 (temp_prop->type == PROP_WHITE_MOVE && color == WHITE))
1725 {
1726 if (temp_prop->data.position == pos)
1727 {
1728 return 0;
1729 }
1730 else
1731 {
1732 return -4;
1733 }
1734 }
1735 else if (temp_prop->type == PROP_ADD_WHITE ||
1736 temp_prop->type == PROP_ADD_BLACK ||
1737 temp_prop->type == PROP_ADD_EMPTY)
1738 {
1739 dont_check_first_var_children = true;
1740 }
1741
1742 temp_prop = get_prop (temp_prop->next);
1743 }
1744
1745 if (dont_check_first_var_children)
1746 {
1747 return -1;
1748 }
1749 else
1750 {
1751 saved = current_node;
1752 current_node = temp_node->next;
1753
1754 struct prop_t *temp_move = get_prop (get_move_sgf ());
1755 if (temp_move &&
1756 pos == temp_move->data.position &&
1757 color == (temp_move->type == PROP_BLACK_MOVE ? BLACK : WHITE))
1758 {
1759 current_node = saved;
1760 return 0;
1761 }
1762
1763 current_node = saved;
1764 return -1;
1765 }
1766}
1767
1768int
1769next_variation_sgf (void)
1770{
1771 int saved = current_node;
1772 int saved_start = start_node;
1773 union prop_data_t temp_data;
1774 int prop_handle;
1775 int num_vars = 0;
1776
1777 if (current_node < 0 || get_node (current_node)->prev < 0)
1778 {
1779 return -1;
1780 }
1781
1782 start_node = NO_NODE;
1783
1784
1785
1786 if (num_variations_sgf () < 2)
1787 {
1788
1789 current_node = saved;
1790 start_node = saved_start;
1791 return -2;
1792 }
1793
1794 /* now we're at a branch node which we should go to the next variation
1795 (we were at the "chosen" one, so go to the next one after that,
1796 (mod the number of variations)) */
1797
1798 int chosen = 0;
1799 int branch_node = get_node (get_node (current_node)->prev)->next;
1800
1801 prop_handle = get_prop_sgf (branch_node, PROP_VARIATION_CHOICE, NULL);
1802
1803 if (prop_handle >= 0)
1804 {
1805 chosen = get_prop (prop_handle)->data.number;
1806 }
1807
1808 ++chosen;
1809
1810 if (chosen >= (num_vars = num_variations_sgf ()))
1811 {
1812 chosen = 0;
1813 }
1814
1815 temp_data.number = chosen;
1816 add_or_set_prop_sgf (branch_node, PROP_VARIATION_CHOICE, temp_data);
1817
1818 if (!undo_node_sgf ())
1819 {
1820 current_node = saved;
1821 start_node = saved_start;
1822 return -3;
1823 }
1824
1825 if (redo_node_sgf ())
1826 {
1827 start_node = saved_start;
1828 return chosen + 1;
1829 }
1830 else
1831 {
1832 current_node = saved;
1833 start_node = saved_start;
1834 return -4;
1835 }
1836}
1837
1838int
1839add_child_variation (int *variation_number)
1840{
1841 struct node_t *temp_node = get_node (current_node);
1842 struct prop_t *temp_prop;
1843 int temp_prop_handle;
1844 int new_node = alloc_storage_sgf ();
1845 int new_prop = alloc_storage_sgf ();
1846 int temp_variation_number;
1847
1848 if (new_node < 0 || new_prop < 0)
1849 {
1850 if (new_node >= 0)
1851 {
1852 free_storage_sgf (new_node);
1853 }
1854 if (new_prop >= 0)
1855 {
1856 free_storage_sgf (new_prop);
1857 }
1858
1859 return NO_NODE;
1860 }
1861
1862 if (!temp_node)
1863 {
1864 free_storage_sgf (new_node);
1865 free_storage_sgf (new_prop);
1866
1867 return NO_NODE;
1868 }
1869
1870 temp_node = get_node (temp_node->next);
1871
1872 if (!temp_node)
1873 {
1874 free_storage_sgf (new_node);
1875 free_storage_sgf (new_prop);
1876
1877 return NO_NODE;
1878 }
1879
1880 get_node (new_node)->prev = current_node;
1881 get_node (new_node)->next = NO_NODE;
1882 get_node (new_node)->props = NO_PROP;
1883
1884 get_prop (new_prop)->type = PROP_VARIATION;
1885 get_prop (new_prop)->next = NO_PROP;
1886 get_prop (new_prop)->data.node = new_node;
1887
1888 temp_prop_handle = temp_node->props;
1889
1890 if (temp_prop_handle < 0)
1891 {
1892 temp_node->props = new_prop;
1893
1894 if (variation_number)
1895 {
1896 *variation_number = 1;
1897 }
1898
1899 return new_node;
1900 }
1901
1902 if (get_prop (temp_prop_handle)->type != PROP_VARIATION)
1903 {
1904 get_prop (new_prop)->next = temp_node->props;
1905 temp_node->props = new_prop;
1906
1907 if (variation_number)
1908 {
1909 *variation_number = 1;
1910 }
1911
1912 return new_node;
1913 }
1914
1915 /* the lowest it can be, since 1 isn't it */
1916 temp_variation_number = 2;
1917
1918 while (1)
1919 {
1920 temp_prop = get_prop (temp_prop_handle);
1921 if (temp_prop->next < 0 ||
1922 get_prop (temp_prop->next)->type != PROP_VARIATION)
1923 {
1924 get_prop (new_prop)->next = temp_prop->next;
1925 temp_prop->next = new_prop;
1926
1927 if (variation_number)
1928 {
1929 *variation_number = temp_variation_number;
1930 }
1931
1932 return new_node;
1933 }
1934
1935 ++temp_variation_number;
1936 temp_prop_handle = temp_prop->next;
1937 }
1938}
1939
1940static bool
1941is_important_node (int handle)
1942{
1943 struct prop_t *temp_prop;
1944
1945 if (handle < 0)
1946 {
1947 return false;
1948 }
1949
1950 if (handle == start_node)
1951 {
1952 return true;
1953 }
1954
1955 if (get_node (handle)->prev < 0)
1956 {
1957 return true;
1958 }
1959
1960 temp_prop = get_prop (get_node (handle)->props);
1961
1962 while (temp_prop)
1963 {
1964 if (temp_prop->type == PROP_BLACK_MOVE ||
1965 temp_prop->type == PROP_WHITE_MOVE ||
1966 temp_prop->type == PROP_ADD_BLACK ||
1967 temp_prop->type == PROP_ADD_WHITE ||
1968 temp_prop->type == PROP_ADD_EMPTY ||
1969 temp_prop->type == PROP_VARIATION)
1970 {
1971 return true;
1972 }
1973
1974 temp_prop = get_prop (temp_prop->next);
1975 }
1976
1977 return false;
1978}
1979
1980static bool
1981goto_next_important_node (bool forward)
1982{
1983 int temp_node = current_node;
1984 int last_good = temp_node;
1985
1986 while (temp_node >= 0 && !is_important_node (temp_node))
1987 {
1988 last_good = temp_node;
1989
1990 temp_node = forward ? get_node (temp_node)->next :
1991 get_node (temp_node)->prev;
1992
1993 }
1994
1995 if (temp_node < 0)
1996 {
1997 current_node = last_good;
1998 }
1999 else
2000 {
2001 current_node = temp_node;
2002 }
2003
2004 if (temp_node < 0)
2005 {
2006 return false;
2007 }
2008 else
2009 {
2010 return true;
2011 }
2012}
2013
2014bool
2015is_handled_sgf (enum prop_type_t type)
2016{
2017 if (type == PROP_BLACK_MOVE ||
2018 type == PROP_WHITE_MOVE ||
2019 type == PROP_ADD_BLACK ||
2020 type == PROP_ADD_WHITE ||
2021 type == PROP_ADD_EMPTY ||
2022 type == PROP_CIRCLE || type == PROP_SQUARE || type == PROP_TRIANGLE ||
2023#if 0
2024 /* these marks are stupid and nobody uses them. if we could find
2025 a good way to draw them we could do them anyway, but no reason
2026 to unless it's easy */
2027 type == PROP_DIM || type == PROP_SELECTED ||
2028#endif
2029 type == PROP_COMMENT ||
2030 type == PROP_MARK ||
2031 type == PROP_LABEL ||
2032 type == PROP_GAME ||
2033 type == PROP_FILE_FORMAT ||
2034 type == PROP_APPLICATION ||
2035 type == PROP_CHARSET ||
2036 type == PROP_SIZE ||
2037 type == PROP_KOMI ||
2038 type == PROP_BLACK_NAME ||
2039 type == PROP_WHITE_NAME ||
2040 type == PROP_BLACK_RANK ||
2041 type == PROP_WHITE_RANK ||
2042 type == PROP_BLACK_TEAM ||
2043 type == PROP_WHITE_TEAM ||
2044 type == PROP_DATE ||
2045 type == PROP_ROUND ||
2046 type == PROP_EVENT ||
2047 type == PROP_PLACE ||
2048 type == PROP_OVERTIME ||
2049 type == PROP_RESULT ||
2050 type == PROP_TIME_LIMIT ||
2051 type == PROP_RULESET ||
2052 type == PROP_HANDICAP || type == PROP_VARIATION_TYPE)
2053 {
2054 return true;
2055 }
2056
2057 return false;
2058}
2059
2060void
2061setup_handicap_sgf (void)
2062{
2063 union prop_data_t temp_data;
2064
2065 if (header.handicap <= 1)
2066 {
2067 return;
2068 }
2069
2070 current_node = start_node;
2071
2072 temp_data.number = header.handicap;
2073 add_prop_sgf (current_node, PROP_HANDICAP, temp_data);
2074
2075 /* now, add the actual stones */
2076
2077 if ((board_width != 19 && board_width != 13 && board_width != 9) ||
2078 board_width != board_height || header.handicap > 9)
2079 {
2080 rb->splashf (5 * HZ,
2081 "Use the 'Add Black' tool to add %d handicap stones!",
2082 header.handicap);
2083 return;
2084 }
2085
2086 int handicaps_to_place = header.handicap;
2087
2088 int low_coord = 0, mid_coord = 0, high_coord = 0;
2089
2090 if (board_width == 19)
2091 {
2092 low_coord = 3;
2093 mid_coord = 9;
2094 high_coord = 15;
2095 }
2096 else if (board_width == 13)
2097 {
2098 low_coord = 3;
2099 mid_coord = 6;
2100 high_coord = 9;
2101 }
2102 else if (board_width == 9)
2103 {
2104 low_coord = 2;
2105 mid_coord = 4;
2106 high_coord = 6;
2107 }
2108
2109 /* first four go in the corners */
2110 handicaps_to_place -= 2;
2111 setup_handicap_helper (POS (high_coord, low_coord));
2112 setup_handicap_helper (POS (low_coord, high_coord));
2113
2114 if (!handicaps_to_place)
2115 {
2116 goto done_adding_stones;
2117 }
2118
2119 --handicaps_to_place;
2120 setup_handicap_helper (POS (high_coord, high_coord));
2121
2122 if (!handicaps_to_place)
2123 {
2124 goto done_adding_stones;
2125 }
2126
2127 --handicaps_to_place;
2128 setup_handicap_helper (POS (low_coord, low_coord));
2129
2130 if (!handicaps_to_place)
2131 {
2132 goto done_adding_stones;
2133 }
2134
2135 /* now done with first four, if only one left it goes in the center */
2136 if (handicaps_to_place == 1)
2137 {
2138 --handicaps_to_place;
2139 setup_handicap_helper (POS (mid_coord, mid_coord));
2140 }
2141 else
2142 {
2143 handicaps_to_place -= 2;
2144 setup_handicap_helper (POS (high_coord, mid_coord));
2145 setup_handicap_helper (POS (low_coord, mid_coord));
2146 }
2147
2148 if (!handicaps_to_place)
2149 {
2150 goto done_adding_stones;
2151 }
2152
2153 /* done with first 6 */
2154
2155 if (handicaps_to_place == 1)
2156 {
2157 --handicaps_to_place;
2158 setup_handicap_helper (POS (mid_coord, mid_coord));
2159 }
2160 else
2161 {
2162 handicaps_to_place -= 2;
2163 setup_handicap_helper (POS (mid_coord, high_coord));
2164 setup_handicap_helper (POS (mid_coord, low_coord));
2165 }
2166
2167 if (!handicaps_to_place)
2168 {
2169 goto done_adding_stones;
2170 }
2171
2172 /* done with first eight, there can only be the tengen remaining */
2173
2174 setup_handicap_helper (POS (mid_coord, mid_coord));
2175
2176 done_adding_stones:
2177 goto_handicap_start_sgf ();
2178 return;
2179}
2180
2181static void
2182setup_handicap_helper (unsigned short pos)
2183{
2184 union prop_data_t temp_data;
2185
2186 temp_data.position = pos;
2187
2188 add_prop_sgf (current_node, PROP_ADD_BLACK, temp_data);
2189}
2190
2191void
2192goto_handicap_start_sgf (void)
2193{
2194 if (start_node != tree_head)
2195 {
2196 current_node = get_node (start_node)->prev;
2197 redo_node_sgf ();
2198 }
2199}
2200
2201bool
2202post_game_setup_sgf (void)
2203{
2204 int temp_handle = alloc_storage_sgf ();
2205 int saved = current_node;
2206
2207 if (temp_handle < 0)
2208 {
2209 return false;
2210 }
2211
2212 union prop_data_t temp_data;
2213 temp_data.number = 0; /* meaningless */
2214
2215 if (!header_marked)
2216 {
2217 add_prop_sgf (tree_head, PROP_ROOT_PROPS, temp_data);
2218 header_marked = true;
2219 }
2220
2221 get_node (temp_handle)->next = current_node;
2222 get_node (temp_handle)->prev = NO_NODE;
2223 get_node (temp_handle)->props = NO_PROP;
2224
2225 current_node = temp_handle;
2226
2227 redo_node_sgf ();
2228
2229 if (current_node == temp_handle)
2230 {
2231 current_node = saved;
2232 }
2233
2234 free_storage_sgf (temp_handle);
2235
2236 return true;
2237}