task_database: rework cd scanning

Support chd files.  Fall back to using the CRC32 of the last track
of CDs if a serial number is not available.  This requires new
database files, but is necessary for chd scanning to work.
This commit is contained in:
Brian Koropoff 2017-09-16 22:31:58 -07:00
parent 08ce00d798
commit 5a3969d4fa
2 changed files with 196 additions and 93 deletions

View File

@ -24,6 +24,8 @@
#include <file/file_path.h>
#include <encodings/crc32.h>
#include <streams/file_stream.h>
#include <streams/chd_stream.h>
#include <streams/interface_stream.h>
#include "tasks_internal.h"
#include "../database_info.h"
@ -69,13 +71,60 @@ typedef struct db_handle
int find_first_data_track(const char* cue_path,
int32_t* offset, char* track_path, size_t max_len);
int detect_system(const char* track_path, const char** system_name);
int detect_system(intfstream_t *fd, const char** system_name);
int detect_ps1_game(const char *track_path, char *game_id);
int detect_ps1_game(intfstream_t *fd, char *game_id);
int detect_psp_game(const char *track_path, char *game_id);
int detect_psp_game(intfstream_t *fd, char *game_id);
int detect_serial_ascii_game(const char *track_path, char *game_id);
int detect_serial_ascii_game(intfstream_t *fd, char *game_id);
static intfstream_t*
open_file(const char *path)
{
intfstream_info_t info;
intfstream_t *fd = NULL;
info.type = INTFSTREAM_FILE;
fd = intfstream_init(&info);
if (!fd)
{
return NULL;
}
if (!intfstream_open(fd, path, RFILE_MODE_READ, -1))
{
intfstream_close(fd);
return NULL;
}
return fd;
}
static intfstream_t*
open_chd_track(const char *path, int32_t track)
{
intfstream_info_t info;
intfstream_t *fd = NULL;
info.type = INTFSTREAM_CHD;
info.chd.track = track;
fd = intfstream_init(&info);
if (!fd)
{
return NULL;
}
if (!intfstream_open(fd, path, RFILE_MODE_READ, -1))
{
intfstream_close(fd);
return NULL;
}
return fd;
}
static void database_info_set_type(database_info_handle_t *handle, enum database_type type)
{
@ -137,16 +186,16 @@ static int task_database_iterate_start(database_info_handle_t *db,
return 0;
}
static int iso_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, const char *name, char* serial)
static int stream_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, intfstream_t *fd, char* serial)
{
const char* system_name = NULL;
/* Check if the system was not auto-detected. */
if (detect_system(name, &system_name) < 0)
if (detect_system(fd, &system_name) < 0)
{
/* Attempt to read an ASCII serial, like Wii. */
if (detect_serial_ascii_game(name, serial))
if (detect_serial_ascii_game(fd, serial))
{
/* ASCII serial (Wii) was detected. */
RARCH_LOG("%s '%s'\n", msg_hash_to_str(MSG_FOUND_DISK_LABEL), serial);
@ -159,18 +208,38 @@ static int iso_get_serial(database_state_handle_t *db_state,
if (string_is_equal_fast(system_name, "psp", 3))
{
if (detect_psp_game(name, serial) == 0)
if (detect_psp_game(fd, serial) == 0)
return 0;
RARCH_LOG("%s '%s'\n", msg_hash_to_str(MSG_FOUND_DISK_LABEL), serial);
}
else if (string_is_equal_fast(system_name, "ps1", 3))
{
if (detect_ps1_game(name, serial) == 0)
if (detect_ps1_game(fd, serial) == 0)
return 0;
RARCH_LOG("%s '%s'\n", msg_hash_to_str(MSG_FOUND_DISK_LABEL), serial);
}
else
{
return 0;
}
return 0;
return 1;
}
static int iso_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, const char *name, char* serial)
{
intfstream_t *fd = open_file(name);
int rv;
if (!fd)
{
return 0;
}
rv = stream_get_serial(db_state, db, fd, serial);
intfstream_close(fd);
return rv;
}
static int cue_get_serial(database_state_handle_t *db_state,
@ -193,7 +262,7 @@ static int cue_get_serial(database_state_handle_t *db_state,
msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK),
strerror(-rv));
free(track_path);
return rv;
return 0;
}
RARCH_LOG("%s\n", msg_hash_to_str(MSG_READING_FIRST_DATA_TRACK));
@ -204,6 +273,23 @@ static int cue_get_serial(database_state_handle_t *db_state,
return ret;
}
static int chd_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, const char *name, char* serial)
{
intfstream_t *fd = NULL;
int result;
fd = open_chd_track(name, CHDSTREAM_TRACK_FIRST_DATA);
if (!fd)
{
return 0;
}
result = stream_get_serial(db_state, db, fd, serial);
intfstream_close(fd);
return result;
}
static bool file_get_crc(database_state_handle_t *db_state,
const char *name, uint32_t *crc)
{
@ -219,6 +305,37 @@ static bool file_get_crc(database_state_handle_t *db_state,
return 1;
}
static bool chd_get_crc(database_state_handle_t *db_state,
const char *name, uint32_t *crc)
{
intfstream_t *fd = NULL;
int result;
uint32_t acc = 0;
uint8_t buffer[4096];
ssize_t size;
fd = open_chd_track(name, CHDSTREAM_TRACK_FIRST_DATA);
if (!fd)
{
return 0;
}
while ((size = intfstream_read(fd, buffer, sizeof(buffer))) > 0)
{
acc = encoding_crc32(acc, buffer, size);
}
if (size < 0)
{
return 0;
}
RARCH_LOG("CHD '%s' crc: %x\n", name, acc);
*crc = acc;
return 1;
}
static int task_database_iterate_playlist(
database_state_handle_t *db_state,
database_info_handle_t *db, const char *name)
@ -235,14 +352,33 @@ static int task_database_iterate_playlist(
#endif
case FILE_TYPE_CUE:
db_state->serial[0] = '\0';
cue_get_serial(db_state, db, name, db_state->serial);
database_info_set_type(db, DATABASE_TYPE_SERIAL_LOOKUP);
if (cue_get_serial(db_state, db, name, db_state->serial))
{
database_info_set_type(db, DATABASE_TYPE_SERIAL_LOOKUP);
}
else
{
database_info_set_type(db, DATABASE_TYPE_CRC_LOOKUP);
return file_get_crc(db_state, name, &db_state->crc);
}
break;
case FILE_TYPE_ISO:
db_state->serial[0] = '\0';
iso_get_serial(db_state, db, name, db_state->serial);
database_info_set_type(db, DATABASE_TYPE_SERIAL_LOOKUP);
break;
case FILE_TYPE_CHD:
db_state->serial[0] = '\0';
if (chd_get_serial(db_state, db, name, db_state->serial))
{
database_info_set_type(db, DATABASE_TYPE_SERIAL_LOOKUP);
}
else
{
database_info_set_type(db, DATABASE_TYPE_CRC_LOOKUP);
return chd_get_crc(db_state, name, &db_state->crc);
}
break;
case FILE_TYPE_LUTRO:
database_info_set_type(db, DATABASE_TYPE_ITERATE_LUTRO);
break;

View File

@ -25,6 +25,7 @@
#include <file/file_path.h>
#include <retro_endianness.h>
#include <streams/file_stream.h>
#include <streams/interface_stream.h>
#include <string/stdstring.h>
#ifdef HAVE_CONFIG_H
@ -63,7 +64,7 @@ static struct magic_entry MAGIC_NUMBERS[] = {
{ 0, NULL, NULL}
};
static ssize_t get_token(RFILE *fd, char *token, size_t max_len)
static ssize_t get_token(intfstream_t *fd, char *token, size_t max_len)
{
char *c = token;
ssize_t len = 0;
@ -71,7 +72,7 @@ static ssize_t get_token(RFILE *fd, char *token, size_t max_len)
while (1)
{
int rv = (int)filestream_read(fd, c, 1);
int rv = (int)intfstream_read(fd, c, 1);
if (rv == 0)
return 0;
@ -123,7 +124,7 @@ static ssize_t get_token(RFILE *fd, char *token, size_t max_len)
}
}
static int find_token(RFILE *fd, const char *token)
static int find_token(intfstream_t *fd, const char *token)
{
int tmp_len = (int)strlen(token);
char *tmp_token = (char*)calloc(tmp_len+1, 1);
@ -146,30 +147,26 @@ static int find_token(RFILE *fd, const char *token)
}
static int detect_ps1_game_sub(const char *track_path,
static int detect_ps1_game_sub(intfstream_t *fp,
char *game_id, int sub_channel_mixed)
{
uint8_t* tmp;
uint8_t* boot_file;
int skip, frame_size, is_mode1, cd_sector;
uint8_t buffer[2048 * 2];
RFILE *fp =
filestream_open(track_path, RFILE_MODE_READ, -1);
if (!fp)
return 0;
buffer[0] = '\0';
is_mode1 = 0;
filestream_seek(fp, 0, SEEK_END);
intfstream_seek(fp, 0, SEEK_END);
if (!sub_channel_mixed)
{
if (!(filestream_tell(fp) & 0x7FF))
if (!(intfstream_tell(fp) & 0x7FF))
{
unsigned int mode_test = 0;
filestream_seek(fp, 0, SEEK_SET);
filestream_read(fp, &mode_test, 4);
intfstream_seek(fp, 0, SEEK_SET);
intfstream_read(fp, &mode_test, 4);
if (mode_test != MODETEST_VAL)
is_mode1 = 1;
}
@ -178,12 +175,12 @@ static int detect_ps1_game_sub(const char *track_path,
skip = is_mode1? 0: 24;
frame_size = sub_channel_mixed? 2448: is_mode1? 2048: 2352;
filestream_seek(fp, 156 + skip + 16 * frame_size, SEEK_SET);
filestream_read(fp, buffer, 6);
intfstream_seek(fp, 156 + skip + 16 * frame_size, SEEK_SET);
intfstream_read(fp, buffer, 6);
cd_sector = buffer[2] | (buffer[3] << 8) | (buffer[4] << 16);
filestream_seek(fp, skip + cd_sector * frame_size, SEEK_SET);
filestream_read(fp, buffer, 2048 * 2);
intfstream_seek(fp, skip + cd_sector * frame_size, SEEK_SET);
intfstream_read(fp, buffer, 2048 * 2);
tmp = buffer;
while (tmp < (buffer + 2048 * 2))
@ -201,8 +198,8 @@ static int detect_ps1_game_sub(const char *track_path,
goto error;
cd_sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
filestream_seek(fp, skip + cd_sector * frame_size, SEEK_SET);
filestream_read(fp, buffer, 256);
intfstream_seek(fp, skip + cd_sector * frame_size, SEEK_SET);
intfstream_read(fp, buffer, 256);
buffer[256] = '\0';
tmp = buffer;
@ -240,41 +237,30 @@ static int detect_ps1_game_sub(const char *track_path,
*game_id = 0;
filestream_close(fp);
return 1;
error:
filestream_close(fp);
return 0;
}
int detect_ps1_game(const char *track_path, char *game_id)
int detect_ps1_game(intfstream_t *fd, char *game_id)
{
if (detect_ps1_game_sub(track_path, game_id, 0))
if (detect_ps1_game_sub(fd, game_id, 0))
return 1;
return detect_ps1_game_sub(track_path, game_id, 1);
return detect_ps1_game_sub(fd, game_id, 1);
}
int detect_psp_game(const char *track_path, char *game_id)
int detect_psp_game(intfstream_t *fd, char *game_id)
{
unsigned pos;
bool rv = false;
RFILE *fd = filestream_open(track_path, RFILE_MODE_READ, -1);
if (!fd)
{
RARCH_LOG("%s: %s\n",
msg_hash_to_str(MSG_COULD_NOT_OPEN_DATA_TRACK),
strerror(errno));
return -errno;
}
for (pos = 0; pos < 100000; pos++)
{
filestream_seek(fd, pos, SEEK_SET);
intfstream_seek(fd, pos, SEEK_SET);
if (filestream_read(fd, game_id, 5) > 0)
if (intfstream_read(fd, game_id, 5) > 0)
{
game_id[5] = '\0';
@ -306,8 +292,8 @@ int detect_psp_game(const char *track_path, char *game_id)
|| (string_is_equal(game_id, "NPJZ-"))
)
{
filestream_seek(fd, pos, SEEK_SET);
if (filestream_read(fd, game_id, 10) > 0)
intfstream_seek(fd, pos, SEEK_SET);
if (intfstream_read(fd, game_id, 10) > 0)
{
#if 0
game_id[4] = '-';
@ -324,34 +310,22 @@ int detect_psp_game(const char *track_path, char *game_id)
break;
}
filestream_close(fd);
return rv;
}
/**
* Check for an ASCII serial in the first few bits of the ISO (Wii).
*/
int detect_serial_ascii_game(const char *track_path, char *game_id)
int detect_serial_ascii_game(intfstream_t *fd, char *game_id)
{
unsigned pos;
int numberOfAscii = 0;
bool rv = false;
RFILE *fd = filestream_open(track_path, RFILE_MODE_READ, -1);
/* Attempt to load the file. */
if (!fd)
{
RARCH_LOG("%s: %s\n",
msg_hash_to_str(MSG_COULD_NOT_OPEN_DATA_TRACK),
strerror(errno));
return -errno;
}
for (pos = 0; pos < 10000; pos++)
{
filestream_seek(fd, pos, SEEK_SET);
/* Current logic only requires 15 characters (max of 4096 per sizeof game_id). */
if (filestream_read(fd, game_id, 15) > 0)
intfstream_seek(fd, pos, SEEK_SET);
if (intfstream_read(fd, game_id, 15) > 0)
{
unsigned i;
game_id[15] = '\0';
@ -378,34 +352,24 @@ int detect_serial_ascii_game(const char *track_path, char *game_id)
}
}
filestream_close(fd);
return rv;
}
int detect_system(const char *track_path, const char **system_name)
int detect_system(intfstream_t *fd, const char **system_name)
{
int rv;
char magic[MAGIC_LEN];
int i;
RFILE *fd = filestream_open(track_path, RFILE_MODE_READ, -1);
if (!fd)
{
RARCH_LOG("Could not open data track of file '%s': %s\n",
track_path, strerror(errno));
rv = -errno;
goto clean;
}
RARCH_LOG("%s\n", msg_hash_to_str(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS));
for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++)
{
filestream_seek(fd, MAGIC_NUMBERS[i].offset, SEEK_SET);
intfstream_seek(fd, MAGIC_NUMBERS[i].offset, SEEK_SET);
if (filestream_read(fd, magic, MAGIC_LEN) < MAGIC_LEN)
if (intfstream_read(fd, magic, MAGIC_LEN) < MAGIC_LEN)
{
RARCH_LOG("Could not read data from file '%s' at offset %d: %s\n",
track_path, MAGIC_NUMBERS[i].offset, strerror(errno));
RARCH_LOG("Could not read data at offset %d: %s\n",
MAGIC_NUMBERS[i].offset, strerror(errno));
rv = -errno;
goto clean;
}
@ -418,8 +382,8 @@ int detect_system(const char *track_path, const char **system_name)
}
}
filestream_seek(fd, 0x8008, SEEK_SET);
if (filestream_read(fd, magic, 8) > 0)
intfstream_seek(fd, 0x8008, SEEK_SET);
if (intfstream_read(fd, magic, 8) > 0)
{
magic[8] = '\0';
if (!string_is_empty(magic) &&
@ -435,7 +399,6 @@ int detect_system(const char *track_path, const char **system_name)
rv = -EINVAL;
clean:
filestream_close(fd);
return rv;
}
@ -443,11 +406,19 @@ int find_first_data_track(const char *cue_path,
int32_t *offset, char *track_path, size_t max_len)
{
int rv;
char * tmp_token = (char*)(MAX_TOKEN_LEN * sizeof(char));
RFILE *fd =
filestream_open(cue_path, RFILE_MODE_READ, -1);
char * tmp_token = malloc(MAX_TOKEN_LEN * sizeof(char));
intfstream_info_t info;
intfstream_t *fd = NULL;
info.type = INTFSTREAM_FILE;
fd = intfstream_init(&info);
if (!fd)
{
return -errno;
}
if (!intfstream_open(fd, cue_path, RFILE_MODE_READ, -1))
{
RARCH_LOG("Could not open CUE file '%s': %s\n", cue_path,
strerror(errno));
@ -494,10 +465,6 @@ int find_first_data_track(const char *cue_path,
*offset = ((m * 60) * (s * 75) * f) * 25;
RARCH_LOG("%s '%s+%d'\n",
msg_hash_to_str(MSG_FOUND_FIRST_DATA_TRACK_ON_FILE),
track_path, *offset);
rv = 0;
goto clean;
}
@ -507,12 +474,12 @@ int find_first_data_track(const char *cue_path,
clean:
free(tmp_token);
filestream_close(fd);
intfstream_close(fd);
return rv;
error:
free(tmp_token);
if (fd)
filestream_close(fd);
intfstream_close(fd);
return -errno;
}