#include <ctype.h>
#include "shared.h"
#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 1
#define P6BUTTONS 2
#define PKEYBOARD 4
#define PPRINTER 8
#define PBALL 16
#define PFLOPPY 32
#define PACTIVATOR 64
#define PTEAMPLAYER 128
#define PMSYSTEMPAD 256
#define PSERIAL 512
#define PTABLET 1024
#define PPADDLE 2048
#define PCDROM 4096
#define PMOUSE 8192
#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;
uint8 rom_region;
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"}
};
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"},
};
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;
}
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)];
}
}
void getrominfo(char *romheader)
{
memset (&rominfo, 0, sizeof (ROMINFO));
if (system_hw & SYSTEM_MD)
{
int i,j;
memcpy (&rominfo.consoletype, romheader + ROMCONSOLE, 16);
memcpy (&rominfo.copyright, romheader + ROMCOPYRIGHT, 16);
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;
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;
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);
#ifdef LSB_FIRST
rominfo.checksum = (rominfo.checksum >> 8) | ((rominfo.checksum & 0xff) << 8);
#endif
rominfo.realchecksum = getchecksum(((uint8 *) cart.rom) + 0x200, cart.romsize - 0x200);
rominfo.peripherals = 0;
for (i = 0; i < 14; i++)
for (j=0; j < 14; j++)
if (romheader[ROMIOSUPPORT+i] == peripheralinfo[j].pID[0])
rominfo.peripherals |= (1 << j);
}
else
{
uint16 offset = 0;
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 (offset)
{
rominfo.checksum = romheader[offset + 0x0a] | (romheader[offset + 0x0b] << 8);
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);
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;
}
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;
}
}
}
}
int load_bios(void)
{
int size = 0;
switch (system_hw)
{
case SYSTEM_MCD:
{
if (!(system_bios & 0x10) || ((system_bios & 0x0c) != (region_code >> 4)))
{
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;
}
if (size > 0)
{
#ifdef LSB_FIRST
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
system_bios = system_bios | 0x10;
system_bios = (system_bios & 0xf0) | (region_code >> 4);
}
return size;
}
return -1;
}
case SYSTEM_GG:
case SYSTEM_GGMS:
{
if (!(system_bios & SYSTEM_GG))
{
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
if (cart.romsize <= 0x400000)
{
size = load_archive(GG_BIOS, cart.rom + 0x400000, 0x100000, 0);
if (size > 0)
{
system_bios |= SYSTEM_GG;
}
}
return size;
}
return -1;
}
case SYSTEM_SMS:
case SYSTEM_SMS2:
{
if (!(system_bios & SYSTEM_SMS) || ((system_bios & 0x0c) != (region_code >> 4)))
{
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
if (cart.romsize <= 0x400000)
{
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)
{
system_bios |= SYSTEM_SMS;
system_bios = (system_bios & 0xf0) | (region_code >> 4);
}
}
return size;
}
return -1;
}
default:
{
return 0;
}
}
}
int load_rom(const char *filename)
{
int i, size;
ggenie_shutdown();
areplay_shutdown();
if (cart.romsize > 0x800000)
{
cdd.loaded = 0;
}
size = cdd_load("PRIMARY_CD", (char *)(cart.rom));
if (size < 0)
{
return 0;
}
if (size > 0)
{
system_hw = SYSTEM_MCD;
}
else
{
char extension[4];
size = load_archive(filename, cart.rom, sizeof(cart.rom), extension);
if (!size)
{
system_bios &= ~(0x10 | SYSTEM_SMS | SYSTEM_GG);
return 0;
}
if (size > 0x800000)
{
system_bios &= ~0x10;
}
else if (size > 0x400000)
{
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
}
*(uint32 *)(extension) &= 0xdfdfdfdf;
if (!memcmp("SMS", &extension[0], 3))
{
system_hw = SYSTEM_SMS2;
}
else if (!memcmp("GG", &extension[1], 2))
{
system_hw = SYSTEM_GG;
}
else if (!memcmp("SG", &extension[1], 2))
{
system_hw = SYSTEM_SG;
}
else
{
system_hw = SYSTEM_MD;
if (!memcmp("MDX", &extension[0], 3))
{
for (i = 4; i < size - 1; i++)
{
cart.rom[i-4] = cart.rom[i] ^ 0x40;
}
size = size - 5;
}
if (!memcmp((char *)(cart.rom + 0x100),"ESAGM GE ARDVI E", 16) ||
!memcmp((char *)(cart.rom + 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;
}
}
}
if (memcmp((char *)(cart.rom + 0x100), "SEGA", 4) && ((size / 512) & 1) && !(size % 512))
{
size -= 512;
memcpy (cart.rom, cart.rom + 512, size);
if (system_hw == SYSTEM_MD)
{
for (i = 0; i < (size / 0x4000); i++)
{
deinterleave_block (cart.rom + (i * 0x4000));
}
}
}
}
cart.romsize = size;
getrominfo((char *)(cart.rom));
get_region((char *)(cart.rom));
if (system_hw == SYSTEM_MCD)
{
if (!load_bios())
{
cdd_unload();
return (0);
}
scd.cartridge.boot = 0x00;
}
#ifdef LSB_FIRST
else if (system_hw == SYSTEM_MD)
{
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
romtype = system_hw;
if (strstr(rominfo.consoletype, "SEGA PICO") != NULL)
{
system_hw = SYSTEM_PICO;
}
else if (strstr(rominfo.ROMType, "BR") != NULL)
{
system_hw = SYSTEM_MCD;
scd.cartridge.boot = 0x00;
memcpy(scd.bootrom, cart.rom, sizeof(scd.bootrom));
system_bios = system_bios | 0x10;
system_bios = (system_bios & 0xf0) | (region_code >> 4);
}
else if ((strstr(rominfo.domestic,"FLUX") != NULL) ||
(strstr(rominfo.domestic,"WONDER LIBRARY") != NULL) ||
(strstr(rominfo.product,"T-5740") != NULL))
{
if (config.system == 0x00)
{
system_hw = SYSTEM_MCD;
if (load_bios())
{
scd.cartridge.boot = 0x40;
if (cdd_load("SECONDARY_CD", (char *)cdc.ram) <= 0)
system_hw = SYSTEM_MD;
}
else
{
system_hw = SYSTEM_MD;
}
}
}
if (config.system == SYSTEM_MD)
{
if (!(system_hw & SYSTEM_MD))
{
system_hw = SYSTEM_PBC;
}
}
else if (config.system == SYSTEM_GG)
{
if (system_hw != SYSTEM_GG)
{
system_hw = SYSTEM_GGMS;
}
}
else if (config.system)
{
system_hw = config.system;
}
if (old_system[0] != -1)
{
input.system[0] = old_system[0];
}
if (old_system[1] != -1)
{
input.system[1] = old_system[1];
}
input.x_offset = (input.system[1] == SYSTEM_MENACER) ? 64 : 0;
input.y_offset = 0;
if (strstr(rominfo.international,"MENACER") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 82;
input.y_offset = 0;
}
else if (strstr(rominfo.international,"T2 ; THE ARCADE GAME") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 133;
input.y_offset = -8;
}
else if (strstr(rominfo.international,"BODY COUNT") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 68;
input.y_offset = -24;
}
else if (strstr(rominfo.international,"CORPSE KILLER") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 64;
input.y_offset = -8;
}
else if (strstr(rominfo.international,"CRIME PATROL") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_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)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 70;
input.y_offset = 18;
}
else if (strstr(rominfo.international,"MAD DOG MCCREE") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_MENACER;
input.x_offset = 49;
input.y_offset = 0;
}
else if (strstr(rominfo.international,"WHO SHOT JOHNNY ROCK?") != NULL)
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_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))
{
if (old_system[0] == -1)
{
old_system[0] = input.system[0];
}
if (old_system[1] == -1)
{
old_system[1] = input.system[1];
}
input.system[0] = SYSTEM_MD_GAMEPAD;
input.system[1] = SYSTEM_JUSTIFIER;
input.x_offset = (strstr(rominfo.international,"GUN FIGHTERS") != NULL) ? 24 : 0;
input.y_offset = 0;
}
return(1);
}
void get_region(char *romheader)
{
if (romheader)
{
if (system_hw == SYSTEM_MCD)
{
switch (romheader[0x20b])
{
case 0x7a:
region_code = REGION_USA;
break;
case 0x64:
region_code = REGION_EUROPE;
break;
default:
region_code = REGION_JAPAN_NTSC;
break;
}
}
else if (system_hw & SYSTEM_MD)
{
int country = 0;
if (!memcmp(rominfo.country, "eur", 3)) country |= 8;
else if (!memcmp(rominfo.country, "EUR", 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;
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;
}
}
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;
if (((strstr(rominfo.product,"T-45033") != NULL) && (rominfo.checksum == 0x0F81)) ||
(strstr(rominfo.product,"T-69046-50") != NULL) ||
(strstr(rominfo.product,"T-120106-00") != NULL) ||
(strstr(rominfo.product,"T-70096 -00") != NULL))
{
region_code = REGION_EUROPE;
}
else if ((rominfo.realchecksum == 0x532e) && (strstr(rominfo.product,"1011-00") != NULL))
{
region_code = REGION_JAPAN_NTSC;
}
}
else
{
region_code = sms_cart_region_detect();
}
rom_region = region_code;
}
else
{
region_code = rom_region;
}
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;
vdp_pal = (region_code >> 6) & 0x01;
system_clock = vdp_pal ? MCLOCK_PAL : MCLOCK_NTSC;
if (config.vdp_mode == 1) vdp_pal = 0;
else if (config.vdp_mode == 2) vdp_pal = 1;
if (config.master_clock == 1) system_clock = MCLOCK_NTSC;
else if (config.master_clock == 2) system_clock = MCLOCK_PAL;
}
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;
s = strstr (company, "-");
if (s != NULL)
{
s++;
strcpy (company, s);
}
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;
}
char *get_peripheral(int index)
{
if (index < MAXPERIPHERALS)
return (char *)peripheralinfo[index].pName;
return (char *)companyinfo[MAXCOMPANY - 1].company;
}