290 lines
5.7 KiB
C
Raw Normal View History

#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <limits.h>
2014-08-13 03:36:44 +02:00
#include "../../hash.h"
#include "parser.h"
#include "cd_detect.h"
#include "../../compat/rarch_fnmatch.h"
#include "../../file.h"
#include "log.h"
#define SHA1_LEN 40
#define HASH_LEN SHA1_LEN
static int find_hash(int fd, const char *hash, char *game_name, size_t max_len)
{
char token[MAX_TOKEN_LEN] = {0};
2014-08-13 02:51:49 +02:00
while (1)
{
if (find_token(fd, "game") < 0)
return -1;
2014-08-13 02:51:49 +02:00
if (find_token(fd, "name") < 0)
return -1;
2014-08-13 02:51:49 +02:00
if (get_token(fd, game_name, max_len) < 0)
return -1;
2014-08-13 02:51:49 +02:00
if (find_token(fd, "sha1") < 0)
return -1;
2014-08-13 02:51:49 +02:00
if (get_token(fd, token, MAX_TOKEN_LEN) < 0)
return -1;
2014-08-13 03:08:07 +02:00
if (!strcasecmp(hash, token))
2014-08-13 02:51:49 +02:00
return 0;
}
}
static int
find_content_canonical_name(const char *hash, char *game_name, size_t max_len)
{
2014-08-13 02:51:49 +02:00
// TODO: Error handling
size_t i;
int rv, fd, offs;
const char *dat_name, *dat_name_dot;
struct string_list *files = dir_list_new("db", "dat", false);
if (!files)
return -1;
for (i = 0; i < files->size; i++)
{
2014-08-13 06:07:00 +02:00
dat_name = path_basename(files->elems[i].data);
2014-08-13 02:51:49 +02:00
dat_name_dot = strchr(dat_name, '.');
if (!dat_name_dot)
continue;
offs = dat_name_dot - dat_name + 1;
memcpy(game_name, dat_name, offs);
2014-08-13 06:07:00 +02:00
fd = open(files->elems[i].data, O_RDONLY);
2014-08-13 02:51:49 +02:00
if (fd < 0)
continue;
if (find_hash(fd, hash, game_name + offs, max_len - offs) == 0)
{
rv = 0;
close(fd);
goto clean;
}
2014-08-13 02:51:49 +02:00
close(fd);
}
rv = -1;
clean:
2014-08-13 02:51:49 +02:00
dir_list_free(files);
return rv;
}
static int get_sha1(const char *path, char *result)
{
2014-08-13 02:51:49 +02:00
int rv;
unsigned char buff[4096];
SHA1Context sha;
2014-08-13 03:08:07 +02:00
int fd = open(path, O_RDONLY);
2014-08-13 02:51:49 +02:00
if (fd < 0)
return -errno;
SHA1Reset(&sha);
rv = 1;
while (rv > 0)
{
rv = read(fd, buff, 4096);
if (rv < 0)
{
close(fd);
return -errno;
}
SHA1Input(&sha, buff, rv);
}
if (!SHA1Result(&sha))
{
close(fd);
return -1;
}
sprintf(result, "%08X%08X%08X%08X%08X",
sha.Message_Digest[0],
sha.Message_Digest[1],
sha.Message_Digest[2],
sha.Message_Digest[3], sha.Message_Digest[4]);
close(fd);
return 0;
}
2014-08-13 02:51:49 +02:00
struct RunInfo
{
char broken_cores[PATH_MAX];
int multitap;
int dualanalog;
char system[10];
};
static int read_launch_conf(struct RunInfo *info, const char *game_name)
{
int fd = open("./launch.conf", O_RDONLY);
int rv;
int bci = 0;
char token[MAX_TOKEN_LEN];
2014-08-13 02:51:49 +02:00
if (fd < 0)
return -errno;
2014-08-13 02:51:49 +02:00
while (1)
{
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0)
goto clean;
2014-08-13 02:51:49 +02:00
if (rl_fnmatch(token, game_name, 0) != 0)
{
if ((rv = find_token(fd, ";")) < 0)
goto clean;
continue;
}
LOG_DEBUG("Matched rule '%s'", token);
break;
}
2014-08-13 02:51:49 +02:00
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0)
goto clean;
2014-08-13 02:51:49 +02:00
while (strcmp(token, ";") != 0)
{
2014-08-13 03:08:07 +02:00
if (!strcmp(token, "multitap"))
2014-08-13 02:51:49 +02:00
info->multitap = 1;
2014-08-13 03:08:07 +02:00
else if (!strcmp(token, "dualanalog"))
2014-08-13 02:51:49 +02:00
info->dualanalog = 1;
else if (token[0] == '!')
{
strncpy(&info->broken_cores[bci], &token[1], PATH_MAX - bci);
bci += strnlen(&token[1], PATH_MAX) + 1;
}
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0)
goto clean;
}
rv = 0;
clean:
close(fd);
return rv;
}
2014-08-13 02:51:49 +02:00
static int get_run_info(struct RunInfo *info, const char *game_name)
{
int i;
memset(info, 0, sizeof(struct RunInfo));
for (i = 0; i < 9; i++)
{
if (game_name[i] == '.')
break;
info->system[i] = game_name[i];
}
info->system[i] = '\0';
info->multitap = 0;
info->dualanalog = 0;
read_launch_conf(info, game_name);
return 0;
}
static int detect_content_game(const char *path, char *game_name, size_t max_len)
{
char hash[HASH_LEN + 1];
2014-08-13 02:51:49 +02:00
const char *suffix = strrchr(path, '.');
2014-08-13 06:07:00 +02:00
2014-08-13 02:51:49 +02:00
if (!suffix)
{
LOG_WARN("Could not find extension for: %s", path);
return -EINVAL;
}
memset(hash, 0, sizeof(hash));
2014-08-13 06:07:00 +02:00
get_sha1(path, hash);
#if 0
if (rv < 0)
LOG_WARN("Could not calculate hash: %s", strerror(-rv));
2014-08-13 06:07:00 +02:00
#endif
if (find_content_canonical_name(hash, game_name, max_len) < 0)
2014-08-13 02:51:49 +02:00
{
LOG_DEBUG("Could not detect content with hash `%s`.", hash);
return -EINVAL;
}
return 0;
}
2014-08-13 06:07:00 +02:00
int detect_file(const char *path, char *game_name, size_t max_len)
{
2014-08-13 03:50:53 +02:00
if ((!strcasecmp(path + strlen(path) - 4, ".cue")) ||
(!strcasecmp(path + strlen(path) - 4, ".m3u")))
2014-08-13 02:51:49 +02:00
{
LOG_INFO("Starting CD game content detection...");
2014-08-13 03:50:53 +02:00
return detect_cd_game(path, game_name, max_len);
}
LOG_INFO("Starting game content detection...");
return detect_content_game(path, game_name, max_len);
}
#ifndef RARCH_CONSOLE
int main(int argc, char *argv[])
{
2014-08-13 03:44:55 +02:00
struct RunInfo info;
int rv;
char game_name[MAX_TOKEN_LEN], game_name_test[256];
2014-08-13 03:44:55 +02:00
char *path = argv[1];
2014-08-13 02:51:49 +02:00
if (argc < 2)
{
printf("usage: retrolaunch <ROM>\n");
return -1;
}
LOG_INFO("Analyzing '%s'", path);
2014-08-13 06:07:00 +02:00
if ((rv = detect_file(path, game_name, MAX_TOKEN_LEN)) < 0)
2014-08-13 02:51:49 +02:00
{
LOG_WARN("Could not detect game: %s", strerror(-rv));
2014-08-13 06:07:00 +02:00
return -1;
2014-08-13 02:51:49 +02:00
}
LOG_INFO("Game is `%s`", game_name);
char *substr = strrchr(game_name, '.');
if (!substr)
*substr = '\0';
else
{
substr = substr + 1;
LOG_INFO("Game description name is `%s`", substr);
}
2014-08-13 02:51:49 +02:00
if ((rv = get_run_info(&info, game_name)) < 0)
{
LOG_WARN("Could not detect run info: %s", strerror(-rv));
return -1;
}
return 0;
}
#endif