merge total remake

This commit is contained in:
Eduardo Bart
2011-08-13 23:09:11 -03:00
parent 0af7856475
commit 55862b07ad
253 changed files with 6777 additions and 8463 deletions

View File

@@ -1,6 +1,8 @@
#ifndef CONST_H
#define CONST_H
//namespace Fw {
enum AlignmentFlag {
AlignLeft = 1,
AlignRight = 2,
@@ -19,4 +21,42 @@ enum AlignmentFlag {
AlignCenter = AlignVerticalCenter | AlignHorizontalCenter
};
#endif // CONST_H
enum AnchorPoint {
AnchorNone = 0,
AnchorTop,
AnchorBottom,
AnchorLeft,
AnchorRight,
AnchorVerticalCenter,
AnchorHorizontalCenter,
};
enum MouseButton {
MouseNoButton = 0,
MouseLeftButton,
MouseRightButton,
MouseMidButton
};
enum MouseWheelDirection {
MouseNoWheel = 0,
MouseWheelUp,
MouseWheelDown
};
enum KeyboardModifier {
KeyboardNoModifier = 0,
KeyboardCtrlModifier = 1,
KeyboardAltModifier = 2,
KeyboardShiftModifier = 4
};
enum ButtonState {
ButtonUp = 0,
ButtonDown,
ButtonHover
};
//}
#endif

View File

@@ -0,0 +1,35 @@
#include "configmanager.h"
#include "resourcemanager.h"
#include <otml/otml.h>
ConfigManager g_configs;
bool ConfigManager::load(const std::string& fileName)
{
m_fileName = fileName;
if(!g_resources.fileExists(fileName))
return false;
try {
OTMLDocumentPtr doc = OTMLDocument::parse(fileName);
for(const OTMLNodePtr& child : doc->childNodes())
m_confsMap[child->tag()] = child->value();
} catch(std::exception& e) {
logError("ERROR: could not load configurations: ", e.what());
return false;
}
return true;
}
bool ConfigManager::save()
{
if(!m_fileName.empty()) {
OTMLDocumentPtr doc = OTMLDocument::create();
doc->write(m_confsMap);
return doc->save(m_fileName);
}
return false;
}

View File

@@ -1,25 +1,25 @@
#ifndef CONFIGS_H
#define CONFIGS_H
#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H
#include <global.h>
struct ConfigValueProxy {
ConfigValueProxy(const std::string& v) : value(v) { }
operator std::string() const { return convert<std::string>(value); }
operator float() const { return convert<float>(value); }
operator int() const { return convert<int>(value); }
operator bool() const { return convert<bool>(value); }
operator std::string() const { return aux::unsafe_cast<std::string>(value); }
operator float() const { return aux::unsafe_cast<float>(value); }
operator int() const { return aux::unsafe_cast<int>(value); }
operator bool() const { return aux::unsafe_cast<bool>(value); }
std::string value;
};
class Configs
class ConfigManager
{
public:
bool load(const std::string& fileName);
void save();
bool save();
template<class T>
void set(const std::string& key, const T& value) { m_confsMap[key] = convert<std::string>(value); }
void set(const std::string& key, const T& value) { m_confsMap[key] = aux::unsafe_cast<std::string>(value); }
ConfigValueProxy get(const std::string& key) { return ConfigValueProxy(m_confsMap[key]); }
@@ -28,6 +28,6 @@ private:
std::map<std::string, std::string> m_confsMap;
};
extern Configs g_configs;
extern ConfigManager g_configs;
#endif // CONFIGS_H
#endif

View File

@@ -1,37 +0,0 @@
#include "configs.h"
#include "resources.h"
#include <otml/otml.h>
Configs g_configs;
bool Configs::load(const std::string& fileName)
{
m_fileName = fileName;
if(!g_resources.fileExists(fileName))
return false;
std::stringstream fin;
if(!g_resources.loadFile(fileName, fin))
return false;
try {
OTMLParser parser(fin, fileName);
parser.getDocument()->read(&m_confsMap);
} catch(OTMLException e) {
logError("ERROR: Malformed config file: ", e.what());
return false;
}
return true;
}
void Configs::save()
{
if(!m_fileName.empty()) {
OTMLEmitter emitter;
emitter.createDocument()->write(m_confsMap);
g_resources.saveFile(m_fileName, emitter.emitDocument());
}
}

View File

@@ -1,43 +0,0 @@
#include "dispatcher.h"
#include "engine.h"
Dispatcher g_dispatcher;
void Dispatcher::cleanup()
{
while(!m_scheduledTaskList.empty()) {
ScheduledTask *task = m_scheduledTaskList.top();
m_scheduledTaskList.pop();
delete task;
}
}
void Dispatcher::poll()
{
while(!m_taskList.empty()) {
m_taskList.front()();
m_taskList.pop_front();
}
while(!m_scheduledTaskList.empty()) {
ScheduledTask *task = m_scheduledTaskList.top();
if(g_engine.getCurrentFrameTicks() < task->ticks)
break;
m_scheduledTaskList.pop();
task->callback();
delete task;
}
}
void Dispatcher::scheduleTask(const std::function<void()>& callback, int delay)
{
m_scheduledTaskList.push(new ScheduledTask(g_engine.getCurrentFrameTicks() + delay, callback));
}
void Dispatcher::addTask(const std::function<void()>& callback, bool pushFront)
{
if(pushFront)
m_taskList.push_front(callback);
else
m_taskList.push_back(callback);
}

View File

@@ -1,43 +0,0 @@
#ifndef DISPATCHER_H
#define DISPATCHER_H
#include <global.h>
#include <queue>
struct ScheduledTask {
ScheduledTask(const std::function<void()>& _callback) : ticks(0), callback(_callback) { }
ScheduledTask(int _ticks, const std::function<void()>& _callback) : ticks(_ticks), callback(_callback) { }
bool operator<(const ScheduledTask& other) const { return ticks > other.ticks; }
int ticks;
std::function<void()> callback;
};
struct lessScheduledTask : public std::binary_function<ScheduledTask*&, ScheduledTask*&, bool> {
bool operator()(ScheduledTask*& t1,ScheduledTask*& t2) { return (*t1) < (*t2); }
};
class Dispatcher
{
public:
Dispatcher() { }
/// Cleanup scheduled events
void cleanup();
/// Execute scheduled events
void poll();
/// Add an event
void addTask(const std::function<void()>& callback, bool pushFront = false);
/// Schedula an event
void scheduleTask(const std::function<void()>& callback, int delay);
private:
std::list<std::function<void()>> m_taskList;
std::priority_queue<ScheduledTask*, std::vector<ScheduledTask*>, lessScheduledTask> m_scheduledTaskList;
};
extern Dispatcher g_dispatcher;
#endif // DISPATCHER_H

View File

@@ -1,155 +0,0 @@
#include "engine.h"
#include "platform.h"
#include "dispatcher.h"
#include <graphics/graphics.h>
#include <graphics/fonts.h>
#include <graphics/textures.h>
#include <ui/uicontainer.h>
#include <ui/uiskins.h>
#include <script/luainterface.h>
#include <net/connection.h>
#include <../game.h>
#include <../item.h>
Engine g_engine;
void Engine::init()
{
// initialize stuff
g_graphics.init();
g_fonts.init();
g_lua.init();
}
void Engine::terminate()
{
// destroy root ui
UIContainer::getRoot()->destroy();
// cleanup script stuff
g_lua.terminate();
// poll remaning events
g_engine.poll();
// terminate stuff
g_fonts.terminate();
g_graphics.terminate();
g_dispatcher.cleanup();
}
void Engine::poll()
{
// poll platform events
Platform::poll();
// poll diaptcher tasks
g_dispatcher.poll();
// poll network events
Connection::poll();
}
void Engine::run()
{
// check if root container has elements
const UIContainerPtr& rootContainer = UIContainer::getRoot();
if(rootContainer->getChildCount() == 0)
logFatal("FATAL ERROR: no ui loaded at all, no reason to continue running");
std::string fpsText;
Size fpsTextSize;
FontPtr defaultFont = g_uiSkins.getDefaultFont();
m_lastFrameTicks = Platform::getTicks();
int lastFpsTicks = m_lastFrameTicks;
int frameCount = 0;
int fps = 0;
m_running = true;
while(!m_stopping) {
m_lastFrameTicks = Platform::getTicks();
poll();
// render only when visible
if(Platform::isWindowVisible()) {
// calculate fps
if(m_calculateFps) {
frameCount++;
if(m_lastFrameTicks - lastFpsTicks >= 1000) {
lastFpsTicks = m_lastFrameTicks;
fps = frameCount;
frameCount = 0;
// update fps text
fpsText = make_string("FPS: ", fps);
fpsTextSize = defaultFont->calculateTextRectSize(fpsText);
}
}
// render
g_graphics.beginRender();
rootContainer->render();
// todo remove. render map
g_game.getMap()->draw(0, 0);
// todo remove. view items
static Item *item = NULL;
if(!item) {
item = new Item();
item->setId(8377);
}
//item->draw(1, 1, 7);
// render fps
if(m_calculateFps)
defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - fpsTextSize.width() - 10, 10));
g_graphics.endRender();
// swap buffers
Platform::swapBuffers();
}
}
m_stopping = false;
m_running = false;
}
void Engine::stop()
{
m_stopping = true;
}
void Engine::onClose()
{
g_lua.getGlobal("onClose")->call("onClose");
}
void Engine::onResize(const Size& size)
{
g_graphics.resize(size);
UIContainer::getRoot()->setSize(size);
}
void Engine::onInputEvent(const InputEvent& event)
{
UIContainer::getRoot()->onInputEvent(event);
ProtocolGame *protocol = g_game.getProtocol();
if(protocol) {
if(event.type == EV_KEY_DOWN) {
if(event.keycode == KC_UP)
protocol->sendWalkNorth();
if(event.keycode == KC_RIGHT)
protocol->sendWalkEast();
if(event.keycode == KC_DOWN)
protocol->sendWalkSouth();
if(event.keycode == KC_LEFT)
protocol->sendWalkWest();
}
}
}

View File

@@ -1,53 +0,0 @@
#ifndef ENGINE_H
#define ENGINE_H
#include <global.h>
#include "input.h"
class Engine
{
public:
Engine() : m_stopping(false),
m_running(false),
m_calculateFps(false) { }
void init();
void terminate();
/// Poll events
void poll();
/// Main loop
void run();
/// Stops main loop
void stop();
/// Change current game state
bool isRunning() const { return m_running; }
bool isStopping() const { return m_stopping; }
/// Fired by platform on window close
void onClose();
/// Fired by platform on window resize
void onResize(const Size& size);
/// Fired by platform on mouse/keyboard input
void onInputEvent(const InputEvent& event);
/// Enable FPS counter on screen
void enableFpsCounter(bool enable = true) { m_calculateFps = enable; };
/// Return the current ticks on this frame
int getCurrentFrameTicks() const { return m_lastFrameTicks; }
private:
bool m_stopping;
bool m_running;
bool m_calculateFps;
int m_lastFrameTicks;
};
extern Engine g_engine;
#endif // ENGINE_H

View File

@@ -0,0 +1,45 @@
#include "eventdispatcher.h"
#include <core/platform.h>
EventDispatcher g_dispatcher;
void EventDispatcher::init()
{
// nothing to do
}
void EventDispatcher::terminate()
{
// clean scheduled events
while(!m_scheduledEventList.empty())
m_scheduledEventList.pop();
}
void EventDispatcher::poll()
{
while(!m_eventList.empty()) {
m_eventList.front()();
m_eventList.pop_front();
}
while(!m_scheduledEventList.empty()) {
if(g_platform.getTicks() < m_scheduledEventList.top().ticks)
break;
SimpleCallback callback = std::move(m_scheduledEventList.top().callback);
m_scheduledEventList.pop();
callback();
}
}
void EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay)
{
m_scheduledEventList.push(ScheduledEvent(g_platform.getTicks() + delay, callback));
}
void EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
{
if(pushFront)
m_eventList.push_front(callback);
else
m_eventList.push_back(callback);
}

View File

@@ -0,0 +1,38 @@
#ifndef EVENTDISPATCHER_H
#define EVENTDISPATCHER_H
#include <global.h>
struct ScheduledEvent {
ScheduledEvent(int ticks, const SimpleCallback& callback) : ticks(ticks), callback(callback) { }
bool operator<(const ScheduledEvent& other) const { return ticks > other.ticks; }
int ticks;
SimpleCallback callback;
};
class EventDispatcher
{
public:
/// Initialize dispatcher
void init();
/// Cleanup scheduled events
void terminate();
/// Execute scheduled events
void poll();
/// Add an event
void addEvent(const SimpleCallback& callback, bool pushFront = false);
/// Schedule an event
void scheduleEvent(const SimpleCallback& callback, int delay);
private:
std::list<SimpleCallback> m_eventList;
std::priority_queue<ScheduledEvent> m_scheduledEventList;
};
extern EventDispatcher g_dispatcher;
#endif

View File

@@ -1,10 +1,9 @@
#ifndef INPUT_H
#define INPUT_H
#ifndef INPUTEVENT_H
#define INPUTEVENT_H
#include <util/types.h>
#include <util/point.h>
#include <global.h>
enum EKeyCode {
enum InputKeyCode {
KC_UNKNOWN = 0x00,
KC_ESCAPE = 0x01,
KC_1 = 0x02,
@@ -152,28 +151,28 @@ enum EKeyCode {
KC_MEDIASELECT = 0xED // Media Select
};
enum EEvent {
EV_MOUSE = 1,
EV_KEYBOARD = 2,
EV_DOWN = 4,
EV_UP = 8,
EV_MOUSE_WHEEL = 16,
EV_MOUSE_LEFT = 32,
EV_MOUSE_RIGHT = 64,
EV_MOUSE_MIDDLE = 128,
EV_TEXT_ENTER = EV_KEYBOARD | 256,
EV_KEY_DOWN = EV_KEYBOARD | EV_DOWN,
EV_KEY_UP = EV_KEYBOARD | EV_UP,
EV_MOUSE_MOVE = EV_MOUSE | 512,
EV_MOUSE_LDOWN = EV_MOUSE | EV_MOUSE_LEFT | EV_DOWN,
EV_MOUSE_LUP = EV_MOUSE | EV_MOUSE_LEFT | EV_UP,
EV_MOUSE_MDOWN = EV_MOUSE | EV_MOUSE_MIDDLE | EV_DOWN,
EV_MOUSE_MUP = EV_MOUSE | EV_MOUSE_MIDDLE | EV_UP,
EV_MOUSE_RDOWN = EV_MOUSE | EV_MOUSE_RIGHT | EV_DOWN,
EV_MOUSE_RUP = EV_MOUSE | EV_MOUSE_RIGHT | EV_UP,
EV_MOUSE_WHEEL_UP = EV_MOUSE | EV_MOUSE_WHEEL | EV_UP,
EV_MOUSE_WHEEL_DOWN = EV_MOUSE | EV_MOUSE_WHEEL | EV_DOWN
enum InputEventType {
EventNone = 0,
EventMouseAction = 1,
EventKeyboardAction = 2,
EventDown = 4,
EventUp = 8,
EventMouseWheel = 16,
EventMouseLeftButton = 32,
EventMouseRightButton = 64,
EventMouseMidButton = 128,
EventTextEnter = 256,
EventKeyDown = EventKeyboardAction | EventDown,
EventKeyUp = EventKeyboardAction | EventUp,
EventMouseMove = EventMouseAction | 512,
EventMouseLeftButtonDown = EventMouseAction | EventMouseLeftButton | EventDown,
EventMouseLeftButtonUp = EventMouseAction | EventMouseLeftButton | EventUp,
EventMouseMiddleButtonDown = EventMouseAction | EventMouseMidButton | EventDown,
EventMouseMiddleButtonUp = EventMouseAction | EventMouseMidButton | EventUp,
EventMouseRightButtonDown = EventMouseAction | EventMouseRightButton | EventDown,
EventMouseRightButtonUp = EventMouseAction | EventMouseRightButton | EventUp,
EventMouseWheelUp = EventMouseAction | EventMouseWheel | EventUp,
EventMouseWheelDown = EventMouseAction | EventMouseWheel | EventDown
};
struct InputEvent {
@@ -187,4 +186,4 @@ struct InputEvent {
bool alt;
};
#endif // INPUT_H
#endif

View File

@@ -0,0 +1,73 @@
#include "module.h"
#include <otml/otml.h>
#include <luascript/luainterface.h>
#include "modulemanager.h"
void Module::discover(const OTMLNodePtr& moduleNode)
{
m_description = moduleNode->readAt<std::string>("description");
m_author = moduleNode->readAt<std::string>("author");
m_website = moduleNode->readAt<std::string>("website");
m_version = moduleNode->readAt<std::string>("version");
if(OTMLNodePtr node = moduleNode->get("dependencies")) {
for(const OTMLNodePtr& tmp : node->childNodes())
m_dependencies.push_back(tmp->value());
}
// set onLoad callback
if(OTMLNodePtr node = moduleNode->get("onLoad")) {
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
g_lua.useValue();
m_loadCallback = g_lua.polymorphicPop<BooleanCallback>();
}
// set onUnload callback
if(OTMLNodePtr node = moduleNode->get("onUnload")) {
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
g_lua.useValue();
m_unloadCallback = g_lua.polymorphicPop<SimpleCallback>();
}
// load if autoLoad is set
m_autoLoad = moduleNode->readAt<bool>("autoLoad", false);
}
bool Module::load()
{
for(const std::string& depName : m_dependencies) {
ModulePtr dep = g_modules.getModule(depName);
if(!dep) {
logError("ERROR: failed to load module '",m_name,"': could not find module dependency '",depName,"'");
return false;
}
if(!dep->isLoaded()) {
if(!dep->load()) {
logError("ERROR: failed to load module '",m_name,"': a dependency has failed to load");
return false;
}
}
}
if(m_loadCallback) {
m_loaded = m_loadCallback();
if(!m_loaded) {
logError("ERROR: failed to load module '",m_name, "': onLoad returned false");
return false;
}
}
logInfo("Loaded module '", m_name, "'");
return true;
}
void Module::unload()
{
if(m_loaded) {
if(m_unloadCallback)
m_unloadCallback();
m_loaded = false;
}
}

View File

@@ -0,0 +1,40 @@
#ifndef MODULE_H
#define MODULE_H
#include <otml/otmldeclarations.h>
class Module;
typedef std::shared_ptr<Module> ModulePtr;
class Module
{
public:
Module(const std::string& name) : m_loaded(false), m_autoLoad(false), m_name(name) { }
void discover(const OTMLNodePtr& moduleNode);
bool load();
void unload();
bool isLoaded() const { return m_loaded; }
std::string getDescription() const { return m_description; }
std::string getName() const { return m_name; }
std::string getAuthor() const { return m_author; }
std::string getWebsite() const { return m_website; }
std::string getVersion() const { return m_version; }
bool autoLoad() const { return m_autoLoad; }
private:
bool m_loaded;
bool m_autoLoad;
std::string m_name;
std::string m_description;
std::string m_author;
std::string m_website;
std::string m_version;
BooleanCallback m_loadCallback;
SimpleCallback m_unloadCallback;
std::list<std::string> m_dependencies;
};
#endif

View File

@@ -0,0 +1,58 @@
#include "modulemanager.h"
#include "resourcemanager.h"
#include <otml/otml.h>
ModuleManager g_modules;
void ModuleManager::discoverAndLoadModules()
{
auto moduleDirs = g_resources.listDirectoryFiles("/");
for(const std::string& moduleDir : moduleDirs) {
auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir);
for(const std::string& file : moduleFiles) {
if(boost::ends_with(file, ".otmod"))
discoverModule("/" + moduleDir + "/" + file);
}
}
// auto load modules
for(const ModulePtr& module : m_modules) {
if(!module->isLoaded() && module->autoLoad())
module->load();
}
}
bool ModuleManager::discoverModule(const std::string& file)
{
ModulePtr module;
try {
OTMLDocumentPtr doc = OTMLDocument::parse(file);
OTMLNodePtr moduleNode = doc->at("Module");
std::string name = moduleNode->readAt<std::string>("name");
if(getModule(name))
throw OTMLException(moduleNode, "a module with the same name is already discovered, did you duplicate module names?");
module = ModulePtr(new Module(name));
module->discover(moduleNode);
m_modules.push_back(module);
} catch(std::exception& e) {
logError("ERROR: failed to load module from '", file, "':\n", e.what());
return false;
}
return true;
}
void ModuleManager::unloadModules()
{
for(const ModulePtr& module : m_modules)
module->unload();
}
ModulePtr ModuleManager::getModule(const std::string& moduleName)
{
for(const ModulePtr& module : m_modules)
if(module->getName() == moduleName)
return module;
return nullptr;
}

View File

@@ -0,0 +1,21 @@
#ifndef MODULEMANAGER_H
#define MODULEMANAGER_H
#include "module.h"
class ModuleManager
{
public:
void discoverAndLoadModules();
bool discoverModule(const std::string& file);
void unloadModules();
ModulePtr getModule(const std::string& moduleName);
private:
std::vector<ModulePtr> m_modules;
};
extern ModuleManager g_modules;
#endif

View File

@@ -1,29 +0,0 @@
#include "packages.h"
#include <core/resources.h>
#include <script/luainterface.h>
Packages g_packages;
void Packages::loadPackages()
{
std::list<std::string> packages = g_resources.listDirectoryFiles("modules");
foreach(const std::string& package, packages) {
std::string dir = make_string("modules/", package);
g_resources.pushCurrentPath(dir);
std::list<std::string> packagesFiles = g_resources.listDirectoryFiles();
foreach(const std::string& packageFile, packagesFiles) {
if(boost::ends_with(packageFile, ".lua")) {
g_lua.runScript(packageFile);
}
}
g_resources.popCurrentPath();
}
}
void Packages::terminate()
{
}

View File

@@ -1,15 +0,0 @@
#ifndef PACKAGES_H
#define PACKAGES_H
#include <global.h>
class Packages
{
public:
void loadPackages();
void terminate();
};
extern Packages g_packages;
#endif // MODULES_H

View File

@@ -3,53 +3,63 @@
#include <global.h>
class PlatformListener;
class Platform
{
public:
static void init(const char *appName);
static void terminate();
void init(PlatformListener* platformListener, const char* appName);
void terminate();
/// Poll platform input/window events
static void poll();
void poll();
/// Get current time in milliseconds since init
static int getTicks();
int getTicks();
/// Sleep in current thread
static void sleep(ulong miliseconds);
void sleep(ulong ms);
static bool createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized);
static void destroyWindow();
static void showWindow();
static void setWindowTitle(const char *title);
static bool isWindowFocused();
static bool isWindowVisible();
static int getWindowX();
static int getWindowY();
static int getWindowWidth();
static int getWindowHeight();
static bool isWindowMaximized();
bool createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized);
void destroyWindow();
void showWindow();
void hideWindow();
void setWindowTitle(const char* title);
bool isWindowFocused();
bool isWindowVisible();
int getWindowX();
int getWindowY();
int getWindowWidth();
int getWindowHeight();
bool isWindowMaximized();
static int getDisplayHeight();
static int getDisplayWidth();
int getDisplayHeight();
int getDisplayWidth();
/// Get GL extension function address
static void *getExtensionProcAddress(const char *ext);
/// Check if GL extension is supported
static bool isExtensionSupported(const char *ext);
void* getExtensionProcAddress(const char* ext);
/// Check if GLX/WGL extension is supported
bool isExtensionSupported(const char* ext);
static const char *getClipboardText();
static void setClipboardText(const char *text);
/// Get text from Ctrl+c
const char* getClipboardText();
/// Set text for Ctrl+v
void setClipboardText(const char* text);
static void hideMouseCursor();
static void showMouseCursor();
void hideMouseCursor();
void showMouseCursor();
/// Enable/disable vertical synchronization
static void setVsync(bool enable = true);
/// Enable or disable vertical synchronization
void setVerticalSync(bool enable);
/// Swap GL buffers
static void swapBuffers();
void swapBuffers();
/// Get the app user directory, the place to save files configurations files
static std::string getAppUserDir();
std::string getAppUserDir();
private:
PlatformListener* m_listener;
};
#endif // PLATFORM_H
extern Platform g_platform;
#endif

View File

@@ -0,0 +1,17 @@
#ifndef PLATFORMLISTENER_H
#define PLATFORMLISTENER_H
#include "inputevent.h"
class PlatformListener
{
public:
/// Fired when user tries to close the window
virtual void onClose() = 0;
/// Fired when user resize the window
virtual void onResize(const Size& size) = 0;
/// Fired when user press a key or move the mouse
virtual void onInputEvent(const InputEvent& event) = 0;
};
#endif

View File

@@ -0,0 +1,170 @@
#include "resourcemanager.h"
#include <core/platform.h>
#include <luascript/luainterface.h>
#include <physfs.h>
ResourceManager g_resources;
void ResourceManager::init(const char* argv0)
{
PHYSFS_init(argv0);
// try to find modules directory, all data lives there
std::string baseDir = PHYSFS_getBaseDir();
std::string possibleDirs[] = { "modules",
baseDir + "modules",
baseDir + "../modules",
baseDir + "../share/otclient/modules",
"" };
bool found = false;
for(const std::string& dir : possibleDirs) {
if(g_resources.addToSearchPath(dir)) {
logInfo("Using modules directory '", dir.c_str(), "'");
found = true;
break;
}
}
if(!found)
throw std::runtime_error("could not find modules directory");
// setup write directory
std::string dir = g_platform.getAppUserDir();
if(g_resources.setWriteDir(dir))
g_resources.addToSearchPath(dir);
else
throw std::runtime_error("could not setup write directory");
}
void ResourceManager::terminate()
{
PHYSFS_deinit();
}
bool ResourceManager::setWriteDir(const std::string& path)
{
if(!PHYSFS_setWriteDir(path.c_str()))
return false;
return true;
}
bool ResourceManager::addToSearchPath(const std::string& path, bool insertInFront)
{
if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1))
return false;
return true;
}
void ResourceManager::searchAndAddPackages(const std::string& packagesDir, const std::string& packageExt, bool append)
{
auto files = listDirectoryFiles(resolvePath(packagesDir));
for(const std::string& file : files) {
if(boost::ends_with(file, packageExt))
addToSearchPath(packagesDir + "/" + file, !append);
}
}
bool ResourceManager::fileExists(const std::string& fileName)
{
return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str()));
}
bool ResourceManager::directoryExists(const std::string& directoryName)
{
return (PHYSFS_exists(resolvePath(directoryName).c_str()) && PHYSFS_isDirectory(resolvePath(directoryName).c_str()));
}
void ResourceManager::loadFile(const std::string& fileName, std::iostream& out)
{
std::string fullPath = resolvePath(fileName);
out.clear(std::ios::goodbit);
PHYSFS_file* file = PHYSFS_openRead(fullPath.c_str());
if(!file) {
out.clear(std::ios::failbit);
throw std::runtime_error(aux::make_string("failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError()));
} else {
int fileSize = PHYSFS_fileLength(file);
if(fileSize > 0) {
char* buffer = new char[fileSize];
PHYSFS_read(file, (void*)buffer, 1, fileSize);
out.write(buffer, fileSize);
delete[] buffer;
} else
out.clear(std::ios::eofbit);
PHYSFS_close(file);
out.seekg(0, std::ios::beg);
}
}
std::string ResourceManager::loadFile(const std::string& fileName)
{
std::stringstream fin;
loadFile(fileName, fin);
return fin.str();
}
bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, uint size)
{
PHYSFS_file* file = PHYSFS_openWrite(resolvePath(fileName).c_str());
if(!file)
return false;
PHYSFS_write(file, (void*)data, size, 1);
PHYSFS_close(file);
return true;
}
bool ResourceManager::saveFile(const std::string& fileName, std::istream& in)
{
std::streampos oldPos = in.tellg();
in.seekg(0, std::ios::end);
std::streampos size = in.tellg();
in.seekg(0, std::ios::beg);
char* buffer = new char[size];
in.read(buffer, size);
bool ret = saveFile(fileName, (const uchar*)buffer, size);
delete[] buffer;
in.seekg(oldPos, std::ios::beg);
return ret;
}
bool ResourceManager::saveFile(const std::string& fileName, const std::string& data)
{
return saveFile(fileName, (const uchar*)data.c_str(), data.size());
}
bool ResourceManager::deleteFile(const std::string& fileName)
{
return PHYSFS_delete(resolvePath(fileName).c_str()) != 0;
}
std::list<std::string> ResourceManager::listDirectoryFiles(const std::string& directoryPath)
{
std::list<std::string> files;
char** rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str());
for(char** i = rc; *i != NULL; i++)
files.push_back(*i);
PHYSFS_freeList(rc);
return files;
}
std::string ResourceManager::resolvePath(const std::string& path)
{
std::string fullPath;
if(boost::starts_with(path, "/"))
fullPath = path.substr(1);
else {
std::string scriptPath = g_lua.currentSourcePath();
if(!scriptPath.empty()) {
fullPath += scriptPath + "/";
}
fullPath += path;
}
return fullPath;
}

View File

@@ -0,0 +1,40 @@
#ifndef RESOURCES_H
#define RESOURCES_H
#include <global.h>
class ResourceManager
{
public:
void init(const char* argv0);
void terminate();
/// Set output files directory
bool setWriteDir(const std::string& path);
/// Add an package or directory to the search path
bool addToSearchPath(const std::string& path, bool insertInFront = true);
/// Search and packages from a directory to the search path
void searchAndAddPackages(const std::string& packagesDir, const std::string& packagesExt, bool append);
bool fileExists(const std::string& fileName);
bool directoryExists(const std::string& directoryName);
void loadFile(const std::string& fileName, std::iostream& out);
std::string loadFile(const std::string& fileName);
bool saveFile(const std::string& fileName, const uchar* data, uint size);
bool saveFile(const std::string& fileName, const std::string& data);
bool saveFile(const std::string& fileName, std::istream& in);
bool deleteFile(const std::string& fileName);
std::list<std::string> listDirectoryFiles(const std::string& directoryPath = "");
std::string resolvePath(const std::string& path);
};
extern ResourceManager g_resources;
#endif

View File

@@ -1,176 +0,0 @@
#include <global.h>
#include "resources.h"
#include "platform.h"
#include <physfs.h>
Resources g_resources;
void Resources::init(const char *argv0)
{
PHYSFS_init(argv0);
// try to find data directory
std::string dir;
std::string baseDir = PHYSFS_getBaseDir();
std::string possibleDirs[] = { "data",
baseDir + "data",
baseDir + "../data",
baseDir + "../share/otclient/data",
"" };
bool found = false;
foreach(dir, possibleDirs) {
if(g_resources.addToSearchPath(dir)) {
logInfo("Using data directory: ", dir.c_str());
found = true;
break;
}
}
if(!found)
logFatal("ERROR: could not find data directory");
// setup write directory
dir = Platform::getAppUserDir();
if(g_resources.setWriteDir(dir))
g_resources.addToSearchPath(dir);
else
logError("ERROR: could not setup write directory");
}
void Resources::terminate()
{
PHYSFS_deinit();
}
bool Resources::setWriteDir(const std::string& path)
{
if(!PHYSFS_setWriteDir(path.c_str()))
return false;
return true;
}
bool Resources::addToSearchPath(const std::string& path, bool insertInFront /*= true*/)
{
if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1))
return false;
return true;
}
void Resources::addPackagesToSearchPath(const std::string &packagesDirectory, const std::string &packageExtension, bool append)
{
auto files = listDirectoryFiles(resolvePath(packagesDirectory));
foreach(const std::string& file, files) {
if(boost::ends_with(file, packageExtension))
addToSearchPath(packagesDirectory + "/" + file, !append);
}
}
bool Resources::fileExists(const std::string& fileName)
{
return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str()));
}
bool Resources::directoryExists(const std::string& directoryName)
{
return (PHYSFS_exists(resolvePath(directoryName).c_str()) && PHYSFS_isDirectory(resolvePath(directoryName).c_str()));
}
bool Resources::loadFile(const std::string& fileName, std::iostream& out)
{
std::string fullPath = resolvePath(fileName);
out.clear(std::ios::goodbit);
PHYSFS_file *file = PHYSFS_openRead(fullPath.c_str());
if(!file) {
logError("ERROR: Failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError());
out.clear(std::ios::failbit);
return false;
} else {
int fileSize = PHYSFS_fileLength(file);
if(fileSize > 0) {
char *buffer = new char[fileSize];
PHYSFS_read(file, (void*)buffer, 1, fileSize);
out.write(buffer, fileSize);
delete[] buffer;
} else
out.clear(std::ios::eofbit);
PHYSFS_close(file);
out.seekg(0, std::ios::beg);
return true;
}
}
bool Resources::saveFile(const std::string &fileName, const uchar *data, uint size)
{
PHYSFS_file *file = PHYSFS_openWrite(resolvePath(fileName).c_str());
if(!file) {
logError("ERROR: Failed to save file '",fileName,"': ",PHYSFS_getLastError());
return false;
}
PHYSFS_write(file, (void*)data, size, 1);
PHYSFS_close(file);
return true;
}
bool Resources::saveFile(const std::string &fileName, std::istream& in)
{
std::streampos oldPos = in.tellg();
in.seekg(0, std::ios::end);
std::streampos size = in.tellg();
in.seekg(0, std::ios::beg);
char *buffer = new char[size];
in.read(buffer, size);
bool ret = saveFile(fileName, (const uchar*)buffer, size);
delete[] buffer;
in.seekg(oldPos, std::ios::beg);
return ret;
}
bool Resources::deleteFile(const std::string& fileName)
{
return PHYSFS_delete(resolvePath(fileName).c_str()) != 0;
}
std::list<std::string> Resources::listDirectoryFiles(const std::string& directoryPath)
{
std::list<std::string> files;
char **rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str());
for(char **i = rc; *i != NULL; i++)
files.push_back(*i);
PHYSFS_freeList(rc);
return files;
}
void Resources::pushCurrentPath(const std::string &currentPath)
{
//logTraceDebug(currentPath);
m_currentPaths.push(currentPath);
}
void Resources::popCurrentPath()
{
m_currentPaths.pop();
//if(!m_currentPaths.empty())
// logTraceDebug(m_currentPaths.top());
}
std::string Resources::resolvePath(const std::string& path)
{
std::string fullPath;
if(boost::starts_with(path, "/"))
fullPath = path.substr(1);
else {
if(m_currentPaths.size() > 0) {
std::string currentPath = m_currentPaths.top();
if(currentPath.length() > 0)
fullPath += currentPath + "/";
}
fullPath += path;
}
return fullPath;
}

View File

@@ -1,46 +0,0 @@
#ifndef RESOURCES_H
#define RESOURCES_H
#include <global.h>
#include <stack>
class Resources
{
public:
Resources() { }
void init(const char *argv0);
void terminate();
/// Set output files dir
bool setWriteDir(const std::string &path);
/// Add an package or directory to the search path
bool addToSearchPath(const std::string& path, bool insertInFront = true);
/// Add all packages from a directory
void addPackagesToSearchPath(const std::string &packagesDirectory, const std::string &packageExtension, bool append);
bool fileExists(const std::string& fileName);
bool directoryExists(const std::string& directoryName);
bool loadFile(const std::string& fileName, std::iostream& out);
bool saveFile(const std::string& fileName, const uchar *data, uint size);
bool saveFile(const std::string& fileName, const std::string& data) { return saveFile(fileName, (const uchar*)data.c_str(), data.size()); }
bool saveFile(const std::string& fileName, std::istream& in);
bool deleteFile(const std::string& fileName);
std::list<std::string> listDirectoryFiles(const std::string& directoryPath = "");
void pushCurrentPath(const std::string &currentPath);
void popCurrentPath();
std::string resolvePath(const std::string& path);
private:
std::stack<std::string> m_currentPaths;
};
extern Resources g_resources;
#endif // RESOURCES_H

View File

View File

@@ -1,6 +1,5 @@
#include <global.h>
#include <core/platform.h>
#include <core/engine.h>
#include "platform.h"
#include "platformlistener.h"
#include <sys/time.h>
#include <sys/stat.h>
@@ -41,12 +40,15 @@ struct X11PlatformPrivate {
int y;
std::string clipboardText;
std::map<int, uchar> keyMap;
PlatformListener* listener;
} x11;
void Platform::init(const char *appName)
Platform g_platform;
void Platform::init(PlatformListener* platformListener, const char *appName)
{
// seend random numbers
std::srand(std::time(NULL));
srand(time(NULL));
x11.appName = appName;
x11.display = NULL;
@@ -62,6 +64,7 @@ void Platform::init(const char *appName)
x11.width = 0;
x11.height = 0;
x11.maximizeOnFirstShow = false;
m_listener = platformListener;
// setup keymap
x11.keyMap[XK_1] = KC_1;
@@ -280,7 +283,7 @@ void Platform::poll()
static int oldWidth = -1;
static int oldHeight = -1;
if(oldWidth != event.xconfigure.width || oldHeight != event.xconfigure.height) {
g_engine.onResize(Size(event.xconfigure.width, event.xconfigure.height));
m_listener->onResize(Size(event.xconfigure.width, event.xconfigure.height));
oldWidth = event.xconfigure.width;
oldHeight = event.xconfigure.height;
}
@@ -329,10 +332,10 @@ void Platform::poll()
(uchar)(buf[0]) >= 32
) {
//logDebug("char: ", buf[0], " code: ", (uint)buf[0]);
inputEvent.type = EV_TEXT_ENTER;
inputEvent.type = EventTextEnter;
inputEvent.keychar = buf[0];
inputEvent.keycode = KC_UNKNOWN;
g_engine.onInputEvent(inputEvent);
m_listener->onInputEvent(inputEvent);
}
}
@@ -343,9 +346,9 @@ void Platform::poll()
// fire key up/down event
if(x11.keyMap.find(keysym) != x11.keyMap.end()) {
inputEvent.keycode = x11.keyMap[keysym];
inputEvent.type = (event.type == KeyPress) ? EV_KEY_DOWN : EV_KEY_UP;
inputEvent.type = (event.type == KeyPress) ? EventKeyDown : EventKeyUp;
inputEvent.keychar = (len > 0) ? buf[0] : 0;
g_engine.onInputEvent(inputEvent);
m_listener->onInputEvent(inputEvent);
}
break;
}
@@ -353,31 +356,31 @@ void Platform::poll()
case ButtonRelease:
switch(event.xbutton.button) {
case Button1:
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_LDOWN : EV_MOUSE_LUP;
inputEvent.type = (event.type == ButtonPress) ? EventMouseLeftButtonDown : EventMouseLeftButtonUp;
break;
case Button3:
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_RDOWN : EV_MOUSE_RUP;
inputEvent.type = (event.type == ButtonPress) ? EventMouseRightButtonDown : EventMouseRightButtonUp;
break;
case Button2:
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_MDOWN : EV_MOUSE_MUP;
inputEvent.type = (event.type == ButtonPress) ? EventMouseMiddleButtonDown : EventMouseMiddleButtonUp;
break;
case Button4:
inputEvent.type = EV_MOUSE_WHEEL_UP;
inputEvent.type = EventMouseWheelUp;
break;
case Button5:
inputEvent.type = EV_MOUSE_WHEEL_DOWN;
inputEvent.type = EventMouseWheelDown;
break;
}
g_engine.onInputEvent(inputEvent);
m_listener->onInputEvent(inputEvent);
break;
case MotionNotify:
{
inputEvent.type = EV_MOUSE_MOVE;
inputEvent.type = EventMouseMove;
Point newMousePos(event.xbutton.x, event.xbutton.y);
inputEvent.mouseMoved = newMousePos - inputEvent.mousePos;
inputEvent.mousePos = newMousePos;
g_engine.onInputEvent(inputEvent);
m_listener->onInputEvent(inputEvent);
break;
}
@@ -437,7 +440,7 @@ void Platform::poll()
case ClientMessage:
{
if((Atom)event.xclient.data.l[0] == x11.atomDeleteWindow)
g_engine.onClose();
m_listener->onClose();
break;
}
}
@@ -555,7 +558,7 @@ bool Platform::createWindow(int x, int y, int width, int height, int minWidth, i
x11.maximizeOnFirstShow = maximized;
// call first onResize
g_engine.onResize(Size(width, height));
m_listener->onResize(Size(width, height));
return true;
}
@@ -579,7 +582,6 @@ void Platform::destroyWindow()
}
if(x11.window != None) {
XUnmapWindow(x11.display, x11.window);
XDestroyWindow(x11.display, x11.window);
x11.window = None;
}
@@ -607,7 +609,7 @@ void Platform::showWindow()
// set window maximized if needed
if(x11.maximizeOnFirstShow) {
XEvent e;
bzero(&e, sizeof(XEvent));
memset(&e, 0, sizeof(XEvent));
e.xany.type = ClientMessage;
e.xclient.message_type = x11.atomWindowState;
e.xclient.format = 32;
@@ -624,6 +626,11 @@ void Platform::showWindow()
}
}
void Platform::hideWindow()
{
XUnmapWindow(x11.display, x11.window);
}
void Platform::setWindowTitle(const char *title)
{
XStoreName(x11.display, x11.window, title);
@@ -722,7 +729,7 @@ void Platform::showMouseCursor()
}
}
void Platform::setVsync(bool enable)
void Platform::setVerticalSync(bool enable)
{
typedef GLint (*glSwapIntervalProc)(GLint);
glSwapIntervalProc glSwapInterval = NULL;
@@ -817,4 +824,4 @@ std::string Platform::getAppUserDir()
if((mkdir(sdir.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST))
logError("ERROR: Couldn't create directory for saving configuration file. (",sdir.str(),")");
return sdir.str();
}
}

View File

@@ -16,24 +16,30 @@
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <algorithm>
#include <exception>
#include <memory>
#include <type_traits>
#include <tuple>
#include <functional>
#include <regex>
#include <unordered_map>
#include <typeinfo>
#include <array>
// string algorithms
// boost utilities
#include <boost/algorithm/string.hpp>
// constants
#include <const.h>
// easy types
// additional utilities
#include <util/types.h>
#include <util/auxiliary.h>
#include <util/logger.h>
#include <util/translator.h>
// custom types
#include <util/point.h>
@@ -41,11 +47,4 @@
#include <util/rect.h>
#include <util/size.h>
// additional utilities
#include <util/convert.h>
#include <util/foreach.h>
#include <util/makestring.h>
#include <util/logger.h>
#include <util/algorithms.h>
#endif // GLOBAL_H
#endif

View File

@@ -1,30 +1,9 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "animatedtexture.h"
#include "graphics.h"
#include <core/platform.h>
#include <core/eventdispatcher.h>
#include <graphics/animatedtexture.h>
#include <core/engine.h>
#include <core/dispatcher.h>
#include <GL/gl.h>
AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) :
Texture(),
@@ -44,11 +23,13 @@ AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFra
}
m_currentFrame = -1;
g_dispatcher.scheduleTask(std::bind(&AnimatedTexture::processAnimation, this), 0);
g_dispatcher.scheduleEvent(std::bind(&AnimatedTexture::processAnimation, this), 0);
}
AnimatedTexture::~AnimatedTexture()
{
g_graphics.disableDrawing();
glDeleteTextures(m_numFrames, m_framesTextureId);
delete[] m_framesTextureId;
delete[] m_framesDelay;
@@ -57,6 +38,8 @@ AnimatedTexture::~AnimatedTexture()
void AnimatedTexture::enableBilinearFilter()
{
g_graphics.disableDrawing();
for(int i=0;i<m_numFrames;++i) {
glBindTexture(GL_TEXTURE_2D, m_framesTextureId[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -72,5 +55,5 @@ void AnimatedTexture::processAnimation()
m_textureId = m_framesTextureId[m_currentFrame];
AnimatedTexturePtr me = std::static_pointer_cast<AnimatedTexture>(shared_from_this());
if(me.use_count() > 1)
g_dispatcher.scheduleTask(std::bind(&AnimatedTexture::processAnimation, me), m_framesDelay[m_currentFrame]);
g_dispatcher.addEvent(std::bind(&AnimatedTexture::processAnimation, me), m_framesDelay[m_currentFrame]);
}

View File

@@ -1,32 +1,7 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef ANIMATEDTEXTURE_H
#define ANIMATEDTEXTURE_H
#include <global.h>
#include <graphics/graphics.h>
#include "texture.h"
class AnimatedTexture : public Texture
{
@@ -48,4 +23,4 @@ private:
typedef std::shared_ptr<AnimatedTexture> AnimatedTexturePtr;
typedef std::weak_ptr<AnimatedTexture> AnimatedTextureWeakPtr;
#endif // ANIMATEDTEXTURE_H
#endif

View File

@@ -1,73 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef BORDEREDIMAGE_H
#define BORDEREDIMAGE_H
#include <global.h>
#include <graphics/image.h>
#include <graphics/texture.h>
#include <otml/otmlnode.h>
class BorderedImage;
typedef std::shared_ptr<BorderedImage> BorderedImagePtr;
class BorderedImage : public Image
{
public:
BorderedImage(TexturePtr texture,
const Rect& left,
const Rect& right,
const Rect& top,
const Rect& bottom,
const Rect& topLeft,
const Rect& topRight,
const Rect& bottomLeft,
const Rect& bottomRight,
const Rect& center);
void draw(const Rect& screenCoords);
Size getDefaultSize() const { return m_defaultSize; }
static BorderedImagePtr loadFromOTMLNode(OTMLNode *node, TexturePtr defaultTexture = TexturePtr());
private:
Rect m_leftBorderTexCoords;
Rect m_rightBorderTexCoords;
Rect m_topBorderTexCoords;
Rect m_bottomBorderTexCoords;
Rect m_topLeftCornerTexCoords;
Rect m_topRightCornerTexCoords;
Rect m_bottomLeftCornerTexCoords;
Rect m_bottomRightCornerTexCoords;
Rect m_centerTexCoords;
Size m_bordersSize;
Size m_defaultSize;
};
#endif // BORDEREDIMAGE_H

View File

@@ -1,33 +1,11 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "borderimage.h"
#include "graphics.h"
#include "texture.h"
#include "texturemanager.h"
#include <otml/otml.h>
#include <global.h>
#include <graphics/borderedimage.h>
#include <graphics/graphics.h>
#include <graphics/textures.h>
BorderedImage::BorderedImage(TexturePtr texture,
BorderImage::BorderImage(TexturePtr texture,
const Rect& left,
const Rect& right,
const Rect& top,
@@ -59,7 +37,7 @@ BorderedImage::BorderedImage(TexturePtr texture,
center.height());
}
BorderedImagePtr BorderedImage::loadFromOTMLNode(OTMLNode* node, TexturePtr defaultTexture)
BorderImagePtr BorderImage::loadFromOTML(const OTMLNodePtr& borderImageNode)
{
Rect leftBorder;
Rect rightBorder;
@@ -75,26 +53,27 @@ BorderedImagePtr BorderedImage::loadFromOTMLNode(OTMLNode* node, TexturePtr defa
Size size;
Point offset;
TexturePtr texture;
std::string textureSource = node->readAt("source", std::string());
if(!textureSource.empty())
texture = g_textures.get(textureSource);
else
texture = defaultTexture;
// load texture
std::string source = borderImageNode->at("source")->value();
TexturePtr texture = g_textures.getTexture(source);
if(!texture)
return BorderedImagePtr();
throw OTMLException(borderImageNode, "could not load border-image texture");
// load basic border confs
size = texture->getSize();
size = node->readAt("size", size);
offset = node->readAt("offset", offset);
size = borderImageNode->readAt("size", size);
offset = borderImageNode->readAt("offset", offset);
border = borderImageNode->readAt("border", 0);
subRect = Rect(offset, size);
border = node->readAt("border", 0);
// load border margins
top = bottom = left = right = border;
top = node->readAt("top", top);
bottom = node->readAt("bottom", bottom);
left = node->readAt("left", left);
right = node->readAt("right", right);
top = borderImageNode->readAt("border.top", top);
bottom = borderImageNode->readAt("border.bottom", bottom);
left = borderImageNode->readAt("border.left", left);
right = borderImageNode->readAt("border.right", right);
// calculates border coords
leftBorder = Rect(subRect.left(), subRect.top() + top, left, subRect.height() - top - bottom);
rightBorder = Rect(subRect.right() - right + 1, subRect.top() + top, right, subRect.height() - top - bottom);
topBorder = Rect(subRect.left() + left, subRect.top(), subRect.width() - right - left, top);
@@ -104,17 +83,21 @@ BorderedImagePtr BorderedImage::loadFromOTMLNode(OTMLNode* node, TexturePtr defa
bottomLeftCorner = Rect(subRect.left(), subRect.bottom() - bottom, left, bottom);
bottomRightCorner = Rect(subRect.right() - right + 1, subRect.bottom() - bottom + 1, right, bottom);
center = Rect(subRect.left() + left, subRect.top() + top, subRect.width() - right - left, subRect.height() - top - bottom);
leftBorder = node->readAt("left border", leftBorder);
rightBorder = node->readAt("right border", rightBorder);
topBorder = node->readAt("top border", topBorder);
bottomBorder = node->readAt("bottom border", bottomBorder);
topLeftCorner = node->readAt("top left corner", topLeftCorner);
topRightCorner = node->readAt("top right corner", topRightCorner);
bottomLeftCorner = node->readAt("bottom left corner", bottomLeftCorner);
bottomRightCorner = node->readAt("bottom right corner", bottomRightCorner);
center = node->readAt("center", center);
return BorderedImagePtr(new BorderedImage(texture,
// load individual border conf if supplied
/*
leftBorder = borderImageNode->readAt("left border", leftBorder);
rightBorder = borderImageNode->readAt("right border", rightBorder);
topBorder = borderImageNode->readAt("top border", topBorder);
bottomBorder = borderImageNode->readAt("bottom border", bottomBorder);
topLeftCorner = borderImageNode->readAt("top left corner", topLeftCorner);
topRightCorner = borderImageNode->readAt("top right corner", topRightCorner);
bottomLeftCorner = borderImageNode->readAt("bottom left corner", bottomLeftCorner);
bottomRightCorner = borderImageNode->readAt("bottom right corner", bottomRightCorner);
center = borderImageNode->readAt("center", center);
*/
return BorderImagePtr(new BorderImage(texture,
leftBorder,
rightBorder,
topBorder,
@@ -126,8 +109,10 @@ BorderedImagePtr BorderedImage::loadFromOTMLNode(OTMLNode* node, TexturePtr defa
center));
}
void BorderedImage::draw(const Rect& screenCoords)
void BorderImage::draw(const Rect& screenCoords)
{
//TODO: borderimage drawing could be optimized by caching the render into a texture
Rect rectCoords;
Size centerSize = screenCoords.size() - m_bordersSize;
@@ -190,5 +175,4 @@ void BorderedImage::draw(const Rect& screenCoords)
screenCoords.top() + m_topRightCornerTexCoords.height() + centerSize.height(),
m_bottomRightCornerTexCoords.size());
g_graphics.drawTexturedRect(rectCoords, m_texture, m_bottomRightCornerTexCoords);
}
}

View File

@@ -0,0 +1,43 @@
#ifndef BORDERIMAGE_H
#define BORDERIMAGE_H
#include "image.h"
class BorderImage : public Image
{
public:
BorderImage(TexturePtr texture,
const Rect& left,
const Rect& right,
const Rect& top,
const Rect& bottom,
const Rect& topLeft,
const Rect& topRight,
const Rect& bottomLeft,
const Rect& bottomRight,
const Rect& center);
static BorderImagePtr loadFromOTML(const OTMLNodePtr& borderImageNode);
void draw(const Rect& screenCoords);
Size getDefaultSize() const { return m_defaultSize; }
private:
Rect m_leftBorderTexCoords;
Rect m_rightBorderTexCoords;
Rect m_topBorderTexCoords;
Rect m_bottomBorderTexCoords;
Rect m_topLeftCornerTexCoords;
Rect m_topRightCornerTexCoords;
Rect m_bottomLeftCornerTexCoords;
Rect m_bottomRightCornerTexCoords;
Rect m_centerTexCoords;
Size m_bordersSize;
Size m_defaultSize;
};
#endif

View File

@@ -1,104 +1,40 @@
#include <global.h>
#include <core/resources.h>
#include <graphics/font.h>
#include <graphics/textures.h>
#include <graphics/graphics.h>
#include "font.h"
#include "texturemanager.h"
#include "graphics.h"
#include <otml/otml.h>
void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize)
void Font::load(const OTMLNodePtr& fontNode)
{
std::string textureName = fontNode->readAt<std::string>("texture");
Size glyphSize = fontNode->readAt<Size>("glyph size");
m_glyphHeight = fontNode->readAt<int>("height");
m_topMargin = fontNode->readAt("top margin", 0);
m_firstGlyph = fontNode->readAt("first glyph", 32);
m_glyphSpacing = fontNode->readAt("spacing", Size(0,0));
// load font texture
m_texture = g_textures.getTexture(textureName);
if(!m_texture)
throw OTMLException(fontNode, "failed to load texture for font");
// auto calculate widths
calculateGlyphsWidthsAutomatically(glyphSize);
// read custom widths
if(OTMLNodePtr node = fontNode->get("glyph widths")) {
for(const OTMLNodePtr& child : node->childNodes())
m_glyphsSize[aux::safe_cast<int>(child->tag())].setWidth(child->read<int>());
}
// calculate glyphs texture coords
int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width();
uchar *texturePixels = m_texture->getPixels();
// small AI to auto calculate pixels widths
for(int glyph = m_firstGlyph; glyph< 256; ++glyph) {
Rect glyphCoords(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
glyphSize.width(),
m_glyphHeight);
int width = glyphSize.width();
int lastColumnFilledPixels = 0;
for(int x = glyphCoords.left() + 1; x <= glyphCoords.right(); ++x) {
int columnFilledPixels = 0;
// check if all vertical pixels are alpha
for(int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) {
if(texturePixels[(y * m_texture->getSize().width() * 4) + (x*4) + 3] != 0)
columnFilledPixels++;
}
// if all pixels were alpha we found the width
if(columnFilledPixels == 0) {
width = x - glyphCoords.left();
width += m_glyphSpacing.width();
if(m_glyphHeight >= 16 && lastColumnFilledPixels >= m_glyphHeight/3)
width += 1;
break;
}
lastColumnFilledPixels = columnFilledPixels;
}
// store glyph size
m_glyphsSize[glyph].setSize(width, m_glyphHeight);
for(int glyph = m_firstGlyph; glyph < 256; ++glyph) {
m_glyphsTextureCoords[glyph].setRect(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
m_glyphsSize[glyph].width(),
m_glyphHeight);
}
delete[] texturePixels;
}
bool Font::load(const std::string& file)
{
std::stringstream fin;
if(!g_resources.loadFile(file, fin)) {
logError("ERROR: Coult not load font file '",file,"'");
return false;
}
std::string textureName;
Size glyphSize;
try {
OTMLParser parser(fin, file);
OTMLNode* doc = parser.getDocument();
// required values
textureName = doc->valueAt("image");
glyphSize = doc->readAt("image-glyph-size", Size(16, 16));
m_glyphHeight = doc->readAt("glyph-height", 11);
m_firstGlyph = doc->readAt("first-glyph", 32);
m_topMargin = doc->readAt("top-margin", 0);
m_glyphSpacing = doc->readAt("glyph-spacing", Size(0,0));
// load texture
m_texture = g_textures.get(textureName);
if(!m_texture) {
logError("ERROR: Failed to load image for font file '",file,"'");
return false;
}
// auto calculate widths
calculateGlyphsWidthsAutomatically(glyphSize);
// read custom widths
if(doc->hasChild("glyph-widths")) {
std::map<int, int> glyphWidths;
doc->readAt("glyph-widths", &glyphWidths);
foreach(const auto& pair, glyphWidths)
m_glyphsSize[pair.first].setWidth(pair.second);
}
// calculate glyphs texture coords
int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width();
for(int glyph = m_firstGlyph; glyph < 256; ++glyph) {
m_glyphsTextureCoords[glyph].setRect(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
m_glyphsSize[glyph].width(),
m_glyphHeight);
}
} catch(OTMLException e) {
logError("ERROR: Malformed font file \"", file.c_str(), "\":\n ", e.what());
return false;
}
return true;
}
void Font::renderText(const std::string& text,
@@ -112,9 +48,9 @@ void Font::renderText(const std::string& text,
void Font::renderText(const std::string& text,
const Rect& screenCoords,
AlignmentFlag align,
const Color& color)
const Rect& screenCoords,
AlignmentFlag align,
const Color& color)
{
// prevent glitches from invalid rects
if(!screenCoords.isValid())
@@ -190,7 +126,9 @@ void Font::renderText(const std::string& text,
}
}
const std::vector<Point>& Font::calculateGlyphsPositions(const std::string& text, AlignmentFlag align, Size *textBoxSize) const
const std::vector<Point>& Font::calculateGlyphsPositions(const std::string& text,
AlignmentFlag align,
Size *textBoxSize) const
{
// for performance reasons we use statics vectors that are allocated on demand
static std::vector<Point> glyphsPositions(1);
@@ -277,3 +215,41 @@ Size Font::calculateTextRectSize(const std::string& text)
return size;
}
void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize)
{
int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width();
uchar *texturePixels = m_texture->getPixels();
// small AI to auto calculate pixels widths
for(int glyph = m_firstGlyph; glyph< 256; ++glyph) {
Rect glyphCoords(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
glyphSize.width(),
m_glyphHeight);
int width = glyphSize.width();
int lastColumnFilledPixels = 0;
for(int x = glyphCoords.left() + 1; x <= glyphCoords.right(); ++x) {
int columnFilledPixels = 0;
// check if all vertical pixels are alpha
for(int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) {
if(texturePixels[(y * m_texture->getSize().width() * 4) + (x*4) + 3] != 0)
columnFilledPixels++;
}
// if all pixels were alpha we found the width
if(columnFilledPixels == 0) {
width = x - glyphCoords.left();
width += m_glyphSpacing.width();
if(m_glyphHeight >= 16 && lastColumnFilledPixels >= m_glyphHeight/3)
width += 1;
break;
}
lastColumnFilledPixels = columnFilledPixels;
}
// store glyph size
m_glyphsSize[glyph].setSize(width, m_glyphHeight);
}
delete[] texturePixels;
}

View File

@@ -1,68 +1,46 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FONT_H
#define FONT_H
#include <global.h>
#include <graphics/graphics.h>
#include "graphicsdeclarations.h"
#include <otml/otmldeclarations.h>
class Font
{
public:
Font(const std::string& name) :
m_name(name) { }
Font(const std::string& name) : m_name(name) { }
/// Load font from file
bool load(const std::string &file);
/// Load font from otml node
void load(const OTMLNodePtr& fontNode);
/// Simple text render starting at startPos
void renderText(const std::string& text,
const Point& startPos,
const Color& color = Color::white);
/// Advanced text render
/// Advanced text render delimited by a screen region and alignment
void renderText(const std::string& text,
const Rect& screenCoords,
AlignmentFlag align = AlignTopLeft,
const Color& color = Color::white);
/// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted
const std::vector<Point>& calculateGlyphsPositions(const std::string& text, AlignmentFlag align = AlignTopLeft, Size *textBoxSize = NULL) const;
const std::vector<Point>& calculateGlyphsPositions(const std::string& text,
AlignmentFlag align = AlignTopLeft,
Size* textBoxSize = NULL) const;
/// Simulate render and calculate text size
Size calculateTextRectSize(const std::string& text);
std::string getName() const { return m_name; }
int getGlyphHeight() const { return m_glyphHeight; }
const Rect *getGlyphsTextureCoords() const { return m_glyphsTextureCoords; }
const Size *getGlyphsSize() const { return m_glyphsSize; }
const Rect* getGlyphsTextureCoords() const { return m_glyphsTextureCoords; }
const Size* getGlyphsSize() const { return m_glyphsSize; }
const TexturePtr& getTexture() const { return m_texture; }
int getTopMargin() const { return m_topMargin; }
Size getGlyphSpacing() const { return m_glyphSpacing; }
private:
/// Calculates each font character by inspecting font bitmap
void calculateGlyphsWidthsAutomatically(const Size& glyphSize);
std::string m_name;
@@ -75,6 +53,6 @@ private:
Size m_glyphsSize[256];
};
typedef std::shared_ptr<Font> FontPtr;
#endif // FONT_H
#endif

View File

@@ -0,0 +1,69 @@
#include "fontmanager.h"
#include <core/resourcemanager.h>
#include <otml/otml.h>
FontManager g_fonts;
void FontManager::releaseFonts()
{
m_defaultFont.reset();
m_fonts.clear();
}
bool FontManager::importFont(std::string fontFile)
{
try {
if(!boost::ends_with(fontFile, ".otfont"))
fontFile += ".otfont";
OTMLDocumentPtr doc = OTMLDocument::parse(fontFile);
OTMLNodePtr fontNode = doc->at("Font");
std::string name = fontNode->readAt<std::string>("name");
if(fontExists(name))
throw OTMLException(fontNode, "a font with the same name is already imported, did you duplicate font names?");
FontPtr font(new Font(name));
font->load(fontNode);
m_fonts.push_back(font);
// set as default if needed
if(!m_defaultFont)
m_defaultFont = font;
return true;
} catch(std::exception& e) {
logError("ERROR: could not load font '", fontFile, "': ", e.what());
return false;
}
}
bool FontManager::fontExists(const std::string& fontName)
{
for(const FontPtr& font : m_fonts) {
if(font->getName() == fontName)
return true;
}
return false;
}
FontPtr FontManager::getFont(const std::string& fontName)
{
// find font by name
for(const FontPtr& font : m_fonts) {
if(font->getName() == fontName)
return font;
}
// when not found, fallback to default font
return getDefaultFont();
}
FontPtr FontManager::getDefaultFont()
{
// default font should always exists, otherwise the app may crash
if(!m_defaultFont)
throw std::runtime_error("no default font to display, cannot continue to run");
return m_defaultFont;
}

View File

@@ -0,0 +1,27 @@
#ifndef FONTMANAGER_H
#define FONTMANAGER_H
#include "font.h"
class FontManager
{
public:
// Release fonts references, thus making possible to destruct them
void releaseFonts();
bool importFont(std::string fontFile);
bool fontExists(const std::string& fontName);
FontPtr getFont(const std::string& fontName);
FontPtr getDefaultFont();
void setDefaultFont(const std::string& fontName) { m_defaultFont = getFont(fontName); }
private:
std::vector<FontPtr> m_fonts;
FontPtr m_defaultFont;
};
extern FontManager g_fonts;
#endif

View File

@@ -1,38 +0,0 @@
#include <global.h>
#include <core/resources.h>
#include <graphics/fonts.h>
Fonts g_fonts;
void Fonts::init()
{
g_resources.pushCurrentPath("fonts");
// load all fonts
std::list<std::string> files = g_resources.listDirectoryFiles();
foreach(const std::string& file, files) {
if(boost::ends_with(file, ".otml")) {
std::string name = file;
boost::erase_first(name, ".otml");
FontPtr font(new Font(name));
if(font->load(file))
m_fonts.push_back(font);
}
}
g_resources.popCurrentPath();
}
FontPtr Fonts::get(const std::string& fontName)
{
// find font by name
foreach(const FontPtr& font, m_fonts) {
if(font->getName() == fontName)
return font;
}
logFatal("ERROR: Font '",fontName,"' not found");
return FontPtr();
}

View File

@@ -1,51 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FONTS_H
#define FONTS_H
#include <global.h>
#include <graphics/font.h>
class Fonts
{
public:
Fonts() { }
/// Initialize all fonts
void init();
/// Terminate all fonts
void terminate() { }
/// Get a font by name
FontPtr get(const std::string& fontName);
private:
std::vector<FontPtr> m_fonts;
};
extern Fonts g_fonts;
#endif // FONTS_H

View File

@@ -1,30 +1,9 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include "framebuffer.h"
#include "graphics.h"
#include <core/platform.h>
#include <graphics/framebuffer.h>
#include <graphics/graphics.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
PFNGLGENFRAMEBUFFERSPROC oglGenFramebuffers = 0;
PFNGLBINDFRAMEBUFFERPROC oglBindFramebuffer = 0;
@@ -55,11 +34,11 @@ FrameBuffer::FrameBuffer(int width, int height)
if(g_graphics.isExtensionSupported("GL_ARB_framebuffer_object")) {
m_fallbackOldImp = false;
if(!oglGenFramebuffers) {
oglGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)Platform::getExtensionProcAddress("glGenFramebuffers");
oglBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)Platform::getExtensionProcAddress("glBindFramebuffer");
oglFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)Platform::getExtensionProcAddress("glFramebufferTexture2D");
oglDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)Platform::getExtensionProcAddress("glDeleteFramebuffers");
oglCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)Platform::getExtensionProcAddress("glCheckFramebufferStatus");
oglGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)g_platform.getExtensionProcAddress("glGenFramebuffers");
oglBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)g_platform.getExtensionProcAddress("glBindFramebuffer");
oglFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)g_platform.getExtensionProcAddress("glFramebufferTexture2D");
oglDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)g_platform.getExtensionProcAddress("glDeleteFramebuffers");
oglCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)g_platform.getExtensionProcAddress("glCheckFramebufferStatus");
}
// generate FBO

View File

@@ -1,34 +1,7 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FRAMEBUFFER_H
#define FRAMEBUFFER_H
#include <global.h>
class FrameBuffer;
typedef std::shared_ptr<FrameBuffer> FrameBufferPtr;
#include "graphicsdeclarations.h"
class FrameBuffer
{
@@ -53,4 +26,4 @@ private:
int m_height;
};
#endif // FRAMEBUFFER_H
#endif

View File

@@ -1,38 +1,22 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include <graphics/graphics.h>
#include <graphics/texture.h>
#include "fontmanager.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
Graphics g_graphics;
void Graphics::init()
{
m_drawMode = DRAW_NONE;
// setup opengl
glEnable(GL_ALPHA_TEST); // enable alpha by default
glAlphaFunc(GL_GREATER, 0.0f); // default alpha mode
glDisable(GL_DEPTH_TEST); // we are rendering 2D only, we don't need it
glAlphaFunc(GL_GREATER, 0.0f); // default alpha func
glDisable(GL_DEPTH_TEST); // we are rendering 2D only, we don't need depth buffer
//glEnable(GL_TEXTURE_2D); // enable textures by default
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND);
@@ -45,7 +29,7 @@ void Graphics::init()
void Graphics::terminate()
{
m_bindedTexture.reset();
g_fonts.releaseFonts();
}
bool Graphics::isExtensionSupported(const char *extension)
@@ -114,11 +98,18 @@ void Graphics::beginRender()
{
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
// bind white color by default
glColor4ubv(Color::white.rgbaPtr());
m_boundColor = Color::white;
}
void Graphics::endRender()
{
disableDrawing();
// clear any bound texture
m_boundTexture.reset();
}
void Graphics::disableDrawing()
@@ -127,12 +118,15 @@ void Graphics::disableDrawing()
glEnd();
m_drawMode = DRAW_NONE;
m_bindedTexture.reset();
m_boundTexture.reset();
glColor4ubv(Color::white.rgbaPtr());
}
}
void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color)
void Graphics::drawTexturedRect(const Rect& screenCoords,
const TexturePtr& texture,
const Rect& textureCoords,
const Color& color)
{
if(screenCoords.isEmpty() || textureCoords.isEmpty())
return;
@@ -163,7 +157,10 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& text
glTexCoord2f(textureRight, textureTop); glVertex2i(right, top);
}
void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color)
void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords,
const TexturePtr& texture,
const Rect& textureCoords,
const Color& color)
{
if(screenCoords.isEmpty() || textureCoords.isEmpty())
return;
@@ -177,11 +174,13 @@ void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TextureP
// partialCoords to screenCoords bottomRight
if(partialCoords.bottom() > virtualScreenCoords.bottom()) {
partialTextureCoords.setBottom(partialTextureCoords.bottom() + (virtualScreenCoords.bottom() - partialCoords.bottom()));
partialTextureCoords.setBottom(partialTextureCoords.bottom() +
(virtualScreenCoords.bottom() - partialCoords.bottom()));
partialCoords.setBottom(virtualScreenCoords.bottom());
}
if(partialCoords.right() > virtualScreenCoords.right()) {
partialTextureCoords.setRight(partialTextureCoords.right() + (virtualScreenCoords.right() - partialCoords.right()));
partialTextureCoords.setRight(partialTextureCoords.right() +
(virtualScreenCoords.right() - partialCoords.right()));
partialCoords.setRight(virtualScreenCoords.right());
}
@@ -191,7 +190,8 @@ void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TextureP
}
}
void Graphics::drawFilledRect(const Rect& screenCoords, const Color& color)
void Graphics::drawFilledRect(const Rect& screenCoords,
const Color& color)
{
if(screenCoords.isEmpty())
return;
@@ -210,7 +210,9 @@ void Graphics::drawFilledRect(const Rect& screenCoords, const Color& color)
}
void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, int innerLineWidth)
void Graphics::drawBoundingRect(const Rect& screenCoords,
const Color& color,
int innerLineWidth)
{
if(2 * innerLineWidth > screenCoords.height() || screenCoords.isEmpty())
return;
@@ -251,13 +253,13 @@ void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, in
void Graphics::bindColor(const Color& color)
{
// switch drawing to colored quads
if(m_drawMode != DRAW_COLOR_QUADS || m_bindedColor != color) {
if(m_drawMode != DRAW_COLOR_QUADS || m_boundColor != color) {
if(m_drawMode != DRAW_NONE)
glEnd();
glDisable(GL_TEXTURE_2D);
if(m_bindedColor != color) {
if(m_boundColor != color) {
glColor4ubv(color.rgbaPtr());
m_bindedColor = color;
m_boundColor = color;
}
glBegin(GL_QUADS);
m_drawMode = DRAW_COLOR_QUADS;
@@ -267,17 +269,17 @@ void Graphics::bindColor(const Color& color)
void Graphics::bindTexture(const TexturePtr& texture, const Color& color)
{
// switch drawing to textured quads
if(m_drawMode != DRAW_TEXTURE_QUADS || m_bindedTexture != texture || m_bindedColor != color) {
if(m_drawMode != DRAW_TEXTURE_QUADS || m_boundTexture != texture || m_boundColor != color) {
if(m_drawMode != DRAW_NONE)
glEnd();
glEnable(GL_TEXTURE_2D);
if(m_bindedTexture != texture) {
if(m_boundTexture != texture) {
glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
m_bindedTexture = texture;
m_boundTexture = texture;
}
if(m_bindedColor != color) {
if(m_boundColor != color) {
glColor4ubv(color.rgbaPtr());
m_bindedColor = color;
m_boundColor = color;
}
glBegin(GL_QUADS);
m_drawMode = DRAW_TEXTURE_QUADS;

View File

@@ -1,36 +1,7 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <global.h>
#include <graphics/textures.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include "graphicsdeclarations.h"
class Graphics
{
@@ -44,9 +15,7 @@ class Graphics
};
public:
Graphics() : m_drawMode(DRAW_NONE) { }
/// Initialize graphics
/// Initialize default OpenGL states
void init();
/// Termiante graphics
@@ -55,7 +24,7 @@ public:
/// Check if a GL extension is supported
bool isExtensionSupported(const char *extension);
/// Called after every window resize
/// Resizes OpenGL viewport
void resize(const Size& size);
/// Restore original viewport
@@ -66,27 +35,38 @@ public:
/// Called after every render
void endRender();
void disableDrawing();
// drawing API
void drawTexturedRect(const Rect& screenCoords,
const TexturePtr& texture,
const Rect& textureCoords = Rect(),
const Color& color = Color::white);
void drawRepeatedTexturedRect(const Rect& screenCoords,
const TexturePtr& texture,
const Rect& textureCoords,
const Color& color = Color::white);
void drawFilledRect(const Rect& screenCoords,
const Color& color);
void drawBoundingRect(const Rect& screenCoords,
const Color& color = Color::green,
int innerLineWidth = 1);
const Size& getScreenSize() const { return m_screenSize; }
void disableDrawing();
void enableDrawing();
void drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords = Rect(), const Color& color = Color::white);
void drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color = Color::white);
void drawFilledRect(const Rect& screenCoords, const Color& color);
void drawBoundingRect(const Rect& screenCoords, const Color& color = Color::green, int innerLineWidth = 1);
private:
void bindTexture(const TexturePtr& texture, const Color& color = Color::white);
void bindColor(const Color& color);
TexturePtr m_bindedTexture;
Color m_bindedColor;
TexturePtr m_boundTexture;
Color m_boundColor;
Size m_screenSize;
DrawMode m_drawMode;
};
extern Graphics g_graphics;
#endif // GRAPHICS_H
#endif

View File

@@ -0,0 +1,22 @@
#ifndef GRAPHICSDECLARATIONS_H
#define GRAPHICSDECLARATIONS_H
#include <global.h>
class Texture;
class Font;
class Image;
class BorderImage;
class TextArea;
class FrameBuffer;
typedef std::weak_ptr<Texture> TextureWeakPtr;
typedef std::shared_ptr<Texture> TexturePtr;
typedef std::shared_ptr<Font> FontPtr;
typedef std::shared_ptr<Image> ImagePtr;
typedef std::shared_ptr<BorderImage> BorderImagePtr;
typedef std::shared_ptr<TextArea> TextAreaPtr;
typedef std::shared_ptr<FrameBuffer> FrameBufferPtr;
#endif

View File

@@ -1,48 +1,37 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "image.h"
#include "texture.h"
#include "graphics.h"
#include "texturemanager.h"
#include <otml/otml.h>
#include <global.h>
#include <graphics/image.h>
#include <graphics/graphics.h>
#include <graphics/textures.h>
Image::Image(TexturePtr texture)
: m_texture(texture)
Image::Image(TexturePtr texture, Rect textureCoords)
{
m_textureCoords = Rect(0, 0, m_texture->getSize());
m_texture = texture;
if(!textureCoords.isValid())
m_textureCoords = Rect(0, 0, m_texture->getSize());
else
m_textureCoords = textureCoords;
}
Image::Image(const std::string& texture)
ImagePtr Image::loadFromOTML(const OTMLNodePtr& imageNode)
{
m_texture = g_textures.get(texture);
m_textureCoords = Rect(0, 0, m_texture->getSize());
}
// load configs from otml node
std::string source = imageNode->hasValue() ? imageNode->read<std::string>() : imageNode->readAt<std::string>("source");
bool smooth = imageNode->readAt("smooth", false);
Rect textureCoords = imageNode->readAt("coords", Rect());
Image::Image(const std::string& texture, Rect textureCoords) :
m_textureCoords(textureCoords)
{
m_texture = g_textures.get(texture);
// load texture
TexturePtr texture = g_textures.getTexture(source);
if(!texture)
throw OTMLException(imageNode, "could not load image texture");
// enable texture bilinear filter
if(smooth)
texture->enableBilinearFilter();
// create image
return ImagePtr(new Image(texture, textureCoords));
}
void Image::draw(const Rect& screenCoords)
@@ -50,5 +39,3 @@ void Image::draw(const Rect& screenCoords)
if(m_texture)
g_graphics.drawTexturedRect(screenCoords, m_texture, m_textureCoords);
}

View File

@@ -1,42 +1,16 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef IMAGE_H
#define IMAGE_H
#include <global.h>
#include <graphics/texture.h>
#include "graphicsdeclarations.h"
#include <otml/otmldeclarations.h>
class Image
{
public:
Image(TexturePtr texture);
Image(TexturePtr texture, Rect textureCoords) : m_texture(texture), m_textureCoords(textureCoords) { }
Image(const std::string& texture);
Image(const std::string& texture, Rect textureCoords);
Image(TexturePtr texture, Rect textureCoords = Rect());
static ImagePtr loadFromOTML(const OTMLNodePtr& imageNode);
/// Draw image on screen
virtual void draw(const Rect& screenCoords);
protected:
@@ -44,6 +18,4 @@ protected:
Rect m_textureCoords;
};
typedef std::shared_ptr<Image> ImagePtr;
#endif // IMAGE_H
#endif

View File

@@ -1,31 +1,8 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "textarea.h"
#include "font.h"
#include "graphics.h"
#include <global.h>
#include <core/engine.h>
#include <graphics/textarea.h>
#include <graphics/graphics.h>
#include <core/platform.h>
TextArea::TextArea() :
m_align(AlignLeftCenter),
@@ -55,6 +32,8 @@ TextArea::TextArea(FontPtr font,
void TextArea::draw()
{
//TODO: text rendering could be much optimized by using vertex buffer or caching the render into a texture
int textLength = m_text.length();
const TexturePtr& texture = m_font->getTexture();
for(int i=0;i<textLength;++i) {
@@ -65,7 +44,7 @@ void TextArea::draw()
if(m_cursorVisible && m_cursorPos >= 0) {
assert(m_cursorPos <= textLength);
const int delay = 500;
int ticks = g_engine.getCurrentFrameTicks();
int ticks = g_platform.getTicks();
// draw every 500ms
if(ticks - m_cursorTicks <= delay) {
Rect cursorRect;
@@ -76,7 +55,7 @@ void TextArea::draw()
cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight());
g_graphics.drawFilledRect(cursorRect, m_color);
} else if(ticks - m_cursorTicks >= 2*delay) {
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = ticks;
}
}
}
@@ -113,12 +92,12 @@ void TextArea::recalculate()
} else if(m_cursorPos > m_startRenderPos || // cursor is after the previuos first rendered glyph
(m_cursorPos == m_startRenderPos && textLength == m_cursorPos)) // cursor is at the previuos rendered element, and is the last text element
{
Rect virtualRect(m_startInternalPos, m_screenCoords.size()); // previus rendered virtual rect
Rect virtualRect(m_startInternalPos, m_screenCoords.size()); // previous rendered virtual rect
int pos = m_cursorPos - 1; // element before cursor
glyph = (uchar)m_text[pos]; // glyph of the element before cursor
Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
// if the cursor is not on the previus rendered virtual rect we need to update it
// if the cursor is not on the previous rendered virtual rect we need to update it
if(!virtualRect.contains(glyphRect.topLeft()) || !virtualRect.contains(glyphRect.bottomRight())) {
// calculate where is the first glyph visible
Point startGlyphPos;
@@ -246,7 +225,7 @@ void TextArea::setText(const std::string& text)
m_text = text;
if(m_cursorPos >= 0) {
m_cursorPos = 0;
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
}
recalculate();
}
@@ -285,7 +264,7 @@ void TextArea::enableCursor(bool enable)
{
if(enable) {
m_cursorPos = 0;
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
} else
m_cursorPos = -1;
recalculate();
@@ -298,7 +277,7 @@ void TextArea::appendCharacter(char c)
tmp = c;
m_text.insert(m_cursorPos, tmp);
m_cursorPos++;
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
recalculate();
}
}
@@ -310,7 +289,7 @@ void TextArea::removeCharacter(bool right)
m_text.erase(m_text.begin() + m_cursorPos);
else if((uint)m_cursorPos == m_text.length()) {
m_text.erase(m_text.begin() + (--m_cursorPos));
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
}
recalculate();
}
@@ -321,12 +300,12 @@ void TextArea::moveCursor(bool right)
if(right) {
if((uint)m_cursorPos+1 <= m_text.length()) {
m_cursorPos++;
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
}
} else {
if(m_cursorPos-1 >= 0) {
m_cursorPos--;
m_cursorTicks = g_engine.getCurrentFrameTicks();
m_cursorTicks = g_platform.getTicks();
}
}
recalculate();

View File

@@ -1,32 +1,7 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TEXTAREA_H
#define TEXTAREA_H
#include <global.h>
#include "font.h"
#include "graphicsdeclarations.h"
class TextArea
{
@@ -77,4 +52,4 @@ private:
std::vector<Rect> m_glyphsTexCoords;
};
#endif // TEXTAREA_H
#endif

View File

@@ -1,34 +1,11 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include <graphics/texture.h>
#include "texture.h"
#include "graphics.h"
#include <GL/gl.h>
Texture::Texture(int width, int height, int channels, uchar *pixels)
{
// generate opengl texture
// generate opengl texture
m_textureId = internalLoadGLTexture(pixels, channels, width, height);
}
@@ -36,10 +13,16 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
{
g_graphics.disableDrawing();
GLint texSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
if(width > texSize || height > texSize) {
logError("loading texture with size ",width,"x",height," failed, the maximum size is ",texSize,"x",texSize);
// get smax texture size supported by the driver
static GLint maxTexSize = -1;
if(maxTexSize == -1)
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
// checks texture max size
if(width > maxTexSize || height > maxTexSize) {
logError("ERROR: loading texture with size ", width, "x", height, " failed, "
"the maximum size allowed by the graphics card is ", maxTexSize, "x", maxTexSize, ",",
"to prevent crashes the texture will be displayed as a blank texture");
return 0;
}
@@ -50,6 +33,7 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
m_size.setSize(width, height);
bool mustFree = false;
// old opengl drivers only accept power of two dimensions
if(!g_graphics.isExtensionSupported("GL_ARB_texture_non_power_of_two") && pixels) {
int glWidth = 1;
while(glWidth < width)
@@ -63,9 +47,9 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
uchar *tmp = new uchar[glHeight*glWidth*channels];
memset(tmp, 0, glHeight*glWidth*channels);
if(pixels)
for(int y=0;y<height;++y)
for(int x=0;x<width;++x)
for(int i=0;i<channels;++i)
for(int y=0; y<height; ++y)
for(int x=0; x<width; ++x)
for(int i=0; i<channels; ++i)
tmp[y*glWidth*channels+x*channels+i] = pixels[y*width*channels+x*channels+i];
pixels = tmp;
mustFree = true;
@@ -99,11 +83,10 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// nearest filtering
// nearest filtering (non smooth)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// free if needed
if(mustFree)
delete[] pixels;
@@ -112,30 +95,37 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
Texture::~Texture()
{
g_graphics.disableDrawing();
// free texture from gl memory
if(m_textureId)
glDeleteTextures(1, &m_textureId);
}
void Texture::enableBilinearFilter()
{
g_graphics.disableDrawing();
// enable smooth texture
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
uchar *Texture::getPixels()
uchar* Texture::getPixels()
{
g_graphics.disableDrawing();
// copy pixels from opengl memory
uchar *pixels = new uchar[m_glSize.area()*4];
uchar* pixels = new uchar[m_glSize.area()*4];
glBindTexture(GL_TEXTURE_2D, m_textureId);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// convert pixels to the real texture size
if(m_size != m_glSize)
for(int y=0;y<m_size.height();++y)
for(int x=0;x<m_size.width();++x)
for(int i=0;i<4;++i)
for(int y=0; y<m_size.height(); ++y)
for(int x=0; x<m_size.width(); ++x)
for(int i=0; i<4; ++i)
pixels[y*m_size.width()*4+x*4+i] = pixels[y*m_glSize.width()*4+x*4+i];
return pixels;
}

View File

@@ -1,37 +1,13 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TEXTURE_H
#define TEXTURE_H
#include <global.h>
#include "graphicsdeclarations.h"
class Texture : public std::enable_shared_from_this<Texture>
{
public:
/// Create a texture, width and height must be a multiple of 2
Texture(int width, int height, int channels, uchar *pixels = NULL);
Texture(int width, int height, int channels, uchar* pixels = NULL);
virtual ~Texture();
/// Enable texture bilinear filter (smooth scaled textures)
@@ -41,21 +17,18 @@ public:
virtual uint getTextureId() const { return m_textureId; }
/// Copy pixels from OpenGL texture
uchar *getPixels();
uchar* getPixels();
const Size& getSize() const { return m_size; }
const Size& getGlSize() const { return m_glSize; }
protected:
Texture() { }
uint internalLoadGLTexture(uchar *pixels, int channels, int w, int h);
uint internalLoadGLTexture(uchar* pixels, int channels, int w, int h);
uint m_textureId;
Size m_size;
Size m_glSize;
};
typedef std::shared_ptr<Texture> TexturePtr;
typedef std::weak_ptr<Texture> TextureWeakPtr;
#endif // TEXTURE_H
#endif

View File

@@ -1,46 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include <graphics/textureloader.h>
#include <graphics/texture.h>
#include <util/apngloader.h>
#include "animatedtexture.h"
TexturePtr TextureLoader::loadPNG(std::stringstream& file)
{
TexturePtr texture;
apng_data apng;
if(load_apng(file, &apng) == 0) {
if(apng.num_frames > 1) { // animated texture
uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
} else
texture = TexturePtr(new Texture(apng.width, apng.height, apng.bpp, apng.pdata));
free_apng(&apng);
}
return texture;
}

View File

@@ -1,38 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TEXTURELOADER_H
#define TEXTURELOADER_H
#include <global.h>
#include <graphics/texture.h>
class TextureLoader
{
public:
/// Load a png textures
static TexturePtr loadPNG(std::stringstream& file);
};
#endif // TEXTURELOADER_H

View File

@@ -0,0 +1,55 @@
#include "texturemanager.h"
#include "animatedtexture.h"
#include <core/resourcemanager.h>
#include <thirdparty/apngloader.h>
TextureManager g_textures;
TexturePtr TextureManager::getTexture(const std::string& textureFile)
{
TexturePtr texture;
// check if the texture is already loaded
auto it = m_textures.find(textureFile);
if(it != m_textures.end()) {
if(it->second.expired())
m_textures.erase(it);
else
texture = it->second.lock();
}
// texture not found, load it
if(!texture) {
try {
// currently only png textures are supported
if(!boost::ends_with(textureFile, ".png"))
throw std::logic_error("texture file format no supported");
// load texture file data
std::stringstream fin;
g_resources.loadFile(textureFile, fin);
texture = loadPNG(fin);
} catch(std::exception& e) {
logError("ERROR: unable to load texture '",textureFile,"': ", e.what());
}
}
return texture;
}
TexturePtr TextureManager::loadPNG(std::stringstream& file)
{
TexturePtr texture;
apng_data apng;
if(load_apng(file, &apng) == 0) {
if(apng.num_frames > 1) { // animated texture
uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
} else
texture = TexturePtr(new Texture(apng.width, apng.height, apng.bpp, apng.pdata));
free_apng(&apng);
}
return texture;
}

View File

@@ -0,0 +1,21 @@
#ifndef TEXTUREMANAGER_H
#define TEXTUREMANAGER_H
#include "texture.h"
class TextureManager
{
public:
/// Load a texture from file, if is already loaded, it will be retrieved from cache
TexturePtr getTexture(const std::string& textureFile);
/// Load a png textures
static TexturePtr loadPNG(std::stringstream& file);
private:
std::map<std::string, TextureWeakPtr> m_textures;
};
extern TextureManager g_textures;
#endif

View File

@@ -1,68 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include <core/resources.h>
#include <graphics/textures.h>
#include <graphics/textureloader.h>
#include <core/dispatcher.h>
Textures g_textures;
TexturePtr Textures::get(const std::string& textureFile)
{
TexturePtr texture;
// check if the texture is already loaded
auto it = m_textures.find(textureFile);
if(it != m_textures.end()) {
if(it->second.expired())
m_textures.erase(it);
else
texture = it->second.lock();
}
// texture not found, load it
if(!texture) {
// currently only png textures are supported
if(!boost::ends_with(textureFile, ".png")) {
logError("ERROR: Unable to load texture '",textureFile,"', file format no supported.");
return texture;
}
// load texture file data
std::stringstream fin;
if(!g_resources.loadFile(textureFile, fin)) {
logError("ERROR: Unable to load texture '",textureFile,"', file could not be read.");
return texture;
}
// load the texture
texture = TexturePtr(TextureLoader::loadPNG(fin));
if(!texture)
logError("ERROR: Unable to load texture '",textureFile,"'");
}
return texture;
}

View File

@@ -1,45 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TEXTURES_H
#define TEXTURES_H
#include <global.h>
#include <graphics/texture.h>
class Textures
{
public:
Textures() { }
/// Load a texture from file, if it was already loaded it will be retrieved from cache
TexturePtr get(const std::string& textureFile);
private:
std::map<std::string, TextureWeakPtr> m_textures;
};
extern Textures g_textures;
#endif // TEXTURES_H

View File

@@ -1,71 +1,79 @@
#ifndef LUABINDER_H
#define LUABINDER_H
#include "luavalue.h"
#include "luastate.h"
// this file is and must be included only from luainterface.h
#include "luainterface.h"
#include "luaexception.h"
/// This namespace contains some dirty metaprogamming with C++0x features
/// The purpose here is to create templates that can bind any function from C++
/// and expose in lua environment. This is done combining variadic templates,
/// lambdas, tuples and some type traits features from the new C++0x standard to create
/// templates that can detect functions arguments to generate a lambdas. These lambdas
/// pops arguments from lua stack, call the bound C++ function and then
/// pushes the result to lua.
namespace luabinder
{
// transform const T& -> T
/// Removes const references, transforming 'const T&' into 'T'
template<typename T>
struct remove_const_ref {
typedef typename std::remove_const<typename std::remove_reference<T>::type>::type type;
};
// pack an value into tuple recursively
/// Pack arguments from lua stack into a tuple recursively
template<int N>
struct pack_values_into_tuple {
template<typename Tuple>
static void call(Tuple& tuple, LuaState* lua) {
static void call(Tuple& tuple, LuaInterface* lua) {
typedef typename std::tuple_element<N-1, Tuple>::type ValueType;
std::get<N-1>(tuple) = safe_luavalue_cast<ValueType>(lua->popValue());
std::get<N-1>(tuple) = lua->polymorphicPop<ValueType>();
pack_values_into_tuple<N-1>::call(tuple, lua);
}
};
template<>
struct pack_values_into_tuple<0> {
template<typename Tuple>
static void call(Tuple &tuple, LuaState* lua) { }
static void call(Tuple &tuple, LuaInterface* lua) { }
};
// call function
/// C++ function caller that can push results to lua
template<typename Ret, typename F, typename... Args>
typename std::enable_if<!std::is_void<Ret>::value, int>::type
call_fun_and_push_result(const F& f, LuaState* lua, Args... args) {
call_fun_and_push_result(const F& f, LuaInterface* lua, Args... args) {
Ret ret = f(args...);
lua->pushValue(safe_to_luavalue(ret));
lua->polymorphicPush(ret);
return 1;
}
// call function with no return
/// C++ void function caller
template<typename Ret, typename F, typename... Args>
typename std::enable_if<std::is_void<Ret>::value, int>::type
call_fun_and_push_result(const F& f, LuaState* lua, Args... args) {
call_fun_and_push_result(const F& f, LuaInterface* lua, Args... args) {
f(args...);
return 0;
}
// expand function arguments for calling
/// Expand arguments from tuple for later calling the C++ function
template<int N, typename Ret>
struct expand_fun_arguments {
template<typename Tuple, typename F, typename... Args>
static int call(const Tuple& tuple, const F& f, LuaState* lua, Args... args) {
static int call(const Tuple& tuple, const F& f, LuaInterface* lua, Args... args) {
return expand_fun_arguments<N-1,Ret>::call(tuple, f, lua, std::cref(std::get<N-1>(tuple)), args...);
}
};
template<typename Ret>
struct expand_fun_arguments<0,Ret> {
template<typename Tuple, typename F, typename... Args>
static int call(const Tuple& tuple, const F& f, LuaState* lua, Args... args) {
static int call(const Tuple& tuple, const F& f, LuaInterface* lua, Args... args) {
return call_fun_and_push_result<Ret>(f, lua, args...);
}
};
// bind different types of functions
/// Bind different types of functions generating a lambda
template<typename Ret, typename F, typename Tuple>
LuaCppFunction bind_fun_specializer(const F& f) {
enum { N = std::tuple_size<Tuple>::value };
return [=](LuaState* lua) {
return [=](LuaInterface* lua) {
if(lua->stackSize() != N)
throw LuaBadNumberOfArgumentsException(N, lua->stackSize());
Tuple tuple;
@@ -74,7 +82,7 @@ namespace luabinder
};
}
// bind a std::function
/// Bind a std::function
template<typename Ret, typename... Args>
LuaCppFunction bind_fun(const std::function<Ret(Args...)>& f) {
typedef typename std::tuple<typename remove_const_ref<Args>::type...> Tuple;
@@ -83,13 +91,14 @@ namespace luabinder
Tuple>(f);
}
// bind a custumized function
/// Bind a customized functions
template<>
inline
LuaCppFunction bind_fun(const std::function<int(LuaState*)>& f) {
LuaCppFunction bind_fun(const std::function<int(LuaInterface*)>& f) {
return f;
}
// convert to std::function then bind
/// Convert to C++ functions pointers to std::function then bind
template<typename Ret, typename... Args>
LuaCppFunction bind_fun(Ret (*f)(Args...)) {
return bind_fun(std::function<Ret(Args...)>(f));
@@ -108,12 +117,17 @@ namespace luabinder
template<std::size_t __i>
struct tuple_element<__i,std::tuple<>> { typedef void type; };
// rebind functions already binded by std::bind that have placeholders
template<typename Enable, int N, typename ArgsTuple, typename HoldersTuple, typename... Args>
struct get_holded_tuple;
// some dirty stuff to extract arguments from std::bind holders
template<int N, typename ArgsTuple, typename HoldersTuple, typename... Args>
struct get_holded_tuple<typename std::enable_if<(N > 0 && std::is_placeholder<typename tuple_element<N-1, HoldersTuple>::type>::value > 0), void>::type, N, ArgsTuple, HoldersTuple, Args...> {
struct get_holded_tuple<
typename std::enable_if<
(N > 0 && std::is_placeholder<
typename tuple_element<N-1, HoldersTuple>::type
>::value > 0), void
>::type, N, ArgsTuple, HoldersTuple, Args...> {
typedef typename std::tuple_element<N-1, HoldersTuple>::type holder_type;
typedef typename tuple_element<std::is_placeholder<holder_type>::value-1, ArgsTuple>::type _arg_type;
typedef typename remove_const_ref<_arg_type>::type arg_type;
@@ -128,6 +142,7 @@ namespace luabinder
typedef typename std::tuple<Args...> type;
};
/// Rebind functions already bound by std::bind handling it's placeholders
template<typename Ret, typename... Args, typename... Holders>
LuaCppFunction bind_fun(const std::_Bind<Ret (*(Holders...))(Args...)>& f) {
typedef typename std::tuple<Args...> ArgsTuple;
@@ -138,6 +153,7 @@ namespace luabinder
Tuple>(f);
}
/// Rebind member functions already bound by std::bind handling it's placeholders
template<typename Obj, typename Ret, typename... Args, typename... Holders>
LuaCppFunction bind_fun(const std::_Bind<std::_Mem_fn<Ret (Obj::*)(Args...)>(Obj*, Holders...)>& f) {
typedef typename std::tuple<Args...> ArgsTuple;
@@ -148,17 +164,17 @@ namespace luabinder
Tuple>(f);
}
// custumized functions already binded by std::bind doesn't need to be bind again
/// Bind customized functions already bound by std::bind
template<typename Obj>
LuaCppFunction bind_fun(const std::_Bind<std::_Mem_fn<int (Obj::*)(LuaState*)>(Obj*, std::_Placeholder<1>)>& f) {
LuaCppFunction bind_fun(const std::_Bind<std::_Mem_fn<int (Obj::*)(LuaInterface*)>(Obj*, std::_Placeholder<1>)>& f) {
return f;
}
inline
LuaCppFunction bind_fun(const std::_Bind<int (*(std::_Placeholder<1>))(LuaState*)>& f) {
LuaCppFunction bind_fun(const std::_Bind<int (*(std::_Placeholder<1>))(LuaInterface*)>& f) {
return f;
}
// bind member function
/// Bind member functions
template<typename Ret, typename Obj, typename... Args>
LuaCppFunction bind_mem_fun(Ret (Obj::*f)(Args...)) {
auto mf = std::mem_fn(f);
@@ -176,16 +192,16 @@ namespace luabinder
Tuple>(mf);
}
// bind custumized member function
/// Bind customized member functions
template<typename Obj>
LuaCppFunction bind_mem_fun(int (Obj::*f)(LuaState*)) {
LuaCppFunction bind_mem_fun(int (Obj::*f)(LuaInterface*)) {
auto mf = std::mem_fn(f);
return [=](LuaState* lua) {
lua->insert(1);
auto obj = safe_luavalue_cast<Obj*>(lua->popValue());
return [=](LuaInterface* lua) {
auto obj = lua->castValue<Obj*>(1);
lua->remove(1);
return mf(obj, lua);
};
}
}
#endif // LUABINDER_H
#endif

View File

@@ -0,0 +1,12 @@
#ifndef LUADECLARATIONS_H
#define LUADECLARATIONS_H
#include <global.h>
class LuaInterface;
class LuaObject;
typedef std::function<int(LuaInterface*)> LuaCppFunction;
typedef std::unique_ptr<LuaCppFunction> LuaCppFunctionPtr;
typedef std::shared_ptr<LuaObject> LuaObjectPtr;
#endif

View File

@@ -0,0 +1,31 @@
#include "luaexception.h"
#include "luainterface.h"
LuaException::LuaException(const std::string& error, int traceLevel)
{
g_lua.clearStack(); // on every exception, clear lua stack
generateLuaErrorMessage(error, traceLevel);
}
void LuaException::generateLuaErrorMessage(const std::string& error, int traceLevel)
{
// append trace level to error message
if(traceLevel >= 0)
m_what = aux::make_string("LUA ERROR: ", g_lua.traceback(error, traceLevel));
else
m_what = aux::make_string("LUA ERROR: ", error);
}
LuaBadNumberOfArgumentsException::LuaBadNumberOfArgumentsException(int expected, int got)
{
std::string error = "attempt to call a function with wrong number of arguments";
if(expected >= 0 && got >= 0)
error = aux::make_string(error, " (expected ", expected, ", but got ", got, ")");
generateLuaErrorMessage(error, 1);
}
LuaBadValueCastException::LuaBadValueCastException(const std::string& luaTypeName, const std::string& cppTypeName)
{
std::string error = aux::make_string("attempt to cast a '", luaTypeName, "' lua value to '", cppTypeName, "'");
generateLuaErrorMessage(error, 0);
}

View File

@@ -3,12 +3,11 @@
#include "luadeclarations.h"
/// LuaException, all lua errors are throwed by it
class LuaException : public std::exception
{
public:
LuaException(const std::string& error, int traceLevel = -1);
virtual ~LuaException() throw() { };
virtual ~LuaException() throw() { }
void generateLuaErrorMessage(const std::string& error, int traceLevel);
@@ -20,19 +19,16 @@ protected:
std::string m_what;
};
class LuaBadNumberOfArgumentsException : public LuaException {
class LuaBadNumberOfArgumentsException : public LuaException
{
public:
LuaBadNumberOfArgumentsException(int expected = -1, int got = -1);
};
class LuaBadTypeConversinException : public LuaException {
class LuaBadValueCastException : public LuaException
{
public:
LuaBadTypeConversinException(const std::string& typeName = "an incompatible lua type");
};
class LuaBadValueCastException : public LuaException {
public:
LuaBadValueCastException(const LuaValuePtr& value, const std::string& typeName = "an incompatible lua type");
LuaBadValueCastException(const std::string& luaTypeName, const std::string& cppTypeName);
};
#endif

View File

@@ -0,0 +1,63 @@
#include "luainterface.h"
#include <graphics/fontmanager.h>
#include <ui/ui.h>
#include <net/protocol.h>
void LuaInterface::registerFunctions()
{
// easy typing _1, _2, ...
using namespace std::placeholders;
// UIWidget
g_lua.registerClass<UIWidget>();
g_lua.bindClassStaticFunction<UIWidget>("create", &UIWidget::create);
g_lua.bindClassMemberFunction("destroy", &UIWidget::destroy);
g_lua.bindClassMemberFunction("addChild", &UIWidget::addChild);
g_lua.bindClassMemberField<UIWidget>("id", &UIWidget::getId, &UIWidget::setId);
g_lua.bindClassMemberField<UIWidget>("enabled", &UIWidget::isEnabled, &UIWidget::setEnabled);
g_lua.bindClassMemberField<UIWidget>("visible", &UIWidget::isVisible, &UIWidget::setVisible);
g_lua.bindClassMemberField<UIWidget>("width", &UIWidget::getWidth, &UIWidget::setWidth);
g_lua.bindClassMemberField<UIWidget>("height", &UIWidget::getHeight, &UIWidget::setHeight);
g_lua.bindClassMemberField<UIWidget>("parent", &UIWidget::getParent, &UIWidget::setParent);
g_lua.bindClassMemberField<UIWidget>("marginTop", &UIWidget::getMarginTop, &UIWidget::setMarginTop);
g_lua.bindClassMemberField<UIWidget>("marginBottom", &UIWidget::getMarginBottom, &UIWidget::setMarginBottom);
g_lua.bindClassMemberField<UIWidget>("marginLeft", &UIWidget::getMarginLeft, &UIWidget::setMarginLeft);
g_lua.bindClassMemberField<UIWidget>("marginRight", &UIWidget::getMarginRight, &UIWidget::setMarginRight);
g_lua.bindClassMemberFunction<UIWidget>("centerIn", &UIWidget::centerIn);
g_lua.bindClassMemberFunction<UIWidget>("addAnchor", &UIWidget::addAnchor);
g_lua.bindClassMemberFunction<UIWidget>("getChild", &UIWidget::getChildById);
g_lua.bindClassMemberFunction<UIWidget>("addChild", &UIWidget::addChild);
// UILabel
g_lua.registerClass<UILabel, UIWidget>();
g_lua.bindClassStaticFunction<UILabel>("create", &UILabel::create);
g_lua.bindClassMemberField("text", &UILabel::getText, &UILabel::setText);
g_lua.bindClassMemberFunction("resizeToText", &UILabel::resizeToText);
// UIButton
g_lua.registerClass<UIButton, UIWidget>();
g_lua.bindClassStaticFunction<UIButton>("create", &UIButton::create);
g_lua.bindClassMemberField("text", &UIButton::getText, &UIButton::setText);
// UILineEdit
g_lua.registerClass<UILineEdit, UIWidget>();
g_lua.bindClassStaticFunction<UILineEdit>("create", &UILineEdit::create);
g_lua.bindClassMemberField("text", &UILineEdit::getText, &UILineEdit::setText);
// UIWindow
g_lua.registerClass<UIWindow, UIWidget>();
g_lua.bindClassStaticFunction<UIWindow>("create", &UIWindow::create);
g_lua.bindClassMemberField("title", &UIWindow::getTitle, &UIWindow::setTitle);
// Protocol
g_lua.registerClass<Protocol>();
// global functions
g_lua.bindGlobalFunction("importFont", std::bind(&FontManager::importFont, &g_fonts, _1));
g_lua.bindGlobalFunction("setDefaultFont", std::bind(&FontManager::setDefaultFont, &g_fonts, _1));
g_lua.bindGlobalFunction("importStyles", std::bind(&UIManager::importStyles, &g_ui, _1));
g_lua.bindGlobalFunction("loadUI", std::bind(&UIManager::loadUI, &g_ui, _1));
g_lua.bindGlobalFunction("getRootWidget", std::bind(&UIManager::getRootWidget, &g_ui));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
#ifndef LUAINTERFACE_H
#define LUAINTERFACE_H
#include "luadeclarations.h"
struct lua_State;
typedef int (*LuaCFunction) (lua_State *L);
/// Class that manages LUA stuff
class LuaInterface
{
public:
LuaInterface();
~LuaInterface();
void init();
void terminate();
/// Register core script functions
void registerFunctions();
// functions that will register all script stuff in lua global environment
void registerClass(const std::string& className, const std::string& baseClass = "LuaObject");
void registerClassStaticFunction(const std::string& className,
const std::string& functionName,
const LuaCppFunction& function);
void registerClassMemberFunction(const std::string& className,
const std::string& functionName,
const LuaCppFunction& function);
void registerClassMemberField(const std::string& className,
const std::string& field,
const LuaCppFunction& getFunction,
const LuaCppFunction& setFunction);
void registerGlobalFunction(const std::string& functionName,
const LuaCppFunction& function);
// register shortcuts using templates
template<class C, class B = LuaObject>
void registerClass() {
registerClass(aux::demangle_type<C>(), aux::demangle_type<B>());
}
template<class C>
void registerClassStaticFunction(const std::string& functionName, const LuaCppFunction& function) {
registerClassStaticFunction(aux::demangle_type<C>(), functionName, function);
}
template<class C>
void registerClassMemberFunction(const std::string& functionName, const LuaCppFunction& function) {
registerClassMemberFunction(aux::demangle_type<C>(), functionName, function);
}
template<class C>
void registerClassMemberField(const std::string& field,
const LuaCppFunction& getFunction,
const LuaCppFunction& setFunction) {
registerClassMemberField(aux::demangle_type<C>(), field, getFunction, setFunction);
}
// methods for binding functions
template<class C, typename F>
void bindClassStaticFunction(const std::string& functionName, const F& function);
template<class C, typename F>
void bindClassMemberFunction(const std::string& functionName, F C::*function);
template<class C, typename F1, typename F2>
void bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction);
template<class C, typename F>
void bindClassMemberGetField(const std::string& fieldName, F C::*getFunction);
template<class C, typename F>
void bindClassMemberSetField(const std::string& fieldName, F C::*setFunction);
template<typename F>
void bindGlobalFunction(const std::string& functionName, const F& function);
private:
/// Metamethod that will retrieve fields values (that include functions) from the object when using '.' or ':'
static int luaObjectGetEvent(LuaInterface* lua);
/// Metamethod that is called when setting a field of the object by using the keyword '='
static int luaObjectSetEvent(LuaInterface* lua);
/// Metamethod that will check equality of objects by using the keyword '=='
static int luaObjectEqualEvent(LuaInterface* lua);
/// Metamethod that is called every two lua garbage collections
/// for any LuaObject that have no references left in lua environment
/// anymore, thus this creates the possibility of holding an object
/// existence by lua until it got no references left
static int luaObjectCollectEvent(LuaInterface* lua);
public:
/// Loads and runs a script
/// @exception LuaException is thrown on any lua error
void runScript(const std::string& fileName);
/// Loads and runs the script from buffer
/// @exception LuaException is thrown on any lua error
void runBuffer(const std::string& buffer, const std::string& source);
/// Loads a script file and pushes it's main function onto stack,
/// @exception LuaException is thrown on any lua error
void loadScript(const std::string& fileName);
/// Loads a function from buffer and pushes it onto stack,
/// @exception LuaException is thrown on any lua error
void loadFunction(const std::string& buffer, const std::string& source = "lua function buffer");
/// Evaluates a lua expression and pushes the result value onto the stack
/// @exception LuaException is thrown on any lua error
void evaluateExpression(const std::string& expression, const std::string& source = "lua expression");
/// Generates a traceback message for the current call stack
/// @param errorMessage is an additional error message
/// @param level is the level of the traceback, 0 means trace from calling function
/// @return the generated traceback message
std::string traceback(const std::string& errorMessage = "", int level = 0);
/// Searches for the source of the current running function
std::string currentSourcePath();
/// @brief Calls a function
/// The function and arguments must be on top of the stack in order,
/// results are pushed onto the stack.
/// @exception LuaException is thrown on any lua error
/// @return number of results
int safeCall(int numArgs = 0);
/// Same as safeCall but catches exceptions and can also calls a table of functions,
/// if any error occurs it will be reported to stdout and returns 0 results
/// @param requestedResults is the number of results requested to pushes onto the stack,
/// if supplied, the call will always pushes that number of results, even if it fails
int protectedCall(int numArgs = 0, int requestedResults = -1);
/// @brief Creates a new environment table
/// The new environment table is redirected to the global environment (aka _G),
/// this allows to access global variables from _G in the new environment and
/// prevents new variables in this new environment to be set on the global environment
void newEnvironment();
private:
/// Load scripts requested by lua 'require'
static int luaScriptLoader(lua_State* L);
/// Handle lua errors from safeCall
static int luaErrorHandler(lua_State* L);
/// Handle bound cpp functions callbacks
static int luaCppFunctionCallback(lua_State* L);
/// Collect bound cpp function pointers
static int luaCollectCppFunction(lua_State* L);
public:
void createLuaState();
void closeLuaState();
void collectGarbage();
void loadBuffer(const std::string& buffer, const std::string& source);
int pcall(int numArgs = 0, int numRets = 0, int errorFuncIndex = 0);
void call(int numArgs = 0, int numRets = 0);
void throwError();
int ref();
int weakRef();
void unref(int ref);
void useValue() { pushValue(); ref(); }
const char* typeName(int index = -1);
std::string functionSourcePath();
void insert(int index);
void remove(int index);
bool next(int index = -2);
void copy(int index = -1);
void getStackFunction(int level = 0);
void getRef(int ref);
void getWeakRef(int weakRef);
void getGlobalEnvironment();
void setGlobalEnvironment();
void setMetatable(int index = -2);
void getMetatable(int index = -1);
void getField(const char* key, int index = -1);
void getField(const std::string& key, int index = -1) { return getField(key.c_str(), index); }
void setField(const char* key, int index = -2);
void setField(const std::string& key, int index = -2) { return setField(key.c_str(), index); }
void getTable(int index = -2);
void setTable(int index = -3);
void getEnv(int index = -1);
void setEnv(int index = -2);
void getGlobal(const std::string& key);
void setGlobal(const std::string& key);
void rawGet(int index = -1);
void rawGeti(int n, int index = -1);
void rawSet(int index = -3);
void rawSeti(int n, int index = -2);
void newTable();
void* newUserdata(int size);
void pop(int n = 1);
int popInteger();
double popNumber();
bool popBoolean();
std::string popString();
void* popUserdata();
void* popUpvalueUserdata();
LuaObjectPtr popObject();
void pushNil();
void pushInteger(int v);
void pushNumber(double v);
void pushBoolean(bool v);
void pushCString(const char* v);
void pushString(const std::string& v) { pushCString(v.c_str()); }
void pushLightUserdata(void* p);
void pushThread();
void pushValue(int index = -1);
void pushObject(const LuaObjectPtr& obj);
void pushCFunction(LuaCFunction func, int n = 0);
void pushCppFunction(const LuaCppFunction& func);
bool isNil(int index = -1);
bool isBoolean(int index = -1);
bool isNumber(int index = -1);
bool isString(int index = -1);
bool isTable(int index = -1);
bool isFunction(int index = -1);
bool isCFunction(int index = -1);
bool isLuaFunction(int index = -1) { return (isFunction() && !isCFunction()); }
bool isUserdata(int index = -1);
bool toBoolean(int index = -1);
int toInteger(int index = -1);
double toNumber(int index = -1);
const char* toCString(int index = -1);
std::string toString(int index = -1);
void* toUserdata(int index = -1);
LuaObjectPtr toObject(int index = -1);
int getTop();
int stackSize() { return getTop(); }
void clearStack() { pop(stackSize()); }
bool hasIndex(int index) { return (stackSize() >= (index < 0 ? -index : index) && index != 0); }
/// Pushes any type onto the stack
template<typename T, typename... Args>
void polymorphicPush(T v, Args... args) { push_luavalue(v); polymorphicPush(args...); }
void polymorphicPush() { }
/// Casts a value from stack to any type
/// @exception LuaBadValueCastException thrown if the cast fails
template<class T>
T castValue(int index = -1);
/// Same as castValue but also pops
template<class T>
T polymorphicPop() { T v = castValue<T>(); pop(1); return v; }
private:
lua_State* L;
int m_weakTableRef;
};
extern LuaInterface g_lua;
// must be included after, because they need LuaInterface fully declared
#include "luaexception.h"
#include "luabinder.h"
#include "luavaluecasts.h"
// next templates must be defined after above includes
template<class C, typename F>
void LuaInterface::bindClassStaticFunction(const std::string& functionName, const F& function) {
registerClassStaticFunction<C>(functionName, luabinder::bind_fun(function));
}
template<class C, typename F>
void LuaInterface::bindClassMemberFunction(const std::string& functionName, F C::*function) {
registerClassMemberFunction<C>(functionName, luabinder::bind_mem_fun(function));
}
template<class C, typename F1, typename F2>
void LuaInterface::bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) {
registerClassMemberField<C>(fieldName,
luabinder::bind_mem_fun(getFunction),
luabinder::bind_mem_fun(setFunction));
}
template<class C, typename F>
void LuaInterface::bindClassMemberGetField(const std::string& fieldName, F C::*getFunction) {
registerClassMemberField<C>(fieldName,
luabinder::bind_mem_fun(getFunction),
LuaCppFunction());
}
template<class C, typename F>
void LuaInterface::bindClassMemberSetField(const std::string& fieldName, F C::*setFunction) {
registerClassMemberField<C>(fieldName,
LuaCppFunction(),
luabinder::bind_mem_fun(setFunction));
}
template<typename F>
void LuaInterface::bindGlobalFunction(const std::string& functionName, const F& function) {
registerGlobalFunction(functionName, luabinder::bind_fun(function));
}
template<class T>
T LuaInterface::castValue(int index) {
T o;
if(!luavalue_cast(index, o))
throw LuaBadValueCastException(typeName(index), aux::demangle_type<T>());
return o;
}
#endif

View File

@@ -0,0 +1,47 @@
#include "luaobject.h"
#include "luainterface.h"
LuaObject::LuaObject() : m_fieldsTableRef(-1)
{
}
LuaObject::~LuaObject()
{
luaReleaseFieldsTable();
}
void LuaObject::luaReleaseFieldsTable()
{
if(m_fieldsTableRef != -1)
g_lua.unref(m_fieldsTableRef);
}
void LuaObject::luaSetField(const std::string& key)
{
// create fields table on the fly
if(m_fieldsTableRef == -1) {
g_lua.newTable(); // create fields table
m_fieldsTableRef = g_lua.ref(); // save a reference for it
}
g_lua.getRef(m_fieldsTableRef); // push the table
g_lua.insert(-2); // move the value to the top
g_lua.setField(key); // set the field
g_lua.pop(); // pop the fields table
}
void LuaObject::luaGetField(const std::string& key)
{
if(m_fieldsTableRef != -1) {
g_lua.getRef(m_fieldsTableRef); // push the obj's fields table
g_lua.getField(key); // push the field value
g_lua.remove(-2); // remove the table
} else {
g_lua.pushNil();
}
}
int LuaObject::getUseCount()
{
return shared_from_this().use_count() - 1;
}

View File

@@ -0,0 +1,81 @@
#ifndef LUAOBJECT_H
#define LUAOBJECT_H
#include "luadeclarations.h"
/// LuaObject, all script-able classes have it as base
class LuaObject : public std::enable_shared_from_this<LuaObject>
{
public:
LuaObject();
virtual ~LuaObject() ;
/// Calls a function or table of functions stored in a lua field, results are pushed onto the stack,
/// if any lua error occurs, it will be reported to stdout and return 0 results
/// @return the number of results
template<typename... T>
int callLuaField(const std::string& field, const T&... args);
/// Sets a field in this lua object
template<typename T>
void setLuaField(const std::string& key, const T& value);
/// Gets a field from this lua object
template<typename T>
T getLuaField(const std::string& key);
/// Release fields table reference
void luaReleaseFieldsTable();
/// Sets a field from this lua object, the value must be on the stack
void luaSetField(const std::string& key);
/// Gets a field from this lua object, the result is pushed onto the stack
void luaGetField(const std::string& key);
/// Returns the number of references of this object
/// @note each userdata of this object on lua counts as a reference
int getUseCount();
/// Returns the class name used in Lua
virtual std::string getLuaObjectName() const {
// this could later be cached for more performance
return aux::demangle_name(typeid(*this).name());
}
LuaObjectPtr asLuaObject() { return shared_from_this(); }
private:
int m_fieldsTableRef;
};
#include "luainterface.h"
template<typename... T>
int LuaObject::callLuaField(const std::string& field, const T&... args) {
// note that the field must be retrieved from this object lua value
// to force using the __index metamethod of it's metatable
// so cannot use LuaObject::getField here
// push field
g_lua.pushObject(asLuaObject());
g_lua.getField(field);
// the first argument is always this object (self)
g_lua.insert(-2);
g_lua.polymorphicPush(args...);
return g_lua.protectedCall(1 + sizeof...(args));
}
template<typename T>
void LuaObject::setLuaField(const std::string& key, const T& value) {
g_lua.polymorphicPush(value);
luaSetField(key);
}
template<typename T>
T LuaObject::getLuaField(const std::string& key) {
luaGetField(key);
return g_lua.polymorphicPop<T>();
}
#endif

View File

@@ -0,0 +1,195 @@
#ifndef LUAVALUECASTS_H
#define LUAVALUECASTS_H
// this file is and must be included only from luainterface.h
#include "luainterface.h"
#include "luaexception.h"
/// Pushes bool
inline void push_luavalue(bool v) {
g_lua.pushBoolean(v);
}
/// Pushes int
inline void push_luavalue(int v) {
g_lua.pushInteger(v);
}
/// Pushes double
inline void push_luavalue(double v) {
g_lua.pushNumber(v);
}
/// Pushes std::string
inline void push_luavalue(const std::string& v) {
g_lua.pushString(v);
}
/// Pushes const char*
inline void push_luavalue(const char* v) {
g_lua.pushCString(v);
}
/// Pushes LuaCppFunction
inline void push_luavalue(const LuaCppFunction& v) {
g_lua.pushCppFunction(v);
}
/// Pushes LuaObjectPtr
template<class T>
typename std::enable_if<std::is_base_of<LuaObject, typename T::element_type>::value, void>::type
push_luavalue(const T& v) {
if(v)
return g_lua.pushObject(v);
return g_lua.pushNil();
}
/// Push std::function types
template<typename Ret, typename... Args>
void push_luavalue(const std::function<Ret(Args...)>& v) {
if(v) {
LuaCppFunction f = luabinder::bind_fun(v);
g_lua.pushCppFunction(f);
} else
g_lua.pushNil();
}
/// Casts lua value to bool
inline bool luavalue_cast(int index, bool& o) {
o = g_lua.toBoolean(index);
return true;
}
/// Casts lua value to int
inline bool luavalue_cast(int index, int& o) {
o = g_lua.toInteger(index);
if(o == 0 && !g_lua.isNumber(index))
return false;
return true;
}
/// Casts lua value to double
inline bool luavalue_cast(int index, double& o) {
o = g_lua.toNumber(index);
if(o == 0 && !g_lua.isNumber(index))
return false;
return true;
}
/// Casts lua value to std::string
inline bool luavalue_cast(int index, std::string& o) {
o = g_lua.toString(index);
if(o.empty() && !g_lua.isString(index))
return false;
return true;
}
/// Casts lua value to LuaObjectPtr
inline bool luavalue_cast(int index, LuaObjectPtr& o) {
if(g_lua.isUserdata(index)) {
o = g_lua.toObject(index);
return true;
} else if(g_lua.isNil(index)) {
o = nullptr;
return true;
}
return false;
}
/// Casts lua value to enum
template<class T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
luavalue_cast(int index, T& o) {
return luavalue_cast(index, (int&)o);
}
/// Cast lua userdata to a class pointer
template<class T>
typename std::enable_if<std::is_base_of<LuaObject, T>::value, bool>::type
luavalue_cast(int index, T*& o) {
LuaObjectPtr obj;
if(!luavalue_cast(index, obj))
return false;
o = std::dynamic_pointer_cast<T>(obj).get();
return !!o;
}
/// Cast lua userdata to a class shared pointer
template<class T>
bool luavalue_cast(int index, std::shared_ptr<T>& o) {
LuaObjectPtr obj;
if(!luavalue_cast(index, obj))
return false;
o = std::dynamic_pointer_cast<T>(obj);
return !!o;
}
/// Cast a lua function to a std::function
template<typename... Args>
bool luavalue_cast(int index, std::function<void(Args...)>& o) {
if(g_lua.isFunction(index)) {
g_lua.pushValue(index);
// weak references are used here, this means that the script must hold another reference
// to this function, otherwise it will expire
int funcWeakRef = g_lua.weakRef();
o = [=](Args... args...) {
// note that we must catch exceptions, because this lambda can be called from anywhere
// and most of them won't catch exceptions (e.g. dispatcher)
g_lua.getWeakRef(funcWeakRef);
try {
if(g_lua.isFunction()) {
g_lua.polymorphicPush(args...);
assert(g_lua.safeCall(sizeof...(Args)) == 0);
} else {
throw LuaException("attempt to call an expired lua function from C++,"
"did you forget to hold a reference for that function?", 0);
}
} catch(std::exception& e) {
logError(e.what());
}
};
return true;
} else if(g_lua.isNil(index)) {
o = std::function<void(Args...)>();
return true;
}
return false;
}
/// Cast a lua function to a std::function that can return
template<typename Ret, typename... Args>
typename std::enable_if<!std::is_void<Ret>::value, bool>::type
luavalue_cast(int index, std::function<Ret(Args...)>& o) {
if(g_lua.isFunction(index)) {
g_lua.pushValue(index);
// weak references are used here, this means that the script must hold another reference
// to this function, otherwise it will expire
int funcWeakRef = g_lua.weakRef();
o = [=](Args... args...) -> Ret {
// note that we must catch exceptions, because this lambda can be called from anywhere
// and most of them won't catch exceptions (e.g. dispatcher)
try {
g_lua.getWeakRef(funcWeakRef);
if(g_lua.isFunction()) {
g_lua.polymorphicPush(args...);
if(g_lua.safeCall(sizeof...(Args)) != 1)
throw LuaException("a function from lua didn't retrieve the expected number of results", 0);
return g_lua.polymorphicPop<Ret>();
} else {
throw LuaException("attempt to call an expired lua function from C++,"
"did you forget to hold a reference for that function?", 0);
}
} catch(std::exception& e) {
logError(e.what());
}
return Ret();
};
return true;
} else if(g_lua.isNil(index)) {
o = std::function<Ret(Args...)>();
return true;
}
return false;
}
#endif

View File

@@ -1,6 +1,6 @@
#include "connection.h"
#include <core/dispatcher.h>
#include <core/eventdispatcher.h>
static asio::io_service ioService;
@@ -21,7 +21,7 @@ void Connection::connect(const std::string& host, uint16 port, const ConnectCall
{
m_connectCallback = connectCallback;
asio::ip::tcp::resolver::query query(host, convert<std::string>(port));
asio::ip::tcp::resolver::query query(host, aux::unsafe_cast<std::string>(port));
m_resolver.async_resolve(query, std::bind(&Connection::onResolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
m_timer.expires_from_now(boost::posix_time::seconds(2));
@@ -56,7 +56,7 @@ void Connection::recv(uint16 bytes, uint32 timeout, const RecvCallback& callback
void Connection::onTimeout(const boost::system::error_code& error)
{
if(error != asio::error::operation_aborted)
g_dispatcher.addTask(std::bind(m_errorCallback, error));
g_dispatcher.addEvent(std::bind(m_errorCallback, error));
}
void Connection::onResolve(const boost::system::error_code& error, asio::ip::tcp::resolver::iterator endpointIterator)
@@ -65,7 +65,7 @@ void Connection::onResolve(const boost::system::error_code& error, asio::ip::tcp
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(std::bind(m_errorCallback, error));
g_dispatcher.addEvent(std::bind(m_errorCallback, error));
return;
}
@@ -81,12 +81,12 @@ void Connection::onConnect(const boost::system::error_code& error)
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(std::bind(m_errorCallback, error));
g_dispatcher.addEvent(std::bind(m_errorCallback, error));
return;
}
if(m_connectCallback)
g_dispatcher.addTask(m_connectCallback);
g_dispatcher.addEvent(m_connectCallback);
}
void Connection::onSend(const boost::system::error_code& error, size_t)
@@ -95,7 +95,7 @@ void Connection::onSend(const boost::system::error_code& error, size_t)
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(std::bind(m_errorCallback, error));
g_dispatcher.addEvent(std::bind(m_errorCallback, error));
return;
}
}
@@ -106,10 +106,10 @@ void Connection::onRecv(const boost::system::error_code& error)
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(std::bind(m_errorCallback, error));
g_dispatcher.addEvent(std::bind(m_errorCallback, error));
return;
}
if(m_recvCallback)
g_dispatcher.addTask(std::bind(m_recvCallback, m_recvBuffer, m_recvSize));
g_dispatcher.addEvent(std::bind(m_recvCallback, m_recvBuffer, m_recvSize));
}

View File

@@ -82,7 +82,7 @@ void Protocol::onError(const boost::system::error_code& err)
// displays a dialog, finish protocol
callField("onError", message.str());
callLuaField("onError", message.str());
}
bool Protocol::xteaDecrypt(InputMessage& inputMessage)

View File

@@ -5,7 +5,7 @@
#include "inputmessage.h"
#include "outputmessage.h"
#include <script/luaobject.h>
#include <luascript/luaobject.h>
class Protocol : public LuaObject
{
@@ -24,8 +24,6 @@ public:
ProtocolPtr asProtocol() { return std::static_pointer_cast<Protocol>(shared_from_this()); }
virtual const char* getLuaTypeName() const { return "Protocol"; }
protected:
uint32 m_xteaKey[4];
bool m_checksumEnabled, m_xteaEncryptionEnabled;

View File

@@ -1,8 +1,8 @@
#ifndef OTML_H
#define OTML_H
#include "otmldocument.h"
#include "otmlnode.h"
#include "otmlemitter.h"
#include "otmlparser.h"
#include "otmlnodeext.h"
#endif // OTML_H
#endif

View File

@@ -0,0 +1,16 @@
#ifndef OTMLDECLARATIONS_H
#define OTMLDECLARATIONS_H
#include <global.h>
class OTMLNode;
class OTMLDocument;
class OTMLParser;
class OTMLEmitter;
typedef std::shared_ptr<OTMLNode> OTMLNodePtr;
typedef std::shared_ptr<OTMLDocument> OTMLDocumentPtr;
typedef std::weak_ptr<OTMLNode> OTMLNodeWeakPtr;
typedef std::vector<OTMLNodePtr> OTMLNodeList;
#endif

View File

@@ -0,0 +1,41 @@
#include "otmldocument.h"
#include "otmlparser.h"
#include "otmlemitter.h"
#include <core/resourcemanager.h>
OTMLDocumentPtr OTMLDocument::create()
{
OTMLDocumentPtr doc(new OTMLDocument);
doc->setTag("doc");
return doc;
}
OTMLDocumentPtr OTMLDocument::parse(const std::string& fileName)
{
std::stringstream fin;
g_resources.loadFile(fileName, fin);
return parse(fin, fileName);
}
OTMLDocumentPtr OTMLDocument::parse(std::istream& in, const std::string& source)
{
OTMLDocumentPtr doc(new OTMLDocument);
doc->setSource(source);
OTMLParser parser(doc, in);
parser.parse();
return doc;
}
std::string OTMLDocument::emit()
{
return OTMLEmitter::emitNode(shared_from_this());
}
bool OTMLDocument::save(const std::string& fileName)
{
setSource(fileName);
return g_resources.saveFile(fileName, emit());
}

View File

@@ -0,0 +1,29 @@
#ifndef OTMLDOCUMENT_H
#define OTMLDOCUMENT_H
#include "otmlnode.h"
class OTMLDocument : public OTMLNode
{
public:
OTMLDocument() { }
virtual ~OTMLDocument() { }
/// Create a new OTML document for filling it with nodes
static OTMLDocumentPtr create();
/// Parse OTML from a file
static OTMLDocumentPtr parse(const std::string& fileName);
/// Parse OTML from input stream
/// @param source is the file name that will be used to show errors messages
static OTMLDocumentPtr parse(std::istream& in, const std::string& source);
/// Emits this document and all it's children to a std::string
std::string emit();
/// Save this document to a file
bool save(const std::string& fileName);
};
#endif

View File

@@ -1,63 +1,68 @@
#include "otmlemitter.h"
#include "otmlnode.h"
#include "otmldocument.h"
#include <boost/algorithm/string.hpp>
OTMLEmitter::OTMLEmitter() :
m_rootNode(0)
{
}
OTMLEmitter::~OTMLEmitter()
{
if(m_rootNode)
delete m_rootNode;
}
OTMLNode* OTMLEmitter::createDocument()
{
m_rootNode = new OTMLNode;
return m_rootNode;
}
std::string OTMLEmitter::emitDocument()
{
if(m_rootNode)
return emitNode(m_rootNode, 0);
return std::string();
}
std::string OTMLEmitter::emitNodeValue(OTMLNode* node)
{
std::string value = node->value();
if(!value.empty() && (value[0] == '"' || *value.rbegin() == '"'||
value[0] == ' ' || *value.rbegin() == ' '||
value[0] == '-' || value[0] == '{' || value[0] == '[' || value[0] == '|' ||
value.find("\n") != std::string::npos)) {
boost::replace_all(value, "\\", "\\\\");
boost::replace_all(value, "\"", "\\\"");
boost::replace_all(value, "\n", "\\n");
value.append("\"");
value.insert(0, "\"");
}
return value;
}
std::string OTMLEmitter::emitNode(OTMLNode* node, int currentDepth)
std::string OTMLEmitter::emitNode(const OTMLNodePtr& node, int currentDepth)
{
std::stringstream ss;
for(int i=1;i<currentDepth;++i)
ss << " ";
if(currentDepth > 0) {
if(node->hasTag())
// emit nodes
if(currentDepth >= 0) {
// fill spaces for current depth
for(int i=0;i<currentDepth;++i)
ss << " ";
// emit node tag
if(node->hasTag()) {
ss << node->tag();
if(node->hasValue())
ss << (node->hasTag() ? ": " : "- ") << emitNodeValue(node);
if(!node->hasTag() && !node->hasValue())
// add ':' to if the node is unique or has value
if(node->hasValue() || node->isUnique())
ss << ":";
} else
ss << "-";
ss << std::endl;
// emit node value
if(node->hasValue()) {
ss << " ";
std::string value = node->value();
// emit multiline values
if(value.find("\n") != std::string::npos) {
if(value[value.length()-1] == '\n' && value[value.length()-2] == '\n')
ss << "|+";
else if(value[value.length()-1] == '\n')
ss << "|";
else
ss << "|-";
// multilines
for(std::size_t pos = 0; pos < value.length(); ++pos) {
ss << "\n";
// fill spaces for multiline depth
for(int i=0;i<currentDepth+1;++i)
ss << " ";
// fill until a new line
while(pos < value.length()) {
if(value[pos] == '\n')
break;
ss << value[pos++];
}
}
// emit inline values
} else
ss << value;
}
}
for(int i=0;i<node->size();++i)
// emit children
for(int i=0;i<node->size();++i) {
if(currentDepth >= 0 || i != 0)
ss << "\n";
ss << emitNode(node->at(i), currentDepth+1);
}
return ss.str();
}

View File

@@ -1,24 +1,13 @@
#ifndef OTMLEMITTER_H
#define OTMLEMITTER_H
#include <string>
class OTMLNode;
#include "otmldeclarations.h"
class OTMLEmitter
{
public:
OTMLEmitter();
~OTMLEmitter();
OTMLNode* createDocument();
std::string emitDocument();
static std::string emitNodeValue(OTMLNode* node);
static std::string emitNode(OTMLNode* node, int currentDepth = 0);
private:
OTMLNode* m_rootNode;
/// Emits a node and it's children to a std::string
static std::string emitNode(const OTMLNodePtr& node, int currentDepth = -1);
};
#endif // OTMLEMITTER_H
#endif

View File

@@ -0,0 +1,25 @@
#include "otmlexception.h"
#include "otmldocument.h"
OTMLException::OTMLException(const OTMLNodePtr& node, const std::string& error)
{
std::stringstream ss;
ss << "OTML error";
if(!node->source().empty())
ss << " in '" << node->source() << "'";
ss << ": " << error;
m_what = ss.str();
}
OTMLException::OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line)
{
std::stringstream ss;
ss << "OTML error";
if(doc && !doc->source().empty()) {
ss << " in '" << doc->source() << "'";
if(line >= 0)
ss << " at line " << line;
}
ss << ": " << error;
m_what = ss.str();
}

View File

@@ -0,0 +1,23 @@
#ifndef OTMLEXCEPTION_H
#define OTMLEXCEPTION_H
#include "otmldeclarations.h"
/// All OTML errors throw this exception
class OTMLException : public std::exception
{
public:
OTMLException(const OTMLNodePtr& node, const std::string& error);
OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line = -1);
virtual ~OTMLException() throw() { };
virtual const char* what() const throw() { return m_what.c_str(); }
protected:
OTMLException() { }
void generateErrorMessage(const OTMLDocumentPtr& doc, const std::string& error, int line);
void generateErrorMessage(const OTMLNodePtr& node, const std::string& error);
std::string m_what;
};
#endif

View File

@@ -1,93 +1,225 @@
#include "otmlnode.h"
#include "otmlemitter.h"
#include "otmldocument.h"
#include <boost/algorithm/string.hpp>
OTMLNode::OTMLNode(std::string what) :
m_parent(0), m_line(0), m_what(what)
OTMLNode::OTMLNode()
{
m_unique = false;
}
OTMLNode::~OTMLNode()
std::string OTMLNode::tag() const
{
for(int i=0;i<size();++i)
delete at(i);
return m_tag;
}
OTMLNode* OTMLNode::at(const std::string& childTag) const {
int i=0;
while(i<size() && at(i)->tag()!=childTag)
++i;
return at(i);
std::string OTMLNode::value() const
{
// ~ is an alias for no value
if(m_value == "~")
return aux::empty_string;
return m_value;
}
OTMLNode* OTMLNode::at(int pos) const
int OTMLNode::size() const
{
if(pos < (int)m_children.size() && pos >= 0)
return m_children[pos];
return 0;
return m_childNodes.size();
}
OTMLNode* OTMLNode::atPath(const std::string& path) const
OTMLNodePtr OTMLNode::parent() const
{
std::vector<std::string> nodeTags;
OTMLNode* node = const_cast<OTMLNode*>(this);
std::string shortcutKey;
return m_parent.lock();
}
boost::split(nodeTags, path, boost::is_any_of(std::string("/")));
foreach(std::string& stag, nodeTags) {
if(!shortcutKey.empty())
shortcutKey += '.';
shortcutKey += stag;
if(node)
node = node->at(stag);
const OTMLNodeList& OTMLNode::childNodes() const
{
return m_childNodes;
}
std::string OTMLNode::source() const
{
return m_source;
}
bool OTMLNode::hasTag() const
{
return !m_tag.empty();
}
bool OTMLNode::hasValue() const
{
return (!m_value.empty() && m_value != "~");
}
bool OTMLNode::hasChildNodes() const
{
return size() > 0;
}
bool OTMLNode::hasChild(const std::string& childTag) const
{
return !!get(childTag);
}
bool OTMLNode::hasChild(int index) const
{
return !!get(index);
}
bool OTMLNode::isUnique() const
{
return m_unique;
}
void OTMLNode::setTag(std::string tag)
{
m_tag = tag;
// valued nodes that has tags are always unique
if(!m_value.empty() && hasTag())
setUnique();
}
void OTMLNode::setValue(const std::string& value)
{
m_value = value;
// valued nodes that has tags are always unique
if(!m_value.empty() && hasTag())
setUnique();
}
void OTMLNode::setParent(const OTMLNodePtr& parent)
{
m_parent = parent;
}
void OTMLNode::setUnique(bool unique)
{
m_unique = unique;
}
void OTMLNode::setSource(const std::string& source)
{
m_source = source;
}
OTMLNodePtr OTMLNode::at(const std::string& childTag)
{
for(const OTMLNodePtr& child : m_childNodes) {
if(child->tag() == childTag)
return child;
}
throw OTMLException(shared_from_this(), aux::make_string("child node with tag '", childTag, "' not found"));
return nullptr;
}
OTMLNodePtr OTMLNode::at(int childIndex)
{
if(childIndex < size() && childIndex >= 0)
return m_childNodes[childIndex];
throw OTMLException(shared_from_this(), aux::make_string("child node at index '", childIndex, "' not found"));
return nullptr;
}
OTMLNodePtr OTMLNode::get(const std::string& childTag) const
{
for(const OTMLNodePtr& child : m_childNodes) {
if(child->tag() == childTag)
return child;
}
return nullptr;
}
OTMLNodePtr OTMLNode::get(int childIndex) const
{
if(childIndex < size() && childIndex >= 0)
return m_childNodes[childIndex];
return nullptr;
}
void OTMLNode::addChild(const OTMLNodePtr& newChild)
{
// replace is needed when the tag is marked as unique
if(newChild->hasTag()) {
for(OTMLNodePtr node : m_childNodes) {
if(node->tag() == newChild->tag() && (node->isUnique() || newChild->isUnique())) {
newChild->setUnique();
replaceChild(node, newChild);
// remove any other child with the same tag
auto it = m_childNodes.begin();
while(it != m_childNodes.end()) {
OTMLNodePtr node = (*it);
if(node != newChild && node->tag() == newChild->tag()) {
node->setParent(nullptr);
it = m_childNodes.erase(it);
} else
++it;
}
return;
}
}
}
if(node)
return node;
else
return at(shortcutKey);
m_childNodes.push_back(newChild);
newChild->setParent(shared_from_this());
}
OTMLNode* OTMLNode::createNode(std::string tag)
bool OTMLNode::removeChild(const OTMLNodePtr& oldChild)
{
OTMLNode* node = new OTMLNode;
node->setTag(tag);
addNode(node);
return node;
}
void OTMLNode::addNode(OTMLNode* node) {
if(node->hasTag() && node->hasValue())
if(OTMLNode* other = at(node->tag()))
removeNode(other);
m_children.push_back(node);
node->setParent(this);
}
bool OTMLNode::removeNode(OTMLNode* node) {
for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
if((*it) == node) {
m_children.erase(it);
for(auto it = m_childNodes.begin(); it != m_childNodes.end(); ++it) {
if((*it) == oldChild) {
m_childNodes.erase(it);
oldChild->setParent(nullptr);
return true;
}
}
return false;
}
std::string OTMLNode::generateErrorMessage(const std::string& message) const {
std::stringstream ss;
ss << "OTML error";
if(!what().empty())
ss << " in '" << what() << "'";
if(m_line > 0)
ss << " at line " << m_line;
if(m_line > 0 && hasTag())
ss << ", in node '" << tag() << "'";
ss << ": " << message;
return ss.str();
bool OTMLNode::replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild)
{
for(auto it = m_childNodes.begin(); it != m_childNodes.end(); ++it) {
if((*it) == oldChild) {
oldChild->setParent(nullptr);
newChild->setParent(shared_from_this());
it = m_childNodes.erase(it);
m_childNodes.insert(it, newChild);
return true;
}
}
return false;
}
void OTMLNode::throwError(const std::string& message) const
void OTMLNode::clear()
{
throw OTMLException(generateErrorMessage(message));
m_childNodes.clear();
}
void OTMLNode::merge(const OTMLNodePtr& node)
{
for(const OTMLNodePtr& child : node->childNodes()) {
OTMLNodePtr newNode(new OTMLNode);
newNode->setUnique(child->isUnique());
newNode->setTag(child->tag());
newNode->setValue(child->value());
addChild(newNode);
newNode->merge(child);
}
}
OTMLNodePtr OTMLNode::clone() const
{
OTMLNodePtr myClone(new OTMLNode);
myClone->setTag(tag());
myClone->setValue(value());
myClone->setUnique(isUnique());
for(OTMLNodePtr child : childNodes())
myClone->addChild(child->clone());
return myClone;
}
std::string OTMLNode::emit()
{
return OTMLEmitter::emitNode(shared_from_this(), 0);
}

View File

@@ -1,260 +1,215 @@
#ifndef OTMLNODE_H
#define OTMLNODE_H
#include <string>
#include <vector>
#include <list>
#include <map>
#include <stdexcept>
#include <typeinfo>
#include "otmldeclarations.h"
#include "otmlexception.h"
#include <util/foreach.h>
#include <util/makestring.h>
#include <util/convert.h>
class OTMLException : public std::runtime_error {
public:
OTMLException(const std::string& what) : std::runtime_error(what) {}
};
class OTMLNode
class OTMLNode : public std::enable_shared_from_this<OTMLNode>
{
public:
typedef std::vector<OTMLNode*> NodeList;
typedef NodeList::iterator iterator;
typedef NodeList::const_iterator const_iterator;
OTMLNode();
virtual ~OTMLNode() { }
OTMLNode(std::string what = "");
~OTMLNode();
std::string value() const;
std::string tag() const;
int size() const;
OTMLNodePtr parent() const;
const OTMLNodeList& childNodes() const;
std::string source() const;
bool hasTag() const { return !m_tag.empty(); }
bool hasChildren() const { return size() > 0; }
bool hasValue() const { return !m_value.empty(); }
bool hasChild(const std::string ctag) const { return at(ctag) != 0; }
bool hasTag() const;
bool hasValue() const;
bool hasChildNodes() const;
bool hasChild(const std::string& childTag) const;
bool hasChild(int index) const;
bool isUnique() const;
void setTag(std::string tag) { m_tag = tag; }
void setLine(int line) { m_line = line; }
void setValue(const std::string& value) { m_value = value; }
void setParent(OTMLNode* parent) { m_parent = parent; }
void setTag(std::string tag);
void setValue(const std::string& value);
void setParent(const OTMLNodePtr& parent);
void setUnique(bool unique = true);
void setSource(const std::string& source);
std::string tag() const { return m_tag; }
int line() const { return m_line; }
int size() const { return m_children.size(); }
OTMLNode* parent() { return m_parent; }
std::string what() const { return (m_parent ? m_parent->what() : m_what); }
/// Same as get but if the child node doesn't exist throws an OTMLException
OTMLNodePtr at(const std::string& childTag);
OTMLNodePtr at(int childIndex);
iterator begin() { return m_children.begin(); }
iterator end() { return m_children.end(); }
const_iterator begin() const { return m_children.begin(); }
const_iterator end() const { return m_children.end(); }
/// Get a child node, if doesn't exists returns nullptr
OTMLNodePtr get(const std::string& childTag) const;
OTMLNodePtr get(int childIndex) const;
OTMLNode* front() const { return at(0); }
OTMLNode* back() const { return at(size()-1); }
void addChild(const OTMLNodePtr& newChild);
bool removeChild(const OTMLNodePtr& oldChild);
bool replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild);
OTMLNode* at(const std::string& ctag) const;
OTMLNode* at(int pos) const;
OTMLNode *atPath(const std::string& path) const;
/// Remove all children
void clear();
OTMLNode* createNode(std::string tag = "");
void addNode(OTMLNode* node);
bool removeNode(OTMLNode* node);
/// Recursively copy children from another node to this node
void merge(const OTMLNodePtr& node);
std::string generateErrorMessage(const std::string& message) const;
void throwError(const std::string& message) const;
/// Recursively clone this node into a new one
OTMLNodePtr clone() const;
std::string value(const std::string& def = "") const { return (m_value.empty() ? def : m_value); }
std::string valueAt(const std::string ctag, const std::string& def = "") const {
OTMLNode* c = at(ctag);
return (c ? c->value() : def);
}
std::string valueAt(int pos, const std::string& def = "") const {
OTMLNode* n = at(pos);
return (n ? n->value() : def);
}
std::string valueAtPath(const std::string path, const std::string& def = "") const {
OTMLNode* c = atPath(path);
return (c ? c->value() : def);
}
/// Emits this node to a std::string
virtual std::string emit();
// read into memory
template <typename T>
void read(T* v) const {
if(!(*this >> *v))
throwError(make_string("failed to cast node value to type ", std::string(typeid(T).name())));
}
template<typename T>
T read();
template <typename T>
bool readAt(const std::string& ctag, T* v) const {
if(OTMLNode* node = at(ctag)) {
node->read<T>(v);
return true;
}
return false;
}
template<typename T>
T read(const T& def);
template <typename T>
bool readAt(int pos, T* v) const {
if(OTMLNode* node = at(pos)) {
node->read<T>(v);
return true;
}
return false;
}
template<typename T, typename U>
T readAt(const U& childIdentifier);
template <typename T>
bool readAtPath(const std::string& ctag, T* v) const {
if(OTMLNode* node = atPath(ctag)) {
node->read<T>(v);
return true;
}
return false;
}
template<typename T, typename U>
T readAt(const U& childIdentifier, const T& def);
// read returning the result
template <typename T>
T read() const { T v; read<T>(&v); return v;}
template<typename T>
void write(const T& v);
template <typename T>
T readAt(const std::string& ctag) const {
T v;
if(!readAt(ctag, &v))
throwError(make_string("child node \'", ctag, "\' not found"));
return v;
}
template<typename T>
void writeAt(const std::string& childTag, const T& v);
template <typename T>
T readAt(int pos) const {
T v;
if(!readAt(pos, &v))
throwError(make_string("child node at pos ", pos, " not found"));
return v;
}
template <typename T>
T readAtPath(const std::string& ctag) const {
T v;
if(!readAtPath(ctag, &v))
throwError(make_string("child node in path \'", ctag, "\' not found"));
return v;
}
// read with default supplied
template <typename T>
T readAt(const std::string& ctag, const T& def) const {
OTMLNode* c = at(ctag);
return (c ? c->read<T>() : def);
}
template <typename T>
T readAt(int pos, const T& def) const {
OTMLNode* c = at(pos);
return (c ? c->read<T>() : def);
}
template <typename T>
T readAtPath(const std::string& path, const T& def) const {
OTMLNode* c = atPath(path);
return (c ? c->read<T>() : def);
}
// writing
template <typename T>
void write(T v) {
if(!(*this << v))
throwError(make_string("failed to cast to string node value of type ", typeid(T).name()));
}
template <typename T>
void writeIn(int pos, T v) {
OTMLNode* c;
while(!at(pos))
c = createNode();
c->write<T>(v);
}
template <typename T>
void writeIn(const std::string& ctag, T v) {
OTMLNode* c = at(ctag);
if(!c)
c = createNode(ctag);
c->write<T>(v);
}
template<typename T>
void writeIn(const T& v);
private:
OTMLNode* m_parent;
int m_line;
std::string m_what;
NodeList m_children;
std::string m_tag;
std::string m_value;
std::string m_source;
bool m_unique;
OTMLNodeList m_childNodes;
OTMLNodeWeakPtr m_parent;
};
// read operators
template <typename T>
bool operator >> (const OTMLNode& node, T& v) { return safe_convert(node.value(), v); }
// templates for reading values
template<typename T>
T OTMLNode::read() {
T v;
if(!from_otmlnode(shared_from_this(), v))
throw OTMLException(shared_from_this(),
aux::make_string("failed to cast node value to type '", aux::demangle_type<T>(), "'"));
return v;
}
template <typename T>
bool operator >> (const OTMLNode& node, std::vector<T>& v) {
v.resize(node.size());
for(unsigned i=0;i<node.size();++i)
v[i] = node.readAt<T>(i);
template<typename T>
T OTMLNode::read(const T& def) {
if(hasValue())
return read<T>();
return def;
}
template<typename T, typename U>
T OTMLNode::readAt(const U& childIdentifier) {
OTMLNodePtr child = at(childIdentifier);
return child->read<T>();
}
template<typename T, typename U>
T OTMLNode::readAt(const U& childIdentifier, const T& def) {
OTMLNodePtr child = get(childIdentifier);
if(!child)
return def;
return child->read<T>(def);
}
// templates for writing values
template<typename T>
void OTMLNode::write(const T& v) {
to_otmlnode(shared_from_this(), v);
}
template<typename T>
void OTMLNode::writeAt(const std::string& childTag, const T& v) {
OTMLNodePtr child = get(childTag);
bool created = false;
if(!child) {
child = OTMLNodePtr(new OTMLNode);
child->setTag(childTag);
child->setUnique();
created = true;
}
child->write<T>(v);
if(created)
addChild(child);
}
template<typename T>
void OTMLNode::writeIn(const T& v) {
OTMLNodePtr child = OTMLNodePtr(new OTMLNode);
child->write<T>(v);
addChild(child);
}
// templates for casting a node to another type
template<typename T>
bool from_otmlnode(OTMLNodePtr node, T& v) {
return aux::cast(node->value(), v);
}
template<typename T>
bool from_otmlnode(OTMLNodePtr node, std::vector<T>& v) {
v.resize(node->size());
for(unsigned i=0;i<node->size();++i)
v[i] = node->readAt<T>(i);
return true;
}
template <typename T>
bool operator >> (const OTMLNode& node, std::list<T>& v) {
for(unsigned i=0;i<node.size();++i)
v.push_back(node.readAt<T>(i));
template<typename T>
bool from_otmlnode(OTMLNodePtr node, std::list<T>& v) {
for(unsigned i=0;i<node->size();++i)
v.push_back(node->readAt<T>(i));
return true;
}
template <typename K, typename T>
bool operator >> (const OTMLNode& node, std::map<K, T>& m) {
for(int i=0;i<node.size();++i) {
bool from_otmlnode(OTMLNodePtr node, std::map<K, T>& m) {
for(int i=0;i<node->size();++i) {
K k;
if(!safe_convert(node.at(i)->tag(), k))
if(!aux::cast(node->at(i)->tag(), k))
return false;
m[k] = node.at(i)->read<T>();
m[k] = node->at(i)->read<T>();
}
return true;
}
// write operators
template <typename T>
bool operator << (OTMLNode& node, const T& v) {
std::string out;
if(!safe_convert(v, out))
return false;
node.setValue(out);
return true;
// templates for casting a type to a node
template<typename T>
void to_otmlnode(OTMLNodePtr node, const T& v) {
node->setValue(aux::unsafe_cast<std::string>(v));
}
template <typename T>
bool operator << (OTMLNode& node, const std::vector<T>& v) {
for(unsigned i=0;i<v.size();++i)
node.createNode()->write(v[i]);
return true;
template<typename T>
void to_otmlnode(OTMLNodePtr node, const std::vector<T>& v) {
for(unsigned i=0;i<v.size();++i) {
OTMLNodePtr newNode(new OTMLNode);
newNode->write(v[i]);
node->addChild(newNode);
}
}
template <typename T>
bool operator << (OTMLNode& node, const std::list<T>& v) {
for(unsigned i=0;i<v.size();++i)
node.createNode()->write(v[i]);
return true;
template<typename T>
void to_otmlnode(OTMLNodePtr node, const std::list<T>& v) {
for(unsigned i=0;i<v.size();++i) {
OTMLNodePtr newNode(new OTMLNode);
newNode->write(v[i]);
node->addChild(newNode);
}
}
template <typename K, typename T>
bool operator << (OTMLNode& node, const std::map<K, T>& m) {
typename std::map<K, T>::const_iterator it;
for(it = m.begin(); it != m.end(); ++it) {
std::string k;
if(!safe_convert(it->first, k))
return false;
node.createNode(k)->write(it->second);
void to_otmlnode(OTMLNodePtr node, const std::map<K, T>& m) {
for(auto it = m.begin(); it != m.end(); ++it) {
std::string k = aux::unsafe_cast<std::string>(it->first);
OTMLNodePtr newNode(new OTMLNode);
newNode->setTag(k);
newNode->setUnique();
newNode->write(it->second);
node->addChild(newNode);
}
return true;
}
#include "otmlnodeext.h"
#endif // OTMLNODE_H
#endif

View File

@@ -1,27 +1,3 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef OTMLNODEEXT_H
#define OTMLNODEEXT_H
@@ -30,55 +6,41 @@
#include <util/rect.h>
#include <util/size.h>
inline bool operator>>(const OTMLNode& node, Color& color)
{
int r, g, b, a;
if(node.readAt(0, &r) &&
node.readAt(1, &g) &&
node.readAt(2, &b)) {
a = 255;
node.readAt(3, &a);
color.setRGBA(r,g,b,a);
return true;
}
return false;
}
//inline bool from_otmlnode(const OTMLNodePtr& node, Color& color)
//{
//int r, g, b, a;
//r = node->readAt<int>(0);
//g = node->readAt<int>(1);
//b = node->readAt<int>(2);
//a = 255;
//if(node->hasChild(3))
//a = node->readAt<int>(3);
//return true;
//}
template <class T>
inline bool operator>>(const OTMLNode& node, TPoint<T>& point)
{
T x, y;
if(node.readAt(0, &x) && node.readAt(1, &y)) {
point.x = x;
point.y = y;
return true;
}
return false;
}
//template <class T>
//bool from_otmlnode(const OTMLNodePtr& node, TPoint<T>& point)
//{
//point.x = node->readAt<T>(0);
//point.y = node->readAt<T>(1);
//return true;
//}
template <class T>
inline bool operator>>(const OTMLNode& node, TSize<T>& size)
{
T w, h;
if(node.readAt(0, &w) && node.readAt(1, &h)) {
size.setSize(w, h);
return true;
}
return false;
}
//template <class T>
//bool from_otmlnode(const OTMLNodePtr& node, TSize<T>& size)
//{
//size.setSize(node->readAt<T>(0), node->readAt<T>(1));
//return true;
//}
template <class T>
inline bool operator>>(const OTMLNode& node, TRect<T>& rect)
{
T x, y, width, height;
if(node.readAt(0, &x) &&
node.readAt(1, &y) &&
node.readAt(2, &width) &&
node.readAt(3, &height)) {
rect.setRect(x, y, width, height);
return true;
}
return false;
}
//template <class T>
//bool from_otmlnode(const OTMLNodePtr& node, TRect<T>& rect)
//{
//rect.setRect(node->readAt<int>(0),
//node->readAt<int>(1),
//node->readAt<int>(2),
//node->readAt<int>(3));
//return true;
//}
#endif // OTMLNODEEXT_H
#endif

View File

@@ -1,259 +1,160 @@
#include "otmlparser.h"
#include "otmlnode.h"
#include "otmldocument.h"
#include <boost/algorithm/string.hpp>
OTMLParser::OTMLParser(std::istream& in, std::string what) :
m_currentDepth(0), m_currentLine(0),
m_rootNode(new OTMLNode(what)), m_currentParent(m_rootNode), m_previousNode(0),
m_in(in)
OTMLParser::OTMLParser(OTMLDocumentPtr doc, std::istream& in) :
currentDepth(0), currentLine(0),
doc(doc), currentParent(doc), previousNode(0),
in(in)
{
parse();
}
OTMLParser::~OTMLParser()
{
delete m_rootNode;
}
void OTMLParser::throwError(const std::string& message, int line)
{
std::stringstream ss;
ss << "OTML syntax error";
if(!what().empty())
ss << " in '" << what() << "'";
if(line > 0)
ss << " at line " << line;
ss << ": " << message;
throw OTMLException(ss.str());
}
void OTMLParser::parse()
{
m_rootNode->setTag("document");
if(!in.good())
throw OTMLException(doc, "cannot read from input stream");
while(m_in.good() && !m_in.eof()) {
m_currentLine++;
std::string line;
std::getline(m_in, line);
parseLine(line);
while(!in.eof())
parseLine(getNextLine());
}
std::string OTMLParser::getNextLine()
{
currentLine++;
std::string line;
std::getline(in, line);
return line;
}
int OTMLParser::getLineDepth(const std::string& line, bool multilining)
{
// count number of spaces at the line beginning
std::size_t spaces = 0;
while(line[spaces] == ' ')
spaces++;
// pre calculate depth
int depth = spaces / 2;
if(!multilining || depth <= currentDepth) {
// check the next character is a tab
if(line[spaces] == '\t')
throw OTMLException(doc, "indentation with tabs are not allowed", currentLine);
// must indent every 2 spaces
if(spaces % 2 != 0)
throw OTMLException(doc, "must indent every 2 spaces", currentLine);
}
return depth;
}
void OTMLParser::parseLine(std::string line)
{
// calculate depth
std::size_t numSpaces = line.find_first_not_of(' ');
int depth = getLineDepth(line);
// trim left whitespaces
boost::trim_left(line);
// remove line sides spaces
boost::trim(line);
// skip comment or empty lines
if(line[0] == '#' || line.empty())
// skip empty lines
if(line.empty())
return;
// calculate depth
int depth = 0;
if(numSpaces != std::string::npos)
depth = numSpaces / 2;
// check for syntax error
if(numSpaces != std::string::npos && numSpaces % 2 != 0)
throwError("file must be idented every 2 whitespaces", m_currentLine);
// skip comments
if(boost::starts_with(line, "//"))
return;
// a depth above, change current parent to the previous added node
if(depth == m_currentDepth+1) {
m_currentParent = m_previousNode;
// a depth below, change parent to previus parent
} else if(depth < m_currentDepth) {
for(int i=0;i<m_currentDepth-depth;++i)
m_currentParent = m_currentParent->parent();
// else if it isn't the current depth it's a syntax error
} else if(depth != m_currentDepth) {
throwError("invalid indentation level", m_currentLine);
}
if(depth == currentDepth+1) {
currentParent = previousNode;
// a depth below, change parent to previous parent
} else if(depth < currentDepth) {
for(int i=0;i<currentDepth-depth;++i)
currentParent = currentParent->parent();
// if it isn't the current depth, it's a syntax error
} else if(depth != currentDepth)
throw OTMLException(doc, "invalid indentation depth, are you indenting correctly?", currentLine);
// update current depth
m_currentDepth = depth;
// sets current depth
currentDepth = depth;
// add node
OTMLNode* node = m_currentParent->createNode();
m_previousNode = node;
parseNode(node, line);
// alright, new depth is set, the line is not empty and it isn't a comment
// then it must be a node, so we parse it
parseNode(line);
}
void OTMLParser::parseNode(OTMLNode* node, std::string data)
void OTMLParser::parseNode(const std::string& data)
{
std::string tag;
std::string value;
std::size_t dotsPos = data.find_first_of(':');
// its a node that has a value but no tag
// node that has no tag and may have a value
if(!data.empty() && data[0] == '-') {
value = data.substr(1);
boost::trim(value);
// check if it's value is shortcut for adding a child node
if(dotsPos != std::string::npos && !value.empty() && value[0] != '"') {
OTMLNode* child = node->createNode();
parseNode(child, value);
value.clear();
}
}
// its a node that has tag and possible a value
else if(dotsPos != std::string::npos) {
// node that has tag and possible a value
} else if(dotsPos != std::string::npos) {
tag = data.substr(0, dotsPos);
value = data.substr(dotsPos+1);
}
// its a node that has only a tag
else {
if(data.size() > dotsPos+1)
value = data.substr(dotsPos+1);
// node that has only a tag
} else {
tag = data;
}
// set node tag
boost::trim(tag);
node->setTag(tag);
// set node line
node->setLine(m_currentLine);
// process node value
parseNodeValue(node, value);
}
void OTMLParser::parseNodeValue(OTMLNode* node, std::string value)
{
boost::trim(value);
if(value.empty())
return;
// multiline text scalar
if(value[0] == '|') {
// process multitine values
if(value == "|" || value == "|-" || value == "|+") {
// reads next lines until we can a value below the same depth
std::string multiLineData;
do {
std::string line;
size_t lastPos = m_in.tellg();
std::getline(m_in, line);
// calculate numspaces
std::size_t numSpaces = line.find_first_not_of(' ');
size_t lastPos = in.tellg();
std::string line = getNextLine();
int depth = getLineDepth(line, true);
// depth above current depth, add the text to the multiline
if(numSpaces != std::string::npos && (int)numSpaces >= (m_currentDepth+1)*2) {
if(depth > currentDepth) {
multiLineData += line.substr((currentDepth+1)*2);
// it has contents below the current depth
} else {
// if not empty, its a node
boost::trim(line);
parseTextValue(line);
multiLineData += line + "\n";
}
// if has contents below the current depth, its a node
else if(numSpaces != std::string::npos) {
m_in.seekg(lastPos, std::ios::beg);
break;
}
// else its just a new line
else {
multiLineData += "\n";
}
} while(!m_in.eof());
// determine how to treat last new lines
if(value.length() == 1 || (value.length() == 2 && value[1] == '-')) {
// remove all new lines at the end
while(*multiLineData.rbegin() == '\n')
multiLineData.erase(multiLineData.length()-1, 1);
// keep just one extra line
if(value[0] == '-')
multiLineData.append("\n");
} else if(value.length() > 2 || value[1] != '+')
throwError("invalid multiline identifier", m_currentLine);
node->setValue(multiLineData);
}
// sequence
else if(value[0] == '[') {
std::vector<std::string> tokens;
parseTokens(value.substr(1), tokens);
foreach(std::string& token, tokens) {
OTMLNode* child = node->createNode();
child->setLine(m_currentLine);
parseNodeValue(child, token);
}
}
// inline map
else if(value[0] == '{') {
std::vector<std::string> tokens;
parseTokens(value.substr(1), tokens);
foreach(std::string& token, tokens) {
OTMLNode* child = node->createNode();
parseNode(child, token);
}
}
// text scalar
else {
parseTextValue(value);
node->setValue(value);
}
}
void OTMLParser::parseTextValue(std::string& value)
{
if(value[0] == '"' && value[value.length()-1] == '"') {
value = value.substr(1, value.length()-2);
// escape characters
boost::replace_all(value, "\\\\", "\\");
boost::replace_all(value, "\\\"", "\"");
boost::replace_all(value, "\\n", "\n");
}
}
void OTMLParser::parseTokens(std::string data, std::vector<std::string>& out)
{
bool inQuote = false;
int brackets = 1;
std::string tmp;
uint i = 0;
do {
if(i<data.length()) {
char c = data[i];
tmp += c;
if(c == '"') {
if(!inQuote)
inQuote = true;
else if(data[i-1] != '\\')
inQuote = false;
} else if(!inQuote) {
if(c == '{' || c == '[')
brackets++;
else if(c == '}' || c == ']')
brackets--;
else if(c == ',' && brackets == 1) {
tmp.erase(tmp.length()-1);
boost::trim(tmp);
if(!tmp.empty())
out.push_back(tmp);
tmp.clear();
if(!line.empty()) {
// rewind and break
in.seekg(lastPos, std::ios::beg);
currentLine--;
break;
}
}
multiLineData += "\n";
} while(!in.eof());
if(brackets == 0) {
tmp.erase(tmp.length()-1);
boost::trim(tmp);
if(!tmp.empty())
out.push_back(tmp);
break;
}
}
if(i+1 >= data.length() && !m_in.eof()) {
std::string line;
std::getline(m_in, line);
boost::trim(line);
data += " ";
data += line;
}
++i;
} while(i<data.length());
/* determine how to treat new lines at the end
* | strip all new lines at the end and add just a new one
* |- strip all new lines at the end
* |+ keep all the new lines at the end (the new lines until next node)
*/
if(value == "|" || value == "|-") {
// remove all new lines at the end
int lastPos = multiLineData.length();
while(multiLineData[--lastPos] == '\n')
multiLineData.erase(lastPos, 1);
if(brackets != 0)
throwError("no matching bracket while parsing, did you forget to close one?", m_currentLine);
if(value == "|")
multiLineData.append("\n");
} // else it's |+
value = multiLineData;
}
// create the node
OTMLNodePtr node(new OTMLNode);
node->setUnique(dotsPos != std::string::npos);
node->setTag(tag);
node->setValue(value);
node->setSource(doc->source() + ":" + aux::safe_cast<std::string>(currentLine));
currentParent->addChild(node);
previousNode = node;
}

View File

@@ -1,37 +1,33 @@
#ifndef OTMLPARSER_H
#define OTMLPARSER_H
#include <otml/otmlnode.h>
#include <istream>
#include "otmldeclarations.h"
class OTMLParser
{
public:
OTMLParser(std::istream& in, std::string what = "");
~OTMLParser();
OTMLParser(OTMLDocumentPtr doc, std::istream& in);
OTMLNode* getDocument() const { return m_rootNode; }
std::string what() { return m_rootNode->what(); }
void throwError(const std::string& message, int line);
protected:
/// Parse the entire document
void parse();
void parseLine(std::string line);
void parseNode(OTMLNode* node, std::string data);
void parseNodeValue(OTMLNode* node, std::string value);
void parseTextValue(std::string& value);
void parseTokens(std::string data, std::vector<std::string>& out);
private:
int m_currentDepth;
int m_currentLine;
OTMLNode* m_rootNode;
OTMLNode* m_currentParent;
OTMLNode* m_previousNode;
std::istream& m_in;
/// Retrieve next line from the input stream
std::string getNextLine();
/// Counts depth of a line (every 2 spaces increments one depth)
int getLineDepth(const std::string& line, bool multilining = false);
/// Parse each line of the input stream
void parseLine(std::string line);
/// Parse nodes tag and value
void parseNode(const std::string& data);
int currentDepth;
int currentLine;
OTMLDocumentPtr doc;
OTMLNodePtr currentParent;
OTMLNodePtr previousNode;
std::istream& in;
};
#endif // OTMLPARSER_H
#endif

6
src/framework/pch.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef PCH_H
#define PCH_H
#include "global.h"
#endif

View File

@@ -1,645 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <global.h>
#include <core/platform.h>
#include <core/engine.h>
#include <windows.h>
#include <dir.h>
#include <physfs.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
struct Win32PlatformPrivate {
HWND window;
HINSTANCE instance;
HDC hdc;
HGLRC hrc;
std::string appName;
int x, y;
int width, height;
int minWidth, minHeight;
bool focused, visible, maximized;
std::map<int, uchar> keyMap;
} win32;
void Platform::init(const char *appName)
{
// seend random numbers
std::srand(std::time(NULL));
win32.appName = appName;
win32.instance = GetModuleHandle(NULL);
// setup keys
win32.keyMap[0x30] = KC_1;
win32.keyMap[0x31] = KC_2;
win32.keyMap[0x32] = KC_3;
win32.keyMap[0x33] = KC_4;
win32.keyMap[0x34] = KC_5;
win32.keyMap[0x35] = KC_6;
win32.keyMap[0x36] = KC_7;
win32.keyMap[0x37] = KC_8;
win32.keyMap[0x38] = KC_9;
win32.keyMap[0x39] = KC_0;
win32.keyMap[VK_BACK] = KC_BACK;
win32.keyMap[VK_OEM_MINUS] = KC_MINUS;
win32.keyMap[VK_OEM_PLUS] = KC_EQUALS;
win32.keyMap[VK_SPACE] = KC_SPACE;
win32.keyMap[VK_OEM_COMMA] = KC_COMMA;
win32.keyMap[VK_OEM_PERIOD] = KC_PERIOD;
//win32.keyMap[VK_OEM_102] = KC_BACKSLASH;
//win32.keyMap[XK_slash] = KC_SLASH;
//win32.keyMap[XK_bracketleft] = KC_LBRACKET;
//win32.keyMap[XK_bracketright] = KC_RBRACKET;
win32.keyMap[VK_ESCAPE] = KC_ESCAPE;
win32.keyMap[VK_CAPITAL] = KC_CAPITAL;
win32.keyMap[VK_TAB] = KC_TAB;
win32.keyMap[VK_RETURN] = KC_RETURN;
win32.keyMap[VK_LCONTROL] = KC_LCONTROL;
win32.keyMap[VK_RCONTROL] = KC_RCONTROL;
//win32.keyMap[XK_colon] = KC_COLON;
//win32.keyMap[XK_semicolon] = KC_SEMICOLON;
//win32.keyMap[XK_apostrophe] = KC_APOSTROPHE;
//win32.keyMap[XK_grave] = KC_GRAVE;
win32.keyMap[0x41] = KC_A;
win32.keyMap[0x42] = KC_B;
win32.keyMap[0x43] = KC_C;
win32.keyMap[0x44] = KC_D;
win32.keyMap[0x45] = KC_E;
win32.keyMap[0x46] = KC_F;
win32.keyMap[0x47] = KC_G;
win32.keyMap[0x48] = KC_H;
win32.keyMap[0x49] = KC_I;
win32.keyMap[0x4A] = KC_J;
win32.keyMap[0x4B] = KC_K;
win32.keyMap[0x4C] = KC_L;
win32.keyMap[0x4D] = KC_M;
win32.keyMap[0x4E] = KC_N;
win32.keyMap[0x4F] = KC_O;
win32.keyMap[0x50] = KC_P;
win32.keyMap[0x51] = KC_Q;
win32.keyMap[0x52] = KC_R;
win32.keyMap[0x53] = KC_S;
win32.keyMap[0x54] = KC_T;
win32.keyMap[0x55] = KC_U;
win32.keyMap[0x56] = KC_V;
win32.keyMap[0x57] = KC_W;
win32.keyMap[0x58] = KC_X;
win32.keyMap[0x59] = KC_Y;
win32.keyMap[0x5A] = KC_Z;
win32.keyMap[VK_F1] = KC_F1;
win32.keyMap[VK_F2] = KC_F2;
win32.keyMap[VK_F3] = KC_F3;
win32.keyMap[VK_F4] = KC_F4;
win32.keyMap[VK_F5] = KC_F5;
win32.keyMap[VK_F6] = KC_F6;
win32.keyMap[VK_F7] = KC_F7;
win32.keyMap[VK_F8] = KC_F8;
win32.keyMap[VK_F9] = KC_F9;
win32.keyMap[VK_F10] = KC_F10;
win32.keyMap[VK_F11] = KC_F11;
win32.keyMap[VK_F12] = KC_F12;
win32.keyMap[VK_F13] = KC_F13;
win32.keyMap[VK_F14] = KC_F14;
win32.keyMap[VK_F15] = KC_F15;
// keypad
win32.keyMap[VK_NUMPAD0] = KC_NUMPAD0;
win32.keyMap[VK_NUMPAD1] = KC_NUMPAD1;
win32.keyMap[VK_NUMPAD2] = KC_NUMPAD2;
win32.keyMap[VK_NUMPAD3] = KC_NUMPAD3;
win32.keyMap[VK_NUMPAD4] = KC_NUMPAD4;
win32.keyMap[VK_NUMPAD5] = KC_NUMPAD5;
win32.keyMap[VK_NUMPAD6] = KC_NUMPAD6;
win32.keyMap[VK_NUMPAD7] = KC_NUMPAD7;
win32.keyMap[VK_NUMPAD8] = KC_NUMPAD8;
win32.keyMap[VK_NUMPAD9] = KC_NUMPAD9;
win32.keyMap[VK_ADD] = KC_ADD;
win32.keyMap[VK_SUBTRACT] = KC_SUBTRACT;
win32.keyMap[VK_DECIMAL] = KC_DECIMAL;
//win32.keyMap[XK_KP_Equal] = KC_NUMPADEQUALS;
win32.keyMap[VK_DIVIDE] = KC_DIVIDE;
win32.keyMap[VK_MULTIPLY] = KC_MULTIPLY;
win32.keyMap[VK_SEPARATOR] = KC_NUMPADENTER;
// keypad with numlock off
//win32.keyMap[XK_KP_Home] = KC_NUMPAD7;
//win32.keyMap[XK_KP_Up] = KC_NUMPAD8;
//win32.keyMap[XK_KP_Page_Up] = KC_NUMPAD9;
//win32.keyMap[XK_KP_Left] = KC_NUMPAD4;
//win32.keyMap[XK_KP_Begin] = KC_NUMPAD5;
//win32.keyMap[XK_KP_Right] = KC_NUMPAD6;
//win32.keyMap[XK_KP_End] = KC_NUMPAD1;
//win32.keyMap[XK_KP_Down] = KC_NUMPAD2;
//win32.keyMap[XK_KP_Page_Down] = KC_NUMPAD3;
//win32.keyMap[XK_KP_Insert] = KC_NUMPAD0;
//win32.keyMap[XK_KP_Delete] = KC_DECIMAL;
win32.keyMap[VK_UP] = KC_UP;
win32.keyMap[VK_DOWN] = KC_DOWN;
win32.keyMap[VK_LEFT] = KC_LEFT;
win32.keyMap[VK_RIGHT] = KC_RIGHT;
win32.keyMap[VK_PRIOR] = KC_PGUP;
win32.keyMap[VK_NEXT] = KC_PGDOWN;
win32.keyMap[VK_HOME] = KC_HOME;
win32.keyMap[VK_END] = KC_END;
win32.keyMap[VK_NUMLOCK] = KC_NUMLOCK;
win32.keyMap[VK_SNAPSHOT] = KC_SYSRQ;
win32.keyMap[VK_SCROLL] = KC_SCROLL;
win32.keyMap[VK_PAUSE] = KC_PAUSE;
win32.keyMap[VK_RSHIFT] = KC_RSHIFT;
win32.keyMap[VK_LSHIFT] = KC_LSHIFT;
win32.keyMap[VK_RMENU] = KC_RALT;
win32.keyMap[VK_LMENU] = KC_LALT;
win32.keyMap[VK_INSERT] = KC_INSERT;
win32.keyMap[VK_DELETE] = KC_DELETE;
win32.keyMap[VK_LWIN] = KC_LWIN;
win32.keyMap[VK_RWIN] = KC_RWIN;
win32.keyMap[VK_APPS] = KC_APPS;
// win class
WNDCLASSA wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC)WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = win32.instance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = win32.appName.c_str(); // Set The Class Name
if(!RegisterClassA(&wc))
logFatal("FATAL ERROR: Failed to register the window class.");
// force first tick
Platform::getTicks();
}
void Platform::terminate()
{
if(win32.window) {
destroyWindow();
win32.window = NULL;
}
if(win32.instance) {
if(!UnregisterClassA(win32.appName.c_str(), win32.instance))
logError("ERROR: Unregister class failed.");
win32.instance = NULL;
}
}
void Platform::poll()
{
MSG msg;
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int Platform::getTicks()
{
static ulong firstTick = 0;
if(!firstTick)
firstTick = GetTickCount();
return (uint32_t)(GetTickCount() - firstTick);
}
void Platform::sleep(ulong miliseconds)
{
Sleep(miliseconds);
}
bool Platform::createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized)
{
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
win32.x = x;
win32.y = y;
win32.width = width;
win32.height = height;
win32.minWidth = minWidth;
win32.minHeight = minHeight;
win32.maximized = maximized;
RECT windowRect = {x, y, x + width, y + height};
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
win32.window = CreateWindowExA(dwExStyle, // Extended Style For The Window
win32.appName.c_str(), // Class Name
win32.appName.c_str(), // Window Title
dwStyle, // Required Window Style
windowRect.left, // Window X Position
windowRect.top, // Window Y Position
windowRect.right - windowRect.left, // Calculate Window Width
windowRect.bottom - windowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
win32.instance, // Instance
NULL);
if(!win32.window) {
terminate();
logFatal("FATAL ERROR: Window creation error.");
return false;
}
GLuint pixelFormat;
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
32, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if(!(win32.hdc = GetDC(win32.window))) {
terminate();
logFatal("FATAL ERROR: Can't Create A GL Device Context.");
return false;
}
if(!(pixelFormat = ChoosePixelFormat(win32.hdc, &pfd))) {
terminate();
logFatal("FATAL ERROR: Can't Find A Suitable PixelFormat.");
return false;
}
if(!SetPixelFormat(win32.hdc, pixelFormat, &pfd)) {
terminate();
logFatal("FATAL ERROR: Can't Set The PixelFormat.");
return false;
}
if(!(win32.hrc = wglCreateContext(win32.hdc))) {
terminate();
logFatal("FATAL ERROR: Can't Create A GL Rendering Context.");
return false;
}
if(!wglMakeCurrent(win32.hdc, win32.hrc)) {
terminate();
logFatal("FATAL ERROR: Can't Activate The GL Rendering Context.");
return false;
}
return true;
}
void Platform::destroyWindow()
{
if(win32.hrc) {
if(!wglMakeCurrent(NULL, NULL))
logError("ERROR: Release Of DC And RC Failed.");
if(!wglDeleteContext(win32.hrc))
logError("ERROR: Release Rendering Context Failed.");
win32.hrc = NULL;
}
if(win32.hdc) {
if(!ReleaseDC(win32.window, win32.hdc))
logError("ERROR: Release Device Context Failed.");
win32.hdc = NULL;
}
if(win32.window) {
if(!DestroyWindow(win32.window))
logError("ERROR: Destroy window failed.");
win32.window = NULL;
}
}
void Platform::showWindow()
{
if(win32.maximized)
ShowWindow(win32.window, SW_MAXIMIZE);
else
ShowWindow(win32.window, SW_SHOW);
}
void Platform::setWindowTitle(const char *title)
{
SetWindowTextA(win32.window, title);
}
void *Platform::getExtensionProcAddress(const char *ext)
{
return (void*)wglGetProcAddress(ext);
}
bool Platform::isExtensionSupported(const char *ext)
{
typedef const char* _wglGetExtensionsStringARB(HDC hdc);
_wglGetExtensionsStringARB *wglGetExtensionsStringARB = (_wglGetExtensionsStringARB*)getExtensionProcAddress("wglGetExtensionsStringARB");
const char *exts = wglGetExtensionsStringARB(win32.hdc);
if(strstr(exts, ext))
return true;
return false;
}
const char *Platform::getClipboardText()
{
const char *text = "";
if(OpenClipboard(NULL)) {
text = (const char*)GetClipboardData(CF_TEXT);
CloseClipboard();
}
return text;
}
void Platform::setClipboardText(const char *text)
{
int textLenght = strlen(text);
HANDLE hData = new HANDLE[textLenght + 1];
memcpy(hData, text, textLenght + 1);
if(OpenClipboard(NULL)) {
EmptyClipboard();
SetClipboardData(CF_TEXT, hData);
CloseClipboard();
}
}
void Platform::hideMouseCursor()
{
ShowCursor(false);
}
void Platform::showMouseCursor()
{
ShowCursor(true);
}
void Platform::setVsync(bool enable /*= true*/)
{
typedef GLint (*glSwapIntervalProc)(GLint);
glSwapIntervalProc glSwapInterval = NULL;
if(isExtensionSupported("WGL_EXT_swap_control"))
glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("wglSwapIntervalEXT");
if(glSwapInterval)
glSwapInterval(enable ? 1 : 0);
}
void Platform::swapBuffers()
{
SwapBuffers(win32.hdc);
}
bool Platform::isWindowFocused()
{
return win32.focused;
}
bool Platform::isWindowVisible()
{
return win32.visible;
}
int Platform::getWindowX()
{
return win32.x;
}
int Platform::getWindowY()
{
return win32.y;
}
int Platform::getWindowWidth()
{
return win32.width;
}
int Platform::getWindowHeight()
{
return win32.height;
}
int Platform::getDisplayWidth()
{
return GetSystemMetrics(SM_CXSCREEN);
}
int Platform::getDisplayHeight()
{
return GetSystemMetrics(SM_CYSCREEN);
}
bool Platform::isWindowMaximized()
{
return win32.maximized;
}
std::string Platform::getAppUserDir()
{
std::stringstream sdir;
sdir << PHYSFS_getUserDir() << "/." << win32.appName << "/";
if((mkdir(sdir.str().c_str()) != 0) && (errno != EEXIST))
flogError("ERROR: Couldn't create directory for saving configuration file. (%s)", sdir.str().c_str());
return sdir.str();
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int lastX, lastY;
static InputEvent inputEvent;
switch(uMsg)
{
case WM_ACTIVATE:
{
win32.focused = !(wParam == WA_INACTIVE);
break;
}
case WM_CHAR:
{
if(wParam >= 32 && wParam <= 255) {
inputEvent.type = EV_TEXT_ENTER;
inputEvent.keychar = wParam;
inputEvent.keycode = KC_UNKNOWN;
g_engine.onInputEvent(inputEvent);
}
break;
}
case WM_CLOSE:
{
g_engine.onClose();
break;
}
case WM_GETMINMAXINFO:
{
MINMAXINFO *minMax = (MINMAXINFO*)lParam;
minMax->ptMinTrackSize.x = win32.minWidth;
minMax->ptMinTrackSize.y = win32.minHeight;
break;
}
case WM_KEYDOWN:
case WM_KEYUP:
{
if(win32.keyMap.find(wParam) != win32.keyMap.end()) {
inputEvent.type = uMsg == WM_KEYDOWN ? EV_KEY_DOWN : EV_KEY_UP;
inputEvent.ctrl = HIWORD(GetKeyState(VK_CONTROL)) == 0 ? false : true;
inputEvent.alt = HIWORD(GetKeyState(VK_MENU)) == 0 ? false : true;
inputEvent.shift = HIWORD(GetKeyState(VK_SHIFT)) == 0 ? false : true;
inputEvent.keycode = win32.keyMap[wParam];
}
g_engine.onInputEvent(inputEvent);
break;
}
case WM_LBUTTONDOWN:
{
inputEvent.type = EV_MOUSE_LDOWN;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_LBUTTONUP:
{
inputEvent.type = EV_MOUSE_LUP;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_MBUTTONDOWN:
{
inputEvent.type = EV_MOUSE_MDOWN;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_MBUTTONUP:
{
inputEvent.type = EV_MOUSE_MUP;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_RBUTTONDOWN:
{
inputEvent.type = EV_MOUSE_RDOWN;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_RBUTTONUP:
{
inputEvent.type = EV_MOUSE_RUP;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_MOUSEMOVE:
{
inputEvent.type = EV_MOUSE_MOVE;
Point newMousePos(LOWORD(lParam), HIWORD(lParam));
inputEvent.mouseMoved = newMousePos - inputEvent.mousePos;
inputEvent.mousePos = newMousePos;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_MOUSEWHEEL:
{
inputEvent.type = HIWORD(wParam) > 0 ? EV_MOUSE_WHEEL_UP : EV_MOUSE_WHEEL_DOWN;
g_engine.onInputEvent(inputEvent);
break;
}
case WM_MOVE:
{
lastX = win32.x;
lastY = win32.y;
win32.x = LOWORD(lParam);
win32.y = HIWORD(lParam);
break;
}
case WM_SIZE:
{
switch(wParam)
{
case SIZE_MAXIMIZED:
win32.x = lastX;
win32.y = lastY;
win32.maximized = true;
break;
case SIZE_RESTORED:
win32.maximized = false;
break;
}
win32.visible = !(wParam == SIZE_MINIMIZED);
if(!win32.maximized) {
win32.width = LOWORD(lParam);
win32.height = HIWORD(lParam);
}
g_engine.onResize(Size(LOWORD(lParam), HIWORD(lParam)));
break;
}
default:
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
return 0;
}

View File

@@ -1,19 +0,0 @@
#ifndef LUADECLARATIONS_H
#define LUADECLARATIONS_H
#include <global.h>
class LuaInterface;
class LuaState;
class LuaValue;
class LuaObject;
typedef std::function<int(LuaState*)> LuaCppFunction;
typedef std::shared_ptr<LuaCppFunction> LuaCppFunctionPtr;
typedef std::shared_ptr<LuaValue> LuaValuePtr;
typedef std::shared_ptr<LuaObject> LuaObjectPtr;
typedef std::vector<LuaValuePtr> LuaValueList;
#endif // LUADECLARATIONS_H

View File

@@ -1,39 +0,0 @@
#include "luaexception.h"
#include "luainterface.h"
LuaException::LuaException(const std::string& error, int traceLevel)
{
generateLuaErrorMessage(error, traceLevel);
}
void LuaException::generateLuaErrorMessage(const std::string& error, int traceLevel)
{
// clear stack, to prevent further errors
g_lua.clearStack();
// append trace level to error message
if(traceLevel >= 0)
m_what = make_string("LUA ERROR: ", g_lua.getTraceback(error, traceLevel));
else
m_what = make_string("LUA ERROR: ", error);
}
LuaBadNumberOfArgumentsException::LuaBadNumberOfArgumentsException(int expected, int got)
{
std::string error = "attempt to call a function with wrong number of arguments";
if(expected >= 0 && got >= 0)
error = make_string(error, " (expected ", expected, ", but got ", got, ")");
generateLuaErrorMessage(error, 1);
}
LuaBadTypeConversinException::LuaBadTypeConversinException(const std::string& typeName)
{
std::string error = make_string("attempt to convert ", typeName, " to a lua value");
generateLuaErrorMessage(error, 0);
}
LuaBadValueCastException::LuaBadValueCastException(const LuaValuePtr& value, const std::string& typeName)
{
std::string error = make_string("attempt to cast a ", value->getTypeName(), " lua value to ", typeName);
generateLuaErrorMessage(error, 0);
}

View File

@@ -1,54 +0,0 @@
#include "luainterface.h"
#include <core/engine.h>
#include <ui/ui.h>
#include "../../protocollogin.h"
void LuaInterface::registerFunctions()
{
g_lua.bindGlobalFunction("exit", std::bind(&Engine::stop, &g_engine));
g_lua.bindGlobalFunction("loadUI", std::bind(&UILoader::loadFromFile, &g_uiLoader, std::placeholders::_1, std::placeholders::_2));
g_lua.setGlobal("rootUI", safe_to_luavalue(UIContainer::getRoot()));
g_lua.registerClass("UIElement");
g_lua.bindClassStaticFunction("UIElement", "new", &UIElement::create);
g_lua.bindClassMemberField("UIElement", "id", &UIElement::getId, &UIElement::setId);
g_lua.bindClassMemberField("UIElement", "enabled", &UIElement::isEnabled, &UIElement::setEnabled);
g_lua.bindClassMemberField("UIElement", "visible", &UIElement::isVisible, &UIElement::setVisible);
g_lua.bindClassMemberField("UIElement", "focused", &UIElement::isFocused, &UIElement::setFocused);
g_lua.bindClassMemberField("UIElement", "width", &UIElement::getWidth, &UIElement::setWidth);
g_lua.bindClassMemberField("UIElement", "height", &UIElement::getHeight, &UIElement::setHeight);
g_lua.bindClassMemberField("UIElement", "parent", &UIElement::getParent, &UIElement::setParent);
g_lua.bindClassMemberFunction("UIElement", "setLocked", &UIElement::setLocked);
//g_lua.bindClassMemberFunction("UIElement", "setMargin", &UIElement::luaSetMargin);
g_lua.bindClassMemberFunction("UIElement", "destroy", &UIElement::destroyLater);
g_lua.bindClassMemberFunction("UIElement", "centerIn", &UIElement::centerIn);
g_lua.bindClassMemberFunction("UIElement", "addAnchor", &UIElement::addAnchor);
g_lua.registerClass("UIContainer", "UIElement");
g_lua.bindClassStaticFunction("UIContainer", "create", &UIContainer::create);
g_lua.bindClassMemberFunction("UIContainer", "getChild", &UIContainer::getChildById);
//g_lua.bindClassMemberFunction("UIContainer", "children", &UIContainer::getChildren);
g_lua.bindClassMemberFunction("UIContainer", "addChild", &UIContainer::addChild);
g_lua.registerClass("UILabel", "UIElement");
g_lua.bindClassStaticFunction("UILabel", "create", &UILabel::create);
g_lua.bindClassMemberField("UILabel", "text", &UILabel::getText, &UILabel::setText);
g_lua.registerClass("UIButton", "UIElement");
g_lua.bindClassStaticFunction("UIButton", "create", &UIButton::create);
g_lua.bindClassMemberField("UIButton", "text", &UIButton::getText, &UIButton::setText);
g_lua.registerClass("UITextEdit", "UIElement");
g_lua.bindClassStaticFunction("UITextEdit", "create", &UITextEdit::create);
g_lua.bindClassMemberField("UITextEdit", "text", &UITextEdit::getText, &UITextEdit::setText);
g_lua.registerClass("UIWindow", "UIContainer");
g_lua.bindClassStaticFunction("UIWindow", "create", &UIWindow::create);
g_lua.bindClassMemberField("UIWindow", "title", &UIWindow::getTitle, &UIWindow::setTitle);
g_lua.registerClass("Protocol");
g_lua.registerClass("ProtocolLogin", "Protocol");
g_lua.bindClassStaticFunction("ProtocolLogin", "create", &ProtocolLogin::create);
g_lua.bindClassMemberFunction("ProtocolLogin", "login", &ProtocolLogin::login);
g_lua.bindClassMemberFunction("ProtocolLogin", "cancel", &ProtocolLogin::cancel);
}

View File

@@ -1,235 +0,0 @@
#include "luainterface.h"
LuaInterface g_lua;
void LuaInterface::init()
{
// installs a custom loader that will load scripts correctlyfrom lua require keyword
auto loaders = getGlobal("package")->getField("loaders");
loaders->setTable(loaders->getTableSize() + 1, createCppFunction(&LuaInterface::luaScriptLoader));
// register LuaObject, the class that all other classes will derive from
registerClass("LuaObject");
bindClassMemberGetField("LuaObject", "use_count", &LuaObject::getUseCount);
// register other custom classes and functions
registerFunctions();
}
void LuaInterface::terminate()
{
collectGarbage();
}
LuaValuePtr LuaInterface::loadFunction(const std::string& buffer, const std::string& source)
{
LuaValuePtr func;
// get the function contained the buffer
if(boost::starts_with(buffer, "function")) {
runBuffer(make_string("__func = ", buffer), source);
func = getGlobal("__func");
setGlobal("__func", createNil());
// use the buffer as a function
} else
func = loadBuffer(buffer, source);
return func;
}
LuaValuePtr LuaInterface::evaluateExpression(const std::string& expression, const std::string& source)
{
// run the expression
std::string buffer = make_string("__exp = (", expression, ")");
runBuffer(buffer, source);
// get the expression result
LuaValuePtr res = getGlobal("__exp");
setGlobal("__exp", createNil());
return res;
}
LuaValuePtr LuaInterface::createEnvironment()
{
// creates a new environment table and
// redirect the new environment to the current thread global environment (aka _G)
// this allows to access global variables from _G in the new environment
// and prevents new variables on the new environment to be set on the global environment
auto env = createTable();
auto mt = createTable();
mt->setField("__index", getGlobalEnvironment());
env->setMetatable(mt);
return env;
}
void LuaInterface::registerClass(const std::string& className, const std::string& baseClass)
{
// create the class table (that it is also the class methods table)
auto klass = createTable(className);
// create the class metatable
auto klass_mt = createTable(className + "_mt");
// create the class fieldmethods table
auto klass_fieldmethods = createTable(className + "_fieldmethods");
// set metatable metamethods
klass_mt->setField("__index", &LuaInterface::luaObjectGetEvent);
klass_mt->setField("__newindex", &LuaInterface::luaObjectSetEvent);
klass_mt->setField("__eq", &LuaInterface::luaObjectEqualEvent);
klass_mt->setField("__gc", &LuaInterface::luaObjectCollectEvent);
klass_mt->setField("methods", klass);
klass_mt->setField("fieldmethods", klass_fieldmethods);
// redirect methods and fieldmethods to the base class ones
if(!className.empty() && className != "LuaObject") {
/* the following code what create classes hierarchy for lua, by reproducing the following:
* DerivedClass = { __index = BaseClass }
* DerivedClass_fieldmethods = { __index = BaseClass_methods }
*/
// redirect the class methods to the base methods
auto redirect = createTable();
auto tmp = getGlobal(baseClass);
redirect->setField("__index", tmp);
klass->setMetatable(redirect);
// redirect the class fieldmethods to the base fieldmethods
redirect = createTable();
tmp = getGlobal(baseClass + "_fieldmethods");
redirect->setField("__index", tmp);
klass_fieldmethods->setMetatable(redirect);
}
}
void LuaInterface::registerClassStaticFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function)
{
registerClassMemberFunction(className, functionName, function);
}
void LuaInterface::registerClassMemberFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function)
{
auto table = getGlobal(className);
table->setField(functionName, function);
}
void LuaInterface::registerClassMemberField(const std::string& className, const std::string& field, const LuaCppFunction& getFunction, const LuaCppFunction& setFunction)
{
auto fieldmethods = getGlobal(className + "_fieldmethods");
auto methods = getGlobal(className);
auto capitalized = field;
capitalized[0] = std::toupper(field[0]);
if(getFunction) {
auto func = createCppFunction(getFunction);
fieldmethods->setField(make_string("get_", field), func);
methods->setField(make_string("get", capitalized), func);
}
if(setFunction) {
auto func = createCppFunction(setFunction);
fieldmethods->setField(make_string("set_", field), func);
methods->setField(make_string("set", capitalized), func);
}
}
void LuaInterface::registerGlobalFunction(const std::string& functionName, const LuaCppFunction& function)
{
setGlobal(functionName, createCppFunction(function));
}
int LuaInterface::luaScriptLoader(LuaState* lua)
{
// load the script as a function
auto fileName = make_string(lua->popValue()->toString(), + ".lua");
auto scriptMain = lua->loadScript(fileName);
lua->pushValue(scriptMain);
return 1;
}
int LuaInterface::luaObjectGetEvent(LuaState* lua)
{
// metamethod that will retrive fields values (that include functions) from the object
auto key = lua->popValue()->toString();
auto objRef = lua->popValue();
auto obj = safe_luavalue_cast<LuaObjectPtr>(objRef);
// if a get method for this key exists, calls it
auto get_method = objRef->getMetatable()->getField("fieldmethods")->getField("get_" + key);
if(!get_method->isNil()) {
LuaValueList rets = get_method->call(make_string(obj->getLuaTypeName(), " obj.", key), objRef);
assert(rets.size() == 1);
lua->pushValues(rets);
return 1;
}
// if the field for this key exists, returns it
auto field = obj->getField(key);
if(!field->isNil()) {
lua->pushValue(field);
return 1;
}
// if a method for this key exists, returns it
auto method = objRef->getMetatable()->getField("methods")->getField(key);
lua->pushValue(method);
return 1;
}
int LuaInterface::luaObjectSetEvent(LuaState* lua)
{
// this metamethod is called when setting a field of the object by using the keyword '='
auto value = lua->popValue();
auto key = lua->popValue()->toString();
auto objRef = lua->popValue();
auto obj = safe_luavalue_cast<LuaObjectPtr>(objRef);
// check if a set method for this field exists and call it
auto set_method = objRef->getMetatable()->getField("fieldmethods")->getField("set_" + key);
if(!set_method->isNil()) {
set_method->call(make_string(obj->getLuaTypeName(), " obj.", key, "="), objRef, value);
return 0;
}
// no set method exists, then sets the object table
obj->setField(key, value);
return 0;
}
int LuaInterface::luaObjectEqualEvent(LuaState* lua)
{
// metamethod that will check equality of objects
auto objRef2 = lua->popValue();
auto objRef1 = lua->popValue();
bool ret = false;
// check if obj1 == obj2
if(objRef1->isUserdata() && objRef2->isUserdata()) {
LuaObjectPtr* objPtr1 = static_cast<LuaObjectPtr*>(objRef1->toUserdata());
LuaObjectPtr* objPtr2 = static_cast<LuaObjectPtr*>(objRef2->toUserdata());
assert(objPtr1 && objPtr2);
if(*objPtr1 == *objPtr2)
ret = true;
}
lua->pushBoolean(ret);
return 1;
}
int LuaInterface::luaObjectCollectEvent(LuaState* lua)
{
/* this metamethod is called every two lua garbage collections
* for any LuaObject that have no references left in lua environment
* anymore, thus this creates the possibility of holding an object
* existence by lua
*/
// get object pointer
auto objRef = lua->popValue();
auto objPtr = static_cast<LuaObjectPtr*>(objRef->toUserdata());
assert(objPtr);
// reset pointer to decrease object use count
objPtr->reset();
return 0;
}

View File

@@ -1,73 +0,0 @@
#ifndef LUAINTERFACE_H
#define LUAINTERFACE_H
#include "luabinder.h"
#include "luaobject.h"
class LuaInterface : public LuaState
{
public:
void init();
void terminate();
/// Load a function from buffef into a LuaValue
LuaValuePtr loadFunction(const std::string& buffer, const std::string& source = "lua function buffer");
/// Evaluate a lua expression to a LuaValue
LuaValuePtr evaluateExpression(const std::string& expression, const std::string& source = "lua expression");
LuaValuePtr createEnvironment();
/// Register core script functions
void registerFunctions();
// methods for registring classes and functions
void registerClass(const std::string& className, const std::string& baseClass = "LuaObject");
void registerClassStaticFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function);
void registerClassMemberFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function);
void registerClassMemberField(const std::string& className, const std::string& field, const LuaCppFunction& getFunction, const LuaCppFunction& setFunction);
void registerGlobalFunction(const std::string& functionName, const LuaCppFunction& function);
// methods for binding functions
template<typename F>
void bindClassStaticFunction(const std::string& className, const std::string& functionName, const F& function) {
registerClassStaticFunction(className, functionName, luabinder::bind_fun(function));
}
template<class C, typename F>
void bindClassMemberFunction(const std::string& className, const std::string& functionName, F C::*function) {
registerClassMemberFunction(className, functionName, luabinder::bind_mem_fun(function));
}
template<class C, typename F1, typename F2>
void bindClassMemberField(const std::string& className, const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) {
registerClassMemberField(className, fieldName, luabinder::bind_mem_fun(getFunction), luabinder::bind_mem_fun(setFunction));
}
template<class C, typename F>
void bindClassMemberGetField(const std::string& className, const std::string& fieldName, F C::*getFunction) {
registerClassMemberField(className, fieldName, luabinder::bind_mem_fun(getFunction), LuaCppFunction());
}
template<class C, typename F>
void bindClassMemberSetField(const std::string& className, const std::string& fieldName, F C::*setFunction) {
registerClassMemberField(className, fieldName, LuaCppFunction(), luabinder::bind_mem_fun(setFunction));
}
template<typename F>
void bindGlobalFunction(const std::string& functionName, const F& function) {
registerGlobalFunction(functionName, luabinder::bind_fun(function));
}
private:
// installed lua callbacks by this interface
static int luaScriptLoader(LuaState* lua);
static int luaObjectGetEvent(LuaState* lua);
static int luaObjectSetEvent(LuaState* lua);
static int luaObjectEqualEvent(LuaState* lua);
static int luaObjectCollectEvent(LuaState* lua);
};
extern LuaInterface g_lua;
#endif // LUAINTERFACE_H

View File

@@ -1,24 +0,0 @@
#include "luaobject.h"
#include "luainterface.h"
LuaObject::LuaObject()
{
// creates own table
m_luaTable = g_lua.createTable();
}
LuaObject::~LuaObject()
{
}
LuaValuePtr LuaObject::toLuaValue()
{
// fills a new userdata with a new LuaObject pointer
new(g_lua.newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(shared_from_this());
// set the metatable for the userdata
g_lua.setMetatable(make_string(getLuaTypeName(), "_mt"));
return g_lua.popValue();
}

View File

@@ -1,50 +0,0 @@
#ifndef LUAOBJECT_H
#define LUAOBJECT_H
#include "luavalue.h"
/// LuaObject, all bound classes must be derived from it
class LuaObject : public std::enable_shared_from_this<LuaObject>
{
public:
LuaObject();
virtual ~LuaObject();
/// Call a field of this lua object
template<typename... T>
LuaValueList callField(const std::string& field, const T&... args) {
// note that we must retrive the field from or lua value (to use the __index metamethod)
// so we cannot use our getField here
auto fieldFunc = toLuaValue()->getField(field);
auto funcName = make_string(getLuaTypeName(), ":", field);
return fieldFunc->call(funcName, shared_from_this(), args...);
}
/// Set a field of this lua object
template<typename T>
void setField(const std::string& key, const T& value) {
m_luaTable->setField(key, value);
}
/// Get a field of this lua object
LuaValuePtr getField(const std::string& key) {
return m_luaTable->getField(key);
}
/// Returns current use count by the shared_ptr
int getUseCount() { return shared_from_this().use_count() - 1; }
/// Class name used in lua, must be overridden by derived classes
virtual const char* getLuaTypeName() const = 0;
/** Convert to a lua value for pushing this object into lua stack.
* Each call to this will create a new pointer, thus increasing the use count.*/
LuaValuePtr toLuaValue();
LuaObjectPtr asLuaObject() { return shared_from_this(); }
private:
LuaValuePtr m_luaTable;
};
#endif // LUAOBJECT_H

View File

@@ -1,604 +0,0 @@
#include "luastate.h"
#include "luavalue.h"
#include <lua.hpp>
#include <core/resources.h>
/// Retrive LuaState from L
LuaState* retriveLuaState(lua_State* L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "LuaStateInstance");
auto lua = static_cast<LuaState*>(const_cast<void*>(lua_topointer(L, -1)));
lua_pop(L, 1);
return lua;
}
/// Handle errors by protected lua calls (pcall)
int luaErrorHandler(lua_State* L)
{
// pops the error message
auto lua = retriveLuaState(L);
auto error = lua->popString();
// prevents repeated tracebacks
if(error.find("stack traceback:") != std::string::npos)
error = lua->getTraceback(error, 1);
// pushes the new error message with traceback information
lua->pushString(error);
return 1;
}
/// Handle bound cpp functions callbacks
int luaCppFunctionCallback(lua_State* L)
{
auto lua = retriveLuaState(L);
auto funcPtr = static_cast<LuaCppFunctionPtr*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(funcPtr);
try {
return (*(funcPtr->get()))(lua);
} catch(LuaException &e) {
logError(e.what());
return 0;
}
}
/// Free cpp function pointer when it expires
int luaCppFunctionCallbackPointerExpire(lua_State* L)
{
auto funcPtr = static_cast<LuaCppFunctionPtr*>(lua_touserdata(L, -1));
assert(funcPtr);
funcPtr->reset();
return 0;
}
LuaState::LuaState() : m_collecting(false)
{
// create lua state
L = luaL_newstate();
if(!L)
throw LuaException("failed to create lua state");
// load lua standard libraries
luaL_openlibs(L);
// store this into lua
pushLightUserdata(this);
lua_setfield(L, LUA_REGISTRYINDEX, "LuaStateInstance");
}
LuaState::~LuaState()
{
// close lua state
m_collecting = true;
lua_close(L);
}
void LuaState::collectGarbage()
{
// prevents recursive collects
if(!m_collecting) {
m_collecting = true;
// we must collect two times for __gc metamethod be called on uservalues
for(int i=0;i<2;++i)
lua_gc(L, LUA_GCCOLLECT, 0);
m_collecting = false;
}
}
LuaValuePtr LuaState::loadBuffer(const std::string& buffer, const std::string& source)
{
// load buffer from lua, the "@" means that the source is a file path, thus lua will
// print file paths in tracebacks
int ret = luaL_loadbuffer(L, buffer.c_str(), buffer.length(), ("@" + source).c_str());
if(ret != 0)
throw LuaException(popString(), 0);
return popValue();
}
LuaValuePtr LuaState::loadScript(const std::string &fileName)
{
std::stringstream fin;
if(!g_resources.loadFile(fileName, fin))
throw LuaException(make_string("failed to open lua script ", g_resources.resolvePath(fileName)), 0);
return loadBuffer(fin.str(), g_resources.resolvePath(fileName));
}
void LuaState::runBuffer(const std::string& buffer, const std::string& source)
{
auto main = loadBuffer(buffer, source);
main->call(source);
}
void LuaState::runScript(const std::string& fileName)
{
auto main = loadScript(fileName);
main->call(fileName);
}
int LuaState::safeCall(const std::string& functionName, int numArgs)
{
assert(hasIndex(-numArgs-1));
int previousStackSize = stackSize();
// push error function
int errorFuncIndex = previousStackSize - numArgs;
lua_pushcfunction(L, luaErrorHandler);
insert(errorFuncIndex);
m_callStack.push_front(functionName);
// call the func
int ret = lua_pcall(L, numArgs, LUA_MULTRET, errorFuncIndex);
m_callStack.pop_front();
// pop error func
remove(errorFuncIndex);
if(ret != 0)
throw LuaException(popString(), 0);
// return the number of results
return (stackSize() + numArgs + 1) - previousStackSize;
}
LuaValuePtr LuaState::popValue()
{
assert(hasIndex(-1));
LuaValuePtr value;
int refId = ref();
if(refId < 0) {
// refs < 0 are considered as nil values
value = LuaValuePtr(new LuaValue());
} else {
pushRef(refId);
value = LuaValuePtr(new LuaValue(refId));
pop();
}
return value;
}
LuaValueList LuaState::popValues()
{
int numValues = stackSize();
LuaValueList values(numValues);
for(int i=numValues-1;i>=0;--i)
values[i] = popValue();
return values;
}
void LuaState::pushCppFunction(const LuaCppFunction& func)
{
// create a pointer to func (this pointer will hold the function existence)
new(newUserdata(sizeof(LuaCppFunctionPtr))) LuaCppFunctionPtr(new LuaCppFunction(func));
// set the userdata __gc metamethod
newTable();
lua_pushcfunction(L, luaCppFunctionCallbackPointerExpire);
setField("__gc");
setMetatable();
// pushes the func
lua_pushcclosure(L, luaCppFunctionCallback, 1);
}
void LuaState::pushValue(const LuaValuePtr& value)
{
if(value->isNil())
pushNil();
else
pushRef(value->m_ref);
}
void LuaState::pushValues(const LuaValueList& values)
{
foreach(const LuaValuePtr& value, values)
pushValue(value);
}
std::string LuaState::getTraceback(const std::string& errorMessage, int depth)
{
// get traceback from debug.traceback
getGlobalField("debug");
getField("traceback");
remove(-2);
pushString(errorMessage);
pushInteger(depth);
call(2,1);
std::string luaTraceback = popString();
boost::replace_first(luaTraceback, "stack traceback:", "lua stack traceback:");
std::stringstream ss;
ss << luaTraceback;
// add application call traceback
if(m_callStack.size() > 0) {
ss << "\napplication call stack traceback:";
foreach(const std::string& func, m_callStack)
ss << "\n\t" << func;
}
return ss.str();
}
std::string LuaState::getCurrentSource()
{
std::string path;
int level = 0;
// get the current running script path
LuaValuePtr func;
do {
func = getStackFunction(level);
if(func->isLuaFunction()) {
path = getFunctionSource(func);
break;
}
level++;
} while(!func->isNil());
return path;
}
std::string LuaState::getFunctionSource(LuaValuePtr func)
{
std::string path;
pushValue(func);
lua_Debug ar;
lua_getinfo(L, ">Sn", &ar);
if(ar.source) {
path = ar.source;
path = path.substr(1, std::string::npos);
}
return path;
}
LuaValuePtr LuaState::getGlobalEnvironment()
{
lua_pushthread(L);
getEnv();
remove(-2);
return popValue();
}
void LuaState::setGlobalEnvironment(const LuaValuePtr& env)
{
lua_pushthread(L);
pushValue(env);
setEnv();
pop();
}
LuaValuePtr LuaState::getStackFunction(int level)
{
// get current stack func
lua_Debug ar;
if(lua_getstack(L, level, &ar) == 1) {
lua_getinfo(L, "f", &ar);
return popValue();
}
return createNil();
}
std::string LuaState::typeName(int index)
{
assert(hasIndex(index));
int type = lua_type(L, index);
std::string name = lua_typename(L, type);
return name;
}
int LuaState::ref()
{
return luaL_ref(L, LUA_REGISTRYINDEX);
}
void LuaState::unref(int ref)
{
if(ref > 0)
luaL_unref(L, LUA_REGISTRYINDEX, ref);
}
void LuaState::call(int numArgs, int numRets)
{
assert(hasIndex(-numArgs - 1));
lua_call(L, numArgs, numRets);
}
void LuaState::insert(int index)
{
assert(hasIndex(index));
lua_insert(L, index);
}
void LuaState::remove(int index)
{
assert(hasIndex(index));
lua_remove(L, index);
}
bool LuaState::next(int index)
{
assert(hasIndex(index));
return lua_next(L, index);
}
void LuaState::copy(int index)
{
assert(hasIndex(index));
lua_pushvalue(L, index);
}
void LuaState::setMetatable(int index)
{
assert(hasIndex(index));
lua_setmetatable(L, index);
}
void LuaState::setMetatable(const std::string& name)
{
assert(hasIndex(-1));
getGlobalField(name);
setMetatable();
}
void LuaState::getMetatable(int index)
{
assert(hasIndex(index));
lua_getmetatable(L, index);
}
void LuaState::getField(const std::string& key, int index)
{
assert(hasIndex(index));
lua_getfield(L, index, key.c_str());
}
void LuaState::setField(const std::string& key, int index)
{
assert(hasIndex(index));
lua_setfield(L, index, key.c_str());
}
void LuaState::getEnv(int index)
{
assert(hasIndex(index));
lua_getfenv(L, index);
}
void LuaState::setEnv(int index)
{
assert(hasIndex(index));
assert(lua_setfenv(L, index) == 1);
}
void LuaState::getTable(int index)
{
assert(hasIndex(index));
lua_gettable(L, index);
}
void LuaState::setTable(int index)
{
assert(hasIndex(index));
lua_settable(L, index);
}
void LuaState::getGlobalField(const std::string& key)
{
lua_getglobal(L, key.c_str());
}
void LuaState::setGlobalField(const std::string& key)
{
assert(hasIndex(-1));
lua_setglobal(L, key.c_str());
}
void LuaState::rawGet(int index)
{
assert(hasIndex(index));
lua_rawget(L, index);
}
void LuaState::rawGeti(int n, int index)
{
assert(hasIndex(index));
lua_rawgeti(L, index, n);
}
void LuaState::rawSet(int index)
{
assert(hasIndex(index));
lua_rawset(L, index);
}
void LuaState::rawSeti(int n, int index)
{
assert(hasIndex(index));
lua_rawseti(L, index, n);
}
void LuaState::newTable()
{
lua_newtable(L);
}
void* LuaState::newUserdata(int size)
{
return lua_newuserdata(L, size);
}
void LuaState::pop(int n)
{
assert(hasIndex(-n));
lua_pop(L, n);
}
int LuaState::popInteger()
{
assert(hasIndex(-1));
int v = toInteger(-1);
pop();
return v;
}
double LuaState::popNumber()
{
assert(hasIndex(-1));
double v = toNumber(-1);
pop();
return v;
}
bool LuaState::popBoolean()
{
assert(hasIndex(-1));
bool v = toBoolean(-1);
pop();
return v;
}
std::string LuaState::popString()
{
assert(hasIndex(-1));
std::string v = toString(-1);
pop();
return v;
}
void* LuaState::popUserdata()
{
assert(hasIndex(-1));
void* v = toUserdata(-1);
pop();
return v;
}
void LuaState::pushNil()
{
lua_pushnil(L);
}
void LuaState::pushInteger(int v)
{
lua_pushinteger(L, v);
}
void LuaState::pushNumber(double v)
{
lua_pushnumber(L, v);
}
void LuaState::pushBoolean(bool v)
{
lua_pushboolean(L, v);
}
void LuaState::pushString(const std::string& v)
{
lua_pushstring(L, v.c_str());
}
void LuaState::pushRef(int ref)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
}
void LuaState::pushLightUserdata(void* p)
{
lua_pushlightuserdata(L, p);
}
bool LuaState::isNil(int index)
{
assert(hasIndex(index));
return lua_isnil(L, index);
}
bool LuaState::isBoolean(int index)
{
assert(hasIndex(index));
return lua_isboolean(L, index);
}
bool LuaState::isNumber(int index)
{
assert(hasIndex(index));
return lua_isnumber(L, index);
}
bool LuaState::isString(int index)
{
assert(hasIndex(index));
return lua_isstring(L, index);
}
bool LuaState::isTable(int index)
{
assert(hasIndex(index));
return lua_istable(L, index);
}
bool LuaState::isFunction(int index)
{
assert(hasIndex(index));
return lua_isfunction(L, index);
}
bool LuaState::isCFunction(int index)
{
assert(hasIndex(index));
return lua_iscfunction(L, index);
}
bool LuaState::isUserdata(int index)
{
assert(hasIndex(index));
return lua_isuserdata(L, index);
}
bool LuaState::toBoolean(int index)
{
assert(hasIndex(index));
return (bool)lua_toboolean(L, index);
}
int LuaState::toInteger(int index)
{
assert(hasIndex(index));
return lua_tointeger(L, index);
}
double LuaState::toNumber(int index)
{
assert(hasIndex(index));
return lua_tonumber(L, index);
}
std::string LuaState::toString(int index)
{
assert(hasIndex(index));
return std::string(isString() ? lua_tostring(L, index) : "");
}
void* LuaState::toUserdata(int index)
{
assert(hasIndex(index));
return lua_touserdata(L, index);
}
int LuaState::stackSize()
{
return lua_gettop(L);
}

View File

@@ -1,126 +0,0 @@
#ifndef LUASTATE_H
#define LUASTATE_H
#include "luadeclarations.h"
struct lua_State;
class LuaState
{
public:
LuaState();
virtual ~LuaState();
// high level functions
void collectGarbage();
LuaValuePtr loadBuffer(const std::string& buffer, const std::string& source);
LuaValuePtr loadScript(const std::string& fileName);
void runBuffer(const std::string& buffer, const std::string& source);
void runScript(const std::string& fileName);
LuaValuePtr getGlobal(const std::string& name) { getGlobalField(name); return popValue(); }
void setGlobal(const std::string& name, const LuaValuePtr& value) { pushValue(value); setGlobalField(name); }
LuaValuePtr createTable() { newTable(); return popValue(); }
LuaValuePtr createTable(const std::string& name) { newTable(); copy(); setGlobalField(name); return popValue(); }
LuaValuePtr createCppFunction(const LuaCppFunction& v) { pushCppFunction(v); return popValue(); }
LuaValuePtr createNil() { pushNil(); return popValue(); }
LuaValuePtr createNumber(double v) { pushNumber(v); return popValue(); }
LuaValuePtr createInteger(int v) { pushInteger(v); return popValue(); }
LuaValuePtr createString(const std::string& v) { pushString(v); return popValue(); }
LuaValuePtr createBoolean(bool v) { pushBoolean(v); return popValue(); }
std::string getTraceback(const std::string& errorMessage = "", int depth = 0);
std::string getCurrentSource();
std::string getFunctionSource(LuaValuePtr func);
LuaValuePtr getStackFunction(int level);
LuaValuePtr getGlobalEnvironment();
void setGlobalEnvironment(const LuaValuePtr& env);
// low level lua functions
std::string typeName(int index = -1);
int ref();
void unref(int ref);
void call(int numArgs = 0, int numRets = 0);
int safeCall(const std::string& functionName, int numArgs = 0);
void insert(int index);
void remove(int index);
bool next(int index = -2);
void copy(int index = -1);
void setMetatable(int index = -2);
void setMetatable(const std::string& name);
void getMetatable(int index = -1);
void getField(const std::string& key, int index = -1);
void setField(const std::string& key, int index = -2);
void getTable(int index = -2);
void setTable(int index = -3);
void getEnv(int index = -1);
void setEnv(int index = -2);
void getGlobalField(const std::string& key);
void setGlobalField(const std::string& key);
void rawGet(int index = -1);
void rawGeti(int n, int index = -1);
void rawSet(int index = -3);
void rawSeti(int n, int index = -2);
void newTable();
void* newUserdata(int size);
void pop(int n = 1);
int popInteger();
double popNumber();
bool popBoolean();
std::string popString();
void* popUserdata();
LuaValuePtr popValue();
LuaValueList popValues();
void pushNil();
void pushInteger(int v);
void pushNumber(double v);
void pushBoolean(bool v);
void pushString(const std::string& v);
void pushRef(int ref);
void pushLightUserdata(void* p);
void pushCppFunction(const LuaCppFunction& func);
void pushValue(const LuaValuePtr& value);
void pushValues(const LuaValueList& values);
bool isNil(int index = -1);
bool isBoolean(int index = -1);
bool isNumber(int index = -1);
bool isString(int index = -1);
bool isTable(int index = -1);
bool isFunction(int index = -1);
bool isCFunction(int index = -1);
bool isLuaFunction(int index = -1) { return (isFunction() && !isCFunction()); }
bool isUserdata(int index = -1);
bool toBoolean(int index = -1);
int toInteger(int index = -1);
double toNumber(int index = -1);
std::string toString(int index = -1);
void* toUserdata(int index = -1);
int stackSize();
void clearStack() { pop(stackSize()); }
bool hasIndex(int index) { return (stackSize() >= (index < 0 ? -index : index)); }
private:
lua_State* L;
bool m_collecting;
std::deque<std::string> m_callStack;
};
#endif // LUASTATE_H

View File

@@ -1,234 +0,0 @@
#include "luavalue.h"
#include "luainterface.h"
#include "luaobject.h"
#include <core/resources.h>
LuaValue::LuaValue(int ref) : m_ref(ref)
{
}
LuaValue::~LuaValue()
{
// releases the reference of this value from lua
g_lua.unref(m_ref);
}
LuaValueList LuaValue::call(const std::string& funcName, const LuaValueList& args)
{
LuaValueList rets;
// if is a function, calls it
if(isFunction()) {
g_resources.pushCurrentPath(getPathDirectory(g_lua.getFunctionSource(shared_from_this())));
try {
push();
g_lua.pushValues(args);
int numRets = g_lua.safeCall(funcName, args.size());
for(int i=0;i<numRets; ++i)
rets.push_back(g_lua.popValue());
} catch(LuaException &e) {
logError(e.what());
}
g_resources.popCurrentPath();
}
// also calls a table of functions
else if(isTable()) {
LuaValueList functions = getTableValues();
foreach(const LuaValuePtr& function, functions) {
if(function->isFunction())
function->call(funcName, args);
}
};
return rets;
}
int LuaValue::getTableSize()
{
int size = 0;
push();
assert(g_lua.isTable());
g_lua.pushNil();
while(g_lua.next()) {
g_lua.pop();
size++;
}
g_lua.pop();
return size;
}
LuaValueList LuaValue::getTableValues()
{
LuaValueList ret;
push();
assert(g_lua.isTable());
g_lua.pushNil();
while(g_lua.next())
ret.push_back(g_lua.popValue());
return ret;
}
LuaValuePtr LuaValue::getField(const std::string& key)
{
push();
assert(g_lua.isTable() || g_lua.isUserdata());
g_lua.getField(key);
g_lua.remove(-2);
return g_lua.popValue();
}
void LuaValue::setField(const std::string& key, const LuaValuePtr& value)
{
push();
assert(g_lua.isTable() || g_lua.isUserdata());
g_lua.pushValue(value);
g_lua.setField(key);
g_lua.pop();
}
LuaValuePtr LuaValue::getMetatable()
{
push();
assert(g_lua.isUserdata() || g_lua.isTable());
g_lua.getMetatable();
g_lua.remove(-2);
return g_lua.popValue();
}
void LuaValue::setMetatable(const LuaValuePtr& mt)
{
push();
assert(g_lua.isUserdata() || g_lua.isTable());
g_lua.pushValue(mt);
g_lua.setMetatable();
g_lua.pop();
}
void LuaValue::setTable(int n, const LuaValuePtr& value)
{
push();
assert(g_lua.isTable());
g_lua.pushInteger(n);
g_lua.pushValue(value);
g_lua.setTable();
g_lua.pop();
}
void LuaValue::setEnvironment(LuaValuePtr env)
{
push();
g_lua.pushValue(env);
g_lua.setEnv();
g_lua.pop();
}
std::string LuaValue::getTypeName()
{
push();
std::string typeName = g_lua.typeName();
g_lua.pop();
return typeName;
}
bool LuaValue::isBoolean()
{
push();
bool ret = g_lua.isBoolean();
g_lua.pop();
return ret;
}
bool LuaValue::isString()
{
push();
bool ret = g_lua.isString();
g_lua.pop();
return ret;
}
bool LuaValue::isNumber()
{
push();
bool ret = g_lua.isNumber();
g_lua.pop();
return ret;
}
bool LuaValue::isTable()
{
push();
bool ret = g_lua.isTable();
g_lua.pop();
return ret;
}
bool LuaValue::isFunction()
{
push();
bool ret = g_lua.isFunction();
g_lua.pop();
return ret;
}
bool LuaValue::isCFunction()
{
push();
bool ret = g_lua.isCFunction();
g_lua.pop();
return ret;
}
bool LuaValue::isLuaFunction()
{
push();
bool ret = g_lua.isLuaFunction();
g_lua.pop();
return ret;
}
bool LuaValue::isUserdata()
{
push();
bool ret = g_lua.isUserdata();
g_lua.pop();
return ret;
}
bool LuaValue::toBoolean()
{
push();
return g_lua.popBoolean();
}
int LuaValue::toInteger()
{
push();
return g_lua.popInteger();
}
double LuaValue::toNumber()
{
push();
return g_lua.popNumber();
}
std::string LuaValue::toString()
{
push();
return g_lua.popString();
}
void* LuaValue::toUserdata()
{
push();
return g_lua.popUserdata();
}
void LuaValue::push()
{
g_lua.pushValue(shared_from_this());
}

View File

@@ -1,78 +0,0 @@
#ifndef LUAVALUE_H
#define LUAVALUE_H
#include "luavaluecasts.h"
/// Utility used to fill a LuaValueList by args...
inline void fillLuaValueList(LuaValueList& argsList) { }
template<class T, class... Args>
void fillLuaValueList(LuaValueList& argsList, const T& first, const Args&... rest) {
argsList.push_back(safe_to_luavalue(first));
fillLuaValueList(argsList, rest...);
}
/// LuaValue, all values from and to lua can be represented by this type
class LuaValue : public std::enable_shared_from_this<LuaValue>
{
public:
LuaValue(int ref = -1);
virtual ~LuaValue();
/** If this value is a function or a list of functions, calls it.
* Otherwise, if is not a nil value, throws an LuaException */
LuaValueList call(const std::string& funcName, const LuaValueList& args = LuaValueList());
template<class... T>
LuaValueList call(const std::string& funcName, const T&... args) {
LuaValueList argsList;
push();
fillLuaValueList(argsList, args...);
return call(funcName, argsList);
}
int getTableSize();
LuaValueList getTableValues();
LuaValuePtr getField(const std::string& key);
void setField(const std::string& key, const LuaValuePtr& value);
template<typename T>
void setField(const std::string& key, const T& value) {
setField(key, safe_to_luavalue<T>(value));
}
LuaValuePtr getMetatable();
void setMetatable(const LuaValuePtr& mt);
void setTable(int n, const LuaValuePtr& value);
void setEnvironment(LuaValuePtr env);
std::string getTypeName();
// check types
bool isNil() { return m_ref < 0; }
bool isBoolean();
bool isString();
bool isNumber();
bool isTable();
bool isFunction();
bool isCFunction();
bool isLuaFunction();
bool isUserdata();
// convert types
bool toBoolean();
int toInteger();
double toNumber();
std::string toString();
void* toUserdata();
private:
/// Pushes this value into lua stack
void push();
int m_ref;
friend class LuaState;
};
#endif // LUAVALUE_H

View File

@@ -1,76 +0,0 @@
#include "luavaluecasts.h"
#include "luainterface.h"
bool to_luavalue(const LuaValuePtr& v, LuaValuePtr& o) {
o = v;
return true;
}
bool to_luavalue(bool v, LuaValuePtr& o) {
o = g_lua.createBoolean(v);
return true;
}
bool to_luavalue(int v, LuaValuePtr& o) {
o = g_lua.createInteger(v);
return true;
}
bool to_luavalue(double v, LuaValuePtr& o) {
o = g_lua.createNumber(v);
return true;
}
bool to_luavalue(const std::string& v, LuaValuePtr& o) {
o = g_lua.createString(v);
return true;
}
bool to_luavalue(const char* v, LuaValuePtr& o) {
o = g_lua.createString(v);
return true;
}
bool to_luavalue(int (*v)(LuaState*), LuaValuePtr& o) {
o = g_lua.createCppFunction(v);
return true;
}
bool to_luavalue(const LuaCppFunction& v, LuaValuePtr& o) {
o = g_lua.createCppFunction(v);
return true;
}
bool luavalue_cast(const LuaValuePtr& v, bool& o) {
o = v->toBoolean();
return true;
}
bool luavalue_cast(const LuaValuePtr& v, int& o) {
o = v->toInteger();
return true;
}
bool luavalue_cast(const LuaValuePtr& v, double& o) {
o = v->toNumber();
return true;
}
bool luavalue_cast(const LuaValuePtr& v, std::string& o) {
o = v->toString();
return true;
}
bool luavalue_cast(const LuaValuePtr& v, LuaObjectPtr& o) {
if(v->isUserdata()) {
LuaObjectPtr* objRef = static_cast<LuaObjectPtr*>(v->toUserdata());
if(objRef && *objRef) {
o = *objRef;
return true;
}
} else if(v->isNil()) {
o = LuaObjectPtr();
return true;
}
return false;
}

View File

@@ -1,80 +0,0 @@
#ifndef LUAVALUECASTS_H
#define LUAVALUECASTS_H
#include "luadeclarations.h"
#include "luaexception.h"
// convert from common types
bool to_luavalue(const LuaValuePtr& v, LuaValuePtr& o);
bool to_luavalue(bool v, LuaValuePtr& o);
bool to_luavalue(int v, LuaValuePtr& o);
bool to_luavalue(double v, LuaValuePtr& o);
bool to_luavalue(const std::string& v, LuaValuePtr& o);
bool to_luavalue(const char* v, LuaValuePtr& o);
bool to_luavalue(int (*v)(LuaState*), LuaValuePtr& o);
bool to_luavalue(const LuaCppFunction& v, LuaValuePtr& o);
// convert from LuaObject
template<class T>
typename std::enable_if<std::is_base_of<LuaObject, typename T::element_type>::value, bool>::type
to_luavalue(const T& v, LuaValuePtr& o) {
if(v) {
o = v->toLuaValue();
return true;
}
return false;
}
/// Safe convert, if any errors occurs throws an LuaException
template<class T>
LuaValuePtr safe_to_luavalue(const T& v) {
LuaValuePtr o;
if(!to_luavalue(v, o))
throw LuaBadTypeConversinException();
return o;
}
// common types casting
bool luavalue_cast(const LuaValuePtr& v, bool& o);
bool luavalue_cast(const LuaValuePtr& v, int& o);
bool luavalue_cast(const LuaValuePtr& v, double& o);
bool luavalue_cast(const LuaValuePtr& v, std::string& o);
bool luavalue_cast(const LuaValuePtr& v, LuaObjectPtr& o);
// convert enums
template<class T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
luavalue_cast(const LuaValuePtr& v, T& o) {
return luavalue_cast(v, (int&)o);
}
// cast pointer
template<class T>
bool luavalue_cast(const LuaValuePtr& v, T*& o) {
LuaObjectPtr obj;
if(!luavalue_cast(v, obj))
return false;
o = std::dynamic_pointer_cast<T>(obj).get();
return !!o;
}
// cast shared pointers
template<class T>
bool luavalue_cast(const LuaValuePtr& v, std::shared_ptr<T>& o) {
LuaObjectPtr obj;
if(!luavalue_cast(v, obj))
return false;
o = std::dynamic_pointer_cast<T>(obj);
return !!o;
}
/// Safe casting, if any errors occurs throws an LuaException
template<class T>
T safe_luavalue_cast(const LuaValuePtr& v) {
T o;
if(!luavalue_cast(v, o))
throw LuaBadValueCastException(v);
return o;
}
#endif // LUAVALUECASTS_H

View File

@@ -1,285 +0,0 @@
#include <global.h>
#include <script/luafunctions.h>
#include <script/luacontext.h>
#include <core/engine.h>
#include <core/resources.h>
#include <ui/ui.h>
#include <core/dispatcher.h>
#include "../../protocollogin.h"
#include <boost/algorithm/string.hpp>
int lua_App_exit() {
g_engine.stop();
return 0;
}
int lua_App_loadUI() {
UIContainerPtr parent;
if(g_lua.stackSize() > 1)
parent = std::dynamic_pointer_cast<UIContainer>(g_lua.popLuaObjectInstance());
else
parent = UIContainer::getRoot();
std::string uiFile = g_lua.popString();
if(!boost::ends_with(uiFile, ".otml"))
uiFile.append(".otml");
UIElementPtr element;
if(parent) {
element = g_uiLoader.loadFromFile(uiFile.c_str(), parent);
if(!element) {
g_lua.reportFuncErrorWithTraceback("failed to load UI");
}
} else
g_lua.reportFuncErrorWithTraceback("invalid parent container");
g_lua.pushLuaObjectInstance(element);
return 1;
}
int lua_App_getRootContainer() {
UIContainerPtr rootContainer = UIContainer::getRoot();
g_lua.pushLuaObjectInstance(rootContainer);
return 1;
}
UIElementPtr lua_createUIElement(UI::ElementType elementType)
{
std::string id;
UIContainerPtr parent;
if(g_lua.stackSize() > 1)
parent = std::dynamic_pointer_cast<UIContainer>(g_lua.popLuaObjectInstance());
if(g_lua.stackSize() > 0) {
if(g_lua.isString())
id = g_lua.popString();
else
parent = std::dynamic_pointer_cast<UIContainer>(g_lua.popLuaObjectInstance());
}
UIElementPtr element;
switch(elementType) {
case UI::Element:
element = UIElementPtr(new UIElement);
break;
case UI::Container:
element = UIElementPtr(new UIContainer);
break;
case UI::Label:
element = UIElementPtr(new UILabel);
break;
case UI::Button:
element = UIElementPtr(new UIButton);
break;
case UI::TextEdit:
element = UIElementPtr(new UITextEdit);
break;
case UI::Window:
element = UIElementPtr(new UIWindow);
break;
default:
break;
}
if(element) {
if(!id.empty())
element->setId(id);
if(parent)
parent->addChild(element);
element->applyDefaultSkin();
}
return element;
}
int lua_UIElement_setLocked() {
bool locked = true;
if(g_lua.stackSize() > 1)
locked = g_lua.popBoolean();
if(UIElementPtr element = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance())) {
if(UIContainerPtr container = element->getParent()) {
if(locked)
container->lockElement(element);
else
container->unlockElement(element);
} else
g_lua.reportFuncErrorWithTraceback("locking failed, element has no parent");
}
return 0;
}
int lua_UIElement_setMargin() {
int top = 0, right = 0, bottom = 0, left = 0;
if(g_lua.stackSize() == 5) {
left = g_lua.popInteger();
bottom = g_lua.popInteger();
right = g_lua.popInteger();
top = g_lua.popInteger();
} else if(g_lua.stackSize() == 3) {
left = right = g_lua.popInteger();
top = bottom = g_lua.popInteger();
} else if(g_lua.stackSize() == 2) {
top = right = bottom = left = g_lua.popInteger();
} else {
g_lua.reportFuncErrorWithTraceback("invalid number of arguments");
}
if(UIElementPtr element = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance()))
element->setMargin(top, right, bottom, left);
return 0;
}
int lua_UIElement_centerIn() {
std::string targetId;
if(g_lua.isString())
targetId = g_lua.popString();
else if(UIElementPtr target = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance()))
targetId = target->getId();
if(UIElementPtr element = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance()))
element->centerIn(targetId);
return 0;
}
int lua_UIElement_addAnchor() {
AnchorPoint targetEdge = (AnchorPoint)g_lua.popInteger();
std::string targetId;
if(g_lua.isString())
targetId = g_lua.popString();
else if(UIElementPtr target = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance()))
targetId = target->getId();
AnchorPoint anchoredEdge = (AnchorPoint)g_lua.popInteger();
if(UIElementPtr element = std::dynamic_pointer_cast<UIElement>(g_lua.popLuaObjectInstance()))
element->addAnchor(anchoredEdge, AnchorLine(targetId, targetEdge));
return 0;
}
void registerScriptFunctions()
{
////////////////////////////////////////////////////////////////////////////////
// App
g_lua.registerModule("App");
// App.exit()
g_lua.registerMemberFunction("exit", &lua_App_exit);
// App.loadUI(file, [parent])
g_lua.registerMemberFunction("loadUI", &lua_App_loadUI);
// App.getRootContainer()
g_lua.registerMemberFunction("getRootContainer", &lua_App_getRootContainer);
////////////////////////////////////////////////////////////////////////////////
// UIElement
g_lua.registerClass("UIElement");
// (UIElement) UIElement.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UIElement_new);
// (string) UIElement.id
g_lua.registerMemberField("id", &lua_UIElement_getId, &lua_UIElement_setId);
// (boolean) UIElement.enabled
g_lua.registerMemberField("enabled", &lua_UIElement_isEnabled, &lua_UIElement_setEnabled);
// (boolean UIElement.visible
g_lua.registerMemberField("visible", &lua_UIElement_isVisible, &lua_UIElement_setVisible);
// (boolean UIElement.focused
g_lua.registerMemberField("focused", &lua_UIElement_isFocused, &lua_UIElement_setFocused);
// (integer) UIElement.width
g_lua.registerMemberField("width", &lua_UIElement_getWidth, &lua_UIElement_setWidth);
// (integer) UIElement.height
g_lua.registerMemberField("height", &lua_UIElement_getHeight, &lua_UIElement_setHeight);
// (UIContainer UIElement.parent
g_lua.registerMemberField("parent", &lua_UIElement_getParent, &lua_UIElement_setParent);
// element:setLocked(boolean locked)
g_lua.registerMemberFunction("setLocked", &lua_UIElement_setLocked);
// element:setMargin(int all)
// element:setMargin(int vertical, int horizontal)
// element:setMargin(int top, right, bottom, left)
g_lua.registerMemberFunction("setMargin", &lua_UIElement_setMargin);
// element:destroy()
g_lua.registerMemberFunction("destroy", &lua_UIElement_destroy);
// element:centerIn(UIContainer target)
g_lua.registerMemberFunction("centerIn", &lua_UIElement_centerIn);
// element:addAnchor(AnchorPoint anchoredEdge, UIElement target, AnchorPoint targetEdge)
// element:addAnchor(AnchorPoint anchoredEdge, string targetId, AnchorPoint targetEdge)
g_lua.registerMemberFunction("addAnchor", &lua_UIElement_addAnchor);
////////////////////////////////////////////////////////////////////////////////
// UIContainer
g_lua.registerClass("UIContainer", "UIElement");
// UIContainer.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UIContainer_new);
// container:child(string id)
g_lua.registerMemberFunction("child", &lua_UIContainer_child);
// (table) container:children()
g_lua.registerMemberFunction("children", &lua_UIContainer_getChildren);
// container:addChild(UIElement child)
g_lua.registerMemberFunction("addChild", &lua_UIContainer_addChild);
////////////////////////////////////////////////////////////////////////////////
// UILabel
g_lua.registerClass("UILabel", "UIElement");
// UILabel.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UILabel_new);
// label.text
g_lua.registerMemberField("text", &lua_UILabel_getText, &lua_UILabel_setText);
////////////////////////////////////////////////////////////////////////////////
// UIButton
g_lua.registerClass("UIButton", "UIElement");
// UIButton.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UIButton_new);
// button.text
g_lua.registerMemberField("text", &lua_UIButton_getText, &lua_UIButton_setText);
////////////////////////////////////////////////////////////////////////////////
// UITextEdit
g_lua.registerClass("UITextEdit", "UIElement");
// UITextEdit.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UITextEdit_new);
// textEdit.text
g_lua.registerMemberField("text", &lua_UITextEdit_getText, &lua_UITextEdit_setText);
////////////////////////////////////////////////////////////////////////////////
// UIWindow
g_lua.registerClass("UIWindow", "UIContainer");
// UIWindow.new([UIContainer parent], [string id])
g_lua.registerMemberFunction("new", &lua_UIWindow_new);
// window.title
g_lua.registerMemberField("title", &lua_UIWindow_getTitle, &lua_UIWindow_setTitle);
////////////////////////////////////////////////////////////////////////////////
// Protocol
g_lua.registerClass("ProtocolLogin");
g_lua.registerMemberFunction("new", []{
g_lua.pushLuaObjectInstance(LuaObjectPtr(new ProtocolLogin));
return 1;
});
g_lua.registerMemberFunction("login", []{
std::string accountPassword = g_lua.popString();
std::string accountName = g_lua.popString();
if(ProtocolLoginPtr protocolLogin = std::dynamic_pointer_cast<ProtocolLogin>(g_lua.popLuaObjectInstance()))
protocolLogin->login(accountName, accountPassword);
return 0;
});
g_lua.registerMemberFunction("cancel", []{
//TODO: this func
return 0;
});
}

23
src/framework/thirdparty/apngloader.h vendored Normal file
View File

@@ -0,0 +1,23 @@
#ifndef APNGLOADER_H
#define APNGLOADER_H
#include <sstream>
struct apng_data {
unsigned char *pdata;
unsigned int width;
unsigned int height;
unsigned int first_frame;
unsigned int last_frame;
unsigned char bpp;
unsigned char coltype;
unsigned int num_frames;
unsigned int num_plays;
unsigned int *frames_delay; // each frame delay in ms
};
// returns -1 on error, 0 on success
int load_apng(std::stringstream& file, struct apng_data *apng);
void free_apng(struct apng_data *apng);
#endif

View File

@@ -1,17 +1,12 @@
#ifndef UI_H
#define UI_H
#include <global.h>
#include "uimanager.h"
#include "uiwidget.h"
#include "uibutton.h"
#include "uilabel.h"
#include "uilayout.h"
#include "uilineedit.h"
#include "uiwindow.h"
#include <ui/uiskins.h>
#include <ui/uiloader.h>
#include <ui/uielement.h>
#include <ui/uielementskin.h>
#include <ui/uicontainer.h>
#include <ui/uibutton.h>
#include <ui/uilabel.h>
#include <ui/uiwindow.h>
#include <ui/uitextedit.h>
#include <ui/uicheckbox.h>
#endif // UI_H
#endif

View File

@@ -0,0 +1,44 @@
#include "uianchor.h"
#include "uiwidget.h"
UIAnchor::UIAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
: m_anchoredWidget(anchoredWidget), m_anchoredEdge(anchoredEdge), m_anchorLine(anchorLine) {
}
UIWidgetPtr UIAnchor::getAnchorLineWidget() const {
if(!m_anchoredWidget.expired() && !m_anchoredWidget.lock()->isDestroyed())
return m_anchoredWidget.lock()->backwardsGetWidgetById(m_anchorLine.widgetId);
return nullptr;
}
UIWidgetPtr UIAnchor::getAnchoredWidget() const {
return m_anchoredWidget.lock();
}
AnchorPoint UIAnchor::getAnchoredEdge() const {
return m_anchoredEdge;
}
int UIAnchor::getAnchorLinePoint() const {
UIWidgetPtr anchorLineWidget = getAnchorLineWidget();
if(anchorLineWidget) {
switch(m_anchorLine.edge) {
case AnchorLeft:
return anchorLineWidget->getGeometry().left();
case AnchorRight:
return anchorLineWidget->getGeometry().right();
case AnchorTop:
return anchorLineWidget->getGeometry().top();
case AnchorBottom:
return anchorLineWidget->getGeometry().bottom();
case AnchorHorizontalCenter:
return anchorLineWidget->getGeometry().horizontalCenter();
case AnchorVerticalCenter:
return anchorLineWidget->getGeometry().verticalCenter();
default:
break;
}
}
return -9999;
}

Some files were not shown because too many files have changed in this diff Show More