dep: Bump rcheevos to 3af1e2fc5188d6e932ee379942f4049ea877e648
This commit is contained in:
@@ -22,6 +22,7 @@ struct cdrom_t
|
||||
void* file_handle; /* the file handle for reading the track data */
|
||||
int sector_size; /* the size of each sector in the track data */
|
||||
int sector_header_size; /* the offset to the raw data within a sector block */
|
||||
int raw_data_size; /* the amount of raw data within a sector block */
|
||||
int64_t file_track_offset;/* the offset of the track data within the file */
|
||||
int track_first_sector; /* the first absolute sector associated to the track (includes pregap) */
|
||||
int track_pregap_sectors; /* the number of pregap sectors */
|
||||
@@ -58,6 +59,7 @@ static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
|
||||
|
||||
cdrom->sector_size = 0;
|
||||
cdrom->sector_header_size = 0;
|
||||
cdrom->raw_data_size = 2048;
|
||||
|
||||
rc_file_seek(cdrom->file_handle, toc_sector * 2352 + cdrom->file_track_offset, SEEK_SET);
|
||||
if (rc_file_read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
|
||||
@@ -122,6 +124,8 @@ static void* cdreader_open_bin_track(const char* path, uint32_t track)
|
||||
return NULL;
|
||||
|
||||
cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
|
||||
if (!cdrom)
|
||||
return NULL;
|
||||
cdrom->file_handle = file_handle;
|
||||
#ifndef NDEBUG
|
||||
cdrom->track_id = track;
|
||||
@@ -219,6 +223,7 @@ static int cdreader_open_bin(struct cdrom_t* cdrom, const char* path, const char
|
||||
{
|
||||
cdrom->sector_size = 2352;
|
||||
cdrom->sector_header_size = 0;
|
||||
cdrom->raw_data_size = 2352; /* no header or footer data on audio tracks */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +287,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
char* bin_filename = NULL;
|
||||
char *ptr, *ptr2, *end;
|
||||
int done = 0;
|
||||
int session = 1;
|
||||
size_t num_read = 0;
|
||||
struct cdrom_t* cdrom = NULL;
|
||||
|
||||
@@ -339,7 +345,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
++ptr;
|
||||
|
||||
/* convert mm:ss:ff to sector count */
|
||||
sscanf(ptr, "%d:%d:%d", &m, &s, &f);
|
||||
sscanf_s(ptr, "%d:%d:%d", &m, &s, &f);
|
||||
sector_offset = ((m * 60) + s) * 75 + f;
|
||||
|
||||
if (current_track.first_sector == -1)
|
||||
@@ -389,6 +395,13 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (track == RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION && session == 2)
|
||||
{
|
||||
track = current_track.id;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncasecmp(ptr, "TRACK ", 6) == 0)
|
||||
@@ -465,6 +478,20 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
if (ptr2 - ptr < (int)sizeof(current_track.filename))
|
||||
memcpy(current_track.filename, ptr, ptr2 - ptr);
|
||||
}
|
||||
else if (strncasecmp(ptr, "REM ", 4) == 0)
|
||||
{
|
||||
ptr += 4;
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
|
||||
if (strncasecmp(ptr, "SESSION ", 8) == 0)
|
||||
{
|
||||
ptr += 8;
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
session = atoi(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
while (*ptr && *ptr != '\n')
|
||||
++ptr;
|
||||
@@ -694,8 +721,8 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||
largest_track_size = track_size;
|
||||
largest_track = current_track;
|
||||
largest_track_lba = lba;
|
||||
strcpy(largest_track_file, file);
|
||||
strcpy(largest_track_sector_size, sector_size);
|
||||
strcpy_s(largest_track_file, sizeof(largest_track_file), file);
|
||||
strcpy_s(largest_track_sector_size, sizeof(largest_track_sector_size), sector_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,8 +750,8 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||
if (largest_track != 0 && largest_track != current_track)
|
||||
{
|
||||
current_track = largest_track;
|
||||
strcpy(file, largest_track_file);
|
||||
strcpy(sector_size, largest_track_sector_size);
|
||||
strcpy_s(file, sizeof(file), largest_track_file);
|
||||
strcpy_s(sector_size, sizeof(sector_size), largest_track_sector_size);
|
||||
lba = largest_track_lba;
|
||||
}
|
||||
|
||||
@@ -794,18 +821,18 @@ static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* bu
|
||||
sector_start = (int64_t)(sector - cdrom->track_first_sector) * cdrom->sector_size +
|
||||
cdrom->sector_header_size + cdrom->file_track_offset;
|
||||
|
||||
while (requested_bytes > 2048)
|
||||
while (requested_bytes > (size_t)cdrom->raw_data_size)
|
||||
{
|
||||
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
|
||||
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, 2048);
|
||||
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, cdrom->raw_data_size);
|
||||
total_read += num_read;
|
||||
|
||||
if (num_read < 2048)
|
||||
if (num_read < (size_t)cdrom->raw_data_size)
|
||||
return total_read;
|
||||
|
||||
buffer_ptr += 2048;
|
||||
buffer_ptr += cdrom->raw_data_size;
|
||||
sector_start += cdrom->sector_size;
|
||||
requested_bytes -= 2048;
|
||||
requested_bytes -= cdrom->raw_data_size;
|
||||
}
|
||||
|
||||
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
|
||||
@@ -844,7 +871,7 @@ void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader)
|
||||
cdreader->first_track_sector = cdreader_first_track_sector;
|
||||
}
|
||||
|
||||
void rc_hash_init_default_cdreader()
|
||||
void rc_hash_init_default_cdreader(void)
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
rc_hash_get_default_cdreader(&cdreader);
|
||||
|
||||
@@ -48,7 +48,13 @@ static struct rc_hash_filereader* filereader = NULL;
|
||||
|
||||
static void* filereader_open(const char* path)
|
||||
{
|
||||
#if defined(__STDC_WANT_SECURE_LIB__)
|
||||
FILE* fp;
|
||||
fopen_s(&fp, path, "rb");
|
||||
return fp;
|
||||
#else
|
||||
return fopen(path, "rb");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void filereader_seek(void* file_handle, int64_t offset, int origin)
|
||||
@@ -223,12 +229,17 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
{
|
||||
uint8_t buffer[2048], *tmp;
|
||||
int sector;
|
||||
unsigned num_sectors = 0;
|
||||
size_t filename_length;
|
||||
const char* slash;
|
||||
|
||||
if (!track_handle)
|
||||
return 0;
|
||||
|
||||
/* we start at the root. don't need to explicitly find it */
|
||||
if (*path == '\\')
|
||||
++path;
|
||||
|
||||
filename_length = strlen(path);
|
||||
slash = strrchr(path, '\\');
|
||||
if (slash)
|
||||
@@ -247,6 +258,8 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned logical_block_size;
|
||||
|
||||
/* find the cd information */
|
||||
if (!rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle) + 16, buffer, 256))
|
||||
return 0;
|
||||
@@ -255,6 +268,15 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
* https://www.cdroller.com/htm/readdata.html
|
||||
*/
|
||||
sector = buffer[156 + 2] | (buffer[156 + 3] << 8) | (buffer[156 + 4] << 16);
|
||||
|
||||
/* if the table of contents spans more than one sector, it's length of section will exceed it's logical block size */
|
||||
logical_block_size = (buffer[128] | (buffer[128 + 1] << 8)); /* logical block size */
|
||||
if (logical_block_size == 0) {
|
||||
num_sectors = 1;
|
||||
} else {
|
||||
num_sectors = (buffer[156 + 10] | (buffer[156 + 11] << 8) | (buffer[156 + 12] << 16) | (buffer[156 + 13] << 24)); /* length of section */
|
||||
num_sectors /= logical_block_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* fetch and process the directory record */
|
||||
@@ -262,21 +284,34 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
return 0;
|
||||
|
||||
tmp = buffer;
|
||||
while (tmp < buffer + sizeof(buffer))
|
||||
do
|
||||
{
|
||||
if (!*tmp)
|
||||
return 0;
|
||||
if (tmp >= buffer + sizeof(buffer) || !*tmp)
|
||||
{
|
||||
/* end of this path table block. if the path table spans multiple sectors, keep scanning */
|
||||
if (num_sectors > 1)
|
||||
{
|
||||
--num_sectors;
|
||||
if (rc_cd_read_sector(track_handle, ++sector, buffer, sizeof(buffer)))
|
||||
{
|
||||
tmp = buffer;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* filename is 33 bytes into the record and the format is "FILENAME;version" or "DIRECTORY" */
|
||||
if ((tmp[33 + filename_length] == ';' || tmp[33 + filename_length] == '\0') &&
|
||||
if ((tmp[32] == filename_length || tmp[33 + filename_length] == ';') &&
|
||||
strncasecmp((const char*)(tmp + 33), path, filename_length) == 0)
|
||||
{
|
||||
sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)buffer, sizeof(buffer), "Found %s at sector %d", path, sector);
|
||||
verbose_message_callback((const char*)buffer);
|
||||
char message[128];
|
||||
snprintf(message, sizeof(message), "Found %s at sector %d", path, sector);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
|
||||
if (size)
|
||||
@@ -287,7 +322,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
|
||||
/* the first byte of the record is the length of the record */
|
||||
tmp += *tmp;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -347,6 +382,34 @@ int rc_path_compare_extension(const char* path, const char* ext)
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
static void rc_hash_byteswap16(uint8_t* buffer, const uint8_t* stop)
|
||||
{
|
||||
uint32_t* ptr = (uint32_t*)buffer;
|
||||
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||
while (ptr < stop32)
|
||||
{
|
||||
uint32_t temp = *ptr;
|
||||
temp = (temp & 0xFF00FF00) >> 8 |
|
||||
(temp & 0x00FF00FF) << 8;
|
||||
*ptr++ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_hash_byteswap32(uint8_t* buffer, const uint8_t* stop)
|
||||
{
|
||||
uint32_t* ptr = (uint32_t*)buffer;
|
||||
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||
while (ptr < stop32)
|
||||
{
|
||||
uint32_t temp = *ptr;
|
||||
temp = (temp & 0xFF000000) >> 24 |
|
||||
(temp & 0x00FF0000) >> 8 |
|
||||
(temp & 0x0000FF00) << 8 |
|
||||
(temp & 0x000000FF) << 24;
|
||||
*ptr++ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_hash_finalize(md5_state_t* md5, char hash[33])
|
||||
{
|
||||
md5_byte_t digest[16];
|
||||
@@ -415,6 +478,9 @@ static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
|
||||
if (size < (unsigned)num_read)
|
||||
size = (unsigned)num_read;
|
||||
|
||||
do
|
||||
{
|
||||
md5_append(md5, buffer, (int)num_read);
|
||||
@@ -684,6 +750,142 @@ static int rc_hash_text(char hash[33], const uint8_t* buffer, size_t buffer_size
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
/* helper variable only used for testing */
|
||||
const char* _rc_hash_jaguar_cd_homebrew_hash = NULL;
|
||||
|
||||
static int rc_hash_jaguar_cd(char hash[33], const char* path)
|
||||
{
|
||||
uint8_t buffer[2352];
|
||||
char message[128];
|
||||
void* track_handle;
|
||||
md5_state_t md5;
|
||||
int byteswapped = 0;
|
||||
unsigned size = 0;
|
||||
unsigned offset = 0;
|
||||
unsigned sector = 0;
|
||||
unsigned remaining;
|
||||
unsigned i;
|
||||
|
||||
/* Jaguar CD header is in the first sector of the first data track OF THE SECOND SESSION.
|
||||
* The first track must be an audio track, but may be a warning message or actual game audio */
|
||||
track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* The header is an unspecified distance into the first sector, but usually two bytes in.
|
||||
* It consists of 64 bytes of "TAIR" or "ATRI" repeating, depending on whether or not the data
|
||||
* is byteswapped. Then another 32 byte that reads "ATARI APPROVED DATA HEADER ATRI "
|
||||
* (possibly byteswapped). Then a big-endian 32-bit value for the address where the boot code
|
||||
* should be loaded, and a second big-endian 32-bit value for the size of the boot code. */
|
||||
sector = rc_cd_first_track_sector(track_handle);
|
||||
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
|
||||
|
||||
for (i = 64; i < sizeof(buffer) - 32 - 4 * 3; i++)
|
||||
{
|
||||
if (memcmp(&buffer[i], "TARA IPARPVODED TA AEHDAREA RT I", 32) == 0)
|
||||
{
|
||||
byteswapped = 1;
|
||||
offset = i + 32 + 4;
|
||||
size = (buffer[offset] << 16) | (buffer[offset + 1] << 24) | (buffer[offset + 2]) | (buffer[offset + 3] << 8);
|
||||
break;
|
||||
}
|
||||
else if (memcmp(&buffer[i], "ATARI APPROVED DATA HEADER ATRI ", 32) == 0)
|
||||
{
|
||||
byteswapped = 0;
|
||||
offset = i + 32 + 4;
|
||||
size = (buffer[offset] << 24) | (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | (buffer[offset + 3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) /* did not see ATARI APPROVED DATA HEADER */
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Not a Jaguar CD");
|
||||
}
|
||||
|
||||
i = 0; /* only loop once */
|
||||
do
|
||||
{
|
||||
md5_init(&md5);
|
||||
|
||||
offset += 4;
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf(message, sizeof(message), "Hashing boot executable (%u bytes starting at %u bytes into sector %u)", size, offset, sector);
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
if (size > MAX_BUFFER_SIZE)
|
||||
size = MAX_BUFFER_SIZE;
|
||||
|
||||
do
|
||||
{
|
||||
if (byteswapped)
|
||||
rc_hash_byteswap16(buffer, &buffer[sizeof(buffer)]);
|
||||
|
||||
remaining = sizeof(buffer) - offset;
|
||||
if (remaining >= size)
|
||||
{
|
||||
md5_append(&md5, &buffer[offset], size);
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
md5_append(&md5, &buffer[offset], remaining);
|
||||
size -= remaining;
|
||||
offset = 0;
|
||||
} while (rc_cd_read_sector(track_handle, ++sector, buffer, sizeof(buffer)) == sizeof(buffer));
|
||||
|
||||
rc_cd_close_track(track_handle);
|
||||
|
||||
if (size > 0)
|
||||
return rc_hash_error("Not enough data");
|
||||
|
||||
rc_hash_finalize(&md5, hash);
|
||||
|
||||
/* homebrew games all seem to have the same boot executable and store the actual game code in track 2.
|
||||
* if we generated something other than the homebrew hash, return it. assume all homebrews are byteswapped. */
|
||||
if (strcmp(hash, "254487b59ab21bc005338e85cbf9fd2f") != 0 || !byteswapped) {
|
||||
if (_rc_hash_jaguar_cd_homebrew_hash == NULL || strcmp(hash, _rc_hash_jaguar_cd_homebrew_hash) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if we've already been through the loop a second time, just return the hash */
|
||||
if (i == 1)
|
||||
return 1;
|
||||
++i;
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf(message, sizeof(message), "Potential homebrew at sector %u, checking for KART data in track 2", sector);
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
track_handle = rc_cd_open_track(path, 2);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* track 2 of the homebrew code has the 64 bytes or ATRI followed by 32 bytes of "ATARI APPROVED DATA HEADER ATRI!",
|
||||
* then 64 bytes of KART repeating. */
|
||||
sector = rc_cd_first_track_sector(track_handle);
|
||||
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
|
||||
if (memcmp(&buffer[0x5E], "RT!IRTKA", 8) != 0)
|
||||
return rc_hash_error("Homebrew executable not found in track 2");
|
||||
|
||||
/* found KART data*/
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf(message, sizeof(message), "Found KART data in track 2");
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
offset = 0xA6;
|
||||
size = (buffer[offset] << 16) | (buffer[offset + 1] << 24) | (buffer[offset + 2]) | (buffer[offset + 3] << 8);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
/* if the file contains a header, ignore it */
|
||||
@@ -698,6 +900,70 @@ static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size
|
||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||
}
|
||||
|
||||
static int rc_hash_neogeo_cd(char hash[33], const char* path)
|
||||
{
|
||||
char buffer[1024], *ptr;
|
||||
void* track_handle;
|
||||
uint32_t sector;
|
||||
unsigned size;
|
||||
md5_state_t md5;
|
||||
|
||||
track_handle = rc_cd_open_track(path, 1);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* https://wiki.neogeodev.org/index.php?title=IPL_file, https://wiki.neogeodev.org/index.php?title=PRG_file
|
||||
* IPL file specifies data to be loaded before the game starts. PRG files are the executable code
|
||||
*/
|
||||
sector = rc_cd_find_file_sector(track_handle, "IPL.TXT", &size);
|
||||
if (!sector)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Not a NeoGeo CD game disc");
|
||||
}
|
||||
|
||||
if (rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer)) == 0)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
md5_init(&md5);
|
||||
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
ptr = &buffer[0];
|
||||
do
|
||||
{
|
||||
char* start = ptr;
|
||||
while (*ptr && *ptr != '.')
|
||||
++ptr;
|
||||
|
||||
if (strncasecmp(ptr, ".PRG", 4) == 0)
|
||||
{
|
||||
ptr += 4;
|
||||
*ptr++ = '\0';
|
||||
|
||||
sector = rc_cd_find_file_sector(track_handle, start, &size);
|
||||
if (!sector || !rc_hash_cd_file(&md5, track_handle, sector, NULL, size, start))
|
||||
{
|
||||
char error[128];
|
||||
rc_cd_close_track(track_handle);
|
||||
snprintf(error, sizeof(error), "Could not read %.16s", start);
|
||||
return rc_hash_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
while (*ptr && *ptr != '\n')
|
||||
++ptr;
|
||||
if (*ptr != '\n')
|
||||
break;
|
||||
++ptr;
|
||||
} while (*ptr != '\0' && *ptr != '\x1a');
|
||||
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_nes(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
/* if the file contains a header, ignore it */
|
||||
@@ -719,34 +985,6 @@ static int rc_hash_nes(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||
}
|
||||
|
||||
static void rc_hash_v64_to_z64(uint8_t* buffer, const uint8_t* stop)
|
||||
{
|
||||
uint32_t* ptr = (uint32_t*)buffer;
|
||||
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||
while (ptr < stop32)
|
||||
{
|
||||
uint32_t temp = *ptr;
|
||||
temp = (temp & 0xFF00FF00) >> 8 |
|
||||
(temp & 0x00FF00FF) << 8;
|
||||
*ptr++ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_hash_n64_to_z64(uint8_t* buffer, const uint8_t* stop)
|
||||
{
|
||||
uint32_t* ptr = (uint32_t*)buffer;
|
||||
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||
while (ptr < stop32)
|
||||
{
|
||||
uint32_t temp = *ptr;
|
||||
temp = (temp & 0xFF000000) >> 24 |
|
||||
(temp & 0x00FF0000) >> 8 |
|
||||
(temp & 0x0000FF00) << 8 |
|
||||
(temp & 0x000000FF) << 24;
|
||||
*ptr++ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_hash_n64(char hash[33], const char* path)
|
||||
{
|
||||
uint8_t* buffer;
|
||||
@@ -787,6 +1025,9 @@ static int rc_hash_n64(char hash[33], const char* path)
|
||||
rc_hash_verbose("converting n64 to z64");
|
||||
is_n64 = 1;
|
||||
}
|
||||
else if (buffer[0] == 0xE8 || buffer[0] == 0x22) /* ndd format (don't byteswap) */
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
free(buffer);
|
||||
@@ -818,9 +1059,9 @@ static int rc_hash_n64(char hash[33], const char* path)
|
||||
rc_file_read(file_handle, buffer, (int)buffer_size);
|
||||
|
||||
if (is_v64)
|
||||
rc_hash_v64_to_z64(buffer, stop);
|
||||
rc_hash_byteswap16(buffer, stop);
|
||||
else if (is_n64)
|
||||
rc_hash_n64_to_z64(buffer, stop);
|
||||
rc_hash_byteswap32(buffer, stop);
|
||||
|
||||
md5_append(&md5, buffer, (int)buffer_size);
|
||||
remaining -= buffer_size;
|
||||
@@ -832,9 +1073,9 @@ static int rc_hash_n64(char hash[33], const char* path)
|
||||
|
||||
stop = buffer + remaining;
|
||||
if (is_v64)
|
||||
rc_hash_v64_to_z64(buffer, stop);
|
||||
rc_hash_byteswap16(buffer, stop);
|
||||
else if (is_n64)
|
||||
rc_hash_n64_to_z64(buffer, stop);
|
||||
rc_hash_byteswap32(buffer, stop);
|
||||
|
||||
md5_append(&md5, buffer, (int)remaining);
|
||||
}
|
||||
@@ -957,6 +1198,121 @@ static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_gamecube(char hash[33], const char* path)
|
||||
{
|
||||
md5_state_t md5;
|
||||
void* file_handle;
|
||||
const uint32_t BASE_HEADER_SIZE = 0x2440;
|
||||
const uint32_t MAX_HEADER_SIZE = 1024 * 1024;
|
||||
uint32_t apploader_header_size, apploader_body_size, apploader_trailer_size, header_size;
|
||||
uint8_t quad_buffer[4];
|
||||
uint8_t addr_buffer[0xD8];
|
||||
uint8_t* buffer;
|
||||
uint32_t dol_offset;
|
||||
uint32_t dol_offsets[18];
|
||||
uint32_t dol_sizes[18];
|
||||
uint32_t dol_buf_size = 0;
|
||||
uint32_t ix;
|
||||
|
||||
file_handle = rc_file_open(path);
|
||||
if (!file_handle)
|
||||
return rc_hash_error("Could not open file");
|
||||
|
||||
/* Verify Gamecube */
|
||||
rc_file_seek(file_handle, 0x1c, SEEK_SET);
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
if (quad_buffer[0] != 0xC2|| quad_buffer[1] != 0x33 || quad_buffer[2] != 0x9F || quad_buffer[3] != 0x3D)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Not a Gamecube disc");
|
||||
}
|
||||
|
||||
/* GetApploaderSize */
|
||||
rc_file_seek(file_handle, BASE_HEADER_SIZE + 0x14, SEEK_SET);
|
||||
apploader_header_size = 0x20;
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
apploader_body_size =
|
||||
(quad_buffer[0] << 24) | (quad_buffer[1] << 16) | (quad_buffer[2] << 8) | quad_buffer[3];
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
apploader_trailer_size =
|
||||
(quad_buffer[0] << 24) | (quad_buffer[1] << 16) | (quad_buffer[2] << 8) | quad_buffer[3];
|
||||
header_size = BASE_HEADER_SIZE + apploader_header_size + apploader_body_size + apploader_trailer_size;
|
||||
if (header_size > MAX_HEADER_SIZE) header_size = MAX_HEADER_SIZE;
|
||||
|
||||
/* Hash headers */
|
||||
buffer = (uint8_t*)malloc(header_size);
|
||||
if (!buffer)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
}
|
||||
rc_file_seek(file_handle, 0, SEEK_SET);
|
||||
rc_file_read(file_handle, buffer, header_size);
|
||||
md5_init(&md5);
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
char message[128];
|
||||
snprintf(message, sizeof(message), "Hashing %u byte header", header_size);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
md5_append(&md5, buffer, header_size);
|
||||
|
||||
/* GetBootDOLOffset
|
||||
* Base header size is guaranteed larger than 0x423 therefore buffer contains dol_offset right now
|
||||
*/
|
||||
dol_offset = (buffer[0x420] << 24) | (buffer[0x421] << 16) | (buffer[0x422] << 8) | buffer[0x423];
|
||||
free(buffer);
|
||||
|
||||
/* Find offsetsand sizes for the 7 main.dol code segments and 11 main.dol data segments */
|
||||
rc_file_seek(file_handle, dol_offset, SEEK_SET);
|
||||
rc_file_read(file_handle, addr_buffer, 0xD8);
|
||||
for (ix = 0; ix < 18; ix++)
|
||||
{
|
||||
dol_offsets[ix] =
|
||||
(addr_buffer[0x0 + ix * 4] << 24) |
|
||||
(addr_buffer[0x1 + ix * 4] << 16) |
|
||||
(addr_buffer[0x2 + ix * 4] << 8) |
|
||||
addr_buffer[0x3 + ix * 4];
|
||||
dol_sizes[ix] =
|
||||
(addr_buffer[0x90 + ix * 4] << 24) |
|
||||
(addr_buffer[0x91 + ix * 4] << 16) |
|
||||
(addr_buffer[0x92 + ix * 4] << 8) |
|
||||
addr_buffer[0x93 + ix * 4];
|
||||
dol_buf_size = (dol_sizes[ix] > dol_buf_size) ? dol_sizes[ix] : dol_buf_size;
|
||||
}
|
||||
|
||||
/* Iterate through the 18 main.dol segments and hash each */
|
||||
buffer = (uint8_t*)malloc(dol_buf_size);
|
||||
if (!buffer)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
}
|
||||
for (ix = 0; ix < 18; ix++)
|
||||
{
|
||||
if (dol_sizes[ix] == 0)
|
||||
continue;
|
||||
rc_file_seek(file_handle, dol_offsets[ix], SEEK_SET);
|
||||
rc_file_read(file_handle, buffer, dol_sizes[ix]);
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
char message[128];
|
||||
if (ix < 7)
|
||||
snprintf(message, sizeof(message), "Hashing %u byte main.dol code segment %u", dol_sizes[ix], ix);
|
||||
else
|
||||
snprintf(message, sizeof(message), "Hashing %u byte main.dol data segment %u", dol_sizes[ix], ix - 7);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
md5_append(&md5, buffer, dol_sizes[ix]);
|
||||
}
|
||||
|
||||
/* Finalize */
|
||||
rc_file_close(file_handle);
|
||||
free(buffer);
|
||||
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_pce(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
/* if the file contains a header, ignore it (expect ROM data to be multiple of 128KB) */
|
||||
@@ -1290,7 +1646,7 @@ static int rc_hash_find_playstation_executable(void* track_handle, const char* b
|
||||
|
||||
if (strncmp(ptr, cdrom_prefix, cdrom_prefix_len) == 0)
|
||||
ptr += cdrom_prefix_len;
|
||||
if (*ptr == '\\')
|
||||
while (*ptr == '\\')
|
||||
++ptr;
|
||||
|
||||
start = ptr;
|
||||
@@ -1454,18 +1810,30 @@ static int rc_hash_psp(char hash[33], const char* path)
|
||||
*/
|
||||
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\PARAM.SFO", &size);
|
||||
if (!sector)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Not a PSP game disc");
|
||||
}
|
||||
|
||||
md5_init(&md5);
|
||||
if (!rc_hash_cd_file(&md5, track_handle, sector, NULL, size, "PSP_GAME\\PARAM.SFO"))
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\SYSDIR\\EBOOT.BIN", &size);
|
||||
if (!sector)
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_error("Could not find primary executable");
|
||||
}
|
||||
|
||||
if (!rc_hash_cd_file(&md5, track_handle, sector, NULL, size, "PSP_GAME\\SYSDIR\\EBOOT.BIN"))
|
||||
{
|
||||
rc_cd_close_track(track_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc_cd_close_track(track_handle);
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
@@ -1527,7 +1895,11 @@ static struct rc_buffered_file rc_buffered_file;
|
||||
static void* rc_file_open_buffered_file(const char* path)
|
||||
{
|
||||
struct rc_buffered_file* handle = (struct rc_buffered_file*)malloc(sizeof(struct rc_buffered_file));
|
||||
memcpy(handle, &rc_buffered_file, sizeof(rc_buffered_file));
|
||||
(void)path;
|
||||
|
||||
if (handle)
|
||||
memcpy(handle, &rc_buffered_file, sizeof(rc_buffered_file));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1631,7 +2003,9 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
case RC_CONSOLE_SG1000:
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
case RC_CONSOLE_TI83:
|
||||
case RC_CONSOLE_TIC80:
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
case RC_CONSOLE_VECTREX:
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
case RC_CONSOLE_WASM4:
|
||||
@@ -1659,6 +2033,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
|
||||
case RC_CONSOLE_NINTENDO_64:
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return rc_hash_file_from_buffer(hash, console_id, buffer, buffer_size);
|
||||
}
|
||||
}
|
||||
@@ -1922,7 +2297,9 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
case RC_CONSOLE_SG1000:
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
case RC_CONSOLE_TI83:
|
||||
case RC_CONSOLE_TIC80:
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
case RC_CONSOLE_VECTREX:
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
case RC_CONSOLE_WASM4:
|
||||
@@ -1946,6 +2323,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_ATARI_7800:
|
||||
case RC_CONSOLE_ATARI_LYNX:
|
||||
case RC_CONSOLE_NINTENDO:
|
||||
case RC_CONSOLE_PC_ENGINE:
|
||||
case RC_CONSOLE_SUPER_NINTENDO:
|
||||
/* additional logic whole-file hash - buffer then call rc_hash_generate_from_buffer */
|
||||
return rc_hash_buffered_file(hash, console_id, path);
|
||||
@@ -1959,13 +2337,29 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_ARCADE:
|
||||
return rc_hash_arcade(hash, path);
|
||||
|
||||
case RC_CONSOLE_ATARI_JAGUAR_CD:
|
||||
return rc_hash_jaguar_cd(hash, path);
|
||||
|
||||
case RC_CONSOLE_DREAMCAST:
|
||||
if (rc_path_compare_extension(path, "m3u"))
|
||||
return rc_hash_generate_from_playlist(hash, console_id, path);
|
||||
|
||||
return rc_hash_dreamcast(hash, path);
|
||||
|
||||
case RC_CONSOLE_GAMECUBE:
|
||||
return rc_hash_gamecube(hash, path);
|
||||
|
||||
case RC_CONSOLE_NEO_GEO_CD:
|
||||
return rc_hash_neogeo_cd(hash, path);
|
||||
|
||||
case RC_CONSOLE_NINTENDO_64:
|
||||
return rc_hash_n64(hash, path);
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return rc_hash_nintendo_ds(hash, path);
|
||||
|
||||
case RC_CONSOLE_PC_ENGINE:
|
||||
case RC_CONSOLE_PC_ENGINE_CD:
|
||||
if (rc_path_compare_extension(path, "cue") || rc_path_compare_extension(path, "chd"))
|
||||
return rc_hash_pce_cd(hash, path);
|
||||
|
||||
@@ -1995,12 +2389,6 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_PSP:
|
||||
return rc_hash_psp(hash, path);
|
||||
|
||||
case RC_CONSOLE_DREAMCAST:
|
||||
if (rc_path_compare_extension(path, "m3u"))
|
||||
return rc_hash_generate_from_playlist(hash, console_id, path);
|
||||
|
||||
return rc_hash_dreamcast(hash, path);
|
||||
|
||||
case RC_CONSOLE_SEGA_CD:
|
||||
case RC_CONSOLE_SATURN:
|
||||
if (rc_path_compare_extension(path, "m3u"))
|
||||
@@ -2077,7 +2465,7 @@ static void rc_hash_initialize_dsk_iterator(struct rc_hash_iterator* iterator, c
|
||||
rc_hash_iterator_append_console(iterator, RC_CONSOLE_APPLE_II);
|
||||
}
|
||||
|
||||
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, uint8_t* buffer, size_t buffer_size)
|
||||
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
int need_path = !buffer;
|
||||
|
||||
@@ -2108,6 +2496,15 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
break;
|
||||
|
||||
case '8':
|
||||
/* http://tibasicdev.wikidot.com/file-extensions */
|
||||
if (rc_path_compare_extension(ext, "83g") ||
|
||||
rc_path_compare_extension(ext, "83p"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_TI83;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
if (rc_path_compare_extension(ext, "a78"))
|
||||
{
|
||||
@@ -2162,9 +2559,11 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
||||
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
||||
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
|
||||
iterator->consoles[5] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[6] = RC_CONSOLE_PCFX;
|
||||
iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD;
|
||||
iterator->consoles[8] = RC_CONSOLE_ATARI_JAGUAR_CD;
|
||||
need_path = 1;
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "chd"))
|
||||
@@ -2173,7 +2572,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
||||
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
||||
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
|
||||
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
|
||||
iterator->consoles[5] = RC_CONSOLE_3DO;
|
||||
iterator->consoles[6] = RC_CONSOLE_PCFX;
|
||||
need_path = 1;
|
||||
@@ -2199,7 +2598,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "d64"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
|
||||
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "d88"))
|
||||
{
|
||||
@@ -2219,7 +2618,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "fd"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
|
||||
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2330,7 +2729,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "nds"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_NINTENDO_DS;
|
||||
iterator->consoles[0] = RC_CONSOLE_NINTENDO_DS; /* ASSERT: handles both DS and DSi */
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "n64") ||
|
||||
rc_path_compare_extension(ext, "ndd"))
|
||||
@@ -2412,6 +2811,13 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
if (rc_path_compare_extension(ext, "uze"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_UZEBOX;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
if (rc_path_compare_extension(ext, "vb"))
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
@@ -161,7 +162,7 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
if (!((ptrdiff_t)data & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user