Transition from QtCore to stdc++ / STL / boost

This looks like a pretty major change, but in reality,
80% of it is just renames of types and corresponding
methods.

The config parsing code has been completely replaced
with a boost::program_options based version. This
means that the config file format slightly changed
(checkout the updated README).

I still expect there to be bugs / unforseen events.
Those should be fixed in follow up commits.

Also, finally reverted back to using pkg-config to
locate and link libruby. Yay for less hacks!
This commit is contained in:
Jonas Kulla 2013-12-11 20:46:54 +01:00
parent 01529c5741
commit 2adf8ab265
40 changed files with 722 additions and 456 deletions

View File

@ -5,7 +5,7 @@ mkxp is a project that seeks to provide a fully open source implementation of th
It is licensed under the GNU General Public License v2+.
## Bindings
Bindings provide the glue code for an interpreted language environment to run game scripts in. As of right now, they are compiled directly into the executable (however, the scripting language itself might not be). Currently there are three bindings:
Bindings provide the glue code for an interpreted language environment to run game scripts in. Currently there are three bindings:
### MRI
Website: https://www.ruby-lang.org/en/
@ -15,8 +15,6 @@ Matz's Ruby Interpreter, also called CRuby, is the most widely deployed version
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 binding, run `qmake BINDING=BINDING_MRI`
### mruby (Lightweight Ruby)
Website: https://github.com/mruby/mruby
@ -24,23 +22,20 @@ mruby is a new endeavor by Matz and others to create a more lightweight, spec-ad
Due to heavy differences between mruby and MRI as well as lacking modules, running RPG Maker games with this binding will most likely not work correctly. It is provided as experimental code. You can eg. write your own ruby scripts and run them.
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.
Some extensions to the standard classes/modules are provided, taking the RPG Maker XP helpfile as a quasi "reference". These include Marshal, File, FileTest and Time.
**Important:** If you decide to use [mattn's oniguruma regexp gem](https://github.com/mattn/mruby-onig-regexp), don't forget to add `-lonig` to the linker flags to avoid ugly symbol overlaps with libc.
To select this binding, run `qmake BINDING=BINDING_MRUBY`
### null
This binding only exists for testing purposes and does nothing (the engine quits immediately). It can be used to eg. run a minimal RGSS game loop directly in C++.
To select this binding, run `qmake BINDING=BINDING_NULL`
## Dependencies / Building
## Dependencies
* QtCore 4.8
* libsigc++
* PhysFS
* glew
* Boost.Unordered (headers only)
* Boost.Program_options
* libsigc++ 2.0
* PhysFS (latest hg)
* GLEW >= 1.7
* OpenAL
* SDL2
* SDL2_image
@ -49,44 +44,44 @@ To select this binding, run `qmake BINDING=BINDING_NULL`
* pixman
* zlib (only ruby bindings)
(If no version specified, assume latest *development version*, ie. freshest one from git/hg/svn)
mkxp employs Qt's qmake build system, so you'll need to install that beforehand.
qmake will use pkg-config to locate the respective include/library paths. If you installed any dependencies into non-standard prefixes, make sure to adjust your `PKG_CONFIG_PATH` variable accordingly.
The exception is boost, which is weird in that it still hasn't managed to pull off pkg-config support (seriously?). *If you installed boost in a non-standard prefix*, you will need to pass its include path via `BOOST_I` and library path via `BOOST_L`, either as direct arguments to qmake (`qmake BOOST_I="/usr/include" ...`) or via environment variables.
**MRI-Binding**: pkg-config will look for `ruby-2.1.pc`, but you can modify mkxp.pro to use 2.0 instead. This is the default binding, so no arguments to qmake needed (`BINDING=BINDING_MRI` to be explicit).
**MRuby-Binding**: place the "mruby" folder into the project folder and build it first. Add `BINDING=BINDING_MRUBY` to qmake's arguments.
**Null-Binding**: Add `BINDING=BINDING_NULL` to qmake's arguments.
### Supported image/audio formats
These depend on the auxiliary libraries. For maximum RGSS compliance, build SDL2_image with png/jpg support, and SDL_sound with oggvorbis/wav/mp3 support.
### MRI binding:
Place a recent version of ruby in the project folder, apply all patches from "patches/ruby" and build it.
### mruby binding:
Place a recent version of mruby in the project folder and build it.
These depend on the SDL auxiliary libraries. For maximum RGSS compliance, build SDL2_image with png/jpg support, and SDL_sound with oggvorbis/wav/mp3 support.
To run mkxp, you should have a graphics card capable of at least **OpenGL 2.0** with an up-to-date driver installed.
## 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 binding (currently MRI), then `make`.
## Configuration
mkxp reads configuration data from the file "mkxp.conf" contained in the current directory. The format is ini-style. The "[General]" group may contain following entries:
mkxp reads configuration data from the file "mkxp.conf" contained in the current directory. The format is ini-style. Do *not* use quotes around file paths (spaces won't break). Following entries are interpreted:
| Key | Type | Default | Description |
| ---------------- | ----------- | ------- | ------------------------------------------------------------------------------- |
| debugMode | bool | false | Log OpenGL debug information to the console |
| winResizable | bool | false | Game window is resizable |
| fullscreen | bool | false | Start game in fullscreen (this can always be toggled with Alt-Enter at runtime) |
| fixedAspectRatio | bool | true | Don't stretch the game screen to fit the window size |
| smoothScaling | bool | false | Apply linear interpolation when game screen is stretched |
| vsync | bool | false | Sync screen redraws to the monitor refresh rate |
| defScreenW | int | 640 | Width the game window starts in (this is **not** the game resolution) |
| defScreenH | int | 480 | Height the game window starts in |
| fixedFramerate | int | 0 | FPS will be fixed to this amount. Ignored if 0. |
| frameSkip | bool | true | Skip frames to catch up (useful to disable eg. with Valgrind) |
| solidFonts | bool | false | Don't use alpha blending for fonts |
| gameFolder | string | "." | mkxp will look for all game related files here |
| allowSymlinks | bool | false | Allow symlinks to be followed in the game folder. |
| customScript | string | "" | Execute a raw ruby script file instead of an RPG Maker game. |
| RTPs | string list | "" | A list of space separated paths to RTPs to be used (See next section) |
| Key | Type | Default | Description |
| ---------------- | ------ | ------- | ------------------------------------------------------------------------------- |
| debugMode | bool | false | Log OpenGL debug information to the console |
| winResizable | bool | false | Game window is resizable |
| fullscreen | bool | false | Start game in fullscreen (this can always be toggled with Alt-Enter at runtime) |
| fixedAspectRatio | bool | true | Don't stretch the game screen to fit the window size |
| smoothScaling | bool | false | Apply linear interpolation when game screen is stretched |
| vsync | bool | false | Sync screen redraws to the monitor refresh rate |
| defScreenW | int | 640 | Width the game window starts in (this is **not** the game resolution) |
| defScreenH | int | 480 | Height the game window starts in |
| fixedFramerate | int | 0 | FPS will be fixed to this amount. Ignored if 0. |
| frameSkip | bool | true | Skip frames to catch up (useful to disable eg. with Valgrind) |
| solidFonts | bool | false | Don't use alpha blending for fonts |
| gameFolder | string | "." | mkxp will look for all game related files here |
| allowSymlinks | bool | false | Allow symlinks to be followed in the game folder. |
| customScript | string | "" | Execute a raw ruby script file instead of an RPG Maker game. |
| RTP | string | "" | Path to a Run Time Package to be used. Can be specified multiple times. |
## RTPs

View File

@ -24,19 +24,18 @@
#include "sharedstate.h"
#include "eventthread.h"
#include "filesystem.h"
#include "util.h"
#include "debugwriter.h"
#include "./ruby/include/ruby.h"
#include "./ruby/include/ruby/encoding.h"
#include <ruby.h>
#include <ruby/encoding.h>
#include <string>
#include <zlib.h>
#include <SDL_filesystem.h>
#include <QFile>
#include <QByteArray>
#include <QDebug>
extern const char module_rpg[];
static void mriBindingExecute();
@ -102,9 +101,9 @@ static void mriBindingInit()
}
static void
showMsg(const QByteArray &msg)
showMsg(const std::string &msg)
{
shState->eThread().showMessageBox(msg.constData());
shState->eThread().showMessageBox(msg.c_str());
}
RB_METHOD(mkxpPuts)
@ -114,7 +113,7 @@ RB_METHOD(mkxpPuts)
const char *str;
rb_get_args(argc, argv, "z", &str RB_ARG_END);
qDebug() << str;
Debug() << str;
return Qnil;
}
@ -174,49 +173,45 @@ RB_METHOD(mriDataDirectory)
static void runCustomScript(const char *filename)
{
QFile scriptFile(filename);
if (!scriptFile.open(QFile::ReadOnly))
std::string scriptData("#encoding:utf-8\n");
if (!readFile(filename, scriptData))
{
showMsg(QByteArray("Unable to open '") + filename + "'");
showMsg(std::string("Unable to open '") + filename + "'");
return;
}
QByteArray scriptData = scriptFile.readAll();
scriptFile.close();
scriptData.prepend("#encoding:utf-8\n");
rb_eval_string_protect(scriptData.constData(), 0);
rb_eval_string_protect(scriptData.c_str(), 0);
}
VALUE kernelLoadDataInt(const char *filename);
struct Script
{
QByteArray name;
QByteArray encData;
std::string name;
std::string encData;
uint32_t unknown;
QByteArray decData;
std::string decData;
};
static void runRMXPScripts()
{
const QByteArray &scriptPack = shState->rtData().config.game.scripts;
const std::string &scriptPack = shState->rtData().config.game.scripts;
if (scriptPack.isEmpty())
if (scriptPack.empty())
{
showMsg("No game scripts specified (missing Game.ini?)");
return;
}
if (!shState->fileSystem().exists(scriptPack.constData()))
if (!shState->fileSystem().exists(scriptPack.c_str()))
{
showMsg("Unable to open '" + scriptPack + "'");
return;
}
VALUE scriptArray = kernelLoadDataInt(scriptPack.constData());
VALUE scriptArray = kernelLoadDataInt(scriptPack.c_str());
if (rb_type(scriptArray) != RUBY_T_ARRAY)
{
@ -226,7 +221,7 @@ static void runRMXPScripts()
size_t scriptCount = RARRAY_LEN(scriptArray);
QByteArray decodeBuffer;
std::string decodeBuffer;
decodeBuffer.resize(0x1000);
std::vector<Script> encScripts(scriptCount);
@ -246,7 +241,7 @@ static void runRMXPScripts()
Script &sc = encScripts[i];
sc.name = RSTRING_PTR(scriptName);
sc.encData = QByteArray(RSTRING_PTR(scriptString), RSTRING_LEN(scriptString));
sc.encData = std::string(RSTRING_PTR(scriptString), RSTRING_LEN(scriptString));
sc.unknown = FIX2UINT(scriptUnknown);
}
@ -260,9 +255,9 @@ static void runRMXPScripts()
while (true)
{
unsigned char *bufferPtr =
reinterpret_cast<unsigned char*>(const_cast<char*>(decodeBuffer.constData()));
reinterpret_cast<unsigned char*>(const_cast<char*>(decodeBuffer.c_str()));
const unsigned char *sourcePtr =
reinterpret_cast<const unsigned char*>(sc.encData.constData());
reinterpret_cast<const unsigned char*>(sc.encData.c_str());
bufferLen = decodeBuffer.length();
@ -282,21 +277,25 @@ static void runRMXPScripts()
static char buffer[256];
/* FIXME: '%zu' apparently gcc only? */
snprintf(buffer, sizeof(buffer), "Error decoding script %zu: '%s'",
i, sc.name.constData());
i, sc.name.c_str());
showMsg(buffer);
break;
}
sc.decData = QByteArray(decodeBuffer.constData(), bufferLen);
sc.decData.prepend("#encoding:utf-8\n");
/* Store encoding header + the decoded script
* in 'sc.decData' */
sc.decData = "#encoding:utf-8\n";
size_t hdSize = sc.decData.size();
sc.decData.resize(hdSize + bufferLen);
memcpy(&sc.decData[hdSize], decodeBuffer.c_str(), bufferLen);
ruby_script(sc.name.constData());
ruby_script(sc.name.c_str());
rb_gc_start();
/* Execute code */
rb_eval_string_protect(sc.decData.constData(), 0);
rb_eval_string_protect(sc.decData.c_str(), 0);
VALUE exc = rb_gv_get("$!");
if (rb_type(exc) != RUBY_T_NIL)
@ -314,23 +313,23 @@ static void mriBindingExecute()
mriBindingInit();
QByteArray &customScript = shState->rtData().config.customScript;
if (!customScript.isEmpty())
runCustomScript(customScript.constData());
std::string &customScript = shState->rtData().config.customScript;
if (!customScript.empty())
runCustomScript(customScript.c_str());
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));
Debug() << "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));
Debug() << (RSTRING_PTR(msg));
}
ruby_cleanup(0);

View File

@ -26,8 +26,7 @@
#include "util.h"
#include <stdarg.h>
#include <QDebug>
#include <assert.h>
void initType(rb_data_type_struct &type,
const char *name,
@ -244,7 +243,7 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...)
}
}
#ifndef QT_NO_DEBUG
#ifndef NDEBUG
/* Pop remaining arg pointers off
* the stack to check for RB_ARG_END */
@ -288,8 +287,8 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...)
/* Verify correct termination */
void *argEnd = va_arg(ap, void*);
Q_UNUSED(argEnd);
Q_ASSERT(argEnd == RB_ARG_END_VAL);
(void) argEnd;
assert(argEnd == RB_ARG_END_VAL);
#endif

View File

@ -22,7 +22,7 @@
#ifndef BINDING_UTIL_H
#define BINDING_UTIL_H
#include "./ruby/ruby.h"
#include <ruby.h>
enum RbException
{
@ -134,7 +134,7 @@ int
rb_get_args(int argc, VALUE *argv, const char *format, ...);
/* Always terminate 'rb_get_args' with this */
#ifndef QT_NO_DEBUG
#ifndef NDEBUG
# define RB_ARG_END_VAL ((void*) -1)
# define RB_ARG_END ,RB_ARG_END_VAL
#else

View File

@ -26,8 +26,6 @@
#include "binding-util.h"
#include "binding-types.h"
#include <QDebug>
#define DISP_CLASS_NAME "bitmap"
DEF_TYPE(Bitmap);

View File

@ -23,8 +23,6 @@
#include "binding-util.h"
#include "serializable-binding.h"
#include <QDebug>
DEF_TYPE(Color);
DEF_TYPE(Tone);
DEF_TYPE(Rect);

View File

@ -27,8 +27,6 @@
#include "ruby/encoding.h"
#include "ruby/intern.h"
#include <QDebug>
DEF_TYPE(FileInt);
static VALUE

View File

@ -30,11 +30,11 @@
#include <mruby/proc.h>
#include <mruby/dump.h>
#include "binding-util.h"
#include <stdio.h>
#include <zlib.h>
#include <string>
#include <SDL_messagebox.h>
#include <SDL_rwops.h>
#include <SDL_timer.h>
@ -45,6 +45,7 @@
#include "filesystem.h"
#include "exception.h"
#include "binding-util.h"
#include "binding-types.h"
#include "mrb-ext/marshal.h"
@ -180,9 +181,9 @@ checkException(mrb_state *mrb)
static void
showError(const QByteArray &msg)
showError(const std::string &msg)
{
shState->eThread().showMessageBox(msg.constData());
shState->eThread().showMessageBox(msg.c_str());
}
static void
@ -245,17 +246,17 @@ runMrbFile(mrb_state *mrb, const char *filename)
static void
runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
{
const QByteArray &scriptPack = shState->rtData().config.game.scripts;
const std::string &scriptPack = shState->rtData().config.game.scripts;
if (scriptPack.isEmpty())
if (scriptPack.empty())
{
showError("No game scripts specified (missing Game.ini?)");
return;
}
if (!shState->fileSystem().exists(scriptPack.constData()))
if (!shState->fileSystem().exists(scriptPack.c_str()))
{
showError("Unable to open '" + scriptPack + "'");
showError(std::string("Unable to open '") + scriptPack + "'");
return;
}
@ -263,10 +264,10 @@ runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
mrb_state *scriptMrb = mrb_open();
SDL_RWops ops;
shState->fileSystem().openRead(ops, scriptPack.constData());
shState->fileSystem().openRead(ops, scriptPack.c_str());
mrb_value scriptArray = mrb_nil_value();
QByteArray readError;
std::string readError;
try
{
@ -275,22 +276,22 @@ runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
catch (const Exception &e)
{
char buffer[512];
snprintf(buffer, sizeof(buffer), e.fmt.constData(), e.arg1.constData(), e.arg2.constData());
readError = QByteArray(": ") + QByteArray(buffer);
snprintf(buffer, sizeof(buffer), e.fmt.c_str(), e.arg1.c_str(), e.arg2.c_str());
readError = std::string(": ") + std::string(buffer);
}
SDL_RWclose(&ops);
if (!mrb_array_p(scriptArray))
{
showError(QByteArray("Failed to read script data") + readError);
showError(std::string("Failed to read script data") + readError);
mrb_close(scriptMrb);
return;
}
int scriptCount = mrb_ary_len(scriptMrb, scriptArray);
QByteArray decodeBuffer;
std::string decodeBuffer;
decodeBuffer.resize(0x1000);
for (int i = 0; i < scriptCount; ++i)
@ -309,7 +310,7 @@ runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
while (true)
{
unsigned char *bufferPtr =
reinterpret_cast<unsigned char*>(const_cast<char*>(decodeBuffer.constData()));
reinterpret_cast<unsigned char*>(const_cast<char*>(decodeBuffer.c_str()));
unsigned char *sourcePtr =
reinterpret_cast<unsigned char*>(RSTRING_PTR(scriptString));
@ -342,7 +343,7 @@ runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
int ai = mrb_gc_arena_save(mrb);
/* Execute code */
mrb_load_nstring_cxt(mrb, decodeBuffer.constData(), bufferLen, ctx);
mrb_load_nstring_cxt(mrb, decodeBuffer.c_str(), bufferLen, ctx);
mrb_gc_arena_restore(mrb, ai);
@ -370,14 +371,15 @@ static void mrbBindingExecute()
mrbc_context *ctx = mrbc_context_new(mrb);
ctx->capture_errors = 1;
Config &conf = shState->rtData().config;
QByteArray &customScript = conf.customScript;
QByteArray mrbFile = conf.bindingConf.value("mrbFile").toByteArray();
const Config &conf = shState->rtData().config;
const std::string &customScript = conf.customScript;
// const std::string &mrbFile = conf.mrbFile;
(void) runMrbFile; // FIXME mrbFile support on ice for now
if (!customScript.isEmpty())
runCustomScript(mrb, ctx, customScript.constData());
else if (!mrbFile.isEmpty())
runMrbFile(mrb, mrbFile.constData());
if (!customScript.empty())
runCustomScript(mrb, ctx, customScript.c_str());
// else if (!mrbFile.empty())
// runMrbFile(mrb, mrbFile.c_str());
else
runRMXPScripts(mrb, ctx);

View File

@ -26,8 +26,6 @@
#include "binding-util.h"
#include "binding-types.h"
#include <QDebug>
#define DISP_CLASS_NAME "bitmap"
DEF_TYPE(Bitmap);

View File

@ -25,6 +25,8 @@
#include "disposable.h"
#include "binding-util.h"
#include <string.h>
template<class C>
MRB_METHOD(disposableDispose)
{

View File

@ -24,8 +24,6 @@
#include "binding-types.h"
#include "serializable-binding.h"
#include <QDebug>
#define ATTR_RW(Type, attr, arg_type, mrb_val, arg_t_s) \
MRB_METHOD(Type##Get_##attr) \
{ \

View File

@ -21,6 +21,8 @@
#include "file.h"
#include "debugwriter.h"
#include "../binding-util.h"
#include <mruby/string.h>
#include <mruby/array.h>
@ -36,8 +38,6 @@
#include <vector>
#include <QDebug>
extern mrb_value timeFromSecondsInt(mrb_state *mrb, time_t seconds);
static void
@ -87,7 +87,7 @@ getOpenMode(const char *mode)
STR_CASE("a", Write)
STR_CASE("a+", ReadWrite)
qDebug() << "getOpenMode failed for:" << mode;
Debug() << "getOpenMode failed for:" << mode;
return 0;
}
@ -225,7 +225,7 @@ MRB_FUNCTION(fileExpandPath)
// FIXME No idea how to integrate 'default_dir' right now
if (defDir)
qDebug() << "FIXME: File.expand_path: default_dir not implemented";
Debug() << "FIXME: File.expand_path: default_dir not implemented";
char buffer[512];
char *unused = realpath(path, buffer);
@ -457,7 +457,7 @@ MRB_METHOD(fileReadLines)
const char *rs = "\n"; (void) rs;
if (mrb->c->ci->argc > 0)
{
qDebug() << "FIXME: File.readlines: rs not implemented";
Debug() << "FIXME: File.readlines: rs not implemented";
if (mrb_string_p(arg))
rs = RSTRING_PTR(arg);

View File

@ -36,8 +36,6 @@
#include "filesystem.h"
#include "binding.h"
#include <QDebug>
void mrbBindingTerminate();
MRB_FUNCTION(kernelEval)

View File

@ -38,14 +38,11 @@
#include "file.h"
#include "rwmem.h"
#include "exception.h"
#include "boost-hash.h"
#include "debugwriter.h"
#include <SDL_timer.h>
#include <QHash>
#include <QByteArray>
#include <QDebug>
#define MARSHAL_MAJOR 4
#define MARSHAL_MINOR 8
@ -76,9 +73,9 @@
// FIXME make these dynamically allocatd, per MarshalContext
static char gpbuffer[512];
inline uint qHash(mrb_value key)
inline size_t hash_value(mrb_value key)
{
return qHash(key.value.p);
return boost::hash_value(key.value.p);
}
inline bool operator==(mrb_value v1, mrb_value v2)
@ -109,7 +106,7 @@ template<typename T>
struct LinkBuffer
{
/* Key: val, Value: idx in vec */
QHash<T, int> hash;
BoostHash<T, int> hash;
std::vector<T> vec;
bool contains(T value)
@ -551,9 +548,8 @@ read_value(MarshalContext *ctx)
break;
default :
qDebug() << "Value type" << (char)type << "not supported!";
throw Exception(Exception::MKXPError, "Marshal.load: unsupported value type '%s'",
QByteArray(1, (char)type));
std::string(1, (char)type));
}
mrb_gc_arena_restore(mrb, arena);
@ -973,7 +969,7 @@ MRB_FUNCTION(marshalLoad)
}
else
{
qDebug() << "FIXME: Marshal.load: generic IO port not implemented";
Debug() << "FIXME: Marshal.load: generic IO port not implemented";
return mrb_nil_value();
}

View File

@ -24,8 +24,6 @@
#include "binding-types.h"
#include "serializable-binding.h"
#include <QDebug>
DEF_TYPE(Table);
MRB_METHOD(tableInitialize)

View File

@ -22,12 +22,11 @@
#include "binding.h"
#include "sharedstate.h"
#include "eventthread.h"
#include <QDebug>
#include "debugwriter.h"
static void nullBindingExecute()
{
qDebug() << "The null binding doesn't do anything, so we're done!";
Debug() << "The null binding doesn't do anything, so we're done!";
shState->rtData().rqTermAck = true;
}

View File

@ -5,7 +5,29 @@ QT =
TARGET = mkxp
DEPENDPATH += src shader assets
INCLUDEPATH += . src
QMAKE_LFLAGS += '-Wl,-rpath,\'\$$ORIGIN/lib\''
LIBS += -lGL
# Deal with boost paths...
isEmpty(BOOST_I) {
BOOST_I = $$(BOOST_I)
}
isEmpty(BOOST_I) {}
else {
INCLUDEPATH += $$BOOST_I
}
isEmpty(BOOST_L) {
BOOST_L = $$(BOOST_L)
}
isEmpty(BOOST_L) {}
else {
LIBS += -L$$BOOST_L
}
LIBS += -lboost_program_options
CONFIG(release, debug|release): DEFINES += NDEBUG
isEmpty(BINDING) {
BINDING = BINDING_MRI
@ -19,18 +41,14 @@ RGSS2 {
unix {
CONFIG += link_pkgconfig
PKGCONFIG += QtCore sigc++-2.0 glew pixman-1 zlib \
physfs sdl2 SDL2_image SDL2_ttf SDL_sound \
openal
PKGCONFIG += sigc++-2.0 glew pixman-1 zlib physfs \
sdl2 SDL2_image SDL2_ttf SDL_sound openal
RGSS2 {
PKGCONFIG += vorbisfile
}
}
# 'slots' keyword fucks with libsigc++
DEFINES += QT_NO_KEYWORDS
# Input
HEADERS += \
src/quadarray.h \
@ -72,7 +90,9 @@ HEADERS += \
src/tileatlas.h \
src/perftimer.h \
src/sharedstate.h \
src/al-util.h
src/al-util.h \
src/boost-hash.h \
src/debugwriter.h
SOURCES += \
src/main.cpp \
@ -185,9 +205,7 @@ BINDING_MRUBY {
}
BINDING_MRI {
LIBS += ./ruby/libruby.so
INCLUDEPATH += ruby/include ruby/.ext/include/x86_64-linux # need i386 paths here too!
DEPENDPATH += ruby/include ruby/.ext/include/x86_64-linux
PKGCONFIG += ruby-2.1
DEFINES += BINDING_MRI
# EMBED2 = binding-mri/module_rpg.rb

View File

@ -27,12 +27,12 @@
#include "filesystem.h"
#include "exception.h"
#include "al-util.h"
#include <QByteArray>
#include <QHash>
#include "boost-hash.h"
#include "debugwriter.h"
#include <vector>
#include <string>
#include <assert.h>
#include <SDL_audio.h>
#include <SDL_thread.h>
@ -47,8 +47,6 @@
#include <alc.h>
#include <QDebug>
#define AUDIO_SLEEP 10
#define SE_SOURCES 6
#define SE_CACHE_MEM (10*1024*1024) // 10 MB
@ -71,8 +69,8 @@ static uint8_t formatSampleSize(int sdlFormat)
return 4;
default:
qDebug() << "Unhandled sample format";
Q_ASSERT(0);
Debug() << "Unhandled sample format";
abort();
}
return 0;
@ -87,23 +85,23 @@ static ALenum chooseALFormat(int sampleSize, int channelCount)
{
case 1 : return AL_FORMAT_MONO8;
case 2 : return AL_FORMAT_STEREO8;
default: Q_ASSERT(0);
default: abort();
}
case 2 :
switch (channelCount)
{
case 1 : return AL_FORMAT_MONO16;
case 2 : return AL_FORMAT_STEREO16;
default : Q_ASSERT(0);
default : abort();
}
case 4 :
switch (channelCount)
{
case 1 : return AL_FORMAT_MONO_FLOAT32;
case 2 : return AL_FORMAT_STEREO_FLOAT32;
default : Q_ASSERT(0);
default : abort();
}
default : Q_ASSERT(0);
default : abort();
}
return 0;
@ -114,7 +112,7 @@ static const int streamBufSize = 32768;
struct SoundBuffer
{
/* Uniquely identifies this or equal buffer */
QByteArray key;
std::string key;
AL::Buffer::ID alBuffer;
@ -135,11 +133,6 @@ struct SoundBuffer
alBuffer = AL::Buffer::gen();
}
~SoundBuffer()
{
AL::Buffer::del(alBuffer);
}
static SoundBuffer *ref(SoundBuffer *buffer)
{
++buffer->refCount;
@ -152,12 +145,20 @@ struct SoundBuffer
if (--buffer->refCount == 0)
delete buffer;
}
private:
~SoundBuffer()
{
AL::Buffer::del(alBuffer);
}
};
struct SoundEmitter
{
typedef BoostHash<std::string, SoundBuffer*> BufferHash;
IntruList<SoundBuffer> buffers;
QHash<QByteArray, SoundBuffer*> bufferHash;
BufferHash bufferHash;
/* Byte count sum of all cached / playing buffers */
uint32_t bufferBytes;
@ -189,12 +190,12 @@ struct SoundEmitter
SoundBuffer::deref(atchBufs[i]);
}
QHash<QByteArray, SoundBuffer*>::iterator iter;
for (iter = bufferHash.begin(); iter != bufferHash.end(); ++iter)
delete iter.value();
BufferHash::const_iterator iter;
for (iter = bufferHash.cbegin(); iter != bufferHash.cend(); ++iter)
SoundBuffer::deref(iter->second);
}
void play(const QByteArray &filename,
void play(const std::string &filename,
int volume,
int pitch)
{
@ -233,7 +234,7 @@ struct SoundEmitter
}
private:
SoundBuffer *allocateBuffer(const QByteArray &filename)
SoundBuffer *allocateBuffer(const std::string &filename)
{
SoundBuffer *buffer = bufferHash.value(filename, 0);
@ -252,7 +253,7 @@ private:
SDL_RWops dataSource;
const char *extension;
shState->fileSystem().openRead(dataSource, filename.constData(),
shState->fileSystem().openRead(dataSource, filename.c_str(),
FileSystem::Audio, false, &extension);
Sound_Sample *sampleHandle = Sound_NewSample(&dataSource, extension, 0, streamBufSize);
@ -285,7 +286,7 @@ private:
while (wouldBeBytes > SE_CACHE_MEM && !buffers.isEmpty())
{
SoundBuffer *last = buffers.tail();
bufferHash.remove(last->key);
bufferHash.erase(last->key);
buffers.remove(last->link);
wouldBeBytes -= last->bytes;

View File

@ -28,9 +28,6 @@
#include <pixman.h>
#include <QString>
#include <QChar>
#include "gl-util.h"
#include "quad.h"
#include "quadarray.h"
@ -944,6 +941,50 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
modified();
}
/* http://www.lemoda.net/c/utf8-to-ucs2/index.html */
static uint16_t utf8_to_ucs2(const char *_input,
const char **end_ptr)
{
const unsigned char *input =
reinterpret_cast<const unsigned char*>(_input);
*end_ptr = _input;
if (input[0] == 0)
return -1;
if (input[0] < 0x80)
{
*end_ptr = _input + 1;
return input[0];
}
if ((input[0] & 0xE0) == 0xE0)
{
if (input[1] == 0 || input[2] == 0)
return -1;
*end_ptr = _input + 3;
return (input[0] & 0x0F)<<12 |
(input[1] & 0x3F)<<6 |
(input[2] & 0x3F);
}
if ((input[0] & 0xC0) == 0xC0)
{
if (input[1] == 0)
return -1;
*end_ptr = _input + 2;
return (input[0] & 0x1F)<<6 |
(input[1] & 0x3F);
}
return -1;
}
IntRect Bitmap::textSize(const char *str)
{
GUARD_DISPOSED;
@ -955,10 +996,14 @@ IntRect Bitmap::textSize(const char *str)
int w, h;
TTF_SizeUTF8(font, str, &w, &h);
QString qstr = QString::fromUtf8(str);
/* If str is one character long, *endPtr == 0 */
const char *endPtr;
uint16_t ucs2 = utf8_to_ucs2(str, &endPtr);
if (p->font->getItalic() && qstr.length() == 1)
TTF_GlyphMetrics(font, qstr.at(0).unicode(), 0, 0, 0, 0, &w);
/* For cursive characters, returning the advance
* as width yields better results */
if (p->font->getItalic() && *endPtr == '\0')
TTF_GlyphMetrics(font, ucs2, 0, 0, 0, 0, &w);
return IntRect(0, 0, w, h);
}

130
src/boost-hash.h Normal file
View File

@ -0,0 +1,130 @@
/*
** boost-hash.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 BOOSTHASH_H
#define BOOSTHASH_H
#include <boost/unordered/unordered_map.hpp>
#include <boost/unordered/unordered_set.hpp>
#include <utility>
/* Wrappers around the boost unordered template classes,
* exposing an interface similar to Qt's QHash/QSet */
template<typename K, typename V>
class BoostHash
{
private:
typedef boost::unordered_map<K, V> BoostType;
typedef std::pair<K, V> PairType;
BoostType p;
public:
typedef typename BoostType::const_iterator const_iterator;
inline bool contains(const K &key) const
{
const_iterator iter = p.find(key);
return (iter != p.cend());
}
inline void insert(const K &key, const V &value)
{
p.insert(PairType(key, value));
}
inline void erase(const K &key)
{
p.erase(key);
}
inline const V value(const K &key) const
{
const_iterator iter = p.find(key);
if (iter == p.cend())
return V();
return iter->second;
}
inline const V value(const K &key, const V &defaultValue) const
{
const_iterator iter = p.find(key);
if (iter == p.cend())
return defaultValue;
return iter->second;
}
inline V &operator[](const K &key)
{
return p[key];
}
inline const_iterator cbegin() const
{
return p.cbegin();
}
inline const_iterator cend() const
{
return p.cend();
}
};
template<typename K>
class BoostSet
{
private:
typedef boost::unordered_set<K> BoostType;
BoostType p;
public:
typedef typename BoostType::const_iterator const_iterator;
inline bool contains(const K &key)
{
const_iterator iter = p.find(key);
return (iter != p.cend());
}
inline void insert(const K &key)
{
p.insert(key);
}
inline const_iterator cbegin() const
{
return p.cbegin();
}
inline const_iterator cend() const
{
return p.cend();
}
};
#endif // BOOSTHASH_H

View File

@ -21,12 +21,16 @@
#include "config.h"
#include <QSettings>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QFile>
#include <QRegExp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <fstream>
#include "debugwriter.h"
#include "util.h"
typedef std::vector<std::string> StringVec;
namespace po = boost::program_options;
Config::Config()
: debugMode(false),
@ -46,68 +50,84 @@ Config::Config()
void Config::read()
{
QSettings confFile("mkxp.conf", QSettings::IniFormat);
#define PO_DESC_ALL \
PO_DESC(debugMode, bool) \
PO_DESC(winResizable, bool) \
PO_DESC(fullscreen, bool) \
PO_DESC(fixedAspectRatio, bool) \
PO_DESC(smoothScaling, bool) \
PO_DESC(vsync, bool) \
PO_DESC(defScreenW, int) \
PO_DESC(defScreenH, int) \
PO_DESC(fixedFramerate, int) \
PO_DESC(frameSkip, bool) \
PO_DESC(solidFonts, bool) \
PO_DESC(gameFolder, std::string) \
PO_DESC(allowSymlinks, bool) \
PO_DESC(customScript, std::string)
#define READ_VAL(key, Type) key = confFile.value(#key, key).to##Type()
#define PO_DESC(key, type) (#key, po::value< type >()->default_value(key))
READ_VAL(debugMode, Bool);
READ_VAL(winResizable, Bool);
READ_VAL(fullscreen, Bool);
READ_VAL(fixedAspectRatio, Bool);
READ_VAL(smoothScaling, Bool);
READ_VAL(vsync, Bool);
READ_VAL(defScreenW, Int);
READ_VAL(defScreenH, Int);
READ_VAL(fixedFramerate, Int);
READ_VAL(frameSkip, Bool);
READ_VAL(solidFonts, Bool);
READ_VAL(gameFolder, ByteArray);
READ_VAL(allowSymlinks, Bool);
READ_VAL(customScript, ByteArray);
po::options_description podesc;
podesc.add_options()
PO_DESC_ALL
("RTP", po::value<StringVec>())
;
QStringList _rtps = confFile.value("RTPs").toStringList();
Q_FOREACH(const QString &s, _rtps)
rtps.push_back(s.toUtf8());
std::ifstream confFile;
confFile.open("mkxp.conf");
confFile.beginGroup("Binding");
po::variables_map vm;
po::store(po::parse_config_file(confFile, podesc, true), vm);
po::notify(vm);
QStringList bindingKeys = confFile.childKeys();
Q_FOREACH (const QString &key, bindingKeys)
{
QVariant value = confFile.value(key);
confFile.close();
/* Convert QString to QByteArray */
if (value.type() == QVariant::String)
value = value.toString().toUtf8();
// Not gonna take your shit boost
#define GUARD_ALL( exp ) try { exp } catch(...) {}
bindingConf.insert(key.toLatin1(), value);
}
#undef PO_DESC
#define PO_DESC(key, type) GUARD_ALL( key = vm[#key].as< type >(); )
confFile.endGroup();
PO_DESC_ALL;
GUARD_ALL( rtps = vm["RTP"].as<StringVec>(); );
#undef PO_DESC
#undef PO_DESC_ALL
}
void Config::readGameINI()
{
if (!customScript.isEmpty())
if (!customScript.empty())
{
game.title = basename(customScript.constData());
game.title = basename(customScript.c_str());
return;
}
QSettings gameINI(gameFolder + "/Game.ini", QSettings::IniFormat);
QFileInfo finfo(gameFolder.constData());
game.title = gameINI.value("Game/Title", finfo.baseName()).toByteArray();
po::options_description podesc;
podesc.add_options()
("Game.Title", po::value<std::string>())
("Game.Scripts", po::value<std::string>())
;
/* Gotta read the "Scripts" entry manually because Qt can't handle '\' */
QFile gameINIFile(gameFolder + "/Game.ini");
if (gameINIFile.open(QFile::ReadOnly))
{
QString gameINIContents = gameINIFile.readAll();
QRegExp scriptsRE(".*Scripts=(.*)\r\nTitle=.*");
if (scriptsRE.exactMatch(gameINIContents))
{
game.scripts = scriptsRE.cap(1).toUtf8();
game.scripts.replace('\\', '/');
}
}
std::string iniPath = gameFolder + "/Game.ini";
std::ifstream iniFile;
iniFile.open((iniPath).c_str());
po::variables_map vm;
po::store(po::parse_config_file(iniFile, podesc, true), vm);
po::notify(vm);
iniFile.close();
GUARD_ALL( game.title = vm["Game.Title"].as<std::string>(); );
GUARD_ALL( game.scripts = vm["Game.Scripts"].as<std::string>(); );
strReplace(game.scripts, '\\', '/');
if (game.title.empty())
game.title = basename(gameFolder.c_str());
}

View File

@ -22,10 +22,7 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <QByteArray>
#include <QHash>
#include <QVariant>
#include <string>
#include <vector>
struct Config
@ -46,20 +43,16 @@ struct Config
bool solidFonts;
QByteArray gameFolder;
std::string gameFolder;
bool allowSymlinks;
QByteArray customScript;
std::vector<QByteArray> rtps;
/* Any values in the [Binding]
* group are collected here */
QHash<QByteArray, QVariant> bindingConf;
std::string customScript;
std::vector<std::string> rtps;
/* Game INI contents */
struct {
QByteArray scripts;
QByteArray title;
std::string scripts;
std::string title;
} game;
Config();

View File

@ -20,42 +20,30 @@
*/
#include "debuglogger.h"
#include "debugwriter.h"
#include <glew.h>
#include <QFile>
#include <QTime>
#include <QTextStream>
#include <QDebug>
#include <iostream>
struct DebugLoggerPrivate
{
QFile logFile;
QTextStream *stream;
std::ostream *stream;
DebugLoggerPrivate(const char *logFilename)
{
if (logFilename)
{
logFile.setFileName(logFilename);
logFile.open(QFile::WriteOnly);
stream = new QTextStream(&logFile);
}
else
{
stream = new QTextStream(stderr, QIODevice::WriteOnly);
}
(void) logFilename;
stream = &std::clog;
}
~DebugLoggerPrivate()
{
delete stream;
}
void writeTimestamp()
{
QTime time = QTime::currentTime();
*stream << "[" << time.toString().toLatin1() << "] ";
// FIXME reintroduce proper time stamps (is this even necessary??)
*stream << "[GLDEBUG] ";
}
void writeLine(const char *line)
@ -114,7 +102,7 @@ DebugLogger::DebugLogger(const char *filename)
else if (GLEW_AMD_debug_output)
glDebugMessageCallbackAMD(amdDebugFunc, p);
else
qDebug() << "DebugLogger: no debug extensions found";
Debug() << "DebugLogger: no debug extensions found";
}
DebugLogger::~DebugLogger()

56
src/debugwriter.h Normal file
View File

@ -0,0 +1,56 @@
/*
** debugwriter.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 DEBUGWRITER_H
#define DEBUGWRITER_H
#include <iostream>
#include <sstream>
/* A cheap replacement for Debug() */
class Debug
{
public:
Debug()
{
buf << std::boolalpha;
}
template<typename T>
Debug &operator<<(const T &t)
{
buf << t;
buf << " ";
return *this;
}
~Debug()
{
std::clog << buf.str() << "\n";
}
private:
std::stringstream buf;
};
#endif // DEBUGWRITER_H

View File

@ -29,11 +29,10 @@
#include "sharedstate.h"
#include "graphics.h"
#include "debugwriter.h"
#include <string.h>
#include <QDebug>
bool EventThread::keyStates[] = { false };
EventThread::JoyState EventThread::joyState =
@ -97,7 +96,7 @@ void EventThread::process(RGSSThreadData &rtData)
{
if (!SDL_WaitEvent(&event))
{
qDebug() << "EventThread: Event error";
Debug() << "EventThread: Event error";
break;
}
@ -141,7 +140,7 @@ void EventThread::process(RGSSThreadData &rtData)
case SDL_QUIT :
case REQUEST_TERMINATION :
terminate = true;
qDebug() << "EventThread termination requested";
Debug() << "EventThread termination requested";
break;
case SDL_KEYDOWN :
@ -173,13 +172,13 @@ void EventThread::process(RGSSThreadData &rtData)
if (fullscreen)
{
/* Prevent fullscreen flicker */
strncpy(pendingTitle, rtData.config.game.title.constData(),
strncpy(pendingTitle, rtData.config.game.title.c_str(),
sizeof(pendingTitle));
havePendingTitle = true;
break;
}
SDL_SetWindowTitle(win, rtData.config.game.title.constData());
SDL_SetWindowTitle(win, rtData.config.game.title.c_str());
}
break;
@ -201,7 +200,7 @@ void EventThread::process(RGSSThreadData &rtData)
case REQUEST_MESSAGEBOX :
SDL_ShowSimpleMessageBox(event.user.code,
rtData.config.game.title.constData(),
rtData.config.game.title.c_str(),
(const char*) event.user.data1, win);
free(event.user.data1);
msgBoxDone = true;
@ -217,7 +216,7 @@ void EventThread::process(RGSSThreadData &rtData)
break;
snprintf(buffer, sizeof(buffer), "%s - %d FPS",
rtData.config.game.title.constData(), event.user.code);
rtData.config.game.title.c_str(), event.user.code);
/* Updating the window title in fullscreen
* mode seems to cause flickering */

View File

@ -30,7 +30,7 @@
#include <SDL_mouse.h>
#include <SDL_mutex.h>
#include <QByteArray>
#include <string>
#include <stdint.h>
@ -173,7 +173,7 @@ struct RGSSThreadData
Config config;
QByteArray rgssErrorMsg;
std::string rgssErrorMsg;
volatile bool rqScreenshot;

View File

@ -22,7 +22,7 @@
#ifndef EXCEPTION_H
#define EXCEPTION_H
#include <QByteArray>
#include <string>
#include <stdio.h>
struct Exception
@ -44,19 +44,19 @@ struct Exception
};
Type type;
QByteArray fmt;
QByteArray arg1;
QByteArray arg2;
std::string fmt;
std::string arg1;
std::string arg2;
Exception(Type type, QByteArray fmt,
QByteArray arg1 = QByteArray(),
QByteArray arg2 = QByteArray())
Exception(Type type, std::string fmt,
std::string arg1 = std::string(),
std::string arg2 = std::string())
: type(type), fmt(fmt), arg1(arg1), arg2(arg2)
{}
void snprintf(char *buffer, size_t bufSize) const
{
::snprintf(buffer, bufSize, fmt.constData(), arg1.constData(), arg2.constData());
::snprintf(buffer, bufSize, fmt.c_str(), arg1.c_str(), arg2.c_str());
}
};

View File

@ -23,22 +23,18 @@
#include "util.h"
#include "exception.h"
#include "boost-hash.h"
#include "debugwriter.h"
#include <physfs.h>
#include <SDL_sound.h>
#include <QHash>
#include <QSet>
#include <QByteArray>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <QDebug>
struct RGSS_entryData
{
int64_t offset;
@ -73,11 +69,11 @@ struct RGSS_archiveData
/* Maps: file path
* to: entry data */
QHash<QByteArray, RGSS_entryData> entryHash;
BoostHash<std::string, RGSS_entryData> entryHash;
/* Maps: directory path,
* to: list of contained entries */
QHash<QByteArray, QSet<QByteArray> > dirHash;
BoostHash<std::string, BoostSet<std::string> > dirHash;
};
static bool
@ -313,7 +309,7 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
uint32_t magic = RGSS_MAGIC;
/* Top level entry list */
QSet<QByteArray> &topLevel = data->dirHash["."];
BoostSet<std::string> &topLevel = data->dirHash["."];
while (true)
{
@ -373,7 +369,7 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
const char *dir = nameBuf;
const char *entry = &nameBuf[i+1];
QSet<QByteArray> &entryList = data->dirHash[dir];
BoostSet<std::string> &entryList = data->dirHash[dir];
entryList.insert(entry);
}
@ -390,16 +386,16 @@ RGSS_enumerateFiles(void *opaque, const char *dirname,
{
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
QByteArray _dirname(dirname);
std::string _dirname(dirname);
if (!data->dirHash.contains(_dirname))
return;
const QSet<QByteArray> &entries = data->dirHash.value(_dirname);
const BoostSet<std::string> &entries = data->dirHash[_dirname];
QSet<QByteArray>::const_iterator iter;
for (iter = entries.begin(); iter != entries.end(); ++iter)
cb(callbackdata, origdir, iter->constData());
BoostSet<std::string>::const_iterator iter;
for (iter = entries.cbegin(); iter != entries.cend(); ++iter)
cb(callbackdata, origdir, iter->c_str());
}
static PHYSFS_Io*
@ -500,9 +496,9 @@ static const PHYSFS_Archiver RGSS_Archiver =
struct FileSystemPrivate
{
/* All keys are lower case */
QHash<QByteArray, QByteArray> pathCache;
BoostHash<std::string, std::string> pathCache;
std::vector<QByteArray> extensions[FileSystem::Undefined];
std::vector<std::string> extensions[FileSystem::Undefined];
/* Attempt to locate an extension string in a filename.
* Either a pointer into the input string pointing at the
@ -535,7 +531,7 @@ struct FileSystemPrivate
buff[i] = '\0';
QByteArray key(buff);
std::string key(buff);
if (pathCache.contains(key))
{
@ -544,17 +540,17 @@ struct FileSystemPrivate
if (foundExt)
*foundExt = findExt(filename);
return pathCache[key].constData();
return pathCache[key].c_str();
}
char buff2[512];
if (type != FileSystem::Undefined)
{
std::vector<QByteArray> &extList = extensions[type];
std::vector<std::string> &extList = extensions[type];
for (size_t i = 0; i < extList.size(); ++i)
{
const char *ext = extList[i].constData();
const char *ext = extList[i].c_str();
snprintf(buff2, sizeof(buff2), "%s.%s", buff, ext);
key = buff2;
@ -564,7 +560,7 @@ struct FileSystemPrivate
if (foundExt)
*foundExt = ext;
return pathCache[key].constData();
return pathCache[key].c_str();
}
}
}
@ -640,7 +636,7 @@ FileSystem::~FileSystem()
delete p;
if (PHYSFS_deinit() == 0)
qDebug() << "PhyFS failed to deinit.";
Debug() << "PhyFS failed to deinit.";
}
void FileSystem::addPath(const char *path)
@ -660,16 +656,16 @@ static void cacheEnumCB(void *d, const char *origdir,
else
strncpy(buf, fname, sizeof(buf));
QByteArray mixedCase(buf);
std::string mixedCase(buf);
for (char *p = buf; *p; ++p)
*p = tolower(*p);
QByteArray lowerCase(buf);
std::string lowerCase(buf);
p->pathCache.insert(lowerCase, mixedCase);
PHYSFS_enumerateFilesCallback(mixedCase.constData(), cacheEnumCB, p);
PHYSFS_enumerateFilesCallback(mixedCase.c_str(), cacheEnumCB, p);
}
void FileSystem::createPathCache()

View File

@ -25,8 +25,6 @@
#include "etc.h"
#include "etc-internal.h"
#include <QDebug>
class Flashable
{
public:

View File

@ -24,17 +24,16 @@
#include "sharedstate.h"
#include "filesystem.h"
#include "exception.h"
#include "boost-hash.h"
#include "util.h"
#include <string>
#include <utility>
#include "../liberation.ttf.xxd"
#include <SDL_ttf.h>
#include <QHash>
#include <QByteArray>
#include <QPair>
#include <QDebug>
#define BUNDLED_FONT liberation
#define BUNDLED_FONT_D(f) assets_## f ##_ttf
@ -44,11 +43,17 @@
#define BNDL_F_D(f) BUNDLED_FONT_D(f)
#define BNDL_F_L(f) BUNDLED_FONT_L(f)
typedef QPair<QByteArray, int> FontKey;
typedef std::pair<std::string, int> FontKey;
static void strToLower(std::string &str)
{
for (size_t i = 0; i < str.size(); ++i)
str[i] = tolower(str[i]);
}
struct FontPoolPrivate
{
QHash<FontKey, TTF_Font*> hash;
BoostHash<FontKey, TTF_Font*> hash;
};
FontPool::FontPool()
@ -58,9 +63,9 @@ FontPool::FontPool()
FontPool::~FontPool()
{
QHash<FontKey, TTF_Font*>::const_iterator iter;
for (iter = p->hash.begin(); iter != p->hash.end(); ++iter)
TTF_CloseFont(iter.value());
BoostHash<FontKey, TTF_Font*>::const_iterator iter;
for (iter = p->hash.cbegin(); iter != p->hash.cend(); ++iter)
TTF_CloseFont(iter->second);
delete p;
}
@ -71,16 +76,22 @@ static SDL_RWops *openBundledFont()
}
_TTF_Font *FontPool::request(const char *filename,
int size)
int size)
{
// FIXME Find out how font path resolution is done in VX/Ace
QByteArray nameKey = QByteArray(filename).toLower();
nameKey.replace(' ', '_');
std::string nameKey(filename);
strToLower(nameKey);
strReplace(nameKey, ' ', '_');
bool useBundled = false;
QByteArray path = QByteArray("Fonts/") + nameKey;
if (!shState->fileSystem().exists(path.constData(), FileSystem::Font))
std::string path = std::string("Fonts/") + nameKey;
if (!shState->fileSystem().exists(path.c_str(), FileSystem::Font))
{
/* Use the same name key for the bundled font
* even when it resulted from multiple different
* font name requests. The space at the front is
* to prevent collisions (spaces are normally
* replaced with '_' */
useBundled = true;
nameKey = " bundled";
}
@ -90,14 +101,9 @@ _TTF_Font *FontPool::request(const char *filename,
TTF_Font *font = p->hash.value(key, 0);
if (font)
{
// static int i=0;qDebug() << "FontPool: <?+>" << i++;
return font;
}
// qDebug() << "FontPool: <?->";
/* Not in hash, create */
/* Not in hash, open */
SDL_RWops *ops;
if (useBundled)
@ -107,7 +113,7 @@ _TTF_Font *FontPool::request(const char *filename,
else
{
ops = SDL_AllocRW();
shState->fileSystem().openRead(*ops, path.constData(), FileSystem::Font, true);
shState->fileSystem().openRead(*ops, path.c_str(), FileSystem::Font, true);
}
// FIXME 0.9 is guesswork at this point
@ -124,7 +130,7 @@ _TTF_Font *FontPool::request(const char *filename,
struct FontPrivate
{
QByteArray name;
std::string name;
int size;
bool bold;
bool italic;
@ -132,7 +138,7 @@ struct FontPrivate
Color colorTmp;
static QByteArray defaultName;
static std::string defaultName;
static int defaultSize;
static bool defaultBold;
static bool defaultItalic;
@ -144,15 +150,15 @@ struct FontPrivate
FontPrivate(const char *name = 0,
int size = 0)
: name(name ? QByteArray(name) : defaultName),
: name(name ? std::string(name) : defaultName),
size(size ? size : defaultSize),
bold(defaultBold),
italic(defaultItalic),
color(&colorTmp),
colorTmp(*defaultColor)
{
sdlFont = shState->fontPool().request(this->name.constData(),
this->size);
sdlFont = shState->fontPool().request(this->name.c_str(),
this->size);
}
FontPrivate(const FontPrivate &other)
@ -166,19 +172,19 @@ struct FontPrivate
{}
};
QByteArray FontPrivate::defaultName = "Arial";
int FontPrivate::defaultSize = 22;
bool FontPrivate::defaultBold = false;
bool FontPrivate::defaultItalic = false;
Color *FontPrivate::defaultColor = &FontPrivate::defaultColorTmp;
std::string FontPrivate::defaultName = "Arial";
int FontPrivate::defaultSize = 22;
bool FontPrivate::defaultBold = false;
bool FontPrivate::defaultItalic = false;
Color *FontPrivate::defaultColor = &FontPrivate::defaultColorTmp;
Color FontPrivate::defaultColorTmp(255, 255, 255, 255);
bool Font::doesExist(const char *name)
{
QByteArray path = QByteArray("fonts/") + QByteArray(name);
std::string path = std::string("fonts/") + std::string(name);
return shState->fileSystem().exists(path.constData(), FileSystem::Font);
return shState->fileSystem().exists(path.c_str(), FileSystem::Font);
}
Font::Font(const char *name,
@ -199,7 +205,7 @@ Font::~Font()
const char *Font::getName() const
{
return p->name.constData();
return p->name.c_str();
}
void Font::setName(const char *value)
@ -213,7 +219,7 @@ void Font::setSize(int value)
return;
p->size = value;
p->sdlFont = shState->fontPool().request(p->name.constData(), value);
p->sdlFont = shState->fontPool().request(p->name.c_str(), value);
}
#undef CHK_DISP
@ -231,7 +237,7 @@ DEF_ATTR_SIMPLE_STATIC(Font, DefaultColor, Color*, FontPrivate::defaultColor)
const char *Font::getDefaultName()
{
return FontPrivate::defaultName.constData();
return FontPrivate::defaultName.c_str();
}
void Font::setDefaultName(const char *value)

View File

@ -27,14 +27,15 @@
#include <SDL_ttf.h>
#include <SDL_sound.h>
#include <string>
#include "sharedstate.h"
#include "eventthread.h"
#include "debuglogger.h"
#include "debugwriter.h"
#include "binding.h"
#include <QDebug>
static const char *reqExt[] =
{
"GL_ARB_fragment_shader",
@ -53,7 +54,7 @@ static const char *reqExt[] =
};
static void
rgssThreadError(RGSSThreadData *rtData, const QByteArray &msg)
rgssThreadError(RGSSThreadData *rtData, const std::string &msg)
{
rtData->rgssErrorMsg = msg;
rtData->ethread->requestTerminate();
@ -69,10 +70,10 @@ glGetStringInt(GLenum name)
static void
printGLInfo()
{
qDebug() << "GL Vendor :" << glGetStringInt(GL_VENDOR);
qDebug() << "GL Renderer :" << glGetStringInt(GL_RENDERER);
qDebug() << "GL Version :" << glGetStringInt(GL_VERSION);
qDebug() << "GLSL Version :" << glGetStringInt(GL_SHADING_LANGUAGE_VERSION);
Debug() << "GL Vendor :" << glGetStringInt(GL_VENDOR);
Debug() << "GL Renderer :" << glGetStringInt(GL_RENDERER);
Debug() << "GL Version :" << glGetStringInt(GL_VERSION);
Debug() << "GLSL Version :" << glGetStringInt(GL_SHADING_LANGUAGE_VERSION);
}
int rgssThreadFun(void *userdata)
@ -91,7 +92,7 @@ int rgssThreadFun(void *userdata)
if (!glCtx)
{
rgssThreadError(threadData, QByteArray("Error creating context: ") + SDL_GetError());
rgssThreadError(threadData, std::string("Error creating context: ") + SDL_GetError());
return 0;
}
@ -121,7 +122,7 @@ int rgssThreadFun(void *userdata)
{
if (!glewIsSupported(reqExt[i]))
{
rgssThreadError(threadData, QByteArray("Required GL extension \"")
rgssThreadError(threadData, std::string("Required GL extension \"")
+ reqExt[i] + "\" not present");
SDL_GL_DeleteContext(glCtx);
return 0;
@ -181,7 +182,7 @@ int main(int, char *argv[])
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
{
qDebug() << "Error initializing SDL:" << SDL_GetError();
Debug() << "Error initializing SDL:" << SDL_GetError();
return 0;
}
@ -189,7 +190,7 @@ int main(int, char *argv[])
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
if (IMG_Init(imgFlags) != imgFlags)
{
qDebug() << "Error initializing SDL_image:" << SDL_GetError();
Debug() << "Error initializing SDL_image:" << SDL_GetError();
SDL_Quit();
return 0;
@ -197,7 +198,7 @@ int main(int, char *argv[])
if (TTF_Init() < 0)
{
qDebug() << "Error initializing SDL_ttf:" << SDL_GetError();
Debug() << "Error initializing SDL_ttf:" << SDL_GetError();
IMG_Quit();
SDL_Quit();
@ -206,7 +207,7 @@ int main(int, char *argv[])
if (Sound_Init() == 0)
{
qDebug() << "Error initializing SDL_sound:" << Sound_GetError();
Debug() << "Error initializing SDL_sound:" << Sound_GetError();
TTF_Quit();
IMG_Quit();
SDL_Quit();
@ -224,13 +225,13 @@ int main(int, char *argv[])
if (conf.fullscreen)
winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
win = SDL_CreateWindow(conf.game.title.constData(),
win = SDL_CreateWindow(conf.game.title.c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
conf.defScreenW, conf.defScreenH, winFlags);
if (!win)
{
qDebug() << "Error creating window";
Debug() << "Error creating window";
return 0;
}
@ -254,7 +255,7 @@ int main(int, char *argv[])
/* We can stop waiting when the request was ack'd */
if (rtData.rqTermAck)
{
qDebug() << "RGSS thread ack'd request after" << i*10 << "ms";
Debug() << "RGSS thread ack'd request after" << i*10 << "ms";
break;
}
@ -267,17 +268,17 @@ int main(int, char *argv[])
if (rtData.rqTermAck)
SDL_WaitThread(rgssThread, 0);
else
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.constData(),
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
"The RGSS script seems to be stuck and mkxp will now force quit", win);
if (!rtData.rgssErrorMsg.isEmpty())
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.constData(),
rtData.rgssErrorMsg.constData(), win);
if (!rtData.rgssErrorMsg.empty())
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.c_str(),
rtData.rgssErrorMsg.c_str(), win);
/* Clean up any remainin events */
eventThread.cleanup();
qDebug() << "Shutting down.";
Debug() << "Shutting down.";
SDL_DestroyWindow(win);

View File

@ -3,7 +3,7 @@
#include <SDL_timer.h>
#include <glew.h>
#include <QDebug>
#include "debugwriter.h"
struct TimerQuery
{
@ -132,7 +132,7 @@ struct GPUTimerGLQuery : public PerfTimer
if (++counter < iter)
return;
qDebug() << " "
Debug() << " "
"Avg. GPU time:" << ((double) acc / (iter * 1000 * 1000)) << "ms";
acc = counter = 0;
@ -181,7 +181,7 @@ struct CPUTimer : public PerfTimer
if (++counter < iter)
return;
qDebug() << "Avg. CPU time:" << ((double) acc / (iter * (perfFreq / 1000))) << "ms";
Debug() << "Avg. CPU time:" << ((double) acc / (iter * (perfFreq / 1000))) << "ms";
acc = counter = 0;
}
};
@ -199,7 +199,7 @@ PerfTimer *createGPUTimer(int iter)
}
else
{
qDebug() << "GL_EXT_timer_query not present: cannot measure GPU performance";
Debug() << "GL_EXT_timer_query not present: cannot measure GPU performance";
return new GPUTimerDummy();
}
}

View File

@ -22,8 +22,6 @@
#include "scene.h"
#include "sharedstate.h"
#include <QDebug>
Scene::Scene()
{
geometry.xOrigin = geometry.yOrigin = 0;

View File

@ -22,10 +22,11 @@
#include "shader.h"
#include "sharedstate.h"
#include "glstate.h"
#include "debugwriter.h"
#include <glew.h>
#include <QFile>
#include <assert.h>
#include "../sprite.frag.xxd"
#include "../hue.frag.xxd"
@ -48,16 +49,14 @@
#include "../blurV.vert.xxd"
#endif
#include <QDebug>
#define INIT_SHADER(vert, frag) \
{ \
Shader::init(shader_##vert##_vert, shader_##vert##_vert_len, shader_##frag##_frag, shader_##frag##_frag_len); \
qDebug() << " From:" << #vert ".vert" << #frag ".frag"; \
Debug() << " From:" << #vert ".vert" << #frag ".frag"; \
}
#define COMP(shader) qDebug() << "--- Compiling " #shader
#define COMP(shader) Debug() << "--- Compiling " #shader
#define GET_U(name) u_##name = glGetUniformLocation(program, #name)
@ -98,14 +97,14 @@ void Shader::init(const unsigned char *vert, int vertSize,
glCompileShader(vertShader);
glGetObjectParameterivARB(vertShader, GL_COMPILE_STATUS, &success);
Q_ASSERT(success);
assert(success); // FIXME should really throw here instead
/* Compile fragment shader */
glShaderSource(fragShader, 1, (const GLchar**) &frag, (const GLint*) &fragSize);
glCompileShader(fragShader);
glGetObjectParameterivARB(fragShader, GL_COMPILE_STATUS, &success);
Q_ASSERT(success);
assert(success);
/* Link shader program */
glAttachShader(program, vertShader);
@ -118,23 +117,17 @@ void Shader::init(const unsigned char *vert, int vertSize,
glLinkProgram(program);
glGetObjectParameterivARB(program, GL_LINK_STATUS, &success);
Q_ASSERT(success);
assert(success);
}
void Shader::initFromFile(const char *_vertFile, const char *_fragFile)
{
QFile vertFile(_vertFile);
vertFile.open(QFile::ReadOnly);
QByteArray vertContents = vertFile.readAll();
vertFile.close();
std::string vertContents, fragContents;
readFile(_vertFile, vertContents);
readFile(_fragFile, fragContents);
QFile fragFile(_fragFile);
fragFile.open(QFile::ReadOnly);
QByteArray fragContents = fragFile.readAll();
fragFile.close();
init((const unsigned char*) vertContents.constData(), vertContents.size(),
(const unsigned char*) fragContents.constData(), fragContents.size());
init((const unsigned char*) vertContents.c_str(), vertContents.size(),
(const unsigned char*) fragContents.c_str(), fragContents.size());
}
void Shader::setVec4Uniform(GLint location, const Vec4 &vec)

View File

@ -36,11 +36,9 @@
#include "quad.h"
#include "binding.h"
#include <QFile>
#include <unistd.h>
#include <QDebug>
#include <stdio.h>
#include <string>
SharedState *SharedState::instance = 0;
static GlobalIBO *globalIBO = 0;
@ -93,21 +91,26 @@ struct SharedStatePrivate
graphics(threadData),
stampCounter(0)
{
if (!config.gameFolder.isEmpty())
if (!config.gameFolder.empty())
{
int unused = chdir(config.gameFolder.constData());
int unused = chdir(config.gameFolder.c_str());
(void) unused;
fileSystem.addPath(".");
}
// FIXME find out correct archive filename
QByteArray archPath = GAME_ARCHIVE;
std::string archPath = GAME_ARCHIVE;
if (QFile::exists(archPath.constData()))
fileSystem.addPath(archPath.constData());
/* Check if a game archive exists */
FILE *tmp = fopen(archPath.c_str(), "r");
if (tmp)
{
fileSystem.addPath(archPath.c_str());
fclose(tmp);
}
for (size_t i = 0; i < config.rtps.size(); ++i)
fileSystem.addPath(config.rtps[i].constData());
fileSystem.addPath(config.rtps[i].c_str());
fileSystem.createPathCache();

View File

@ -37,8 +37,6 @@
#include <sigc++/connection.h>
#include <QDebug>
struct SpritePrivate
{
Bitmap *bitmap;

View File

@ -23,17 +23,15 @@
#include "exception.h"
#include "sharedstate.h"
#include "glstate.h"
#include "boost-hash.h"
#include "debugwriter.h"
#include <QHash>
#include <QQueue>
#include <QList>
#include <QLinkedList>
#include <QPair>
#include <list>
#include <utility>
#include <assert.h>
#include <string.h>
#include <QDebug>
typedef QPair<uint16_t, uint16_t> Size;
typedef QQueue<TEXFBO> ObjList;
typedef std::pair<uint16_t, uint16_t> Size;
static uint32_t byteCount(Size &s)
{
@ -43,23 +41,23 @@ static uint32_t byteCount(Size &s)
struct CacheNode
{
TEXFBO obj;
QLinkedList<TEXFBO>::iterator prioIter;
std::list<TEXFBO>::iterator prioIter;
bool operator==(const CacheNode &o)
bool operator==(const CacheNode &o) const
{
return obj == o.obj;
}
};
typedef QList<CacheNode> CNodeList;
typedef std::list<CacheNode> CNodeList;
struct TexPoolPrivate
{
/* Contains all cached TexFBOs, grouped by size */
QHash<Size, CNodeList> poolHash;
BoostHash<Size, CNodeList> poolHash;
/* Contains all cached TexFBOs, sorted by release time */
QLinkedList<TEXFBO> priorityQueue;
std::list<TEXFBO> priorityQueue;
/* Maximal allowed cache memory */
const uint32_t maxMemSize;
@ -88,14 +86,18 @@ TexPool::TexPool(uint32_t maxMemSize)
TexPool::~TexPool()
{
while (!p->priorityQueue.isEmpty())
std::list<TEXFBO>::iterator iter;
for (iter = p->priorityQueue.begin();
iter != p->priorityQueue.end();
++iter)
{
TEXFBO obj = p->priorityQueue.takeFirst();
TEXFBO obj = *iter;
TEXFBO::fini(obj);
--p->objCount;
}
Q_ASSERT(p->objCount == 0);
assert(p->objCount == 0);
delete p;
}
@ -108,33 +110,39 @@ TEXFBO TexPool::request(int width, int height)
/* See if we can statisfy request from cache */
CNodeList &bucket = p->poolHash[size];
if (!bucket.isEmpty())
if (!bucket.empty())
{
/* Found one! */
cnode = bucket.takeLast();
cnode = bucket.back();
bucket.pop_back();
p->priorityQueue.erase(cnode.prioIter);
p->memSize -= byteCount(size);
--p->objCount;
// qDebug() << "TexPool: <?+> (" << width << height << ")";
// Debug() << "TexPool: <?+> (" << width << height << ")";
return cnode.obj;
}
int maxSize = glState.caps.maxTexSize;
if (width > maxSize || height > maxSize)
throw Exception(Exception::MKXPError,
"Texture dimensions [%s, %s] exceed hardware capabilities",
QByteArray::number(width), QByteArray::number(height));
{
char buffer[128];
snprintf(buffer, sizeof(buffer),
"Texture dimensions [%d, %d] exceed hardware capabilities",
width, height);
throw Exception(Exception::MKXPError, buffer);
}
/* Nope, create it instead */
TEXFBO::init(cnode.obj);
TEXFBO::allocEmpty(cnode.obj, width, height);
TEXFBO::linkFBO(cnode.obj);
// qDebug() << "TexPool: <?-> (" << width << height << ")";
// Debug() << "TexPool: <?-> (" << width << height << ")";
return cnode.obj;
}
@ -150,7 +158,7 @@ void TexPool::release(TEXFBO &obj)
if (p->disabled)
{
/* If we're disabled, delete without caching */
// qDebug() << "TexPool: <!#> (" << obj.width << obj.height << ")";
// Debug() << "TexPool: <!#> (" << obj.width << obj.height << ")";
TEXFBO::fini(obj);
return;
}
@ -166,39 +174,43 @@ void TexPool::release(TEXFBO &obj)
if (p->objCount == 0)
break;
// qDebug() << "TexPool: <!~> Size:" << p->memSize;
// Debug() << "TexPool: <!~> Size:" << p->memSize;
/* Retrieve object with lowest priority for deletion */
CacheNode last;
last.obj = p->priorityQueue.last();
last.obj = p->priorityQueue.back();
Size removedSize(last.obj.width, last.obj.height);
CNodeList &bucket = p->poolHash[removedSize];
Q_ASSERT(bucket.contains(last));
bucket.removeOne(last);
p->priorityQueue.removeLast();
std::list<CacheNode>::iterator toRemove =
std::find(bucket.begin(), bucket.end(), last);
assert(toRemove != bucket.end());
bucket.erase(toRemove);
p->priorityQueue.pop_back();
TEXFBO::fini(last.obj);
newMemSize -= byteCount(removedSize);;
newMemSize -= byteCount(removedSize);
--p->objCount;
// qDebug() << "TexPool: <!-> (" << last.obj.tex << last.obj.fbo << ")";
// Debug() << "TexPool: <!-> (" << last.obj.width << last.obj.height << ")";
}
p->memSize = newMemSize;
/* Retain object */
p->priorityQueue.prepend(obj);
p->priorityQueue.push_front(obj);
CacheNode cnode;
cnode.obj = obj;
cnode.prioIter = p->priorityQueue.begin();
CNodeList &bucket = p->poolHash[size];
bucket.append(cnode);
bucket.push_back(cnode);
++p->objCount;
// qDebug() << "TexPool: <!+> (" << obj.width << obj.height << ") Current size:" << p->memSize;
// Debug() << "TexPool: <!+> (" << obj.width << obj.height << ") Current size:" << p->memSize;
}
void TexPool::disable()

View File

@ -22,6 +22,9 @@
#ifndef UTIL_H
#define UTIL_H
#include <stdio.h>
#include <string>
static inline int
wrapRange(int value, int min, int max)
{
@ -56,6 +59,40 @@ findNextPow2(int start)
return i;
}
/* Reads the contents of the file at 'path' and
* appends them to 'out'. Returns false on failure */
inline bool readFile(const char *path,
std::string &out)
{
FILE *f = fopen(path, "r");
if (!f)
return false;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
size_t back = out.size();
out.resize(back+size);
size_t read = fread(&out[back], 1, size, f);
fclose(f);
if (read != (size_t) size)
out.resize(back+read);
return true;
}
inline void strReplace(std::string &str,
char before, char after)
{
for (size_t i = 0; i < str.size(); ++i)
if (str[i] == before)
str[i] = after;
}
#define ARRAY_SIZE(obj) (sizeof(obj) / sizeof((obj)[0]))
#define elementsN(obj) const int obj##N = ARRAY_SIZE(obj)

View File

@ -31,8 +31,6 @@
#include <sigc++/connection.h>
#include <QDebug>
struct ViewportPrivate
{
/* Needed for geometry changes */

View File

@ -36,8 +36,6 @@
#include <sigc++/connection.h>
#include <QDebug>
template<typename T>
struct Sides
{