RetroArch/cheats.c

382 lines
8.5 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2013-01-01 00:37:37 +00:00
* Copyright (C) 2010-2013 - Hans-Kristian Arntzen
2011-04-17 11:30:59 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-04-17 11:30:59 +00:00
* 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.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-04-17 11:30:59 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-04-17 11:30:59 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "cheats.h"
2012-03-25 21:29:39 +00:00
#include "hash.h"
2011-04-17 11:30:59 +00:00
#include "dynamic.h"
#include "general.h"
2012-03-16 22:26:57 +00:00
#include "compat/strl.h"
#include "compat/posix_string.h"
2011-04-17 11:30:59 +00:00
2011-09-05 15:57:30 +00:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "conf/config_file.h"
2011-04-17 11:30:59 +00:00
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#ifdef HAVE_LIBXML2
2011-04-17 11:30:59 +00:00
#include <libxml/parser.h>
#include <libxml/tree.h>
#else
#define RXML_LIBXML2_COMPAT
#include "compat/rxml/rxml.h"
#endif
2011-04-17 11:30:59 +00:00
struct cheat
{
char *desc;
bool state;
char *code;
};
struct cheat_manager
{
struct cheat *cheats;
unsigned ptr;
unsigned size;
2011-04-17 13:58:18 +00:00
unsigned buf_size;
2011-04-17 11:30:59 +00:00
};
2011-11-02 18:31:36 +00:00
static char *strcat_alloc(char *dest, const char *input)
2011-04-17 13:58:18 +00:00
{
size_t dest_len = dest ? strlen(dest) : 0;
size_t input_len = strlen(input);
size_t required_len = dest_len + input_len + 1;
2011-12-24 12:46:12 +00:00
char *output = (char*)realloc(dest, required_len);
if (!output)
return NULL;
2011-04-17 13:58:18 +00:00
if (dest)
strlcat(output, input, required_len);
else
strlcpy(output, input, required_len);
return output;
}
static bool xml_grab_cheat(struct cheat *cht, xmlNodePtr ptr)
2011-04-17 13:58:18 +00:00
{
if (!ptr)
return false;
2011-04-17 13:58:18 +00:00
memset(cht, 0, sizeof(struct cheat));
bool first = true;
for (; ptr; ptr = ptr->next)
2011-04-17 13:58:18 +00:00
{
if (strcmp((const char*)ptr->name, "description") == 0)
2011-04-17 13:58:18 +00:00
{
cht->desc = (char*)xmlNodeGetContent(ptr);
2011-04-17 13:58:18 +00:00
}
else if (strcmp((const char*)ptr->name, "code") == 0)
2011-04-17 13:58:18 +00:00
{
if (!first)
{
2011-04-17 13:58:18 +00:00
cht->code = strcat_alloc(cht->code, "+");
if (!cht->code)
return false;
}
2011-04-17 13:58:18 +00:00
xmlChar *code = xmlNodeGetContent(ptr);
if (!code)
return false;
2011-04-17 13:58:18 +00:00
cht->code = strcat_alloc(cht->code, (const char*)code);
xmlFree(code);
if (!cht->code)
return false;
2011-04-17 13:58:18 +00:00
first = false;
}
}
return true;
2011-04-17 13:58:18 +00:00
}
static bool xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
2011-04-17 11:30:59 +00:00
{
for (; ptr; ptr = ptr->next)
2011-04-17 11:30:59 +00:00
{
if (strcmp((const char*)ptr->name, "name") == 0)
2011-04-17 11:30:59 +00:00
{
xmlChar *name = xmlNodeGetContent(ptr);
2011-04-17 11:30:59 +00:00
if (name)
{
2012-04-21 21:25:32 +00:00
RARCH_LOG("Found cheat for game: \"%s\"\n", name);
2011-04-17 11:30:59 +00:00
xmlFree(name);
}
}
else if (strcmp((const char*)ptr->name, "cheat") == 0)
2011-04-17 13:58:18 +00:00
{
if (handle->size == handle->buf_size)
{
handle->buf_size *= 2;
2011-12-24 12:46:12 +00:00
handle->cheats = (struct cheat*)realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
if (!handle->cheats)
return false;
2011-04-17 13:58:18 +00:00
}
if (xml_grab_cheat(&handle->cheats[handle->size], ptr->children))
handle->size++;
2011-04-17 13:58:18 +00:00
}
2011-04-17 11:30:59 +00:00
}
return true;
2011-04-17 11:30:59 +00:00
}
2011-09-07 16:59:51 +00:00
static void cheat_manager_apply_cheats(cheat_manager_t *handle)
2011-09-05 15:57:30 +00:00
{
unsigned index = 0;
2012-04-07 10:17:40 +00:00
pretro_cheat_reset();
2011-09-05 15:57:30 +00:00
for (unsigned i = 0; i < handle->size; i++)
{
if (handle->cheats[i].state)
2012-04-07 10:17:40 +00:00
pretro_cheat_set(index++, true, handle->cheats[i].code);
2011-09-05 15:57:30 +00:00
}
}
static void cheat_manager_load_config(cheat_manager_t *handle, const char *path, const char *sha256)
{
if (!(*path))
return;
config_file_t *conf = config_file_new(path);
if (!conf)
return;
2013-01-02 15:43:53 +00:00
char *str = NULL;
2011-09-05 15:57:30 +00:00
if (!config_get_string(conf, sha256, &str))
{
config_file_free(conf);
return;
}
char *save;
const char *num = strtok_r(str, ";", &save);
2011-09-05 15:57:30 +00:00
while (num)
{
unsigned index = strtoul(num, NULL, 0);
if (index < handle->size)
handle->cheats[index].state = true;
num = strtok_r(NULL, ";", &save);
2011-09-05 15:57:30 +00:00
}
free(str);
config_file_free(conf);
cheat_manager_apply_cheats(handle);
2011-09-05 15:57:30 +00:00
}
static void cheat_manager_save_config(cheat_manager_t *handle, const char *path, const char *sha256)
{
if (!(*path))
return;
config_file_t *conf = config_file_new(path);
2011-09-06 09:51:52 +00:00
if (!conf)
conf = config_file_new(NULL);
2011-09-05 15:57:30 +00:00
if (!conf)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Cannot save XML cheat settings.\n");
2011-09-05 15:57:30 +00:00
return;
}
char conf_str[512] = {0};
char tmp[32] = {0};
for (unsigned i = 0; i < handle->size; i++)
{
if (handle->cheats[i].state)
{
snprintf(tmp, sizeof(tmp), "%u;", i);
strlcat(conf_str, tmp, sizeof(conf_str));
}
}
if (*conf_str)
conf_str[strlen(conf_str) - 1] = '\0'; // Remove the trailing ';'
config_set_string(conf, sha256, conf_str);
if (!config_file_write(conf, path))
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to write XML cheat settings to \"%s\". Check permissions.\n", path);
2011-09-05 15:57:30 +00:00
config_file_free(conf);
}
cheat_manager_t *cheat_manager_new(const char *path)
2011-04-17 11:30:59 +00:00
{
2011-09-09 07:48:46 +00:00
LIBXML_TEST_VERSION;
2012-04-07 10:17:40 +00:00
pretro_cheat_reset();
2011-04-17 11:30:59 +00:00
2011-04-17 13:58:18 +00:00
xmlParserCtxtPtr ctx = NULL;
xmlDocPtr doc = NULL;
2011-12-24 12:46:12 +00:00
cheat_manager_t *handle = (cheat_manager_t*)calloc(1, sizeof(struct cheat_manager));
2011-04-17 11:30:59 +00:00
if (!handle)
return NULL;
2011-12-24 12:46:12 +00:00
xmlNodePtr head = NULL;
xmlNodePtr cur = NULL;
handle->buf_size = 1;
2011-12-24 12:46:12 +00:00
handle->cheats = (struct cheat*)calloc(handle->buf_size, sizeof(struct cheat));
2011-04-17 13:58:18 +00:00
if (!handle->cheats)
{
handle->buf_size = 0;
goto error;
}
2011-04-17 11:30:59 +00:00
ctx = xmlNewParserCtxt();
if (!ctx)
goto error;
doc = xmlCtxtReadFile(ctx, path, NULL, 0);
if (!doc)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to parse XML file: %s\n", path);
2011-04-17 11:30:59 +00:00
goto error;
}
#ifdef HAVE_LIBXML2
2011-04-17 11:30:59 +00:00
if (ctx->valid == 0)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Cannot validate XML file: %s\n", path);
2011-04-17 11:30:59 +00:00
goto error;
}
#endif
2011-04-17 11:30:59 +00:00
2011-12-24 12:46:12 +00:00
head = xmlDocGetRootElement(doc);
2011-04-17 11:30:59 +00:00
for (cur = head; cur; cur = cur->next)
{
if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "database") == 0)
break;
}
if (!cur)
goto error;
for (cur = cur->children; cur; cur = cur->next)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char*)cur->name, "cartridge") == 0)
{
xmlChar *sha256 = xmlGetProp(cur, (const xmlChar*)"sha256");
if (!sha256)
continue;
if (*g_extern.sha256 && strcmp((const char*)sha256, g_extern.sha256) == 0)
2011-04-17 11:30:59 +00:00
{
xmlFree(sha256);
break;
}
xmlFree(sha256);
}
}
if (!cur)
goto error;
if (!xml_grab_cheats(handle, cur->children))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to grab cheats. This should not happen.\n");
goto error;
}
if (handle->size == 0)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Did not find any cheats in XML file: %s\n", path);
goto error;
}
2011-04-17 11:30:59 +00:00
2011-09-05 15:57:30 +00:00
cheat_manager_load_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
2011-04-17 11:30:59 +00:00
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
return handle;
error:
cheat_manager_free(handle);
if (doc)
xmlFreeDoc(doc);
if (ctx)
xmlFreeParserCtxt(ctx);
return NULL;
}
void cheat_manager_free(cheat_manager_t *handle)
{
if (!handle)
return;
if (handle->cheats)
{
2011-09-05 15:57:30 +00:00
cheat_manager_save_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
2011-04-17 11:30:59 +00:00
for (unsigned i = 0; i < handle->size; i++)
{
xmlFree(handle->cheats[i].desc);
2011-04-17 13:58:18 +00:00
free(handle->cheats[i].code);
2011-04-17 11:30:59 +00:00
}
free(handle->cheats);
}
free(handle);
}
2011-04-17 14:53:19 +00:00
static void cheat_manager_update(cheat_manager_t *handle)
{
msg_queue_clear(g_extern.msg_queue);
char msg[256];
snprintf(msg, sizeof(msg), "Cheat: #%u [%s]: %s", handle->ptr, handle->cheats[handle->ptr].state ? "ON" : "OFF", handle->cheats[handle->ptr].desc);
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
2012-04-21 21:25:32 +00:00
RARCH_LOG("%s\n", msg);
2011-04-17 14:53:19 +00:00
}
2011-09-05 15:57:30 +00:00
2011-04-17 11:30:59 +00:00
void cheat_manager_toggle(cheat_manager_t *handle)
{
handle->cheats[handle->ptr].state ^= true;
cheat_manager_apply_cheats(handle);
2011-04-17 14:53:19 +00:00
cheat_manager_update(handle);
}
void cheat_manager_index_next(cheat_manager_t *handle)
{
handle->ptr = (handle->ptr + 1) % handle->size;
cheat_manager_update(handle);
}
void cheat_manager_index_prev(cheat_manager_t *handle)
{
if (handle->ptr == 0)
handle->ptr = handle->size - 1;
else
handle->ptr--;
cheat_manager_update(handle);
2011-04-17 11:30:59 +00:00
}