RetroArch/gfx/py_state/py_state.c

324 lines
8.0 KiB
C
Raw Normal View History

/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <Python.h>
#include <stdbool.h>
2011-06-07 13:33:29 +00:00
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dynamic.h"
#include "libsnes.hpp"
#include "py_state.h"
2011-06-06 18:18:31 +00:00
#include "general.h"
2011-06-07 13:33:29 +00:00
#include "strl.h"
#define PY_READ_FUNC_DECL(RAMTYPE) py_read_##RAMTYPE
#define PY_READ_FUNC(RAMTYPE) \
static PyObject* PY_READ_FUNC_DECL(RAMTYPE) (PyObject *self, PyObject *args) \
{ \
(void)self; \
\
const uint8_t *data = psnes_get_memory_data(SNES_MEMORY_##RAMTYPE); \
if (!data) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
unsigned max = psnes_get_memory_size(SNES_MEMORY_##RAMTYPE); \
\
unsigned addr; \
if (!PyArg_ParseTuple(args, "I", &addr)) \
return NULL; \
\
if (addr >= max || addr < 0) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
\
return PyLong_FromLong((long)data[addr]); \
}
PY_READ_FUNC(WRAM)
PY_READ_FUNC(VRAM)
PY_READ_FUNC(APURAM)
PY_READ_FUNC(CGRAM)
PY_READ_FUNC(OAM)
static PyObject *py_read_input(PyObject *self, PyObject *args)
2011-06-08 15:53:15 +00:00
{
(void)self;
if (!driver.input_data)
return PyBool_FromLong(0);
unsigned player;
unsigned key;
if (!PyArg_ParseTuple(args, "II", &player, &key))
return NULL;
if (player > MAX_PLAYERS || player < 1 || key >= SSNES_FIRST_META_KEY)
2011-06-08 15:53:15 +00:00
return NULL;
const struct snes_keybind *binds[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
binds[i] = g_settings.input.binds[i];
2011-06-08 15:55:32 +00:00
int16_t res = driver.input->input_state(driver.input_data,
2011-06-08 15:53:15 +00:00
binds, player > 1,
player > 2 ? SNES_DEVICE_MULTITAP : SNES_DEVICE_JOYPAD,
player > 2 ? player - 2 : 0,
key);
return PyBool_FromLong((long)res);
}
static PyObject *py_read_input_meta(PyObject *self, PyObject *args)
{
(void)self;
if (!driver.input_data)
return PyBool_FromLong(0);
unsigned key;
if (!PyArg_ParseTuple(args, "I", &key))
return NULL;
if (key < SSNES_FIRST_META_KEY)
return NULL;
bool ret = driver.input->key_pressed(driver.input_data, key);
return PyBool_FromLong((long)ret);
}
static PyMethodDef SNESMethods[] = {
{ "read_wram", PY_READ_FUNC_DECL(WRAM), METH_VARARGS, "Read WRAM from SNES." },
{ "read_vram", PY_READ_FUNC_DECL(VRAM), METH_VARARGS, "Read VRAM from SNES." },
{ "read_apuram", PY_READ_FUNC_DECL(APURAM), METH_VARARGS, "Read APURAM from SNES." },
{ "read_cgram", PY_READ_FUNC_DECL(CGRAM), METH_VARARGS, "Read CGRAM from SNES." },
{ "read_oam", PY_READ_FUNC_DECL(OAM), METH_VARARGS, "Read OAM from SNES." },
2011-06-08 15:53:15 +00:00
{ "input", py_read_input, METH_VARARGS, "Read input state from SNES." },
{ "input_meta", py_read_input_meta, METH_VARARGS, "Read SSNES specific input." },
{ NULL, NULL, 0, NULL }
};
#define DECL_ATTR_SNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_##attr))
#define DECL_ATTR_SSNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SSNES_##attr))
2011-06-08 15:53:15 +00:00
static void py_set_attrs(PyObject *mod)
{
DECL_ATTR_SNES(B);
DECL_ATTR_SNES(Y);
DECL_ATTR_SNES(SELECT);
DECL_ATTR_SNES(START);
DECL_ATTR_SNES(UP);
DECL_ATTR_SNES(DOWN);
DECL_ATTR_SNES(LEFT);
DECL_ATTR_SNES(RIGHT);
DECL_ATTR_SNES(A);
DECL_ATTR_SNES(X);
DECL_ATTR_SNES(L);
DECL_ATTR_SNES(R);
DECL_ATTR_SSNES(FAST_FORWARD_KEY);
DECL_ATTR_SSNES(FAST_FORWARD_HOLD_KEY);
DECL_ATTR_SSNES(LOAD_STATE_KEY);
DECL_ATTR_SSNES(SAVE_STATE_KEY);
DECL_ATTR_SSNES(FULLSCREEN_TOGGLE_KEY);
DECL_ATTR_SSNES(QUIT_KEY);
DECL_ATTR_SSNES(STATE_SLOT_PLUS);
DECL_ATTR_SSNES(STATE_SLOT_MINUS);
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_PLUS);
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_MINUS);
DECL_ATTR_SSNES(REWIND);
DECL_ATTR_SSNES(MOVIE_RECORD_TOGGLE);
DECL_ATTR_SSNES(PAUSE_TOGGLE);
DECL_ATTR_SSNES(FRAMEADVANCE);
DECL_ATTR_SSNES(RESET);
DECL_ATTR_SSNES(SHADER_NEXT);
DECL_ATTR_SSNES(SHADER_PREV);
DECL_ATTR_SSNES(CHEAT_INDEX_PLUS);
DECL_ATTR_SSNES(CHEAT_INDEX_MINUS);
DECL_ATTR_SSNES(CHEAT_TOGGLE);
DECL_ATTR_SSNES(SCREENSHOT);
DECL_ATTR_SSNES(DSP_CONFIG);
2011-06-08 15:53:15 +00:00
}
static PyModuleDef SNESModule = {
PyModuleDef_HEAD_INIT, "snes", NULL, -1, SNESMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_SNES(void)
{
2011-06-08 15:53:15 +00:00
PyObject *mod = PyModule_Create(&SNESModule);
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)
{
if (!str)
return NULL;
unsigned size = strlen(str) + 2;
char *ret = malloc(size);
if (!ret)
return NULL;
strlcpy(ret, str, size);
ret[size - 2] = '\n';
ret[size - 1] = '\0';
return ret;
}
// Need to make sure that first-line indentation is 0. :(
static char* align_program(const char *program)
{
char *prog = strdup(program);
if (!prog)
return NULL;
size_t prog_size = strlen(program) + 1;
char *new_prog = calloc(1, prog_size);
if (!new_prog)
return NULL;
char *line = dupe_newline(strtok(prog, "\n"));
if (!line)
{
free(prog);
return NULL;
}
unsigned skip_chars = 0;
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(NULL, "\n"));
}
free(prog);
return new_prog;
}
py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
{
PyImport_AppendInittab("snes", &PyInit_SNES);
Py_Initialize();
py_state_t *handle = calloc(1, sizeof(*handle));
handle->main = PyImport_AddModule("__main__");
if (!handle->main)
goto error;
2011-06-07 13:33:29 +00:00
if (is_file)
{
FILE *file = fopen(script, "r");
if (!file)
goto error;
PyRun_SimpleFile(file, script);
fclose(file);
}
else
{
char *script_ = align_program(script);
if (script_)
{
PyRun_SimpleString(script_);
free(script_);
}
}
handle->dict = PyModule_GetDict(handle->main);
if (!handle->dict)
goto error;
PyObject *hook = PyDict_GetItemString(handle->dict, pyclass);
if (!hook)
goto error;
handle->inst = PyObject_CallFunction(hook, NULL);
if (!handle->inst)
goto error;
return handle;
error:
2011-06-08 15:53:15 +00:00
PyErr_Print();
py_state_free(handle);
return NULL;
}
void py_state_free(py_state_t *handle)
{
if (handle)
{
if (handle->main)
Py_DECREF(handle->main);
if (handle->dict)
Py_DECREF(handle->dict);
if (handle->inst)
Py_DECREF(handle->inst);
free(handle);
}
Py_Finalize();
}
int py_state_get(py_state_t *handle, const char *id,
unsigned frame_count)
{
PyObject *ret = PyObject_CallMethod(handle->inst, (char*)id, (char*)"I", frame_count);
if (!ret)
2011-06-06 18:18:31 +00:00
{
if (!handle->warned_ret)
SSNES_WARN("Didn't get return value from script! Bug?\n");
handle->warned_ret = true;
return 0;
2011-06-06 18:18:31 +00:00
}
int retval = PyLong_AsLong(ret);
Py_DECREF(ret);
return retval;
}