/***************************************************************************************
 *  Genesis Plus
 *  ROM Loading Support
 *
 *  Copyright (C) 1998-2003  Charles Mac Donald (original code)
 *  Copyright (C) 2007-2025  Eke-Eke (Genesis Plus GX)
 *
 *  Redistribution and use of this code or any derivative works are permitted
 *  provided that the following conditions are met:
 *
 *   - Redistributions may not be sold, nor may they be used in a commercial
 *     product or activity.
 *
 *   - Redistributions that are modified from the original source must include the
 *     complete source code, including the source code for all components used by a
 *     binary built from the modified sources. However, as a special exception, the
 *     source code distributed need not include anything that is normally distributed
 *     (in either source or binary form) with the major components (compiler, kernel,
 *     and so on) of the operating system on which the executable runs, unless that
 *     component itself accompanies the executable.
 *
 *   - Redistributions must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other
 *     materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************************/

#include <ctype.h>
#include "shared.h"

/*** ROM Information ***/
#define ROMCONSOLE    256
#define ROMCOPYRIGHT  272
#define ROMDOMESTIC   288
#define ROMWORLD      336
#define ROMTYPE       384
#define ROMPRODUCT    386
#define ROMCHECKSUM   398
#define ROMIOSUPPORT  400
#define ROMROMSTART   416
#define ROMROMEND     420
#define ROMRAMINFO    424
#define ROMRAMSTART   436
#define ROMRAMEND     440
#define ROMMODEMINFO  444
#define ROMMEMO       456
#define ROMCOUNTRY    496

#define P3BUTTONS   0x0001
#define P6BUTTONS   0x0002
#define PKEYBOARD   0x0004
#define PPRINTER    0x0008
#define PBALL       0x0010
#define PFLOPPY     0x0020
#define PACTIVATOR  0x0040
#define PTEAMPLAYER 0x0080
#define PMSYSTEMPAD 0x0100
#define PSERIAL     0x0200
#define PTABLET     0x0400
#define PPADDLE     0x0800
#define PCDROM      0x1000
#define PMOUSE      0x2000
#define PMENACER    0x4000

#define MAXCOMPANY 64
#define MAXPERIPHERALS 15

typedef struct
{
  char companyid[6];
  char company[26];
} COMPANYINFO;

typedef struct
{
  char pID[2];
  char pName[14];
} PERIPHERALINFO;


ROMINFO rominfo;
uint8 romtype;
char rompath[256];

static uint8 rom_region;

/***************************************************************************
 * Genesis ROM Manufacturers
 *
 * Based on the document provided at
 * http://www.zophar.net/tech/files/Genesis_ROM_Format.txt
 **************************************************************************/
static const COMPANYINFO companyinfo[MAXCOMPANY] =
{
  {"ACLD", "Ballistic"},
  {"RSI", "Razorsoft"},
  {"SEGA", "SEGA"},
  {"TREC", "Treco"},
  {"VRGN", "Virgin Games"},
  {"WSTN", "Westone"},
  {"10", "Takara"},
  {"11", "Taito or Accolade"},
  {"12", "Capcom"},
  {"13", "Data East"},
  {"14", "Namco or Tengen"},
  {"15", "Sunsoft"},
  {"16", "Bandai"},
  {"17", "Dempa"},
  {"18", "Technosoft"},
  {"19", "Technosoft"},
  {"20", "Asmik"},
  {"22", "Micronet"},
  {"23", "Vic Tokai"},
  {"24", "American Sammy"},
  {"29", "Kyugo"},
  {"32", "Wolfteam"},
  {"33", "Kaneko"},
  {"35", "Toaplan"},
  {"36", "Tecmo"},
  {"40", "Toaplan"},
  {"42", "UFL Company Limited"},
  {"43", "Human"},
  {"45", "Game Arts"},
  {"47", "Sage's Creation"},
  {"48", "Tengen"},
  {"49", "Renovation or Telenet"},
  {"50", "Electronic Arts"},
  {"56", "Razorsoft"},
  {"58", "Mentrix"},
  {"60", "Victor Musical Ind."},
  {"69", "Arena"},
  {"70", "Virgin"},
  {"73", "Soft Vision"},
  {"74", "Palsoft"},
  {"76", "Koei"},
  {"79", "U.S. Gold"},
  {"81", "Acclaim/Flying Edge"},
  {"83", "Gametek"},
  {"86", "Absolute"},
  {"87", "Mindscape"},
  {"93", "Sony"},
  {"95", "Konami"},
  {"97", "Tradewest"},
  {"100", "T*HQ Software"},
  {"101", "Tecmagik"},
  {"112", "Designer Software"},
  {"113", "Psygnosis"},
  {"119", "Accolade"},
  {"120", "Code Masters"},
  {"125", "Interplay"},
  {"130", "Activision"},
  {"132", "Shiny & Playmates"},
  {"144", "Atlus"},
  {"151", "Infogrames"},
  {"161", "Fox Interactive"},
  {"177", "Ubisoft"},
  {"239", "Disney Interactive"},
  {"---", "Unknown"}
};

/***************************************************************************
 * Genesis Peripheral Information
 *
 * Based on the document provided at
 * http://www.zophar.net/tech/files/Genesis_ROM_Format.txt
 ***************************************************************************/
static const PERIPHERALINFO peripheralinfo[MAXPERIPHERALS] =
{
  {"J", "3B Joypad"},
  {"6", "6B Joypad"},
  {"K", "Keyboard"},
  {"P", "Printer"},
  {"B", "Control Ball"},
  {"F", "Floppy Drive"},
  {"L", "Activator"},
  {"4", "Team Player"},
  {"0", "MS Joypad"},
  {"R", "RS232C Serial"},
  {"T", "Tablet"},
  {"V", "Paddle"},
  {"C", "CD-ROM"},
  {"M", "Mega Mouse"},
  {"G", "Menacer"},
};

/***************************************************************************
 *
 * Compute ROM real checksum.
 ***************************************************************************/
static uint16 getchecksum(uint8 *rom, int length)
{
  int i;
  uint16 checksum = 0;

  for (i = 0; i < length; i += 2)
  {
    checksum += ((rom[i] << 8) + rom[i + 1]);
  }

  return checksum;
}


/***************************************************************************
 * deinterleave_block
 *
 * Convert interleaved (.smd) ROM files.
 ***************************************************************************/
static void deinterleave_block(uint8 * src)
{
  int i;
  uint8 block[0x4000];
  memcpy (block, src, 0x4000);
  for (i = 0; i < 0x2000; i += 1)
  {
    src[i * 2 + 0] = block[0x2000 + (i)];
    src[i * 2 + 1] = block[0x0000 + (i)];
  }
}

/***************************************************************************
 *
 * Pass a pointer to the ROM base address.
 ***************************************************************************/
void getrominfo(char *romheader)
{
  /* Clear ROM info structure */
  memset (&rominfo, 0, sizeof (ROMINFO));

  /* Genesis ROM header support */
  if (system_hw & SYSTEM_MD)
  {
    int i,j;

    memcpy (&rominfo.consoletype, romheader + ROMCONSOLE, 16);
    memcpy (&rominfo.copyright, romheader + ROMCOPYRIGHT, 16);

    /* Domestic (japanese) name */
    rominfo.domestic[0] = romheader[ROMDOMESTIC];
    j = 1;
    for (i=1; i<48; i++)
    {
      if ((rominfo.domestic[j-1] != 32) || (romheader[ROMDOMESTIC + i] != 32))
      {
        rominfo.domestic[j] = romheader[ROMDOMESTIC + i];
        j++;
      }
    }
    rominfo.domestic[j] = 0;

    /* International name */
    rominfo.international[0] = romheader[ROMWORLD];
    j=1;
    for (i=1; i<48; i++)
    {
      if ((rominfo.international[j-1] != 32) || (romheader[ROMWORLD + i] != 32))
      {
        rominfo.international[j] = romheader[ROMWORLD + i];
        j++;
      }
    }
    rominfo.international[j] = 0;

    /* ROM informations */
    memcpy (&rominfo.ROMType, romheader + ROMTYPE, 2);
    memcpy (&rominfo.product, romheader + ROMPRODUCT, 12);
    memcpy (&rominfo.checksum, romheader + ROMCHECKSUM, 2);
    memcpy (&rominfo.romstart, romheader + ROMROMSTART, 4);
    memcpy (&rominfo.romend, romheader + ROMROMEND, 4);
    memcpy (&rominfo.country, romheader + ROMCOUNTRY, 16);

    /* Checksums */
#ifdef LSB_FIRST
    rominfo.checksum =  (rominfo.checksum >> 8) | ((rominfo.checksum & 0xff) << 8);
#endif
    rominfo.realchecksum = getchecksum(((uint8 *) cart.rom) + 0x200, cart.romsize - 0x200);

    /* Supported peripherals */
    rominfo.peripherals = 0;
    for (i = 0; i < 14; i++)
      for (j=0; j < MAXPERIPHERALS; j++)
        if (romheader[ROMIOSUPPORT+i] == peripheralinfo[j].pID[0])
          rominfo.peripherals |= (1 << j);
  }
  else
  {
    uint16 offset = 0;

    /* detect Master System ROM header */
    if (!memcmp (&romheader[0x1ff0], "TMR SEGA", 8))
    {
      offset = 0x1ff0;
    }
    else if (!memcmp (&romheader[0x3ff0], "TMR SEGA", 8))
    {
      offset = 0x3ff0;
    }
    else if (!memcmp (&romheader[0x7ff0], "TMR SEGA", 8))
    {
      offset = 0x7ff0;
    }

    /* if found, get infos from header */
    if ((offset > 0) && (offset < cart.romsize))
    {
      /* checksum */
      rominfo.checksum = romheader[offset + 0x0a] | (romheader[offset + 0x0b] << 8);

      /* product code & version */
      sprintf(&rominfo.product[0], "%02d", romheader[offset + 0x0e] >> 4);
      sprintf(&rominfo.product[2], "%02x", romheader[offset + 0x0d]);
      sprintf(&rominfo.product[4], "%02x", romheader[offset + 0x0c]);
      sprintf(&rominfo.product[6], "-%d", romheader[offset + 0x0e] & 0x0F);

      /* region code */
      switch (romheader[offset + 0x0f] >> 4)
      {
        case 3:
          strcpy(rominfo.country,"SMS Japan");
          break;
        case 4:
          strcpy(rominfo.country,"SMS Export");
          break;
        case 5:
          strcpy(rominfo.country,"GG Japan");
          break;
        case 6:
          strcpy(rominfo.country,"GG Export");
          break;
        case 7:
          strcpy(rominfo.country,"GG International");
          break;
        default:
          sprintf(rominfo.country,"Unknown (%d)", romheader[offset + 0x0f] >> 4);
          break;
      }

      /* ROM size */
      rominfo.romstart = 0;
      switch (romheader[offset + 0x0f] & 0x0F)
      {
        case 0x00:
          rominfo.romend = 0x3FFFF;
          break;
        case 0x01:
          rominfo.romend = 0x7FFFF;
          break;
        case 0x02:
          rominfo.romend = 0xFFFFF;
          break;
        case 0x0a:
          rominfo.romend = 0x1FFF;
          break;
        case 0x0b:
          rominfo.romend = 0x3FFF;
          break;
        case 0x0c:
          rominfo.romend = 0x7FFF;
          break;
        case 0x0d:
          rominfo.romend = 0xBFFF;
          break;
        case 0x0e:
          rominfo.romend = 0xFFFF;
          break;
        case 0x0f:
          rominfo.romend = 0x1FFFF;
          break;
      }
    }
  }
}

/***************************************************************************
 * load_bios
 *
 * Load current system BIOS file.
 *
 * Return loaded size (-1 if already loaded)
 *
 ***************************************************************************/
int load_bios(int system)
{
  int size = 0;

  switch (system)
  {
    case SYSTEM_MCD:
    {
      /* check if CD BOOTROM is already loaded */
      if (!(system_bios & 0x10) || ((system_bios & 0x0c) != (region_code >> 4)))
      {
        /* load CD BOOTROM (fixed 128KB size) */
        switch (region_code)
        {
          case REGION_USA:
            size = load_archive(CD_BIOS_US, scd.bootrom, sizeof(scd.bootrom), 0);
            break;
          case REGION_EUROPE:
            size = load_archive(CD_BIOS_EU, scd.bootrom, sizeof(scd.bootrom), 0);
            break;
          default:
            size = load_archive(CD_BIOS_JP, scd.bootrom, sizeof(scd.bootrom), 0);
            break;
        }

        /* CD BOOTROM loaded ? */
        if (size > 0)
        {
          /* auto-detect CD hardware model */
          if (!memcmp(&scd.bootrom[0x120], "WONDER-MEGA BOOT", 16))
          {
            /* Wondermega CD hardware */
            scd.type = CD_TYPE_WONDERMEGA;
          }
          else if (!memcmp(&scd.bootrom[0x120], "WONDERMEGA2 BOOT", 16))
          {
            /* Wondermega M2 / X'Eye CD hardware */
            scd.type = CD_TYPE_WONDERMEGA_M2;
          }
          else if (!memcmp(&scd.bootrom[0x120], "CDX BOOT ROM    ", 16))
          {
            /* CDX / Multi-Mega CD hardware */
            scd.type = CD_TYPE_CDX;
          }
          else
          {
            /* default CD hardware */
            scd.type = CD_TYPE_DEFAULT;
          }
         
#ifdef LSB_FIRST
          /* Byteswap ROM to optimize 16-bit access */
          int i;
          for (i = 0; i < size; i += 2)
          {
            uint8 temp = scd.bootrom[i];
            scd.bootrom[i] = scd.bootrom[i+1];
            scd.bootrom[i+1] = temp;
          }
#endif
          /* mark CD BIOS as being loaded */
          system_bios = system_bios | 0x10;

          /* loaded BIOS region */
          system_bios = (system_bios & 0xf0) | (region_code >> 4);
        }

        return size;
      }
      
      return -1;
    }

    case SYSTEM_GG:
    case SYSTEM_GGMS:
    {
      /* check if Game Gear BOOTROM is already loaded */
      if (!(system_bios & SYSTEM_GG))
      {      
        /* mark both Master System & Game Gear BOOTROM as unloaded */
        system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);

        /* BOOTROM is stored above cartridge ROM area (max. 4MB) */
        if (cart.romsize <= 0x400000)
        {
          /* load Game Gear BOOTROM file */
          size = load_archive(GG_BIOS, cart.rom + 0x400000, 0x400000, 0);

          if (size > 0)
          {
            /* mark Game Gear BOOTROM as loaded */
            system_bios |= SYSTEM_GG;
          }
        }

        return size;
      }
      
      return -1;
    }

    case SYSTEM_SMS:
    case SYSTEM_SMS2:
    {
      /* check if Master System BOOTROM is already loaded */
      if (!(system_bios & SYSTEM_SMS) || ((system_bios & 0x0c) != (region_code >> 4)))
      {      
        /* mark both Master System & Game Gear BOOTROM as unloaded */
        system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);

        /* BOOTROM is stored above cartridge ROM area (max. 4MB) */
        if (cart.romsize <= 0x400000)
        {
          /* load Master System BOOTROM file */
          switch (region_code)
          {
            case REGION_USA:
              size = load_archive(MS_BIOS_US, cart.rom + 0x400000, 0x400000, 0);
              break;
            case REGION_EUROPE:
              size = load_archive(MS_BIOS_EU, cart.rom + 0x400000, 0x400000, 0);
              break;
            default:
              size = load_archive(MS_BIOS_JP, cart.rom + 0x400000, 0x400000, 0);
              break;
          }

          if (size > 0)
          {
            /* mark Master System BOOTROM as loaded */
            system_bios |= SYSTEM_SMS;

            /* loaded BOOTROM region */
            system_bios = (system_bios & 0xf0) | (region_code >> 4);
          }
        }

        return size;
      }
      
      return -1;
    }

    default:
    {
      /* mark all BOOTROM as unloaded  */
      system_bios &= ~(0x10 | SYSTEM_SMS | SYSTEM_GG);
      return 0;
    }
  }
}

/***************************************************************************
 * load_rom
 *
 * Load a new ROM file.
 *
 * Return 0 on error, 1 on success
 *
 ***************************************************************************/
int load_rom(char *filename)
{
  int i, size;

#ifdef USE_DYNAMIC_ALLOC
  if (!ext)
  {
    /* allocate & initialize memory for Cartridge / CD hardware if required */
    ext = (external_t *)calloc(1, sizeof(external_t));
    if (!ext) return (0);
  }
#endif

  /* clear any existing patches */
  ggenie_shutdown();
  areplay_shutdown();

  /* check previous loaded ROM size */
  if (cart.romsize > 0x800000)
  {
    /* assume no CD is currently loaded */
    cdd.loaded = 0;
  }

  /* auto-detect CD image file */
  size = cdd_load(filename, (char *)(cart.rom));
  if (size < 0)
  {
    /* error opening file */
    return (0);
  }

  /* CD image file ? */
  if (size)
  {
    /* enable CD hardware */
    system_hw = SYSTEM_MCD;
  }
  else
  {
    /* load file into ROM buffer */
    char extension[4];
    size = load_archive(filename, cart.rom, cdd.loaded ? 0x800000 : MAXROMSIZE, extension);

    /* mark BOOTROM as unloaded if they have been overwritten by cartridge ROM */
    if (size > 0x800000)
    {
      /* CD BIOS ROM are loaded at the start of CD area */
      system_bios &= ~0x10;
    }
    else if (size > 0x400000)
    {
      /* Master System or Game Gear BIOS ROM are loaded within $400000-$4FFFFF area */
      system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
    }
    else if (size <= 0)
    {
      /* mark all BOOTROM as unloaded since they could have been overwritten */
      system_bios &= ~(0x10 | SYSTEM_SMS | SYSTEM_GG);
      
      /* error loading file */
      return 0;
    }

    /* convert lower case file extension to upper case */
    *(uint32 *)(extension) &= 0xdfdfdfdf;

    /* auto-detect system hardware from ROM file extension */
    if (!memcmp("SMS", &extension[0], 3))
    {
      /* Master System II hardware */
      system_hw = SYSTEM_SMS2;
    }
    else if (!memcmp("GG", &extension[1], 2))
    {
      /* Game Gear hardware (GG mode) */
      system_hw = SYSTEM_GG;
    }
    else if (!memcmp("SG", &extension[1], 2))
    {
      /* SG-1000 hardware */
      system_hw = SYSTEM_SG;
    }
    else
    {
      /* default is Mega Drive / Genesis hardware (16-bit mode) */
      system_hw = SYSTEM_MD;

      /* decode .MDX format */
      if (!memcmp("MDX", &extension[0], 3))
      {
        for (i = 4; i < size - 1; i++)
        {
          cart.rom[i-4] = cart.rom[i] ^ 0x40;
        }
        size = size - 5;
      }

      /* auto-detect byte-swapped dumps */
      if (!memcmp((char *)(cart.rom + 0x100),"ESAGM GE ARDVI E", 16) ||
          !memcmp((char *)(cart.rom + 0x100),"ESAGG NESESI", 12) ||
          !memcmp((char *)(cart.rom + 0x80000 + 0x100),"ESAGM GE ARDVI E", 16) ||
          !memcmp((char *)(cart.rom + 0x80000 + 0x100),"ESAGG NESESI", 12))
      {
        for(i = 0; i < size; i += 2)
        {
          uint8 temp = cart.rom[i];
          cart.rom[i] = cart.rom[i+1];
          cart.rom[i+1] = temp;
        }
      }
    }

    /* auto-detect 512 byte extra header */
    if (memcmp((char *)(cart.rom + 0x100), "SEGA", 4) && ((size / 512) & 1) && !(size % 512))
    {
      /* remove header */
      size -= 512;
      memmove (cart.rom, cart.rom + 512, size);

      /* assume interleaved Mega Drive / Genesis ROM format (.smd) */
      if (system_hw == SYSTEM_MD)
      {
        for (i = 0; i < (size / 0x4000); i++)
        {
          deinterleave_block (cart.rom + (i * 0x4000));
        }
      }
    }
  }
    
  /* initialize ROM size */
  cart.romsize = size;

  /* get infos from ROM header */
  getrominfo((char *)(cart.rom));

  /* set console region */
  get_region((char *)(cart.rom));

#ifdef LSB_FIRST
  /* 16-bit ROM specific */
  if (system_hw == SYSTEM_MD)
  {
    /* Byteswap ROM to optimize 16-bit access */
    for (i = 0; i < cart.romsize; i += 2)
    {
      uint8 temp = cart.rom[i];
      cart.rom[i] = cart.rom[i+1];
      cart.rom[i+1] = temp;
    }
  }
#endif

  /* PICO ROM */
  if (strstr(rominfo.consoletype, "SEGA PICO") != NULL)
  {
    /* PICO hardware */
    system_hw = SYSTEM_PICO;
  }

  /* Save auto-detected system hardware  */
  romtype = system_hw;

  /* CD image file */
  if (system_hw == SYSTEM_MCD)
  {
    /* try to load CD BOOTROM for selected region */
    if (!load_bios(SYSTEM_MCD))
    {
      /* unmount CD image */
      cdd_unload();

      /* error booting from CD */
      return (0);
    }

    /* boot from CD hardware */
    scd.cartridge.boot = 0x00;
  }

  /* 16-bit ROM cartridge (max. 8MB) with optional CD hardware add-on support enabled */
  else if ((system_hw == SYSTEM_MD) && (cart.romsize <= 0x800000) && (config.add_on != HW_ADDON_NONE))
  {
    int len;
    char fname[256];

#if defined(USE_LIBCHDR)
    /* automatically try to load associated .chd file if no .cue file CD image loaded yet */
    if (!cdd.loaded)
    {
      len = strlen(filename);
      while ((len && (filename[len] != '.')) || (len > 251)) len--;
      strncpy(fname, filename, len);
      strcpy(&fname[len], ".chd");
      fname[len+4] = 0;
      cdd_load(fname, (char *)cdc.ram);
    }
#endif

    /* automatically enable CD hardware emulation (Mode 1) in case :             */
    /*  - loaded ROM has known CD hardware support                               */
    /*      or                                                                   */
    /*  - CD hardware emulation is forced on                                     */
    /*      or                                                                   */
    /*  - MegaSD add-on emulation is disabled and normal CD image file is loaded */
    if ((rominfo.peripherals & PCDROM) || (strstr(rominfo.domestic,"FLUX") != NULL) ||
        (config.add_on == HW_ADDON_MEGACD) || ((config.add_on | cdd.loaded) == HW_ADDON_MEGACD))
    {
      /* try to load CD BOOTROM for selected region */
      if (load_bios(SYSTEM_MCD))
      {
        /* automatically try to load associated .iso file if no CD image loaded yet */
        if (!cdd.loaded)
        {
          len = strlen(filename);
          while ((len && (filename[len] != '.')) || (len > 251)) len--;
          strncpy(fname, filename, len);
          strcpy(&fname[len], ".iso");
          fname[len+4] = 0;
          cdd_load(fname, (char *)cdc.ram);
        }

        /* enable CD hardware */
        system_hw = SYSTEM_MCD;

        /* boot from cartridge */
        scd.cartridge.boot = 0x40;
      }
      else
      {
        /* unmount any loaded CD image */
        cdd_unload();
      }
    }

    /* CD BOOTROM */
    else if (strstr(rominfo.ROMType, "BR") != NULL)
    {
      /* enable CD hardware */
      system_hw = SYSTEM_MCD;

      /* auto-detect CD hardware model */
      if (strstr(rominfo.domestic, "WONDER-MEGA BOOT"))
      {
        /* Wondermega CD hardware */
        scd.type = CD_TYPE_WONDERMEGA;
      }
      else if (strstr(rominfo.domestic, "WONDERMEGA2 BOOT"))
      {
        /* Wondermega M2 / X'Eye CD hardware */
        scd.type = CD_TYPE_WONDERMEGA_M2;
      }
      else if (strstr(rominfo.domestic, "CDX BOOT ROM"))
      {
        /* CDX / Multi-Mega CD hardware */
        scd.type = CD_TYPE_CDX;
      }
      else
      {
        /* default CD hardware */
        scd.type = CD_TYPE_DEFAULT;
      }

      /* boot from CD hardware */
      scd.cartridge.boot = 0x00;

      /* copy ROM to BOOTROM area */
      memcpy(scd.bootrom, cart.rom, sizeof(scd.bootrom));

      /* mark CD BIOS as being loaded */
      system_bios = system_bios | 0x10;

      /* loaded CD BIOS region */
      system_bios = (system_bios & 0xf0) | (region_code >> 4);
    }
  }

  /* Force system hardware if requested */
  if (config.system == SYSTEM_MD)
  {
    if (!(system_hw & SYSTEM_MD))
    {
      /* Mega Drive in MS compatibility mode  */
      system_hw = SYSTEM_PBC;
    }
  }
  else if (config.system == SYSTEM_GG)
  {
    if (system_hw != SYSTEM_GG)
    {
      /* Game Gear in MS compatibility mode  */
      system_hw = SYSTEM_GGMS;
    }
  }
  else if (config.system)
  {
    system_hw = config.system;
  }

  /* restore previous input settings */
  if (old_system[0] != -1)
  {
    input.system[0] = old_system[0];
  }
  if (old_system[1] != -1)
  {
    input.system[1] = old_system[1];
  }

  /* default gun settings */
  input.x_offset = (input.system[1] == SYSTEM_MENACER) ? 64 : 0;
  input.y_offset = 0;

  /* autodetect gun support */
  if (strstr(rominfo.international,"MENACER") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 82;
    input.y_offset = 0;
  }
  else if (strstr(rominfo.international,"T2 ; THE ARCADE GAME") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 133;
    input.y_offset = -8;
  }
  else if (strstr(rominfo.international,"BODY COUNT") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 68;
    input.y_offset = -24;
  }
  else if (strstr(rominfo.international,"CORPSE KILLER") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 64;
    input.y_offset = -8;
  }
  else if (strstr(rominfo.international,"CRIME PATROL") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 61;
    input.y_offset = 0;
  }
  else if (strstr(rominfo.international,"MAD DOG II THE LOST GOLD") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 70;
    input.y_offset = 18;
  }
  else if (strstr(rominfo.international,"MAD DOG MCCREE") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 49;
    input.y_offset = 0;
  }
  else if (strstr(rominfo.international,"WHO SHOT JOHNNY ROCK?") != NULL)
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force MENACER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_MENACER;
    input.x_offset = 60;
    input.y_offset = 30;
  }
  else if ((strstr(rominfo.international,"LETHAL ENFORCERS") != NULL) ||
           (strstr(rominfo.international,"SNATCHER") != NULL))
  {
    /* save current setting */
    if (old_system[0] == -1)
    {
      old_system[0] = input.system[0];
    }
    if (old_system[1] == -1)
    {
      old_system[1] = input.system[1];
    }

    /* force JUSTIFIER configuration */
    input.system[0] = SYSTEM_GAMEPAD;
    input.system[1] = SYSTEM_JUSTIFIER;
    input.x_offset = (strstr(rominfo.international,"GUN FIGHTERS") != NULL) ? 24 : 0;
    input.y_offset = 0;
  }

  /* extract loaded game filepath */
  strncpy(rompath, filename, sizeof(rompath));
  rompath[sizeof(rompath) - 1] = 0;
  i = strlen(rompath);
  while ((i>0) && (rompath[i-1]!='/') && (rompath[i-1]!='\\')) i--;
  rompath[i] = 0;

  return(1);
}

/****************************************************************************
 * get_region
 *
 * Set console region from ROM header passed as parameter or 
 * from previous auto-detection (if NULL) 
 *
 ****************************************************************************/
void get_region(char *romheader)
{
  /* region auto-detection ? */
  if (romheader)
  {
    /* Mega CD image */
    if (system_hw == SYSTEM_MCD)
    {
      /* security code */
      switch ((unsigned char)romheader[0x20b])
      {
        case 0x64:
          region_code = REGION_EUROPE;
          break;
   
        case 0xa1:
          region_code = REGION_JAPAN_NTSC;
          break;

        default:
          region_code = REGION_USA;
          break;
      }
    }

    /* 16-bit cartridge */
    else if (system_hw & SYSTEM_MD)
    {
      /* country codes used to differentiate region */
      /* 0001 = japan ntsc (1) */
      /* 0010 = japan pal  (2) -> does not exist ? */
      /* 0100 = usa        (4) */
      /* 1000 = europe     (8) */
      int country = 0;

      /* from Gens */
      if (!memcmp(rominfo.country, "eur", 3)) country |= 8;
      else if (!memcmp(rominfo.country, "EUR", 3)) country |= 8;
      else if (!memcmp(rominfo.country, "Europe", 3)) country |= 8;
      else if (!memcmp(rominfo.country, "jap", 3)) country |= 1;
      else if (!memcmp(rominfo.country, "JAP", 3)) country |= 1;
      else if (!memcmp(rominfo.country, "usa", 3)) country |= 4;
      else if (!memcmp(rominfo.country, "USA", 3)) country |= 4;
      else
      {
        int i;
        char c;

        /* look for each characters */
        for(i = 0; i < 4; i++)
        {
          c = toupper((int)rominfo.country[i]);

          if (c == 'U') country |= 4;
          else if (c == 'J') country |= 1;
          else if (c == 'E') country |= 8;
          else if (c == 'K') country |= 1;
          else if (c < 16) country |= c;
          else if ((c >= '0') && (c <= '9')) country |= c - '0';
          else if ((c >= 'A') && (c <= 'F')) country |= c - 'A' + 10;
        }
      }

      /* set default console region (USA > JAPAN > EUROPE) */
      if (country & 4) region_code = REGION_USA;
      else if (country & 1) region_code = REGION_JAPAN_NTSC;
      else if (country & 8) region_code = REGION_EUROPE;
      else if (country & 2) region_code = REGION_JAPAN_PAL;
      else region_code = REGION_USA;

      /* some games need specific region settings but have wrong header*/
      if (((strstr(rominfo.product,"T-45033") != NULL) && (rominfo.checksum == 0x0F81)) || /* Alisia Dragon (Europe) */
           (strstr(rominfo.product,"T-69046-50") != NULL) ||    /* Back to the Future III (Europe) */
           (strstr(rominfo.product,"T-120106-00") != NULL) ||   /* Brian Lara Cricket (Europe) */
           (strstr(rominfo.product,"T-97126 -50") != NULL) ||   /* Williams Arcade's Greatest Hits (Europe) */
           (strstr(rominfo.product,"T-113026-50") != NULL) ||   /* Wiz'n'Liz - The Frantic Wabbit Wescue (Europe) */
           (strstr(rominfo.product,"T-70096 -00") != NULL) ||   /* Muhammad Ali Heavyweight Boxing (Europe) */
           ((rominfo.checksum == 0x0000) && (rominfo.realchecksum == 0x1f7f))) /* Radica - Sensible Soccer Plus edition */
      {
        /* need PAL settings */
        region_code = REGION_EUROPE;
      }
      else if ((rominfo.realchecksum == 0x532e) && (strstr(rominfo.product,"1011-00") != NULL)) 
      {
        /* On Dal Jang Goon (Korea) needs JAPAN region code */
        region_code = REGION_JAPAN_NTSC;
      }
    }

    /* 8-bit cartridge */
    else
    {
      region_code = sms_cart_region_detect();
    }

    /* save auto-detected region */
    rom_region = region_code;
  }
  else
  {
    /* restore auto-detected region */
    region_code = rom_region;
  }
  
  /* force console region if requested */
  if (config.region_detect == 1) region_code = REGION_USA;
  else if (config.region_detect == 2) region_code = REGION_EUROPE;
  else if (config.region_detect == 3) region_code = REGION_JAPAN_NTSC;
  else if (config.region_detect == 4) region_code = REGION_JAPAN_PAL;

  /* autodetect PAL/NTSC timings */
  vdp_pal = (region_code >> 6) & 0x01;

  /* autodetect PAL/NTSC master clock */
  system_clock = vdp_pal ? MCLOCK_PAL : MCLOCK_NTSC;

  /* force PAL/NTSC timings if requested */
  if (config.vdp_mode == 1) vdp_pal = 0;
  else if (config.vdp_mode == 2) vdp_pal = 1;

  /* force PAL/NTSC master clock if requested */
  if (config.master_clock == 1) system_clock = MCLOCK_NTSC;
  else if (config.master_clock == 2) system_clock = MCLOCK_PAL;
}

/****************************************************************************
 * get_company (Softdev - 2006)
 *
 * Try to determine which company made this rom
 *
 * Ok, for some reason there's no standard for this.
 * It seems that there can be pretty much anything you like following the
 * copyright (C) symbol!
 ****************************************************************************/
char *get_company(void)
{
  char *s;
  int i;
  char company[10];

  for (i = 3; i < 8; i++) 
  {
    company[i - 3] = rominfo.copyright[i];
  }
  company[5] = 0;

  /** OK, first look for a hyphen
   *  Capcom use T-12 for example
   */
  s = strstr (company, "-");
  if (s != NULL)
  {
    s++;
    strcpy (company, s);
  }

  /** Strip any trailing spaces **/
  for (i = strlen (company) - 1; i >= 0; i--)
    if (company[i] == 32)
      company[i] = 0;

  if (strlen (company) == 0)
    return (char *)companyinfo[MAXCOMPANY - 1].company;

  for (i = 0; i < MAXCOMPANY - 1; i++)
  {
    if (!(strncmp (company, companyinfo[i].companyid, strlen (company))))
      return (char *)companyinfo[i].company;
  }

  return (char *)companyinfo[MAXCOMPANY - 1].company;
}

/****************************************************************************
 * get_peripheral (Softdev - 2006)
 *
 * Return peripheral name based on header code
 *
 ****************************************************************************/
char *get_peripheral(int index)
{
  if (index < MAXPERIPHERALS)
    return (char *)peripheralinfo[index].pName;
  return (char *)companyinfo[MAXCOMPANY - 1].company;
}

