/* more or less ripped from http://msaconverter.free.fr/index-uk.html */
/* big hack, full of security holes... */
/* as it is, don't use it. Just rip the routines you need. */
/* what it does:
 * take a MSA file in input, read it into the array 'data',
 * decode it to a ST raw floppy into the array 'dest'.
 * Then it runs through the FAT, file by file.
 * Each file is decoded into the array 'file' and output
 * to the file out/XXX where XXX is the filename found in
 * the FAT.
 *
 * Note that the subdirectory 'out/' must exist.
 *
 * Normally the code can go to subdirectories of the floppy, but
 * it was not done (there is an 'abort' in the code) because the
 * floppies I needed don't have subdirectories. So I didn't
 * bother creating subdirectories in 'out/' and managing the up
 * and down to save files at the correct place.
 * Anyway, just a hack...
 *
 * For some floppies, go to:
 * http://atariste.free.fr/index.html
 * I took:
 * http://atariste.free.fr/INDY3A.ZIP
 * http://atariste.free.fr/INDY3B.ZIP
 * http://atariste.free.fr/INDY3C.ZIP
 * that's what I needed. That's why I wrote this little hack.
 * Then:
 *   mkdir out
 *   ./msa INDY3A.MSA
 *   mv out diskA
 *   mkdir out
 *   ./msa INDY3B.MSA
 *   mv out diskB
 *   mkdir out
 *   ./msa INDY3C.MSA
 *   mv out diskC
 * And put all the files into one directory and enjoy ScummVM. (Some files
 * appear in several disks and are different! I don't know why. And I
 * don't care. Neither do you, do you?)
 * So be it.
 * this hack is a public domain shit. Feel free to fill your ass with it
 * as you like. Or the ass of someone else. As long as the person agrees.
 * Not to be used by pedophiles. Since pedophilia is illegal. Anyway.
 * Fuck me, I'm anonymous.
 * I just wanted to salute Grigory Perelman for he refuses the Fields
 * and also the Clay shit. So man, you wanted to remain anonymous but
 * still enjoy some fun with your funky brain? You're doomed. I feel
 * like you. Except I'll never do anything useful, so the world won't
 * give a shit. So I'll stay anonymous.
 * What do you think of that, huh? I beat you, clearly. Ah, and cut
 * your beard, you look ugly.
 *
 * Fuck this insane world.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char data[4096*1024];
int datalen;

unsigned char dest[4096*1024];
int destlen;

unsigned char file[4096*1024];

unsigned short identifier;
unsigned short sectors;
unsigned short sides;
unsigned short starttrack;
unsigned short endtrack;
int sector_size = 512;

unsigned short getword(int pos)
{
  return (data[pos] << 8) | data[pos+1];
}

void go(void)
{
  int src, dst;
  int track;
  int face;

  /* get stuff about msa file */
  identifier = getword(0);
  sectors = getword(2);
  sides = getword(4) + 1;
  starttrack = getword(6);
  endtrack = getword(8);

  printf("id %d sectors %d sides %d starttrack %d endtrack %d\n",
         identifier, sectors, sides, starttrack, endtrack);

  src = 10;
  dst = 0;
  destlen = (endtrack-starttrack+1) * sides * sectors * sector_size;
  printf("dstlen %d\n", destlen);

  for (track = starttrack; track <= endtrack; track++) {
    for (face = 1; face <= sides; face++) {
      int track_size;
      int count;

      track_size = getword(src);
      src += 2;

printf("track size %d (%d)\n", track_size, sector_size * sectors);
      if (track_size == sector_size * sectors) {
        memcpy(dest+dst, data+src, track_size);
        dst += track_size;
        src += track_size;
        if (dst > destlen) abort();
        continue;
      }

      int saved_src = src;

      count = 0;
      while (count < sector_size * sectors) {
        int c;
        int nb_data;
        int i;

        c = data[src];
        src++;

        if (c != 0xe5) {
          dest[dst] = c;
          dst++;
          count++;
          if (dst > destlen) abort();
          continue;
        }

        c = data[src];
        src++;
        nb_data = getword(src);
        src += 2;

        for (i = 0; i < nb_data; i++) {
          dest[dst] = c;
          dst++;
          if (dst > destlen) abort();
        }
        count += nb_data;
      }
      if (src - saved_src != track_size) abort();
    }
  }
  {
    FILE *f = fopen("out.st", "w");
    fwrite(dest, dst, 1, f);
    fclose(f);
  }
}

void get(char *fn)
{
  FILE *f = fopen(fn, "r");
  int i=0;
  if (!f) { perror(fn); exit(1); }
  while (1) {
    int c = fgetc(f);
    if (c == EOF) break;
    data[i] = c;
    i++;
  }
  datalen = i;
  fclose(f);
}

unsigned short gw(int pos)
{
  return (dest[pos+1] << 8) | dest[pos];
}

int dir_entries;
int cluster_size;
int start_rep;
int sector_size;

int get_cluster_state(int cluster)
{
  int adr;
  int num;
  int calc;

  if (cluster % 2 == 0) {
    adr = sector_size + ((3 * cluster) >> 1);
    num = dest[adr];
    calc = dest[adr+1] & 0xf;
    calc <<= 8;
    num += calc;
    return num;
  }

  adr = sector_size + ((3 * (cluster - 1)) >> 1) + 1;
  calc = dest[adr] & 0xf0;
  calc >>= 4;
  num = dest[adr+1] << 4;
  num += calc;
  return num;
}

void browse(int p, int level)
{
  int i;
  char name[16];
  int len;

  i = 0;

more:
  if (dest[p+i] != 0xe5 && dest[p+i] != 0) {
    /* not empty entry */
    /* get name */
    int n = 0;
    int nd = 0;
    while (n != 8 && dest[p+i+n] != 32) {
      int x = dest[p+i+n];
      if (n == 0 && x == 5) x = 0xe5;
      name[nd] = x; nd++;
      n++;
    }
    name[nd] = '.'; nd++;
    n = 8;
    /* get extension */
    while (n != 11 && dest[p+i+n] != 32) {
      name[nd] = dest[p+i+n]; nd++;
      n++;
    }
    name[nd] = 0;
    /* name of floppy? */
    if ((dest[p+i+11] & 8) == 8) {
      printf("floppy name %s\n", name);
      goto next;
    }
    printf("file name %s\n", name);
    if (!strcmp(name, "..") || !strcmp(name, "...")) goto next;

    /* file or dir? */
    if ((dest[p+i+11] & 0x10) == 0x10) {
      /* dir */
      int np;
      printf("  DIR\n");
      /* first cluster of dir */
      np = dest[p+i+26] + (dest[p+i+27] << 8);
      /* browse dir */
abort();
      browse((np-2) * cluster_size + start_rep + (dir_entries << 5), level+1);
      goto next;
    }

    /* file */
    {
      int cluster = dest[p+i+26] + (dest[p+i+27] << 8);
      int src;
      int dst;
      int todo;
      int next_cluster;
      char fn[64];
      FILE *out;

      len = dest[p+i+28] + (dest[p+i+29] << 8) +
            (dest[p+i+30]<<16) + (dest[p+i+31] << 24);
      todo = len;

      printf("  start cluster %d, len %d\n", cluster, len);

      src = (cluster - 2) * cluster_size + start_rep + (dir_entries << 5);
      dst = 0;

      if (len <= cluster_size) {
        memcpy(file, dest+src, len);
        goto save_file;
      }

      next_cluster = cluster;

      while (1) {
        if (todo <= cluster_size) {
          memcpy(file+dst, dest+src, todo);
          goto save_file;
        }

        memcpy(file+dst, dest+src, cluster_size);
        dst += cluster_size;
        todo -= cluster_size;

        next_cluster = get_cluster_state(next_cluster);

        printf("  cluster %d next cluster %d\n", cluster, next_cluster);

        if (next_cluster < 2 || next_cluster > 4080)
          goto save_file;

        src = (next_cluster - 2) * cluster_size;
        src += start_rep + (dir_entries << 5);
      }

save_file:
      sprintf(fn, "out/%s", name);
      out = fopen(fn, "w");
      if (!out) abort();
      fwrite(file, len, 1, out);
      fclose(out);
    }
  }

next:
  i += 32;

  if (level == 0) {
    if (i >= dir_entries * 32 || dest[p+i] == 0) return;
    goto more;
  }

  /* not sure of this code, this is the case of subdirectories
   * and I don't deal with that that much.
   */
  if (i < cluster_size) {
    if (dest[p+i] == 0) return;
    goto more;
  }

  /* over? just return? shouldn't we proceed to next cluster or something? */
}

void view(void)
{
  int byte_per_sect = gw(11);
  int sec_per_cluster = dest[13];
  int nb_fats = dest[16];
  int endtrack = gw(19);
  int fat_size = gw(22);
  int nb_sectors = gw(24);
  int nb_sides = gw(26);
  dir_entries = gw(17);

  sector_size = byte_per_sect;

  endtrack /= nb_sectors * nb_sides;
  endtrack--;

  start_rep = nb_fats * fat_size * byte_per_sect + byte_per_sect;
  cluster_size = sec_per_cluster * byte_per_sect;

  printf("byte per sect %d\n", byte_per_sect);
  printf("sector per cluster %d\n", sec_per_cluster);
  printf("%d fats\n", nb_fats);
  printf("%d dir entries\n", dir_entries);
  printf("endtrack %d\n", endtrack);
  printf("fat size %d\n", fat_size);
  printf("sectors %d\n", nb_sectors);
  printf("sides %d\n", nb_sides);

  printf("start of rep %d\n", start_rep);
  printf("cluster size %d\n", cluster_size);

  browse(start_rep, 0);
}

int main(int n, char **v)
{
  if (n != 2) { printf("gimme a file\n"); return 1; }

  get(v[1]);
  printf("disk len %d\n", datalen);

  go();
  view();

  return 0;
}
