|
@@ -0,0 +1,1728 @@
|
|
|
+/*
|
|
|
+genesis.c - Sega Genesis/Mega Drive support for uCON64
|
|
|
+
|
|
|
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
|
|
|
+Copyright (c) 2002 - 2004 dbjh
|
|
|
+
|
|
|
+
|
|
|
+This program is free software; you can redistribute it and/or modify
|
|
|
+it under the terms of the GNU General Public License as published by
|
|
|
+the Free Software Foundation; either version 2 of the License, or
|
|
|
+(at your option) any later version.
|
|
|
+
|
|
|
+This program is distributed in the hope that it will be useful,
|
|
|
+but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+GNU General Public License for more details.
|
|
|
+
|
|
|
+You should have received a copy of the GNU General Public License
|
|
|
+along with this program; if not, write to the Free Software
|
|
|
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
+*/
|
|
|
+// NOTE: The people at Sega refer to their company as Sega in normal text (not as SEGA)
|
|
|
+#ifdef HAVE_CONFIG_H
|
|
|
+#include "config.h"
|
|
|
+#endif
|
|
|
+#include <ctype.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#ifdef HAVE_UNISTD_H
|
|
|
+#include <unistd.h>
|
|
|
+#endif
|
|
|
+#include <sys/stat.h>
|
|
|
+#include "misc/misc.h"
|
|
|
+#include "misc/chksum.h"
|
|
|
+#include "misc/file.h"
|
|
|
+#ifdef USE_ZLIB
|
|
|
+#include "misc/archive.h"
|
|
|
+#endif
|
|
|
+#include "misc/getopt2.h" // st_getopt2_t
|
|
|
+#include "ucon64.h"
|
|
|
+#include "ucon64_misc.h"
|
|
|
+#include "console/genesis.h"
|
|
|
+#include "backup/mgd.h"
|
|
|
+#include "backup/smd.h"
|
|
|
+
|
|
|
+
|
|
|
+#define GENESIS_HEADER_START 256
|
|
|
+#define GENESIS_HEADER_LEN (sizeof (st_genesis_header_t))
|
|
|
+#define GENESIS_NAME_LEN 48
|
|
|
+
|
|
|
+static int genesis_chksum (unsigned char *rom_buffer);
|
|
|
+static unsigned char *load_rom (st_rominfo_t *rominfo, const char *name,
|
|
|
+ unsigned char *rom_buffer);
|
|
|
+static int save_rom (st_rominfo_t *rominfo, const char *name,
|
|
|
+ unsigned char **buffer, int size);
|
|
|
+
|
|
|
+
|
|
|
+const st_getopt2_t genesis_usage[] =
|
|
|
+ {
|
|
|
+ {
|
|
|
+ NULL, 0, 0, 0,
|
|
|
+ NULL, "Genesis/Sega Mega Drive/Sega CD/32X/Nomad"/*"1989/19XX/19XX Sega http://www.sega.com"*/,
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "gen", 0, 0, UCON64_GEN,
|
|
|
+ NULL, "force recognition",
|
|
|
+ &ucon64_wf[WF_OBJ_GEN_SWITCH]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "int", 0, 0, UCON64_INT,
|
|
|
+ NULL, "force ROM is in interleaved format (SMD)",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "int2", 0, 0, UCON64_INT2,
|
|
|
+ NULL, "force ROM is in interleaved format 2 (MGD)",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "nint", 0, 0, UCON64_NINT,
|
|
|
+ NULL, "force ROM is not in interleaved format (BIN/RAW)",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "n", 1, 0, UCON64_N,
|
|
|
+ "NEW_NAME", "change foreign ROM name to NEW_NAME",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "n2", 1, 0, UCON64_N2,
|
|
|
+ "NEW_NAME", "change Japanese ROM name to NEW_NAME",
|
|
|
+ &ucon64_wf[WF_OBJ_GEN_DEFAULT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "smd", 0, 0, UCON64_SMD,
|
|
|
+ NULL, "convert to Super Magic Drive/SMD",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "smds", 0, 0, UCON64_SMDS,
|
|
|
+ NULL, "convert emulator (*.srm) SRAM to Super Magic Drive/SMD",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "bin", 0, 0, UCON64_BIN,
|
|
|
+ NULL, "convert to Magicom/BIN/RAW",
|
|
|
+ &ucon64_wf[WF_OBJ_GEN_DEFAULT_NO_SPLIT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "mgd", 0, 0, UCON64_MGD,
|
|
|
+ NULL, "convert to Multi Game*/MGD2/MGH",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
|
|
|
+ },
|
|
|
+#if 0
|
|
|
+ {
|
|
|
+ "gf", 0, 0, UCON64_GF,
|
|
|
+ NULL, "convert Sega CD country code to Europe; ROM=$CD_IMAGE",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "ga", 0, 0, UCON64_GA,
|
|
|
+ NULL, "convert Sega CD country code to U.S.A.; ROM=$CD_IMAGE",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "gym", 0, 0, UCON64_GYM,
|
|
|
+ NULL, "convert GYM (Genecyst) sound to WAV; " OPTION_LONG_S "rom=GYMFILE",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cym", 0, 0, UCON64_CYM,
|
|
|
+ NULL, "convert CYM (Callus emulator) sound to WAV; " OPTION_LONG_S "rom=CYMFILE",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ "stp", 0, 0, UCON64_STP,
|
|
|
+ NULL, "convert SRAM from backup unit for use with an emulator\n"
|
|
|
+ OPTION_LONG_S "stp just strips the first 512 bytes",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "j", 0, 0, UCON64_J,
|
|
|
+ NULL, "join split ROM",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_INIT_PROBE]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "s", 0, 0, UCON64_S,
|
|
|
+ NULL, "split ROM; default part size is 8 Mb (4 Mb for SMD)",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "ssize", 1, 0, UCON64_SSIZE,
|
|
|
+ "SIZE", "specify split part size in Mbit",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "f", 0, 0, UCON64_F,
|
|
|
+ NULL, "remove NTSC/PAL protection",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "chk", 0, 0, UCON64_CHK,
|
|
|
+ NULL, "fix ROM checksum",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_DEFAULT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "1991", 0, 0, UCON64_1991,
|
|
|
+ NULL, "fix old third party ROMs to work with consoles build after\n"
|
|
|
+ "October 1991 by inserting \"(C) SEGA\" and \"(C)SEGA\"",
|
|
|
+ &ucon64_wf[WF_OBJ_GEN_DEFAULT]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "multi", 1, 0, UCON64_MULTI,
|
|
|
+ "SIZE", "make multi-game file for use with MD-PRO flash card, truncated\n"
|
|
|
+ "to SIZE Mbit; file with loader must be specified first, then\n"
|
|
|
+ "all the ROMs, multi-game file to create last",
|
|
|
+ &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "region", 1, 0, UCON64_REGION,
|
|
|
+ "CODE", "enable region function; use with -multi\n"
|
|
|
+ "CODE=0 force NTSC/Japan for all games\n"
|
|
|
+ "CODE=1 force NTSC/U.S.A. for all games\n"
|
|
|
+ "CODE=2 force PAL for all games\n"
|
|
|
+ "CODE=x use whatever setting games expect",
|
|
|
+ &ucon64_wf[WF_OBJ_GEN_SWITCH]
|
|
|
+ },
|
|
|
+ {NULL, 0, 0, 0, NULL, NULL, NULL}
|
|
|
+ };
|
|
|
+
|
|
|
+const st_getopt2_t bin_usage[] =
|
|
|
+ {
|
|
|
+ {
|
|
|
+ NULL, 0, 0, 0,
|
|
|
+ NULL, "Magicom/BIN/RAW",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ {NULL, 0, 0, 0, NULL, NULL, NULL}
|
|
|
+ };
|
|
|
+
|
|
|
+typedef struct st_genesis_header
|
|
|
+{
|
|
|
+ char pad[256];
|
|
|
+} st_genesis_header_t;
|
|
|
+
|
|
|
+static st_genesis_header_t genesis_header;
|
|
|
+static genesis_file_t type;
|
|
|
+static int genesis_rom_size, genesis_has_ram, genesis_tv_standard, genesis_japanese;
|
|
|
+
|
|
|
+
|
|
|
+genesis_file_t
|
|
|
+genesis_get_file_type (void)
|
|
|
+{
|
|
|
+ return type;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_smd (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ st_smd_header_t header;
|
|
|
+ char dest_name[FILENAME_MAX];
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ memset (&header, 0, SMD_HEADER_LEN);
|
|
|
+ header.size = genesis_rom_size / 16384;
|
|
|
+ header.id0 = 3;
|
|
|
+ header.id1 = 0xaa;
|
|
|
+ header.id2 = 0xbb;
|
|
|
+ header.type = 6;
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".smd");
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+
|
|
|
+ ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb");
|
|
|
+ smd_interleave (rom_buffer, genesis_rom_size);
|
|
|
+ ucon64_fwrite (rom_buffer, SMD_HEADER_LEN, genesis_rom_size, dest_name, "ab");
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_smds (void)
|
|
|
+{
|
|
|
+ char dest_name[FILENAME_MAX];
|
|
|
+ unsigned char buf[32768];
|
|
|
+ st_smd_header_t header;
|
|
|
+
|
|
|
+ memset (&header, 0, SMD_HEADER_LEN);
|
|
|
+ memset (&buf, 0, 32768);
|
|
|
+ ucon64_fread (buf, 0, ucon64.file_size, ucon64.rom);
|
|
|
+
|
|
|
+ header.id1 = 0xaa;
|
|
|
+ header.id2 = 0xbb;
|
|
|
+ header.type = 7; // SRAM file
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".sav");
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+
|
|
|
+ ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb");
|
|
|
+ smd_interleave (buf, 32768); // SMD SRAM files are interleaved (Are they? - dbjh)
|
|
|
+ ucon64_fwrite (buf, SMD_HEADER_LEN, 32768, dest_name, "ab");
|
|
|
+
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_bin (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ char dest_name[FILENAME_MAX];
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".bin");
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+
|
|
|
+ ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb");
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// see src/backup/mgd.h for the file naming scheme
|
|
|
+int
|
|
|
+genesis_mgd (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+#define CC (const char)
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+ char dest_name[FILENAME_MAX];
|
|
|
+#if 0 // TODO: We need more info about the Multi Game Hunter
|
|
|
+ int x, y;
|
|
|
+ char mgh[512], buf[FILENAME_MAX];
|
|
|
+ const char mghcharset[1024] = {
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* ! */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* & */ 0x3c, 0x66, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x02,
|
|
|
+ /* ' */ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* ( */ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00,
|
|
|
+ /* ) */ 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* , */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08,
|
|
|
+ /* - */ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* . */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
|
|
|
+ /* / */ 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00,
|
|
|
+ /* 0 */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* 1 */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* 2 */ 0x3c, 0x66, 0x06, 0x06, 0x7c, 0x60, 0x7e, 0x00,
|
|
|
+ /* 3 */ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
|
|
|
+ /* 4 */ 0x18, 0x38, 0x58, 0x7c, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* 5 */ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
|
|
|
+ /* 6 */ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* 7 */ 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x00,
|
|
|
+ /* 8 */ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* 9 */ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
|
|
|
+ /* : */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00,
|
|
|
+ /* ; */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x08, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* = */ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* A */ 0x18, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00,
|
|
|
+ /* B */ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
|
|
|
+ /* C */ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
|
|
|
+ /* D */ 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00,
|
|
|
+ /* E */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x7e, 0x00,
|
|
|
+ /* F */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x60, 0x00,
|
|
|
+ /* G */ 0x3e, 0x60, 0x60, 0x6e, 0x66, 0x66, 0x3e, 0x00,
|
|
|
+ /* H */ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
|
|
|
+ /* I */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
|
|
|
+ /* J */ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* K */ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
|
|
|
+ /* L */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
|
|
|
+ /* M */ CC 0xc6, CC 0xee, CC 0xfe, CC 0xd6, CC 0xc6, CC 0xc6, CC 0xc6, 0x00,
|
|
|
+ /* N */ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
|
|
|
+ /* O */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* P */ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
|
|
|
+ /* Q */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x6e, 0x3e, 0x00,
|
|
|
+ /* R */ 0x7c, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x00,
|
|
|
+ /* S */ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
|
|
|
+ /* T */ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
|
|
|
+ /* U */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
|
|
|
+ /* V */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x00,
|
|
|
+ /* W */ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x2c, 0x00,
|
|
|
+ /* X */ 0x66, 0x66, 0x3c, 0x10, 0x3c, 0x66, 0x66, 0x00,
|
|
|
+ /* Y */ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
|
|
|
+ /* Z */ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* _ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* a */ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
|
|
|
+ /* b */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00,
|
|
|
+ /* c */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00,
|
|
|
+ /* d */ 0x06, 0x06, 0x6e, 0x66, 0x66, 0x66, 0x3e, 0x00,
|
|
|
+ /* e */ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
|
|
|
+ /* f */ 0x0c, 0x18, 0x18, 0x3c, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* g */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x7c,
|
|
|
+ /* h */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
|
|
|
+ /* i */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* j */ 0x00, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x6c, 0x38,
|
|
|
+ /* k */ 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00,
|
|
|
+ /* l */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* m */ 0x00, 0x00, 0x5c, 0x76, 0x56, 0x56, 0x56, 0x00,
|
|
|
+ /* n */ 0x00, 0x00, 0x6c, 0x7e, 0x66, 0x66, 0x66, 0x00,
|
|
|
+ /* o */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
|
|
|
+ /* p */ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
|
|
|
+ /* q */ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
|
|
|
+ /* r */ 0x00, 0x00, 0x18, 0x1a, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* s */ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
|
|
|
+ /* t */ 0x00, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
|
|
+ /* u */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3a, 0x00,
|
|
|
+ /* v */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x00,
|
|
|
+ /* w */ 0x00, 0x00, 0x56, 0x56, 0x56, 0x56, 0x7e, 0x00,
|
|
|
+ /* x */ 0x00, 0x00, 0x66, 0x66, 0x18, 0x66, 0x66, 0x00,
|
|
|
+ /* y */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x7c,
|
|
|
+ /* z */ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ /* | */ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ };
|
|
|
+#endif
|
|
|
+
|
|
|
+ // pad the file to the next valid MGD2 size if it doesn't have a valid size
|
|
|
+ if (genesis_rom_size <= 1 * MBIT)
|
|
|
+ genesis_rom_size = 1 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 2 * MBIT)
|
|
|
+ genesis_rom_size = 2 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 4 * MBIT)
|
|
|
+ genesis_rom_size = 4 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 8 * MBIT)
|
|
|
+ genesis_rom_size = 8 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 16 * MBIT)
|
|
|
+ genesis_rom_size = 16 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 20 * MBIT)
|
|
|
+ genesis_rom_size = 20 * MBIT;
|
|
|
+ else if (genesis_rom_size <= 24 * MBIT)
|
|
|
+ genesis_rom_size = 24 * MBIT;
|
|
|
+ else
|
|
|
+ genesis_rom_size = 32 * MBIT;
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name);
|
|
|
+ ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME);
|
|
|
+
|
|
|
+ mgd_interleave (&rom_buffer, genesis_rom_size);
|
|
|
+ ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb");
|
|
|
+
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ free (rom_buffer);
|
|
|
+
|
|
|
+#if 0 // TODO: We need more info about the Multi Game Hunter (and a new option)
|
|
|
+ // automatically create MGH name file
|
|
|
+ memset (mgh, 0, sizeof (mgh));
|
|
|
+ mgh[0] = 'M';
|
|
|
+ mgh[1] = 'G';
|
|
|
+ mgh[2] = 'H';
|
|
|
+ mgh[3] = 0x1a;
|
|
|
+ mgh[4] = 0x06;
|
|
|
+ mgh[5] = (char) 0xf0;
|
|
|
+ mgh[31] = (char) 0xff;
|
|
|
+
|
|
|
+ // In addition to the above, uCON also does "memcpy (mgh + 16, "MGH By uCON/chp", 15);"
|
|
|
+
|
|
|
+ memcpy (buf, rominfo->name, 15); // copy first 15 bytes (don't use strlen() or strcpy())
|
|
|
+ for (x = 0; x < 15; x++)
|
|
|
+ {
|
|
|
+ for (y = 0; y < 4; y++)
|
|
|
+ mgh[((x + 2) * 16) + y + 4] = mghcharset[(((unsigned char *) buf)[x] * 8) + y];
|
|
|
+ for (y = 4; y < 8; y++)
|
|
|
+ mgh[((x + 2) * 16) + y + 244] = mghcharset[(((unsigned char *) buf)[x] * 8) + y];
|
|
|
+ }
|
|
|
+
|
|
|
+ set_suffix (dest_name, ".mgh");
|
|
|
+ ucon64_output_fname (dest_name, OF_FORCE_BASENAME);
|
|
|
+ /*
|
|
|
+ If a backup would be created it would overwrite the backup of the ROM. The
|
|
|
+ ROM backup is more important, so we don't write a backup of the MGH file.
|
|
|
+ */
|
|
|
+ ucon64_fwrite (mgh, 0, sizeof (mgh), dest_name, "wb");
|
|
|
+#endif
|
|
|
+ mgd_write_index_file ((char *) basename2 (dest_name), 1);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_s (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ st_smd_header_t smd_header;
|
|
|
+ char dest_name[FILENAME_MAX], *p;
|
|
|
+ int x, nparts, surplus, size = ucon64.file_size - rominfo->buheader_len,
|
|
|
+ part_size;
|
|
|
+
|
|
|
+ if (UCON64_ISSET (ucon64.part_size))
|
|
|
+ {
|
|
|
+ part_size = ucon64.part_size;
|
|
|
+ // Don't allow too small part sizes, see src/console/snes.c (snes_s())
|
|
|
+ if (part_size < 4 * MBIT)
|
|
|
+ {
|
|
|
+ fprintf (stderr,
|
|
|
+ "ERROR: Split part size must be larger than or equal to 4 Mbit\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (type == SMD)
|
|
|
+ part_size = 4 * MBIT; // SMD uses 4 Mb parts
|
|
|
+ else
|
|
|
+ part_size = 8 * MBIT; // MGD and Magicom ("BIN") use
|
|
|
+ } // 8 Mb parts
|
|
|
+
|
|
|
+ if (size <= part_size)
|
|
|
+ {
|
|
|
+ printf (
|
|
|
+ "NOTE: ROM size is smaller than or equal to %d Mbit -- won't be split\n",
|
|
|
+ part_size / MBIT);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ nparts = size / part_size;
|
|
|
+ surplus = size % part_size;
|
|
|
+
|
|
|
+ if (type == SMD)
|
|
|
+ {
|
|
|
+ ucon64_fread (&smd_header, 0, SMD_HEADER_LEN, ucon64.rom);
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".1");
|
|
|
+ ucon64_output_fname (dest_name, 0);
|
|
|
+ p = strrchr (dest_name, '.') + 1;
|
|
|
+
|
|
|
+ smd_header.size = part_size / 16384;
|
|
|
+ smd_header.id0 = 3;
|
|
|
+ // if smd_header.split bit 6 == 0 -> last file of the ROM
|
|
|
+ smd_header.split |= 0x40;
|
|
|
+ for (x = 0; x < nparts; x++)
|
|
|
+ {
|
|
|
+ if (surplus == 0 && x == nparts - 1)
|
|
|
+ smd_header.split &= ~0x40; // last file -> clear bit 6
|
|
|
+
|
|
|
+ // don't write backups of parts, because one name is used
|
|
|
+ ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb");
|
|
|
+ fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "ab");
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+
|
|
|
+ (*p)++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (surplus != 0)
|
|
|
+ {
|
|
|
+ smd_header.size = surplus / 16384;
|
|
|
+ smd_header.split &= ~0x40; // last file -> clear bit 6
|
|
|
+
|
|
|
+ // don't write backups of parts, because one name is used
|
|
|
+ ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb");
|
|
|
+ fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "ab");
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (type == MGD_GEN)
|
|
|
+ {
|
|
|
+ char suffix[5], *p_index, name[9], *names[16], names_mem[16][9];
|
|
|
+ int n, offset, size, name_i = 0;
|
|
|
+
|
|
|
+ for (n = 0; n < 16; n++)
|
|
|
+ names[n] = names_mem[n];
|
|
|
+
|
|
|
+ mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name);
|
|
|
+ strcpy (suffix, (char *) get_suffix (dest_name));
|
|
|
+ if ((p = strchr (dest_name, '.')))
|
|
|
+ *p = 0;
|
|
|
+ n = strlen (dest_name);
|
|
|
+ if (n > 7)
|
|
|
+ n = 7;
|
|
|
+ dest_name[n] = 'A';
|
|
|
+ dest_name[n + 1] = 0;
|
|
|
+
|
|
|
+ strcpy (name, dest_name);
|
|
|
+ p_index = &name[n];
|
|
|
+
|
|
|
+ strcat (dest_name, suffix);
|
|
|
+ ucon64_output_fname (dest_name, OF_FORCE_BASENAME);
|
|
|
+ p = strrchr (dest_name, '.') - 1;
|
|
|
+
|
|
|
+ if (surplus)
|
|
|
+ nparts++;
|
|
|
+ for (x = 0; x < nparts; x++)
|
|
|
+ {
|
|
|
+ offset = x * (part_size / 2);
|
|
|
+ size = part_size / 2;
|
|
|
+ if (offset + size > ucon64.file_size / 2)
|
|
|
+ size = ucon64.file_size / 2 - offset;
|
|
|
+ // don't write backups of parts, because one name is used
|
|
|
+ // write first half of file
|
|
|
+ fcopy (ucon64.rom, offset, size, dest_name, "wb");
|
|
|
+ // write second half of file; don't do: "(nparts / 2) * part_size"!
|
|
|
+ fcopy (ucon64.rom, ucon64.file_size / 2 + offset, size, dest_name, "ab");
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+
|
|
|
+ (*p)++;
|
|
|
+
|
|
|
+ strcpy (names[name_i], name);
|
|
|
+ name_i++;
|
|
|
+ (*p_index)++;
|
|
|
+ }
|
|
|
+ mgd_write_index_file (names, name_i);
|
|
|
+ }
|
|
|
+ else // type == BIN
|
|
|
+ {
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".r01");
|
|
|
+ ucon64_output_fname (dest_name, 0);
|
|
|
+ p = strrchr (dest_name, '.') + 3;
|
|
|
+
|
|
|
+ for (x = 0; x < nparts; x++)
|
|
|
+ {
|
|
|
+ if (surplus == 0 && x == nparts - 1)
|
|
|
+ *p = '0'; // last file should have suffix ".r00"
|
|
|
+ fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "wb");
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ (*p)++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (surplus != 0)
|
|
|
+ {
|
|
|
+ *p = '0';
|
|
|
+ fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "wb");
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_j (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], *p;
|
|
|
+ int block_size, total_size = 0;
|
|
|
+
|
|
|
+ if (type == SMD)
|
|
|
+ {
|
|
|
+ unsigned char buf[3];
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".smd");
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+
|
|
|
+ strcpy (src_name, ucon64.rom);
|
|
|
+ p = strrchr (src_name, '.') + 1;
|
|
|
+ fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb");
|
|
|
+ block_size = ucon64.file_size - rominfo->buheader_len;
|
|
|
+ while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
|
|
|
+ {
|
|
|
+ printf ("Joined: %s\n", src_name);
|
|
|
+ total_size += block_size;
|
|
|
+ (*p)++;
|
|
|
+ block_size = fsizeof (src_name) - rominfo->buheader_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rominfo->buheader_len)
|
|
|
+ { // fix header
|
|
|
+ buf[0] = total_size / 16384; // # 16 kB blocks
|
|
|
+ buf[1] = 3; // ID 0
|
|
|
+ buf[2] = 0; // last file -> clear bit 6
|
|
|
+ ucon64_fwrite (buf, 0, 3, dest_name, "r+b");
|
|
|
+ buf[0] = 0xaa; // ID 1
|
|
|
+ buf[1] = 0xbb; // ID 2
|
|
|
+ buf[2] = 6; // type Genesis
|
|
|
+ ucon64_fwrite (buf, 8, 3, dest_name, "r+b");
|
|
|
+ }
|
|
|
+
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ }
|
|
|
+ else if (type == MGD_GEN)
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ file1 file2 file3 file4
|
|
|
+ 1/2 3/4 5/6 7/8 (1st half/2nd half)
|
|
|
+ joined file
|
|
|
+ 1/3/5/7/2/4/6/8
|
|
|
+ */
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".tmp"); // users should use -mgd to get
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0); // a correct name
|
|
|
+ remove (dest_name);
|
|
|
+
|
|
|
+ strcpy (src_name, ucon64.rom);
|
|
|
+ p = strrchr (src_name, '.') - 1;
|
|
|
+ block_size = (ucon64.file_size - rominfo->buheader_len) / 2;
|
|
|
+ while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
|
|
|
+ {
|
|
|
+ (*p)++;
|
|
|
+ // BUG ALERT: Assume all parts have the same header length
|
|
|
+ block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ strcpy (src_name, ucon64.rom);
|
|
|
+ p = strrchr (src_name, '.') - 1;
|
|
|
+ block_size = (ucon64.file_size - rominfo->buheader_len) / 2;
|
|
|
+ while (fcopy (src_name, rominfo->buheader_len + block_size,
|
|
|
+ block_size, dest_name, "ab") != -1)
|
|
|
+ {
|
|
|
+ printf ("Joined: %s\n", src_name); // print this here, not in the
|
|
|
+ (*p)++; // previous loop
|
|
|
+ // BUG ALERT: Assume all parts have the same header length
|
|
|
+ block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ }
|
|
|
+ else if (type == BIN)
|
|
|
+ {
|
|
|
+ int tried_r00 = 0;
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ set_suffix (dest_name, ".bin");
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+ remove (dest_name);
|
|
|
+
|
|
|
+ strcpy (src_name, ucon64.rom);
|
|
|
+ p = strrchr (src_name, '.') + 3;
|
|
|
+ *(p - 1) = '0'; // be user friendly and avoid confusion
|
|
|
+ *p = '1'; // (.r01 is first file, not .r00)
|
|
|
+ block_size = ucon64.file_size - rominfo->buheader_len;
|
|
|
+ while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
|
|
|
+ {
|
|
|
+ printf ("Joined: %s\n", src_name);
|
|
|
+ if (tried_r00)
|
|
|
+ break; // quit after joining last file
|
|
|
+ (*p)++;
|
|
|
+ if (!tried_r00 && access (src_name, F_OK) != 0)
|
|
|
+ { // file does not exist -> try .r00
|
|
|
+ *p = '0';
|
|
|
+ tried_r00 = 1;
|
|
|
+ }
|
|
|
+ block_size = fsizeof (src_name) - rominfo->buheader_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+genesis_name (st_rominfo_t *rominfo, const char *name1, const char *name2)
|
|
|
+{
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+ char buf[FILENAME_MAX];
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (name1)
|
|
|
+ {
|
|
|
+ memset (buf, ' ', GENESIS_NAME_LEN);
|
|
|
+ strncpy (buf, name1, strlen (name1) > GENESIS_NAME_LEN ?
|
|
|
+ GENESIS_NAME_LEN : strlen (name1));
|
|
|
+ memcpy (&rom_buffer[GENESIS_HEADER_START + 32 + GENESIS_NAME_LEN], buf,
|
|
|
+ GENESIS_NAME_LEN);
|
|
|
+ }
|
|
|
+ if (name2)
|
|
|
+ {
|
|
|
+ memset (buf, ' ', GENESIS_NAME_LEN);
|
|
|
+ strncpy (buf, name2, strlen (name2) > GENESIS_NAME_LEN ?
|
|
|
+ GENESIS_NAME_LEN : strlen (name2));
|
|
|
+ memcpy (&rom_buffer[GENESIS_HEADER_START + 32], buf, GENESIS_NAME_LEN);
|
|
|
+ }
|
|
|
+
|
|
|
+ strcpy (buf, ucon64.rom);
|
|
|
+ ucon64_file_handler (buf, NULL, 0);
|
|
|
+ save_rom (rominfo, buf, &rom_buffer, genesis_rom_size);
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf (ucon64_msg[WROTE], buf);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_n (st_rominfo_t *rominfo, const char *name)
|
|
|
+{
|
|
|
+ return genesis_name (rominfo, name, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_n2 (st_rominfo_t *rominfo, const char *name)
|
|
|
+{
|
|
|
+ return genesis_name (rominfo, NULL, name);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_1991 (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ return genesis_name (rominfo, "(C)SEGA", "(C) SEGA");
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_chk (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+ char dest_name[FILENAME_MAX];
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ rom_buffer[GENESIS_HEADER_START + 143] = rominfo->current_internal_crc; // low byte of checksum
|
|
|
+ rom_buffer[GENESIS_HEADER_START + 142] = rominfo->current_internal_crc >> 8; // high byte of checksum
|
|
|
+
|
|
|
+ dumper (stdout, &rom_buffer[GENESIS_HEADER_START + 0x8e], 2, GENESIS_HEADER_START + 0x8e, DUMPER_HEX);
|
|
|
+
|
|
|
+ strcpy (dest_name, ucon64.rom);
|
|
|
+ ucon64_file_handler (dest_name, NULL, 0);
|
|
|
+ save_rom (rominfo, dest_name, &rom_buffer, genesis_rom_size);
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf (ucon64_msg[WROTE], dest_name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+genesis_fix_pal_protection (st_rominfo_t *rominfo)
|
|
|
+/*
|
|
|
+ This function searches for PAL protection codes. If it finds one it will
|
|
|
+ fix the code so that the game will run on a Genesis (NTSC).
|
|
|
+*/
|
|
|
+{
|
|
|
+ char fname[FILENAME_MAX];
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+ int offset = 0, block_size, n = 0, n_extra_patterns, n2;
|
|
|
+ st_cm_pattern_t *patterns = NULL;
|
|
|
+
|
|
|
+ strcpy (fname, "genpal.txt");
|
|
|
+ // First try the current directory, then the configuration directory
|
|
|
+ if (access (fname, F_OK | R_OK) == -1)
|
|
|
+ sprintf (fname, "%s" FILE_SEPARATOR_S "genpal.txt", ucon64.configdir);
|
|
|
+ n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0);
|
|
|
+ if (n_extra_patterns >= 0)
|
|
|
+ printf ("Found %d additional code%s in %s\n",
|
|
|
+ n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname);
|
|
|
+
|
|
|
+ puts ("Attempting to fix PAL protection code...");
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ while ((n2 = ucon64.file_size - offset) > 0)
|
|
|
+ {
|
|
|
+ block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2;
|
|
|
+ for (n2 = 0; n2 < n_extra_patterns; n2++)
|
|
|
+ n += change_mem2 ((char *) rom_buffer + offset, block_size,
|
|
|
+ patterns[n2].search,
|
|
|
+ patterns[n2].search_size,
|
|
|
+ patterns[n2].wildcard,
|
|
|
+ patterns[n2].escape,
|
|
|
+ patterns[n2].replace,
|
|
|
+ patterns[n2].replace_size,
|
|
|
+ patterns[n2].offset,
|
|
|
+ patterns[n2].sets);
|
|
|
+ offset += 16 * 1024;
|
|
|
+ }
|
|
|
+ cleanup_cm_patterns (&patterns, n_extra_patterns);
|
|
|
+
|
|
|
+ strcpy (fname, ucon64.rom);
|
|
|
+ ucon64_file_handler (fname, NULL, 0);
|
|
|
+ save_rom (rominfo, fname, &rom_buffer, genesis_rom_size);
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
|
|
|
+ printf (ucon64_msg[WROTE], fname);
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+genesis_fix_ntsc_protection (st_rominfo_t *rominfo)
|
|
|
+/*
|
|
|
+ This function searches for NTSC protection codes. If it finds one it will
|
|
|
+ fix the code so that the game will run on a Mega Drive (PAL).
|
|
|
+*/
|
|
|
+{
|
|
|
+ char fname[FILENAME_MAX];
|
|
|
+ unsigned char *rom_buffer = NULL;
|
|
|
+ int offset = 0, block_size, n = 0, n_extra_patterns, n2;
|
|
|
+ st_cm_pattern_t *patterns = NULL;
|
|
|
+
|
|
|
+ strcpy (fname, "mdntsc.txt");
|
|
|
+ // First try the current directory, then the configuration directory
|
|
|
+ if (access (fname, F_OK | R_OK) == -1)
|
|
|
+ sprintf (fname, "%s" FILE_SEPARATOR_S "mdntsc.txt", ucon64.configdir);
|
|
|
+ n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0);
|
|
|
+ if (n_extra_patterns >= 0)
|
|
|
+ printf ("Found %d additional code%s in %s\n",
|
|
|
+ n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname);
|
|
|
+
|
|
|
+ puts ("Attempting to fix NTSC protection code...");
|
|
|
+
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ while ((n2 = ucon64.file_size - offset) > 0)
|
|
|
+ {
|
|
|
+ block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2;
|
|
|
+ for (n2 = 0; n2 < n_extra_patterns; n2++)
|
|
|
+ n += change_mem2 ((char *) rom_buffer + offset, block_size,
|
|
|
+ patterns[n2].search,
|
|
|
+ patterns[n2].search_size,
|
|
|
+ patterns[n2].wildcard,
|
|
|
+ patterns[n2].escape,
|
|
|
+ patterns[n2].replace,
|
|
|
+ patterns[n2].replace_size,
|
|
|
+ patterns[n2].offset,
|
|
|
+ patterns[n2].sets);
|
|
|
+ offset += 16 * 1024;
|
|
|
+ }
|
|
|
+ cleanup_cm_patterns (&patterns, n_extra_patterns);
|
|
|
+
|
|
|
+ strcpy (fname, ucon64.rom);
|
|
|
+ ucon64_file_handler (fname, NULL, 0);
|
|
|
+ save_rom (rominfo, fname, &rom_buffer, genesis_rom_size);
|
|
|
+
|
|
|
+ free (rom_buffer);
|
|
|
+ printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
|
|
|
+ printf (ucon64_msg[WROTE], fname);
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_f (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ In the Philipines the television standard is NTSC, but do games made
|
|
|
+ for the Philipines exist?
|
|
|
+ Just like with SNES we don't guarantee anything for files that needn't be
|
|
|
+ fixed/cracked/patched.
|
|
|
+ */
|
|
|
+ if (genesis_tv_standard == 0) // NTSC (Japan, U.S.A. or Brazil ('4'))
|
|
|
+ return genesis_fix_ntsc_protection (rominfo);
|
|
|
+ else
|
|
|
+ return genesis_fix_pal_protection (rominfo);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+unsigned char *
|
|
|
+load_rom (st_rominfo_t *rominfo, const char *name, unsigned char *rom_buffer)
|
|
|
+{
|
|
|
+ FILE *file;
|
|
|
+ int bytesread;
|
|
|
+
|
|
|
+ if ((file = fopen (name, "rb")) == NULL)
|
|
|
+ return NULL;
|
|
|
+ if (!(rom_buffer = (unsigned char *) malloc (genesis_rom_size)))
|
|
|
+ {
|
|
|
+ fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], genesis_rom_size);
|
|
|
+ fclose (file);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ fseek (file, rominfo->buheader_len, SEEK_SET); // don't do this only for SMD!
|
|
|
+ bytesread = fread (rom_buffer, 1, genesis_rom_size, file);
|
|
|
+ if (bytesread < genesis_rom_size)
|
|
|
+ memset (rom_buffer + bytesread, 0, genesis_rom_size - bytesread);
|
|
|
+
|
|
|
+ if (type != BIN)
|
|
|
+ {
|
|
|
+ if (ucon64.fcrc32 == 0)
|
|
|
+ ucon64.fcrc32 = crc32 (ucon64.fcrc32, rom_buffer, genesis_rom_size);
|
|
|
+
|
|
|
+ if (type == SMD)
|
|
|
+ smd_deinterleave (rom_buffer, bytesread);
|
|
|
+ else // type == MGD_GEN
|
|
|
+ mgd_deinterleave (&rom_buffer, bytesread, genesis_rom_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ucon64.crc32 == 0) // calculate the CRC32 only once
|
|
|
+ ucon64.crc32 = crc32 (0, rom_buffer, bytesread);
|
|
|
+
|
|
|
+ fclose (file);
|
|
|
+ return rom_buffer;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+save_rom (st_rominfo_t *rominfo, const char *name, unsigned char **buffer, int size)
|
|
|
+{
|
|
|
+ if (type == SMD)
|
|
|
+ {
|
|
|
+ int n;
|
|
|
+
|
|
|
+ /*
|
|
|
+ Copy the complete backup unit header length, no matter how strange or
|
|
|
+ how large the size. fcopy() doesn't copy if source and destination are
|
|
|
+ one and the same. Copying is necessary if source and destination are
|
|
|
+ different so that we can use "r+b" when writing the contents of buffer
|
|
|
+ to disk.
|
|
|
+ */
|
|
|
+ fcopy (ucon64.rom, 0, rominfo->buheader_len, name, "wb");
|
|
|
+
|
|
|
+ smd_interleave (*buffer, size);
|
|
|
+ n = ucon64_fwrite (*buffer, rominfo->buheader_len, size, name, "r+b");
|
|
|
+ if (size < (int) (ucon64.file_size - rominfo->buheader_len))
|
|
|
+ truncate2 (name, rominfo->buheader_len + size);
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+ else if (type == MGD_GEN)
|
|
|
+ {
|
|
|
+ mgd_interleave (buffer, size); // allocates new buffer
|
|
|
+ return ucon64_fwrite (*buffer, 0, size, name, "wb");
|
|
|
+ }
|
|
|
+ else // type == BIN
|
|
|
+ return ucon64_fwrite (*buffer, 0, size, name, "wb");
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+write_game_table_entry (FILE *destfile, int file_no, st_rominfo_t *rominfo,
|
|
|
+ int totalsize)
|
|
|
+{
|
|
|
+ static int sram_page = 0, file_no_sram = 0;
|
|
|
+ int n;
|
|
|
+ unsigned char name[0x1c], flags = 0; // SRAM/region flags: F, D (reserved), E, P, V, T, S1, S0
|
|
|
+
|
|
|
+ fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET);
|
|
|
+ fputc (0xff, destfile); // 0x0 = 0xff
|
|
|
+ memcpy (name, rominfo->name, 0x1c);
|
|
|
+ for (n = 0; n < 0x1c; n++)
|
|
|
+ {
|
|
|
+ if (!isprint ((int) name[n]))
|
|
|
+ name[n] = '.';
|
|
|
+ else
|
|
|
+ name[n] = toupper (name[n]); // according to Leo, MDPACKU4.BIN
|
|
|
+ } // only supports upper case characters
|
|
|
+ fwrite (name, 1, 0x1c, destfile); // 0x1 - 0x1c = name
|
|
|
+ fputc (0, destfile); // 0x1d = 0
|
|
|
+ fputc (totalsize / (2 * MBIT), destfile); // 0x1e = bank code
|
|
|
+
|
|
|
+ flags = 0x80; // set F (?, default)
|
|
|
+ if (genesis_has_ram)
|
|
|
+ {
|
|
|
+ if (sram_page == 3)
|
|
|
+ file_no_sram = file_no;
|
|
|
+ else if (sram_page > 3)
|
|
|
+ {
|
|
|
+ printf ("WARNING: This ROM will share SRAM with ROM %d\n", file_no_sram);
|
|
|
+ sram_page = 3;
|
|
|
+ }
|
|
|
+ flags |= sram_page++;
|
|
|
+ if ((ucon64.file_size - rominfo->buheader_len) <= 16 * MBIT)
|
|
|
+ flags &= ~0x80; // clear F (<=16 Mb & SRAM)
|
|
|
+ }
|
|
|
+ else
|
|
|
+ flags |= 4; // set T (no SRAM)
|
|
|
+
|
|
|
+ /*
|
|
|
+ J(apan) would probably be a more logical name for bit E(urope). I (dbjh)
|
|
|
+ base that on information of SamIAm. According to him there are three types
|
|
|
+ of the console:
|
|
|
+ American Genesis NTSC/no Japanese switch
|
|
|
+ Japanese Genesis NTSC/Japanese switch
|
|
|
+ Mega Drive (rest of the world?) PAL/no Japanese switch
|
|
|
+ So, besides PAL and NTSC there is the variable whether the console contains
|
|
|
+ a Japanese "switch". The region function is used to make the game "think"
|
|
|
+ it's running on another type of console. For example, a Japanese game
|
|
|
+ running on a (European) Mega Drive should have the P and E bit set to 0.
|
|
|
+ */
|
|
|
+ if (UCON64_ISSET (ucon64.region))
|
|
|
+ {
|
|
|
+ if (!genesis_japanese)
|
|
|
+ flags |= 0x20; // set E(urope)
|
|
|
+ if (genesis_tv_standard == 1)
|
|
|
+ flags |= 0x10; // set P(AL)
|
|
|
+
|
|
|
+ flags |= 8; // set V (enable region function)
|
|
|
+ }
|
|
|
+
|
|
|
+ fputc (flags, destfile); // 0x1f = flags
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_multi (int truncate_size, char *fname)
|
|
|
+{
|
|
|
+#define BUFSIZE (32 * 1024) // must be a multiple of 16 kB
|
|
|
+ int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done,
|
|
|
+ truncated = 0, paddedsize, org_do_not_calc_crc = ucon64.do_not_calc_crc;
|
|
|
+ struct stat fstate;
|
|
|
+ FILE *srcfile, *destfile;
|
|
|
+ char destname[FILENAME_MAX];
|
|
|
+ unsigned char buffer[BUFSIZE];
|
|
|
+
|
|
|
+ if (truncate_size == 0)
|
|
|
+ {
|
|
|
+ fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fname != NULL)
|
|
|
+ {
|
|
|
+ strcpy(destname, fname);
|
|
|
+ n_files = ucon64.argc;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ strcpy(destname, ucon64.argv[ucon64.argc - 1]);
|
|
|
+ n_files = ucon64.argc - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME);
|
|
|
+ if ((destfile = fopen (destname, "wb")) == NULL)
|
|
|
+ {
|
|
|
+ fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // do this check here, because one error message is enough (not for every game)
|
|
|
+ if (UCON64_ISSET (ucon64.region))
|
|
|
+ switch (ucon64.region)
|
|
|
+ {
|
|
|
+ case 0: // NTSC/Japan
|
|
|
+ case 1: // NTSC/U.S.A.
|
|
|
+ case 2: // PAL
|
|
|
+ case 256:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ printf ("WARNING: Invalid region code specified, using values games expect\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ printf ("Creating multi-game file for MD-PRO: %s\n", destname);
|
|
|
+
|
|
|
+ file_no = 0;
|
|
|
+ for (n = 1; n < n_files; n++)
|
|
|
+ {
|
|
|
+ if (access (ucon64.argv[n], F_OK))
|
|
|
+ continue; // "file" does not exist (option)
|
|
|
+ stat (ucon64.argv[n], &fstate);
|
|
|
+ if (!S_ISREG (fstate.st_mode))
|
|
|
+ continue;
|
|
|
+ if (file_no == 32) // loader + 31 games
|
|
|
+ {
|
|
|
+ printf ("WARNING: A multi-game file can contain a maximum of 31 games. The other files\n"
|
|
|
+ " are ignored.\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ucon64.console = UCON64_UNKNOWN;
|
|
|
+ ucon64.rom = ucon64.argv[n];
|
|
|
+ ucon64.file_size = fsizeof (ucon64.rom);
|
|
|
+ // DON'T use fstate.st_size, because file could be compressed
|
|
|
+ ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
|
|
|
+ ucon64.buheader_len : 0;
|
|
|
+ ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ?
|
|
|
+ ucon64.interleaved : 0;
|
|
|
+ ucon64.do_not_calc_crc = 1;
|
|
|
+ if (genesis_init (ucon64.rominfo) != 0)
|
|
|
+ printf ("WARNING: %s does not appear to be a Genesis ROM\n", ucon64.rom);
|
|
|
+ /*
|
|
|
+ NOTE: This is NOT the place to mess with ucon64.console. When this
|
|
|
+ function is entered ucon64.console must have been UCON64_GEN. We
|
|
|
+ modify ucon64.console temporarily only to be able to help detect
|
|
|
+ problems with incorrect files.
|
|
|
+ */
|
|
|
+ if (UCON64_ISSET (ucon64.region))
|
|
|
+ switch (ucon64.region)
|
|
|
+ {
|
|
|
+ case 0: // NTSC/Japan
|
|
|
+ genesis_tv_standard = 0;
|
|
|
+ genesis_japanese = 1;
|
|
|
+ break;
|
|
|
+ case 1: // NTSC/U.S.A.
|
|
|
+ genesis_tv_standard = 0;
|
|
|
+ genesis_japanese = 0;
|
|
|
+ break;
|
|
|
+ case 2: // PAL
|
|
|
+ genesis_tv_standard = 1;
|
|
|
+ genesis_japanese = 0;
|
|
|
+ break;
|
|
|
+ case 256:
|
|
|
+ // Do nothing. Use whatever values we found for genesis_tv_standard and
|
|
|
+ // genesis_japanese.
|
|
|
+ break;
|
|
|
+ // no default case, because we already checked value of ucon64.region
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((srcfile = fopen (ucon64.rom, "rb")) == NULL)
|
|
|
+ {
|
|
|
+ fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (ucon64.rominfo->buheader_len)
|
|
|
+ fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET);
|
|
|
+
|
|
|
+ if (file_no == 0)
|
|
|
+ {
|
|
|
+ printf ("Loader: %s\n", ucon64.rom);
|
|
|
+ if (ucon64.file_size - ucon64.rominfo->buheader_len != MBIT)
|
|
|
+ printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ printf ("ROM%d: %s\n", file_no, ucon64.rom);
|
|
|
+ write_game_table_entry (destfile, file_no, ucon64.rominfo, totalsize);
|
|
|
+ fseek (destfile, totalsize, SEEK_SET); // restore file pointer
|
|
|
+ }
|
|
|
+ file_no++;
|
|
|
+
|
|
|
+ done = 0;
|
|
|
+ byteswritten = 0; // # of bytes written per file
|
|
|
+ while (!done)
|
|
|
+ {
|
|
|
+ if (ucon64.rominfo->interleaved == 2)
|
|
|
+ bytestowrite = fread_mgd (buffer, 1, BUFSIZE, srcfile);
|
|
|
+ else
|
|
|
+ {
|
|
|
+ bytestowrite = fread (buffer, 1, BUFSIZE, srcfile);
|
|
|
+ if (ucon64.rominfo->interleaved)
|
|
|
+ smd_deinterleave (buffer, BUFSIZE);
|
|
|
+ // yes, BUFSIZE. bytestowrite might not be n * 16 kB
|
|
|
+ }
|
|
|
+ if (totalsize + bytestowrite > truncate_size)
|
|
|
+ {
|
|
|
+ bytestowrite = truncate_size - totalsize;
|
|
|
+ done = 1;
|
|
|
+ truncated = 1;
|
|
|
+ printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n",
|
|
|
+ truncate_size / MBIT, ucon64.rom, ucon64.file_size -
|
|
|
+ ucon64.rominfo->buheader_len - (byteswritten + bytestowrite));
|
|
|
+ }
|
|
|
+ totalsize += bytestowrite;
|
|
|
+ if (bytestowrite == 0)
|
|
|
+ done = 1;
|
|
|
+ fwrite (buffer, 1, bytestowrite, destfile);
|
|
|
+ byteswritten += bytestowrite;
|
|
|
+ }
|
|
|
+ fclose (srcfile);
|
|
|
+ if (truncated)
|
|
|
+ break;
|
|
|
+
|
|
|
+ // games have to be aligned to (start at) a 2 Mbit boundary
|
|
|
+ paddedsize = (totalsize + 2 * MBIT - 1) & ~(2 * MBIT - 1);
|
|
|
+// printf ("paddedsize: %d (%f); totalsize: %d (%f)\n",
|
|
|
+// paddedsize, paddedsize / (1.0 * MBIT), totalsize, totalsize / (1.0 * MBIT));
|
|
|
+ if (paddedsize > totalsize)
|
|
|
+ {
|
|
|
+ // I (dbjh) don't think it is really necessary to pad to the truncate
|
|
|
+ // size, but it won't hurt
|
|
|
+ if (paddedsize > truncate_size)
|
|
|
+ {
|
|
|
+ truncated = 1; // not *really* truncated
|
|
|
+ paddedsize = truncate_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset (buffer, 0, BUFSIZE);
|
|
|
+ while (totalsize < paddedsize)
|
|
|
+ {
|
|
|
+ bytestowrite = paddedsize - totalsize > BUFSIZE ?
|
|
|
+ BUFSIZE : paddedsize - totalsize;
|
|
|
+ fwrite (buffer, 1, bytestowrite, destfile);
|
|
|
+ totalsize += bytestowrite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (truncated)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // fill the next game table entry
|
|
|
+ fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET);
|
|
|
+ fputc (0, destfile); // indicate no next game
|
|
|
+ fclose (destfile);
|
|
|
+ ucon64.console = UCON64_GEN;
|
|
|
+ ucon64.do_not_calc_crc = org_do_not_calc_crc;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+genesis_testinterleaved (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ unsigned char buf[16384] = { 0 };
|
|
|
+
|
|
|
+ ucon64_fread (buf, rominfo->buheader_len, 8192 + (GENESIS_HEADER_START + 4) / 2, ucon64.rom);
|
|
|
+ if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ smd_deinterleave (buf, 16384);
|
|
|
+ if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ q_fread_mgd (buf, rominfo->buheader_len + GENESIS_HEADER_START, 4, ucon64.rom);
|
|
|
+ if (!memcmp (buf, "SEGA", 4))
|
|
|
+ return 2;
|
|
|
+
|
|
|
+ return 0; // unknown, act as if it's BIN
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_init (st_rominfo_t *rominfo)
|
|
|
+{
|
|
|
+ int result = -1, value = 0, x, y;
|
|
|
+ unsigned char *rom_buffer = NULL, buf[MAXBUFSIZE], name[GENESIS_NAME_LEN + 1],
|
|
|
+ smd_header_split;
|
|
|
+ static char maker[9], country[200]; // 200 characters should be enough for 5 country names
|
|
|
+ static const char *genesis_maker[0x100] =
|
|
|
+ {
|
|
|
+ NULL, "Accolade/Infogrames", "Virgin Games", "Parker Brothers", "Westone",
|
|
|
+ NULL, NULL, NULL, NULL, "Westone",
|
|
|
+ "Takara", "Taito/Accolade", "Capcom", "Data East", "Namco/Tengen",
|
|
|
+ "Sunsoft", "Bandai", "Dempa", "Technosoft", "Technosoft",
|
|
|
+ "Asmik", NULL, "Extreme/Micronet", "Vic Tokai", "American Sammy",
|
|
|
+ "NCS", "Sigma Enterprises", "Toho", NULL, "Kyugo",
|
|
|
+ NULL, NULL, "Wolfteam", "Kaneko", NULL,
|
|
|
+ "Toaplan", "Tecmo", NULL, NULL, NULL,
|
|
|
+ "Toaplan", "Unipac", "UFL Company Ltd.", "Human", NULL,
|
|
|
+ "Game Arts", "Hot-B", "Sage's Creation", "Tengen/Time Warner",
|
|
|
+ "Renovation/Telenet",
|
|
|
+ "Electronic Arts", NULL, NULL, NULL, NULL,
|
|
|
+ "Psygnosis", "Razorsoft", NULL, "Mentrix", NULL,
|
|
|
+ "JVC/Victor Musical Industries", NULL, NULL, NULL, "IGS Corp.",
|
|
|
+ NULL, NULL, "CRI/Home Data", "Arena", "Virgin Games",
|
|
|
+ NULL, "Nichibutsu", NULL, "Soft Vision", "Palsoft",
|
|
|
+ NULL, "KOEI", NULL, NULL, "U.S. Gold",
|
|
|
+ NULL, "Acclaim/Flying Edge", NULL, "Gametek", NULL,
|
|
|
+ NULL, "Absolute", "Mindscape", "Domark", "Parker Brothers",
|
|
|
+ NULL, NULL, NULL, "Sony Imagesoft", "Sony Imagesoft",
|
|
|
+ "Konami", NULL, "Tradewest/Williams", NULL, "Codemasters",
|
|
|
+ "T*HQ Software", "TecMagik", NULL, "Takara", NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, "Hi Tech Entertainment/Designer Software", "Psygnosis", NULL,
|
|
|
+ NULL, NULL, NULL, NULL, "Accolade",
|
|
|
+ "Code Masters", NULL, NULL, NULL, "Spectrum HoloByte",
|
|
|
+ "Interplay", NULL, NULL, NULL, NULL,
|
|
|
+ "Activision", NULL, "Shiny & Playmates", NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, "Viacom International",
|
|
|
+ NULL, NULL, NULL, NULL, "Atlus",
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, "Infogrames", NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, "Fox Interactive", NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, "Psygnosis",
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, "Disney Interactive",
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+#define GENESIS_COUNTRY_MAX 0x57
|
|
|
+ *genesis_country[GENESIS_COUNTRY_MAX] =
|
|
|
+ {
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, "Brazil", NULL, NULL, // Brazil NTSC
|
|
|
+ NULL, "Hong Kong", NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ "Asia", "Brazil", NULL, NULL, "Europe", // Brazil PAL
|
|
|
+ "France", NULL, NULL, NULL, "Japan",
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ "U.S.A.", NULL
|
|
|
+ },
|
|
|
+#define GENESIS_IO_MAX 0x58
|
|
|
+ *genesis_io[GENESIS_IO_MAX] =
|
|
|
+ {
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, "Joystick for MS", NULL,
|
|
|
+ NULL, NULL, "Team Play", NULL, "6 Button Pad",
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, "Control Ball", "CD-ROM", NULL, NULL,
|
|
|
+ "Floppy Disk Drive", NULL, NULL, NULL, "3 Button Pad",
|
|
|
+ "Keyboard", "Activator", "Mega Mouse", NULL, NULL,
|
|
|
+ "Printer", NULL, "Serial RS232C", NULL, "Tablet",
|
|
|
+ NULL, "Paddle Controller", NULL
|
|
|
+ };
|
|
|
+
|
|
|
+ ucon64_fread (buf, 0, 11, ucon64.rom);
|
|
|
+ smd_header_split = buf[2];
|
|
|
+ if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 7)
|
|
|
+ {
|
|
|
+ rominfo->buheader_len = SMD_HEADER_LEN;
|
|
|
+ strcpy (rominfo->name, "Name: N/A");
|
|
|
+ rominfo->console_usage = NULL;
|
|
|
+ rominfo->copier_usage = smd_usage[0].help;
|
|
|
+ rominfo->maker = "Publisher: You?";
|
|
|
+ rominfo->country = "Country: Your country?";
|
|
|
+ rominfo->has_internal_crc = 0;
|
|
|
+ strcat (rominfo->misc, "Type: Super Magic Drive SRAM file\n");
|
|
|
+ ucon64.split = 0; // SRAM files are never split
|
|
|
+ type = SMD;
|
|
|
+ return 0; // rest is nonsense for SRAM file
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 6)
|
|
|
+ {
|
|
|
+ type = SMD;
|
|
|
+ rominfo->buheader_len = SMD_HEADER_LEN;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (UCON64_ISSET (ucon64.buheader_len)) // -hd, -nhd or -hdn option was specified
|
|
|
+ rominfo->buheader_len = ucon64.buheader_len;
|
|
|
+
|
|
|
+ rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ?
|
|
|
+ ucon64.interleaved : genesis_testinterleaved (rominfo);
|
|
|
+
|
|
|
+ if (rominfo->interleaved == 0)
|
|
|
+ type = BIN;
|
|
|
+ else if (rominfo->interleaved == 1)
|
|
|
+ type = SMD;
|
|
|
+ else if (rominfo->interleaved == 2)
|
|
|
+ type = MGD_GEN;
|
|
|
+
|
|
|
+ if (type == SMD)
|
|
|
+ {
|
|
|
+ genesis_rom_size = ((ucon64.file_size - rominfo->buheader_len) / 16384) * 16384;
|
|
|
+ if (genesis_rom_size != ucon64.file_size - rominfo->buheader_len)
|
|
|
+ rominfo->data_size = genesis_rom_size;
|
|
|
+
|
|
|
+ memset (buf, 0, 16384);
|
|
|
+ ucon64_fread (buf, rominfo->buheader_len,
|
|
|
+ 8192 + (GENESIS_HEADER_START + GENESIS_HEADER_LEN) / 2, ucon64.rom);
|
|
|
+ smd_deinterleave (buf, 16384); // buf will contain the deinterleaved data
|
|
|
+ memcpy (&genesis_header, buf + GENESIS_HEADER_START, GENESIS_HEADER_LEN);
|
|
|
+ }
|
|
|
+ else if (type == MGD_GEN)
|
|
|
+ {
|
|
|
+ // We use rominfo->buheader_len to make it user definable. Normally it
|
|
|
+ // should be 0 for MGD_GEN.
|
|
|
+ genesis_rom_size = ucon64.file_size - rominfo->buheader_len;
|
|
|
+ q_fread_mgd (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START,
|
|
|
+ GENESIS_HEADER_LEN, ucon64.rom);
|
|
|
+ }
|
|
|
+ else // type == BIN
|
|
|
+ {
|
|
|
+ // We use rominfo->buheader_len to make it user definable.
|
|
|
+ genesis_rom_size = ucon64.file_size - rominfo->buheader_len;
|
|
|
+ ucon64_fread (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START,
|
|
|
+ GENESIS_HEADER_LEN, ucon64.rom);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!UCON64_ISSET (ucon64.split))
|
|
|
+ {
|
|
|
+ if (type == SMD)
|
|
|
+ {
|
|
|
+ // This code does not work for the last part of a file
|
|
|
+ int split = 0;
|
|
|
+ if (smd_header_split & 0x40)
|
|
|
+ split = ucon64_testsplit (ucon64.rom);
|
|
|
+ ucon64.split = split; // force displayed info to be correct
|
|
|
+ } // if not split (see ucon64.c)
|
|
|
+ else if (type == BIN)
|
|
|
+ {
|
|
|
+ // This code isn't fool-proof, but it's not that important
|
|
|
+ const char *ptr = get_suffix (ucon64.rom);
|
|
|
+ if (strlen (ptr) == 4 && ptr[1] == 'r' &&
|
|
|
+ ptr[2] >= '0' && ptr[2] <= '9' && ptr[3] >= '0' && ptr[3] <= '9')
|
|
|
+ {
|
|
|
+ int n_parts = 0;
|
|
|
+ char fname[FILENAME_MAX], *digits;
|
|
|
+
|
|
|
+ strcpy (fname, ucon64.rom);
|
|
|
+ digits = fname + strlen (fname) - 2;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ sprintf (digits, "%02d", n_parts);
|
|
|
+ if (access (fname, F_OK) == 0)
|
|
|
+ n_parts++;
|
|
|
+ else
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ while (n_parts < 100);
|
|
|
+
|
|
|
+ if (n_parts)
|
|
|
+ ucon64.split = n_parts;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!memcmp (&OFFSET (genesis_header, 0), "SEGA", 4) ||
|
|
|
+ ucon64.console == UCON64_GEN)
|
|
|
+ result = 0;
|
|
|
+ else
|
|
|
+ result = -1;
|
|
|
+
|
|
|
+ rominfo->header_start = GENESIS_HEADER_START;
|
|
|
+ rominfo->header_len = GENESIS_HEADER_LEN;
|
|
|
+ rominfo->header = &genesis_header;
|
|
|
+
|
|
|
+ // internal ROM name
|
|
|
+ memcpy (rominfo->name, &OFFSET (genesis_header, 80), GENESIS_NAME_LEN);
|
|
|
+ rominfo->name[GENESIS_NAME_LEN] = 0;
|
|
|
+
|
|
|
+ // ROM maker
|
|
|
+ memcpy (maker, &OFFSET (genesis_header, 16), 8);
|
|
|
+ if (maker[3] == 'T' && maker[4] == '-')
|
|
|
+ {
|
|
|
+ sscanf (&maker[5], "%03d", &value);
|
|
|
+ rominfo->maker = NULL_TO_UNKNOWN_S (genesis_maker[value & 0xff]);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Don't use genesis_maker here. If it would be corrected/updated an
|
|
|
+ // incorrect publisher name would be displayed.
|
|
|
+ rominfo->maker =
|
|
|
+ (!strncmp (maker, "(C)ACLD", 7)) ? "Ballistic" :
|
|
|
+ (!strncmp (maker, "(C)AESI", 7)) ? "ASCII" :
|
|
|
+ (!strncmp (maker, "(C)ASCI", 7)) ? "ASCII" :
|
|
|
+ (!strncmp (maker, "(C)KANEKO", 9)) ? "Kaneko" :
|
|
|
+ (!strncmp (maker, "(C)PPP", 6)) ? "Gametek" :
|
|
|
+ (!strncmp (maker, "(C)RSI", 6)) ? "Razorsoft" : // or is it "(C)1RSI"?
|
|
|
+ (!strncmp (maker, "(C)SEGA", 7)) ? "Sega" :
|
|
|
+ (!strncmp (maker, "(C)TREC", 7)) ? "Treco" :
|
|
|
+ (!strncmp (maker, "(C)VRGN", 7)) ? "Virgin Games" :
|
|
|
+ (!strncmp (maker, "(C)WADN", 7)) ? "Parker Brothers" :
|
|
|
+ (!strncmp (maker, "(C)WSTN", 7)) ? "Westone" : NULL;
|
|
|
+ if (!rominfo->maker)
|
|
|
+ {
|
|
|
+ maker[8] = 0;
|
|
|
+ rominfo->maker = maker;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ genesis_tv_standard = 1; // default to PAL; NTSC has higher precedence
|
|
|
+ genesis_japanese = 0;
|
|
|
+
|
|
|
+ country[0] = 0;
|
|
|
+ // ROM country
|
|
|
+ for (x = 0; x < 5; x++)
|
|
|
+ {
|
|
|
+ int country_code = OFFSET (genesis_header, 240 + x);
|
|
|
+
|
|
|
+ if ((x > 0 && country_code == 0) || country_code == ' ')
|
|
|
+ continue;
|
|
|
+ if (country_code == 'J')
|
|
|
+ genesis_japanese = 1;
|
|
|
+ if (genesis_japanese || country_code == 'U' || country_code == '4')
|
|
|
+ genesis_tv_standard = 0; // Japan, the U.S.A. and Brazil ('4') use NTSC
|
|
|
+ strcat (country, NULL_TO_UNKNOWN_S
|
|
|
+ (genesis_country[MIN (country_code, GENESIS_COUNTRY_MAX - 1)]));
|
|
|
+ strcat (country, ", ");
|
|
|
+ }
|
|
|
+ x = strlen (country);
|
|
|
+ if (x >= 2 && country[x - 2] == ',' && country[x - 1] == ' ')
|
|
|
+ country[x - 2] = 0;
|
|
|
+ rominfo->country = country;
|
|
|
+
|
|
|
+ // misc stuff
|
|
|
+ memcpy (name, &OFFSET (genesis_header, 32), GENESIS_NAME_LEN);
|
|
|
+ name[GENESIS_NAME_LEN] = 0;
|
|
|
+ sprintf ((char *) buf, "Japanese game name: %s\n", name);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "Date: %.8s\n", &OFFSET (genesis_header, 24));
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ x = (OFFSET (genesis_header, 160) << 24) +
|
|
|
+ (OFFSET (genesis_header, 161) << 16) +
|
|
|
+ (OFFSET (genesis_header, 162) << 8) +
|
|
|
+ OFFSET (genesis_header, 163);
|
|
|
+ y = (OFFSET (genesis_header, 164) << 24) +
|
|
|
+ (OFFSET (genesis_header, 165) << 16) +
|
|
|
+ (OFFSET (genesis_header, 166) << 8) +
|
|
|
+ OFFSET (genesis_header, 167);
|
|
|
+ sprintf ((char *) buf, "Internal size: %.4f Mb\n", (float) (y - x + 1) / MBIT);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "ROM start: %08x\n", x);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "ROM end: %08x\n", y);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ genesis_has_ram = OFFSET (genesis_header, 176) == 'R' &&
|
|
|
+ OFFSET (genesis_header, 177) == 'A';
|
|
|
+ if (genesis_has_ram)
|
|
|
+ {
|
|
|
+ x = (OFFSET (genesis_header, 180) << 24) +
|
|
|
+ (OFFSET (genesis_header, 181) << 16) +
|
|
|
+ (OFFSET (genesis_header, 182) << 8) +
|
|
|
+ OFFSET (genesis_header, 183);
|
|
|
+ y = (OFFSET (genesis_header, 184) << 24) +
|
|
|
+ (OFFSET (genesis_header, 185) << 16) +
|
|
|
+ (OFFSET (genesis_header, 186) << 8) +
|
|
|
+ OFFSET (genesis_header, 187);
|
|
|
+ sprintf ((char *) buf, "Cartridge RAM: Yes, %d kBytes (%s)\n",
|
|
|
+ (y - x + 1) >> 10,
|
|
|
+ OFFSET (genesis_header, 178) & 0x40 ? "backup" : "non-backup");
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "RAM start: %08x\n", x);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "RAM end: %08x\n", y);
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ strcat (rominfo->misc, "Cartridge RAM: No\n");
|
|
|
+
|
|
|
+ /*
|
|
|
+ Only checking for 'G' seems to give better results than checking for "GM".
|
|
|
+ "Officially" "GM" indicates it's a game and "Al" that it's educational.
|
|
|
+ */
|
|
|
+ sprintf ((char *) buf, "Product type: %s\n",
|
|
|
+ (OFFSET (genesis_header, 128) == 'G') ? "Game" : "Educational");
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "I/O device(s): %s",
|
|
|
+ NULL_TO_UNKNOWN_S (genesis_io[MIN ((int) OFFSET (genesis_header, 144), GENESIS_IO_MAX - 1)]));
|
|
|
+ for (x = 0; x < 3; x++)
|
|
|
+ {
|
|
|
+ const char *io_device = genesis_io[MIN (OFFSET (genesis_header, 145 + x), GENESIS_IO_MAX - 1)];
|
|
|
+ if (!io_device)
|
|
|
+ continue;
|
|
|
+ strcat ((char *) buf, ", ");
|
|
|
+ strcat ((char *) buf, io_device);
|
|
|
+ }
|
|
|
+ strcat ((char *) buf, "\n");
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "Modem data: %.10s\n", &OFFSET (genesis_header, 188));
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "Memo: %.40s\n", &OFFSET (genesis_header, 200));
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "Product code: %.8s\n", &OFFSET (genesis_header, 131));
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ sprintf ((char *) buf, "Version: 1.%c%c", OFFSET (genesis_header, 140), OFFSET (genesis_header, 141));
|
|
|
+ strcat (rominfo->misc, (char *) buf);
|
|
|
+
|
|
|
+ // internal ROM crc
|
|
|
+ if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
|
|
|
+ {
|
|
|
+ if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ rominfo->has_internal_crc = 1;
|
|
|
+ rominfo->internal_crc_len = 2;
|
|
|
+
|
|
|
+ rominfo->current_internal_crc = genesis_chksum (rom_buffer);
|
|
|
+ rominfo->internal_crc = OFFSET (genesis_header, 143); // low byte of checksum
|
|
|
+ rominfo->internal_crc += (OFFSET (genesis_header, 142)) << 8; // high byte of checksum
|
|
|
+
|
|
|
+ rominfo->internal_crc2[0] = 0;
|
|
|
+ free (rom_buffer);
|
|
|
+ }
|
|
|
+ rominfo->console_usage = genesis_usage[0].help;
|
|
|
+ if (type == SMD)
|
|
|
+ rominfo->copier_usage = smd_usage[0].help;
|
|
|
+ else if (type == MGD_GEN)
|
|
|
+ rominfo->copier_usage = mgd_usage[0].help;
|
|
|
+ else // type == BIN
|
|
|
+ rominfo->copier_usage = bin_usage[0].help;
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+genesis_chksum (unsigned char *rom_buffer)
|
|
|
+{
|
|
|
+ int i, len = genesis_rom_size - 2;
|
|
|
+ unsigned short checksum = 0;
|
|
|
+
|
|
|
+ for (i = 512; i <= len; i += 2)
|
|
|
+ checksum += (rom_buffer[i] << 8) + (rom_buffer[i + 1]);
|
|
|
+
|
|
|
+ return checksum;
|
|
|
+}
|