793 lines
20 KiB
C
793 lines
20 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2011-2015 - Daniel De Matteis
|
|
*
|
|
* 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 <compat/strcasestr.h>
|
|
#include <compat/strl.h>
|
|
#include <retro_endianness.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "tasks.h"
|
|
|
|
#ifdef HAVE_LIBRETRODB
|
|
#include "../database_info.h"
|
|
#endif
|
|
|
|
#include "../dir_list_special.h"
|
|
#include "../file_ops.h"
|
|
#include "../msg_hash.h"
|
|
#include "../general.h"
|
|
|
|
#define CB_DB_SCAN_FILE 0x70ce56d2U
|
|
#define CB_DB_SCAN_FOLDER 0xde2bef8eU
|
|
|
|
#define HASH_EXTENSION_ZIP 0x0b88c7d8U
|
|
#define HASH_EXTENSION_CUE 0x0b886782U
|
|
|
|
#ifndef COLLECTION_SIZE
|
|
#define COLLECTION_SIZE 99999
|
|
#endif
|
|
|
|
#define MAX_TOKEN_LEN 255
|
|
|
|
#define MAGIC_LEN 16
|
|
|
|
struct MagicEntry
|
|
{
|
|
const char* system_name;
|
|
const char* magic;
|
|
};
|
|
|
|
static struct MagicEntry MAGIC_NUMBERS[] = {
|
|
{"ps1", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x02\x00"},
|
|
{"pcecd", "\x82\xb1\x82\xcc\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x80\x82\xcc\x92"},
|
|
{"scd", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x01\x53"},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
typedef struct database_state_handle
|
|
{
|
|
database_info_list_t *info;
|
|
struct string_list *list;
|
|
size_t list_index;
|
|
size_t entry_index;
|
|
uint32_t crc;
|
|
uint8_t *buf;
|
|
char zip_name[PATH_MAX_LENGTH];
|
|
char *ps1_id;
|
|
} database_state_handle_t;
|
|
|
|
typedef struct db_handle
|
|
{
|
|
database_state_handle_t state;
|
|
database_info_handle_t *handle;
|
|
msg_queue_t *msg_queue;
|
|
unsigned status;
|
|
} db_handle_t;
|
|
|
|
static db_handle_t *db_ptr;
|
|
|
|
static bool pending_scan_finished;
|
|
|
|
bool rarch_main_data_db_pending_scan_finished(void)
|
|
{
|
|
if (!pending_scan_finished)
|
|
return false;
|
|
|
|
pending_scan_finished = false;
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_LIBRETRODB
|
|
|
|
#ifdef HAVE_ZLIB
|
|
static int zlib_compare_crc32(const char *name, const char *valid_exts,
|
|
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
|
uint32_t crc32, void *userdata)
|
|
{
|
|
database_state_handle_t *db_state = (database_state_handle_t*)userdata;
|
|
|
|
db_state->crc = crc32;
|
|
|
|
strlcpy(db_state->zip_name, name, sizeof(db_state->zip_name));
|
|
|
|
#if 0
|
|
RARCH_LOG("Going to compare CRC 0x%x for %s\n", crc32, name);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static int database_info_iterate_start
|
|
(database_info_handle_t *db, const char *name)
|
|
{
|
|
char msg[PATH_MAX_LENGTH] = {0};
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
#ifdef _WIN32
|
|
"%Iu/%Iu: %s %s...\n",
|
|
#else
|
|
"%zu/%zu: %s %s...\n",
|
|
#endif
|
|
db->list_ptr,
|
|
db->list->size,
|
|
msg_hash_to_str(MSG_SCANNING),
|
|
name);
|
|
|
|
if (msg[0] != '\0')
|
|
rarch_main_msg_queue_push(msg, 1, 180, true);
|
|
|
|
#if 0
|
|
RARCH_LOG("msg: %s\n", msg);
|
|
#endif
|
|
|
|
|
|
db->status = DATABASE_STATUS_ITERATE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_iterate_playlist(
|
|
database_state_handle_t *db_state,
|
|
database_info_handle_t *db, const char *name)
|
|
{
|
|
uint32_t extension_hash = 0;
|
|
char parent_dir[PATH_MAX_LENGTH] = {0};
|
|
|
|
path_parent_dir(parent_dir);
|
|
|
|
extension_hash = msg_hash_calculate(path_get_extension(name));
|
|
|
|
switch (extension_hash)
|
|
{
|
|
case HASH_EXTENSION_ZIP:
|
|
#ifdef HAVE_ZLIB
|
|
db->type = DATABASE_TYPE_ITERATE_ZIP;
|
|
memset(&db->state, 0, sizeof(zlib_transfer_t));
|
|
db_state->zip_name[0] = '\0';
|
|
db->state.type = ZLIB_TRANSFER_INIT;
|
|
|
|
return 1;
|
|
#endif
|
|
case HASH_EXTENSION_CUE:
|
|
db->type = DATABASE_TYPE_ITERATE_CUE;
|
|
return 1;
|
|
default:
|
|
{
|
|
ssize_t ret;
|
|
int read_from = read_file(name, (void**)&db_state->buf, &ret);
|
|
|
|
if (read_from != 1 || ret <= 0)
|
|
return 0;
|
|
|
|
|
|
#ifdef HAVE_ZLIB
|
|
db_state->crc = zlib_crc32_calculate(db_state->buf, ret);
|
|
#endif
|
|
db->type = DATABASE_TYPE_CRC_LOOKUP;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int database_info_list_iterate_end_no_match(database_state_handle_t *db_state)
|
|
{
|
|
/* Reached end of database list, CRC match probably didn't succeed. */
|
|
db_state->list_index = 0;
|
|
db_state->entry_index = 0;
|
|
|
|
if (db_state->crc != 0)
|
|
db_state->crc = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_iterate_next(database_info_handle_t *db)
|
|
{
|
|
db->list_ptr++;
|
|
|
|
if (db->list_ptr < db->list->size)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
static int database_info_list_iterate_new(database_state_handle_t *db_state, const char *query)
|
|
{
|
|
const char *new_database = db_state->list->elems[db_state->list_index].data;
|
|
#if 0
|
|
RARCH_LOG("Check database [%d/%d] : %s\n", (unsigned)db_state->list_index,
|
|
(unsigned)db_state->list->size, new_database);
|
|
#endif
|
|
if (db_state->info)
|
|
database_info_list_free(db_state->info);
|
|
db_state->info = database_info_list_new(new_database, query);
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_list_iterate_found_match(
|
|
database_state_handle_t *db_state,
|
|
database_info_handle_t *db,
|
|
const char *zip_name
|
|
)
|
|
{
|
|
char db_crc[PATH_MAX_LENGTH] = {0};
|
|
char db_playlist_path[PATH_MAX_LENGTH] = {0};
|
|
char db_playlist_base_str[PATH_MAX_LENGTH] = {0};
|
|
char entry_path_str[PATH_MAX_LENGTH] = {0};
|
|
content_playlist_t *playlist = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *db_path = db_state->list->elems[db_state->list_index].data;
|
|
const char *entry_path = db ? db->list->elems[db->list_ptr].data : NULL;
|
|
database_info_t *db_info_entry = &db_state->info->list[db_state->entry_index];
|
|
|
|
fill_short_pathname_representation(db_playlist_base_str,
|
|
db_path, sizeof(db_playlist_base_str));
|
|
|
|
path_remove_extension(db_playlist_base_str);
|
|
|
|
strlcat(db_playlist_base_str, ".lpl", sizeof(db_playlist_base_str));
|
|
fill_pathname_join(db_playlist_path, settings->playlist_directory,
|
|
db_playlist_base_str, sizeof(db_playlist_path));
|
|
|
|
playlist = content_playlist_init(db_playlist_path, COLLECTION_SIZE);
|
|
|
|
|
|
snprintf(db_crc, sizeof(db_crc), "%08X|crc", db_info_entry->crc32);
|
|
|
|
strlcpy(entry_path_str, entry_path, sizeof(entry_path_str));
|
|
|
|
if (zip_name && zip_name[0] != '\0')
|
|
fill_pathname_join_delim(entry_path_str, entry_path_str, zip_name,
|
|
'#', sizeof(entry_path_str));
|
|
|
|
#if 0
|
|
RARCH_LOG("Found match in database !\n");
|
|
|
|
RARCH_LOG("Path: %s\n", db_path);
|
|
RARCH_LOG("CRC : %s\n", db_crc);
|
|
RARCH_LOG("Playlist Path: %s\n", db_playlist_path);
|
|
RARCH_LOG("Entry Path: %s\n", entry_path);
|
|
RARCH_LOG("Playlist not NULL: %d\n", playlist != NULL);
|
|
RARCH_LOG("ZIP entry: %s\n", zip_name);
|
|
RARCH_LOG("entry path str: %s\n", entry_path_str);
|
|
#endif
|
|
|
|
content_playlist_push(playlist, entry_path_str,
|
|
db_info_entry->name, "DETECT", "DETECT", db_crc, db_playlist_base_str);
|
|
|
|
content_playlist_write_file(playlist);
|
|
content_playlist_free(playlist);
|
|
return 0;
|
|
}
|
|
|
|
/* End of entries in database info list and didn't find a
|
|
* match, go to the next database. */
|
|
static int database_info_list_iterate_next(
|
|
database_state_handle_t *db_state
|
|
)
|
|
{
|
|
db_state->list_index++;
|
|
db_state->entry_index = 0;
|
|
|
|
database_info_list_free(db_state->info);
|
|
db_state->info = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int database_info_iterate_crc_lookup(
|
|
database_state_handle_t *db_state,
|
|
database_info_handle_t *db,
|
|
const char *zip_entry)
|
|
{
|
|
|
|
if (!db_state->list || (unsigned)db_state->list_index == (unsigned)db_state->list->size)
|
|
return database_info_list_iterate_end_no_match(db_state);
|
|
|
|
if (db_state->entry_index == 0)
|
|
{
|
|
char query[50] = {0};
|
|
snprintf(query, sizeof(query), "{crc: b\"%08X\"}", swap_if_big32(db_state->crc));
|
|
|
|
database_info_list_iterate_new(db_state, query);
|
|
}
|
|
|
|
if (db_state->info)
|
|
{
|
|
database_info_t *db_info_entry = &db_state->info->list[db_state->entry_index];
|
|
|
|
if (db_info_entry && db_info_entry->crc32)
|
|
{
|
|
#if 0
|
|
RARCH_LOG("CRC32: 0x%08X , entry CRC32: 0x%08X (%s).\n",
|
|
db_state->crc, db_info_entry->crc32, db_info_entry->name);
|
|
#endif
|
|
if (db_state->crc == db_info_entry->crc32)
|
|
database_info_list_iterate_found_match(db_state, db, zip_entry);
|
|
}
|
|
}
|
|
|
|
db_state->entry_index++;
|
|
|
|
if (db_state->entry_index >= db_state->info->count)
|
|
return database_info_list_iterate_next(db_state);
|
|
|
|
if (db_state->list_index < db_state->list->size)
|
|
{
|
|
/* Didn't reach the end of the database list yet,
|
|
* continue iterating. */
|
|
return 1;
|
|
}
|
|
|
|
if (db_state->info)
|
|
database_info_list_free(db_state->info);
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_iterate_playlist_zip(
|
|
database_state_handle_t *db_state,
|
|
database_info_handle_t *db, const char *name)
|
|
{
|
|
bool returnerr = true;
|
|
#ifdef HAVE_ZLIB
|
|
if (db_state->crc != 0)
|
|
return database_info_iterate_crc_lookup(db_state, db, db_state->zip_name);
|
|
else
|
|
{
|
|
if (zlib_parse_file_iterate(&db->state,
|
|
&returnerr, name, NULL, zlib_compare_crc32,
|
|
(void*)db_state) != 0)
|
|
return 0;
|
|
|
|
if (db_state->crc)
|
|
zlib_parse_file_iterate_stop(&db->state);
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
ssize_t get_token(int fd, char *token, size_t max_len)
|
|
{
|
|
char *c = token;
|
|
int rv;
|
|
ssize_t len = 0;
|
|
int in_string = 0;
|
|
|
|
while (1)
|
|
{
|
|
rv = read(fd, c, 1);
|
|
if (rv == 0)
|
|
return 0;
|
|
else if (rv < 1)
|
|
{
|
|
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 == (ssize_t)max_len)
|
|
{
|
|
*c = '\0';
|
|
return len;
|
|
}
|
|
}
|
|
}
|
|
|
|
int find_token(int fd, const char *token)
|
|
{
|
|
int tmp_len = strlen(token);
|
|
char *tmp_token = (char*)calloc(tmp_len, 1);
|
|
if (!tmp_token)
|
|
return -1;
|
|
while (strncmp(tmp_token, token, tmp_len) != 0)
|
|
{
|
|
if (get_token(fd, tmp_token, tmp_len) <= 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int detect_ps1_game(const char* track_path, char* game_id)
|
|
{
|
|
const char* pattern = "cdrom:";
|
|
const char* pat_c;
|
|
char* c;
|
|
char* id_start;
|
|
int i;
|
|
|
|
int fd = open(track_path, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
RARCH_LOG("Could not open data track: %s\n", strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
lseek(fd, 0x9340, SEEK_SET);
|
|
if (read(fd, game_id, 10) > 0)
|
|
{
|
|
game_id[10] = '\0';
|
|
game_id[4] = '-';
|
|
}
|
|
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
|
|
static int detect_system(const char* track_path, int32_t offset,
|
|
const char** system_name)
|
|
{
|
|
int rv;
|
|
char magic[MAGIC_LEN];
|
|
int fd;
|
|
int i;
|
|
|
|
fd = open(track_path, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
RARCH_LOG("Could not open data track of file '%s': %s\n",
|
|
track_path, strerror(errno));
|
|
rv = -errno;
|
|
goto clean;
|
|
}
|
|
|
|
lseek(fd, offset, SEEK_SET);
|
|
if (read(fd, magic, MAGIC_LEN) < MAGIC_LEN)
|
|
{
|
|
RARCH_LOG("Could not read data from file '%s' at offset %d: %s\n",
|
|
track_path, offset, strerror(errno));
|
|
rv = -errno;
|
|
goto clean;
|
|
}
|
|
|
|
RARCH_LOG("Comparing with known magic numbers...\n");
|
|
for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++)
|
|
{
|
|
if (memcmp(MAGIC_NUMBERS[i].magic, magic, MAGIC_LEN) == 0)
|
|
{
|
|
*system_name = MAGIC_NUMBERS[i].system_name;
|
|
rv = 0;
|
|
goto clean;
|
|
}
|
|
}
|
|
|
|
RARCH_LOG("Could not find compatible system\n");
|
|
rv = -EINVAL;
|
|
clean:
|
|
close(fd);
|
|
return rv;
|
|
}
|
|
|
|
static int find_first_data_track(const char* cue_path, int32_t* offset,
|
|
char* track_path, size_t max_len)
|
|
{
|
|
int rv;
|
|
int fd = -1;
|
|
char tmp_token[MAX_TOKEN_LEN];
|
|
int m, s, f;
|
|
char cue_dir[PATH_MAX];
|
|
strlcpy(cue_dir, cue_path, PATH_MAX);
|
|
path_basedir(cue_dir);
|
|
|
|
fd = open(cue_path, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
RARCH_LOG("Could not open CUE file '%s': %s\n", cue_path,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
RARCH_LOG("Parsing CUE file '%s'...\n", cue_path);
|
|
|
|
while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0)
|
|
{
|
|
if (strcmp(tmp_token, "FILE") == 0)
|
|
{
|
|
get_token(fd, tmp_token, MAX_TOKEN_LEN);
|
|
fill_pathname_join(track_path, cue_dir, tmp_token, max_len);
|
|
|
|
}
|
|
else if (strcasecmp(tmp_token, "TRACK") == 0)
|
|
{
|
|
get_token(fd, tmp_token, MAX_TOKEN_LEN);
|
|
get_token(fd, tmp_token, MAX_TOKEN_LEN);
|
|
if (strcasecmp(tmp_token, "AUDIO") == 0)
|
|
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);
|
|
return -errno;
|
|
}
|
|
*offset = ((m * 60) * (s * 75) * f) * 25;
|
|
|
|
RARCH_LOG("Found 1st data track on file '%s+%d'\n",
|
|
track_path, *offset);
|
|
|
|
rv = 0;
|
|
goto clean;
|
|
}
|
|
}
|
|
|
|
rv = -EINVAL;
|
|
|
|
clean:
|
|
close(fd);
|
|
return rv;
|
|
}
|
|
|
|
static int database_info_iterate_playlist_cue(
|
|
database_state_handle_t *db_state,
|
|
database_info_handle_t *db, const char *name)
|
|
{
|
|
int rv;
|
|
char track_path[PATH_MAX];
|
|
int32_t offset = 0;
|
|
const char* system_name = NULL;
|
|
size_t max_len = MAX_TOKEN_LEN;
|
|
char game_id[4096];
|
|
|
|
rv = find_first_data_track(name, &offset, track_path, PATH_MAX);
|
|
if (rv < 0)
|
|
{
|
|
RARCH_LOG("Could not find valid data track: %s\n", strerror(-rv));
|
|
return rv;
|
|
}
|
|
|
|
RARCH_LOG("Reading 1st data track...\n");
|
|
|
|
if ((rv = detect_system(track_path, offset, &system_name)) < 0)
|
|
return rv;
|
|
|
|
RARCH_LOG("Detected %s media\n", system_name);
|
|
|
|
if (strcmp(system_name, "ps1") == 0)
|
|
{
|
|
if (detect_ps1_game(track_path, game_id) == 0)
|
|
return 0;
|
|
RARCH_LOG("Found disk label '%s'\n", game_id);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_iterate(database_state_handle_t *db_state, database_info_handle_t *db)
|
|
{
|
|
const char *name = db ? db->list->elems[db->list_ptr].data : NULL;
|
|
|
|
if (!db || !db->list)
|
|
return -1;
|
|
|
|
if (!name)
|
|
return 0;
|
|
|
|
switch (db->type)
|
|
{
|
|
case DATABASE_TYPE_NONE:
|
|
break;
|
|
case DATABASE_TYPE_ITERATE:
|
|
return database_info_iterate_playlist(db_state, db, name);
|
|
case DATABASE_TYPE_ITERATE_ZIP:
|
|
return database_info_iterate_playlist_zip(db_state, db, name);
|
|
case DATABASE_TYPE_ITERATE_CUE:
|
|
return database_info_iterate_playlist_cue(db_state, db, name);
|
|
case DATABASE_TYPE_CRC_LOOKUP:
|
|
return database_info_iterate_crc_lookup(db_state, db, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int database_info_poll(db_handle_t *db)
|
|
{
|
|
char elem0[PATH_MAX_LENGTH];
|
|
uint32_t cb_type_hash = 0;
|
|
struct string_list *str_list = NULL;
|
|
const char *path = msg_queue_pull(db->msg_queue);
|
|
|
|
if (!path)
|
|
return -1;
|
|
|
|
str_list = string_split(path, "|");
|
|
|
|
if (!str_list)
|
|
goto error;
|
|
if (str_list->size < 1)
|
|
goto error;
|
|
|
|
strlcpy(elem0, str_list->elems[0].data, sizeof(elem0));
|
|
if (str_list->size > 1)
|
|
cb_type_hash = msg_hash_calculate(str_list->elems[1].data);
|
|
|
|
switch (cb_type_hash)
|
|
{
|
|
case CB_DB_SCAN_FILE:
|
|
db->handle = database_info_file_init(elem0, DATABASE_TYPE_ITERATE);
|
|
break;
|
|
case CB_DB_SCAN_FOLDER:
|
|
db->handle = database_info_dir_init(elem0, DATABASE_TYPE_ITERATE);
|
|
break;
|
|
}
|
|
|
|
string_list_free(str_list);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (str_list)
|
|
string_list_free(str_list);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void rarch_main_data_db_cleanup_state(database_state_handle_t *db_state)
|
|
{
|
|
if (!db_state)
|
|
return;
|
|
|
|
if (db_state->buf)
|
|
free(db_state->buf);
|
|
db_state->buf = NULL;
|
|
}
|
|
|
|
void rarch_main_data_db_iterate(bool is_thread)
|
|
{
|
|
database_info_handle_t *db = (db_ptr) ? db_ptr->handle : NULL;
|
|
database_state_handle_t *db_state = (db_ptr) ? &db_ptr->state : NULL;
|
|
const char *name = db ? db->list->elems[db->list_ptr].data : NULL;
|
|
|
|
if (!db)
|
|
goto do_poll;
|
|
|
|
switch (db->status)
|
|
{
|
|
case DATABASE_STATUS_ITERATE_BEGIN:
|
|
if (db_state && !db_state->list)
|
|
db_state->list = dir_list_new_special(NULL, DIR_LIST_DATABASES);
|
|
db->status = DATABASE_STATUS_ITERATE_START;
|
|
break;
|
|
case DATABASE_STATUS_ITERATE_START:
|
|
rarch_main_data_db_cleanup_state(db_state);
|
|
db_state->list_index = 0;
|
|
db_state->entry_index = 0;
|
|
database_info_iterate_start(db, name);
|
|
break;
|
|
case DATABASE_STATUS_ITERATE:
|
|
if (database_info_iterate(&db_ptr->state, db) == 0)
|
|
{
|
|
db->status = DATABASE_STATUS_ITERATE_NEXT;
|
|
db->type = DATABASE_TYPE_ITERATE;
|
|
}
|
|
break;
|
|
case DATABASE_STATUS_ITERATE_NEXT:
|
|
if (database_info_iterate_next(db) == 0)
|
|
{
|
|
db->status = DATABASE_STATUS_ITERATE_START;
|
|
db->type = DATABASE_TYPE_ITERATE;
|
|
}
|
|
else
|
|
{
|
|
rarch_main_msg_queue_push_new(MSG_SCANNING_OF_DIRECTORY_FINISHED, 0, 180, true);
|
|
pending_scan_finished = true;
|
|
db->status = DATABASE_STATUS_FREE;
|
|
}
|
|
break;
|
|
case DATABASE_STATUS_FREE:
|
|
if (db_state->list)
|
|
dir_list_free(db_state->list);
|
|
db_state->list = NULL;
|
|
rarch_main_data_db_cleanup_state(db_state);
|
|
database_info_free(db);
|
|
if (db_ptr->handle)
|
|
free(db_ptr->handle);
|
|
db_ptr->handle = NULL;
|
|
break;
|
|
default:
|
|
case DATABASE_STATUS_NONE:
|
|
goto do_poll;
|
|
}
|
|
|
|
return;
|
|
|
|
do_poll:
|
|
if (database_info_poll(db_ptr) != -1)
|
|
{
|
|
if (db_ptr->handle)
|
|
db_ptr->handle->status = DATABASE_STATUS_ITERATE_BEGIN;
|
|
}
|
|
}
|
|
|
|
|
|
void rarch_main_data_db_init_msg_queue(void)
|
|
{
|
|
db_handle_t *db = (db_handle_t*)db_ptr;
|
|
|
|
if (!db || !db->msg_queue)
|
|
rarch_assert(db->msg_queue = msg_queue_new(8));
|
|
}
|
|
|
|
msg_queue_t *rarch_main_data_db_get_msg_queue_ptr(void)
|
|
{
|
|
db_handle_t *db = (db_handle_t*)db_ptr;
|
|
if (!db)
|
|
return NULL;
|
|
return db->msg_queue;
|
|
}
|
|
|
|
void rarch_main_data_db_uninit(void)
|
|
{
|
|
if (db_ptr)
|
|
free(db_ptr);
|
|
db_ptr = NULL;
|
|
pending_scan_finished = false;
|
|
}
|
|
|
|
void rarch_main_data_db_init(void)
|
|
{
|
|
db_ptr = (db_handle_t*)calloc(1, sizeof(*db_ptr));
|
|
pending_scan_finished = false;
|
|
}
|
|
|
|
bool rarch_main_data_db_is_active(void)
|
|
{
|
|
db_handle_t *db = (db_handle_t*)db_ptr;
|
|
database_info_handle_t *dbi = db ? db->handle : NULL;
|
|
if (dbi && dbi->status != DATABASE_STATUS_NONE)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|