RetroArch/tasks/task_database_cue.c

1403 lines
43 KiB
C
Raw Normal View History

2015-09-16 22:39:47 +02:00
/* RetroArch - A frontend for libretro.
2017-01-22 13:40:32 +01:00
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2014-2017 - Jean-André Santoni
2019-02-22 16:31:54 -05:00
* Copyright (C) 2016-2019 - Brad Parker
2015-09-16 22:39:47 +02: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/>.
*/
2015-09-26 00:44:47 +01:00
#include <ctype.h>
#include <string.h>
2015-09-16 22:39:47 +02:00
2016-09-11 16:31:23 +02:00
#include <retro_miscellaneous.h>
#include <retro_endianness.h>
2015-09-16 22:39:47 +02:00
#include <compat/strcasestr.h>
#include <compat/strl.h>
2016-02-07 01:50:08 +01:00
#include <file/file_path.h>
2015-09-16 22:39:47 +02:00
#include <retro_endianness.h>
2016-03-20 16:29:14 +01:00
#include <streams/file_stream.h>
#include <streams/interface_stream.h>
2016-01-20 04:07:24 +01:00
#include <string/stdstring.h>
2022-08-25 03:42:23 +01:00
#include "task_database_cue.h"
2015-09-16 22:39:47 +02:00
2016-09-11 16:24:02 +02:00
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
2015-09-16 22:39:47 +02:00
#include "../database_info.h"
2016-09-11 16:24:02 +02:00
#include "tasks_internal.h"
2015-09-16 22:39:47 +02:00
#include "../msg_hash.h"
2015-11-23 12:03:38 +01:00
#include "../verbosity.h"
2015-09-16 22:39:47 +02:00
2016-01-28 09:33:09 +01:00
#define MAX_TOKEN_LEN 255
2015-09-16 22:39:47 +02:00
2016-02-04 10:30:12 +01:00
#ifdef MSB_FIRST
#define MODETEST_VAL 0x00ffffff
#else
#define MODETEST_VAL 0xffffff00
#endif
2015-09-16 22:39:47 +02:00
static struct magic_entry MAGIC_NUMBERS[] = {
2022-08-25 03:42:23 +01:00
{ "Nintendo - GameCube", "\xc2\x33\x9f\x3d", 0x00001c},
{ "Nintendo - GameCube", "\xc2\x33\x9f\x3d", 0x000074}, /* RVZ, WIA */
2022-08-25 03:42:23 +01:00
{ "Nintendo - Wii", "\x5d\x1c\x9e\xa3", 0x000018},
{ "Nintendo - Wii", "\x5d\x1c\x9e\xa3", 0x000218}, /* WBFS */
{ "Nintendo - Wii", "\x5d\x1c\x9e\xa3", 0x000070}, /* RVZ, WIA */
2022-08-25 03:42:23 +01:00
{ "Sega - Dreamcast", "SEGA SEGAKATANA", 0x000010},
{ "Sega - Mega-CD - Sega CD", "SEGADISCSYSTEM", 0x000010},
{ "Sega - Saturn", "SEGA SEGASATURN", 0x000010},
{ "Sony - PlayStation", "Sony Computer ", 0x0024f8}, /* PS1 CD license string, PS2 CD doesnt have this string */
{ "Sony - PlayStation 2", "PLAYSTATION", 0x009320}, /* PS1 CD and PS2 CD */
{ "Sony - PlayStation 2", "PLAYSTATION", 0x008008}, /* PS2 DVD */
2022-08-25 03:42:23 +01:00
{ "Sony - PlayStation Portable", "PSP GAME", 0x008008},
{ NULL, NULL, 0}
2015-09-16 22:39:47 +02: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);
}
}
}
2024-12-29 00:25:22 +01:00
static int64_t task_database_cue_get_token(intfstream_t *fd, char *s, uint64_t len)
2015-09-16 22:39:47 +02:00
{
2024-12-29 00:25:22 +01:00
char *c = s;
int64_t _len = 0;
2015-09-16 22:39:47 +02:00
int in_string = 0;
for (;;)
2015-09-16 22:39:47 +02:00
{
int64_t rv = (int64_t)intfstream_read(fd, c, 1);
2015-09-16 22:39:47 +02:00
if (rv == 0)
return 0;
else if (rv < 0)
return -1;
2015-09-18 04:03:11 +02:00
2015-09-16 22:39:47 +02:00
switch (*c)
{
case ' ':
case '\t':
case '\r':
case '\n':
2024-12-29 00:25:22 +01:00
if (c == s)
2015-09-16 22:39:47 +02:00
continue;
if (!in_string)
{
*c = '\0';
2024-12-29 00:25:22 +01:00
return _len;
2015-09-16 22:39:47 +02:00
}
break;
case '\"':
2024-12-29 00:25:22 +01:00
if (c == s)
2015-09-16 22:39:47 +02:00
{
in_string = 1;
continue;
}
*c = '\0';
2024-12-29 00:25:22 +01:00
return _len;
2015-09-16 22:39:47 +02:00
}
2024-12-29 00:25:22 +01:00
_len++;
2015-09-16 22:39:47 +02:00
c++;
2024-12-29 00:25:22 +01:00
if (_len == (int64_t)len)
2015-09-16 22:39:47 +02:00
{
*c = '\0';
2024-12-29 00:25:22 +01:00
return _len;
2015-09-16 22:39:47 +02:00
}
}
}
2023-06-20 20:07:44 +02:00
#define DISC_DATA_SIZE_PS1 60000
int detect_ps1_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
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_"))
|| string_is_equal_fast(raw_game_id, "SLKA_", STRLEN_CONST("SLKA_"))
|| string_is_equal_fast(raw_game_id, "SCAJ_", STRLEN_CONST("SCAJ_"))
)
{
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(raw_game_id, "LSP-", STRLEN_CONST("LSP-")))
{
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(raw_game_id, "PSX.EXE", STRLEN_CONST("PSX.EXE")))
{
raw_game_id[7] = '\0';
string_remove_all_whitespace(s, raw_game_id);
cue_append_multi_disc_suffix(s, filename);
return false;
}
}
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_ps2_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
#define DISC_DATA_SIZE_PS2 0x84000
int pos;
char raw_game_id[50];
char disc_data[DISC_DATA_SIZE_PS2];
/* 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_PS2) <= 0)
return false;
disc_data[DISC_DATA_SIZE_PS2 - 1] = '\0';
for (pos = 0; pos < DISC_DATA_SIZE_PS2; 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, "P", STRLEN_CONST("P"))
|| string_is_equal_fast(raw_game_id, "T", STRLEN_CONST("T"))
|| string_is_equal_fast(raw_game_id, "C", STRLEN_CONST("C"))
|| string_is_equal_fast(raw_game_id, "H", STRLEN_CONST("H"))
|| string_is_equal_fast(raw_game_id, "A", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "V", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "L", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "M", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "N", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "U", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "W", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "G", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "K", STRLEN_CONST("A"))
|| string_is_equal_fast(raw_game_id, "R", STRLEN_CONST("A"))
)
{
if ( string_is_equal_fast(raw_game_id, "SLPM_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLES_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCES_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLUS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLPS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCED_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCUS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCPS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCAJ_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLKA_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCKA_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLAJ_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "TCPS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "KOEI_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "PBPX_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "PCPX_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "PAPX_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCCS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "ALCH_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "TCES_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "CPCS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLED_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "TLES_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "GUST_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "CF00_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCPN_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SCPM_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "PSXC_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SLPN_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "ULKS_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "LDTL_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "PKP2_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "WLFD_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "CZP2_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "HAKU_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "SRPM_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "MTP2_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "NMP2_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "ARZE_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "VUGJ_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "ARP2_", STRLEN_CONST("SLUS_"))
|| string_is_equal_fast(raw_game_id, "ROSE_", STRLEN_CONST("SLUS_"))
)
{
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;
}
}
}
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;
}
2024-12-29 00:25:22 +01:00
size_t detect_gc_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
char region_id;
char pre_game_id[20];
char raw_game_id[20];
2023-06-20 20:07:44 +02:00
size_t _len = 0;
/* Load raw serial or quit */
if (intfstream_seek(fd, 0, SEEK_SET) < 0)
2024-12-29 00:25:22 +01:00
return 0;
if (intfstream_read(fd, raw_game_id, 4) <= 0)
2024-12-29 00:25:22 +01:00
return 0;
2023-06-20 20:07:44 +02:00
if ( string_is_equal_fast(raw_game_id, "RVZ", STRLEN_CONST("RVZ"))
|| string_is_equal_fast(raw_game_id, "WIA", STRLEN_CONST("WIA")))
{
if (intfstream_seek(fd, 0x0058, SEEK_SET) < 0)
2024-12-29 00:25:22 +01:00
return 0;
if (intfstream_read(fd, raw_game_id, 4) <= 0)
2024-12-29 00:25:22 +01:00
return 0;
}
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
2024-12-29 00:25:22 +01:00
return 0;
}
/** 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
2024-10-02 03:36:33 +03:00
only fail with the following exceptions: the
subregions of europe P-UKV, P-AUS, X-UKV, X-EUU
will not match redump.**/
/** insert prefix **/
2024-12-29 00:25:22 +01:00
_len = strlcpy(pre_game_id, "DL-DOL-", sizeof(pre_game_id));
/** add raw serial **/
2023-06-20 20:07:44 +02:00
strlcpy(pre_game_id + _len, raw_game_id, sizeof(pre_game_id) - _len);
2016-06-03 10:46:55 +01: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 10:46:55 +01:00
switch (region_id)
2016-06-03 10:46:55 +01:00
{
case 'E':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-USA", len - _len);
return _len;
case 'J':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-JPN", len - _len);
return _len;
case 'P': /** NYI: P can also be P-UKV, P-AUS **/
case 'X': /** NYI: X can also be X-UKV, X-EUU **/
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-EUR", len - _len);
return _len;
case 'Y':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-FAH", len - _len);
return _len;
case 'D':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-NOE", len - _len);
return _len;
case 'S':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-ESP", len - _len);
return _len;
case 'F':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-FRA", len - _len);
return _len;
case 'I':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-ITA", len - _len);
return _len;
case 'H':
2024-12-29 00:25:22 +01:00
_len += strlcpy(s + _len, "-HOL", len - _len);
return _len;
default:
break;
}
2024-12-29 00:25:22 +01:00
return 0;
}
int detect_scd_game(intfstream_t *fd, char *s, size_t len, const char *filename)
{
2022-08-25 03:42:23 +01:00
#define SCD_SERIAL_OFFSET 0x0193
#define SCD_SERIAL_LEN 11
#define SCD_REGION_OFFSET 0x0200
size_t _len;
2022-08-25 03:42:23 +01:00
char pre_game_id[SCD_SERIAL_LEN+1];
char raw_game_id[SCD_SERIAL_LEN+1];
char check_suffix_50[10];
2022-08-25 03:42:23 +01:00
char region_id;
size_t length;
size_t lengthref;
int index;
char lgame_id[10];
/* Load raw serial or quit */
2022-08-25 03:42:23 +01:00
if (intfstream_seek(fd, SCD_SERIAL_OFFSET, SEEK_SET) < 0)
return false;
2022-08-25 03:42:23 +01:00
if (intfstream_read(fd, raw_game_id, SCD_SERIAL_LEN) <= 0)
return false;
2022-08-25 03:42:23 +01:00
raw_game_id[SCD_SERIAL_LEN] = '\0';
/* Load raw region id or quit */
2022-08-25 03:42:23 +01:00
if (intfstream_seek(fd, SCD_REGION_OFFSET, SEEK_SET) < 0)
return false;
2022-08-25 03:42:23 +01:00
if (intfstream_read(fd, &region_id, 1) <= 0)
return false;
#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 **/
/** Dissect this pre serial into parts **/
length = strlen(pre_game_id);
lengthref = length - 2;
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 ( pre_game_id[0] == 'T'
2023-07-16 18:07:49 +02:00
&& pre_game_id[1] == '-')
{
2022-08-25 03:42:23 +01:00
if (region_id == 'U' || 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;
}
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] = '5';
s[++_len] = '0';
s[++_len] = '\0';
cue_append_multi_disc_suffix(s, filename);
return true;
}
else if (pre_game_id[0] == 'G'
2023-07-16 18:07:49 +02:00
&& pre_game_id[1] == '-')
{
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 (pre_game_id[0] == 'M'
&& pre_game_id[1] == 'K'
&& pre_game_id[2] == '-')
{
if ( check_suffix_50[0] == '5'
&& check_suffix_50[1] == '0')
{
strncpy(lgame_id, &pre_game_id[3], 4);
lgame_id[4] = '\0';
_len = strlcat(s, lgame_id, len);
s[ _len] = '-';
s[++_len] = '5';
s[++_len] = '0';
s[++_len] = '\0';
}
2023-06-20 20:07:44 +02:00
else
{
strncpy(s, &pre_game_id[3], 4);
s[4] = '\0';
}
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
2024-12-23 00:51:01 +01:00
string_trim_whitespace_right(raw_game_id);
string_trim_whitespace_left(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)
{
2022-08-25 03:42:23 +01:00
#define SAT_SERIAL_OFFSET 0x0030
#define SAT_SERIAL_LEN 9
#define SAT_REGION_OFFSET 0x0050
size_t _len, length;
2022-08-25 03:42:23 +01:00
char raw_game_id[SAT_SERIAL_LEN+1];
char region_id;
char check_suffix_5[10];
char check_suffix_50[10];
char lgame_id[10];
char rgame_id[10];
/* Load raw serial or quit */
2022-08-25 03:42:23 +01:00
if (intfstream_seek(fd, SAT_SERIAL_OFFSET, SEEK_SET) < 0)
return false;
2022-08-25 03:42:23 +01:00
if (intfstream_read(fd, raw_game_id, SAT_SERIAL_LEN) <= 0)
return false;
2022-08-25 03:42:23 +01:00
raw_game_id[SAT_SERIAL_LEN] = '\0';
/* Load raw region id or quit */
2022-08-25 03:42:23 +01:00
if (intfstream_seek(fd, SAT_REGION_OFFSET, SEEK_SET) < 0)
return false;
2022-08-25 03:42:23 +01:00
if (intfstream_read(fd, &region_id, 1) <= 0)
return false;
/** 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;
}
2024-12-23 00:51:01 +01:00
string_trim_whitespace_right(raw_game_id);
string_trim_whitespace_left(raw_game_id);
/** Dissect this raw serial into parts **/
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 ( raw_game_id[0] == 'M'
&& raw_game_id[1] == 'K'
&& raw_game_id[2] == '-')
{
strncpy(s, &raw_game_id[3], length - 3);
s[length - 3] = '\0';
}
else
strlcpy(s, raw_game_id, len);
2023-06-20 20:07:44 +02:00
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")
|| !strcmp(check_suffix_50, "50"))
{
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';
}
2023-06-20 20:07:44 +02:00
_len = strlcat(s, lgame_id, len);
_len += strlcpy(s + _len, rgame_id, len - _len);
s[ _len] = '-';
s[++_len] = '5';
s[++_len] = '0';
s[++_len] = '\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 22:39:47 +02:00
int detect_dc_game(intfstream_t *fd, char *s, size_t len, const char *filename)
2015-09-27 00:12:17 +02:00
{
size_t _len;
int total_hyphens;
int total_hyphens_recalc;
char pre_game_id[50];
char raw_game_id[50];
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'
2023-06-20 20:07:44 +02:00
|| raw_game_id[0] == ' ')
2015-09-27 00:12:17 +02:00
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
return false;
}
2015-09-27 00:12:17 +02:00
2024-12-23 00:51:01 +01:00
string_trim_whitespace_right(raw_game_id);
string_trim_whitespace_left(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, '-');
/** redump serials are built differently for each prefix **/
if ( raw_game_id[0] == 'T'
&& raw_game_id[1] == '-')
{
if (total_hyphens >= 2)
2015-09-27 00:12:17 +02: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] = '\0';
strlcpy(s + _len, rgame_id, len - _len);
}
2023-06-20 20:07:44 +02:00
else if (length <= 7)
{
strncpy(s, raw_game_id, 7);
s[7] = '\0';
}
2023-06-20 20:07:44 +02:00
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] = '\0';
strlcpy(s + _len, rgame_id, len - _len);
}
cue_append_multi_disc_suffix(s, filename);
return true;
}
else if (raw_game_id[0] == '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] = '\0';
strlcpy(pre_game_id + _len, rgame_id, sizeof(pre_game_id) - _len);
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);
}
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;
}
strncpy(lgame_id, pre_game_id, 7);
lgame_id[7] = '\0';
2015-09-27 00:12:17 +02:00
}
2023-06-20 20:07:44 +02:00
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] = '\0';
strlcpy(s + _len, rgame_id, len - _len);
cue_append_multi_disc_suffix(s, filename);
return true;
}
else if (raw_game_id[0] == 'H'
&& raw_game_id[1] == 'D'
&& raw_game_id[2] == 'R'
&& raw_game_id[3] == '-')
{
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] = '\0';
strlcpy(s + _len, rgame_id, len - _len);
}
2023-06-20 20:07:44 +02:00
else
strlcpy(s, raw_game_id, len);
cue_append_multi_disc_suffix(s, filename);
return true;
2015-09-27 00:12:17 +02:00
}
else if (raw_game_id[0] == 'M'
&& raw_game_id[1] == 'K'
&& raw_game_id[2] == '-')
{
if (length <= 8)
{
/* For 8 chars serials in 'MK-xxxxx' format, we need to remove 'MK-' to match Redump database
* Sega GT being the only exception (MK-51053), we have to check if it's not that game first */
if (string_is_not_equal_fast(raw_game_id, "MK-51053", STRLEN_CONST("MK-51053")))
{
strncpy(s, raw_game_id + 3, 5);
s[5] = '\0';
}
2023-06-20 20:07:44 +02:00
else
{
strncpy(s, raw_game_id, 8);
s[8] = '\0';
}
}
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] = '\0';
strlcpy(s + _len, rgame_id, len - _len);
}
cue_append_multi_disc_suffix(s, filename);
return true;
}
else
{
strlcpy(s, raw_game_id, len);
return true;
}
return false;
2015-09-27 00:12:17 +02:00
}
2024-12-29 00:25:22 +01:00
size_t detect_wii_game(intfstream_t *fd, char *s, size_t len, const char *filename)
2017-10-27 14:58:19 +07:00
{
char raw_game_id[15];
/* Load raw serial or quit */
if (intfstream_seek(fd, 0x0000, SEEK_SET) < 0)
2024-12-29 00:25:22 +01:00
return 0;
2017-10-27 14:58:19 +07:00
if (intfstream_read(fd, raw_game_id, 6) <= 0)
2024-12-29 00:25:22 +01:00
return 0;
if (string_is_equal_fast(raw_game_id, "WBFS", STRLEN_CONST("WBFS")))
{
if (intfstream_seek(fd, 0x0200, SEEK_SET) < 0)
2024-12-29 00:25:22 +01:00
return 0;
if (intfstream_read(fd, raw_game_id, 6) <= 0)
2024-12-29 00:25:22 +01:00
return 0;
}
2023-06-20 20:07:44 +02:00
if ( string_is_equal_fast(raw_game_id, "RVZ", STRLEN_CONST("RVZ"))
|| string_is_equal_fast(raw_game_id, "WIA", STRLEN_CONST("WIA")))
{
if (intfstream_seek(fd, 0x0058, SEEK_SET) < 0)
2024-12-29 00:25:22 +01:00
return 0;
if (intfstream_read(fd, raw_game_id, 6) <= 0)
2024-12-29 00:25:22 +01:00
return 0;
}
raw_game_id[6] = '\0';
/** Scrub files with bad data and log **/
if ( raw_game_id[0] == '\0'
2023-06-20 20:07:44 +02:00
|| raw_game_id[0] == ' ')
2017-10-27 14:58:19 +07:00
{
#ifdef DEBUG
RARCH_LOG("[Scanner]: Scrubbing: %s\n", filename);
#endif
2024-12-29 00:25:22 +01:00
return 0;
2017-10-27 14:58:19 +07:00
}
cue_append_multi_disc_suffix(s, filename);
2024-12-29 00:25:22 +01:00
return strlcpy(s, raw_game_id, len);
2017-10-27 14:58:19 +07:00
}
#if 0
2017-07-29 23:23:05 -04:00
/**
* Check for an ASCII serial in the first few bits of the ISO (Wii).
* TODO/FIXME - unused for now
2017-07-29 23:23:05 -04:00
*/
static int detect_serial_ascii_game(intfstream_t *fd, char *s, size_t len)
2017-07-29 23:23:05 -04:00
{
unsigned pos;
2020-06-10 16:24:52 +02:00
int number_of_ascii = 0;
bool rv = false;
2017-07-29 23:23:05 -04:00
for (pos = 0; pos < 10000; pos++)
{
intfstream_seek(fd, pos, SEEK_SET);
if (intfstream_read(fd, s, 15) > 0)
2017-07-29 23:23:05 -04:00
{
2017-07-30 10:11:41 +02:00
unsigned i;
s[15] = '\0';
2020-06-10 16:24:52 +02:00
number_of_ascii = 0;
2017-07-29 23:23:05 -04:00
/* When scanning WBFS files, "WBFS" is discovered as the first serial. Ignore it. */
if (string_is_equal(s, "WBFS"))
continue;
2017-07-30 10:11:41 +02: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, - */
2023-06-20 20:07:44 +02:00
if ( (s[i] == 45)
|| (s[i] >= 48 && s[i] <= 57)
|| (s[i] >= 65 && s[i] <= 90))
2020-06-10 16:24:52 +02:00
number_of_ascii++;
2017-07-30 10:11:41 +02:00
else
2017-07-29 23:23:05 -04:00
break;
}
/* If the length of the text is between 3 and 9 characters,
2020-06-10 16:24:52 +02:00
* it could be a serial. */
if (number_of_ascii > 3 && number_of_ascii < 9)
2017-07-30 10:11:41 +02:00
{
/* Cut the string off, and return it as a valid serial. */
s[number_of_ascii] = '\0';
2020-06-10 16:24:52 +02:00
rv = true;
2017-07-29 23:23:05 -04:00
break;
}
}
}
return rv;
}
#endif
2017-07-29 23:23:05 -04:00
int detect_system(intfstream_t *fd, const char **system_name, const char * filename)
2015-09-16 22:39:47 +02:00
{
int i;
char magic[50];
#ifdef DEBUG
RARCH_LOG("[Scanner]: %s\n", msg_hash_to_str(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS));
#endif
2015-09-16 22:39:47 +02: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 22:39:47 +02:00
{
size_t magic_len = strlen(MAGIC_NUMBERS[i].magic);
2022-08-25 03:42:23 +01:00
if (intfstream_read(fd, magic, magic_len) > 0)
{
2022-08-25 03:42:23 +01:00
magic[magic_len] = '\0';
if (memcmp(MAGIC_NUMBERS[i].magic, magic, magic_len) == 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 22:39:47 +02:00
}
static int64_t intfstream_get_file_size(const char *path)
{
int64_t rv;
2017-12-11 13:21:44 +01:00
intfstream_t *fd = intfstream_open_file(path,
2017-12-11 12:53:47 +01:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
2017-12-11 13:21:44 +01:00
if (!fd)
return -1;
2017-12-11 13:21:44 +01:00
rv = intfstream_get_size(fd);
intfstream_close(fd);
2017-12-11 20:24:00 +01:00
free(fd);
return rv;
}
static bool update_cand(int64_t *cand_index, int64_t *last_index,
2020-06-10 16:24:52 +02:00
uint64_t *largest, char *last_file, uint64_t *offset,
2024-12-29 00:25:22 +01:00
size_t *size, char *s, size_t len)
{
2018-04-09 19:30:36 +02:00
if (*cand_index != -1)
{
if ((uint64_t)(*last_index - *cand_index) > *largest)
2018-04-09 19:30:36 +02:00
{
2024-12-29 00:25:22 +01:00
size_t _len;
2018-04-09 19:30:36 +02:00
*largest = *last_index - *cand_index;
2024-12-29 00:25:22 +01:00
_len = strlcpy(s, last_file, len);
2018-04-09 19:30:36 +02:00
*offset = *cand_index;
*size = (size_t)*largest;
2018-04-09 19:30:36 +02:00
*cand_index = -1;
2024-12-29 00:25:22 +01:00
return _len;
2018-04-09 19:30:36 +02:00
}
2020-06-10 16:24:52 +02:00
*cand_index = -1;
2018-04-09 19:30:36 +02:00
}
2024-12-29 00:25:22 +01:00
return 0;
}
2017-09-17 17:39:41 -07:00
int cue_find_track(const char *cue_path, bool first,
uint64_t *offset, size_t *size, char *s, size_t len)
2015-09-16 22:39:47 +02:00
{
2016-06-30 06:02:30 +02:00
int rv;
intfstream_info_t info;
2020-08-26 19:46:13 +02:00
char tmp_token[MAX_TOKEN_LEN];
char last_file[PATH_MAX_LENGTH];
char cue_dir[DIR_MAX_LENGTH];
2017-09-19 21:29:28 +02:00
intfstream_t *fd = NULL;
int64_t last_index = -1;
int64_t cand_index = -1;
2017-09-19 21:29:28 +02:00
int32_t cand_track = -1;
int32_t track = 0;
uint64_t largest = 0;
int64_t volatile file_size = -1;
2017-09-19 21:29:28 +02:00
bool is_data = false;
cue_dir[0] = last_file[0] = '\0';
2017-09-17 17:46:02 -07:00
fill_pathname_basedir(cue_dir, cue_path, sizeof(cue_dir));
2020-06-10 16:24:52 +02:00
info.type = INTFSTREAM_FILE;
2015-09-16 22:39:47 +02:00
if (!(fd = (intfstream_t*)intfstream_init(&info)))
goto error;
2017-12-11 12:15:00 +01:00
if (!intfstream_open(fd, cue_path,
2017-12-11 12:53:47 +01:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE))
2015-09-16 22:39:47 +02:00
{
#ifdef DEBUG
RARCH_LOG("Could not open CUE file '%s'\n", cue_path);
#endif
goto error;
2015-09-16 22:39:47 +02:00
}
#ifdef DEBUG
2015-09-16 22:39:47 +02:00
RARCH_LOG("Parsing CUE file '%s'...\n", cue_path);
#endif
2015-09-16 22:39:47 +02:00
2016-10-09 09:56:03 +02:00
tmp_token[0] = '\0';
rv = -1;
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
2015-09-16 22:39:47 +02:00
{
2019-08-01 19:16:18 -04:00
if (string_is_equal_noncase(tmp_token, "FILE"))
2015-09-16 22:39:47 +02:00
{
/* Set last index to last EOF */
2018-04-09 19:30:36 +02:00
if (file_size != -1)
last_index = file_size;
/* We're changing files since the candidate, update it */
2020-06-10 16:24:52 +02:00
if (update_cand(&cand_index, &last_index,
&largest, last_file, offset,
size, s, len))
2018-04-09 19:30:36 +02:00
{
rv = 0;
2018-04-09 19:30:36 +02: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 13:21:44 +01:00
file_size = intfstream_get_file_size(last_file);
2015-09-16 22:39:47 +02:00
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
}
2019-08-01 19:16:18 -04: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 19:16:18 -04:00
is_data = !string_is_equal_noncase(tmp_token, "AUDIO");
++track;
2018-04-09 19:30:36 +02:00
}
2019-08-01 19:16:18 -04:00
else if (string_is_equal_noncase(tmp_token, "INDEX"))
2018-04-09 19:30:36 +02: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 22:39:47 +02:00
/* If we've changed tracks since the candidate, update it */
2023-06-20 20:07:44 +02:00
if ( (cand_track != -1)
&& (track != cand_track)
&& update_cand(&cand_index, &last_index, &largest,
2020-06-10 16:24:52 +02:00
last_file, offset,
size, s, len))
2018-04-09 19:30:36 +02:00
{
rv = 0;
2018-04-09 19:30:36 +02:00
if (first)
goto clean;
}
2018-04-09 19:30:36 +02:00
if (!is_data)
continue;
2018-04-09 19:30:36 +02:00
if (cand_index == -1)
{
cand_index = last_index;
cand_track = track;
}
2015-09-16 22:39:47 +02:00
}
}
2018-04-09 19:30:36 +02:00
if (file_size != -1)
last_index = file_size;
2020-06-10 16:24:52 +02:00
if (update_cand(&cand_index, &last_index,
&largest, last_file, offset,
size, s, len))
rv = 0;
2015-09-16 22:39:47 +02:00
clean:
intfstream_close(fd);
2017-09-22 00:03:40 +02:00
free(fd);
2015-09-16 22:39:47 +02:00
return rv;
error:
if (fd)
2017-09-21 23:38:29 +02:00
{
intfstream_close(fd);
2017-09-21 23:38:29 +02:00
free(fd);
}
return -1;
2015-09-16 22:39:47 +02:00
}
2017-09-17 17:39:41 -07:00
bool cue_next_file(intfstream_t *fd,
const char *cue_path, char *s, uint64_t len)
{
2020-08-26 19:46:13 +02:00
char tmp_token[MAX_TOKEN_LEN];
tmp_token[0] = '\0';
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
{
2019-08-01 19:16:18 -04:00
if (string_is_equal_noncase(tmp_token, "FILE"))
{
2024-12-29 00:25:22 +01:00
char cue_dir[DIR_MAX_LENGTH];
fill_pathname_basedir(cue_dir, cue_path, sizeof(cue_dir));
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;
}
2024-12-30 10:13:05 +01:00
int gdi_find_track(const char *gdi_path, bool first, char *s, size_t len)
2017-09-17 17:39:41 -07:00
{
intfstream_info_t info;
2020-08-26 19:46:13 +02:00
char tmp_token[MAX_TOKEN_LEN];
2017-09-19 21:29:28 +02:00
intfstream_t *fd = NULL;
uint64_t largest = 0;
int rv = -1;
2017-09-19 21:29:28 +02:00
int size = -1;
int mode = -1;
int64_t file_size = -1;
2017-09-17 17:39:41 -07:00
2017-09-19 21:29:28 +02:00
info.type = INTFSTREAM_FILE;
if (!(fd = (intfstream_t*)intfstream_init(&info)))
2017-09-17 17:39:41 -07:00
goto error;
2017-12-11 12:15:00 +01:00
if (!intfstream_open(fd, gdi_path,
2017-12-11 12:53:47 +01:00
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE))
2017-09-17 17:39:41 -07:00
{
#ifdef DEBUG
RARCH_LOG("Could not open GDI file '%s'\n", gdi_path);
#endif
2017-09-17 17:39:41 -07:00
goto error;
}
#ifdef DEBUG
2017-09-17 17:39:41 -07:00
RARCH_LOG("Parsing GDI file '%s'...\n", gdi_path);
#endif
2017-09-17 17:39:41 -07:00
tmp_token[0] = '\0';
/* Skip track count */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
2017-09-17 17:39:41 -07:00
/* Track number */
while (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
2017-09-17 17:39:41 -07:00
{
/* Offset */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-17 17:39:41 -07:00
goto error;
/* Mode */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-17 17:39:41 -07:00
goto error;
2020-06-10 16:24:52 +02:00
2017-09-17 17:39:41 -07:00
mode = atoi(tmp_token);
/* Sector size */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-17 17:39:41 -07:00
goto error;
2020-06-10 16:24:52 +02:00
2017-09-17 17:39:41 -07:00
size = atoi(tmp_token);
/* File name */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-17 17:39:41 -07:00
goto error;
/* Check for data track */
if (!(mode == 0 && size == 2352))
{
char last_file[PATH_MAX_LENGTH];
char gdi_dir[DIR_MAX_LENGTH];
2017-09-20 09:55:53 +02: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-17 17:39:41 -07:00
goto error;
2017-09-19 21:29:28 +02:00
if ((uint64_t)file_size > largest)
2017-09-17 17:39:41 -07:00
{
strlcpy(s, last_file, len);
rv = 0;
2017-09-17 17:39:41 -07:00
largest = file_size;
2017-09-17 17:39:41 -07:00
if (first)
goto clean;
}
}
/* Disc offset (not used?) */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) <= 0)
2017-09-17 17:39:41 -07:00
goto error;
}
clean:
intfstream_close(fd);
2017-09-20 12:18:31 +02:00
free(fd);
2017-09-17 17:39:41 -07:00
return rv;
error:
if (fd)
2017-09-20 12:18:31 +02:00
{
2017-09-17 17:39:41 -07:00
intfstream_close(fd);
2017-09-20 12:18:31 +02:00
free(fd);
}
return -1;
2017-09-17 17:39:41 -07:00
}
size_t gdi_next_file(intfstream_t *fd, const char *gdi_path,
char *s, size_t len)
{
2020-08-26 19:46:13 +02:00
char tmp_token[MAX_TOKEN_LEN];
2017-09-19 21:29:28 +02:00
tmp_token[0] = '\0';
/* Skip initial track count */
2020-08-26 19:46:13 +02: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 09:55:53 +02:00
/* File name */
if (task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token)) > 0)
{
size_t _len;
char gdi_dir[DIR_MAX_LENGTH];
fill_pathname_basedir(gdi_dir, gdi_path, sizeof(gdi_dir));
_len = fill_pathname_join_special(s, gdi_dir, tmp_token, len);
2020-06-10 16:24:52 +02:00
/* Disc offset */
task_database_cue_get_token(fd, tmp_token, sizeof(tmp_token));
return _len;
}
return 0;
}