RetroArch/tasks/task_database_cue.c

1365 lines
39 KiB
C
Raw Normal View History

2015-09-16 20:39:47 +00:00
/* RetroArch - A frontend for libretro.
2017-01-22 12:40:32 +00:00
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2014-2017 - Jean-André Santoni
2019-02-22 21:31:54 +00:00
* Copyright (C) 2016-2019 - Brad Parker
2015-09-16 20:39:47 +00:00
*
* RetroArch 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch 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 RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
2015-09-25 23:44:47 +00:00
#include <ctype.h>
#include <string.h>
2015-09-16 20:39:47 +00:00
2016-09-11 14:31:23 +00:00
#include <retro_miscellaneous.h>
#include <retro_endianness.h>
2015-09-16 20:39:47 +00:00
#include <compat/strcasestr.h>
#include <compat/strl.h>
2016-02-07 00:50:08 +00:00
#include <file/file_path.h>
2015-09-16 20:39:47 +00:00
#include <retro_endianness.h>
2016-03-20 15:29:14 +00:00
#include <streams/file_stream.h>
#include <streams/interface_stream.h>
2016-01-20 03:07:24 +00:00
#include <string/stdstring.h>
#include "../retroarch.h"
2015-09-16 20:39:47 +00:00
2016-09-11 14:24:02 +00:00
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
2015-09-16 20:39:47 +00:00
#include "../database_info.h"
2016-09-11 14:24:02 +00:00
#include "tasks_internal.h"
2016-03-20 16:28:24 +00:00
#include "../list_special.h"
2015-09-16 20:39:47 +00:00
#include "../msg_hash.h"
2015-11-23 11:03:38 +00:00
#include "../verbosity.h"
2015-09-16 20:39:47 +00:00
#define MAGIC_LEN 17
2016-01-28 08:33:09 +00:00
#define MAX_TOKEN_LEN 255
2015-09-16 20:39:47 +00:00
2016-02-04 09:30:12 +00:00
#ifdef MSB_FIRST
#define MODETEST_VAL 0x00ffffff
#else
#define MODETEST_VAL 0xffffff00
#endif
2020-08-14 16:51:50 +00:00
/* TODO/FIXME - reorder this according to CODING-GUIDELINES
* and make sure LUT table below conforms */
2015-09-16 20:39:47 +00:00
struct magic_entry
{
2017-03-07 00:16:26 +00:00
int32_t offset;
const char *system_name;
const char *magic;
int length_magic;
2015-09-16 20:39:47 +00:00
};
static struct magic_entry MAGIC_NUMBERS[] = {
{ 0x000010, "Sega - Mega-CD - Sega CD", "\x53\x45\x47\x41\x44\x49\x53\x43\x53\x59\x53\x54\x45\x4d", 14},
{ 0x000010, "Sega - Saturn", "\x53\x45\x47\x41\x20\x53\x45\x47\x41\x53\x41\x54\x55\x52\x4e", 15},
{ 0x000010, "Sega - Dreamcast", "\x53\x45\x47\x41\x20\x53\x45\x47\x41\x4b\x41\x54\x41\x4e\x41", 15},
{ 0x000018, "Nintendo - Wii", "\x5d\x1c\x9e\xa3", 4},
{ 0x000218, "Nintendo - Wii", "\x5d\x1c\x9e\xa3", 4},
{ 0x00001c, "Nintendo - GameCube", "\xc2\x33\x9f\x3d", 4},
{ 0x008008, "Sony - PlayStation Portable", "\x50\x53\x50\x20\x47\x41\x4d\x45", 8},
{ 0x008008, "Sony - PlayStation", "\x50\x4c\x41\x59\x53\x54\x41\x54\x49\x4f\x4e", 11},
{ 0x009320, "Sony - PlayStation", "\x50\x4c\x41\x59\x53\x54\x41\x54\x49\x4f\x4e", 11},
{ 0, NULL, NULL, 0}
2015-09-16 20:39:47 +00:00
};
/**
* Given a filename and position, find the associated disc number.
*/
static int cue_find_disc_number(const char* str1, char disc)
{
switch (disc)
{
case 'a':
case 'A':
return 1;
case 'b':
case 'B':
return 2;
case 'c':
case 'C':
return 3;
case 'd':
case 'D':
return 4;
case 'e':
case 'E':
return 5;
case 'f':
case 'F':
return 6;
case 'g':
case 'G':
return 7;
case 'h':
case 'H':
return 8;
case 'i':
case 'I':
return 9;
default:
if ((disc - '0') >= 1)
return (disc - '0');
break;
}
return 0;
}
/**
* Given a title and filename, append the appropriate disc number to it.
*/
static void cue_append_multi_disc_suffix(char * str1, const char *filename)
{
/* Check multi-disc and insert suffix */
int result = string_find_index_substring_string(filename, "(Disc ");
if (result < 0)
result = string_find_index_substring_string(filename, "(disc ");
if (result < 0)
result = string_find_index_substring_string(filename, "(Disk ");
if (result < 0)
result = string_find_index_substring_string(filename, "(disk ");
if (result >= 0)
{
int disc_number = cue_find_disc_number(filename, filename[result + 6]);
if (disc_number > 0)
{
char *dest = str1;
sprintf(dest + strlen(dest), "-%i", disc_number - 1);
}
}
}
static int64_t task_database_cue_get_token(intfstream_t *fd, char *token, uint64_t max_len)
2015-09-16 20:39:47 +00:00
{
char *c = token;
int64_t len = 0;
2015-09-16 20:39:47 +00:00
int in_string = 0;
for (;;)
2015-09-16 20:39:47 +00:00
{
int64_t rv = (int64_t)intfstream_read(fd, c, 1);
2015-09-16 20:39:47 +00:00
if (rv == 0)
return 0;
2015-09-18 02:03:11 +00:00
if (rv < 1)
2015-09-16 20:39:47 +00:00
{
switch (errno)
{
case EINTR:
case EAGAIN:
continue;
default:
return -errno;
}
}
switch (*c)
{
case ' ':
case '\t':
case '\r':
case '\n':
if (c == token)
continue;
if (!in_string)
{
*c = '\0';
return len;
}
break;
case '\"':
if (c == token)
{
in_string = 1;
continue;
}
*c = '\0';
return len;
}
len++;
c++;
if (len == (int64_t)max_len)
2015-09-16 20:39:47 +00:00
{
*c = '\0';
return len;
}
}
}
int detect_ps1_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
#define DISC_DATA_SIZE_PS1 60000
int pos;
char raw_game_id[50];
char disc_data[DISC_DATA_SIZE_PS1];
/* Load data into buffer and use pointers */
if (intfstream_seek(fd, 0, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, disc_data, DISC_DATA_SIZE_PS1) <= 0)
return false;
disc_data[DISC_DATA_SIZE_PS1 - 1] = '\0';
for (pos = 0; pos < DISC_DATA_SIZE_PS1; pos++)
{
strncpy(raw_game_id, &disc_data[pos], 12);
raw_game_id[12] = '\0';
if ( string_is_equal_fast(raw_game_id, "S", STRLEN_CONST("S"))
|| string_is_equal_fast(raw_game_id, "E", STRLEN_CONST("E")))
{
if (
( string_is_equal_fast(raw_game_id, "SCUS_", STRLEN_CONST("SCUS_")))
|| (string_is_equal_fast(raw_game_id, "SLUS_", STRLEN_CONST("SLUS_")))
|| (string_is_equal_fast(raw_game_id, "SLES_", STRLEN_CONST("SLES_")))
|| (string_is_equal_fast(raw_game_id, "SCED_", STRLEN_CONST("SCED_")))
|| (string_is_equal_fast(raw_game_id, "SLPS_", STRLEN_CONST("SLPS_")))
|| (string_is_equal_fast(raw_game_id, "SLPM_", STRLEN_CONST("SLPM_")))
|| (string_is_equal_fast(raw_game_id, "SCPS_", STRLEN_CONST("SCPS_")))
|| (string_is_equal_fast(raw_game_id, "SLED_", STRLEN_CONST("SLED_")))
|| (string_is_equal_fast(raw_game_id, "SIPS_", STRLEN_CONST("SIPS_")))
|| (string_is_equal_fast(raw_game_id, "ESPM_", STRLEN_CONST("ESPM_")))
|| (string_is_equal_fast(raw_game_id, "SCES_", STRLEN_CONST("SCES_")))
)
{
raw_game_id[4] = '-';
if (string_is_equal_fast(&raw_game_id[8], ".", STRLEN_CONST(".")))
{
raw_game_id[8] = raw_game_id[9];
raw_game_id[9] = raw_game_id[10];
}
/* A few games have their serial in the form of xx.xxx */
/* Tanaka Torahiko no Ultra-ryuu Shougi - Ibisha Anaguma-hen (Japan) -> SLPS_02.261 */
else if (string_is_equal_fast(&raw_game_id[7], ".", STRLEN_CONST(".")))
{
raw_game_id[7] = raw_game_id[8];
raw_game_id[8] = raw_game_id[9];
raw_game_id[9] = raw_game_id[10];
}
raw_game_id[10] = '\0';
string_remove_all_whitespace(s, raw_game_id);
cue_append_multi_disc_suffix(s, filename);
return true;
}
else if (string_is_equal_fast(&disc_data[pos], "LSP-", STRLEN_CONST("LSP-")))
{
string_remove_all_whitespace(s, raw_game_id);
s[10] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
}
s[0 ] = 'X';
s[1 ] = 'X';
s[2 ] = 'X';
s[3 ] = 'X';
s[4 ] = 'X';
s[5 ] = 'X';
s[6 ] = 'X';
s[7 ] = 'X';
s[8 ] = 'X';
s[9 ] = 'X';
s[10] = '\0';
cue_append_multi_disc_suffix(s, filename);
return false;
}
int detect_psp_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
#define DISC_DATA_SIZE_PSP 40000
int pos;
char disc_data[DISC_DATA_SIZE_PSP];
/* Load data into buffer and use pointers */
if (intfstream_seek(fd, 0, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, disc_data, DISC_DATA_SIZE_PSP) <= 0)
return false;
disc_data[DISC_DATA_SIZE_PSP - 1] = '\0';
for (pos = 0; pos < DISC_DATA_SIZE_PSP; pos++)
{
strncpy(s, &disc_data[pos], 10);
s[10] = '\0';
if ( string_is_equal_fast(s, "U", STRLEN_CONST("U"))
|| string_is_equal_fast(s, "N", STRLEN_CONST("N")))
{
if (
( string_is_equal_fast(s, "ULES-", STRLEN_CONST("ULES-")))
|| (string_is_equal_fast(s, "ULUS-", STRLEN_CONST("ULUS-")))
|| (string_is_equal_fast(s, "ULJS-", STRLEN_CONST("ULJS-")))
|| (string_is_equal_fast(s, "ULEM-", STRLEN_CONST("ULEM-")))
|| (string_is_equal_fast(s, "ULUM-", STRLEN_CONST("ULUM-")))
|| (string_is_equal_fast(s, "ULJM-", STRLEN_CONST("ULJM-")))
|| (string_is_equal_fast(s, "UCES-", STRLEN_CONST("UCES-")))
|| (string_is_equal_fast(s, "UCUS-", STRLEN_CONST("UCUS-")))
|| (string_is_equal_fast(s, "UCJS-", STRLEN_CONST("UCJS-")))
|| (string_is_equal_fast(s, "UCAS-", STRLEN_CONST("UCAS-")))
|| (string_is_equal_fast(s, "UCKS-", STRLEN_CONST("UCKS-")))
|| (string_is_equal_fast(s, "ULKS-", STRLEN_CONST("ULKS-")))
|| (string_is_equal_fast(s, "ULAS-", STRLEN_CONST("ULAS-")))
|| (string_is_equal_fast(s, "NPEH-", STRLEN_CONST("NPEH-")))
|| (string_is_equal_fast(s, "NPUH-", STRLEN_CONST("NPUH-")))
|| (string_is_equal_fast(s, "NPJH-", STRLEN_CONST("NPJH-")))
|| (string_is_equal_fast(s, "NPHH-", STRLEN_CONST("NPHH-")))
|| (string_is_equal_fast(s, "NPEG-", STRLEN_CONST("NPEG-")))
|| (string_is_equal_fast(s, "NPUG-", STRLEN_CONST("NPUG-")))
|| (string_is_equal_fast(s, "NPJG-", STRLEN_CONST("NPJG-")))
|| (string_is_equal_fast(s, "NPHG-", STRLEN_CONST("NPHG-")))
|| (string_is_equal_fast(s, "NPEZ-", STRLEN_CONST("NPEZ-")))
|| (string_is_equal_fast(s, "NPUZ-", STRLEN_CONST("NPUZ-")))
|| (string_is_equal_fast(s, "NPJZ-", STRLEN_CONST("NPJZ-")))
)
{
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
}
return false;
}
int detect_gc_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
size_t _len;
char region_id;
char pre_game_id[20];
char raw_game_id[20];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 4) <= 0)
return false;
raw_game_id[4] = '\0';
/** Scrub files with bad data and log **/
if (raw_game_id[0] == '\0' || raw_game_id[0] == ' ')
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
return false;
}
/** convert raw gamecube serial to redump serial.
not enough is known about the disc data to properly
convert every raw serial to redump serial. it will
only fail with the following excpetions: the
subregions of europe P-UKV, P-AUS, X-UKV, X-EUU
will not match redump.**/
/** insert prefix **/
pre_game_id[0 ] = 'D';
pre_game_id[1 ] = 'L';
pre_game_id[2 ] = '-';
pre_game_id[3 ] = 'D';
pre_game_id[4 ] = 'O';
pre_game_id[5 ] = 'L';
pre_game_id[6 ] = '-';
pre_game_id[7 ] = '\0';
/** add raw serial **/
strlcat(pre_game_id, raw_game_id, sizeof(pre_game_id));
2016-06-03 09:46:55 +00:00
/** check region **/
region_id = pre_game_id[10];
/** check multi-disc and insert suffix **/
cue_append_multi_disc_suffix(pre_game_id, filename);
_len = strlcpy(s, pre_game_id, len);
2016-06-03 09:46:55 +00:00
switch (region_id)
2016-06-03 09:46:55 +00:00
{
case 'E':
s[_len ] = '-';
s[_len+1] = 'U';
s[_len+2] = 'S';
s[_len+3] = 'A';
s[_len+4] = '\0';
return true;
case 'J':
s[_len ] = '-';
s[_len+1] = 'J';
s[_len+2] = 'P';
s[_len+3] = 'N';
s[_len+4] = '\0';
return true;
case 'P': /** NYI: P can also be P-UKV, P-AUS **/
case 'X': /** NYI: X can also be X-UKV, X-EUU **/
s[_len ] = '-';
s[_len+1] = 'E';
s[_len+2] = 'U';
s[_len+3] = 'R';
s[_len+4] = '\0';
return true;
case 'Y':
s[_len ] = '-';
s[_len+1] = 'F';
s[_len+2] = 'A';
s[_len+3] = 'H';
s[_len+4] = '\0';
return true;
case 'D':
s[_len ] = '-';
s[_len+1] = 'N';
s[_len+2] = 'O';
s[_len+3] = 'E';
s[_len+4] = '\0';
return true;
case 'S':
s[_len ] = '-';
s[_len+1] = 'E';
s[_len+2] = 'S';
s[_len+3] = 'P';
s[_len+4] = '\0';
return true;
case 'F':
s[_len ] = '-';
s[_len+1] = 'F';
s[_len+2] = 'R';
s[_len+3] = 'A';
s[_len+4] = '\0';
return true;
case 'I':
s[_len ] = '-';
s[_len+1] = 'I';
s[_len+2] = 'T';
s[_len+3] = 'A';
s[_len+4] = '\0';
return true;
case 'H':
s[_len ] = '-';
s[_len+1] = 'H';
s[_len+2] = 'O';
s[_len+3] = 'L';
s[_len+4] = '\0';
return true;
default:
break;
}
return false;
}
int detect_scd_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
size_t _len;
char pre_game_id[15];
char raw_game_id[15];
char check_prefix_t_hyp[10];
char check_suffix_50[10];
char check_prefix_g_hyp[10];
char check_prefix_mk_hyp[10];
char region_id[10];
size_t length;
int lengthref;
int index;
char lgame_id[10];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0x0193, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 11) <= 0)
return false;
raw_game_id[11] = '\0';
if (raw_game_id[0] == ' ')
{
if (intfstream_seek(fd, 0x0194, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 11) <= 0)
return false;
raw_game_id[11] = '\0';
2016-06-03 09:46:55 +00:00
}
/* Load raw region id or quit */
if (intfstream_seek(fd, 0x0200, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, region_id, 1) <= 0)
return false;
region_id[1] = '\0';
#ifdef DEBUG
/** Scrub files with bad data and log **/
if (raw_game_id[0] == '\0' || raw_game_id[0] == ' ' || raw_game_id[0] == '0')
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
/** convert raw Sega - Mega-CD - Sega CD serial to redump serial. **/
/** process raw serial to a pre serial without spaces **/
string_remove_all_whitespace(pre_game_id, raw_game_id); /** rule: remove all spaces from the raw serial globally **/
/** disect this pre serial into parts **/
length = strlen(pre_game_id);
lengthref = length - 2;
strncpy(check_prefix_t_hyp, pre_game_id, 2);
check_prefix_t_hyp[2] = '\0';
strncpy(check_prefix_g_hyp, pre_game_id, 2);
check_prefix_g_hyp[2] = '\0';
strncpy(check_prefix_mk_hyp, pre_game_id, 3);
check_prefix_mk_hyp[3] = '\0';
strncpy(check_suffix_50, &pre_game_id[lengthref], length - 2 + 1);
check_suffix_50[2] = '\0';
/** redump serials are built differently for each prefix **/
if (!strcmp(check_prefix_t_hyp, "T-"))
{
if (!strcmp(region_id, "U") || !strcmp(region_id, "J"))
{
if ((index = string_index_last_occurance(pre_game_id, '-')) == -1)
return false;
strncpy(s, pre_game_id, index);
s[index] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
if ((index = string_index_last_occurance(pre_game_id, '-')) == -1)
return false;
strncpy(lgame_id, pre_game_id, index);
lgame_id[index] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '5';
s[_len+2] = '0';
s[_len+3] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
else if (!strcmp(check_prefix_g_hyp, "G-"))
{
if ((index = string_index_last_occurance(pre_game_id, '-')) == -1)
return false;
strncpy(s, pre_game_id, index);
s[index] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else if (!strcmp(check_prefix_mk_hyp, "MK-"))
{
if (!strcmp(check_suffix_50, "50"))
{
strncpy(lgame_id, &pre_game_id[3], 4);
lgame_id[4] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '5';
s[_len+2] = '0';
s[_len+3] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
strncpy(s, &pre_game_id[3], 4);
s[4] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
else
{
string_trim_whitespace(raw_game_id);
strlcpy(s, raw_game_id, len);
return true;
}
return false;
}
int detect_sat_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
size_t _len;
char raw_game_id[15];
char raw_region_id[15];
char region_id;
char check_prefix_t_hyp[10];
char check_prefix_mk_hyp[10];
char check_suffix_5[10];
char check_suffix_50[10];
int length;
char lgame_id[10];
char rgame_id[10];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0x0030, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 9) <= 0)
return false;
raw_game_id[9] = '\0';
/* Load raw region id or quit */
if (intfstream_seek(fd, 0x0050, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_region_id, 1) <= 0)
return false;
raw_region_id[1] = '\0';
/** Scrub files with bad data and log **/
if (raw_game_id[0] == '\0' || raw_game_id[0] == ' ')
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
return false;
}
region_id = raw_region_id[0];
string_trim_whitespace(raw_game_id);
/** disect this raw serial into parts **/
strncpy(check_prefix_t_hyp, raw_game_id, 2);
check_prefix_t_hyp[2] = '\0';
strncpy(check_prefix_mk_hyp, raw_game_id, 3);
check_prefix_mk_hyp[3] = '\0';
length = strlen(raw_game_id);
strncpy(check_suffix_5, &raw_game_id[length - 2], 2);
check_suffix_5[2] = '\0';
strncpy(check_suffix_50, &raw_game_id[length - 2], 2);
check_suffix_50[2] = '\0';
/** redump serials are built differently for each region **/
switch (region_id)
{
case 'U':
if (strcmp(check_prefix_mk_hyp, "MK-") == 0)
{
strncpy(s, &raw_game_id[3], length - 3);
s[length - 3] = '\0';
cue_append_multi_disc_suffix(s, filename);
}
else
{
strlcpy(s, raw_game_id, len);
cue_append_multi_disc_suffix(s, filename);
}
return true;
case 'E':
strncpy(lgame_id, &raw_game_id[0], 2);
lgame_id[2] = '\0';
if (strcmp(check_suffix_5, "-5") == 0 || strcmp(check_suffix_50, "50") == 0)
{
strncpy(rgame_id, &raw_game_id[2], length - 4);
rgame_id[length - 4] = '\0';
}
else
{
strncpy(rgame_id, &raw_game_id[2], length - 1);
rgame_id[length - 1] = '\0';
}
strlcat(s, lgame_id, len);
_len = strlcat(s, rgame_id, len);
s[_len ] = '-';
s[_len+1] = '5';
s[_len+2] = '0';
s[_len+3] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
case 'J':
strlcpy(s, raw_game_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
default:
strlcpy(s, raw_game_id, len);
return true;
}
return false;
}
2015-09-16 20:39:47 +00:00
int detect_dc_game(intfstream_t *fd, char *s, size_t len, const char *filename)
2015-09-26 22:12:17 +00:00
{
size_t _len;
int total_hyphens;
int total_hyphens_recalc;
char pre_game_id[50];
char raw_game_id[50];
char check_prefix_t_hyp[10];
char check_prefix_t[10];
char check_prefix_hdr_hyp[10];
char check_prefix_mk_hyp[10];
size_t length;
size_t length_recalc;
int index;
size_t size_t_var;
char lgame_id[20];
char rgame_id[20];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0x0050, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 10) <= 0)
return false;
raw_game_id[10] = '\0';
/** Scrub files with bad data and log **/
if (raw_game_id[0] == '\0' || raw_game_id[0] == ' ')
2015-09-26 22:12:17 +00:00
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
return false;
}
2015-09-26 22:12:17 +00:00
string_trim_whitespace(raw_game_id);
string_replace_multi_space_with_single_space(raw_game_id);
string_replace_whitespace_with_single_character(raw_game_id, '-');
length = strlen(raw_game_id);
total_hyphens = string_count_occurrences_single_character(raw_game_id, '-');
/** disect this raw serial into parts **/
strncpy(check_prefix_t_hyp, raw_game_id, 2);
check_prefix_t_hyp[2] = '\0';
strncpy(check_prefix_t, raw_game_id, 1);
check_prefix_t[1] = '\0';
strncpy(check_prefix_hdr_hyp, raw_game_id, 4);
check_prefix_hdr_hyp[4] = '\0';
strncpy(check_prefix_mk_hyp, raw_game_id, 3);
check_prefix_mk_hyp[3] = '\0';
/** redump serials are built differently for each prefix **/
if (!strcmp(check_prefix_t_hyp, "T-"))
{
if (total_hyphens >= 2)
2015-09-26 22:12:17 +00:00
{
index = string_index_last_occurance(raw_game_id, '-');
if (index < 0)
return false;
size_t_var = (size_t)index;
strncpy(lgame_id, &raw_game_id[0], size_t_var);
lgame_id[index] = '\0';
strncpy(rgame_id, &raw_game_id[index + 1], length - 1);
rgame_id[length - 1] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
if (length <= 7)
{
strncpy(s, raw_game_id, 7);
s[7] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
strncpy(lgame_id, raw_game_id, 7);
lgame_id[7] = '\0';
strncpy(rgame_id, &raw_game_id[length - 2], length - 1);
rgame_id[length - 1] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
}
else if (!strcmp(check_prefix_t, "T"))
{
strncpy(lgame_id, raw_game_id, 1);
lgame_id[1] = '\0';
strncpy(rgame_id, &raw_game_id[1], length - 1);
rgame_id[length - 1] = '\0';
_len = strlcpy(pre_game_id, lgame_id, sizeof(pre_game_id));
pre_game_id[_len ] = '-';
pre_game_id[_len+1] = '\0';
strlcat(pre_game_id, rgame_id, sizeof(pre_game_id));
total_hyphens_recalc = string_count_occurrences_single_character(pre_game_id, '-');
if (total_hyphens_recalc >= 2)
{
index = string_index_last_occurance(pre_game_id, '-');
if (index < 0)
return false;
size_t_var = (size_t)index;
strncpy(lgame_id, pre_game_id, size_t_var);
lgame_id[index] = '\0';
length_recalc = strlen(pre_game_id);
strncpy(rgame_id, &pre_game_id[length_recalc - 2], length_recalc - 1);
rgame_id[length_recalc - 1] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
length_recalc = strlen(pre_game_id) - 1;
if (length_recalc <= 8)
{
strncpy(s, pre_game_id, 8);
s[8] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
strncpy(lgame_id, pre_game_id, 7);
lgame_id[7] = '\0';
strncpy(rgame_id, &pre_game_id[length_recalc - 2], length_recalc - 1);
rgame_id[length_recalc - 1] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id ,len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
2015-09-26 22:12:17 +00:00
}
}
else if (!strcmp(check_prefix_hdr_hyp, "HDR-"))
{
if (total_hyphens >= 2)
{
index = string_index_last_occurance(raw_game_id, '-');
if (index < 0)
return false;
strncpy(lgame_id, raw_game_id, index - 1);
lgame_id[index - 1] = '\0';
strncpy(rgame_id, &raw_game_id[length - 4], length - 3);
rgame_id[length - 3] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
2015-09-26 22:12:17 +00:00
else
{
strlcpy(s, raw_game_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
2015-09-26 22:12:17 +00:00
}
else if (!strcmp(check_prefix_mk_hyp, "MK-"))
{
2015-09-26 22:12:17 +00:00
if (length <= 8)
{
strncpy(s, raw_game_id, 8);
s[8] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
strncpy(lgame_id, raw_game_id, 8);
lgame_id[8] = '\0';
strncpy(rgame_id, &raw_game_id[length - 2], length - 1);
rgame_id[length - 1] = '\0';
_len = strlcat(s, lgame_id, len);
s[_len ] = '-';
s[_len+1] = '\0';
strlcat(s, rgame_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
}
else
{
strlcpy(s, raw_game_id, len);
return true;
}
return false;
2015-09-26 22:12:17 +00:00
}
int detect_wii_game(intfstream_t *fd, char *s, size_t len, const char *filename)
2017-10-27 07:58:19 +00:00
{
char raw_game_id[15];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0x0000, SEEK_SET) < 0)
return false;
2017-10-27 07:58:19 +00:00
if (intfstream_read(fd, raw_game_id, 6) <= 0)
return false;
if (string_is_equal_fast(raw_game_id, "WBFS", STRLEN_CONST("WBFS")))
{
if (intfstream_seek(fd, 0x0200, SEEK_SET) < 0)
return false;
if (intfstream_read(fd, raw_game_id, 6) <= 0)
return false;
}
raw_game_id[6] = '\0';
/** Scrub files with bad data and log **/
if (raw_game_id[0] == '\0' || raw_game_id[0] == ' ')
2017-10-27 07:58:19 +00:00
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
return false;
2017-10-27 07:58:19 +00:00
}
cue_append_multi_disc_suffix(s, filename);
strlcpy(s, raw_game_id, len);
return true;
2017-10-27 07:58:19 +00:00
}
#if 0
2017-07-30 03:23:05 +00:00
/**
* Check for an ASCII serial in the first few bits of the ISO (Wii).
* TODO/FIXME - unused for now
2017-07-30 03:23:05 +00:00
*/
static int detect_serial_ascii_game(intfstream_t *fd, char *s, size_t len)
2017-07-30 03:23:05 +00:00
{
unsigned pos;
2020-06-10 14:24:52 +00:00
int number_of_ascii = 0;
bool rv = false;
2017-07-30 03:23:05 +00:00
for (pos = 0; pos < 10000; pos++)
{
intfstream_seek(fd, pos, SEEK_SET);
if (intfstream_read(fd, s, 15) > 0)
2017-07-30 03:23:05 +00:00
{
2017-07-30 08:11:41 +00:00
unsigned i;
s[15] = '\0';
2020-06-10 14:24:52 +00:00
number_of_ascii = 0;
2017-07-30 03:23:05 +00:00
/* When scanning WBFS files, "WBFS" is discovered as the first serial. Ignore it. */
if (string_is_equal(s, "WBFS"))
continue;
2017-07-30 08:11:41 +00:00
/* Loop through until we run out of ASCII characters. */
for (i = 0; i < 15; i++)
{
/* Is the given character ASCII? A-Z, 0-9, - */
if ( (s[i] == 45) ||
(s[i] >= 48 && s[i] <= 57) ||
(s[i] >= 65 && s[i] <= 90))
2020-06-10 14:24:52 +00:00
number_of_ascii++;
2017-07-30 08:11:41 +00:00
else
2017-07-30 03:23:05 +00:00
break;
}
2020-06-10 14:24:52 +00:00
/* If the length of the text is between 3 and 9 characters,
* it could be a serial. */
if (number_of_ascii > 3 && number_of_ascii < 9)
2017-07-30 08:11:41 +00:00
{
/* Cut the string off, and return it as a valid serial. */
s[number_of_ascii] = '\0';
2020-06-10 14:24:52 +00:00
rv = true;
2017-07-30 03:23:05 +00:00
break;
}
}
}
return rv;
}
#endif
2017-07-30 03:23:05 +00:00
int detect_system(intfstream_t *fd, const char **system_name, const char * filename)
2015-09-16 20:39:47 +00:00
{
int i;
char magic[50];
2015-09-16 20:39:47 +00:00
#ifdef DEBUG
RARCH_LOG("[Scanner]: %s\n", msg_hash_to_str(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS));
#endif
2015-09-16 20:39:47 +00:00
for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++)
{
if (intfstream_seek(fd, MAGIC_NUMBERS[i].offset, SEEK_SET) >= 0)
2015-09-16 20:39:47 +00:00
{
if (intfstream_read(fd, magic, MAGIC_NUMBERS[i].length_magic) > 0)
{
magic[MAGIC_NUMBERS[i].length_magic] = '\0';
if (memcmp(MAGIC_NUMBERS[i].magic, magic, MAGIC_NUMBERS[i].length_magic) == 0)
{
*system_name = MAGIC_NUMBERS[i].system_name;
#ifdef DEBUG
RARCH_LOG("[Scanner]: Name: %s\n", filename);
RARCH_LOG("[Scanner]: System: %s\n", MAGIC_NUMBERS[i].system_name);
#endif
return true;
}
}
}
}
#ifdef DEBUG
RARCH_LOG("[Scanner]: Name: %s\n", filename);
RARCH_LOG("[Scanner]: System: Unknown\n");
#endif
return false;
2015-09-16 20:39:47 +00:00
}
static int64_t intfstream_get_file_size(const char *path)
{
int64_t rv;
2017-12-11 12:21:44 +00:00
intfstream_t *fd = intfstream_open_file(path,
2017-12-11 11:53:47 +00:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
2017-12-11 12:21:44 +00:00
if (!fd)
return -1;
2017-12-11 12:21:44 +00:00
rv = intfstream_get_size(fd);
intfstream_close(fd);
2017-12-11 19:24:00 +00:00
free(fd);
return rv;
}
static bool update_cand(int64_t *cand_index, int64_t *last_index,
2020-06-10 14:24:52 +00:00
uint64_t *largest, char *last_file, uint64_t *offset,
uint64_t *size, char *track_path, uint64_t max_len)
{
2018-04-09 17:30:36 +00:00
if (*cand_index != -1)
{
if ((uint64_t)(*last_index - *cand_index) > *largest)
2018-04-09 17:30:36 +00:00
{
*largest = *last_index - *cand_index;
strlcpy(track_path, last_file, (size_t)max_len);
2018-04-09 17:30:36 +00:00
*offset = *cand_index;
*size = *largest;
*cand_index = -1;
return true;
}
2020-06-10 14:24:52 +00:00
*cand_index = -1;
2018-04-09 17:30:36 +00:00
}
return false;
}
2017-09-18 00:39:41 +00:00
int cue_find_track(const char *cue_path, bool first,
uint64_t *offset, uint64_t *size, char *track_path, uint64_t max_len)
2015-09-16 20:39:47 +00:00
{
2016-06-30 04:02:30 +00:00
int rv;
intfstream_info_t info;
2020-08-26 17:46:13 +00:00
char tmp_token[MAX_TOKEN_LEN];
char last_file[PATH_MAX_LENGTH];
char cue_dir[PATH_MAX_LENGTH];
2017-09-19 19:29:28 +00:00
intfstream_t *fd = NULL;
int64_t last_index = -1;
int64_t cand_index = -1;
2017-09-19 19:29:28 +00:00
int32_t cand_track = -1;
int32_t track = 0;
uint64_t largest = 0;
int64_t volatile file_size = -1;
2017-09-19 19:29:28 +00:00
bool is_data = false;
cue_dir[0] = last_file[0] = '\0';
2017-09-18 00:46:02 +00:00
fill_pathname_basedir(cue_dir, cue_path, sizeof(cue_dir));
2020-06-10 14:24:52 +00:00
info.type = INTFSTREAM_FILE;
2015-09-16 20:39:47 +00:00
if (!(fd = (intfstream_t*)intfstream_init(&info)))
goto error;
2017-12-11 11:15:00 +00:00
if (!intfstream_open(fd, cue_path,
2017-12-11 11:53:47 +00:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE))
2015-09-16 20:39:47 +00:00
{
#ifdef DEBUG
RARCH_LOG("Could not open CUE file '%s'\n", cue_path);
#endif
goto error;
2015-09-16 20:39:47 +00:00
}
#ifdef DEBUG
2015-09-16 20:39:47 +00:00
RARCH_LOG("Parsing CUE file '%s'...\n", cue_path);
#endif
2015-09-16 20:39:47 +00:00
2016-10-09 07:56:03 +00:00
tmp_token[0] = '\0';
rv = -1;
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
2015-09-16 20:39:47 +00:00
{
2019-08-01 23:16:18 +00:00
if (string_is_equal_noncase(tmp_token, "FILE"))
2015-09-16 20:39:47 +00:00
{
/* Set last index to last EOF */
2018-04-09 17:30:36 +00:00
if (file_size != -1)
last_index = file_size;
/* We're changing files since the candidate, update it */
2020-06-10 14:24:52 +00:00
if (update_cand(&cand_index, &last_index,
&largest, last_file, offset,
size, track_path, max_len))
2018-04-09 17:30:36 +00:00
{
rv = 0;
2018-04-09 17:30:36 +00:00
if (first)
goto clean;
}
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
fill_pathname_join_special(last_file, cue_dir,
tmp_token, sizeof(last_file));
2017-12-11 12:21:44 +00:00
file_size = intfstream_get_file_size(last_file);
2015-09-16 20:39:47 +00:00
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
}
2019-08-01 23:16:18 +00:00
else if (string_is_equal_noncase(tmp_token, "TRACK"))
{
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
2019-08-01 23:16:18 +00:00
is_data = !string_is_equal_noncase(tmp_token, "AUDIO");
++track;
2018-04-09 17:30:36 +00:00
}
2019-08-01 23:16:18 +00:00
else if (string_is_equal_noncase(tmp_token, "INDEX"))
2018-04-09 17:30:36 +00:00
{
int m, s, f;
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
if (sscanf(tmp_token, "%02d:%02d:%02d", &m, &s, &f) < 3)
{
#ifdef DEBUG
RARCH_LOG("Error parsing time stamp '%s'\n", tmp_token);
#endif
goto error;
}
last_index = (size_t) (((m * 60 + s) * 75) + f) * 2352;
2015-09-16 20:39:47 +00:00
/* If we've changed tracks since the candidate, update it */
if (cand_track != -1 && track != cand_track &&
2020-06-10 14:24:52 +00:00
update_cand(&cand_index, &last_index, &largest,
last_file, offset,
size, track_path, max_len))
2018-04-09 17:30:36 +00:00
{
rv = 0;
2018-04-09 17:30:36 +00:00
if (first)
goto clean;
}
2018-04-09 17:30:36 +00:00
if (!is_data)
continue;
2018-04-09 17:30:36 +00:00
if (cand_index == -1)
{
cand_index = last_index;
cand_track = track;
}
2015-09-16 20:39:47 +00:00
}
}
2018-04-09 17:30:36 +00:00
if (file_size != -1)
last_index = file_size;
2020-06-10 14:24:52 +00:00
if (update_cand(&cand_index, &last_index,
&largest, last_file, offset,
size, track_path, max_len))
rv = 0;
2015-09-16 20:39:47 +00:00
clean:
intfstream_close(fd);
2017-09-21 22:03:40 +00:00
free(fd);
2015-09-16 20:39:47 +00:00
return rv;
error:
if (fd)
2017-09-21 21:38:29 +00:00
{
intfstream_close(fd);
2017-09-21 21:38:29 +00:00
free(fd);
}
return -1;
2015-09-16 20:39:47 +00:00
}
2017-09-18 00:39:41 +00:00
bool cue_next_file(intfstream_t *fd,
const char *cue_path, char *s, uint64_t len)
{
2020-08-26 17:46:13 +00:00
char tmp_token[MAX_TOKEN_LEN];
char cue_dir[PATH_MAX_LENGTH];
2017-09-19 19:29:28 +00:00
cue_dir[0] = '\0';
fill_pathname_basedir(cue_dir, cue_path, sizeof(cue_dir));
tmp_token[0] = '\0';
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
{
2019-08-01 23:16:18 +00:00
if (string_is_equal_noncase(tmp_token, "FILE"))
{
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
fill_pathname_join_special(s, cue_dir, tmp_token, (size_t)len);
return true;
}
}
return false;
}
2017-09-20 07:55:53 +00:00
int gdi_find_track(const char *gdi_path, bool first,
char *track_path, uint64_t max_len)
2017-09-18 00:39:41 +00:00
{
intfstream_info_t info;
2020-08-26 17:46:13 +00:00
char tmp_token[MAX_TOKEN_LEN];
2017-09-19 19:29:28 +00:00
intfstream_t *fd = NULL;
uint64_t largest = 0;
int rv = -1;
2017-09-19 19:29:28 +00:00
int size = -1;
int mode = -1;
int64_t file_size = -1;
2017-09-18 00:39:41 +00:00
2017-09-19 19:29:28 +00:00
info.type = INTFSTREAM_FILE;
if (!(fd = (intfstream_t*)intfstream_init(&info)))
2017-09-18 00:39:41 +00:00
goto error;
2017-12-11 11:15:00 +00:00
if (!intfstream_open(fd, gdi_path,
2017-12-11 11:53:47 +00:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE))
2017-09-18 00:39:41 +00:00
{
#ifdef DEBUG
RARCH_LOG("Could not open GDI file '%s'\n", gdi_path);
#endif
2017-09-18 00:39:41 +00:00
goto error;
}
#ifdef DEBUG
2017-09-18 00:39:41 +00:00
RARCH_LOG("Parsing GDI file '%s'...\n", gdi_path);
#endif
2017-09-18 00:39:41 +00:00
tmp_token[0] = '\0';
/* Skip track count */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
2017-09-18 00:39:41 +00:00
/* Track number */
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
2017-09-18 00:39:41 +00:00
{
/* Offset */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-18 00:39:41 +00:00
goto error;
/* Mode */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-18 00:39:41 +00:00
goto error;
2020-06-10 14:24:52 +00:00
2017-09-18 00:39:41 +00:00
mode = atoi(tmp_token);
/* Sector size */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-18 00:39:41 +00:00
goto error;
2020-06-10 14:24:52 +00:00
2017-09-18 00:39:41 +00:00
size = atoi(tmp_token);
/* File name */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-18 00:39:41 +00:00
goto error;
/* Check for data track */
if (!(mode == 0 && size == 2352))
{
char last_file[PATH_MAX_LENGTH];
char gdi_dir[PATH_MAX_LENGTH];
2017-09-20 07:55:53 +00:00
fill_pathname_basedir(gdi_dir, gdi_path, sizeof(gdi_dir));
fill_pathname_join_special(last_file,
gdi_dir, tmp_token, sizeof(last_file));
if ((file_size = intfstream_get_file_size(last_file)) < 0)
2017-09-18 00:39:41 +00:00
goto error;
2017-09-19 19:29:28 +00:00
if ((uint64_t)file_size > largest)
2017-09-18 00:39:41 +00:00
{
strlcpy(track_path, last_file, (size_t)max_len);
rv = 0;
2017-09-18 00:39:41 +00:00
largest = file_size;
2017-09-18 00:39:41 +00:00
if (first)
goto clean;
}
}
/* Disc offset (not used?) */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-18 00:39:41 +00:00
goto error;
}
clean:
intfstream_close(fd);
2017-09-20 10:18:31 +00:00
free(fd);
2017-09-18 00:39:41 +00:00
return rv;
error:
if (fd)
2017-09-20 10:18:31 +00:00
{
2017-09-18 00:39:41 +00:00
intfstream_close(fd);
2017-09-20 10:18:31 +00:00
free(fd);
}
return -1;
2017-09-18 00:39:41 +00:00
}
2017-09-20 07:55:53 +00:00
bool gdi_next_file(intfstream_t *fd, const char *gdi_path,
char *path, uint64_t max_len)
{
2020-08-26 17:46:13 +00:00
char tmp_token[MAX_TOKEN_LEN];
2017-09-19 19:29:28 +00:00
tmp_token[0] = '\0';
/* Skip initial track count */
2020-08-26 17:46:13 +00:00
if (intfstream_tell(fd) == 0)
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)); /* Track number */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)); /* Offset */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)); /* Mode */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)); /* Sector size */
2017-09-20 07:55:53 +00:00
/* File name */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
{
char gdi_dir[PATH_MAX_LENGTH];
2017-09-20 07:55:53 +00:00
fill_pathname_basedir(gdi_dir, gdi_path, sizeof(gdi_dir));
fill_pathname_join_special(path, gdi_dir, tmp_token, (size_t)max_len);
2020-06-10 14:24:52 +00:00
/* Disc offset */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
return true;
}
return false;
}