/*
 * This file is part of "VirtuaMUnstaz Demo Distribution".
 *
 * This 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, or (at your option)
 * any later version.
 *
 * "VirtuaMUnstaz Demo Distribution" is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* $Id: vmi.c,v 2.8 2000/03/15 06:41:54 leo Exp $ */

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

#include "vmi.h"
#include "vms.h"
#include "util.h"

/**
 * return the current revision of this module
 * $Revision: 2.8 $
 */
extern char *vmi_revision() {
  return "$Id: vmi.c,v 2.8 2000/03/15 06:41:54 leo Exp $";
}

/**
 * compare the checksum for a vmi file
 */
int checksum(char *sum, char *name, int type) {
  switch(type) {
    case SEGA:
      return sum[0] == (name[0] & 'S') &&
             sum[1] == (name[1] & 'E') &&
	     sum[2] == (name[2] & 'G') &&
	     sum[3] == (name[3] & 'A');
    case AGES:
      return sum[0] == (name[0] & 'A') &&
             sum[1] == (name[1] & 'G') &&
	     sum[2] == (name[2] & 'E') &&
	     sum[3] == (name[3] & 'S');
    default:
      fprintf(stderr, "warning: unknown checksum type: %d\n", type);
  }
  return 0;
}


/**
 * read vmi file data and check some basics for correctness.
 */
extern int vmi_read(char *vmi_fname, struct vmi *data) {
  FILE *vmi_fp;		/* the vmi file pointer */
  struct stat vmi_stat;	/* the vmi file info */
  char *tmp;		/* a temporary buffer */

  /* get the file information and print error if a problem occurs */
  if(stat(vmi_fname, &vmi_stat))
    return E_VMI_NOT_FOUND;

  /* check file extension and file size to catch common mistakes */
  if(strncasecmp(vmi_fname+strlen(vmi_fname)-4, ".vmi", 4))
    return E_VMI_WRONG_EXTENSION;

  /* load the data into an unstructured temporary buffer */
  tmp = (char *)malloc(vmi_stat.st_size);
  vmi_fp = fopen(vmi_fname, "r");
  if(fread(tmp, 1, vmi_stat.st_size, vmi_fp) != vmi_stat.st_size)
    return E_FATAL_ERROR;
  fclose(vmi_fp);

  /* check file size to make sure it is a vmi file */
  if(vmi_stat.st_size != 108) {
    if(vmi_stat.st_size > 108 &&
       (tmp[VMI_OFF_RESOURCE] & 'S' == tmp[VMI_OFF_CHECKSUM] || 
        tmp[VMI_OFF_RESOURCE] & 'A' == tmp[VMI_OFF_CHECKSUM]))
      fprintf(stderr, "warning: corrupt vmi file: size incorrect\n");
    else
      return E_VMI_WRONG_FILESIZE;
  }

  /* copy data into struct vmi */
  memcpy((char *)data, tmp, 108);

  return 0;
}

/**
 * print information that is contained in the vmi file
 */
extern void vmi_print(char *vmi_fname) {
  /* read vmi data into struct */
  struct vmi data;
  int error = vmi_read(vmi_fname, &data);
  switch(error) {
    case E_VMI_NOT_FOUND:
      fprintf(stderr, "error: cannot read vmi file: \"%s\"\n", vmi_fname);
      exit(error);
    case E_VMI_WRONG_EXTENSION:
      fprintf(stderr, "error: wrong extension on file: \"%s\"\n", vmi_fname);
      exit(error);
    case E_FATAL_ERROR:
      fprintf(stderr, "error: short read from vmi file\n");
      exit(error);
    case E_VMI_WRONG_FILESIZE:
      fprintf(stderr, "error: wrong vmi file size\n");
      exit(error);
  }

  /* print all of the known vmi file information */
  printf("-- visual memory information ------------------------------------\n");
  printf("title      : %.32s\n", data.title);
  printf("creator    : %.32s\n", data.creator);
  printf("vms date   : %4d/%02d/%02d %02d:%02d:%02d\n",
         C2TOI(data.year),
	 data.month, data.day, data.hour, data.minute, data.second);
  printf("resource   : %.8s\n", data.resource);
  printf("vms file   : %.12s\n", data.file);
  printf("vms type   : %s ($%x)\n", data.type==0x03?"GAME":"DATA", data.type);
  printf("vms size   : %d\n", C2TOI(data.size));
  printf("# of files : %d\n", C2TOI(data.count));
  printf("checksum   : SEGA: %s, AGES: %s\n", 
         checksum(data.checksum, data.resource, SEGA) ? "ok" : "failed",
         checksum(data.checksum, data.resource, AGES) ? "ok" : "failed");
  printf("-----------------------------------------------------------------\n");
}

/**
 * trim a string to a specific length and warn about it
 */
char *trim(char *str, int l) {
  if(str != NULL && strlen(str) > l) {
    fprintf(stderr, "warning: \"%s\" longer than %d, will be trimmed\n",str,l);
    str[l] = 0x00;
  }
  return str;
}

/**
 * check a string if it is empty or contains only spaces
 */
int strchk(char *str) {
  int len;
  if(str == NULL) return 0;
  len = strlen(str);
  while(len-- > 0)
    if(str[len - 1] != ' ') return 1;
  return 0;
}

/**
 * create a new vmi file by asking for info and using the vms
 * data from the filesystem.
 */
extern void vmi_create(char *vms, 	/* original file name of the .vms */
                       char *resource,	/* the resource name */
		       char *file,	/* file name on the vmu */
		       char *title, 	/* title of the vms file */
		       char *creator,	/* creator of the vms file */
                       int checksum,	/* checksum type */
		       int type,	/* file type */
		       char *command,	/* icon converter command */
		       int script) {

  struct stat vms_stat, vmi_stat;	/* file info struct */
  char **vms_data;
  struct vms vms_header;
  char *path = (char *)pathname(vms);	/* get the path if there is one */
  char *fname = (char *)basename(vms);	/* get the file name only */
  char *target = (char *)malloc(strlen(vms)+1);
  struct tm vms_file_time;		/* vms file time info */

  FILE *vmi_fp;				/* vmi file pointer */
  struct vmi data;			/* vmi data contents */
  char *vmi;				/* vmi file name */

  int i, err;


  /* check for VMI first */
  if(!vmi_read(vms, &data)) {
    fprintf(stderr, "error: specified file is a VMI, not a VMS\n");
    exit(E_VMS_IS_VMI);
  }

  /* try to read the file and exit if an error occured */
  if((err = vms_read(vms, &vms_data, &vms_header)) <= 0)
    exit(-err);

  /* get the file information */
  stat(vms, &vms_stat);

  /* if no resource is specified, copy file name and cut off extension */
  if(resource == NULL) {
    resource = strdup(fname);
    if(strrchr(resource, '.') != NULL)
      resource[strrchr(resource, '.') - resource] = 0x00;
  }

  /* if there is still a .VMS extension cut it off */
  if(strlen(resource) > 3 &&
     !strncasecmp(resource+strlen(resource)-4, ".vms", 4)) {
    fprintf(stderr, "warning: resource contains .VMS, cutting it off\n");
    resource[strlen(resource) - 4] = 0x00;
  }

  resource = trim(resource, 8);

  /* check that there actually is a resource left */
  if(resource == NULL || !strlen(resource)) {
    fprintf(stderr, "error: cannot determine vms resource name\n");
    exit(E_VMI_RESOURCE_NIL);
  }

  /* if no vmu file name was specified copy the real file name */
  if(file == NULL) file = strdup(fname);

  file = trim(file, 12);

  /* check that there actually is a vmu file name left */
  if(file == NULL || !strlen(file)) {
    fprintf(stderr, "error: cannot determine vmu file name\n");
    exit(E_VMI_FILE_NIL);
  }

  /* build the file name of the .VMS file and if it differs from the
   * original file name rename it */
  sprintf(target, "%s%s.VMS", path, resource);
  if(strcmp(vms, target)) {
    fprintf(stderr, "warning: renaming \"%s\" to \"%s\"\n", vms, target);
    if(rename(vms, target)) {
      fprintf(stderr, "error: could not rename file\n",vms,target);
      exit(E_FATAL_ERROR);
    }
  }

  /* clear the data just to be sure */
  clear((char *)&data, 108);

  /* add title and creator to vmi file */
  sprintf(data.title, "%-32.32s", 
          strchk(title) ? title : (strchk(vms_header.rom_description) ? 
	                             vms_header.rom_description : "unknown"));
  if(creator == NULL && strchk(vms_header.creator)) {
    creator = (char *)malloc(33);
    sprintf(creator, "original: %-16.16s", vms_header.creator);
  }
  sprintf(data.creator, "%-32.32s", strchk(creator) ? creator :
		                    "see http://VirtuaMUnstaz.de/");

  /* add the vms file time to the vmi */
  vms_file_time = *localtime(&vms_stat.st_mtime);
  vms_file_time.tm_year += 1900;

  ITOC2(data.year, vms_file_time.tm_year);
  data.month  = vms_file_time.tm_mon;
  data.day    = vms_file_time.tm_mday;
  data.hour   = vms_file_time.tm_hour;
  data.minute = vms_file_time.tm_min;
  data.second = vms_file_time.tm_sec;

  ITOC2(data.count, 0x01);
  
  sprintf(data.resource, "%-8.8s", resource);
  sprintf(data.file, "%-12.12s", file);

  data.type = type;
  ITOC2(data.size, vms_stat.st_size);

  /* generate checksum */
  switch(checksum) {
  case AGES:
    printf("creating AGES checksum ...\n");
    data.checksum[0] = 'A' & data.resource[0];
    data.checksum[1] = 'G' & data.resource[1];
    data.checksum[2] = 'E' & data.resource[2];
    data.checksum[3] = 'S' & data.resource[3];
    break;
  case SEGA|AGES:
    printf("creating combined SEGA/AGES checksum ...\n");
    if((data.resource[0] & 'S') == (data.resource[0] & 'A') &&
       (data.resource[1] & 'E') == (data.resource[1] & 'G') &&
       (data.resource[2] & 'G') == (data.resource[2] & 'E') &&
       (data.resource[3] & 'A') == (data.resource[3] & 'S')) {
      data.checksum[0] = 'S' & 'A' & data.resource[0];
      data.checksum[1] = 'E' & 'G' & data.resource[1];
      data.checksum[2] = 'G' & 'E' & data.resource[2];
      data.checksum[3] = 'A' & 'S' & data.resource[3];
      break;
    }
    printf("warning: cannot create combined SEGA/AGES checksum\n");
  default:
    printf("creating SEGA checksum ...\n");
    data.checksum[0] = 'S' & data.resource[0];
    data.checksum[1] = 'E' & data.resource[1];
    data.checksum[2] = 'G' & data.resource[2];
    data.checksum[3] = 'A' & data.resource[3];
  }

  /* build the vmi file name */
  vmi = (char *)malloc(strlen(path) + strlen(resource) + 5);
  sprintf(vmi, "%s%s.VMI", path, resource);

  /* warn befor overwriting the file */
  if(!stat(vmi, &vmi_stat))
    printf("overwriting vmi file \"%s\" ...\n", vmi);
  else
    printf("creating new vmi file \"%s\" ...\n", vmi);


  /* write data to file */
  vmi_fp = fopen(vmi, "wb");
  if(vmi_fp == NULL) {
    fprintf(stderr, "error: could not write vmi file: %s\n", vmi);
    exit(E_FATAL_ERROR);
  }

  if(fwrite(&data, 1, 108, vmi_fp) != 108) {
    fprintf(stderr, "error: could not write file\n");
    exit(E_FATAL_ERROR);
  }

  fclose(vmi_fp);

  /* now try to get the icon */
  if(command != NULL && strlen(command) > 0) {
    char *gif = (char *)malloc(strlen(target) + 5);
    sprintf(gif, "%s.gif", target);
    vms_write_icon(vms_header, command, gif);
  }

  vmi_print(vmi);

  /* a hack to tell the script what was created */
  if(script) {
    fprintf(stderr, "VMI: %s\n", vmi);
    fprintf(stderr, "VMS: %s\n", target);
  }
}
