744 lines
20 KiB
C
744 lines
20 KiB
C
/*
|
|
n64.c - Nintendo 64 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.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "misc/chksum.h"
|
|
#include "misc/file.h"
|
|
#include "misc/misc.h"
|
|
#ifdef USE_ZLIB
|
|
#include "misc/archive.h"
|
|
#endif
|
|
#include "misc/getopt2.h" // st_getopt2_t
|
|
#include "ucon64.h"
|
|
#include "ucon64_misc.h"
|
|
#include "n64.h"
|
|
#include "patch/ips.h"
|
|
#include "patch/aps.h"
|
|
#include "backup/doctor64.h"
|
|
#include "backup/doctor64jr.h"
|
|
#include "backup/cd64.h"
|
|
#include "backup/dex.h"
|
|
#include "backup/z64.h"
|
|
|
|
|
|
#define N64_HEADER_LEN (sizeof (st_n64_header_t))
|
|
#define N64_SRAM_SIZE 512
|
|
#define N64_NAME_LEN 20
|
|
#define N64_BC_SIZE (0x1000 - N64_HEADER_LEN)
|
|
#define LAC_ROM_SIZE 1310720
|
|
|
|
const st_getopt2_t n64_usage[] =
|
|
{
|
|
{
|
|
NULL, 0, 0, 0,
|
|
NULL, "Nintendo 64"/*"1996 Nintendo http://www.nintendo.com"*/,
|
|
NULL
|
|
},
|
|
{
|
|
"n64", 0, 0, UCON64_N64,
|
|
NULL, "force recognition",
|
|
&ucon64_wf[WF_OBJ_N64_SWITCH]
|
|
},
|
|
{
|
|
"int", 0, 0, UCON64_INT,
|
|
NULL, "force ROM is in interleaved format (2143, V64)",
|
|
&ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
},
|
|
{
|
|
"nint", 0, 0, UCON64_NINT,
|
|
NULL, "force ROM is not in interleaved format (1234, Z64)",
|
|
&ucon64_wf[WF_OBJ_ALL_SWITCH]
|
|
},
|
|
{
|
|
"n", 1, 0, UCON64_N,
|
|
"NEW_NAME", "change internal ROM name to NEW_NAME",
|
|
&ucon64_wf[WF_OBJ_ALL_DEFAULT]
|
|
},
|
|
{
|
|
"v64", 0, 0, UCON64_V64,
|
|
NULL, "convert to Doctor V64 (and compatibles/interleaved)",
|
|
&ucon64_wf[WF_OBJ_N64_DEFAULT]
|
|
},
|
|
{
|
|
"z64", 0, 0, UCON64_Z64,
|
|
NULL, "convert to Mr. Backup Z64 (not interleaved)",
|
|
&ucon64_wf[WF_OBJ_N64_DEFAULT]
|
|
},
|
|
{
|
|
"dint", 0, 0, UCON64_DINT,
|
|
NULL, "convert ROM to (non-)interleaved format (1234 <-> 2143)",
|
|
&ucon64_wf[WF_OBJ_ALL_INIT_PROBE]
|
|
},
|
|
{
|
|
"swap", 0, 0, UCON64_SWAP,
|
|
NULL, "same as " OPTION_LONG_S "dint, byte-swap ROM",
|
|
&ucon64_wf[WF_OBJ_ALL_INIT_PROBE]
|
|
},
|
|
{
|
|
"swap2", 0, 0, UCON64_SWAP2,
|
|
NULL, "word-swap ROM (1234 <-> 3412)",
|
|
NULL
|
|
},
|
|
#if 0
|
|
{
|
|
"f", 0, 0, UCON64_F,
|
|
NULL, "remove NTSC/PAL protection",
|
|
NULL
|
|
},
|
|
#endif
|
|
{
|
|
"bot", 1, 0, UCON64_BOT,
|
|
"BOOTCODE", "replace/extract BOOTCODE (4032 Bytes) in/from ROM;\n"
|
|
"extracts automatically if BOOTCODE does not exist",
|
|
&ucon64_wf[WF_OBJ_N64_DEFAULT]
|
|
},
|
|
{
|
|
"lsram", 1, 0, UCON64_LSRAM,
|
|
"SRAM", "LaC's SRAM upload tool; ROM should be LaC's ROM image\n"
|
|
"the SRAM must have a size of 512 Bytes\n"
|
|
"this option generates a ROM which can be used to transfer\n"
|
|
"SRAMs to your cartridge's SRAM (EEPROM)",
|
|
&ucon64_wf[WF_OBJ_N64_INIT_PROBE]
|
|
},
|
|
{
|
|
"usms", 1, 0, UCON64_USMS,
|
|
"SMSROM", "Jos Kwanten's UltraSMS (Sega Master System/Game Gear emulator);\n"
|
|
"ROM should be Jos Kwanten's UltraSMS ROM image\n"
|
|
"works only for SMS ROMs which are <= 4 Mb in size",
|
|
&ucon64_wf[WF_OBJ_N64_DEFAULT]
|
|
},
|
|
{
|
|
"chk", 0, 0, UCON64_CHK,
|
|
NULL, "fix ROM checksum\n"
|
|
"supports only 6102 and 6105 boot codes",
|
|
&ucon64_wf[WF_OBJ_ALL_DEFAULT]
|
|
},
|
|
#if 0
|
|
{
|
|
"bios", 1, 0, UCON64_BIOS,
|
|
"BIOS", "enable backup in Doctor V64 BIOS",
|
|
NULL
|
|
},
|
|
#endif
|
|
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
|
};
|
|
|
|
|
|
typedef struct st_n64_header
|
|
{
|
|
unsigned char pad[64];
|
|
#if 0
|
|
unsigned char validation[2];
|
|
unsigned char compression;
|
|
unsigned char pad1;
|
|
unsigned long clockrate;
|
|
unsigned long programcounter;
|
|
unsigned long release;
|
|
unsigned long crc1;
|
|
unsigned long crc2;
|
|
unsigned char pad2[8];
|
|
unsigned char name[20];
|
|
unsigned char pad3[7];
|
|
unsigned char maker;
|
|
unsigned char cartridgeid[2];
|
|
unsigned char countrycode;
|
|
unsigned char pad4;
|
|
#endif
|
|
} st_n64_header_t;
|
|
|
|
st_n64_header_t n64_header;
|
|
|
|
typedef struct st_n64_chksum
|
|
{
|
|
unsigned int crc1;
|
|
unsigned int crc2;
|
|
} st_n64_chksum_t;
|
|
|
|
static st_n64_chksum_t n64crc;
|
|
static int n64_chksum (st_rominfo_t *rominfo, const char *filename);
|
|
|
|
|
|
int
|
|
n64_v64 (st_rominfo_t *rominfo)
|
|
{
|
|
char dest_name[FILENAME_MAX];
|
|
|
|
if (rominfo->interleaved)
|
|
{
|
|
fprintf (stderr, "ERROR: Already in V64 format\n");
|
|
exit (1);
|
|
}
|
|
|
|
strcpy (dest_name, ucon64.rom);
|
|
set_suffix (dest_name, ".v64");
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fbswap16 (dest_name, 0, ucon64.file_size);
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_z64 (st_rominfo_t *rominfo)
|
|
{
|
|
char dest_name[FILENAME_MAX];
|
|
|
|
if (!rominfo->interleaved)
|
|
{
|
|
fprintf (stderr, "ERROR: Already in Z64 format\n");
|
|
exit (1);
|
|
}
|
|
|
|
strcpy (dest_name, ucon64.rom);
|
|
set_suffix (dest_name, ".z64");
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fbswap16 (dest_name, 0, ucon64.file_size);
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_n (st_rominfo_t *rominfo, const char *name)
|
|
{
|
|
char buf[N64_NAME_LEN], dest_name[FILENAME_MAX];
|
|
|
|
memset (buf, ' ', N64_NAME_LEN);
|
|
strncpy (buf, name, strlen (name) > N64_NAME_LEN ? N64_NAME_LEN : strlen (name));
|
|
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (buf, N64_NAME_LEN);
|
|
|
|
strcpy (dest_name, ucon64.rom);
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fwrite (buf, rominfo->buheader_len + 32, N64_NAME_LEN, dest_name, "r+b");
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_f (st_rominfo_t *rominfo)
|
|
{
|
|
// TODO: PAL/NTSC fix
|
|
(void) rominfo; // warning remover
|
|
fputs ("ERROR: The function for cracking N64 region protections is not yet implemented\n", stderr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
n64_update_chksum (st_rominfo_t *rominfo, const char *filename, char *buf)
|
|
{
|
|
uint64_t crc;
|
|
int x;
|
|
|
|
// n64crc is set by n64_chksum() when called from n64_init()
|
|
crc = (((uint64_t) n64crc.crc1) << 32) | n64crc.crc2;
|
|
for (x = 0; x < 8; x++)
|
|
{
|
|
buf[x] = (char) (crc >> 56);
|
|
crc <<= 8;
|
|
}
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (buf, 8);
|
|
ucon64_fwrite (buf, rominfo->buheader_len + 16, 8, filename, "r+b");
|
|
}
|
|
|
|
|
|
int
|
|
n64_chk (st_rominfo_t *rominfo)
|
|
{
|
|
char buf[8], dest_name[FILENAME_MAX];
|
|
|
|
strcpy (dest_name, ucon64.rom);
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
|
|
n64_update_chksum (rominfo, dest_name, buf);
|
|
dumper (stdout, buf, 8, rominfo->buheader_len + 16, DUMPER_HEX);
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_sram (st_rominfo_t *rominfo, const char *sramfile)
|
|
// Function to insert an SRAM file in LaC's SRAM upload tool (which is an N64
|
|
// program)
|
|
{
|
|
char sram[N64_SRAM_SIZE], dest_name[FILENAME_MAX], buf[8];
|
|
|
|
if (access (sramfile, F_OK))
|
|
{
|
|
fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], sramfile);
|
|
exit (1);
|
|
}
|
|
|
|
if (fsizeof (sramfile) != N64_SRAM_SIZE || ucon64.file_size != LAC_ROM_SIZE)
|
|
{
|
|
fprintf (stderr, "ERROR: ROM is not %d bytes and/or SRAM is not %d bytes\n",
|
|
LAC_ROM_SIZE, N64_SRAM_SIZE);
|
|
exit (1);
|
|
}
|
|
|
|
ucon64_fread (sram, 0, N64_SRAM_SIZE, sramfile);
|
|
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (sram, N64_SRAM_SIZE);
|
|
|
|
strcpy (dest_name, ucon64.rom);
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fwrite (sram, 0x286c0, N64_SRAM_SIZE, dest_name, "r+b");
|
|
n64_chksum (rominfo, dest_name); // calculate the checksum of the modified file
|
|
n64_update_chksum (rominfo, dest_name, buf);
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_bot (st_rominfo_t *rominfo, const char *bootfile)
|
|
{
|
|
char buf[N64_BC_SIZE], dest_name[FILENAME_MAX];
|
|
|
|
if (!access (bootfile, F_OK))
|
|
{
|
|
strcpy (dest_name, ucon64.rom);
|
|
ucon64_fread (buf, 0, N64_BC_SIZE, bootfile);
|
|
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (buf, N64_BC_SIZE);
|
|
|
|
ucon64_file_handler (dest_name, NULL, 0);
|
|
fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fwrite (buf, rominfo->buheader_len + N64_HEADER_LEN, N64_BC_SIZE,
|
|
dest_name, "r+b");
|
|
}
|
|
else
|
|
{
|
|
strcpy (dest_name, bootfile);
|
|
// set_suffix (dest_name, ".bot");
|
|
ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME | OF_FORCE_SUFFIX);
|
|
fcopy (ucon64.rom, rominfo->buheader_len + N64_HEADER_LEN, N64_BC_SIZE,
|
|
dest_name, "wb");
|
|
|
|
if (rominfo->interleaved)
|
|
ucon64_fbswap16 (dest_name, 0, fsizeof (dest_name));
|
|
}
|
|
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_usms (st_rominfo_t *rominfo, const char *smsrom)
|
|
{
|
|
char dest_name[FILENAME_MAX], *usmsbuf;
|
|
int size;
|
|
|
|
if (access (smsrom, F_OK))
|
|
{
|
|
fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], smsrom);
|
|
exit (1);
|
|
}
|
|
|
|
size = fsizeof (smsrom);
|
|
// must be smaller than 4 Mbit, 524288 bytes will be inserted
|
|
// from 0x1b410 to 0x9b40f (0x7ffff)
|
|
if (size > 4 * MBIT)
|
|
{
|
|
fprintf (stderr, "ERROR: The Sega Master System/Game Gear ROM must be 524288 bytes or less\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (!(usmsbuf = (char *) malloc (4 * MBIT)))
|
|
{
|
|
fprintf (stderr, ucon64_msg[BUFFER_ERROR], 4 * MBIT);
|
|
exit (1);
|
|
}
|
|
memset (usmsbuf, 0xff, 4 * MBIT);
|
|
ucon64_fread (usmsbuf, 0, size, smsrom);
|
|
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (usmsbuf, size);
|
|
|
|
// Jos Kwanten's rominserter.exe produces a file named Patched.v64
|
|
strcpy (dest_name, "Patched.v64");
|
|
ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME | OF_FORCE_SUFFIX);
|
|
fcopy (ucon64.rom, rominfo->buheader_len, ucon64.file_size, dest_name, "wb");
|
|
ucon64_fwrite (usmsbuf, rominfo->buheader_len + 0x01b410, 4 * MBIT, dest_name, "r+b");
|
|
|
|
free (usmsbuf);
|
|
printf (ucon64_msg[WROTE], dest_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
n64_init (st_rominfo_t *rominfo)
|
|
{
|
|
int result = -1, x;
|
|
unsigned int value = 0;
|
|
#define N64_MAKER_MAX 0x50
|
|
const char *n64_maker[N64_MAKER_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, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, "Nintendo", NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, "Nintendo", NULL
|
|
},
|
|
#define N64_COUNTRY_MAX 0x5a
|
|
*n64_country[N64_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, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, "Germany", "U.S.A.",
|
|
"France", NULL, NULL, "Italy", "Japan",
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
"Europe", NULL, NULL, "Spain", NULL,
|
|
"Australia", NULL, NULL, "France, Germany, The Netherlands", NULL // Holland is an incorrect name for The Netherlands
|
|
};
|
|
|
|
rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0;
|
|
|
|
ucon64_fread (&n64_header, rominfo->buheader_len, N64_HEADER_LEN, ucon64.rom);
|
|
|
|
value = OFFSET (n64_header, 0);
|
|
value += OFFSET (n64_header, 1) << 8;
|
|
value += OFFSET (n64_header, 2) << 16;
|
|
value += OFFSET (n64_header, 3) << 24;
|
|
/*
|
|
0x41123780 and 0x12418037 can be found in te following files:
|
|
2 Blokes & An Armchair - Nintendo 64 Remix Remix (PD)
|
|
Zelda Boot Emu V1 (PD)
|
|
Zelda Boot Emu V2 (PD)
|
|
*/
|
|
if (value == 0x40123780 || value == 0x41123780) // 0x80371240, 0x80371241
|
|
{
|
|
rominfo->interleaved = 0;
|
|
result = 0;
|
|
}
|
|
else if (value == 0x12408037 || value == 0x12418037) // 0x37804012, 0x37804112
|
|
{
|
|
rominfo->interleaved = 1;
|
|
result = 0;
|
|
}
|
|
else
|
|
result = -1;
|
|
|
|
if (UCON64_ISSET (ucon64.interleaved))
|
|
rominfo->interleaved = ucon64.interleaved;
|
|
if (ucon64.console == UCON64_N64)
|
|
result = 0;
|
|
|
|
// internal ROM header
|
|
rominfo->header_start = 0;
|
|
rominfo->header_len = N64_HEADER_LEN;
|
|
rominfo->header = &n64_header;
|
|
|
|
// internal ROM name
|
|
strncpy (rominfo->name, (char *) &OFFSET (n64_header, 32), N64_NAME_LEN);
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (rominfo->name, N64_NAME_LEN);
|
|
rominfo->name[N64_NAME_LEN] = 0;
|
|
|
|
// ROM maker
|
|
rominfo->maker = NULL_TO_UNKNOWN_S (n64_maker[MIN (OFFSET
|
|
(n64_header, 59 ^ rominfo->interleaved), N64_MAKER_MAX - 1)]);
|
|
|
|
// ROM country
|
|
rominfo->country = NULL_TO_UNKNOWN_S (n64_country[MIN (OFFSET
|
|
(n64_header, 63 ^ (!rominfo->interleaved)), N64_COUNTRY_MAX - 1)]);
|
|
|
|
// CRC stuff
|
|
if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
|
|
{
|
|
rominfo->has_internal_crc = 1;
|
|
rominfo->internal_crc_len = 4;
|
|
|
|
n64_chksum (rominfo, ucon64.rom);
|
|
rominfo->current_internal_crc = n64crc.crc1;
|
|
|
|
value = 0;
|
|
for (x = 0; x < 4; x++)
|
|
{
|
|
rominfo->internal_crc <<= 8;
|
|
rominfo->internal_crc += OFFSET (n64_header, 16 + (x ^ rominfo->interleaved));
|
|
value <<= 8;
|
|
value += OFFSET (n64_header, 20 + (x ^ rominfo->interleaved));
|
|
}
|
|
|
|
sprintf (rominfo->internal_crc2,
|
|
"2nd Checksum: %s, 0x%08x (calculated) %c= 0x%08x (internal)%s",
|
|
#ifdef USE_ANSI_COLOR
|
|
ucon64.ansi_color ?
|
|
((n64crc.crc2 == value) ?
|
|
"\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m")
|
|
:
|
|
((n64crc.crc2 == value) ? "Ok" : "Bad"),
|
|
#else
|
|
(n64crc.crc2 == value) ? "Ok" : "Bad",
|
|
#endif
|
|
n64crc.crc2,
|
|
(n64crc.crc2 == value) ? '=' : '!', value,
|
|
(n64crc.crc2 != value) ?
|
|
"\nNOTE: The checksum routine supports only 6102 and 6105 boot codes" :
|
|
"");
|
|
}
|
|
|
|
rominfo->console_usage = n64_usage[0].help;
|
|
rominfo->copier_usage = (!rominfo->buheader_len ?
|
|
((!rominfo->interleaved) ? z64_usage[0].help : doctor64_usage[0].help) : unknown_usage[0].help);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
ROM check sum routine is based on chksum64 V1.2 by Andreas Sterbenz
|
|
<stan@sbox.tu-graz.ac.at>, a program to calculate the ROM checksum of
|
|
Nintendo 64 ROMs.
|
|
*/
|
|
#define ROL(i, b) (((i) << (b)) | ((i) >> (32 - (b))))
|
|
#define BYTES2LONG(b, s) ( (b)[0^(s)] << 24 | \
|
|
(b)[1^(s)] << 16 | \
|
|
(b)[2^(s)] << 8 | \
|
|
(b)[3^(s)] )
|
|
|
|
#define CHECKSUM_START 0x1000 //(N64_HEADER_LEN + N64_BC_SIZE)
|
|
#define CHECKSUM_LENGTH 0x100000
|
|
#define CHECKSUM_STARTVALUE1 0xf8ca4ddc
|
|
#define CHECKSUM_STARTVALUE2 0xdf26f436
|
|
#define CALC_CRC32 // see this as a marker, don't disable
|
|
|
|
int
|
|
n64_chksum (st_rominfo_t *rominfo, const char *filename)
|
|
{
|
|
unsigned char bootcode_buf[CHECKSUM_START], chunk[MAXBUFSIZE & ~3]; // size must be a multiple of 4
|
|
unsigned int i, c1, k1, k2, t1, t2, t3, t4, t5, t6, clen = CHECKSUM_LENGTH,
|
|
rlen = (ucon64.file_size - rominfo->buheader_len) - CHECKSUM_START,
|
|
n = 0, bootcode; // using ucon64.file_size is ok for n64_init() & n64_sram()
|
|
FILE *file;
|
|
#ifdef CALC_CRC32
|
|
unsigned int scrc32 = 0, fcrc32 = 0; // search CRC32 & file CRC32
|
|
unsigned char *crc32_mem;
|
|
#endif
|
|
|
|
if (rlen < CHECKSUM_START + CHECKSUM_LENGTH)
|
|
{
|
|
#ifdef CALC_CRC32
|
|
n = ucon64.file_size - rominfo->buheader_len;
|
|
if ((crc32_mem = (unsigned char *) malloc (n)) == NULL)
|
|
{
|
|
fprintf (stderr, ucon64_msg[BUFFER_ERROR], n);
|
|
return -1;
|
|
}
|
|
ucon64_fread (crc32_mem, rominfo->buheader_len, n, filename);
|
|
if (!rominfo->interleaved)
|
|
{
|
|
ucon64.fcrc32 = crc32 (0, crc32_mem, n);
|
|
ucon64_bswap16_n (crc32_mem, n);
|
|
}
|
|
ucon64.crc32 = crc32 (0, crc32_mem, n);
|
|
free (crc32_mem);
|
|
#endif
|
|
return -1; // ROM is too small
|
|
}
|
|
|
|
if (!(file = fopen (filename, "rb")))
|
|
return -1;
|
|
|
|
#ifdef CALC_CRC32
|
|
if (!rominfo->interleaved)
|
|
{
|
|
if ((crc32_mem = (unsigned char *) malloc (MAXBUFSIZE)) == NULL)
|
|
{
|
|
fprintf (stderr, ucon64_msg[BUFFER_ERROR], MAXBUFSIZE);
|
|
fclose (file);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
crc32_mem = chunk;
|
|
|
|
fseek (file, rominfo->buheader_len, SEEK_SET);
|
|
fread (crc32_mem, 1, CHECKSUM_START, file);
|
|
memcpy (bootcode_buf, crc32_mem + N64_HEADER_LEN, N64_BC_SIZE);
|
|
if (!rominfo->interleaved)
|
|
{
|
|
fcrc32 = crc32 (0, crc32_mem, CHECKSUM_START);
|
|
ucon64_bswap16_n (crc32_mem, CHECKSUM_START);
|
|
}
|
|
else
|
|
ucon64_bswap16_n (bootcode_buf, N64_BC_SIZE);
|
|
scrc32 = crc32 (0, crc32_mem, CHECKSUM_START);
|
|
#else
|
|
fseek (file, rominfo->buheader_len + N64_HEADER_LEN, SEEK_SET);
|
|
fread (bootcode_buf, 1, N64_BC_SIZE, file);
|
|
if (rominfo->interleaved)
|
|
ucon64_bswap16_n (bootcode_buf, N64_BC_SIZE);
|
|
#endif
|
|
if (crc32 (0, bootcode_buf, N64_BC_SIZE) == 0x98bc2c86)
|
|
{
|
|
bootcode = 6105;
|
|
i = CHECKSUM_STARTVALUE2;
|
|
}
|
|
else
|
|
{
|
|
bootcode = 0; // everything else
|
|
i = CHECKSUM_STARTVALUE1;
|
|
}
|
|
|
|
t1 = i;
|
|
t2 = i;
|
|
t3 = i;
|
|
t4 = i;
|
|
t5 = i;
|
|
t6 = i;
|
|
|
|
while (1)
|
|
{
|
|
if (rlen > 0)
|
|
{
|
|
if ((n = fread (chunk, 1, MIN (sizeof (chunk), clen), file)))
|
|
{
|
|
#ifdef CALC_CRC32
|
|
if (!rominfo->interleaved)
|
|
{
|
|
memcpy (crc32_mem, chunk, n);
|
|
fcrc32 = crc32 (fcrc32, crc32_mem, n);
|
|
ucon64_bswap16_n (crc32_mem, n);
|
|
}
|
|
scrc32 = crc32 (scrc32, crc32_mem, n);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
n = MIN (sizeof (chunk), clen);
|
|
|
|
n &= ~3;
|
|
if (n == 0)
|
|
break;
|
|
for (i = 0; i < n; i += 4)
|
|
{
|
|
c1 = BYTES2LONG (&chunk[i], rominfo->interleaved);
|
|
k1 = t6 + c1;
|
|
if (k1 < t6)
|
|
t4++;
|
|
t6 = k1;
|
|
t3 ^= c1;
|
|
k2 = c1 & 0x1f;
|
|
k1 = ROL (c1, k2);
|
|
t5 += k1;
|
|
if (c1 < t2)
|
|
t2 ^= k1;
|
|
else
|
|
t2 ^= t6 ^ c1;
|
|
|
|
if (bootcode == 6105)
|
|
{
|
|
k1 = 0x710 + (i & 0xff);
|
|
//t1 += BYTES2LONG (&bootcode_buf[k1], 0) ^ c1;
|
|
t1 += ((bootcode_buf[k1] << 24) | (bootcode_buf[k1 + 1] << 16) |
|
|
(bootcode_buf[k1 + 2] << 8) | (bootcode_buf[k1 + 3])) ^ c1;
|
|
}
|
|
else
|
|
t1 += c1 ^ t5;
|
|
}
|
|
if (rlen > 0)
|
|
{
|
|
rlen -= n;
|
|
if (rlen <= 0)
|
|
memset (chunk, 0, sizeof (chunk));
|
|
}
|
|
clen -= n;
|
|
}
|
|
n64crc.crc1 = t6 ^ t4 ^ t3;
|
|
n64crc.crc2 = t5 ^ t2 ^ t1;
|
|
|
|
#ifdef CALC_CRC32
|
|
if (!rominfo->interleaved)
|
|
{
|
|
free (crc32_mem);
|
|
crc32_mem = chunk;
|
|
}
|
|
while ((n = fread (crc32_mem, 1, sizeof (chunk), file)))
|
|
{
|
|
if (!rominfo->interleaved)
|
|
{
|
|
fcrc32 = crc32 (fcrc32, crc32_mem, n);
|
|
ucon64_bswap16_n (crc32_mem, n);
|
|
}
|
|
scrc32 = crc32 (scrc32, crc32_mem, n);
|
|
}
|
|
|
|
ucon64.crc32 = scrc32;
|
|
if (!rominfo->interleaved)
|
|
ucon64.fcrc32 = fcrc32;
|
|
#endif
|
|
|
|
fclose (file);
|
|
return 0;
|
|
}
|