#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define P00_HEADER_SIZE    26
#define P00_CBMNAME_OFFSET 8
#define M2I_ENTRY_LEN      33
#define M2I_ENTRY_OFFSET   18
#define M2I_CBMNAME_OFFSET 15
#define M2I_FATNAME_OFFSET 2
#define M2I_FATNAME_LEN    12

void stripspaces(char *buffer) {
  int len = strlen(buffer);
  while (len-- > 0) {
    if (buffer[len] == ' ')
      buffer[len] = 0;
    else
      return;
  }
}

const char pc64header[] = {
  0x43,0x36,0x34,0x46,0x69,0x6c,0x65
};

char buffer[1024*1024];
char linebuf[33];

int main(int argc, char *argv[]) {
  if (argc != 3) {
    fprintf(stderr,"Syntax: m2i2pc64 m2ifile outdir\n");
    exit(1);
  }

  FILE *m2ifile;
  int curdir;

  curdir = open(".",O_RDONLY);
  if (curdir < 0) {
    perror("open current directory");
    exit(1);
  }

  m2ifile = fopen(argv[1],"rb");
  if (!m2ifile) {
    perror("fopen input");
    exit(1);
  }

  /* Skip header */
  if (fread(linebuf,M2I_ENTRY_OFFSET,1,m2ifile) != 1) {
    perror("read header");
    exit(1);
  }

  int line = 1;

  while (fread(linebuf,M2I_ENTRY_LEN,1,m2ifile) == 1) {
    FILE *infile, *outfile;

    line++;

    /* Skip entries with invalid format */
    if (linebuf[1] != ':' || linebuf[M2I_CBMNAME_OFFSET-1] != ':') {
      fprintf(stderr,"Invalid format in line %d\n",line);
      continue;
    }

    /* Skip entries with unknown/deleted typechar */
    if (tolower(linebuf[0]) != 'p' &&
        tolower(linebuf[0]) != 's' &&
        tolower(linebuf[0]) != 'u') {
      fprintf(stderr,"Invalid typechar %c in line %d\n",linebuf[0],line);
      continue;
    }

    /* Open input file */
    memcpy(buffer, linebuf + M2I_FATNAME_OFFSET, M2I_FATNAME_LEN);
    buffer[M2I_FATNAME_LEN] = 0;
    stripspaces(buffer);
    printf("%d: %s\n",line,buffer);

    infile = fopen(buffer,"rb");
    if (infile == NULL) {
      /* Try lowercasing the file name */
      char *ptr = buffer;
      do { *ptr = tolower(*ptr); } while (*ptr++);
      infile = fopen(buffer,"rb");
    }
    if (infile == NULL) {
      fprintf(stderr,"Error opening input file '%s' (line %d): %s\n",
              buffer,line,strerror(errno));
      exit(1);
    }

    /* Open destination file */
    switch (tolower(linebuf[0])) {
    case 'p':
      strcat(buffer,".p00");
      break;

    case 's':
      strcat(buffer,".s00");
      break;

    case 'u':
      strcat(buffer,".u00");
      break;
    }

    if (chdir(argv[2]) != 0) {
      perror("chdir to destination");
      exit(1);
    }
    
    outfile = fopen(buffer,"wb");
    if (outfile == NULL) {
      fprintf(stderr,"Error opening output file '%s' (line %d): %s\n",
              buffer,line,strerror(errno));
      exit(1);
    }

    /* Write header */
    memset(buffer, 0, P00_HEADER_SIZE);
    memcpy(buffer, pc64header, sizeof(pc64header));
    memcpy(buffer+P00_CBMNAME_OFFSET, linebuf+M2I_CBMNAME_OFFSET, 16);
    stripspaces(buffer+P00_CBMNAME_OFFSET);

    if (fwrite(buffer, P00_HEADER_SIZE, 1,outfile) != 1) {
      perror("write header");
      exit(1);
    }

    /* Copy data */
    size_t bytecount;

    do {
      bytecount = fread(buffer, 1, sizeof(buffer), infile);
      if (ferror(infile)) {
        perror("read data");
        exit(1);
      }
      bytecount = fwrite(buffer, 1, bytecount, outfile);
      if (ferror(outfile)) {
        perror("write data");
        exit(1);
      }
    } while (!feof(infile));

    fclose(infile);
    fclose(outfile);
    if (fchdir(curdir) != 0) {
      perror("open original directory");
    }
  }

  exit(0);
}

