/* riscosio.c */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include <kernel.h>
#include <math.h>

#include <oslib/macros.h>
#include <oslib/osbyte.h>
#include <oslib/osfile.h>
#include <oslib/osfscontrol.h>
#include <oslib/serviceinternational.h>
#include <oslib/colourtrans.h>
#include <oslib/toolbox.h>
#include <oslib/filer.h>
#include <event.h>

#include "options.h"
#include "ztypes.h"
#include "riscosio.h"
#include "riscoswimp.h"
#include "osdepend.h"
#include "screenio.h"
#include "screen.h"
#include "control.h"
#include "fileio.h"
#include "input.h"
#include "rosound.h"
#include "roinput.h"
#include "v6.h"
#include "v6ro.h"
#include "datavox.h"
#include "text.h"
#include "unicutils.h"

#define SAVE_NAME   "SavedGame"
#define SCRIPT_NAME "Transcript"
#define RECORD_NAME "Recording"

zbyte_t h_config;
unsigned h_flags, hx_flags3;
zbyte_t h_screen_rows = DEFAULT_ROWS;
zbyte_t h_screen_cols = DEFAULT_COLS;
unsigned h_screen_width;
unsigned h_screen_height;
zbyte_t h_char_width;
zbyte_t h_char_height;

float screen_gamma = 2.2F;

os_colour screen_bg;
int border=true;
int fullscreen;

//int fg, bg;
os_colour true_fg, true_bg;
int display_font;
int display_attr;

static bool generous_redraw;

int using_mouse;
int mouse_x=1, mouse_y=1, mouse_butt;

static int cursor_saved = OFF;

int os_version;


static int current_row;
static char *current_seg;
static zword_t *current_ptr;
static int sx, sy;

os_box changed_box;

static zword_t **upper_text;
static char **upper_font;
static os_colour **upper_fg, **upper_bg;
static char **lower_window;
static int *lower_size;

char zscii_to_native_table[256];
zword_t native_to_unicode_table[128];
zword_t system_to_zscii_table[256];
zword_t system_to_unicode_table[128];
int system_alphabet_no;

char encoding_name[16]="\\ELatin1";

font_f fontno[F_LAST+1];
int fwidth[F_LAST+1][256];

font_paint_block fblock;

static bool have_bold, have_italic;
#ifdef ZSPEC11DRAFT
static bool have_allstyles;
#endif

char textfont_name[font_NAME_LIMIT+16]="\\FTrinity.Medium";
char fixedfont_name[font_NAME_LIMIT+16]="\\FCorpus.Medium";

static int ccomp_to_system(int c)
{
    return (int) (pow(c / 31.0, 2.2 / screen_gamma) * 255.0 + 0.5);
}

static int ccomp_to_z(int c)
{
    return (int) (pow(c / 255.0, screen_gamma / 2.2) * 31.0 + 0.5);
}

zword_t os_colour_to_z_true_colour(os_colour c)
{
    if (c > 0xFFFFFF00)
        return (zword_t) c;
    else
    {
        unsigned r, g, b;
        r = ccomp_to_z((c >> 8) & 0xFF);
        g = ccomp_to_z((c >> 16) & 0xFF);
        b = ccomp_to_z((c >> 24) & 0xFF);
        return (b << 10) | (g << 5) | r;
    }
}

os_colour z_true_colour_to_os_colour(zword_t c)
{
    if (c & 0x8000)
    {
        return (short) c;
    }
    else
    {
        unsigned r, g, b;
        b = ccomp_to_system((c >> 10) & 0x1F);
        g = ccomp_to_system((c >> 5) & 0x1F);
        r = ccomp_to_system(c & 0x1F);
        return (b << 24) | (g << 16) | (r << 8);
    }
}

int os_colour_to_z_colour(os_colour c)
{
    return z_true_colour_to_z_colour(os_colour_to_z_true_colour(c));
}


int SWidth, SHeight;
int SWidthOS, SHeightOS;

/*
const int char_width=16*400;
const int line_height=32*400;
*/

struct textseg
{
    int offset;
    int left;
    byte font;
    byte attr;
    byte len;
    os_colour fg;
    os_colour bg;
    zword_t text;
};

#define seg_offset(a) (((struct textseg *)a)->offset)
#define seg_left(a) (((struct textseg *)a)->left)
#define seg_fg(a) (((struct textseg *)a)->fg)
#define seg_bg(a) (((struct textseg *)a)->bg)
#define seg_font(a) (((struct textseg *)a)->font)
#define seg_attr(a) (((struct textseg *)a)->attr)
#define seg_len(a) (((struct textseg *)a)->len)
#define seg_text(a) (&((struct textseg *)a)->text)
#define seg_next(a) (a+seg_offset(a))

#define seg_style(a) (seg_attr(a) >> 1)

#define seg_basic_size (sizeof(struct textseg))

int area_no;

osspriteop_area *gfx_area;
osspriteop_header **gfx_ptr;
os_factors factors;
/* Wide enough for 2 64 bit entries (you never know!...)*/
static osspriteop_TRANS_TAB(16) trans_tab;


static void do_gfx(const zword_t *text, int x, int y, os_colour bg, os_colour fg, int n);
static void reset_line(int i);

static char *seg_insert(char *old)
{
    char *new;

    new=(char *)(((int)(seg_text(old)+seg_len(old))+4)&~3);
    seg_offset(new)=seg_offset(old)-(new-old);
    seg_fg(new)=true_fg;
    seg_bg(new)=true_bg;
    seg_font(new)=seg_font(old);
    seg_attr(new)=seg_attr(old);
    seg_left(new)=seg_left(seg_next(old));
    seg_len(new)=0;
    seg_text(new)[0]='\0';
    seg_offset(old)=new-old;

    return new;
}

static void extend_row(int row)
{
    char *temp;

    temp=lower_window[row];
    lower_window[row]=(char *)malloc(lower_size[row]+128);
    memcpy(lower_window[row], temp, lower_size[row]);
    if (temp <= current_seg && current_seg <= temp+lower_size[row])
    {
        current_seg=lower_window[row]+(current_seg-temp);
        current_ptr=(zword_t *) (lower_window[row]+((char *) current_ptr-temp));
    }
    free(temp);

    lower_size[row]+=128;
    for (temp=lower_window[row]; seg_offset(seg_next(temp)); temp=seg_next(temp))
    	;

    memmove(lower_window[row]+lower_size[row]-seg_basic_size, seg_next(temp), seg_basic_size);

    seg_offset(temp)=lower_window[row]+lower_size[row]-seg_basic_size-temp;
}

void resize_window(int new_cols, int new_rows)
{
    int row;
    zword_t **temp;
    char **temp3, **temp5;
    os_colour **temp2, **temp6;
    int *temp4;

    if (new_cols < 60)
    	new_cols=60;
    if (new_rows < 14)
    	new_rows=14;

    if (h_type==V6)
    {
        v6_resize_screen(new_cols, new_rows);
        return;
    }

    if (new_rows<h_screen_rows)
    {
        int a, b;
        a=current_seg-lower_window[current_row];
        b=(char *) current_ptr-lower_window[current_row];
        temp5=lower_window;
        temp4=lower_size;
        lower_window=calloc(new_rows, sizeof(zword_t *));
        lower_size=calloc(new_rows, sizeof(int));
        /* Delete lower window rows from the top */
        for (row=0; row < new_rows; row++)
        {
            lower_window[row]=temp5[row+h_screen_rows-new_rows];
            lower_size[row]=temp4[row+h_screen_rows-new_rows];
        }
        for (row=new_rows; row < h_screen_rows; row++)
            free(temp5[row]);
        free(temp5);
        free(temp4);
        current_row+=new_rows-h_screen_rows;
        current_seg=a+lower_window[current_row];
        current_ptr=(zword_t *) (b+lower_window[current_row]);
        /* To be neat, reset any lines in status_window */
        for (row=0; row < MIN(status_size, new_rows); row++)
            reset_line(row);

        temp=upper_text;
        temp2=upper_bg;
        temp6=upper_fg;
        temp3=upper_font;
        upper_text=calloc(new_rows, sizeof upper_text[0]);
        upper_bg=calloc(new_rows, sizeof upper_bg[0]);
        upper_fg=calloc(new_rows, sizeof upper_fg[0]);
        upper_font=calloc(new_rows, sizeof upper_font[0]);
        /* Delete upper window rows from the bottom */
        for (row=0; row < new_rows; row++)
        {
            upper_text[row]=temp[row];
            upper_bg[row]=temp2[row];
            upper_fg[row]=temp6[row];
            upper_font[row]=temp3[row];
        }
        for (row=new_rows; row < h_screen_rows; row++)
        {
            free(temp[row]);
            free(temp2[row]);
            free(temp3[row]);
            free(temp6[row]);
        }
        free(temp);
        free(temp2);
        free(temp3);
        free(temp6);
        if (status_size>new_rows)
            status_size=new_rows;
        if (sy>=new_rows)
            sy=new_rows-1;
    }
    else if (new_rows>h_screen_rows)
    {
        temp5=lower_window;
        temp4=lower_size;
        lower_window=calloc(new_rows, sizeof(zword_t *));
        lower_size=calloc(new_rows, sizeof(int));
        /* Add lower window rows from the bottom */
        for (row=0; row < h_screen_rows; row++)
        {
            lower_window[row]=temp5[row];
            lower_size[row]=temp4[row];
        }
        for (row=h_screen_rows; row < new_rows; row++)
        {
            lower_window[row]=(char *)malloc(4*h_screen_cols);
            lower_size[row]=4*h_screen_cols;
            reset_line(row);
        }
        free(temp5);
        free(temp4);

        temp=upper_text;
        temp2=upper_bg;
        temp6=upper_fg;
        temp3=upper_font;
        upper_text=calloc(new_rows, sizeof upper_text[0]);
        upper_bg=calloc(new_rows, sizeof upper_bg[0]);
        upper_fg=calloc(new_rows, sizeof upper_fg[0]);
        upper_font=calloc(new_rows, sizeof upper_font[0]);
        /* Add upper window rows from the bottom */
        for (row=0; row < h_screen_rows; row++)
        {
            upper_text[row]=temp[row];
            upper_bg[row]=temp2[row];
            upper_fg[row]=temp6[row];
            upper_font[row]=temp3[row];
        }
        for (row=h_screen_rows; row < new_rows; row++)
        {
            upper_text[row]=calloc(new_cols+1, sizeof upper_text[0][0]);
            upper_fg[row]=calloc(new_cols+1, sizeof upper_fg[0][0]);
            upper_bg[row]=calloc(new_cols+1, sizeof upper_bg[0][0]);
            upper_font[row]=malloc(new_cols+1);
        }
        free(temp);
        free(temp2);
        free(temp3);
        free(temp6);
    }

    /* All that remains now is to stretch upper window arrays, if required */
    if (new_cols > h_screen_cols)
    {
        zword_t *t1;
        char *t3;
        os_colour *t2, *t4;
        for (row=0; row < MIN(h_screen_rows-1, new_rows-1); row++)
        {
            t1=upper_text[row];
            t2=upper_bg[row];
            t4=upper_fg[row];
            t3=upper_font[row];
            upper_text[row]=calloc(new_cols+1, sizeof(zword_t));
            upper_bg[row]=calloc(new_cols+1, sizeof upper_bg[0][0]);
            upper_fg[row]=calloc(new_cols+1, sizeof upper_fg[0][0]);
            upper_font[row]=malloc(new_cols+1);
            memcpy(upper_text[row], t1, h_screen_cols * sizeof(zword_t));
            memcpy(upper_bg[row], t2, h_screen_cols * sizeof(os_colour));
            memcpy(upper_fg[row], t4, h_screen_cols * sizeof(os_colour));
            memcpy(upper_font[row], t3, h_screen_cols);
            free(t1);
            free(t2);
            free(t3);
            free(t4);
            if (upper_text[row][0])
            {
                int i;
                for (i = h_screen_cols-1; i < new_cols; i++)
                {
                    upper_text[row][i] = ' ';
                    upper_bg[row][i] = true_bg;
                    upper_fg[row][i] = true_fg;
                }
    	    	memset(upper_font[row]+h_screen_cols, TEXT_FONT, new_cols-h_screen_cols);
            }
        }
    }

    if (sy>=new_cols)
    	sy=new_cols-1;

    h_screen_rows=h_screen_height=new_rows;
    h_screen_cols=h_screen_width=new_cols;

    restart_header();

    SWidth=h_screen_cols*char_width;
    SHeight=h_screen_rows*line_height;

    SWidthOS=SWidth/400;
    SHeightOS=SHeight/400;
}

static void mark_area(int minx, int maxx, int toprow, int botrow)
{
    if (changed_box.x0 > minx)
    	changed_box.x0 = minx;
    if (changed_box.x1 < maxx)
    	changed_box.x1 = maxx;
    if (changed_box.y0 > toprow)
        changed_box.y0 = toprow;
    if (changed_box.y1 < botrow)
    	changed_box.y1 = botrow;
}

static void update_window(void)
{
    int temp;

    if (changed_box.x1==0)
    	return;

    changed_box.x0/=400;
    changed_box.x1=changed_box.x1/400+1;
    temp=changed_box.y0;
    changed_box.y0=-changed_box.y1*32-32;
    changed_box.y1=-temp*32+1;
    if (generous_redraw || in_input_line)
    {
        changed_box.y1+=16;
        generous_redraw=0;
    }
    do_update_window(&changed_box);
    changed_box.x0=SWidth;
    changed_box.x1=0;
    changed_box.y0=h_screen_rows;
    changed_box.y1=0;
}

void os_update_window(void)
{
    if (h_type==V6)
    	v6ro_update_window();
    else
    	update_window();
}

static void draw_fullscreen_border(int rx, int ry)
{
    colourtrans_set_gcol(screen_bg, NONE, os_ACTION_OVERWRITE, NULL);

    os_plot(os_MOVE_TO, rx-16384, ry+16384);
    os_plot(os_PLOT_TO | os_PLOT_RECTANGLE, rx-1, ry-16384);
    os_plot(os_PLOT_TO | os_PLOT_RECTANGLE, rx+16384, ry-SHeightOS-1);
    os_plot(os_PLOT_TO | os_PLOT_RECTANGLE, rx+SWidthOS, ry+16384);
    os_plot(os_PLOT_TO | os_PLOT_RECTANGLE, rx, ry);
}

static void draw_normal_border(int rx, int ry)
{
    /* large expanse of grey */
    wimp_set_colour(wimp_COLOUR_VERY_LIGHT_GREY);
    os_plot(os_MOVE_TO, rx-32, ry+32);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, 27, -(SHeightOS+64));
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, SWidthOS+36, 27);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, -27, SHeightOS+36);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, -(SWidthOS+8), -27);

    /* 4 os units of background around edge */
    colourtrans_set_gcol(screen_bg, NONE, os_ACTION_OVERWRITE, NULL);
    os_plot(os_MOVE_TO, rx-4, ry+3);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, 3, -(SHeightOS+7));
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, SWidthOS+4, 3);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, -3, SHeightOS+4);
    os_plot(os_PLOT_BY | os_PLOT_RECTANGLE, -SWidthOS, -3);

    /*os_plot(os_MOVE_TO, rx-2, ry+1);
    os_plot(os_PLOT_BY, SWidthOS+3, 0);
    os_plot(os_PLOT_BY, 0, -SHeightOS-3);
    os_plot(os_PLOT_BY, -SWidthOS-3, 0);
    os_plot(os_PLOT_BY, 0, SHeightOS+3);

    os_plot(os_MOVE_TO, rx-4, ry+3);
    os_plot(os_PLOT_BY, SWidthOS+7, 0);
    os_plot(os_PLOT_BY, 0, -SHeightOS-7);
    os_plot(os_PLOT_BY, -SWidthOS-7, 0);
    os_plot(os_PLOT_BY, 0, SHeightOS+7);*/

    /* thin black line */
    wimp_set_colour(wimp_COLOUR_BLACK);
    os_plot(os_MOVE_TO, rx-6, ry+5);
    os_plot(os_PLOT_BY, SWidthOS+11, 0);
    os_plot(os_PLOT_BY, 0, -SHeightOS-11);
    os_plot(os_PLOT_BY, -SWidthOS-11, 0);
    os_plot(os_PLOT_BY, 0, SHeightOS+11);

    /* white highlight - lower right */
    wimp_set_colour(wimp_COLOUR_WHITE);
    os_plot(os_MOVE_TO, rx+SWidthOS+11, ry+11);
    os_plot(os_PLOT_BY, 0, -SHeightOS-23);
    if (hiresmode)
        os_plot(os_PLOT_BY, -SWidthOS-23, 0);

    os_plot(os_MOVE_TO, rx-14, ry-SHeightOS-14);
    os_plot(os_PLOT_BY, SWidthOS+27, 0);
    os_plot(os_PLOT_BY, 0, SHeightOS+27);

    /* dark grey highlight - top left */
    wimp_set_colour(wimp_COLOUR_MID_DARK_GREY);
    os_plot(os_MOVE_TO, rx-12, ry-SHeightOS-12);
    os_plot(os_PLOT_BY, 0, SHeightOS+23);
    if (hiresmode)
        os_plot(os_PLOT_BY, SWidthOS+23, 0);

    os_plot(os_MOVE_TO, rx-14, ry-SHeightOS-14);
    os_plot(os_PLOT_BY, 0, SHeightOS+27);
    os_plot(os_PLOT_BY, SWidthOS+27, 0);
}

void draw_border(int rx, int ry)
{
    if (fullscreen)
        draw_fullscreen_border(rx, ry);
    else if (border)
        draw_normal_border(rx, ry);
}

static void draw_lower_only_row(int row, int rx, int ry)
{
    char *seg;

    /* lower window background */
    for (seg=lower_window[row]; seg_offset(seg); seg=seg_next(seg))
    {
        int bg;

        if (seg_left(seg_next(seg))==seg_left(seg) || seg_bg(seg)==os_COLOUR_TRANSPARENT)
            continue;

        if (seg_attr(seg) & REVERSE)
            bg=seg_fg(seg);
        else
            bg=seg_bg(seg);

        if (seg_font(seg) != GRAPHICS_FONT)
        {
            colourtrans_set_gcol(bg, colourtrans_SET_BG,
                                 os_ACTION_OVERWRITE, 0);
            os_plot(os_MOVE_TO, seg_left(seg)/400+rx, ry-32*row-32);
            os_plot(os_PLOT_BG_TO | os_PLOT_RECTANGLE, seg_left(seg_next(seg))/400+rx-1,
                                                       ry-32*row-1);
        }
    }
    if (seg_bg(seg) != os_COLOUR_TRANSPARENT)
    {
        colourtrans_set_gcol(seg_bg(seg), colourtrans_SET_BG,
                             os_ACTION_OVERWRITE, 0);
        os_plot(os_MOVE_TO, seg_left(seg)/400+rx, ry-32*row-32);
        os_plot(os_PLOT_BG_TO | os_PLOT_RECTANGLE, SWidthOS+rx-1,
                                                   ry-32*row-1);
    }

    /* Now the lower window text */
    for (seg=lower_window[row]; seg_offset(seg); seg=seg_next(seg))
    {
        int bg,fg,font,attr;

        if (seg_left(seg_next(seg))==seg_left(seg))
            continue;

        font=seg_font(seg);
        attr=seg_attr(seg);

        if (font==4)
        {
            font=TEXT_FONT;
            attr|=FIXED_FONT;
        }
        if (attr & REVERSE)
        {
            bg=seg_fg(seg); fg=seg_bg(seg);
        }
        else
        {
            bg=seg_bg(seg); fg=seg_fg(seg);
        }

        if (font == TEXT_FONT)
        {
            colourtrans_set_font_colours(fontno[attr>>1],
                                         bg, fg,
                                         14, 0, 0, 0);
            font_paint_u(fontno[attr>>1],
                       seg_text(seg),
                       /*font_KERN*/ attr&FIXED_FONT?font_GIVEN_BLOCK:NONE,
                       rx*400+seg_left(seg), (ry+8-32)*400-row*line_height,
                       &fblock, NULL, 0);
        }
        else if (font == GRAPHICS_FONT)
        	do_gfx(seg_text(seg), rx*400+seg_left(seg), (ry-32)*400-row*line_height,
        	       bg, fg, seg_len(seg));
    }
}

static void draw_upper_row(int row, int rx, int ry, os_box *box, wimp_draw *r)
{
    int x, maxx;

    /* Right, now the status win */
    x=box->x0*400/char_width;
    if (x<0)
        x=0;

    maxx=box->x1*400/char_width;
    if (maxx > h_screen_cols-1)
        maxx=h_screen_cols-1;

    while (x <= maxx)
    {
        int cfont=upper_font[row][x];
        unsigned font;
        unsigned attr;
        int bg=upper_bg[row][x],fg=upper_fg[row][x],ox;
        unsigned temp;

        ox=x;

        while (x<=maxx && upper_font[row][x]==cfont &&
               upper_bg[row][x]==bg && upper_fg[row][x]==fg)
            x++;

        if (bg==os_COLOUR_TRANSPARENT)
        {
            int minx = ox*16+rx, maxx = x*16+rx;
            if (r->clip.x0 > minx) minx = r->clip.x0;
            if (r->clip.x1 < maxx) maxx = r->clip.x1;
            if (minx < maxx)
            {
                set_graphics_window(minx, r->clip.y0, maxx, r->clip.y1);
                draw_lower_only_row(row, rx, ry);
                set_graphics_window(r->clip.x0, r->clip.y0, r->clip.x1, r->clip.y1);
            }
            continue;
        }

        temp=upper_text[row][x];
        upper_text[row][x]='\0';

        font=cfont & 0xF;
        attr=cfont >> 4;

        if (font==4)
            font=TEXT_FONT;
        attr|=FIXED_FONT;

        if (attr & REVERSE)
        {
            os_colour temp=bg;
            bg=fg;
            fg=temp;
        }

        if (font != GRAPHICS_FONT)
        {
            colourtrans_set_gcol(bg, colourtrans_SET_BG,
                                 os_ACTION_OVERWRITE, 0);
            os_plot(os_MOVE_TO, ox*16+rx, ry-32*row-32);
            os_plot(os_PLOT_BG_TO | os_PLOT_RECTANGLE, x*16+rx-1, ry-32*row-1);
        }
        if (font == TEXT_FONT)
        {
            colourtrans_set_font_colours(fontno[attr>>1],
                                         bg,
                                         fg,
                                         14, 0, 0, 0);
            font_paint_u(fontno[attr>>1],
                         upper_text[row]+ox,
                         font_GIVEN_BLOCK,
                         ox*char_width+rx*400, (ry+8-32)*400-row*line_height,
                         &fblock, NULL, 0);
        }
        else if (font == GRAPHICS_FONT)
            do_gfx(upper_text[row]+ox, ox*char_width+rx*400,
                   (ry-32)*400-row*line_height,
                   bg, fg, x-ox);
        upper_text[row][x]=temp;
    }
}

void set_graphics_window(int xmin, int ymin, int xmax, int ymax)
{
    struct { char a,b,c,cmd; short x0, y0, x1, y1; } vdu;

    vdu.cmd=os_VDU_SET_GRAPHICS_WINDOW;
    vdu.x0=xmin;
    vdu.y0=ymin;
    vdu.x1=xmax-1;
    vdu.y1=ymax-1;
    os_writen(&vdu.cmd, 9);
}

void set_graphics_origin(int x0, int y0)
{
    struct { char a,b,c,cmd; short x, y; } vdu;

    vdu.cmd=os_VDU_SET_GRAPHICS_ORIGIN;
    vdu.x=x0;
    vdu.y=y0;
    os_writen(&vdu.cmd, 5);
}

void redraw_window(wimp_draw *r)
{
    int toprow, bottomrow;
    int rx, ry;
    int row;
    os_box box;

    if (h_type==V6)
    {
        v6ro_redraw_window(r);
        return;
    }

    /* Relative x+y offsets */
    rx=r->box.x0 - r->xscroll;
    ry=r->box.y1 - r->yscroll;

    box.x0=r->clip.x0 - rx;
    box.x1=r->clip.x1 - rx;
    box.y0=r->clip.y0 - ry;
    box.y1=r->clip.y1 - ry;

    toprow=-(box.y1*400/line_height);;
    if (toprow < 0)
    	toprow=0;

    bottomrow=-(box.y0*400/line_height);;
    if (bottomrow > h_screen_rows - 1)
        bottomrow=h_screen_rows-1;

    for (row=toprow; row<=bottomrow; row++)
    {
        if (upper_text[row][0] != '\0')
        {
            draw_upper_row(row, rx, ry, &box, r);
        }
        else
        {
            draw_lower_only_row(row, rx, ry);
        }

    }

    if (box.x0 < 0 || box.x1 > SWidthOS || box.y1 > 0 || box.y0 < - SHeightOS)
        draw_border(rx, ry);
}

void load_gfx(void)
{
    int i, type, size;
    char name[12];

    type=osfile_read_stamped("<Zip2000$Dir>.Resources.Font3", NULL, NULL, &size, NULL, NULL);

    if (type!=fileswitch_IS_FILE)
    	fatal_lookup("NoFnt3");

    gfx_area=malloc(size+4);
    gfx_ptr=calloc(95, sizeof gfx_ptr[0]);

    if (!gfx_area || !gfx_ptr)
    	fatal_lookup("NoMem");

    gfx_area->size=size+4;
    gfx_area->first=16;

    osspriteop_clear_sprites(osspriteop_USER_AREA, gfx_area);

    osspriteop_load_sprite_file(osspriteop_USER_AREA, gfx_area, "<Zip2000$Dir>.Resources.Font3");

    for (i=32; i<=126; i++)
    {
        sprintf(name, "%02x", i);
        gfx_ptr[i-32]=osspriteop_select_sprite(osspriteop_USER_AREA,
                                               gfx_area,
                                               (osspriteop_id) name);
    }

    if (h_type != V6)
    	wimp_read_pix_trans(osspriteop_USER_AREA, gfx_area,
                            (osspriteop_id) "20", &factors, NULL);
}


static void do_gfx(const zword_t *text, int x, int y, os_colour bg, os_colour fg, int n)
{
    os_PALETTE(2) pal;
    int i;
    osspriteop_action flags;
    int ox, oy;

    if (!gfx_area)
    	load_gfx();

    pal.entries[0]=bg;
    pal.entries[1]=fg;

    if (os_version < RISC_OS_360)
    	flags=NONE;
    else
    	flags=colourtrans_RETURN_WIDE_ENTRIES;

    colourtrans_generate_table(0, (os_palette *) &pal,
                               os_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
                               (osspriteop_trans_tab *) &trans_tab,
                               flags, NULL, NULL);

    if (os_version < RISC_OS_360)
    	flags=os_ACTION_OVERWRITE;
    else
    	flags=os_ACTION_OVERWRITE | osspriteop_GIVEN_WIDE_ENTRIES;

    font_convertto_os(x, y, &ox, &oy);
    for (i=0; i<n; i++)
    {
    	osspriteop_put_sprite_scaled(osspriteop_PTR,
                                    gfx_area,
                                    (osspriteop_id) gfx_ptr[text[i]-32],
                                    ox, oy,
                                    flags,
                                    &factors,
                                    (osspriteop_trans_tab *) &trans_tab);
        ox+=char_width/400;
    }
}

void display_char(int c)
{
    if (h_type==V6)
    {
        v6ro_display_char(c);
        return;
    }

#ifdef ALLOW_SPEECH
    if (use_speech && screen_window == TEXT_WINDOW)
    {
    	speech_buffer[speech_ptr++]=c;
    	if (speech_ptr >= SPEECHBUFSIZE)
    	    flush_speech();
    }
#endif

    if (screen_window==TEXT_WINDOW)
    {
        int currently_fixed=seg_attr(current_seg) & FIXED_FONT;
        int oldx;
        if (c=='\n')
        {
            scroll_line();
            return;
        }
        if ((char *) current_ptr > seg_next(current_seg) - seg_basic_size)
            extend_row(current_row);

    	if (buffering==OFF && SWidth - seg_left(seg_next(current_seg)) < char_width)
    	    scroll_line();

        if (fixed_space_bit && !currently_fixed)
        {
            if (seg_len(current_seg))
            {
                current_seg=seg_insert(current_seg);
                current_ptr=seg_text(current_seg);
            }
            seg_attr(current_seg) |= FIXED_FONT;
        }
        else if (!fixed_space_bit && !(display_attr & FIXED_FONT) && currently_fixed)
        {
            if (seg_len(current_seg))
            {
                current_seg=seg_insert(current_seg);
                current_ptr=seg_text(current_seg);
            }
            seg_attr(current_seg) &=~ FIXED_FONT;
        }

        #ifdef ALLOW_QUOTES
        /* Low-level hack for nice quotes */
        if (smartquotes &&
            (c=='i' || c=='l' || c=='-' || c=='\'' || c=='`' || c=='\"') &&
            seg_font(current_seg) == TEXT_FONT &&
            !(seg_attr(current_seg) & FIXED_FONT)) /* not fixed-space */
        {
            int last=*(current_ptr-1);

#ifdef NEW_QUOTING
            if (c=='\'' && unicode_to_native(0x2019, 0))
                c=0x2019 /**/;
            else if (c=='`')
            {
                if (unicode_to_native(0x2018, 0))
                    c=0x2018 /**/;
                else
                    c='\'';
            }
#else
            if (c=='\'' && unicode_to_native(0x2018, 0) && !in_input_line)
            {
                if (seg_len(current_seg)>0 && last != ' ' && last != '(' && last != 0x201C /**/)
                    c=0x2019 /**/;
                else
                    c=0x2018 /**/;
            }
            else if (c=='`' && unicode_to_native(0x2018, 0) && !in_input_line)
                c=0x2018 /**/;
#endif
            else if (c=='"' && unicode_to_native(0x201C, 0) && !in_input_line)
            {
                if (seg_len(current_seg)>0 && last != ' ' && last != '(' && last != '[' && last != 0x2018 /**/)
                    c=0x201D /**/;
                else
                    c=0x201C /**/;
            }
            else if (seg_len(current_seg)>0 && (c=='i' || c=='-' || c=='l') && !in_input_line)
            {
                /* Check for ligatures */

                if (last=='f' && (c=='i' || c=='l') && unicode_to_native(0xFB01, 0))
                {
                    current_ptr--;
                    seg_len(current_seg)--;
                    seg_left(seg_next(current_seg))-=fwidth[seg_style(current_seg)]['f'];
                    c= c=='i' ? 0xFB01 : 0xFB02;
                }
                else if ((last=='-' || last==0x2013) && c=='-' && unicode_to_native(0x2014, 0))
                {
                    current_ptr--;
                    seg_len(current_seg)--;
                    seg_left(seg_next(current_seg))-=fwidth[seg_style(current_seg)]['-'];
                    c=0x2014;
                }
                else if (last==' ' && c=='-' && unicode_to_native(0x2013, 0))
                    c=0x2013;
            }
        }
        #endif
    	*current_ptr++=c;
    	*current_ptr='\0';
    	seg_len(current_seg)++;
    	oldx=seg_left(seg_next(current_seg));
    	if (seg_font(current_seg) != TEXT_FONT)
    	    seg_left(seg_next(current_seg))+=char_width;
    	else
    	    seg_left(seg_next(current_seg))+=fwidth[seg_style(current_seg)][unicode_to_native(c, '?')];

    	/* Allow a little extra for overhanging "f"s and underhanging "j"s, but don't
    	   overflow onto border - this will cause the whole border to get replotted
    	   - slow */
    	mark_area(MAX(oldx-2048,0), MIN(seg_left(seg_next(current_seg))+2048, SWidth),
    	          current_row, current_row);
    }
    else
    {
        if (h_type==V6)
            return;

        if (c=='\n')
        {
            sx=0;
            if (sy<status_size-1)
            	sy++;
            return;
        }
    	if (upper_text[sy][0]==0)
    	{
    	    int i;
    	    for (i=h_screen_cols-1; i>=0; i--)
    	    {
    	        upper_text[sy][i] = ' ';
    	        upper_bg[sy][i] = os_COLOUR_TRANSPARENT;
    	    }
    	    memset(upper_font[sy], TEXT_FONT, h_screen_cols);
    	}
    	#ifdef ALLOW_QUOTES
        /* Low-level hack for nice quotes */
        if ((c=='\'' || c=='`' || c=='"')
            && display_font == TEXT_FONT
            && sx>0)
        {
            if (c=='\'' && unicode_to_native(0x2018, 0))
            {
                if (upper_text[sy][sx-1] != ' ' && upper_text[sy][sx-1] != '(')
                    c=0x2019 /**/;
                else
                    c=0x2018 /**/;
            }
            else if (c=='`' && unicode_to_native(0x2018, 0))
                c=0x2018 /**/;
            else if (c=='"' && unicode_to_native(0x201C, 0))
            {
                if (upper_text[sy][sx-1] != ' ' && upper_text[sy][sx-1] != '(')
                    c=0x201D /**/;
                else
                    c=0x201C /**/;
            }
        }
        #endif
    	upper_text[sy][sx]=c;
    	upper_font[sy][sx]=display_attr<<4 | display_font;
    	upper_bg[sy][sx]=true_bg;
    	upper_fg[sy][sx]=true_fg;
    	mark_area(sx*char_width, (sx+1)*char_width, sy, sy);
    	sx++;

    	if (sx==h_screen_cols)
    	{
    	    if (sy<status_size-1)
    	    {
    	        sx=0; sy++;
    	    }
    	    else
    	    	sx--;
    	}
    }

    /* Allow for accented chars by marking a "generous redraw" - redraw a
       bit above the current line */
    if (c >= 192)
        generous_redraw=true;
}

void display_string(const zword_t *s)
{
    while (*s)
        display_char(*s++);
}


static void init_screen_arrays(void)
{
    int i;

    upper_text=calloc(h_screen_rows, sizeof upper_text[0]);
    upper_bg=calloc(h_screen_rows, sizeof upper_bg[0]);
    upper_fg=calloc(h_screen_rows, sizeof upper_fg[0]);
    upper_font=calloc(h_screen_rows, sizeof(char *));
    lower_window=calloc(h_screen_rows, sizeof(char *));
    lower_size=calloc(h_screen_rows, sizeof(int));

    for (i=0; i<h_screen_rows; i++)
    {
        upper_text[i]=calloc(h_screen_cols+1, sizeof(zword_t));
        upper_text[i][h_screen_cols]=0;
        upper_bg[i]=calloc(h_screen_cols+1, sizeof upper_bg[0][0]);
        upper_fg[i]=calloc(h_screen_cols+1, sizeof upper_fg[0][0]);
        upper_font[i]=malloc(h_screen_cols+1);
        lower_window[i]=malloc(h_screen_cols*4);
        lower_size[i]=h_screen_cols*4;
    }
}

//#pragma check_stack

/*
 * Routines to find a font. First try it specifying the encoding, then without
 * encoding.
 */
static font_f find_font(const char *font_name, int xsize, int ysize,
                             int xres, int yres, int *xres_out, int *yres_out)
{
    char buffer[128];
    font_f f;

    font_apply_fields(font_name, encoding_name, buffer, sizeof buffer);

    if (xfont_find_font(buffer, xsize, ysize, xres, yres, &f, xres_out, yres_out))
    {
        if (font_find_field(font_name, 'E', NULL))
            font_apply_fields(font_name, "\\E", buffer, sizeof buffer);
        else
            strcpy(buffer, font_name);

        f = font_find_font(buffer, xsize, ysize, xres, yres, xres_out, yres_out);
    }

    return f;
}

static os_error *xfind_font(const char *font_name, int xsize, int ysize,
                            int xres, int yres, font_f *font,
                            int *xres_out, int *yres_out)
{
    char buffer[128];
    os_error *e;
    int have_e;

    e = xfont_apply_fields(font_name, encoding_name, buffer, sizeof buffer, NULL);
    if (e) return e;

    if (xfont_find_font(buffer, xsize, ysize, xres, yres, font, xres_out, yres_out))
    {
        e = xfont_find_field(font_name, 'E', NULL, &have_e);
        if (e) return e;

        if (have_e)
        {
            e = xfont_apply_fields(font_name, "\\E", buffer, sizeof buffer, NULL);
            if (e) return e;
        }
        else
            strcpy(buffer, font_name);

        e = xfont_find_font(buffer, xsize, ysize, xres, yres, font, xres_out, yres_out);
        if (e) return e;
    }

    return NULL;
}

static font_f modified_font(char *orig, char *mod,
                            int xsize, int ysize, int xres, int yres)
{
    char buffer[128];
    char origname[128];
    char buffer2[128];
    os_error *e=NULL;
    font_f f;
    bool found;
    char *p, *p2;
    char *lastdot;

    found=font_find_field(orig, 'F', &p);

    if (!found)
    	p=orig;

    p2=origname;
    while (*p>32 && *p!='\\')
        *p2++=*p++;
    *p2='\0';

    lastdot=strrchr(origname, '.');

    p=strtok(mod, " ");

    while (p)
    {
        sprintf(buffer, "\\F%s.%s\\f", origname, p);
        font_apply_fields(orig, buffer, buffer2, sizeof buffer2);
        e=xfind_font(buffer2, xsize, ysize, xres, yres, &f, NULL, NULL);
        if (e==NULL)
            break;

        if (lastdot)
        {
            *lastdot='\0';
            sprintf(buffer, "\\F%s.%s\\f", origname, p);
            *lastdot='.';
            font_apply_fields(orig, buffer, buffer2, sizeof buffer2);
            e=xfind_font(buffer2, xsize, ysize, xres, yres, &f, NULL, NULL);
            if (e==NULL)
                break;
        }
        p=strtok(NULL, " ");
    }

    if (e)
    	f=find_font(orig, xsize, ysize, xres, yres, 0, 0);

    return f;
}

extern int serviceinternational_alphabet_mapping(
                 territory_alphabet_number alphabet_no,
                 const unsigned **table);



void setup_os_mapping_tables(void)
{
    char buffer[256];
    char buffer2[64];
    int i;

    /* Load the encoding table for the current font */
    sprintf(buffer, "<Zip2000$Dir>.Resources.Encodings.%s", encoding_name+2);
    osfile_load_stamped_no_path(buffer, (byte *) native_to_unicode_table,
                                NULL, NULL, NULL, NULL);

    /*
     * And the table for the keyboard. UTF-8 is handled specially.
     * For other alphabets, use the service call to get a table from the
     * system. If we can't we'll try one of our own tables.
     */
    system_alphabet_no = osbyte1(osbyte_ALPHABET_NUMBER, 127, 0);
    if (system_alphabet_no != 111)
    {
        const unsigned *table;
        if (!serviceinternational_alphabet_mapping(system_alphabet_no, &table))
        {
            for (i=0; i<=127; i++)
                system_to_unicode_table[i] = table[i+128] < 0x10000 ? table[i+128] : 0xFFFF;
        }
        else
        {
            if (serviceinternational_alphabet_number_to_alphabet_name(system_alphabet_no,
                             buffer2, sizeof buffer2, &i) == 0)
                buffer2[i] = '\0';
            else
                strcpy(buffer2, "Latin1");

            sprintf(buffer, "<Zip2000$Dir>.Resources.Encodings.%s", buffer2);

            if (xosfile_load_stamped_no_path(buffer, (byte *) system_to_unicode_table,
                                             NULL, NULL, NULL, NULL, NULL))
                for (i=0; i<=127; i++)
                    system_to_unicode_table[i] = i+128;
        }
    }

}

void setup_game_mapping_tables(void)
{
    int i, n;

    /*
     * Set up the mapping table from ZSCII to our encoding. Note the
     * firm assumption that the lower set of our encoding is ASCII/ZSCII.
     */
    memset(zscii_to_native_table, 0, 256);
    for (i=32; i<=126; i++)
    	zscii_to_native_table[i] = i;

    for (i=155; i<155+zscii_to_unicode_table_size; i++)
    {
        int u = zscii_to_unicode_table[i-155];

        if (u < 0x0020 || u == 0x007F || u == 0xFFFF)
            continue;

        for (n=128; n<=255; n++)
            if (native_to_unicode_table[n-128] == u)
            {
                zscii_to_native_table[i] = n;
                break;
            }
    }

    /*
     * And do the table from the keyboard to ZSCII.
     */
    memset(system_to_zscii_table, 0, sizeof system_to_zscii_table);
    system_to_zscii_table[8] = zscii_DELETE;
    system_to_zscii_table[10] = zscii_NEWLINE;
    system_to_zscii_table[13] = zscii_NEWLINE;
    system_to_zscii_table[21] = key_CTRL_U;
    system_to_zscii_table[27] = zscii_ESCAPE;
    for (i=32; i<=126; i++)
        system_to_zscii_table[i] = i;
    system_to_zscii_table[127] = zscii_DELETE;

    /*
     * Assumption here is that chars 0-127 of the system alphabet are ASCII,
     * and that chars 128-255 do not contain any ASCII chars.
     */
    for (i=128; i<=255; i++)
    {
        if (system_to_unicode_table[i-128] != 0xFFFF)
            for (n=155; n<155+zscii_to_unicode_table_size; n++)
                if (system_to_unicode_table[i-128] == zscii_to_unicode_table[n-155])
                {
                    system_to_zscii_table[i] = n;
                    break;
                }
    }
}

void setup_mapping_tables(void)
{
    setup_os_mapping_tables();
    setup_game_mapping_tables();
}

void find_fonts(void)
{
    int i, xr, yr, c;
    char buffer[256];
    int xwidth;

    for (i=0; i<=F_LAST; i++)
    	if (fontno[i])
    	    font_lose_font(fontno[i]);

    xr=yr=180;

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &i);
    xr >>= i;

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &i);
    yr >>= i;

    hiresmode=(i<=1);

    fontno[F_MEDIUM]=find_font(textfont_name,
                                    12*16, 12*16, xr, yr, NULL, NULL);

    fontno[F_BOLD]=modified_font(textfont_name, getenv("Font$Bold"),
                                 12*16, 12*16, xr, yr);

    fontno[F_ITALIC]=modified_font(textfont_name, getenv("Font$Italic"),
                                   12*16, 12*16, xr, yr);

    font_read_identifier(fontno[F_BOLD], (byte *) buffer);

    fontno[F_BOLDITALIC]=modified_font(buffer, getenv("Font$Italic"),
                                       12*16, 12*16, xr, yr);

    fontno[F_FIXED]=find_font(fixedfont_name,
                                   12*16, 12*16, xr, yr, NULL, NULL);

    /* Have to work out required width of font(!). Yuck */
    font_scan_string(fontno[F_FIXED], "0", font_GIVEN_FONT, -1, -1, NULL, NULL, 0,
                     NULL, &xwidth, NULL, NULL);
    /* OK, xwidth now equals width of 12pt font in millipoints. We need the width in
       millipoints to be roughly char_width */
    font_lose_font(fontno[F_FIXED]);

    xwidth=(int)((double)(12*16*char_width)/xwidth+0.5);

    fontno[F_FIXED]=find_font(fixedfont_name,
                                   xwidth, 12*16, xr, yr, NULL, NULL);

    fontno[F_FIXEDBOLD]=modified_font(fixedfont_name, getenv("Font$Bold"),
                                       xwidth, 12*16, xr, yr);
    fontno[F_FIXEDITALIC]=modified_font(fixedfont_name, getenv("Font$Italic"),
                                        xwidth, 12*16, xr, yr);

    font_read_identifier(fontno[F_FIXEDBOLD], (byte *) buffer);

    fontno[F_FIXEDBOLDITALIC]=modified_font(buffer, getenv("Font$Italic"),
                                            xwidth, 12*16, xr, yr);

    buffer[1]='\0';

    for (i=F_MEDIUM; i<=F_LAST; i++)
    {
        int misc[32];
        int xpres;
    	font_read_font_metrics(fontno[i], 0, (font_width_info *)fwidth[i],
    	                                  0, (font_misc_info *)misc,
    	                       0, NULL, NULL, &xpres, NULL, NULL, NULL);

    	if (!xpres)
    	    for (c=0; c<=255; c++)
    	    	fwidth[i][c]=misc[4];
    }

    /*
     * Deduce which fonts we have by comparing font handles. Might fail
     * if we had a Font Manager which for some reason gave different handles
     * for identical claims, but it would just be over-optimistic, which
     * we always used to be anyway.
     */
    have_bold = fontno[F_BOLD] != fontno[F_MEDIUM];
    have_italic = fontno[F_ITALIC] != fontno[F_MEDIUM];
#ifdef ZSPEC11DRAFT
    have_allstyles = true;
    if (have_bold && have_italic)
        have_allstyles = fontno[F_FIXEDBOLDITALIC] != fontno[F_FIXEDBOLD] &&
                         fontno[F_BOLDITALIC] != fontno[F_BOLD];
    if (have_allstyles && have_bold)
        have_allstyles = fontno[F_FIXEDBOLD] != fontno[F_FIXED];
    if (have_allstyles && have_italic)
        have_allstyles = fontno[F_FIXEDITALIC] != fontno[F_FIXED];
#endif

    /* Work out extra inter-letter spacing for fixed space font */

    fblock.space.x=fblock.space.y=0;
    fblock.letter.x=char_width-fwidth[F_FIXED]['0'];
    fblock.letter.y=0;

    for (i=F_FIXED; i<=F_LAST; i++)
        for (c=0; c<=255; c++)
            fwidth[i][c]=char_width;

    if (datap && h_type >= V4)
    {
        if (have_bold)
            h_config |= CONFIG_BOLD;
        else
            h_config &=~ CONFIG_BOLD;

        if (have_italic)
            h_config |= CONFIG_EMPHASIS;
        else
            h_config &=~ CONFIG_EMPHASIS;

#ifdef ZSPEC11DRAFT
        if (have_allstyles)
            hx_flags3 |= FLAG3_ALL_STYLES;
        else
            hx_flags3 &=~ FLAG3_ALL_STYLES;
#endif

        restart_header();
    }
}

//#pragma no_check_stack

void initialize_screen(void)
{
    int temp;

    load_choices();

    setup_os_mapping_tables();

    os_version=osbyte1(129, 0, 0xFF);

    SWidth=h_screen_cols*char_width;
    SHeight=h_screen_rows*line_height;

    SWidthOS=SWidth/400;
    SHeightOS=SHeight/400;

    if (h_type < V6)
    {
        h_char_width = 1;
        h_char_height = 1;
    }
    else
    {
        h_char_width = V6_CHAR_W;
        h_char_height = V6_CHAR_H;
    }
    h_screen_width = h_screen_cols * h_char_width;
    h_screen_height = h_screen_rows * h_char_height;

    true_fg=true_default_fg; true_bg=true_default_bg;

    if (h_type==V6)
    {
        v6ro_initialize_screen(h_screen_cols, h_screen_rows);
        return;
    }

    /* This sets up fonts for the first time */
    modechange_handler(NULL, NULL);

    init_screen_arrays();

#ifdef ALLOW_SPEECH
    use_speech = xspch_ready(NULL)==NULL;
#endif

    /* Hacky bootstrap problem - can't set_colours until have a current_seg
       Can't have current_seg until cleared screen. Can't kill screen until
       colours are chosen */
    clear_screen();
    set_true_colours(TCOL_DEFAULT, TCOL_DEFAULT, -1);
    display_font=TEXT_FONT;

    initialise_wimp();
    /* Major hackery !!!! */
    temp=screen_window;
    sy= (h_screen_rows >> 1) - 1;   /* Not /2 to avoid compiler bug */
    sx= (h_screen_cols - strlen(msgs_lookup("Loading"))) / 2 - 1;
    screen_window=STATUS_WINDOW;
    display_string(msgs_lookup_u("Loading"));
    screen_window=temp;
    update_window();

}



void restart_screen(void)
{
    cursor_saved = OFF;

    v6ro_clear_cache();

    if (h_type == V3)
    {
    	h_config |= CONFIG_WINDOWS | CONFIG_VARIABLE;
    	h_config &=~ CONFIG_NOSTATUSLINE;
    }

    if (h_type >= V4)
    {
    	h_config |= CONFIG_FIXED | CONFIG_TIMED_INPUT;

        if (have_bold)
            h_config |= CONFIG_BOLD;
        else
            h_config &=~ CONFIG_BOLD;

        if (have_italic)
            h_config |= CONFIG_EMPHASIS;
        else
            h_config &=~ CONFIG_EMPHASIS;
    }

    if (h_type >= V5)
    {
        h_config |= CONFIG_COLOUR;
#ifdef ZSPEC11DRAFT
        hx_flags3 |= FLAG3_MULTIPLE_COLOURS;
        hx_flags3 &=~ FLAG3_MULTIPLE_SOUNDS;
        if (have_allstyles)
            hx_flags3 |= FLAG3_ALL_STYLES;
        else
            hx_flags3 &=~ FLAG3_ALL_STYLES;
#endif
        hx_flags3 &=~ FLAG3_UNKNOWN;
    }

    if (h_type >= V6)
    {
        h_config |= CONFIG_GRAPHICS | CONFIG_SOUND;
        //if (GHeader.images)
            //h_config |= CONFIG_GRAPHICS;
        //else
        if (!GHeader.images)
            h_flags &=~ GRAPHICS_FLAG;
    }
    else
        hx_flags3 &=~ FLAG3_TRANSPARENCY;

    using_mouse=h_flags & MOUSE_FLAG;
    setup_mouse(using_mouse);
    /* We can do everything, so don't need to alter h_flags */


    /* Set default text colours */
    h_default_bg_col = os_colour_to_z_colour(true_default_bg);
    h_default_fg_col = os_colour_to_z_colour(true_default_fg);

    h_screen_width = h_screen_cols * h_char_width;
    h_screen_height = h_screen_rows * h_char_height;

    /* Set the title bar */
    SetTitle();
}

void restart_sound(void)
{
    if (h_type >= V5)
    {
        int soundtypes = setup_sound((h_flags & SOUND_FLAG) == SOUND_FLAG);
        if (!soundtypes)
            h_flags &=~ SOUND_FLAG;
#ifdef ZSPEC11DRAFT
        if (!(soundtypes & 2))
            hx_flags3 &=~ FLAG3_MUSIC;
#endif
    }
    else
        setup_sound(1);
}

void reset_screen(void)
{
    int i;

    for (i=0; i<=F_LAST; i++)
    	font_lose_font(fontno[i]);

    lose_music(true);
    lose_all_effects();
}

static void reset_line(int i)
{
    char *l = lower_window[i];

    seg_offset(l)=lower_size[i]-seg_basic_size;
    seg_fg(l)=true_fg;
    seg_bg(l)=true_bg;
    seg_font(l)=display_font;
    seg_attr(l)=display_attr;
    seg_left(l)=0;
    seg_len(l)=0;
    seg_text(l)[0]='\0';
    seg_offset(seg_next(l))=0;
    seg_bg(seg_next(l))=true_bg;
    seg_fg(seg_next(l))=true_fg;
    seg_left(seg_next(l))=0;
}


void clear_screen(void)
{
    int i;

    for (i=0; i<h_screen_rows; i++)
    {
        upper_text[i][0]='\0';
        reset_line(i);
    }

    if (true_bg==screen_bg)
        mark_area(0, SWidth, 0, h_screen_rows-1);
    else
        /* Need to redraw "edge" */
        mark_area(-32767*400, 32767*400, -1023, 1023);

    screen_bg=true_bg;
    SetBorder(fullscreen);
    update_window();

    current_seg=lower_window[current_row];
    current_ptr=seg_text(current_seg);
}

void select_status_window(void)
{
    update_window();
}

void select_text_window(void)
{
    update_window();
    /* Make sure any changes in upper window are reflected */
    set_attribute(display_attr);
}

void create_status_window(void)
{
    int i, x;

    for (i=0; i<status_size; i++)
    	if (seg_offset(seg_next(lower_window[i]))==0 &&
    	          seg_left(seg_next(lower_window[i]))==0)
    	{
    	    seg_bg(seg_next(lower_window[i]))=os_COLOUR_TRANSPARENT;
    	    if (upper_text[i][0]=='\0')
            	for (x=0; x<h_screen_cols; x++)
            	{
            	    upper_text[i][x]=' ';
            	    upper_bg[i][x]=true_bg;
            	    upper_fg[i][x]=true_fg;
            	    upper_font[i][x]=TEXT_FONT;
            	}
            else
            	for (x=0; x<h_screen_cols; x++)
            	{
            	    if (upper_bg[i][x]==os_COLOUR_TRANSPARENT)
            	    {
            	    	upper_text[i][x]=' ';
            	    	upper_bg[i][x]=true_bg;
            	    	upper_fg[i][x]=true_fg;
            	    	upper_font[i][x]=TEXT_FONT;
            	    }
            	}
        }

    /* Check we don't swallow the cursor (8.7.2.2) */
    if (current_row<status_size)
    {
        current_row=status_size;
        if (current_row >= h_screen_rows)
            current_row=h_screen_rows-1;
        current_seg=lower_window[current_row];
        current_ptr=seg_text(current_seg);
    }

}

#if 0
void delete_status_window(void)
{
}
#endif

void clear_line(void)
{
    int x;

    if (screen_window==TEXT_WINDOW)
    {
        char *l = seg_next(current_seg);
        seg_bg(l)=true_bg;
        seg_fg(l)=true_fg;
        mark_area(seg_left(l), SWidth, current_row, current_row);
    }
    else
    {
        for (x=sx; x<h_screen_cols; x++)
        {
            upper_text[sy][x]=' ';
            upper_bg[sy][x]=true_bg;
            upper_fg[sy][x]=true_fg;
            upper_font[sy][x]=TEXT_FONT;
        }
        upper_text[sy][h_screen_cols]='\0';
        mark_area(sx*char_width, SWidth, sy, sy);
        if (sx==0)
        {
            /* We've cleared a whole line - let's hack and delete the underlying
               text window line! */
            reset_line(sy);
            seg_bg(seg_next(lower_window[sy]))=os_COLOUR_TRANSPARENT;
        }
    }
}

void clear_text_window(void)
{
    int i;

    for (i=status_size; i<h_screen_rows; i++)
    {
        upper_text[i][0]='\0';
        reset_line(i);
    }

    if ((status_size==0 || h_type<V5) && true_bg!=screen_bg)
    {
    	screen_bg=true_bg;
    	SetBorder(fullscreen);
        /* Need to redraw "edge" */
        mark_area(-32767*400, 32767*400, -1023, 1023);
        //mark_area(-4*400, SWidth+4*400, -1, h_screen_rows);
    }
    else
    	mark_area(0, SWidth, status_size, h_screen_rows-1);

    update_window();

    if (h_type > V4)
    	current_row=status_size;
    else
    	current_row=h_screen_rows-1;

    current_seg=lower_window[current_row];
    current_ptr=seg_text(current_seg);
}

void clear_status_window(void)
{
    int i, x;

    for (i=0; i<status_size; i++)
    {
        for (x=0; x<h_screen_cols; x++)
        {
            upper_text[i][x]=' ';
            upper_bg[i][x]=true_bg;
            upper_fg[i][x]=true_fg;
            upper_font[i][x]=TEXT_FONT;
        }
        upper_text[i][h_screen_cols]='\0';
        reset_line(i);
        seg_bg(seg_next(lower_window[i]))=os_COLOUR_TRANSPARENT;
    }
    mark_area(0, SWidth, 0, status_size-1);
    update_window();
    if (screen_window==STATUS_WINDOW)
        sx=sy=0;
}

void move_cursor(int row, int col)
{
    if (h_type == V6)
    {
        v6ro_move_cursor(row, col);
        return;
    }

    if (screen_window==STATUS_WINDOW)
    {
        if (row-1 < sy || (row-1==sy && col-1<sx))
            update_window();

    	sx=col-1;
    	sy=row-1;
    }

    if (screen_window==TEXT_WINDOW && col==1)
    {
        reset_line(row-1);
        current_seg=lower_window[row-1];
        current_row=row-1;
        current_ptr=seg_text(current_seg);
        update_window();
    }
}

void get_cursor_position(int *row, int *col)
{
    if (h_type==V6)
    {
        v6ro_get_cursor_position(row, col);
        return;
    }

    if (screen_window == TEXT_WINDOW)
    {
        *row = current_row+1;
        *col = seg_left(seg_next(current_seg))/char_width + 1;
    }
    else
    {
        *row=sy+1;
        *col=sx+1;
    }
}

void set_attribute(int attribute)
{
    if (attribute==NORMAL)
    {
        display_attr=0;
    }
    else
    	display_attr|=attribute;

    if (h_type==V6)
    {
        return;
    }

    if (screen_window==TEXT_WINDOW)
    {
        if (attribute==NORMAL && seg_attr(current_seg)!=0 ||
            attribute!=NORMAL && (seg_attr(current_seg) & attribute)!=attribute)
            if (seg_len(current_seg))
            {
            	current_seg = seg_insert(current_seg);
            	current_ptr = seg_text(current_seg);
            }

        if (attribute==NORMAL)
            seg_attr(current_seg)=0;
        else
            seg_attr(current_seg)|=attribute;
    }
}

void optimise_status_line(void)
{
    int row;
    int left, right, x;
    bool remove;
    char *seg;

    for (row=0; row < status_size; row++)
    {
        if (upper_text[row][0]=='\0')
            continue;

        seg=lower_window[row];
        for (;;)
        {
            left=seg_left(seg)/char_width;
            if (seg_offset(seg))
            	right=seg_left(seg_next(seg))/char_width;
            else
            	right=h_screen_cols-1;

            remove=true;
            for (x=left; x <= right; x++)
            {
                if (upper_bg[row][x]==os_COLOUR_TRANSPARENT)
                {
                    remove=false;
                    break;
                }
            }
            if (remove)
            	seg_bg(seg)=os_COLOUR_TRANSPARENT;

            if (seg_offset(seg)==0)
            	break;

            seg=seg_next(seg);
        }
    }
}

void complete_redraw(void)
{
    wimp_window_state ws;

    ws.w=ScreenW;
    wimp_get_window_state(&ws);
    if (!(ws.flags & wimp_WINDOW_NOT_COVERED))
    {
        wimp_event_no e;
        claim_null_events();
        do
        {
            e = poll_noswitch();
        } while (e==wimp_REDRAW_WINDOW_REQUEST);
        release_null_events();
    }
}

void scroll_line(void)
{
#ifdef ALLOW_SPEECH
    if (use_speech && screen_window == TEXT_WINDOW)
    {
    	speech_buffer[speech_ptr++]=' ';
    	if (speech_ptr >= SPEECHBUFSIZE)
    	    flush_speech();
    }
#endif

    if (current_row == h_screen_rows-1)
    {
        int row;
        char *temp1, *temp4;
        os_colour *temp3, *temp6;
        int temp5;
        zword_t *temp2;
        int old_caret = caret_enabled;

        caret_enabled=false;
        place_caret();
        update_window();
        temp1=lower_window[status_size];
        temp5=lower_size[status_size];
        temp2=upper_text[status_size];
        temp3=upper_bg[status_size];
        temp6=upper_fg[status_size];
        temp4=upper_font[status_size];
        for (row=status_size; row<h_screen_rows-1; row++)
        {
            lower_window[row]=lower_window[row+1];
            lower_size[row]=lower_size[row+1];
            upper_text[row]=upper_text[row+1];
            upper_bg[row]=upper_bg[row+1];
            upper_fg[row]=upper_fg[row+1];
            upper_font[row]=upper_font[row+1];
        }
        lower_window[h_screen_rows-1]=temp1;
        lower_size[h_screen_rows-1]=temp5;
        upper_text[h_screen_rows-1]=temp2;
        upper_bg[h_screen_rows-1]=temp3;
        upper_fg[h_screen_rows-1]=temp6;
        upper_font[h_screen_rows-1]=temp4;
    	reset_line(h_screen_rows-1);
    	upper_text[h_screen_rows-1][0]='\0';

    	current_seg=lower_window[h_screen_rows-1];
        current_ptr=seg_text(current_seg);
        wimp_block_copy(ScreenW,
                        0,
                        -SHeightOS,
                        SWidthOS,
                        -32*status_size-32,
                        0,
                        -(SHeight-line_height)/400);
        mark_area(0, SWidth, h_screen_rows-1, h_screen_rows-1);
        update_window();
    	complete_redraw();
        caret_enabled=old_caret;
        place_caret();
    }
    else
    {
        update_window();
        current_row++;
        current_seg=lower_window[current_row];
        current_ptr=seg_text(current_seg);
        seg_bg(current_seg)=true_bg;
        seg_fg(current_seg)=true_fg;
        seg_font(current_seg)=display_font;
        seg_attr(current_seg)=display_attr;
    }
}

void grab_caret(wimp_caret *caret)
{
    wimp_caret c;
    int x,y;

    if (!caret)
        wimp_get_caret_position(caret = &c);

    if (!(game_wants_caret && caret_enabled))
    {
        if (caret->w != ScreenW || !(caret->height & (1<<25)))
        {
            if (h_type==V6)
                switchoutput(-1);
            wimp_set_caret_position(ScreenW, wimp_ICON_WINDOW, 0, 0, 1<<25, 0);
            if (h_type==V6)
                switchoutput(+1);
        }
        return;
    }

    if (h_type==V6)
    {
        v6ro_place_caret();
        return;
    }

    if (screen_window==TEXT_WINDOW)
    {
        if (in_input_line)
            x=input_x;
        else
            x=seg_left(seg_next(current_seg));
        y=(current_row+1)*line_height;
    }
    else
    {
        if (in_input_line)
            x=input_x;
        else
            x=sx*char_width;
        y=(sy+1)*line_height;
    }

    x/=400;
    y/=-400;

    wimp_set_caret_position(ScreenW, wimp_ICON_WINDOW, x, y, line_height/400, 0);
}

void place_caret(void)
{
    wimp_caret caret;

    wimp_get_caret_position(&caret);

    if (caret.w != ScreenW)
    	return;

    grab_caret(&caret);
}

void set_true_colours(int foreground, int background, int window)
{
    os_colour f, b;

    f = z_true_colour_to_os_colour(foreground);
    b = z_true_colour_to_os_colour(background);

    if (f==TCOL_DEFAULT)
    	f=true_default_fg;

    if (b==TCOL_DEFAULT)
    	b=true_default_bg;

    if (h_type==V6)
    {
        /*if (foreground == -1)
            foreground=v6ro_get_colour()+2;
        if (background == -1)
            background=v6ro_get_colour()+2;*/
        v6ro_set_colours(f, b, window);
        return;
    }

    /* Ignore all other special colour requests (including CURRENT) */
    if (foreground>0xFFFFFF00)
        foreground=true_fg;

    if (background>0xFFFFFF00)
        background=true_bg;

    true_fg = f;
    true_bg = b;

    if (screen_window==TEXT_WINDOW)
    {
        if (seg_len(current_seg)!=0 && (seg_bg(current_seg) != b ||
                                        seg_fg(current_seg) != f))
        {
            current_seg=seg_insert(current_seg);
            current_ptr=seg_text(current_seg);
        }
        else
        {
            seg_bg(current_seg)=b;
            seg_fg(current_seg)=f;
        }
    }
}


/*
 * Translate a single ZSCII character, known to be >127, into a string
 * in the machine alphabet (Latin-1). s contains "?" initially - this
 * can be left untouched.
 */
void codes_to_text(unsigned c, char *s)
{
    if (c < 256 && zscii_to_native_table[c])
        *s = zscii_to_native_table[c];
}

int text_length(const zword_t *line_buffer, int len, int font, int style, int fixed)
{
    int i, c, pos=0;

    for (i=0; i<len; i++)
    {
    	c=unicode_to_native(line_buffer[i], '?');
    	if (c>=32)
        {
            if (font==TEXT_FONT && !fixed)
            {
                if ((c == '\'' || c == '`') && smartquotes)
                {
                    if (c=='\'')
                        c=unicode_to_native(0x2019, '\'') /**/;
                    else
                        c=unicode_to_native(0x2018, '\'') /**/;
                }
            	pos+=fwidth[style][c];
            }
            else
            	pos+=char_width;
        }
        else
        {
            if (c==EMBED_STYLE)
                style=F_MEDIUM;
            else if (c>=EMBED_STYLE+1 && c<=EMBED_STYLE+MAX_ATTRIBUTE)
                style|=(c-EMBED_STYLE)>>1;
            else if (c>=EMBED_FONT+1 && c<=EMBED_FONT+4)
            	font=c-EMBED_FONT;
            else if (c==EMBED_FIXED+0)
            	fixed=OFF;
            else if (c==EMBED_FIXED+1)
            	fixed=ON;
        }
    }
    return pos;
}

/*
 * fit_word
 *
 * This routine determines whether a word of text will fit
 * on the screen.
 *
 * line : Line of text to test.
 *
 * Contains attribute codes - need to be placed into strings.
 */

int fit_word(const zword_t *line_buffer, int len)
{
    int pos;
    int font, style, fixed;
    int width;

    if (h_type==V6)
    	return v6ro_fit_word(line_buffer, len);

    /* Can't scroll if in lower line of status window --
       just let it keep going - will be stopped by display_char */
    if (screen_window==STATUS_WINDOW && sy==status_size-1)
    	return true;

    if (screen_window==STATUS_WINDOW)
        return sx+len <= h_screen_cols;

    /* Right, we know we're in the text window */
    pos=seg_left(seg_next(current_seg));

    style=seg_style(current_seg);
    font=seg_font(current_seg);
    fixed=fixed_space_bit;

    width=text_length(line_buffer, len, font, style, fixed);

    return (pos+width <= SWidth);
}


/*
 * set_font
 *
 * Set a new character font. Font can be either be:
 *
 *    TEXT_FONT (1)     = normal text character font
 *    GRAPHICS_FONT (3) = graphical character font
 *
 */

void set_font(int font_type)
{
    display_font=font_type;

    if (h_type==V6)
    {
        return;
    }

    if (screen_window==TEXT_WINDOW)
    {
        if (seg_len(current_seg)!=0)
        {
            current_seg=seg_insert(current_seg);
            current_ptr=seg_text(current_seg);
        }

        seg_font(current_seg)=font_type;
    }
}

void z_read_mouse(int table)
{
    update_mouse_info();
    set_word(table, mouse_y);
    set_word(table+2, mouse_x);
    set_word(table+4, mouse_butt);
    set_word(table+6, (last_menu<<8)+last_item);
}

void translate_name(char *name)
{
    char temp[FILENAME_SIZE];
    char *p;

    strcpy(temp, name);

    strcpy(name, "Zip2000Files:");

    for (p=temp; *p; p++)
    	*p=toupper(*p);

    p=strchr(temp, '.');

    if (p==0)
    	strcat(temp, ".AUX");

    p=strchr(temp, '.');

    strcat(name, p+1);

    xosfile_create_dir(name, 0);

    *p='\0';
    strcat(name, ".");
    strcat(name, temp);
}

static void open_directory(const char *file)
{
    wimp_message m;
    filer_message_open_dir *f = (filer_message_open_dir *) &m.data;
    m.size = ((28+strlen(file)+4)&~3);
    m.action = message_FILER_OPEN_DIR;
    m.your_ref = 0;
    *(int *)&f->fs_no = fileswitch_FS_NUMBER_NONE;
    f->flags = NONE;
    strcpy(f->dir_name, file);
    wimp_send_message(wimp_USER_MESSAGE, &m, wimp_BROADCAST);
}

static void get_default_path(char *out, int type)
{
    char filename[FILENAME_SIZE];
    char gamedir[FILENAME_SIZE];
    const char *leaf;
    const char *ext;
    const char *central;
    const char *aux = "";
    char *gameleaf;
    bool save = IS_SAVE(type);

    type = GAME_TYPE(type);

    osfscontrol_canonicalise_path(StoryName, gamedir, NULL, NULL, sizeof gamedir);
    gameleaf = strrchr(gamedir, '.')+1;
    gameleaf[-1]='\0';

    ext = file_ext[GAME_IDX(type)];
    switch (type)
    {
      case GAME_SAVE:
        central="Zip2000Save:";
        leaf=SAVE_NAME;
        break;
      case GAME_SCRIPT:
        leaf=SCRIPT_NAME;
        break;
      case GAME_RECORD:
        leaf=RECORD_NAME;
        break;
      case GAME_AUXSAVE:
        central="Zip2000Files:";
        aux="ext.";
        leaf="X";
        break;
    }

    switch (default_path[type &~ GAME_SAVE])
    {
        case 0:
           sprintf(filename, "%s%s%s", central, aux, gameleaf);
           break;
        case 1:
           sprintf(filename, "%s%s", central, gameleaf);
           if (save) xosfile_create_dir(filename, 0);
           sprintf(filename, "%s%s.%s%s", central, gameleaf, aux, leaf);
           break;
        case 2:
           sprintf(filename, "<Zip2000$Dir>.^.%s%s", aux, leaf);
           break;
        case 3:
           sprintf(filename, "%s.%s", gamedir, leaf);
           break;
        case 4:
           if (save && type != GAME_AUXSAVE)
           {
               sprintf(filename, "%s.%s", gamedir, ext);
               xosfile_create_dir(filename, 0);
           }
           sprintf(filename, "%s.%s.%s", gamedir, ext, gameleaf);
           break;
        case 5:
           if (save && type != GAME_AUXSAVE)
           {
               sprintf(filename, "%s.^.%s", gamedir, ext);
               xosfile_create_dir(filename, 0);
           }
           sprintf(filename, "%s.^.%s.%s", gamedir, ext, gameleaf);
           break;
    }

    osfscontrol_canonicalise_path(filename, out, NULL, NULL, FILENAME_SIZE);
}

static void create_aux_name(char *out, const char *gamename, bool save)
{
    char temp[FILENAME_SIZE];
    char path[FILENAME_SIZE];
    char *p;

    strcpy(temp, gamename);
    for (p=temp; *p; p++)
    	*p=toupper(*p);

    p = strchr(temp, '.');
    if (p==0) strcat(temp, ".AUX");
    p = strchr(temp, '.');
    *p = '\0';
    if (save)
    {
        sprintf(path, "%s.^.^.%s", out, p+1);
        xosfile_create_dir(path, 0);
    }
    sprintf(path, "%s.^.^.%s.%s", out, p+1, temp);
    osfscontrol_canonicalise_path(path, out, NULL, NULL, FILENAME_SIZE);
}

static char *save_name;

void open_save_directory(void)
{
    char buffer[FILENAME_SIZE], *p;

    if (save_name)
        strcpy(buffer, save_name);
    else
        get_default_path(buffer, GAME_RESTORE);
    p = strrchr(buffer, '.');
    if (!p) return;
    *p = '\0';

    open_directory(buffer);
}

/*
 * get_file_name
 *
 * Return the name of a file. Flag can be one of:
 *    GAME_SAVE    - Save file (write only)
 *    GAME_RESTORE - Save file (read only)
 *    GAME_SCRIPT  - Script file (write only)
 *    GAME_RECORD  - Keystroke record file (write only)
 *    GAME_PLAYBACK - Keystroke record file (read only)
 *
 */

int get_file_name (char *file_name, char *default_name, int flag, int prompt)
{
    char buffer[FILENAME_SIZE];
    char dir[FILENAME_SIZE], *p, *leaf;
    zword_t buffer2[FILENAME_SIZE];
    int status = 0, continuing = 0;
    int terminator;
    bool savebox, silent;
    os_error *e;

    /* If no default file name then supply the standard name */

    if (default_name[0] == '\0')
        get_default_path(default_name, flag);

    if (GAME_TYPE(flag) == GAME_AUXSAVE)
        create_aux_name(default_name, file_name, IS_SAVE(flag));

    strcpy(dir, default_name);
    p = strrchr(dir, '.');
    if (p)
    {
        *p = '\0';
        leaf = p+1;
    }
    else
        leaf = dir;

    if (prompt == -1)
        prompt = file_prompt[GAME_IDX(flag)];

    silent = !replaying && !prompt;
    savebox = !replaying && !silent && IS_SAVE(flag) && file_savebox[GAME_IDX(flag)];

    if (!silent && !savebox)
    {
        /* Prompt for the file name */

        output_line(msgs_lookup_u(IS_SAVE(flag) ? "EnterFNameS" : "EnterFNameL"));
        output_string(msgs_lookup_u_1("DefltIs", leaf));
    }

    buffer2[0]='\0';

    setup_save_box(default_name, flag, silent, savebox);

    if (savebox)
    {
        open_save_box(true);
        do
        {
            poll();
            terminator = NextKey();
        } while (terminator != key_FILE &&
                 terminator != -1);
    }
    else if (silent)
    {
        /* Act as if they just pressed return */
        terminator = zscii_NEWLINE;
    }
    else
    {
        do
        {
            terminator=get_line(FILENAME_SIZE-1, buffer2, 0, continuing);
            continuing = 1;
            if (terminator == zscii_F2)
                open_directory(dir);
            else if (IS_SAVE(flag) && terminator == zscii_F3)
                open_save_box(false);
        }
        while (terminator != zscii_NEWLINE &&
               terminator != key_FILE &&
               terminator != -1);
    }

    finish_save_box();

    if (!silent && !savebox && terminator != key_FILE) output_new_line();

    if (!silent) reset_line_counts();

    if (terminator == -1)
    	return 1;

    if (terminator == key_FILE)
    {
        strcpy(file_name, os_file_supplied());
        if (strcmp(file_name, default_name)==0)
        {
            /* Don't record a matching filename - saves splurging out a
             * complete path every time they click on Save.
             */
            buffer2[0] = '\0';
        }
        else
            system_string_to_unicode(buffer2, file_name);
    }
    else if (buffer2[0] == '\0')
    {
        strcpy(file_name, default_name);
        system_string_to_unicode(buffer2, leaf);
    }
    else
    {
        unicode_string_to_system(buffer, buffer2, '?');
        leaf[-1]='.';
        leaf[0]='\0';
        e = xosfscontrol_canonicalise_path(buffer, file_name, NULL, dir, FILENAME_SIZE, SKIP);
        if (e) { error(e); return 1; }
    }

    /* Record the file name if it was OK */

    if (recording && !replaying)
        record_line(buffer2, zscii_NEWLINE);

    /* Check if we are going to overwrite the file */

    if (warn_overwrite && !replaying && !silent && IS_SAVE(flag)
             && terminator != key_FILE)
    {
        fileswitch_object_type ot=fileswitch_NOT_FOUND;
        char c, *yn;

        /* Check if file already exists */
        xosfile_read_stamped(file_name, &ot, SKIP, SKIP, SKIP, SKIP, SKIP);

        if (ot != fileswitch_NOT_FOUND)
        {
            output_line(msgs_lookup_u("OverWrt"));
            output_string(msgs_lookup_u("Proceed"));

    	    yn=msgs_lookup("YesNo");

            do
            {
                c = input_character(0);
                c = tolower(c);
            } while (c != yn[0] && c != yn[1]);

            output_char(c);
            output_new_line();

            /* If no overwrite then fail the routine */

            if (c == yn[1]) /* No */
                status = 1;
        }
    }

    return status;

}/* get_file_name */

/*void ro_print_error(void)
{
    output_line(_kernel_last_oserror()->errmess);
}*/

void v5ro_input_blank(const zword_t *buffer)
{
    int length;

    length=strlen_u(buffer);

    if (screen_window==STATUS_WINDOW)
    {
        int oldx=sx;

        while (length--)
            upper_text[sy][--sx]=' ';

        mark_area(sx*char_width, (oldx+1)*char_width, sy, sy);
    }
    else
    {
        unsigned style;
        int old, new;
        old=seg_left(seg_next(current_seg));
        if (seg_font(current_seg)==TEXT_FONT)
        {
            style=seg_style(current_seg);
            new=old;
            while (length--)
                new -= fwidth[style][unicode_to_native(*(--current_ptr), '?')];
        }
        else
        {
            new = old - length*char_width;
            current_ptr-=length;
        }

        seg_left(seg_next(current_seg))=new;
        seg_len(current_seg)=current_ptr-seg_text(current_seg);
        *current_ptr='\0';
        mark_area(new-2048, old+2048, current_row, current_row);
    }
}

void v5ro_input_place_caret(const zword_t *buffer, int left, int pos)
{
    if (screen_window==STATUS_WINDOW)
        input_x=(left+pos)*char_width;
    else
    {
        if (seg_font(current_seg)==TEXT_FONT)
        {
            unsigned style, i;
            const zword_t *lc;

            lc=seg_text(current_seg)+seg_len(current_seg)-strlen_u(buffer);

            style=seg_style(current_seg);

            input_x=left;

            for (i=0; i<pos; i++)
                input_x+=fwidth[style][unicode_to_native(lc[i], '?')];
        }
        else
            input_x=left+pos*char_width;
    }

    place_caret();
}

int v5ro_input_get_left(const zword_t *buffer)
{
    int left, i;
    int length=strlen_u(buffer);

    if (screen_window==STATUS_WINDOW)
    	left=sx-length;
    else
    {
        left=seg_left(seg_next(current_seg));
        if (seg_font(current_seg)==TEXT_FONT)
        {
            unsigned style=seg_style(current_seg);

            for (i=length; i>0; i--)
                left-=fwidth[style][unicode_to_native(*(current_ptr-i), '?')];
        }
        else
            left+=length*char_width;
    }

    return left;
}

int v5ro_input_will_fit(const zword_t *buffer, unsigned u_char)
{
    unsigned style=seg_style(current_seg);
    int right, cwidth;

    NOT_USED(buffer);

    right=seg_left(seg_next(current_seg));

    if (seg_font(current_seg)==TEXT_FONT)
        cwidth=fwidth[style][unicode_to_native(u_char, '?')];
    else
        cwidth=char_width;

    return right+cwidth <= SWidth;
}

#if 0
int v5ro_at_left()
{
    if (screen_window == TEXT_WINDOW)
        return current_seg==lower_window[current_row];
    else
        return sx == 0;
}
#endif
