コード例 #1
0
ファイル: lynx.c プロジェクト: GunioRobot/quickdev16
int
lynx_init (st_rominfo_t *rominfo)
{
  int result = -1;

  rominfo->console_usage = lynx_usage[0].help;
  rominfo->copier_usage = unknown_usage[0].help;

  ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom);
  if (!strncmp (lnx_header.magic, "LYNX", 4))
    result = 0;
  else
    result = -1;
  if (ucon64.console == UCON64_LYNX)
    result = 0;

  if (!strncmp (lnx_header.magic, "LYNX", 4))
    {
      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
        ucon64.buheader_len : (int) LNX_HEADER_LEN;

      if (UCON64_ISSET (ucon64.buheader_len) && !ucon64.buheader_len)
        return ucon64.console == UCON64_LYNX ? 0 : result;

      ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom);
      rominfo->buheader = &lnx_header;

      // internal ROM name
      strcpy (rominfo->name, lnx_header.cartname);

      // ROM maker
      rominfo->maker = lnx_header.manufname;

      // misc stuff
      sprintf (rominfo->misc,
        "Internal Size: Bank0 %hd Bytes (%.4f Mb)\n"
        "               Bank1 %hd Bytes (%.4f Mb)\n"
        "Version: %hd\n"
        "Rotation: %s",
#ifdef  WORDS_BIGENDIAN
        bswap_16 (lnx_header.page_size_bank0) * 256,
        TOMBIT_F (bswap_16 (lnx_header.page_size_bank0) * 256),
        bswap_16 (lnx_header.page_size_bank1) * 256,
        TOMBIT_F (bswap_16 (lnx_header.page_size_bank1) * 256),
        bswap_16 (lnx_header.version),
#else
        lnx_header.page_size_bank0 * 256,
        TOMBIT_F (lnx_header.page_size_bank0 * 256),
        lnx_header.page_size_bank1 * 256,
        TOMBIT_F (lnx_header.page_size_bank1 * 256),
        lnx_header.version,
#endif
        (!lnx_header.rotation) ? "No" : ((lnx_header.rotation == 1) ? "Left" : "Right"));
    }

  return result;
}
コード例 #2
0
ファイル: ppf.c プロジェクト: GunioRobot/quickdev16
int
ppf_set_fid (const char *ppf, const char *fidname)
{
    int fidsize, ppfsize, pos;
    char ppfname[FILENAME_MAX],
         fidbuf[MAX_ID_SIZE + 34 + 1] = "@BEGIN_FILE_ID.DIZ"; // +1 for string terminator

    strcpy (ppfname, ppf);
    ucon64_file_handler (ppfname, NULL, 0);
    fcopy (ppf, 0, fsizeof (ppf), ppfname, "wb"); // no copy if one file

    printf ("Adding FILE_ID.DIZ (%s)...\n", fidname);
    fidsize = ucon64_fread (fidbuf + 18, 0, MAX_ID_SIZE, fidname);
    memcpy (fidbuf + 18 + fidsize, "@END_FILE_ID.DIZ", 16);

    ppfsize = fsizeof (ppfname);
    pos = ucon64_find (ppfname, 0, ppfsize, "@BEGIN_FILE_ID.DIZ", 18,
                       MEMCMP2_CASE | UCON64_FIND_QUIET);
    if (pos == -1)
        pos = ppfsize;
    truncate (ppfname, pos);

    ucon64_fwrite (fidbuf, pos, fidsize + 18 + 16, ppfname, "r+b");
    pos += fidsize + 18 + 16;
#ifdef  WORDS_BIGENDIAN
    fidsize = bswap_32 (fidsize);                 // Write file size in little-endian format
#endif
    ucon64_fwrite (&fidsize, pos, 4, ppfname, "r+b");

    printf (ucon64_msg[WROTE], ppfname);
    return 0;
}
コード例 #3
0
ファイル: lynx.c プロジェクト: GunioRobot/quickdev16
static int
lynx_b (st_rominfo_t *rominfo, int bank, const char *value)
{
  st_lnx_header_t header;
  short int *bankvar;
  char dest_name[FILENAME_MAX];

  if (!rominfo->buheader_len)
    {
      fprintf (stderr, "ERROR: This is no LNX file\n\n");
      return -1;
    }

  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);

  bankvar = (bank == 0 ? &header.page_size_bank0 : &header.page_size_bank1);
  if ((atol (value) % 64) != 0 || (atol (value) > 512))
    *bankvar = 0;
  else
#ifdef  WORDS_BIGENDIAN
    *bankvar = bswap_16 (atol (value) * 4);
#else
    *bankvar = atol (value) * 4;
#endif

  strcpy (dest_name, ucon64.rom);
  ucon64_file_handler (dest_name, NULL, 0);
  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");

  printf (ucon64_msg[WROTE], dest_name);
  return 0;
}
コード例 #4
0
ファイル: pce.c プロジェクト: Godzil/quickdev16
int
pce_swap (st_ucon64_nfo_t *rominfo)
{
  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
  unsigned char *rom_buffer;
  int size = ucon64.file_size - rominfo->backup_header_len;

  if ((rom_buffer = (unsigned char *) malloc (size)) == NULL)
    {
      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size);
      return -1;
    }

  strcpy (src_name, ucon64.fname);
  strcpy (dest_name, ucon64.fname);
  ucon64_file_handler (dest_name, src_name, 0);

  if (rominfo->backup_header_len)                    // copy header (if present)
    fcopy (src_name, 0, rominfo->backup_header_len, dest_name, "wb");

  ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name);
  swapbits (rom_buffer, size);
  ucon64_fwrite (rom_buffer, rominfo->backup_header_len, size, dest_name,
            rominfo->backup_header_len ? "ab" : "wb");
  free (rom_buffer);

  printf (ucon64_msg[WROTE], dest_name);
  remove_temp_file ();
  return 0;
}
コード例 #5
0
ファイル: lynx.c プロジェクト: GunioRobot/quickdev16
int
lynx_n (st_rominfo_t *rominfo, const char *name)
{
  st_lnx_header_t header;
  char dest_name[FILENAME_MAX];

  if (!rominfo->buheader_len)
    {
      fprintf (stderr, "ERROR: This is no LNX file\n\n");
      return -1;
    }

  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);

  memset (header.cartname, 0, sizeof (header.cartname));
  strncpy (header.cartname, name, sizeof (header.cartname));

  strcpy (dest_name, ucon64.rom);
  ucon64_file_handler (dest_name, NULL, 0);
  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");

  printf (ucon64_msg[WROTE], dest_name);
  return 0;
}
コード例 #6
0
ファイル: pce.c プロジェクト: Godzil/quickdev16
int
pce_f (st_ucon64_nfo_t *rominfo)
/*
  Region protection codes are found in (American) TurboGrafx-16 games. It
  prevents those games from running on a PC-Engine. One search pattern seems
  sufficient to fix/crack all TG-16 games. In addition to that, the protection
  code appears to be always somewhere in the first 32 kB.
*/
{
  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], buffer[32 * 1024];
  int bytesread, n;

  puts ("Attempting to fix region protection code...");

  strcpy (src_name, ucon64.fname);
  strcpy (dest_name, ucon64.fname);
  ucon64_file_handler (dest_name, src_name, 0);
  fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); // no copy if one file

  if ((bytesread = ucon64_fread (buffer, rominfo->backup_header_len, 32 * 1024, src_name)) <= 0)
    return -1;

  // '!' == ASCII 33 (\x21), '*' == 42 (\x2a)
  if (rominfo->interleaved)
    n = change_mem (buffer, bytesread, "\x94\x02\x0f", 3, '*', '!', "\x01", 1, 0);
  else
    n = change_mem (buffer, bytesread, "\x29\x40\xf0", 3, '*', '!', "\x80", 1, 0);

  ucon64_fwrite (buffer, rominfo->backup_header_len, 32 * 1024, dest_name, "r+b");

  printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
  printf (ucon64_msg[WROTE], dest_name);
  remove_temp_file ();
  return n;
}
コード例 #7
0
ファイル: jaguar.c プロジェクト: GunioRobot/quickdev16
int
jaguar_init (st_rominfo_t *rominfo)
{
  int result = -1, x, value;

  rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
    ucon64.buheader_len : 0;

  ucon64_fread (&jaguar_header, JAGUAR_HEADER_START +
    rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom);
  value = 0;
  for (x = 0; x < 12; x++)
    value += OFFSET (jaguar_header, x);
  if (value == 0xb0)
    result = 0;
  else
    {
      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
        ucon64.buheader_len : (int) UNKNOWN_HEADER_LEN;

      ucon64_fread (&jaguar_header, JAGUAR_HEADER_START +
          rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom);
      value = 0;
      for (x = 0; x < 12; x++)
        value += OFFSET (jaguar_header, x);

      if (value == 0xb0)
        result = 0;
      else
        result = -1;
    }
  if (ucon64.console == UCON64_JAG)
    result = 0;

  rominfo->header_start = JAGUAR_HEADER_START;
  rominfo->header_len = JAGUAR_HEADER_LEN;
  rominfo->header = &jaguar_header;

  rominfo->console_usage = jaguar_usage[0].help;
  rominfo->copier_usage = unknown_usage[0].help;

  return result;
}
コード例 #8
0
ファイル: pce.c プロジェクト: Godzil/quickdev16
// header format is specified in src/backup/ffe.h
int
pce_msg (st_ucon64_nfo_t *rominfo)
{
  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
  unsigned char *rom_buffer = NULL;
  st_msg_header_t header;
  int size = ucon64.file_size - rominfo->backup_header_len;

  if (rominfo->interleaved)
    if ((rom_buffer = (unsigned char *) malloc (size)) == NULL)
      {
        fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size);
        return -1;
      }

  memset (&header, 0, MSG_HEADER_LEN);
  header.size = (unsigned char) (size / 8192);
  header.emulation = size == 3 * MBIT ? 1 : 0;
  header.id1 = 0xaa;
  header.id2 = 0xbb;
  header.type = 2;

  strcpy (src_name, ucon64.fname);
  strcpy (dest_name, ucon64.fname);
  set_suffix (dest_name, ".msg");
  ucon64_file_handler (dest_name, src_name, 0);

  ucon64_fwrite (&header, 0, MSG_HEADER_LEN, dest_name, "wb");
  if (rominfo->interleaved)
    {
      // Magic Super Griffin files should not be "interleaved"
      ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name);
      swapbits (rom_buffer, size);
      ucon64_fwrite (rom_buffer, MSG_HEADER_LEN, size, dest_name, "ab");
      free (rom_buffer);
    }
  else
    fcopy (src_name, rominfo->backup_header_len, size, dest_name, "ab");

  printf (ucon64_msg[WROTE], dest_name);
  remove_temp_file ();
  return 0;
}
コード例 #9
0
ファイル: lynx.c プロジェクト: GunioRobot/quickdev16
static int
lynx_rot (st_rominfo_t *rominfo, int rotation)
{
  st_lnx_header_t header;
  char dest_name[FILENAME_MAX];

  if (!rominfo->buheader_len)
    {
      fprintf (stderr, "ERROR: This is no LNX file\n\n");
      return -1;
    }

  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);

  header.rotation = rotation;                   // header.rotation is an 8-bit field

  strcpy (dest_name, ucon64.rom);
  ucon64_file_handler (dest_name, NULL, 0);
  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");

  printf (ucon64_msg[WROTE], dest_name);
  return 0;
}
コード例 #10
0
ファイル: pce.c プロジェクト: Godzil/quickdev16
// see src/backup/mgd.h for the file naming scheme
int
pce_mgd (st_ucon64_nfo_t *rominfo)
{
  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
  unsigned char *rom_buffer = NULL;
  int size = ucon64.file_size - rominfo->backup_header_len;

  if (!rominfo->interleaved)
    if ((rom_buffer = (unsigned char *) malloc (size)) == NULL)
      {
        fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size);
        return -1;
      }

  strcpy (src_name, ucon64.fname);
  mgd_make_name (ucon64.fname, UCON64_PCE, size, dest_name);
  ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME);

  // bit-swapping images for the MGD2 only makes sense for owners of a TG-16
  //  (American version of the PCE)
  if (!rominfo->interleaved)
    {
      ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name);
      swapbits (rom_buffer, size);
      ucon64_fwrite (rom_buffer, 0, size, dest_name, "wb");
      free (rom_buffer);
    }
  else
    fcopy (src_name, rominfo->backup_header_len, size, dest_name, "wb");

  printf (ucon64_msg[WROTE], dest_name);
  remove_temp_file ();

  mgd_write_index_file ((char *) basename2 (dest_name), 1);
  return 0;
}
コード例 #11
0
ファイル: ppf.c プロジェクト: GunioRobot/quickdev16
// based on sourcecode of MakePPF v2.0 Linux/Unix by Icarus/Paradox
int
ppf_create (const char *orgname, const char *modname)
{
    FILE *orgfile, *modfile, *ppffile;
    char ppfname[FILENAME_MAX], buffer[MAX_ID_SIZE], obuf[512], mbuf[512];
#if 0
    char *fidname = "FILE_ID.DIZ";
#endif
    int x, osize, msize, blocksize, n_changes, total_changes = 0;
    unsigned int seekpos = 0, pos;

    osize = fsizeof (orgname);
    msize = fsizeof (modname);
#ifndef DIFF_FSIZE
    if (osize != msize)
    {
        fprintf (stderr, "ERROR: File sizes do not match\n");
        return -1;
    }
#endif

    if ((orgfile = fopen (orgname, "rb")) == NULL)
    {
        fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], orgname);
        exit (1);
    }
    if ((modfile = fopen (modname, "rb")) == NULL)
    {
        fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], modname);
        exit (1);
    }
    strcpy (ppfname, modname);
    set_suffix (ppfname, ".ppf");
    ucon64_file_handler (ppfname, NULL, 0);
    if ((ppffile = fopen (ppfname, "wb")) == NULL)
    {
        fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], ppfname);
        exit (1);
    }

    // creating PPF 2.0 header
    fwrite ("PPF20", 5, 1, ppffile);              // magic
    fputc (1, ppffile);                           // encoding method
    memset (buffer, ' ', 50);
    fwrite (buffer, 50, 1, ppffile);              // description line
#ifdef  WORDS_BIGENDIAN
    x = bswap_32 (osize);
    fwrite (&x, 4, 1, ppffile);
#else
    fwrite (&osize, 4, 1, ppffile);               // orgfile size
#endif
    fseek (orgfile, 0x9320, SEEK_SET);
    memset (buffer, 0, 1024);                     // one little hack that makes PPF
    fread (buffer, 1024, 1, orgfile);             //  suitable for files < 38688 bytes
    fwrite (buffer, 1024, 1, ppffile);            // 1024 byte block

    printf ("Writing patch data, please wait...\n");
    // finding changes
    fseek (orgfile, 0, SEEK_SET);
    fseek (modfile, 0, SEEK_SET);
    while ((blocksize = fread (obuf, 1, 255, orgfile)))
    {
        blocksize = fread (mbuf, 1, blocksize, modfile);
#ifdef  DIFF_FSIZE
        if (blocksize == 0)
            break;
#endif
        pos = seekpos;
        x = 0;
        while (x != blocksize)
        {
            if (obuf[x] != mbuf[x])
            {
                pos = seekpos + x;
                n_changes = 0;
                do
                {
                    buffer[n_changes] = mbuf[x];
                    n_changes++;
                    x++;
                }
                while (x != blocksize && obuf[x] != mbuf[x]);
                total_changes += n_changes;
#ifdef  WORDS_BIGENDIAN
                pos = bswap_32 (pos);
#endif
                fwrite (&pos, 4, 1, ppffile);
                fputc (n_changes, ppffile);
                fwrite (buffer, n_changes, 1, ppffile);
            }
            else
                x++;
        }
        seekpos += blocksize;
    }

#ifdef  DIFF_FSIZE
    if (msize > osize)
    {
        pos = seekpos;
        while ((blocksize = fread (buffer, 1, 255, modfile)))
        {
            total_changes += blocksize;
#ifdef  WORDS_BIGENDIAN
            x = bswap_32 (pos);
            fwrite (&x, 4, 1, ppffile);
#else
            fwrite (&pos, 4, 1, ppffile);
#endif
            fputc (blocksize, ppffile);
            fwrite (buffer, blocksize, 1, ppffile);
            pos += blocksize;
        }
    }
    else if (msize < osize)
        printf ("WARNING: %s is smaller than %s\n"
                "         PPF can't store information about that fact\n",
                modname, orgname);
#endif

    fclose (orgfile);
    fclose (modfile);

    if (total_changes == 0)
    {
        printf ("%s and %s are identical\n"
                "Removing: %s\n", orgname, modname, ppfname);
        fclose (ppffile);
        remove (ppfname);
        return -1;
    }

#if 0
    if (fidname)
    {
        int fsize = fsizeof (fidname);
        if (fsize > MAX_ID_SIZE)
            fsize = MAX_ID_SIZE;                    // File id only up to 3072 bytes!
        printf ("Adding FILE_ID.DIZ (%s)...\n", fidname);
        ucon64_fread (buffer, 0, fsize, fidname);
        fwrite ("@BEGIN_FILE_ID.DIZ", 18, 1, ppffile);
        fwrite (buffer, fsize, 1, ppffile);
        fwrite ("@END_FILE_ID.DIZ", 16, 1, ppffile);
#ifdef  WORDS_BIGENDIAN
        fsize = bswap_32 (fsize);                 // Write file size in little-endian format
#endif
        fwrite (&fsize, 4, 1, ppffile);
    }
#endif
    fclose (ppffile);

    printf (ucon64_msg[WROTE], ppfname);
    return 0;
}
コード例 #12
0
ファイル: pce.c プロジェクト: Godzil/quickdev16
int
pce_init (st_ucon64_nfo_t *rominfo)
{
  int result = -1, size, swapped, x;
  unsigned char *rom_buffer;
  st_pce_data_t *info, key;

  x = ucon64.file_size % (16 * 1024);
  rominfo->backup_header_len = UCON64_ISSET (ucon64.backup_header_len) ?
    ucon64.backup_header_len : (ucon64.file_size > x ? x : 0);

  size = ucon64.file_size - rominfo->backup_header_len;
  if ((rom_buffer = (unsigned char *) malloc (size)) == NULL)
    {
      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size);
      return -1;
    }
  ucon64_fread (rom_buffer, rominfo->backup_header_len, size, ucon64.fname);

  if (pce_check (rom_buffer, size) == 1)
    result = 0;

  swapped = -1;
  /*
    29 40 f0 => unhacked
    29 40 80 => U.S.A. -> Jap hack
    94 02 0f => bit-swapped and unhacked
    94 02 01 => bit-swapped and hacked

    According to Cowering 2 or 3 games don't use these opcodes to check if a
    Japanese game is running on an American console. If they are present then
    it is in the first 500 (or so) bytes.
    The check for "A..Z" is a fix for Puzznic (J). The check for "HESM" a fix
    for:
    Fire Pro Wrestling - 2nd Bout Sounds
    Fire Pro Wrestling 3 - Legend Bout Sounds
  */
  x = size > 32768 ? 32768 : size;
  if ((memmem2 (rom_buffer, x, "\x94\x02\x0f", 3, 0) ||
       memmem2 (rom_buffer, x, "\x94\x02\x01", 3, 0)) &&
       memmem2 (rom_buffer, x, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, 0) == 0 &&
       memcmp (rom_buffer, "HESM", 4))
    swapped = 1;
  if (UCON64_ISSET (ucon64.interleaved))
    swapped = ucon64.interleaved;

  if ((result == -1 && swapped != 0) || swapped == 1)
    {                                   // don't swap the bits if -nint is specified
      if (!UCON64_ISSET (ucon64.do_not_calc_crc) || swapped == 1)
        ucon64.fcrc32 = crc32 (0, rom_buffer, size);
      swapbits (rom_buffer, size);
      if (pce_check (rom_buffer, size) == 1)
        {
          swapped = 1;
          result = 0;
        }
      if (swapped != 1)
        {
          ucon64.crc32 = ucon64.fcrc32;
          ucon64.fcrc32 = 0;
        }
    }
  if (swapped != -1)
    rominfo->interleaved = swapped;

  if (ucon64.console == UCON64_PCE)
    result = 0;

  rominfo->header_start = PCE_HEADER_START;
  rominfo->header_len = PCE_HEADER_LEN;
  rominfo->console_usage = pce_usage[0].help;
  rominfo->backup_usage = rominfo->backup_header_len ? msg_usage[0].help : mgd_usage[0].help;

  if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
    {
      if (!ucon64.crc32)
        ucon64.crc32 = crc32 (0, rom_buffer, size);
      // additional info
      key.crc32 = ucon64.crc32;
      info = (st_pce_data_t *) bsearch (&key, pce_data,
                                        sizeof pce_data / sizeof (st_pce_data_t),
                                        sizeof (st_pce_data_t), pce_compare);
      if (info)
        {
          if (info->maker)
            rominfo->maker = NULL_TO_UNKNOWN_S (pce_maker[MIN (info->maker,
                                                               PCE_MAKER_MAX - 1)]);

          if (info->serial)
            if (info->serial[0])
              {
                strcat (rominfo->misc, "\nSerial: ");
                strcat (rominfo->misc, info->serial);
              }

          if (info->date)
            {
              int day = info->date / 10000, month = (info->date % 10000) / 100,
                  year = info->date % 100;
              char date[80];

              date[0] = 0;
              if (day)
                sprintf (date, "\nDate: %d/%d/19%d", day, month, year);
              else if (month)
                sprintf (date, "\nDate: %d/19%d", month, year);
              else if (year)
                sprintf (date, "\nDate: 19%d", year);
              strcat (rominfo->misc, date);
            }

          if (info->comment)
            if (info->comment[0])
              {
                strcat (rominfo->misc, "\nComment: ");
                strcat (rominfo->misc, info->comment);
              }
        }
    }

  free (rom_buffer);
  return result;
}
コード例 #13
0
ファイル: atari.c プロジェクト: Godzil/quickdev16
int
atari_init (st_ucon64_nfo_t * rominfo)
{
  int i, j, bsmode, size = ucon64.file_size;
  unsigned int crc32;
  static char backup_usage[80];
  unsigned char first, image[ATARI_ROM_SIZE], buffer[0x200];
  char md5[32];

  if (size > ATARI_ROM_SIZE)
    return -1;

  if (size != 0x800  && size != 0x1000  && size != 0x2000 && size != 0x2100 &&
      size != 0x2800 && size != 0x28ff  && size != 0x3000 && size != 0x4000 &&
      size != 0x8000 && size != 0x10000 && size != 0x20000)
    return -1;

  ucon64_fread (image, 0, size, ucon64.fname);
  ucon64_chksum (NULL, md5, &crc32, ucon64.fname, ucon64.file_size, 0);

  bsmode = get_game_bsmode_by_crc (crc32);
  if (bsmode == -1)
    bsmode = get_game_bsmode_by_md5 (md5);

  if (bsmode == -1)
    {
      if (!(size % 8448))
        bsmode = BSM_AR;
      else if (size == 2048 || !memcmp (image, image + 2048, 2048))
        bsmode = BSM_2K;
      else if (size == 4096 || !memcmp (image, image + 4096, 4096))
        bsmode = BSM_4K;
      else if (size == 8192 || !memcmp (image, image + 8192, 8192))
        bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F8;
      else if (size == 10495 || (size == 10240))
        bsmode = BSM_DPC;
      else if (size == 12288)
        bsmode = BSM_FASC;
      else if (size == 32768)
        {
          // Assume this is a 32K super-cart then check to see if it is
          bsmode = BSM_F4SC;

          first = image[0];
          for (i = 0; i < 0x100; i++)
            if (image[i] != first)
              {
                // It's not a super cart (probably)
                bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F4;
                break;
              }
        }
      else if (size == 65536)
        bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_MB;
      else if (size == 131072)
        bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_MC;
      else
        {
          // Assume this is a 16K super-cart then check to see if it is
          bsmode = BSM_F6SC;

          first = image[0];
          for (i = 0; i < 0x100; i++)
            if (image[i] != first)
              {
                // It's not a super cart (probably)
                bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F6;
                break;
              }
        }
    }

  if (bsmode > 0)
    {
      memset (&atari_rominfo, 0, sizeof (st_atari_rominfo_t));

      atari_rominfo.bsm = bsmode;

      // set game_page_count and empty_page[]
      for (i = 0; i < size / 0x100; i++)
        {
          ucon64_fread (buffer, i * 0x100, 0x100, ucon64.fname);
          atari_rominfo.empty_page[i] = 1;

          for (j = 0; j < 0x100 - 1; j++)
            if (buffer[j] != buffer[j + 1])
              {
                atari_rominfo.empty_page[i] = 0;
                atari_rominfo.game_page_count++;
                break;
              }
        }

      atari_rominfo.speed_hi = (unsigned char) (atari_rominfo.game_page_count / 21 + 1);
      atari_rominfo.speed_low = (unsigned char) (atari_rominfo.game_page_count * 0x100 / 21 - (atari_rominfo.speed_hi - 1) * 0x100);

      atari_rominfo.ctrl_byte = get_bsmode_by_id (atari_rominfo.bsm)->ctrl_byte;

      // the first two bytes of data indicate the beginning address of the code
      if (atari_rominfo.bsm != BSM_3F)
        {
          ucon64_fread (buffer, get_bsmode_by_id (atari_rominfo.bsm)->start_page * 0x100, 0x100, ucon64.fname);
          atari_rominfo.start_low = buffer[0xfc];
          atari_rominfo.start_hi = buffer[0xfd];
        }

      rominfo->console_usage = atari_usage[0].help;
      // "Cuttle Card (2)/Starpath) Supercharger/YOKO backup unit"
      snprintf (backup_usage, 80, "%s/%s/%s", cc2_usage[0].help,
                spsc_usage[0].help, yoko_usage[0].help);
      backup_usage[80 - 1] = 0;
      rominfo->backup_usage = backup_usage;

      sprintf (rominfo->misc,
               "Bankswitch type: %s\n"
               "Start address: 0x%02x%02x\n"
               "Speed: 0x%02x%02x\n"
               "Control byte: 0x%02x\n"
               "Page count: %d\n"
               "Blank pages: %d\n"
               "Start page: %d",
               get_bsmode_by_id (atari_rominfo.bsm)->bsm_s,
               atari_rominfo.start_hi,
               atari_rominfo.start_low,
               atari_rominfo.speed_hi,
               atari_rominfo.speed_low,
               atari_rominfo.ctrl_byte,
               atari_rominfo.game_page_count,
               size / 0x100 - atari_rominfo.game_page_count,
               get_bsmode_by_id (atari_rominfo.bsm)->start_page);
      return 0;
    }

  return -1;
}