read_dump_file(FILE *fd, VTermPos *cursor_pos)
{
    int		    c;
    garray_T	    ga_text;
    garray_T	    ga_cell;
    char_u	    *prev_char = NULL;
    int		    attr = 0;
    cellattr_T	    cell;
    term_T	    *term = curbuf->b_term;
    int		    max_cells = 0;
    int		    start_row = term->tl_scrollback.ga_len;

    ga_init2(&ga_text, 1, 90);
    ga_init2(&ga_cell, sizeof(cellattr_T), 90);
    vim_memset(&cell, 0, sizeof(cell));
    cursor_pos->row = -1;
    cursor_pos->col = -1;

    c = fgetc(fd);
    for (;;)
    {
	if (c == EOF)
	    break;
	if (c == '\r')
	{
	    // DOS line endings?  Ignore.
	    c = fgetc(fd);
	}
	else if (c == '\n')
	{
	    /* End of a line: append it to the buffer. */
	    if (ga_text.ga_data == NULL)
		dump_is_corrupt(&ga_text);
	    if (ga_grow(&term->tl_scrollback, 1) == OK)
	    {
		sb_line_T   *line = (sb_line_T *)term->tl_scrollback.ga_data
						  + term->tl_scrollback.ga_len;

		if (max_cells < ga_cell.ga_len)
		    max_cells = ga_cell.ga_len;
		line->sb_cols = ga_cell.ga_len;
		line->sb_cells = ga_cell.ga_data;
		line->sb_fill_attr = term->tl_default_color;
		++term->tl_scrollback.ga_len;
		ga_init(&ga_cell);

		ga_append(&ga_text, NUL);
		ml_append(curbuf->b_ml.ml_line_count, ga_text.ga_data,
							ga_text.ga_len, FALSE);
	    }
	    else
		ga_clear(&ga_cell);
	    ga_text.ga_len = 0;

	    c = fgetc(fd);
	}
	else if (c == '|' || c == '>')
	{
	    int prev_len = ga_text.ga_len;

	    if (c == '>')
	    {
		if (cursor_pos->row != -1)
		    dump_is_corrupt(&ga_text);	/* duplicate cursor */
		cursor_pos->row = term->tl_scrollback.ga_len - start_row;
		cursor_pos->col = ga_cell.ga_len;
	    }

	    /* normal character(s) followed by "+", "*", "|", "@" or NL */
	    c = fgetc(fd);
	    if (c != EOF)
		ga_append(&ga_text, c);
	    for (;;)
	    {
		c = fgetc(fd);
		if (c == '+' || c == '*' || c == '|' || c == '>' || c == '@'
						      || c == EOF || c == '\n')
		    break;
		ga_append(&ga_text, c);
	    }

	    /* save the character for repeating it */
	    vim_free(prev_char);
	    if (ga_text.ga_data != NULL)
		prev_char = vim_strnsave(((char_u *)ga_text.ga_data) + prev_len,
						    ga_text.ga_len - prev_len);

	    if (c == '@' || c == '|' || c == '>' || c == '\n')
	    {
		/* use all attributes from previous cell */
	    }
	    else if (c == '+' || c == '*')
	    {
		int is_bg;

		cell.width = c == '+' ? 1 : 2;

		c = fgetc(fd);
		if (c == '&')
		{
		    /* use same attr as previous cell */
		    c = fgetc(fd);
		}
		else if (isdigit(c))
		{
		    /* get the decimal attribute */
		    attr = 0;
		    while (isdigit(c))
		    {
			attr = attr * 10 + (c - '0');
			c = fgetc(fd);
		    }
		    hl2vtermAttr(attr, &cell);
		}
		else
		    dump_is_corrupt(&ga_text);

		/* is_bg == 0: fg, is_bg == 1: bg */
		for (is_bg = 0; is_bg <= 1; ++is_bg)
		{
		    if (c == '&')
		    {
			/* use same color as previous cell */
			c = fgetc(fd);
		    }
		    else if (c == '#')
		    {
			int red, green, blue, index = 0;

			c = fgetc(fd);
			red = hex2nr(c);
			c = fgetc(fd);
			red = (red << 4) + hex2nr(c);
			c = fgetc(fd);
			green = hex2nr(c);
			c = fgetc(fd);
			green = (green << 4) + hex2nr(c);
			c = fgetc(fd);
			blue = hex2nr(c);
			c = fgetc(fd);
			blue = (blue << 4) + hex2nr(c);
			c = fgetc(fd);
			if (!isdigit(c))
			    dump_is_corrupt(&ga_text);
			while (isdigit(c))
			{
			    index = index * 10 + (c - '0');
			    c = fgetc(fd);
			}

			if (is_bg)
			{
			    cell.bg.red = red;
			    cell.bg.green = green;
			    cell.bg.blue = blue;
			    cell.bg.ansi_index = index;
			}
			else
			{
			    cell.fg.red = red;
			    cell.fg.green = green;
			    cell.fg.blue = blue;
			    cell.fg.ansi_index = index;
			}
		    }
		    else
			dump_is_corrupt(&ga_text);
		}
	    }
	    else
		dump_is_corrupt(&ga_text);

	    append_cell(&ga_cell, &cell);
	}
	else if (c == '@')
	{
	    if (prev_char == NULL)
		dump_is_corrupt(&ga_text);
	    else
	    {
		int count = 0;

		/* repeat previous character, get the count */
		for (;;)
		{
		    c = fgetc(fd);
		    if (!isdigit(c))
			break;
		    count = count * 10 + (c - '0');
		}

		while (count-- > 0)
		{
		    ga_concat(&ga_text, prev_char);
		    append_cell(&ga_cell, &cell);
		}
	    }
	}
	else
	{
	    dump_is_corrupt(&ga_text);
	    c = fgetc(fd);
	}
    }

    if (ga_text.ga_len > 0)
    {
	/* trailing characters after last NL */
	dump_is_corrupt(&ga_text);
	ga_append(&ga_text, NUL);
	ml_append(curbuf->b_ml.ml_line_count, ga_text.ga_data,
							ga_text.ga_len, FALSE);
    }

    ga_clear(&ga_text);
    vim_free(prev_char);

    return max_cells;
}