#include <stdlib.h>
#include <stdio.h>
#include <vga.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../src/libvga.h"

/*
 * Note: Observe that when writing the font to a file, the file to write is
 * opened after vga_init has been called (so that root permissions have been
 * given up). This means that there is no major security hole lurking here.
 */

int main(int argc, char *argv[])
{
    FILE *f;
    unsigned char *font;
    size_t font_size = FONT_SIZE; /* buffer size in 1.2.11 and before */
    int can_set = 0;
    struct stat statbuf;

    if (argc == 1) {
	printf("Restore corrupted textmode font.\n");
	printf("Syntax: restorefont option filename\n");
	printf("	-r filename	Restore VGA font from file.\n");
	printf("	-w filename	Write current VGA font to file.\n");
	return 0;
    }
    if (argv[1][0] != '-') {
	printf("Must specify -r or -w.\n");
	return 1;
    }
    switch (argv[1][1]) {
    case 'r':
    case 'w':
	if (argc != 3) {
	    printf("Must specify filename.\n");
	    return 1;
	}
	break;
    default:
	printf("Invalid option. Must specify -r or -w.\n");
	return 1;
    }
    vga_disabledriverreport();
    vga_setchipset(VGA);	/* avoid SVGA detection */
    vga_init();
    /* we are in TEXT mode. Check for availability of vga_ext_set: */
    if (vga_getmodeinfo(TEXT)->flags & HAVE_EXT_SET) {
	if (vga_ext_set(VGA_EXT_AVAILABLE, VGA_AVAIL_SET) &
		(1 << VGA_EXT_FONT_SIZE)) {
	    can_set = 1;
	    /* Query the preferred data size: */
	    font_size = vga_ext_set(VGA_EXT_FONT_SIZE, 0);
	    if (font_size < FONT_SIZE) {
		    font_size = FONT_SIZE;
	    }	    
	}
    }

    /* We never need more than that memory: */
    font = malloc((size_t)font_size);
    if (!font) {
	puts("restorefont: out of memory.");
	goto ex_no_errno;
    }

    vga_setmode(G640x350x16);
    switch (argv[1][1]) {
      case 'r':
	f = fopen(argv[2], "rb");
	if (f == NULL) {
	  error:
	    perror("restorefont");
	  ex_no_errno:
	    vga_setmode(TEXT);
	    return 1;
	}
	if (fstat(fileno(f), &statbuf))
	    goto error;
	font_size = statbuf.st_size;
	/* Check for sensible sizes: */
	switch (font_size) {
	    case 0x2000:
	    case 0x2000 * 4:
	    case 0x2000 * 8:
		if (can_set)
	    	    vga_ext_set(VGA_EXT_FONT_SIZE, font_size);
		break;
	    default:
	    corrupt:
	        puts("restorefont: input file corrupted.");
	        goto ex_no_errno;
	}
		
	if (1 != fread(font, font_size, 1, f)) {
	    if (errno)
		goto error;
	    goto corrupt;
	}
	fclose(f);
	vga_puttextfont(font);
	break;
      case 'w':
	/* save as much as we have.. */
	if (can_set)
	    vga_ext_set(VGA_EXT_FONT_SIZE, font_size);
	vga_gettextfont(font);
	f = fopen(argv[2], "wb");
	if (f == NULL)
	    goto error;
	if (1 != fwrite(font, font_size, 1, f))
	    goto error;
	if (fclose(f))
	    goto error;
	break;
    }
    vga_setmode(TEXT);
    return 0;
}
