/*************************************************************************************** * Genesis Plus * ROM Loading Support * * Copyright (C) 1998-2003 Charles Mac Donald (original code) * Copyright (C) 2007-2023 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 #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; 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; } 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; }