RetroArch/gfx/video_state_python.c

420 lines
9.6 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2014-01-01 00:50:59 +00:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
2016-01-10 03:41:52 +00:00
* Copyright (C) 2011-2016 - Daniel De Matteis
*
2012-04-21 21:13:50 +00:00
* 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.
*
2012-04-21 21:13:50 +00:00
* 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.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
2011-06-07 13:33:29 +00:00
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <Python.h>
2015-11-23 11:03:38 +00:00
#include <compat/strl.h>
#include <compat/posix_string.h>
2016-03-20 15:29:14 +00:00
#include <streams/file_stream.h>
2015-11-23 11:03:38 +00:00
#include <libretro.h>
2015-01-12 15:00:13 +00:00
#include "video_state_python.h"
#include "../dynamic.h"
2016-05-08 03:29:10 +00:00
#include "../core.h"
2015-01-12 15:00:13 +00:00
#include "../general.h"
2015-11-23 11:03:38 +00:00
#include "../verbosity.h"
#include "../input/input_config.h"
2012-04-07 10:17:40 +00:00
static PyObject* py_read_wram(PyObject *self, PyObject *args)
{
2015-01-07 21:18:03 +00:00
unsigned addr;
size_t max;
2016-01-28 03:07:54 +00:00
retro_ctx_memory_info_t mem_info;
const uint8_t *data = NULL;
mem_info.id = RETRO_MEMORY_SYSTEM_RAM;
2016-05-07 23:33:57 +00:00
core_get_memory(&mem_info);
2016-01-28 03:07:54 +00:00
data = (const uint8_t*)mem_info.data;
2015-01-07 21:18:03 +00:00
(void)self;
2012-04-07 10:17:40 +00:00
if (!data)
{
Py_INCREF(Py_None);
return Py_None;
}
2016-01-28 03:07:54 +00:00
max = mem_info.size;
if (!PyArg_ParseTuple(args, "I", &addr))
return NULL;
if (addr >= max)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyLong_FromLong(data[addr]);
}
static PyObject* py_read_vram(PyObject *self, PyObject *args)
{
2015-01-07 21:18:03 +00:00
unsigned addr;
size_t max;
2016-01-28 03:07:54 +00:00
retro_ctx_memory_info_t mem_info;
const uint8_t *data = NULL;
mem_info.id = RETRO_MEMORY_VIDEO_RAM;
2016-05-07 23:33:57 +00:00
core_get_memory(&mem_info);
2016-01-28 03:07:54 +00:00
data = (const uint8_t*)mem_info.data;
2015-01-07 21:18:03 +00:00
(void)self;
if (!data)
{
Py_INCREF(Py_None);
return Py_None;
}
2012-04-07 10:17:40 +00:00
2016-01-28 03:07:54 +00:00
max = mem_info.size;
2012-04-07 10:17:40 +00:00
if (!PyArg_ParseTuple(args, "I", &addr))
return NULL;
2012-05-25 09:55:00 +00:00
if (addr >= max)
2012-04-07 10:17:40 +00:00
{
Py_INCREF(Py_None);
return Py_None;
}
return PyLong_FromLong(data[addr]);
}
static PyObject *py_read_input(PyObject *self, PyObject *args)
2011-06-08 15:53:15 +00:00
{
2015-06-27 13:55:43 +00:00
unsigned user, key, i;
const struct retro_keybind *py_binds[MAX_USERS];
2015-01-07 21:18:03 +00:00
int16_t res = 0;
2015-03-20 22:22:41 +00:00
settings_t *settings = config_get_ptr();
2015-06-27 13:55:43 +00:00
for (i = 0; i < MAX_USERS; i++)
py_binds[i] = settings->input.binds[i];
2015-01-07 21:18:03 +00:00
2011-06-08 15:53:15 +00:00
(void)self;
2015-01-07 21:18:03 +00:00
2014-12-05 12:48:54 +00:00
if (!PyArg_ParseTuple(args, "II", &user, &key))
2011-06-08 15:53:15 +00:00
return NULL;
2015-01-05 00:58:00 +00:00
if (user > MAX_USERS || user < 1 || key >= RARCH_FIRST_META_KEY)
2011-06-08 15:53:15 +00:00
return NULL;
2016-05-08 21:12:04 +00:00
if (!input_driver_is_libretro_input_blocked())
2015-03-23 02:04:05 +00:00
res = input_driver_state(py_binds, user - 1, RETRO_DEVICE_JOYPAD, 0, key);
2011-11-11 18:13:32 +00:00
return PyBool_FromLong(res);
2011-06-08 15:53:15 +00:00
}
static PyObject *py_read_analog(PyObject *self, PyObject *args)
{
2015-06-29 19:39:00 +00:00
unsigned user, index, id, i;
2015-01-07 21:18:03 +00:00
int16_t res = 0;
2015-03-20 22:22:41 +00:00
settings_t *settings = config_get_ptr();
2015-06-29 19:39:00 +00:00
const struct retro_keybind *py_binds[MAX_USERS];
for (i = 0; i < MAX_USERS; i++)
py_binds[i] = settings->input.binds[i];
2015-01-07 21:18:03 +00:00
(void)self;
2015-01-07 21:18:03 +00:00
2014-12-05 12:48:54 +00:00
if (!PyArg_ParseTuple(args, "III", &user, &index, &id))
return NULL;
2015-01-05 00:58:00 +00:00
if (user > MAX_USERS || user < 1 || index > 1 || id > 1)
return NULL;
2015-03-23 02:04:05 +00:00
res = input_driver_state(py_binds, user - 1, RETRO_DEVICE_ANALOG, index, id);
return PyFloat_FromDouble((double)res / 0x7fff);
}
2012-09-07 19:56:26 +00:00
static PyMethodDef RarchMethods[] = {
{ "read_wram", py_read_wram, METH_VARARGS, "Read WRAM from system." },
{ "read_vram", py_read_vram, METH_VARARGS, "Read VRAM from system." },
{ "input", py_read_input, METH_VARARGS, "Read input state from system." },
{ "input_analog", py_read_analog, METH_VARARGS, "Read analog input state from system." },
{ NULL, NULL, 0, NULL }
};
2012-09-07 19:56:26 +00:00
#define DECL_ATTR_RETRO(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(RETRO_DEVICE_ID_JOYPAD_##attr))
2011-06-08 15:53:15 +00:00
static void py_set_attrs(PyObject *mod)
{
2012-09-07 19:56:26 +00:00
DECL_ATTR_RETRO(B);
DECL_ATTR_RETRO(Y);
DECL_ATTR_RETRO(SELECT);
DECL_ATTR_RETRO(START);
DECL_ATTR_RETRO(UP);
DECL_ATTR_RETRO(DOWN);
DECL_ATTR_RETRO(LEFT);
DECL_ATTR_RETRO(RIGHT);
DECL_ATTR_RETRO(A);
DECL_ATTR_RETRO(X);
DECL_ATTR_RETRO(L);
DECL_ATTR_RETRO(R);
DECL_ATTR_RETRO(L2);
DECL_ATTR_RETRO(R2);
DECL_ATTR_RETRO(L3);
DECL_ATTR_RETRO(R3);
2014-10-01 23:09:03 +00:00
PyObject_SetAttrString(mod, "ANALOG_LEFT",
PyLong_FromLong(RETRO_DEVICE_INDEX_ANALOG_LEFT));
PyObject_SetAttrString(mod, "ANALOG_RIGHT",
PyLong_FromLong(RETRO_DEVICE_INDEX_ANALOG_RIGHT));
PyObject_SetAttrString(mod, "ANALOG_X",
PyLong_FromLong(RETRO_DEVICE_ID_ANALOG_X));
PyObject_SetAttrString(mod, "ANALOG_Y",
PyLong_FromLong(RETRO_DEVICE_ID_ANALOG_Y));
2011-06-08 15:53:15 +00:00
}
2012-09-07 19:56:26 +00:00
static PyModuleDef RarchModule = {
PyModuleDef_HEAD_INIT, "rarch", NULL, -1, RarchMethods,
NULL, NULL, NULL, NULL
};
2012-09-07 19:56:26 +00:00
static PyObject* PyInit_Retro(void)
{
2012-09-07 19:56:26 +00:00
PyObject *mod = PyModule_Create(&RarchModule);
2011-06-08 15:53:15 +00:00
if (!mod)
return NULL;
py_set_attrs(mod);
return mod;
}
struct py_state
{
PyObject *main;
PyObject *dict;
PyObject *inst;
2011-06-06 18:18:31 +00:00
bool warned_ret;
bool warned_type;
};
2011-06-07 13:33:29 +00:00
static char *dupe_newline(const char *str)
{
2015-01-07 21:18:03 +00:00
unsigned size;
char *ret = NULL;
2015-06-12 23:18:13 +00:00
2011-06-07 13:33:29 +00:00
if (!str)
return NULL;
2015-01-07 21:18:03 +00:00
size = strlen(str) + 2;
ret = (char*)malloc(size);
2011-06-07 13:33:29 +00:00
if (!ret)
return NULL;
strlcpy(ret, str, size);
ret[size - 2] = '\n';
ret[size - 1] = '\0';
return ret;
}
2014-10-01 23:09:03 +00:00
/* Need to make sure that first-line indentation is 0. */
2011-11-02 18:31:36 +00:00
static char *align_program(const char *program)
2011-06-07 13:33:29 +00:00
{
2015-01-07 21:18:03 +00:00
size_t prog_size;
2015-06-12 23:18:13 +00:00
char *new_prog = NULL;
char *save = NULL;
char *line = NULL;
2015-01-07 21:18:03 +00:00
unsigned skip_chars = 0;
2015-06-12 23:18:13 +00:00
char *prog = strdup(program);
2011-06-07 13:33:29 +00:00
if (!prog)
return NULL;
2015-01-07 21:18:03 +00:00
prog_size = strlen(program) + 1;
new_prog = (char*)calloc(1, prog_size);
2011-06-07 13:33:29 +00:00
if (!new_prog)
{
free(prog);
2011-06-07 13:33:29 +00:00
return NULL;
}
2011-06-07 13:33:29 +00:00
2015-01-07 21:18:03 +00:00
line = dupe_newline(strtok_r(prog, "\n", &save));
2011-06-07 13:33:29 +00:00
if (!line)
{
free(prog);
free(new_prog);
2011-06-07 13:33:29 +00:00
return NULL;
}
while (isblank(line[skip_chars]) && line[skip_chars])
skip_chars++;
while (line)
{
unsigned length = strlen(line);
unsigned skip_len = skip_chars > length ? length : skip_chars;
strlcat(new_prog, line + skip_len, prog_size);
free(line);
line = dupe_newline(strtok_r(NULL, "\n", &save));
2011-06-07 13:33:29 +00:00
}
free(prog);
return new_prog;
}
2014-10-01 23:09:03 +00:00
py_state_t *py_state_new(const char *script,
unsigned is_file, const char *pyclass)
{
2015-06-26 16:35:35 +00:00
py_state_t *handle;
PyObject *hook;
2012-04-21 21:25:32 +00:00
RARCH_LOG("Initializing Python runtime ...\n");
2012-09-07 19:56:26 +00:00
PyImport_AppendInittab("rarch", &PyInit_Retro);
Py_Initialize();
2012-04-21 21:25:32 +00:00
RARCH_LOG("Initialized Python runtime.\n");
2015-06-26 16:35:35 +00:00
handle = (py_state_t*)calloc(1, sizeof(*handle));
hook = NULL;
handle->main = PyImport_AddModule("__main__");
if (!handle->main)
goto error;
2011-11-11 18:13:32 +00:00
Py_INCREF(handle->main);
2011-06-07 13:33:29 +00:00
if (is_file)
{
2014-10-01 23:09:03 +00:00
/* Have to hack around the fact that the FILE struct
* isn't standardized across environments.
* PyRun_SimpleFile() breaks on Windows because it's
* compiled with MSVC. */
2015-02-16 02:46:27 +00:00
ssize_t len;
2015-03-28 20:18:46 +00:00
char *script_ = NULL;
2016-03-24 03:09:25 +00:00
bool ret = filestream_read_file
2016-01-30 02:58:33 +00:00
(script, (void**)&script_, &len);
2015-02-16 02:31:37 +00:00
if (!ret || len < 0)
2011-11-11 18:13:32 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Python: Failed to read script\n");
free(script_);
2011-11-11 18:13:32 +00:00
goto error;
}
PyRun_SimpleString(script_);
free(script_);
2011-06-07 13:33:29 +00:00
}
else
{
char *script_ = align_program(script);
if (script_)
{
PyRun_SimpleString(script_);
free(script_);
}
}
2012-04-21 21:25:32 +00:00
RARCH_LOG("Python: Script loaded.\n");
handle->dict = PyModule_GetDict(handle->main);
if (!handle->dict)
2011-11-11 18:13:32 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Python: PyModule_GetDict() failed.\n");
goto error;
2011-11-11 18:13:32 +00:00
}
Py_INCREF(handle->dict);
2011-12-24 12:46:12 +00:00
hook = PyDict_GetItemString(handle->dict, pyclass);
if (!hook)
2011-11-11 18:13:32 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Python: PyDict_GetItemString() failed.\n");
goto error;
2011-11-11 18:13:32 +00:00
}
handle->inst = PyObject_CallFunction(hook, NULL);
if (!handle->inst)
2011-11-11 18:13:32 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Python: PyObject_CallFunction() failed.\n");
goto error;
2011-11-11 18:13:32 +00:00
}
Py_INCREF(handle->inst);
return handle;
error:
2011-06-08 15:53:15 +00:00
PyErr_Print();
2011-11-11 18:13:32 +00:00
PyErr_Clear();
py_state_free(handle);
return NULL;
}
void py_state_free(py_state_t *handle)
{
if (!handle)
return;
2011-11-11 18:13:32 +00:00
PyErr_Print();
PyErr_Clear();
Py_CLEAR(handle->inst);
Py_CLEAR(handle->dict);
Py_CLEAR(handle->main);
free(handle);
Py_Finalize();
}
float py_state_get(py_state_t *handle, const char *id,
unsigned frame_count)
{
unsigned i;
2015-01-07 21:18:03 +00:00
float retval;
2015-06-12 23:18:13 +00:00
PyObject *ret = NULL;
2015-03-20 19:43:22 +00:00
settings_t *settings = config_get_ptr();
2015-01-07 21:18:03 +00:00
2015-01-05 00:58:00 +00:00
for (i = 0; i < MAX_USERS; i++)
{
2015-03-20 19:43:22 +00:00
input_push_analog_dpad(settings->input.binds[i],
settings->input.analog_dpad_mode[i]);
input_push_analog_dpad(settings->input.autoconf_binds[i],
settings->input.analog_dpad_mode[i]);
}
2015-01-07 21:18:03 +00:00
ret = PyObject_CallMethod(handle->inst, (char*)id, (char*)"I", frame_count);
2015-01-05 00:58:00 +00:00
for (i = 0; i < MAX_USERS; i++)
{
2015-03-20 19:43:22 +00:00
input_pop_analog_dpad(settings->input.binds[i]);
input_pop_analog_dpad(settings->input.autoconf_binds[i]);
}
if (!ret)
2011-06-06 18:18:31 +00:00
{
if (!handle->warned_ret)
2012-11-18 20:23:34 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_WARN("Didn't get return value from script. Bug?\n");
2012-11-18 20:23:34 +00:00
PyErr_Print();
PyErr_Clear();
}
2011-06-06 18:18:31 +00:00
handle->warned_ret = true;
return 0.0f;
2011-06-06 18:18:31 +00:00
}
2015-01-07 21:18:03 +00:00
retval = (float)PyFloat_AsDouble(ret);
Py_DECREF(ret);
return retval;
}