task_database: more scanning rework

- Use the primary (largest data) track for CRC identification, as it
  seems least likely to be shared among multiple discs
- CRC the primary track when importing a loose .cue file.
- If multiple tracks are in the same file, CRC the correct chunk
This commit is contained in:
Brian Koropoff 2017-09-17 14:26:06 -07:00
parent 5b20c13f92
commit fa6d0fac67
2 changed files with 294 additions and 121 deletions

View File

@ -68,8 +68,8 @@ typedef struct db_handle
bool scan_started;
} db_handle_t;
int find_first_data_track(const char* cue_path,
int32_t* offset, char* track_path, size_t max_len);
int find_track(const char *cue_path, bool first, size_t *offset, size_t *size,
char *track_path, size_t max_len);
int detect_system(intfstream_t *fd, const char** system_name);
@ -99,7 +99,34 @@ static intfstream_t* open_file(const char *path)
return fd;
}
static intfstream_t *open_chd_track(const char *path, int32_t track)
static intfstream_t*
open_memory(void *data, size_t size)
{
intfstream_info_t info;
intfstream_t *fd = NULL;
info.type = INTFSTREAM_MEMORY;
info.memory.buf.data = data;
info.memory.buf.size = size;
info.memory.writable = false;
fd = intfstream_init(&info);
if (!fd)
{
return NULL;
}
if (!intfstream_open(fd, NULL, 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;
@ -186,75 +213,98 @@ static int task_database_iterate_start(database_info_handle_t *db,
return 0;
}
static int stream_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, intfstream_t *fd, char* serial)
static int stream_get_serial(intfstream_t *fd, char *serial)
{
const char* system_name = NULL;
const char *system_name = NULL;
/* Check if the system was not auto-detected. */
if (detect_system(fd, &system_name) < 0)
{
/* Attempt to read an ASCII serial, like Wii. */
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);
return 0;
}
/* Any other non-system specific detection methods? */
return 0;
}
if (string_is_equal_fast(system_name, "psp", 3))
{
if (detect_psp_game(fd, serial) == 0)
return 0;
/* Check if the system was not auto-detected. */
if (detect_system(fd, &system_name) < 0)
{
/* Attempt to read an ASCII serial, like Wii. */
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);
}
else if (string_is_equal_fast(system_name, "ps1", 3))
{
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 1;
/* Any other non-system specific detection methods? */
return 0;
}
if (string_is_equal_fast(system_name, "psp", 3))
{
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(fd, serial) == 0)
return 0;
RARCH_LOG("%s '%s'\n", msg_hash_to_str(MSG_FOUND_DISK_LABEL), serial);
}
else {
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)
static bool file_get_serial(const char *name, size_t offset, size_t size, char *serial)
{
intfstream_t *fd = open_file(name);
int rv;
uint8_t *data = NULL;
ssize_t file_size = -1;
if (!fd)
return 0;
rv = stream_get_serial(db_state, db, fd, serial);
intfstream_seek(fd, 0, SEEK_END);
file_size = intfstream_tell(fd);
intfstream_seek(fd, 0, SEEK_SET);
if (file_size < 0) {
intfstream_close(fd);
return 0;
}
if (offset != 0 || size < (size_t) file_size)
{
data = malloc(size);
intfstream_seek(fd, offset, SEEK_SET);
if (intfstream_read(fd, data, size) != (ssize_t) size)
{
intfstream_close(fd);
free(data);
return 0;
}
intfstream_close(fd);
fd = open_memory(data, size);
if (!fd) {
free(data);
return 0;
}
}
rv = stream_get_serial(fd, serial);
intfstream_close(fd);
free(fd);
free(data);
return rv;
}
static int cue_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, const char *name, char* serial)
static int cue_get_serial(const char *name, char* serial)
{
char *track_path = (char*)malloc(PATH_MAX_LENGTH
* sizeof(char));
int ret = 0;
int32_t offset = 0;
size_t offset = 0;
size_t size = 0;
int rv = 0;
track_path[0] = '\0';
rv = find_first_data_track(name,
&offset, track_path, PATH_MAX_LENGTH);
rv = find_track(name, true, &offset, &size, track_path, PATH_MAX_LENGTH);
if (rv < 0)
{
@ -267,14 +317,13 @@ static int cue_get_serial(database_state_handle_t *db_state,
RARCH_LOG("%s\n", msg_hash_to_str(MSG_READING_FIRST_DATA_TRACK));
ret = iso_get_serial(db_state, db, track_path, serial);
ret = file_get_serial(track_path, offset, size, serial);
free(track_path);
return ret;
}
static int chd_get_serial(database_state_handle_t *db_state,
database_info_handle_t *db, const char *name, char* serial)
static int chd_get_serial(const char *name, char* serial)
{
intfstream_t *fd = NULL;
int result;
@ -285,54 +334,125 @@ static int chd_get_serial(database_state_handle_t *db_state,
return 0;
}
result = stream_get_serial(db_state, db, fd, serial);
result = stream_get_serial(fd, serial);
intfstream_close(fd);
return result;
}
static bool file_get_crc(database_state_handle_t *db_state,
const char *name, uint32_t *crc)
static int stream_get_crc(intfstream_t *fd, uint32_t *crc)
{
ssize_t ret;
int read_from = filestream_read_file(
name, (void**)&db_state->buf, &ret);
size_t read = 0;
uint32_t acc = 0;
uint8_t buffer[4096];
if (read_from != 1 || ret <= 0)
while ((read = intfstream_read(fd, buffer, sizeof(buffer))) > 0)
{
acc = encoding_crc32(acc, buffer, read);
}
if (read < 0)
{
return 0;
}
*crc = encoding_crc32(0, db_state->buf, ret);
*crc = acc;
return 1;
}
static bool chd_get_crc(database_state_handle_t *db_state,
const char *name, uint32_t *crc)
static bool file_get_crc(const char *name, size_t offset, size_t size, uint32_t *crc)
{
intfstream_t *fd = NULL;
uint32_t acc = 0;
uint8_t buffer[4096];
ssize_t size;
intfstream_t *fd = open_file(name);
int rv;
uint8_t *data = NULL;
ssize_t file_size = -1;
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);
intfstream_seek(fd, 0, SEEK_END);
file_size = intfstream_tell(fd);
intfstream_seek(fd, 0, SEEK_SET);
if (file_size < 0) {
intfstream_close(fd);
return 0;
}
if (size < 0)
if (offset != 0 || size < (size_t) file_size) {
data = malloc(size);
intfstream_seek(fd, offset, SEEK_SET);
if (intfstream_read(fd, data, size) != (ssize_t) size) {
intfstream_close(fd);
free(data);
return 0;
}
intfstream_close(fd);
fd = open_memory(data, size);
if (!fd) {
free(data);
return 0;
}
}
rv = stream_get_crc(fd, crc);
intfstream_close(fd);
free(data);
return rv;
}
static int cue_get_crc(const char *name, uint32_t *crc)
{
char *track_path = (char *)malloc(PATH_MAX_LENGTH);
int ret = 0;
size_t offset = 0;
size_t size = 0;
int rv = 0;
track_path[0] = '\0';
rv = find_track(name, false, &offset, &size, track_path, PATH_MAX_LENGTH);
if (rv < 0) {
RARCH_LOG("%s: %s\n", msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK),
strerror(-rv));
free(track_path);
return 0;
}
RARCH_LOG("CUE '%s' primary track: %s\n (%Zu, %Zu)", name, track_path, offset, size);
RARCH_LOG("%s\n", msg_hash_to_str(MSG_READING_FIRST_DATA_TRACK));
rv = file_get_crc(track_path, offset, size, crc);
if (rv == 1) {
RARCH_LOG("CUE '%s' crc: %x\n", name, *crc);
}
free(track_path);
return rv;
}
static bool chd_get_crc(const char *name, uint32_t *crc)
{
intfstream_t *fd = NULL;
int rv;
uint32_t acc = 0;
uint8_t buffer[4096];
ssize_t size;
fd = open_chd_track(name, CHDSTREAM_TRACK_PRIMARY);
if (!fd)
{
return 0;
}
RARCH_LOG("CHD '%s' crc: %x\n", name, acc);
*crc = acc;
return 1;
rv = stream_get_crc(fd, crc);
if (rv == 1) {
RARCH_LOG("CHD '%s' crc: %x\n", name, *crc);
}
intfstream_close(fd);
return rv;
}
static int task_database_iterate_playlist(
@ -345,37 +465,37 @@ static int task_database_iterate_playlist(
#ifdef HAVE_COMPRESSION
database_info_set_type(db, DATABASE_TYPE_CRC_LOOKUP);
/* first check crc of archive itself */
return file_get_crc(db_state, name, &db_state->archive_crc);
return file_get_crc(name, 0, SIZE_MAX, &db_state->archive_crc);
#else
break;
#endif
case FILE_TYPE_CUE:
db_state->serial[0] = '\0';
if (cue_get_serial(db_state, db, name, db_state->serial))
if (cue_get_serial(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);
return cue_get_crc(name, &db_state->crc);
}
break;
case FILE_TYPE_ISO:
db_state->serial[0] = '\0';
iso_get_serial(db_state, db, name, db_state->serial);
file_get_serial(name, 0, SIZE_MAX, 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))
if (chd_get_serial(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);
return chd_get_crc(name, &db_state->crc);
}
break;
case FILE_TYPE_LUTRO:
@ -383,7 +503,7 @@ static int task_database_iterate_playlist(
break;
default:
database_info_set_type(db, DATABASE_TYPE_CRC_LOOKUP);
return file_get_crc(db_state, name, &db_state->crc);
return file_get_crc(name, 0, SIZE_MAX, &db_state->crc);
}
return 1;

View File

@ -124,29 +124,6 @@ static ssize_t get_token(intfstream_t *fd, char *token, size_t max_len)
}
}
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);
if (!tmp_token)
return -1;
while (strncmp(tmp_token, token, tmp_len) != 0)
{
if (get_token(fd, tmp_token, tmp_len) <= 0)
{
free(tmp_token);
return -1;
}
}
free(tmp_token);
return 0;
}
static int detect_ps1_game_sub(intfstream_t *fp,
char *game_id, int sub_channel_mixed)
{
@ -402,13 +379,51 @@ clean:
return rv;
}
int find_first_data_track(const char *cue_path,
int32_t *offset, char *track_path, size_t max_len)
static ssize_t get_file_size(const char *path)
{
ssize_t rv;
RFILE *fd = filestream_open(path, RFILE_MODE_READ, -1);
if (fd == NULL) {
return -1;
}
rv = filestream_get_size(fd);
filestream_close(fd);
return rv;
}
static bool update_cand(ssize_t *cand_index, ssize_t *last_index,
size_t *largest, char *last_file, size_t *offset,
size_t *size, char *track_path, size_t max_len)
{
if (*cand_index != -1) {
if (*last_index - *cand_index > *largest) {
*largest = *last_index - *cand_index;
strlcpy(track_path, last_file, max_len);
*offset = *cand_index;
*size = *largest;
*cand_index = -1;
return true;
}
*cand_index = -1;
}
return false;
}
int find_track(const char *cue_path, bool first,
size_t *offset, size_t *size, char *track_path, size_t max_len)
{
int rv;
char *tmp_token = malloc(MAX_TOKEN_LEN);
char *last_file = malloc(PATH_MAX_LENGTH + 1);
intfstream_info_t info;
intfstream_t *fd = NULL;
char * tmp_token = malloc(MAX_TOKEN_LEN * sizeof(char));
ssize_t last_index = -1;
ssize_t cand_index = -1;
int32_t cand_track = -1;
int32_t track = 0;
size_t largest = 0;
ssize_t volatile file_size = -1;
bool is_data = false;
info.type = INTFSTREAM_FILE;
@ -429,56 +444,94 @@ int find_first_data_track(const char *cue_path,
tmp_token[0] = '\0';
rv = -EINVAL;
while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0)
{
if (string_is_equal(tmp_token, "FILE"))
{
char *cue_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
char *cue_dir = (char*)malloc(PATH_MAX_LENGTH);
cue_dir[0] = '\0';
fill_pathname_basedir(cue_dir, cue_path, PATH_MAX_LENGTH * sizeof(char));
/* Set last index to last EOF */
if (file_size != -1) {
last_index = file_size;
}
/* We're changing files since the candidate, update it */
if (update_cand(&cand_index, &last_index, &largest, last_file, offset,
size, track_path, max_len)) {
rv = 0;
if (first) {
goto clean;
}
}
fill_pathname_basedir(cue_dir, cue_path, PATH_MAX_LENGTH);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
fill_pathname_join(track_path, cue_dir, tmp_token, max_len);
fill_pathname_join(last_file, cue_dir, tmp_token, PATH_MAX_LENGTH);
file_size = get_file_size(last_file);
free(cue_dir);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
}
else if (string_is_equal(tmp_token, "TRACK"))
{
get_token(fd, tmp_token, MAX_TOKEN_LEN);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
is_data = !string_is_equal(tmp_token, "AUDIO");
++track;
} else if (string_is_equal(tmp_token, "INDEX")) {
int m, s, f;
get_token(fd, tmp_token, MAX_TOKEN_LEN);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
if (string_is_equal(tmp_token, "AUDIO"))
continue;
find_token(fd, "INDEX");
get_token(fd, tmp_token, MAX_TOKEN_LEN);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
if (sscanf(tmp_token, "%02d:%02d:%02d", &m, &s, &f) < 3)
{
RARCH_LOG("Error parsing time stamp '%s'\n", tmp_token);
goto error;
}
*offset = ((m * 60) * (s * 75) * f) * 25;
last_index = (size_t) (((m * 60 + s) * 75) + f) * 2352;
rv = 0;
goto clean;
/* If we've changed tracks since the candidate, update it */
if (cand_track != -1 && track != cand_track &&
update_cand(&cand_index, &last_index, &largest, last_file, offset,
size, track_path, max_len)) {
rv = 0;
if (first) {
goto clean;
}
}
if (!is_data) {
continue;
}
if (cand_index == -1) {
cand_index = last_index;
cand_track = track;
}
}
}
rv = -EINVAL;
if (update_cand(&cand_index, &last_index, &largest, last_file, offset,
size, track_path, max_len)) {
rv = 0;
}
clean:
free(tmp_token);
free(last_file);
intfstream_close(fd);
return rv;
error:
free(tmp_token);
free(last_file);
if (fd)
intfstream_close(fd);
return -errno;