2009-02-11 16:03:17 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "goban.h"
|
|
|
|
#include "sgf_parse.h"
|
|
|
|
#include "sgf.h"
|
|
|
|
#include "sgf_storage.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "board.h"
|
|
|
|
#include "game.h"
|
|
|
|
|
|
|
|
static void handle_prop_value (enum prop_type_t type);
|
|
|
|
static int read_prop_value (char *buffer, size_t buffer_size);
|
|
|
|
static void do_range (enum prop_type_t type, unsigned short ul,
|
|
|
|
unsigned short br);
|
|
|
|
static void parse_prop (void);
|
|
|
|
static void parse_node (void);
|
|
|
|
static enum prop_type_t parse_prop_type (void);
|
|
|
|
|
|
|
|
static unsigned short sgf_to_pos (char *buffer);
|
|
|
|
|
|
|
|
bool
|
|
|
|
parse_sgf (const char *filename)
|
|
|
|
{
|
|
|
|
int saved = current_node;
|
|
|
|
|
|
|
|
/* for parsing */
|
|
|
|
int first_handle = 0; /* first node in the branch */
|
|
|
|
int file_position = 0;
|
|
|
|
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
close_file (&sgf_fd);
|
|
|
|
|
|
|
|
sgf_fd = rb->open (filename, O_RDONLY);
|
|
|
|
|
|
|
|
if (sgf_fd < 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_node = start_node;
|
|
|
|
|
|
|
|
if (current_node < 0)
|
|
|
|
{
|
|
|
|
current_node = saved;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
empty_stack (&parse_stack);
|
|
|
|
|
|
|
|
/* seek to the first '(' */
|
|
|
|
while (peek_char_no_whitespace (sgf_fd) != '(')
|
|
|
|
{
|
|
|
|
if (read_char_no_whitespace (sgf_fd) == -1)
|
|
|
|
{
|
|
|
|
DEBUGF ("end of file or error before we found a '('\n");
|
|
|
|
current_node = saved;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR));
|
|
|
|
push_int_stack (&parse_stack, current_node);
|
|
|
|
|
|
|
|
while (pop_int_stack (&parse_stack, &first_handle) &&
|
|
|
|
pop_int_stack (&parse_stack, &file_position))
|
|
|
|
{
|
|
|
|
/* DEBUGF("poped off %d\n", file_position); */
|
|
|
|
|
|
|
|
rb->yield ();
|
|
|
|
|
|
|
|
current_node = first_handle;
|
|
|
|
|
|
|
|
if (file_position == -1)
|
|
|
|
{
|
|
|
|
temp = read_char_no_whitespace (sgf_fd);
|
|
|
|
if (temp != '(')
|
|
|
|
{
|
|
|
|
/* we're here because there may have been a sibling after
|
|
|
|
another gametree that was handled, but there's no '(',
|
|
|
|
so there wasnt' a sibling, so just go on to any more
|
|
|
|
gametrees in the stack */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* there may be more siblings after we process this one */
|
|
|
|
push_int_stack (&parse_stack, -1);
|
|
|
|
push_int_stack (&parse_stack, first_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* check for a sibling after we finish with this node */
|
|
|
|
push_int_stack (&parse_stack, -1);
|
|
|
|
push_int_stack (&parse_stack, first_handle);
|
|
|
|
|
|
|
|
rb->lseek (sgf_fd, file_position, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
|
|
/* we're at the start of a gametree here, right at the '(' */
|
|
|
|
temp = read_char_no_whitespace (sgf_fd);
|
|
|
|
|
|
|
|
if (temp != '(')
|
|
|
|
{
|
|
|
|
DEBUGF ("start of gametree doesn't have a '('!\n");
|
|
|
|
current_node = saved;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
temp = peek_char_no_whitespace (sgf_fd);
|
|
|
|
/* DEBUGF("||| %d, %c\n", absolute_position(), (char) temp); */
|
|
|
|
|
|
|
|
if (temp == ';')
|
|
|
|
{
|
|
|
|
/* fill the tree_head node before moving on */
|
|
|
|
if (current_node != tree_head ||
|
|
|
|
get_node (current_node)->props >= 0)
|
|
|
|
{
|
|
|
|
int temp = add_child_sgf (NULL);
|
|
|
|
|
|
|
|
if (temp >= 0)
|
|
|
|
{
|
|
|
|
current_node = temp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rb->splash (2 * HZ, "Out of memory while parsing!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
read_char_no_whitespace (sgf_fd);
|
|
|
|
parse_node ();
|
|
|
|
}
|
|
|
|
else if (temp == ')')
|
|
|
|
{
|
|
|
|
/* finished this gametree */
|
|
|
|
|
|
|
|
/* we want to end one past the ')', so eat it up: */
|
|
|
|
read_char_no_whitespace (sgf_fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (temp == '(')
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
DEBUGF ("adding %d\n", (int) rb->lseek (sgf_fd, 0,
|
|
|
|
SEEK_CUR)); */
|
|
|
|
push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR));
|
|
|
|
push_int_stack (&parse_stack, current_node);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (temp == -1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUGF ("extra characters found while parsing: %c\n", temp);
|
|
|
|
/* skip the extras i guess */
|
|
|
|
read_char_no_whitespace (sgf_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
current_node = get_node (tree_head)->next;
|
|
|
|
while (current_node >= 0 && get_node (current_node)->props < 0)
|
|
|
|
{
|
|
|
|
temp = current_node; /* to be freed later */
|
|
|
|
|
|
|
|
/* update the ->prev pointed on all branches of the next node */
|
|
|
|
current_node = get_node (current_node)->next;
|
|
|
|
/* DEBUGF("trying to set prev for branch %d\n", current_node); */
|
|
|
|
if (current_node >= 0)
|
|
|
|
{
|
|
|
|
get_node (current_node)->prev = tree_head;
|
|
|
|
|
|
|
|
struct prop_t *loop_prop =
|
|
|
|
get_prop (get_node (current_node)->props);
|
|
|
|
|
|
|
|
while (loop_prop != 0)
|
|
|
|
{
|
|
|
|
if (loop_prop->type == PROP_VARIATION)
|
|
|
|
{
|
|
|
|
get_node (loop_prop->data.number)->prev = tree_head;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* all of the variations have to be up front, so we
|
|
|
|
can quit here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
loop_prop = get_prop (loop_prop->next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the tree head */
|
|
|
|
get_node (tree_head)->next = get_node (temp)->next;
|
|
|
|
/* DEBUGF("freeing %d %d %d\n", temp, start_node, saved); */
|
|
|
|
if (start_node == temp || saved == temp)
|
|
|
|
{
|
|
|
|
start_node = saved = tree_head;
|
|
|
|
}
|
|
|
|
free_storage_sgf (temp);
|
|
|
|
|
|
|
|
current_node = get_node (tree_head)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_node = saved;
|
|
|
|
|
|
|
|
|
|
|
|
/* DEBUGF("got past!\n"); */
|
|
|
|
close_file (&sgf_fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_node (void)
|
|
|
|
{
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
temp = peek_char_no_whitespace (sgf_fd);
|
|
|
|
|
|
|
|
if (temp == -1 || temp == ')' || temp == '(' || temp == ';')
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parse_prop ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int start_of_prop = 0;
|
|
|
|
static void
|
|
|
|
parse_prop (void)
|
|
|
|
{
|
|
|
|
enum prop_type_t temp_type = PROP_INVALID;
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
temp = peek_char_no_whitespace (sgf_fd);
|
|
|
|
|
|
|
|
if (temp == -1 || temp == ')' || temp == '(' || temp == ';')
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (temp == '[')
|
|
|
|
{
|
|
|
|
handle_prop_value (temp_type);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start_of_prop = rb->lseek (sgf_fd, 0, SEEK_CUR);
|
|
|
|
temp_type = parse_prop_type ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-02-19 23:51:47 +00:00
|
|
|
|
2009-02-11 16:03:17 +00:00
|
|
|
static enum prop_type_t
|
|
|
|
parse_prop_type (void)
|
|
|
|
{
|
|
|
|
char buffer[3];
|
|
|
|
int pos = 0;
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
rb->memset (buffer, 0, sizeof (buffer));
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
temp = peek_char_no_whitespace (sgf_fd);
|
|
|
|
|
|
|
|
if (temp == ';' || temp == '[' || temp == '(' ||
|
|
|
|
temp == -1 || temp == ')')
|
|
|
|
{
|
|
|
|
if (pos == 1 || pos == 2)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return PROP_INVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (temp >= 'A' && temp <= 'Z')
|
|
|
|
{
|
|
|
|
buffer[pos++] = temp;
|
|
|
|
|
|
|
|
if (pos == 2)
|
|
|
|
{
|
|
|
|
read_char_no_whitespace (sgf_fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = read_char_no_whitespace (sgf_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if we're still reading a prop name, in which case we fail
|
|
|
|
(but first we want to eat up the rest of the prop name) */
|
|
|
|
bool failed = false;
|
|
|
|
while (peek_char_no_whitespace (sgf_fd) != ';' &&
|
|
|
|
peek_char_no_whitespace (sgf_fd) != '[' &&
|
|
|
|
peek_char_no_whitespace (sgf_fd) != '(' &&
|
|
|
|
peek_char_no_whitespace (sgf_fd) != '}' &&
|
|
|
|
peek_char_no_whitespace (sgf_fd) != -1)
|
|
|
|
{
|
|
|
|
failed = true;
|
|
|
|
read_char_no_whitespace (sgf_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (failed)
|
|
|
|
{
|
|
|
|
return PROP_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < PROP_NAMES_SIZE; ++i)
|
|
|
|
{
|
|
|
|
if (rb->strcmp (buffer, prop_names[i]) == 0)
|
|
|
|
{
|
|
|
|
return (enum prop_type_t) i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PROP_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
read_prop_value (char *buffer, size_t buffer_size)
|
|
|
|
{
|
|
|
|
bool escaped = false;
|
|
|
|
int temp;
|
|
|
|
int bytes_read = 0;
|
|
|
|
|
|
|
|
/* make it a string, the lazy way */
|
|
|
|
rb->memset (buffer, 0, buffer_size);
|
|
|
|
--buffer_size;
|
|
|
|
|
|
|
|
if (peek_char (sgf_fd) == '[')
|
|
|
|
{
|
|
|
|
read_char (sgf_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
temp = read_char (sgf_fd);
|
|
|
|
if (temp == ']' && !escaped)
|
|
|
|
{
|
|
|
|
return bytes_read;
|
|
|
|
}
|
|
|
|
else if (temp == '\\')
|
|
|
|
{
|
|
|
|
if (escaped)
|
|
|
|
{
|
|
|
|
if (buffer && buffer_size)
|
|
|
|
{
|
|
|
|
*(buffer++) = temp;
|
|
|
|
++bytes_read;
|
|
|
|
--buffer_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
escaped = !escaped;
|
|
|
|
}
|
|
|
|
else if (temp == -1)
|
|
|
|
{
|
|
|
|
return bytes_read;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
escaped = false;
|
|
|
|
if (buffer && buffer_size)
|
|
|
|
{
|
|
|
|
*(buffer++) = temp;
|
|
|
|
++bytes_read;
|
|
|
|
--buffer_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_prop_value (enum prop_type_t type)
|
|
|
|
{
|
|
|
|
/* max size of generically supported prop values is 6, which is 5 for
|
|
|
|
a point range ab:cd and one for the \0
|
|
|
|
|
|
|
|
(this buffer is only used for them, things such as white and black
|
|
|
|
player names are stored in different buffers) */
|
|
|
|
|
|
|
|
/* make it a little bigger for other random crap, like reading in time
|
|
|
|
*/
|
|
|
|
#define PROP_HANDLER_BUFFER_SIZE 16
|
|
|
|
|
|
|
|
char real_buffer[PROP_HANDLER_BUFFER_SIZE];
|
|
|
|
char *buffer = real_buffer;
|
|
|
|
|
|
|
|
int temp;
|
|
|
|
union prop_data_t temp_data;
|
|
|
|
bool in_prop_value = false;
|
|
|
|
bool escaped = false;
|
|
|
|
bool done = false;
|
|
|
|
int temp_width, temp_height;
|
|
|
|
unsigned short temp_pos_ul, temp_pos_br;
|
|
|
|
int temp_size;
|
|
|
|
char *temp_buffer;
|
|
|
|
bool got_value;
|
|
|
|
|
|
|
|
/* special extra handling for root properties, set a marker telling us
|
|
|
|
the right place to spit the values out in output_sgf */
|
|
|
|
if (type == PROP_GAME ||
|
|
|
|
type == PROP_APPLICATION ||
|
|
|
|
type == PROP_CHARSET ||
|
|
|
|
type == PROP_SIZE ||
|
|
|
|
type == PROP_FILE_FORMAT || type == PROP_VARIATION_TYPE)
|
|
|
|
{
|
|
|
|
header_marked = true;
|
|
|
|
|
|
|
|
temp_data.number = 0; /* meaningless */
|
|
|
|
|
|
|
|
/* don't add more than one, so just set it if we found one already
|
|
|
|
*/
|
|
|
|
add_or_set_prop_sgf (current_node, PROP_ROOT_PROPS, temp_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_handled_sgf (type) || type == PROP_COMMENT)
|
|
|
|
{
|
|
|
|
/* DEBUGF("unhandled prop %d\n", (int) type); */
|
|
|
|
rb->lseek (sgf_fd, start_of_prop, SEEK_SET);
|
|
|
|
|
|
|
|
temp_data.number = rb->lseek (unhandled_fd, 0, SEEK_CUR);
|
|
|
|
/* absolute_position(&unhandled_prop_list); */
|
|
|
|
|
|
|
|
add_prop_sgf (current_node,
|
|
|
|
type == PROP_COMMENT ? PROP_COMMENT :
|
|
|
|
PROP_GENERIC_UNHANDLED, temp_data);
|
|
|
|
|
|
|
|
got_value = false;
|
|
|
|
while (!done)
|
|
|
|
{
|
|
|
|
temp = peek_char (sgf_fd);
|
|
|
|
|
|
|
|
switch (temp)
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
if (got_value && !in_prop_value)
|
|
|
|
{
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
escaped = !escaped;
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
escaped = false;
|
|
|
|
in_prop_value = true;
|
|
|
|
got_value = true;
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
if (!escaped)
|
|
|
|
{
|
|
|
|
in_prop_value = false;
|
|
|
|
}
|
|
|
|
escaped = false;
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
case '(':
|
|
|
|
case ';':
|
|
|
|
if (!in_prop_value)
|
|
|
|
{
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
escaped = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (got_value && !in_prop_value)
|
|
|
|
{
|
|
|
|
if (!is_whitespace (temp))
|
|
|
|
{
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
escaped = false;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
{
|
|
|
|
write_char (unhandled_fd, ';');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* don't write out-of-prop whitespace */
|
|
|
|
if (in_prop_value || !is_whitespace (temp))
|
|
|
|
{
|
|
|
|
write_char (unhandled_fd, (char) temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
read_char (sgf_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (type == PROP_BLACK_MOVE || type == PROP_WHITE_MOVE)
|
|
|
|
{
|
|
|
|
/* DEBUGF("move prop %d\n", (int) type); */
|
|
|
|
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
|
|
|
|
temp_data.position = INVALID_POS;
|
|
|
|
|
|
|
|
/* empty is apparently acceptable as a pass */
|
|
|
|
if (temp == 0)
|
|
|
|
{
|
|
|
|
temp_data.position = PASS_POS;
|
|
|
|
}
|
|
|
|
else if (temp == 2)
|
|
|
|
{
|
|
|
|
temp_data.position = sgf_to_pos (buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUGF ("invalid move position read in, of wrong size!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (temp_data.position != INVALID_POS)
|
|
|
|
{
|
|
|
|
add_prop_sgf (current_node, type, temp_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (type == PROP_ADD_BLACK ||
|
|
|
|
type == PROP_ADD_WHITE ||
|
|
|
|
type == PROP_ADD_EMPTY ||
|
|
|
|
type == PROP_CIRCLE ||
|
|
|
|
type == PROP_SQUARE ||
|
|
|
|
type == PROP_TRIANGLE ||
|
|
|
|
type == PROP_DIM || type == PROP_MARK || type == PROP_SELECTED)
|
|
|
|
{
|
|
|
|
/* DEBUGF("add prop %d\n", (int) type); */
|
|
|
|
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
if (temp == 2)
|
|
|
|
{
|
|
|
|
temp_data.position = sgf_to_pos (buffer);
|
|
|
|
|
|
|
|
if (temp_data.position != INVALID_POS &&
|
|
|
|
temp_data.position != PASS_POS)
|
|
|
|
{
|
|
|
|
add_prop_sgf (current_node, type, temp_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (temp == 5)
|
|
|
|
{
|
|
|
|
/* example: "ab:cd", two positions separated by a colon */
|
|
|
|
temp_pos_ul = sgf_to_pos (buffer);
|
|
|
|
temp_pos_br = sgf_to_pos (&(buffer[3]));
|
|
|
|
|
|
|
|
if (!on_board (temp_pos_ul) || !on_board (temp_pos_br) ||
|
|
|
|
buffer[2] != ':')
|
|
|
|
{
|
|
|
|
DEBUGF ("invalid range value!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
do_range (type, temp_pos_ul, temp_pos_br);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUGF ("invalid position or range read in. wrong size!\n");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (type == PROP_LABEL)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
|
|
|
|
if (temp < 4 || buffer[2] != ':')
|
|
|
|
{
|
|
|
|
DEBUGF ("invalid LaBel property '%s'", buffer);
|
|
|
|
}
|
|
|
|
temp_data.position = sgf_to_pos (buffer);
|
|
|
|
|
|
|
|
if (!on_board (temp_data.position))
|
|
|
|
{
|
|
|
|
DEBUGF ("LaBel set on invalid position!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
temp_data.label_extra = buffer[3];
|
|
|
|
|
|
|
|
add_prop_sgf (current_node, type, temp_data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (type == PROP_GAME)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
if (temp != 1 || buffer[0] != '1')
|
|
|
|
{
|
|
|
|
rb->splash (2 * HZ, "This isn't a Go SGF!! Parsing stopped.");
|
|
|
|
DEBUGF ("incorrect game type loaded!\n");
|
|
|
|
|
|
|
|
close_file (&sgf_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == PROP_FILE_FORMAT)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
if (temp != 1 || (buffer[0] != '3' && buffer[0] != '4'))
|
|
|
|
{
|
|
|
|
rb->splash (2 * HZ, "Wrong SGF file version! Parsing stopped.");
|
|
|
|
DEBUGF ("can't handle file format %c\n", buffer[0]);
|
|
|
|
|
|
|
|
close_file (&sgf_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == PROP_APPLICATION ||
|
|
|
|
type == PROP_CHARSET || type == PROP_VARIATION_TYPE)
|
|
|
|
{
|
|
|
|
/* we don't care. on output we'll write our own values for these */
|
|
|
|
read_prop_value (NULL, 0);
|
|
|
|
}
|
|
|
|
else if (type == PROP_SIZE)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
if (temp == 0)
|
|
|
|
{
|
|
|
|
rb->splash (HZ, "Invalid board size specified in file.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temp_width = rb->atoi (buffer);
|
|
|
|
while (*buffer != ':' && *buffer != '\0')
|
|
|
|
{
|
|
|
|
++buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*buffer != '\0')
|
|
|
|
{
|
|
|
|
++buffer;
|
|
|
|
temp_height = rb->atoi (buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temp_height = temp_width;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!set_size_board (temp_width, temp_height))
|
|
|
|
{
|
|
|
|
rb->splashf (HZ,
|
|
|
|
"Board too big/small! (%dx%d) Stopping parse.",
|
|
|
|
temp_width, temp_height);
|
|
|
|
close_file (&sgf_fd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clear_board ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == PROP_KOMI)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
|
|
|
|
if (temp == 0)
|
|
|
|
{
|
|
|
|
header.komi = 0;
|
|
|
|
DEBUGF ("invalid komi specification. setting to zero\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
header.komi = rb->atoi (buffer) << 1;
|
|
|
|
while (*buffer != '.' && *buffer != ',' && *buffer != '\0')
|
|
|
|
{
|
|
|
|
++buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer != '\0')
|
|
|
|
{
|
|
|
|
++buffer;
|
|
|
|
|
|
|
|
if (*buffer == 0)
|
|
|
|
{
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
else if (*buffer >= '1' && *buffer <= '9')
|
|
|
|
{
|
|
|
|
header.komi += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*buffer != '0')
|
|
|
|
{
|
|
|
|
DEBUGF ("extra characters after komi value!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == PROP_BLACK_NAME ||
|
|
|
|
type == PROP_WHITE_NAME ||
|
|
|
|
type == PROP_BLACK_RANK ||
|
|
|
|
type == PROP_WHITE_RANK ||
|
|
|
|
type == PROP_BLACK_TEAM ||
|
|
|
|
type == PROP_WHITE_TEAM ||
|
|
|
|
type == PROP_DATE ||
|
|
|
|
type == PROP_ROUND ||
|
|
|
|
type == PROP_EVENT ||
|
|
|
|
type == PROP_PLACE ||
|
|
|
|
type == PROP_OVERTIME ||
|
|
|
|
type == PROP_RESULT || type == PROP_RULESET)
|
|
|
|
{
|
|
|
|
if (!get_header_string_and_size
|
|
|
|
(&header, type, &temp_buffer, &temp_size))
|
|
|
|
{
|
|
|
|
rb->splash (5 * HZ,
|
|
|
|
"Error getting header string. Report this.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temp = read_prop_value (temp_buffer, temp_size - 1);
|
|
|
|
#if 0
|
|
|
|
DEBUGF ("read %d bytes into header for type: %d\n", temp, type);
|
|
|
|
DEBUGF ("data: %s\n", temp_buffer);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == PROP_TIME_LIMIT)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
header.time_limit = rb->atoi (buffer);
|
|
|
|
DEBUGF ("setting time: %d (%s)\n", header.time_limit, buffer);
|
|
|
|
}
|
|
|
|
else if (type == PROP_HANDICAP)
|
|
|
|
{
|
|
|
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
|
|
|
if (start_node == tree_head)
|
|
|
|
{
|
|
|
|
if (rb->atoi (buffer) >= 2)
|
|
|
|
{
|
|
|
|
start_node = current_node;
|
|
|
|
temp_data.number = header.handicap = rb->atoi (buffer);
|
|
|
|
add_prop_sgf (current_node, type, temp_data);
|
|
|
|
DEBUGF ("setting handicap: %d\n", header.handicap);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUGF ("invalid HAndicap prop. ignoring\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rb->splash (HZ, "extraneous HAndicap prop present in file!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUGF ("UNHANDLED PROP TYPE!!!\n");
|
|
|
|
rb->splash (3 * HZ,
|
|
|
|
"A SGF prop was not dealt with. Please report this");
|
|
|
|
read_prop_value (NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* upper-left and bottom right */
|
|
|
|
static void
|
|
|
|
do_range (enum prop_type_t type, unsigned short ul, unsigned short br)
|
|
|
|
{
|
|
|
|
/* this code is overly general and accepts ranges even if ul and br
|
|
|
|
aren't the required corners it's easier doing that that failing if
|
|
|
|
the input is bad */
|
|
|
|
|
|
|
|
bool x_reverse = false;
|
|
|
|
bool y_reverse = false;
|
|
|
|
union prop_data_t temp_data;
|
|
|
|
|
|
|
|
if (I (br) < I (ul))
|
|
|
|
{
|
|
|
|
x_reverse = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (J (br) < J (ul))
|
|
|
|
{
|
|
|
|
y_reverse = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
for (x = I (ul);
|
|
|
|
x_reverse ? (x >= I (br)) : (x <= I (br)); x_reverse ? --x : ++x)
|
|
|
|
{
|
|
|
|
for (y = J (ul);
|
|
|
|
y_reverse ? (y >= J (br)) : (y <= J (br)); y_reverse ? --y : ++y)
|
|
|
|
{
|
|
|
|
temp_data.position = POS (x, y);
|
|
|
|
|
|
|
|
DEBUGF ("adding %d %d for range (type %d)\n",
|
|
|
|
I (temp_data.position), J (temp_data.position), type);
|
|
|
|
add_prop_sgf (current_node, type, temp_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned short
|
|
|
|
sgf_to_pos (char *buffer)
|
|
|
|
{
|
|
|
|
if (buffer[0] == 't' && buffer[1] == 't')
|
|
|
|
{
|
|
|
|
return PASS_POS;
|
|
|
|
}
|
|
|
|
else if (buffer[0] < 'a' || buffer[0] > 'z' ||
|
|
|
|
buffer[1] < 'a' || buffer[1] > 'z')
|
|
|
|
{
|
|
|
|
return INVALID_POS;
|
|
|
|
}
|
|
|
|
return POS (buffer[0] - 'a', buffer[1] - 'a');
|
|
|
|
}
|
|
|
|
|