/*
 * 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: vms.c,v 2.10 2000/03/03 11:25:35 leo Exp $ */

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

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

/**
 * return the current revision of this module
 * $Revision: 2.10 $
 */
extern char *vms_revision() {
  return "$Id: vms.c,v 2.10 2000/03/03 11:25:35 leo Exp $";
}

/**
 * read the vms file into a buffer and return the length, the
 * third argument will be the header struct in the file.
 */
extern int vms_read(char *file, char **data, struct vms *header) {
  FILE *vms_fp;
  struct stat vms_stat;
  int n, offset = 0x00;

  /* get the file information and print error if a problem occurs */
  if(stat(file, &vms_stat)) {
    fprintf(stderr, "error: cannot read vms file: \"%s\"\n", file);
    return -E_VMS_NOT_FOUND;
  }

  *data = (char *)malloc(vms_stat.st_size);
  clear(*data, vms_stat.st_size);
  vms_fp = fopen(file, "rb");
  if((n = fread(*data, 1, vms_stat.st_size, vms_fp)) != vms_stat.st_size) {
    if(feof(vms_fp)) fprintf(stderr, "error: early EOF in file\n");
    if(ferror(vms_fp))
      fprintf(stderr, "error: short read from vms file (%d != %d)\n",
		      n, vms_stat.st_size);
    return -E_FATAL_ERROR;
  }
  fclose(vms_fp);

  if(*data[0] == 'L' && *data[1] == 'C' && *data[2] == 'D') {
    fprintf(stderr, "error: specified file is a dc animator LCD file\n");
    return -E_VMS_IS_LCD;
  }


  if(*data[0] == 0x21) offset = 0x200;

  memcpy((char *)header, *data+offset, 0x80);
  clear((*data)+VMS_CHECKSUM+offset, 2);
  header->icons = *data+offset+VMS_ICONS;
  header->ec_palette = 
    *data+offset+VMS_ICONS+(512 * C2TOI(header->icon_count));

  return vms_stat.st_size;
}

/**
 * display information found about a vms file
 */
extern void vms_print(char *vmsfname, char *command) {
  /* depending on the file type set the offset */
  char *data;
  struct vms header;
  /* read the vms file */
  int size = vms_read(vmsfname, &data, &header);
  if(size < 0) exit(-size);

  printf("-- vms information ----------------------------------------------\n");
  printf("vmu description: %-16.16s\n", header.vmu_description);
  printf("rom description: %-32.32s\n", header.rom_description);
  printf("creator        : %-16.16s\n", header.creator);
  printf("# of icons     : %d\n", C2TOI(header.icon_count));
  printf("animation speed: %d\n", C2TOI(header.anim_speed));
  printf("eyecatch type  : %d\n", C2TOI(header.eyecatch));
  printf("checksum       : $%x %s\n", C2TOI(header.checksum),
	 C2TOI(header.checksum) == 
	 check_crc(data, 0x80 + C2TOI(header.icon_count) * 512 +
			 (C2TOI(header.eyecatch) == 0 ? 0 :
			  (C2TOI(header.eyecatch) == 1 ? 8064 :
			   (C2TOI(header.eyecatch) == 2 ? 4544 : 2048))) +
			 C4TOI(header.data_size)) ? "ok" : "failed");
  printf("data size      : %d\n", C4TOI(header.data_size));
  printf("-----------------------------------------------------------------\n");

  if(command != NULL && strlen(command) > 0) {
    char *gif = (char *)malloc(strlen(vmsfname) + 5);
    sprintf(gif, "%s.gif", vmsfname);
    vms_write_icon(header, command, gif);
  }
}

/**
 * calculate the crc for a vms file
 * this code is borrowed from marcus@idonex.se
 */
int check_crc(const unsigned char *buf, int size) {
  int i, c, n = 0;

  for (i = 0; i < size; i++) {
    n ^= (buf[i]<<8);
    for (c = 0; c < 8; c++)
      if (n & 0x8000)
        n = (n << 1) ^ 4129;
      else
        n = (n << 1);
  }
  return n & 0xffff;
}

/**
 * write the icons read from the vms into a picture file using the
 * program specified to convert them into animated gifs
 * the program must be able to read ppm data
 */
int vms_write_icon(struct vms header, char *command, char *file) {
  char icon[1024*3]; 
  FILE *icon_fp;
  int i;

  char *pipe = (char *) malloc(strlen(command) + strlen(file) + 10);
  sprintf(pipe, command, C2TOI(header.anim_speed) * 10, file);
  printf("info: writing to pipe: %s\n", pipe);

  if((icon_fp = popen(pipe, "w")) == NULL) {
    fprintf(stderr, "error: error running command: %s\n", pipe);
    exit(E_FATAL_ERROR);
  }

 /* P6
  * # ... comments
  * # ...
  * width height
  * 255
  * rgbrgb ...
  */
  for(i = 0; i < C2TOI(header.icon_count); i++) {
    fprintf(icon_fp, "P6\n");
    fprintf(icon_fp, "# description: %-16.16s\n", header.vmu_description);
    fprintf(icon_fp, "# description: %-32.32s\n", header.rom_description);
    fprintf(icon_fp, "# creator    : %-16.16s\n", header.creator);
    fprintf(icon_fp, "# icons      : %d\n", C2TOI(header.icon_count));
    fprintf(icon_fp, "32 32\n");
    fprintf(icon_fp, "255\n");
    decode_icon(header.icons + 512 * i, header.palette, &icon); 
    if(fwrite(&icon, 1, 1024 * 3, icon_fp) != 1024 * 3) {
      fprintf(stderr, "error: could not write image file\n");
      exit(E_FATAL_ERROR);
    }
  }
  fclose(icon_fp);
}

#define DBL(b) (((b) & 0xf) | (((b) & 0xf) << 4))

/**
 * decode icon data into RGB values stretched to a whole 256 color map
 */
int decode_icon(const char *data, const char *palette, char *icon) {
  int x, y, index = 0;
  char p[16][2];
  memcpy((char *)&p, palette, 16*2);

  for(y = 0; y < 32; y++) {
    for(x = 0; x < 16; x++) {
      char *nybble = p[(data[y * 16 + x] >> 4) & 0x0f];
      icon[index++] = DBL(nybble[1] & 0xf);
      icon[index++] = DBL((nybble[0] >> 4) & 0xf);
      icon[index++] = DBL(nybble[0] & 0xf);
      nybble = p[data[y * 16 + x]  & 0x0f];
      icon[index++] = DBL(nybble[1] & 0xf);
      icon[index++] = DBL((nybble[0] >> 4) & 0xf);
      icon[index++] = DBL(nybble[0] & 0xf);
    }
  }
}
