Initial commit

This commit is contained in:
Jonas Kulla 2013-09-01 16:27:21 +02:00
commit ff25887f41
119 changed files with 24901 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.o
*.pro.*
*.bak
*.frag.xxd
*.ttf.xxd
mkxp

85
README.md Normal file
View File

@ -0,0 +1,85 @@
# mkxp
mkxp is a project that seeks to provide a fully open source implementation of the RGSS (Ruby Game Scripting System) interface used in the popular game creation software "RPG Maker XP" (trademark by Enterbrain, Inc.), with focus on Linux. The goal is to be able to run games created with the above software natively without changing a single file.
## Bindings
Bindings provide the interpreted language environment to run game scripts in. As of right now, they are compiled directly into the executable. Currently there are three bindings:
### MRI
Website: https://www.ruby-lang.org/en/
Matz's Ruby Interpreter, also called CRuby, is the most widely deployed version of ruby. If you're interested in running games created with RPG Maker XP, this is the one you should go for. MRI 1.8 is what was used in RPG Maker XP, however, this binding is written against 2.0 (the latest version). For games utilizing only the default scripts provided by Enterbrain, this binding works quite well so far. Note that there are language and syntax differences between 1.8 and 2.0, so some user created scripts may not work correctly.
For a list of differences, see:
http://stackoverflow.com/questions/21574/what-is-the-difference-between-ruby-1-8-and-ruby-1-9
To select this backend, run `qmake BINDING=BINDING_MRI`
### mruby (Lightweight Ruby)
Website: https://github.com/mruby/mruby
mruby is a new endeavor by Matz and others to create a more lightweight, spec-adhering, embeddable Ruby implementation. You can think of it as a Ruby version of Lua.
Due to heavy differences between mruby and MRI as well as lacking modules, running RPG Maker games with this backend will most likely not work correctly. It is provided as experimental code. You can eg. write your own ruby scripts and run them with this backend.
Some extensions to the standard classes/modules are provided taking the RPG Maker XP helpfile as a quasi "standard". These include Marshal, File, FileTest and Time.
To select this backend, run `qmake BINDING=BINDING_MRUBY`
### null
This backend only exists for testing purposes and does nothing (the engine quits immediately).
To select this backend, run `qmake BINDING=BINDING_NULL`
## Dependencies
* QtCore 4.8
* libsigc++
* PhysFS
* glew
* SDL2
* SDL2_image
* SDL2_ttf
* sfml-system 2.0
* sfml-audio 2.0
(If no version specified, assume latest)
### MRI binding:
Place a recent version of ruby in the project folder and build it.
### mruby binding:
Place a recent version of mruby in the project folder and build it.
To run mkxp, you should have a graphics card capable of at least **OpenGL 3.0**
## Building
mkxp employs Qt's qmake build system, so you'll need to install that beforehand. After cloning mkxp, run one of the above qmake calls, or simply `qmake` to select the default backend (currently MRI), then `make`.
## Configuration
mkxp reads configuration data from the file "mkxp.conf" contained in the current directory. The format is ini-style.
* "gameFolder": Specifies where mkxp will look for the game scripts. Default is the current directory.
* "customScript": Specifies a raw ruby script file to be run instead of an RPG Maker game, residing in "gameFolder".
* "RTPs": Specifies a list of space separated paths to RTPs to be used. (See next section)
Most other entries are self explanatory.
## RTPs
As of right now, mkxp doesn't support midi files, so to use the default RTPs provided by Enterbrain you will have to convert all midi tracks (those in BGM and ME) to ogg or wav. Make sure that the file names match up, ie. "foobar.mid" should be converted to "foobar.ogg".
## Fonts
In the RMXP version of RGSS, fonts are loaded directly from system specific search paths (meaning they must be installed to be available to games). Because this whole thing is a giant platform-dependent headache, I decided to implement the behavior Enterbrain thankfully added in VX Ace: loading fonts will automatically search a folder called "Fonts", which obeys the default searchpath behavior (ie. it can be located directly in the game folder, or an RTP).
If a requested font is not found, no error is generated. Instead, a built-in font is used (currently "Liberation Sans").
## What doesn't work
* Audio formats other than ogg/wav (this might change in the future)
* Audio "pitch" parameter
* The Win32API ruby class (for obvious reasons)
* Loading Bitmaps with sizes greater than the OpenGL texture size limit (around 8192 on modern cards)

BIN
assets/liberation.ttf Normal file

Binary file not shown.

View File

@ -0,0 +1,88 @@
/*
** audio-binding.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "audio.h"
#include "globalstate.h"
#include "binding-util.h"
#include "exception.h"
#define DEF_PLAY_STOP(entity) \
RB_METHOD(audio_##entity##Play) \
{ \
RB_UNUSED_PARAM; \
const char *filename; \
int volume = 100; \
int pitch = 100; \
rb_get_args(argc, argv, "z|ii", &filename, &volume, &pitch); \
GUARD_EXC( gState->audio().entity##Play(filename, volume, pitch); ) \
return Qnil; \
} \
RB_METHOD(audio_##entity##Stop) \
{ \
RB_UNUSED_PARAM; \
gState->audio().entity##Stop(); \
return Qnil; \
}
#define DEF_FADE(entity) \
RB_METHOD(audio_##entity##Fade) \
{ \
RB_UNUSED_PARAM; \
int time; \
rb_get_args(argc, argv, "i", &time); \
gState->audio().bgmFade(time); \
return Qnil; \
}
#define DEF_PLAY_STOP_FADE(entity) \
DEF_PLAY_STOP(entity) \
DEF_FADE(entity)
DEF_PLAY_STOP_FADE( bgm )
DEF_PLAY_STOP_FADE( bgs )
DEF_PLAY_STOP_FADE( me )
DEF_PLAY_STOP( se )
#define BIND_PLAY_STOP(entity) \
_rb_define_module_function(module, #entity "_play", audio_##entity##Play); \
_rb_define_module_function(module, #entity "_stop", audio_##entity##Stop);
#define BIND_FADE(entity) \
_rb_define_module_function(module, #entity "_fade", audio_##entity##Fade);
#define BIND_PLAY_STOP_FADE(entity) \
BIND_PLAY_STOP(entity) \
BIND_FADE(entity)
void
audioBindingInit()
{
VALUE module = rb_define_module("Audio");
BIND_PLAY_STOP_FADE( bgm )
BIND_PLAY_STOP_FADE( bgs )
BIND_PLAY_STOP_FADE( me )
BIND_PLAY_STOP( se )
}

313
binding-mri/binding-mri.cpp Normal file
View File

@ -0,0 +1,313 @@
/*
** binding-mri.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "binding.h"
#include "binding-util.h"
#include "globalstate.h"
#include "eventthread.h"
#include "filesystem.h"
#include "./ruby/include/ruby.h"
#include "zlib.h"
#include <QFile>
#include <QByteArray>
#include <QDebug>
extern const char module_rpg[];
static void mriBindingExecute();
static void mriBindingTerminate();
ScriptBinding scriptBindingImpl =
{
mriBindingExecute,
mriBindingTerminate
};
ScriptBinding *scriptBinding = &scriptBindingImpl;
void tableBindingInit();
void etcBindingInit();
void fontBindingInit();
void bitmapBindingInit();
void spriteBindingInit();
void viewportBindingInit();
void planeBindingInit();
void windowBindingInit();
void tilemapBindingInit();
void inputBindingInit();
void audioBindingInit();
void graphicsBindingInit();
void fileIntBindingInit();
static VALUE mriPrint(int, VALUE*, VALUE);
static VALUE mriP(int, VALUE*, VALUE);
static void mriBindingInit()
{
tableBindingInit();
etcBindingInit();
fontBindingInit();
bitmapBindingInit();
spriteBindingInit();
viewportBindingInit();
planeBindingInit();
windowBindingInit();
tilemapBindingInit();
inputBindingInit();
audioBindingInit();
graphicsBindingInit();
fileIntBindingInit();
_rb_define_module_function(rb_mKernel, "print", mriPrint);
_rb_define_module_function(rb_mKernel, "p", mriP);
rb_eval_string(module_rpg);
rb_define_global_const("MKXP", Qtrue);
}
static void
showMsg(const QByteArray &msg)
{
gState->eThread().showMessageBox(msg.constData());
}
static void printP(int argc, VALUE *argv,
const char *convMethod, const char *sep)
{
VALUE dispString = rb_str_buf_new(128);
ID conv = rb_intern(convMethod);
for (int i = 0; i < argc; ++i)
{
VALUE str = rb_funcall(argv[i], conv, 0);
rb_str_buf_append(dispString, str);
if (i < argc)
rb_str_buf_cat2(dispString, sep);
}
showMsg(RSTRING_PTR(dispString));
rb_str_free(dispString);
}
RB_METHOD(mriPrint)
{
RB_UNUSED_PARAM;
printP(argc, argv, "to_s", "");
return Qnil;
}
RB_METHOD(mriP)
{
RB_UNUSED_PARAM;
printP(argc, argv, "inspect", "\n");
return Qnil;
}
static void runCustomScript(const char *filename)
{
QFile scriptFile(filename);
if (!scriptFile.open(QFile::ReadOnly))
{
showMsg(QByteArray("Unable to open '") + filename + "'");
return;
}
QByteArray scriptData = scriptFile.readAll();
scriptFile.close();
rb_eval_string_protect(scriptData.constData(), 0);
}
VALUE kernelLoadDataInt(const char *filename);
struct Script
{
QByteArray name;
QByteArray encData;
uint32_t unknown;
QByteArray decData;
};
static void runRMXPScripts()
{
const QByteArray &scriptPack = gState->rtData().config.game.scripts;
if (!gState->fileSystem().exists(scriptPack.constData()))
{
showMsg("Unable to open '" + scriptPack + "'");
return;
}
VALUE scriptArray = kernelLoadDataInt(scriptPack.constData());
if (rb_type(scriptArray) != RUBY_T_ARRAY)
{
showMsg("Failed to read script data");
return;
}
int scriptCount = RARRAY_LEN(scriptArray);
QByteArray decodeBuffer;
decodeBuffer.resize(0x1000);
QVector<Script> encScripts(scriptCount);
for (int i = 0; i < scriptCount; ++i)
{
VALUE script = rb_ary_entry(scriptArray, i);
if (rb_type(script) != RUBY_T_ARRAY)
{
continue;
}
VALUE scriptUnknown = rb_ary_entry(script, 0);
VALUE scriptName = rb_ary_entry(script, 1);
VALUE scriptString = rb_ary_entry(script, 2);
Script &sc = encScripts[i];
sc.name = RSTRING_PTR(scriptName);
sc.encData = QByteArray(RSTRING_PTR(scriptString), RSTRING_LEN(scriptString));
sc.unknown = FIX2UINT(scriptUnknown);
}
for (int i = 0; i < scriptCount; ++i)
{
Script &sc = encScripts[i];
int result = Z_OK;
ulong bufferLen;
while (true)
{
unsigned char *bufferPtr =
reinterpret_cast<unsigned char*>(const_cast<char*>(decodeBuffer.constData()));
const unsigned char *sourcePtr =
reinterpret_cast<const unsigned char*>(sc.encData.constData());
bufferLen = decodeBuffer.length();
result = uncompress(bufferPtr, &bufferLen,
sourcePtr, sc.encData.length());
bufferPtr[bufferLen] = '\0';
if (result != Z_BUF_ERROR)
break;
decodeBuffer.resize(decodeBuffer.size()*2);
}
if (result != Z_OK)
{
static char buffer[256];
snprintf(buffer, sizeof(buffer), "Error decoding script %d: '%s'",
i, sc.name.constData());
showMsg(buffer);
break;
}
// QFile file(QString("/home/Ancurio/programming/C++/mkxp/dump/%1.rb").arg(i, 3, 10, QChar('0')));
// if (file.open(QFile::WriteOnly))
// file.write(decodeBuffer.constData(), bufferLen);
sc.decData = QByteArray(decodeBuffer.constData(), bufferLen);
ruby_script(sc.name.constData());
rb_gc_start();
qDebug() << "Executing script:" << QString("%1").arg(i, 3, 10, QChar('0'));
/* Execute code */
rb_eval_string_protect(decodeBuffer.constData(), 0);
VALUE exc = rb_gv_get("$!");
if (rb_type(exc) != RUBY_T_NIL)
break;
}
// QFile file("/home/Ancurio/programming/C++/mkxp/dump/index");
// if (file.open(QFile::WriteOnly))
// {
// for (int i = 0; i < encScripts.size(); ++i)
// {
// const Script &sc = encScripts[i];
// file.write(sc.name);
// }
// }
}
static void mriBindingExecute()
{
ruby_setup();
RbData rbData;
gState->setBindingData(&rbData);
mriBindingInit();
QByteArray &customScript = gState->rtData().config.customScript;
if (!customScript.isEmpty())
runCustomScript(customScript.constData());
else
runRMXPScripts();
VALUE exc = rb_gv_get("$!");
if (rb_type(exc) != RUBY_T_NIL && !rb_eql(rb_obj_class(exc), rb_eSystemExit))
{
qDebug() << "Had exception:" << rb_class2name(rb_obj_class(exc));
VALUE bt = rb_funcall(exc, rb_intern("backtrace"), 0);
rb_p(bt);
VALUE msg = rb_funcall(exc, rb_intern("message"), 0);
if (RSTRING_LEN(msg) < 256)
showMsg(RSTRING_PTR(msg));
else
qDebug() << (RSTRING_PTR(msg));
}
ruby_cleanup(0);
gState->rtData().rqTermAck = true;
}
static void mriBindingTerminate()
{
rb_raise(rb_eSystemExit, " ");
}

View File

@ -0,0 +1,40 @@
/*
** binding-types.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BINDINGTYPES_H
#define BINDINGTYPES_H
#include "binding-util.h"
DECL_TYPE(Table);
DECL_TYPE(Rect);
DECL_TYPE(Color);
DECL_TYPE(Tone);
DECL_TYPE(Font);
DECL_TYPE(Bitmap);
DECL_TYPE(Sprite);
DECL_TYPE(Plane);
DECL_TYPE(Viewport);
DECL_TYPE(Tilemap);
DECL_TYPE(Window);
#endif // BINDINGTYPES_H

View File

@ -0,0 +1,306 @@
/*
** binding-util.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "binding-util.h"
#include "globalstate.h"
#include "exception.h"
#include "util.h"
#include <stdarg.h>
#include <QDebug>
void initType(rb_data_type_struct &type,
const char *name,
void (*freeInst)(void *))
{
type.wrap_struct_name = name;
type.function.dmark = 0;
type.function.dsize = 0;
type.function.dfree = freeInst;
type.function.reserved[0] =
type.function.reserved[1] = 0;
type.parent = 0;
}
RbData *getRbData()
{
return static_cast<RbData*>(gState->bindingData());
}
//enum RbException
//{
// RGSS = 0,
// PHYSFS,
// SDL,
// ErrnoENOENT,
// IOError,
// TypeError,
// ArgumentError,
// RbExceptionsMax
//};
struct
{
RbException id;
const char *name;
} static customExc[] =
{
{ RGSS, "RGSSError" },
{ PHYSFS, "PHYSFSError" },
{ SDL, "SDLError" }
};
RbData::RbData()
{
for (size_t i = 0; i < ARRAY_SIZE(customExc); ++i)
exc[customExc[i].id] = rb_define_class(customExc[i].name, rb_eException);
exc[ErrnoENOENT] = rb_const_get(rb_const_get(rb_cObject, rb_intern("Errno")), rb_intern("ENOENT"));
exc[IOError] = rb_eIOError;
exc[TypeError] = rb_eTypeError;
exc[ArgumentError] = rb_eArgError;
}
RbData::~RbData()
{
}
/* Indexed with Exception::Type */
static const RbException excToRbExc[] =
{
RGSS, /* RGSSError */
ErrnoENOENT, /* NoFileError */
IOError,
TypeError,
ArgumentError,
PHYSFS, /* PHYSFSError */
SDL /* SDLError */
};
void raiseRbExc(const Exception &exc)
{
RbData *data = getRbData();
VALUE excClass = data->exc[excToRbExc[exc.type]];
static char buffer[512];
exc.snprintf(buffer, sizeof(buffer));
rb_raise(excClass, buffer);
}
int
rb_get_args(int argc, VALUE *argv, const char *format, ...)
{
char c;
VALUE *arg = argv;
va_list ap;
bool opt = false;
int argI = 0;
va_start(ap, format);
while ((c = *format++))
{
switch (c)
{
case '|' :
break;
default:
// FIXME print num of needed args vs provided
if (argc <= argI && !opt)
rb_raise(rb_eArgError, "wrong number of arguments");
break;
}
if (argI >= argc)
break;
switch (c)
{
case 'o' :
{
if (argI >= argc)
break;
VALUE *obj = va_arg(ap, VALUE*);
*obj = *arg++;
++argI;
break;
}
case 'S' :
{
if (argI >= argc)
break;
VALUE *str = va_arg(ap, VALUE*);
VALUE tmp = *arg;
if (!rb_type(tmp) == RUBY_T_STRING)
rb_raise(rb_eTypeError, "Argument %d: Expected string", argI);
*str = tmp;
++argI;
break;
}
case 's' :
{
if (argI >= argc)
break;
const char **s = va_arg(ap, const char**);
int *len = va_arg(ap, int*);
VALUE tmp = *arg;
if (!rb_type(tmp) == RUBY_T_STRING)
rb_raise(rb_eTypeError, "Argument %d: Expected string", argI);
*s = RSTRING_PTR(tmp);
*len = RSTRING_LEN(tmp);
++argI;
break;
}
case 'z' :
{
if (argI >= argc)
break;
const char **s = va_arg(ap, const char**);
VALUE tmp = *arg++;
if (!rb_type(tmp) == RUBY_T_STRING)
rb_raise(rb_eTypeError, "Argument %d: Expected string", argI);
*s = RSTRING_PTR(tmp);
++argI;
break;
}
case 'f' :
{
if (argI >= argc)
break;
double *f = va_arg(ap, double*);
VALUE fVal = *arg++;
switch (rb_type(fVal))
{
case RUBY_T_FLOAT :
*f = rb_float_value(fVal);
break;
case RUBY_T_FIXNUM :
*f = rb_fix2int(fVal);
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected float", argI);
}
++argI;
break;
}
case 'i' :
{
if (argI >= argc)
break;
int *i = va_arg(ap, int*);
VALUE iVal = *arg++;
switch (rb_type(iVal))
{
case RUBY_T_FLOAT :
// FIXME check int range?
*i = rb_num2long(iVal);
break;
case RUBY_T_FIXNUM :
*i = rb_fix2int(iVal);
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected fixnum", argI);
}
++argI;
break;
}
case 'b' :
{
if (argI >= argc)
break;
bool *b = va_arg(ap, bool*);
VALUE bVal = *arg++;
switch (rb_type(bVal))
{
case RUBY_T_TRUE :
*b = true;
break;
case RUBY_T_FALSE :
case RUBY_T_NIL :
*b = false;
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected bool", argI);
}
++argI;
break;
}
case '|' :
opt = true;
break;
default:
rb_raise(rb_eFatal, "invalid argument specifier %c", c);
}
}
// FIXME print num of needed args vs provided
if (!c && argc > argI)
rb_raise(rb_eArgError, "wrong number of arguments");
va_end(ap);
return argI;
}

297
binding-mri/binding-util.h Normal file
View File

@ -0,0 +1,297 @@
/*
** binding-util.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BINDING_UTIL_H
#define BINDING_UTIL_H
#include "./ruby/ruby.h"
#define PRIV_IV "priv"
enum RbException
{
RGSS = 0,
PHYSFS,
SDL,
ErrnoENOENT,
IOError,
TypeError,
ArgumentError,
RbExceptionsMax
};
struct RbData
{
VALUE exc[RbExceptionsMax];
RbData();
~RbData();
};
RbData *getRbData();
struct Exception;
void
raiseRbExc(const Exception &exc);
#define DECL_TYPE(Klass) \
extern rb_data_type_struct Klass##Type
#define DEF_TYPE(Klass) \
rb_data_type_struct Klass##Type
void initType(rb_data_type_struct &type,
const char *name,
void (*freeInst)(void*));
template<class C>
static inline void freeInstance(void *inst)
{
delete static_cast<C*>(inst);
}
#define INIT_TYPE(Klass) initType(Klass##Type, #Klass, freeInstance<Klass>)
template<class C>
static inline C *
getPrivateData(VALUE self)
{
VALUE priv = rb_iv_get(self, PRIV_IV);
return static_cast<C*>(RTYPEDDATA_DATA(priv));
}
template<class C>
static inline C *
getPrivateDataCheck(VALUE self, const rb_data_type_struct &type)
{
VALUE priv = rb_iv_get(self, PRIV_IV);
void *obj = rb_check_typeddata(priv, &type);
return static_cast<C*>(obj);
}
static inline void
setPrivateData(VALUE self, void *p, const rb_data_type_struct &type)
{
VALUE priv = rb_data_typed_object_alloc(rb_cData, p, &type);
rb_iv_set(self, PRIV_IV, priv);
}
inline VALUE
wrapObject(void *p, const rb_data_type_struct &type)
{
VALUE klass = rb_const_get(rb_cObject, rb_intern(type.wrap_struct_name));
VALUE obj = rb_obj_alloc(klass);
setPrivateData(obj, p, type);
return obj;
}
inline VALUE
wrapProperty(VALUE self, void *prop, const char *iv,
const rb_data_type_struct &type)
{
VALUE propObj = wrapObject(prop, type);
rb_iv_set(self, iv, propObj);
return propObj;
}
inline void
wrapNilProperty(VALUE self, const char *iv)
{
rb_iv_set(self, iv, Qnil);
}
/* Implemented: oSszfib| */
int
rb_get_args(int argc, VALUE *argv, const char *format, ...);
typedef VALUE (*RubyMethod)(int argc, VALUE *argv, VALUE self);
static inline void
_rb_define_method(VALUE klass, const char *name, RubyMethod func)
{
rb_define_method(klass, name, RUBY_METHOD_FUNC(func), -1);
}
static inline void
rb_define_class_method(VALUE klass, const char *name, RubyMethod func)
{
rb_define_singleton_method(klass, name, RUBY_METHOD_FUNC(func), -1);
}
static inline void
_rb_define_module_function(VALUE module, const char *name, RubyMethod func)
{
rb_define_module_function(module, name, RUBY_METHOD_FUNC(func), -1);
}
template<class C>
static inline VALUE
objectLoad(int argc, VALUE *argv, VALUE self, rb_data_type_struct &type)
{
const char *data;
int dataLen;
rb_get_args(argc, argv, "s", &data, &dataLen);
VALUE obj = rb_obj_alloc(self);
C *c = C::deserialize(data, dataLen);
setPrivateData(obj, c, type);
return obj;
}
static inline VALUE
rb_bool_new(bool value)
{
return value ? Qtrue : Qfalse;
}
#define RB_METHOD(name) \
static VALUE name(int argc, VALUE *argv, VALUE self)
#define RB_UNUSED_PARAM \
{ (void) argc; (void) argv; (void) self; }
#define MARSH_LOAD_FUN(Typ) \
RB_METHOD(Typ##Load) \
{ \
return objectLoad<Typ>(argc, argv, self, Typ##Type); \
}
#define CLONE_FUNC(Klass) \
static mrb_value \
Klass##Clone(mrb_state *mrb, mrb_value self) \
{ \
Klass *k = getPrivateData<Klass>(mrb, self); \
mrb_value dupObj = mrb_obj_clone(mrb, self); \
Klass *dupK = new Klass(*k); \
setPrivateData(mrb, dupObj, dupK, Klass##Type); \
return dupObj; \
}
#define CLONE_FUN(Klass) \
RB_METHOD(Klass##Clone) \
{ \
RB_UNUSED_PARAM \
Klass *k = getPrivateData<Klass>(self); \
VALUE dupObj = rb_obj_clone(self); \
Klass *dupK = new Klass(*k); \
setPrivateData(dupObj, dupK, Klass##Type); \
return dupObj; \
}
/* If we're not binding a disposable class,
* we want to #undef DEF_PROP_CHK_DISP */
#define DEF_PROP_CHK_DISP \
checkDisposed(k, DISP_CLASS_NAME);
#define DEF_PROP_OBJ(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); (void) k; \
DEF_PROP_CHK_DISP \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj; \
PropKlass *prop; \
rb_get_args(argc, argv, "o", &propObj); \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC( k->set##PropName(prop); ) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
/* Object property with allowed NIL */
#define DEF_PROP_OBJ_NIL(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); (void) k; \
DEF_PROP_CHK_DISP \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj; \
PropKlass *prop; \
rb_get_args(argc, argv, "o", &propObj); \
if (rb_type(propObj) == RUBY_T_NIL) \
prop = 0; \
else \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC( k->set##PropName(prop); ) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
#define DEF_PROP(Klass, type, PropName, param_t_s, value_fun) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); \
DEF_PROP_CHK_DISP \
return value_fun(k->get##PropName()); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
Klass *k = getPrivateData<Klass>(self); \
type value; \
rb_get_args(argc, argv, param_t_s, &value); \
GUARD_EXC( k->set##PropName(value); ) \
return value_fun(value); \
}
#define DEF_PROP_I(Klass, PropName) \
DEF_PROP(Klass, int, PropName, "i", rb_fix_new)
#define DEF_PROP_F(Klass, PropName) \
DEF_PROP(Klass, double, PropName, "f", rb_float_new)
#define DEF_PROP_B(Klass, PropName) \
DEF_PROP(Klass, bool, PropName, "b", rb_bool_new)
#define INIT_PROP_BIND(Klass, PropName, prop_name_s) \
{ \
_rb_define_method(klass, prop_name_s, Klass##Get##PropName); \
_rb_define_method(klass, prop_name_s "=", Klass##Set##PropName); \
}
#define GUARD_EXC(exp) \
{ try { exp } catch (const Exception &exc) { raiseRbExc(exc); } }
#endif // BINDING_UTIL_H

View File

@ -0,0 +1,324 @@
/*
** bitmap-binding.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitmap.h"
#include "font.h"
#include "exception.h"
#include "disposable-binding.h"
#include "binding-util.h"
#include "binding-types.h"
#include <QDebug>
#define DISP_CLASS_NAME "bitmap"
DEF_TYPE(Bitmap);
RB_METHOD(bitmapInitialize)
{
Bitmap *b = 0;
if (argc == 1)
{
char *filename;
rb_get_args(argc, argv, "z", &filename);
GUARD_EXC( b = new Bitmap(filename); )
}
else
{
int width, height;
rb_get_args(argc, argv, "ii", &width, &height);
b = new Bitmap(width, height);
}
setPrivateData(self, b, BitmapType);
/* Wrap properties */
Font *font = new Font();
b->setFont(font);
font->setColor(new Color(*font->getColor()));
VALUE fontProp = wrapProperty(self, font, "font", FontType);
wrapProperty(fontProp, font->getColor(), "color", ColorType);
return self;
}
RB_METHOD(bitmapWidth)
{
RB_UNUSED_PARAM;
Bitmap *b = getPrivateData<Bitmap>(self);
int value = 0;
GUARD_EXC( value = b->width(); );
return INT2FIX(value);
}
RB_METHOD(bitmapHeight)
{
RB_UNUSED_PARAM;
Bitmap *b = getPrivateData<Bitmap>(self);
int value = 0;
GUARD_EXC( value = b->height(); );
return INT2FIX(value);
}
RB_METHOD(bitmapRect)
{
RB_UNUSED_PARAM;
Bitmap *b = getPrivateData<Bitmap>(self);
IntRect rect;
GUARD_EXC( rect = b->rect(); );
Rect *r = new Rect(rect);
return wrapObject(r, RectType);
}
RB_METHOD(bitmapBlt)
{
Bitmap *b = getPrivateData<Bitmap>(self);
int x, y;
VALUE srcObj;
VALUE srcRectObj;
int opacity = 255;
Bitmap *src;
Rect *srcRect;
rb_get_args(argc, argv, "iioo|i", &x, &y, &srcObj, &srcRectObj, &opacity);
src = getPrivateDataCheck<Bitmap>(srcObj, BitmapType);
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
GUARD_EXC( b->blt(x, y, *src, srcRect->toIntRect(), opacity); );
return self;
}
RB_METHOD(bitmapStretchBlt)
{
Bitmap *b = getPrivateData<Bitmap>(self);
VALUE destRectObj;
VALUE srcObj;
VALUE srcRectObj;
int opacity = 255;
Bitmap *src;
Rect *destRect, *srcRect;
rb_get_args(argc, argv, "ooo|i", &destRectObj, &srcObj, &srcRectObj);
src = getPrivateDataCheck<Bitmap>(srcObj, BitmapType);
destRect = getPrivateDataCheck<Rect>(destRectObj, RectType);
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
GUARD_EXC( b->stretchBlt(destRect->toIntRect(), *src, srcRect->toIntRect(), opacity); );
return self;
}
RB_METHOD(bitmapFillRect)
{
Bitmap *b = getPrivateData<Bitmap>(self);
VALUE colorObj;
Color *color;
if (argc == 2)
{
VALUE rectObj;
Rect *rect;
rb_get_args(argc, argv, "oo", &rectObj, &colorObj);
rect = getPrivateDataCheck<Rect>(rectObj, RectType);
color = getPrivateDataCheck<Color>(colorObj, ColorType);
GUARD_EXC( b->fillRect(rect->toIntRect(), color->norm); );
}
else
{
int x, y, width, height;
rb_get_args(argc, argv, "iiiio", &x, &y, &width, &height, &colorObj);
color = getPrivateDataCheck<Color>(colorObj, ColorType);
GUARD_EXC( b->fillRect(x, y, width, height, color->norm); );
}
return self;
}
RB_METHOD(bitmapClear)
{
RB_UNUSED_PARAM;
Bitmap *b = getPrivateData<Bitmap>(self);
GUARD_EXC( b->clear(); )
return self;
}
RB_METHOD(bitmapGetPixel)
{
Bitmap *b = getPrivateData<Bitmap>(self);
int x, y;
rb_get_args(argc, argv, "ii", &x, &y);
GUARD_EXC(
if (x < 0 || y < 0 || x >= b->width() || y >= b->height())
return Qnil;
)
Vec4 value;
GUARD_EXC( value = b->getPixel(x, y); );
Color *color = new Color(value);
return wrapObject(color, ColorType);
}
RB_METHOD(bitmapSetPixel)
{
Bitmap *b = getPrivateData<Bitmap>(self);
int x, y;
VALUE colorObj;
Color *color;
rb_get_args(argc, argv, "iio", &x, &y, &colorObj);
color = getPrivateDataCheck<Color>(colorObj, ColorType);
GUARD_EXC( b->setPixel(x, y, color->norm); );
return self;
}
RB_METHOD(bitmapHueChange)
{
Bitmap *b = getPrivateData<Bitmap>(self);
int hue;
rb_get_args(argc, argv, "i", &hue);
GUARD_EXC( b->hueChange(hue); );
return self;
}
RB_METHOD(bitmapDrawText)
{
Bitmap *b = getPrivateData<Bitmap>(self);
const char *str;
int align = Bitmap::Left;
if (argc == 2 || argc == 3)
{
VALUE rectObj;
Rect *rect;
rb_get_args(argc, argv, "oz|i", &rectObj, &str, &align);
rect = getPrivateDataCheck<Rect>(rectObj, RectType);
GUARD_EXC( b->drawText(rect->toIntRect(), str, align); );
}
else
{
int x, y, width, height;
rb_get_args(argc, argv, "iiiiz|i", &x, &y, &width, &height, &str, &align);
GUARD_EXC( b->drawText(x, y, width, height, str, align); );
}
return self;
}
RB_METHOD(bitmapTextSize)
{
Bitmap *b = getPrivateData<Bitmap>(self);
const char *str;
rb_get_args(argc, argv, "z", &str);
IntRect value;
GUARD_EXC( value = b->textSize(str); );
Rect *rect = new Rect(value);
return wrapObject(rect, RectType);
}
DEF_PROP_OBJ(Bitmap, Font, Font, "font")
CLONE_FUN(Bitmap)
void
bitmapBindingInit()
{
INIT_TYPE(Bitmap);
VALUE klass = rb_define_class("Bitmap", rb_cObject);
disposableBindingInit<Bitmap>(klass);
_rb_define_method(klass, "initialize", bitmapInitialize);
_rb_define_method(klass, "width", bitmapWidth);
_rb_define_method(klass, "height", bitmapHeight);
_rb_define_method(klass, "rect", bitmapRect);
_rb_define_method(klass, "blt", bitmapBlt);
_rb_define_method(klass, "stretch_blt", bitmapStretchBlt);
_rb_define_method(klass, "fill_rect", bitmapFillRect);
_rb_define_method(klass, "clear", bitmapClear);
_rb_define_method(klass, "get_pixel", bitmapGetPixel);
_rb_define_method(klass, "set_pixel", bitmapSetPixel);
_rb_define_method(klass, "hue_change", bitmapHueChange);
_rb_define_method(klass, "draw_text", bitmapDrawText);
_rb_define_method(klass, "text_size", bitmapTextSize);
INIT_PROP_BIND(Bitmap, Font, "font");
_rb_define_method(klass, "clone", BitmapClone);
}

View File

@ -0,0 +1,65 @@
/*
** disposable-binding.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DISPOSABLEBINDING_H
#define DISPOSABLEBINDING_H
#include "disposable.h"
#include "binding-util.h"
template<class C>
RB_METHOD(disposableDispose)
{
RB_UNUSED_PARAM;
Disposable *d = getPrivateData<C>(self);
d->dispose();
return Qnil;
}
template<class C>
RB_METHOD(disposableIsDisposed)
{
RB_UNUSED_PARAM;
Disposable *d = getPrivateData<C>(self);
return rb_bool_new(d->isDisposed());
}
template<class C>
static void disposableBindingInit(VALUE klass)
{
_rb_define_method(klass, "dispose", disposableDispose<C>);
_rb_define_method(klass, "disposed?", disposableIsDisposed<C>);
}
inline void checkDisposed(Disposable *d, const char *klassName)
{
RbData *data = getRbData(); (void) data;
if (d->isDisposed())
rb_raise(getRbData()->exc[RGSS], "disposed %s", klassName);
}
#endif // DISPOSABLEBINDING_H

204
binding-mri/etc-binding.cpp Normal file
View File

@ -0,0 +1,204 @@
/*
** etc-binding.cpp
**
** This file is part