/*
 * amiga.c
 * Detection of Amiga partition maps and file systems
 *
 * Copyright (c) 2003 Christoph Pfisterer
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

#include "global.h"

/*
 * Amiga partition type codes
 *
 * Based on the dostypes list of the Ambient file manager
 * as of Apr 02, 2005.
 */

struct dostype {
  char *typecode;
  int isfs;   /* true = native Amiga filesystem (affects printout when */
              /*  found at the start of the boot sector) */
  char *name;
};

struct dostype amiga_dostypes[] = {

  { "DOS\x00", 1, "Amiga OFS file system (non-intl.)" },
  { "DOS\x01", 1, "Amiga FFS file system (non-intl.)" },
  { "DOS\x02", 1, "Amiga OFS file system (intl., no dir cache)" },
  { "DOS\x03", 1, "Amiga FFS file system (intl., no dir cache)" },
  { "DOS\x04", 1, "Amiga OFS file system (intl., dir cache)" },
  { "DOS\x05", 1, "Amiga FFS file system (intl., dir cache)" },
  { "DOS\x06", 1, "Amiga OFS file system (LNFS)" },
  { "DOS\x07", 1, "Amiga FFS file system (LNFS)" },

  { "muFS",    1, "Amiga muFS FFS file system (intl., no dir cache)" },
  { "muF\x00", 1, "Amiga muFS OFS file system (non-intl.)" },
  { "muF\x01", 1, "Amiga muFS FFS file system (non-intl.)" },
  { "muF\x02", 1, "Amiga muFS OFS file system (intl., no dir cache)" },
  { "muF\x03", 1, "Amiga muFS FFS file system (intl., no dir cache)" },
  { "muF\x04", 1, "Amiga muFS OFS file system (intl., dir cache)" },
  { "muF\x05", 1, "Amiga muFS FFS file system (intl., dir cache)" },

  { "SFS\x00", 1, "Amiga Smart File System" },

  { "PFS\x00", 1, "Amiga PFS file system 0" },
  { "PFS\x01", 1, "Amiga PFS file system 1" },
  { "PFS\x02", 1, "Amiga PFS file system 2" },
  { "PFS\x03", 1, "Amiga PFS file system 3" },
  { "PDS\x02", 1, "Amiga PFS file system 2, SCSIdirect" },
  { "PDS\x03", 1, "Amiga PFS file system 3, SCSIdirect" },
  { "muPF",    1, "Amiga PFS file system, multiuser" },

  { "AFS\x00", 1, "Amiga AFS file system" },
  { "AFS\x01", 1, "Amiga AFS file system (experimental)" },

  { "UNI\x00", 0, "Amiga Amix 0" },
  { "UNI\x01", 0, "Amiga Amix 1" },

  { "KICK",    1, "Amiga Kickstart disk" },
  { "BOOU",    1, "Amiga generic boot disk" },
  { "BAD\x00", 0, "Unreadable disk" },
  { "NDOS",    0, "Not a DOS disk" },
  { "resv",    0, "reserved" },

  { "CD00",    0, "CD-ROM High Sierra format" },
  { "CD01",    0, "CD-ROM ISO9660 format" },
  { "CDDA",    0, "CD Audio" },
  { "CDFS",    0, "CD-ROM - Amiga CDrive or AmiCDFS" },
  { "\x66\x2d\xab\xac", 0, "CD-ROM - AsimCDFS" },

  { "NBR\x07", 0, "NetBSD root" },
  { "NBS\x01", 0, "NetBSD swap" },
  { "NBU\x07", 0, "NetBSD other" },

  { "LNX\x00", 0, "Linux native" },
  { "EXT2",    0, "Linux ext2" },
  { "SWAP",    0, "Linux swap" },
  { "SWP\x00", 0, "Linux swap" },
  { "MNX\x00", 0, "Linux minix" },

  { "MAC\x00", 0, "Macintosh HFS" },
  { "MSD\x00", 0, "MS-DOS disk" },
  { "MSH\x00", 0, "MS-DOS PC-Task hardfile" },
  { "BFFS",    0, "Berkeley Fast Filesystem" },

  { NULL, 0, NULL },
};

static char * get_name_for_dostype(const unsigned char *dostype)
{
  int i;

  for (i = 0; amiga_dostypes[i].name; i++)
    if (memcmp(dostype, amiga_dostypes[i].typecode, 4) == 0)
      return amiga_dostypes[i].name;
  return "Unknown";
}

static void format_dostype(char *buf, const unsigned char *dostype)
{
  int i;
  unsigned char c;
  char *p;

  p = buf;
  for (i = 0; i < 4; i++) {
    c = dostype[i];
    if (c < 10) {
      *p++ = '\\';
      *p++ = '0' + c;
    } else if (c < 32) {
      sprintf(p, "0x%02x", (int)c);
      p = strchr(p, 0);
    } else {
      *p++ = (char)c;
    }
  }
  *p = 0;
}

/*
 * Amiga "Rigid Disk" partition map
 */

void detect_amiga_partmap(SECTION *section, int level)
{
  int i, off, found;
  unsigned char *buf;
  char s[256], append[64];
  u4 blocksize, part_ptr;
  u8 cylsize, start, size;

  for (off = 0, found = 0; off < 16; off++) {
    if (get_buffer(section, off * 512, 512, (void **)&buf) < 512)
      break;

    if (memcmp(buf, "RDSK", 4) == 0) {
      found = 1;
      break;
    }
  }
  if (!found)
    return;

  if (off == 0)
    print_line(level, "Amiga Rigid Disk partition map");
  else
    print_line(level, "Amiga Rigid Disk partition map at sector %d", off);

  /* get device block size (?) */
  blocksize = get_be_long(buf + 16);
  if (blocksize < 256 || (blocksize & (blocksize-1))) {
    print_line(level+1, "Illegal block size %lu", blocksize);
    return;
  } else if (blocksize != 512) {
    print_line(level+1, "Unusual block size %lu, not sure this will work...", blocksize);
  }
  /* TODO: get geometry data for later use */

  /* walk the partition list */
  part_ptr = get_be_long(buf + 28);
  for (i = 1; part_ptr != 0xffffffffUL; i++) {
    if (get_buffer(section, (u8)part_ptr * 512, 256,
		   (void **)&buf) < 256) {
      print_line(level, "Partition %d: Can't read partition info block");
      break;
    }

    /* check signature */
    if (memcmp(buf, "PART", 4) != 0) {
      print_line(level, "Partition %d: Invalid signature");
      break;
    }

    /* get "next" pointer for next iteration */
    part_ptr = get_be_long(buf + 16);

    /* get sizes */
    cylsize = (u8)get_be_long(buf + 140) * (u8)get_be_long(buf + 148);
    start = get_be_long(buf + 164) * cylsize;
    size = (get_be_long(buf + 168) + 1 - get_be_long(buf + 164)) * cylsize;

    snprintf(append, 63, " from %llu", start);
    format_blocky_size(s, size, 512, "sectors", append);
    print_line(level, "Partition %d: %s",
               i, s);

    /* get name */
    get_pstring(buf + 36, s);
    if (s[0])
      print_line(level + 1, "Drive name \"%s\"", s);

    /* show dos type */
    format_dostype(s, buf + 192);
    print_line(level + 1, "Type \"%s\" (%s)", s,
               get_name_for_dostype(buf + 192));

    /* detect contents */
    if (size > 0 && start > 0) {
      analyze_recursive(section, level + 1,
			start * 512, size * 512, 0);
    }
  }
}

/*
 * Amiga file system
 */

void detect_amiga_fs(SECTION *section, int level)
{
  unsigned char *buf;
  int i, isfs;
  char s[256], *typename;

  if (get_buffer(section, 0, 512, (void **)&buf) < 512)
    return;

  /* look for one of the signatures */
  isfs = 0;
  typename = NULL;
  for (i = 0; amiga_dostypes[i].name; i++)
    if (memcmp(buf, amiga_dostypes[i].typecode, 4) == 0) {
      isfs = amiga_dostypes[i].isfs;
      typename = amiga_dostypes[i].name;
      break;
    }
  if (typename == NULL)
    return;

  if (isfs) {

    print_line(level, "%s", typename);

    format_dostype(s, buf);
    print_line(level + 1, "Type \"%s\"", s);

    if (section->size == 512*11*2*80) {
      print_line(level+1, "Size matches DD floppy");
    } else if (section->size == 512*22*2*80) {
      print_line(level+1, "Size matches HD floppy");
    }

  } else {

    format_dostype(s, buf);
    print_line(level, "Amiga type code \"%s\" (%s)", s, typename);

  }
}

/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1