mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 21:59:17 +00:00
561 lines
15 KiB
C++
561 lines
15 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "engines/grim/grim.h"
|
|
#include "engines/grim/lua_v1.h"
|
|
#include "engines/grim/resource.h"
|
|
#include "engines/grim/bitmap.h"
|
|
#include "engines/grim/primitives.h"
|
|
#include "engines/grim/iris.h"
|
|
#include "engines/grim/gfx_base.h"
|
|
#include "engines/grim/set.h"
|
|
#include "engines/grim/actor.h"
|
|
#include "engines/grim/movie/movie.h"
|
|
#include "engines/grim/lua/lua.h"
|
|
|
|
namespace Grim {
|
|
|
|
void Lua_V1::GetImage() {
|
|
lua_Object nameObj = lua_getparam(1);
|
|
if (!lua_isstring(nameObj)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
const char *bitmapName = lua_getstring(nameObj);
|
|
Bitmap *b = Bitmap::create(bitmapName);
|
|
lua_pushusertag(b->getId(), MKTAG('V','B','U','F'));
|
|
}
|
|
|
|
void Lua_V1::FreeImage() {
|
|
lua_Object param = lua_getparam(1);
|
|
if (!lua_isuserdata(param) || lua_tag(param) != MKTAG('V','B','U','F'))
|
|
return;
|
|
Bitmap *bitmap = getbitmap(param);
|
|
delete bitmap;
|
|
}
|
|
|
|
void Lua_V1::BlastImage() {
|
|
lua_Object param = lua_getparam(1);
|
|
if (!lua_isuserdata(param) || lua_tag(param) != MKTAG('V','B','U','F'))
|
|
return;
|
|
Bitmap *bitmap = getbitmap(param);
|
|
lua_Object xObj = lua_getparam(2);
|
|
lua_Object yObj = lua_getparam(3);
|
|
if (!lua_isnumber(xObj) || !lua_isnumber(yObj))
|
|
return;
|
|
|
|
int x = (int)lua_getnumber(xObj);
|
|
int y = (int)lua_getnumber(yObj);
|
|
//bool transparent = getbool(4); // TODO transparent/masked copy into display
|
|
bitmap->draw(x, y);
|
|
}
|
|
|
|
void Lua_V1::CleanBuffer() {
|
|
g_driver->copyStoredToDisplay();
|
|
}
|
|
|
|
void Lua_V1::StartFullscreenMovie() {
|
|
lua_Object name = lua_getparam(1);
|
|
if (!lua_isstring(name)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
Lua_V1::CleanBuffer();
|
|
|
|
GrimEngine::EngineMode prevEngineMode = g_grim->getMode();
|
|
g_grim->setMode(GrimEngine::SmushMode);
|
|
g_grim->setMovieSubtitle(nullptr);
|
|
bool looping = getbool(2);
|
|
bool result = g_movie->play(lua_getstring(name), looping, 0, 0);
|
|
if (!result)
|
|
g_grim->setMode(prevEngineMode);
|
|
pushbool(result);
|
|
}
|
|
|
|
void Lua_V1::StartMovie() {
|
|
lua_Object name = lua_getparam(1);
|
|
if (!lua_isstring(name)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
int x = 0, y = 0;
|
|
if (!lua_isnil(lua_getparam(3)))
|
|
x = (int)lua_getnumber(lua_getparam(3));
|
|
if (!lua_isnil(lua_getparam(4)))
|
|
y = (int)lua_getnumber(lua_getparam(4));
|
|
|
|
GrimEngine::EngineMode prevEngineMode = g_grim->getMode();
|
|
g_grim->setMode(GrimEngine::NormalMode);
|
|
|
|
bool looping = getbool(2);
|
|
bool result = g_movie->play(lua_getstring(name), looping, x, y);
|
|
g_grim->setMovieSetup();
|
|
if (!result)
|
|
g_grim->setMode(prevEngineMode);
|
|
pushbool(result);
|
|
}
|
|
|
|
/* Fullscreen movie playing query and normal movie
|
|
* query should actually detect correctly and not
|
|
* just return true whenever ANY movie is playing
|
|
*/
|
|
void Lua_V1::IsFullscreenMoviePlaying() {
|
|
pushbool(g_movie->isPlaying());
|
|
}
|
|
|
|
void Lua_V1::IsMoviePlaying() {
|
|
// Previously, if the game was *not* the demo, this checked also if the mode
|
|
// was GrimEngine::NormalMode. This doesn't seem to be what original does, and causes
|
|
// bug #301 because the movie eldepot.snm is played before legslide.snm ends.
|
|
pushbool(g_movie->isPlaying());
|
|
}
|
|
|
|
void Lua_V1::StopMovie() {
|
|
g_movie->stop();
|
|
// Delete subtitles that may have not expired.
|
|
g_grim->setMovieSubtitle(nullptr);
|
|
}
|
|
|
|
void Lua_V1::PauseMovie() {
|
|
g_movie->pause(lua_isnil(lua_getparam(1)) == 0);
|
|
}
|
|
|
|
void Lua_V1::PurgePrimitiveQueue() {
|
|
PrimitiveObject::getPool().deleteObjects();
|
|
}
|
|
|
|
void Lua_V1::DrawPolygon() {
|
|
lua_Object tableObj1 = lua_getparam(1);
|
|
if (!lua_istable(tableObj1)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
|
|
//int layer = 2;
|
|
Color color;
|
|
lua_Object tableObj2 = lua_getparam(2);
|
|
if (lua_istable(tableObj2)) {
|
|
lua_pushobject(tableObj2);
|
|
lua_pushstring("color");
|
|
lua_Object colorObj = lua_gettable();
|
|
if (lua_isuserdata(colorObj) && lua_tag(colorObj) == MKTAG('C','O','L','R')) {
|
|
color = getcolor(colorObj);
|
|
}
|
|
lua_pushobject(tableObj2);
|
|
lua_pushstring("layer");
|
|
lua_Object layerObj = lua_gettable();
|
|
if (lua_isnumber(layerObj))
|
|
/*layer = (int)*/lua_getnumber(layerObj);
|
|
}
|
|
|
|
// This code only supports 4 point polygons because the game doesn't
|
|
// use other than that. However, the original engine can support
|
|
// many points per polygon
|
|
lua_Object pointObj;
|
|
Common::Point p[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
// Get X
|
|
lua_pushobject(tableObj1);
|
|
lua_pushnumber(i * 2 + 1);
|
|
pointObj = lua_gettable();
|
|
if (!lua_isnumber(pointObj)) {
|
|
warning("Lua_V1::DrawPolygon: %i Point Parameter X isn't a number!", i * 2 + 1);
|
|
return;
|
|
}
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
p[i].x = (int)lua_getnumber(pointObj);
|
|
else
|
|
p[i].x = (int)((lua_getnumber(pointObj) + 1) * 320);
|
|
|
|
// Get Y
|
|
lua_pushobject(tableObj1);
|
|
lua_pushnumber(i * 2 + 2);
|
|
pointObj = lua_gettable();
|
|
if (!lua_isnumber(pointObj)) {
|
|
warning("Lua_V1::DrawPolygon: %i Point Parameter Y isn't a number!", i * 2 + 2);
|
|
return;
|
|
}
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
p[i].y = (int)lua_getnumber(pointObj);
|
|
else
|
|
p[i].y = (int)((1 - lua_getnumber(pointObj)) * 240);
|
|
}
|
|
|
|
PrimitiveObject *prim = new PrimitiveObject();
|
|
prim->createPolygon(p[0], p[1], p[2], p[3], color);
|
|
lua_pushusertag(prim->getId(), MKTAG('P','R','I','M'));
|
|
}
|
|
|
|
void Lua_V1::DrawLine() {
|
|
Common::Point p1, p2;
|
|
Color color;
|
|
lua_Object x1Obj = lua_getparam(1);
|
|
lua_Object y1Obj = lua_getparam(2);
|
|
lua_Object x2Obj = lua_getparam(3);
|
|
lua_Object y2Obj = lua_getparam(4);
|
|
lua_Object tableObj = lua_getparam(5);
|
|
|
|
if (!lua_isnumber(x1Obj) || !lua_isnumber(y1Obj) || !lua_isnumber(x2Obj) || !lua_isnumber(y2Obj)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
|
|
if (g_grim->getGameType() == GType_GRIM) {
|
|
p1.x = (int)lua_getnumber(x1Obj);
|
|
p1.y = (int)lua_getnumber(y1Obj);
|
|
p2.x = (int)lua_getnumber(x2Obj);
|
|
p2.y = (int)lua_getnumber(y2Obj);
|
|
} else {
|
|
p1.x = (int)((lua_getnumber(x1Obj) + 1) * 320);
|
|
p1.y = (int)((1 - lua_getnumber(y1Obj)) * 240);
|
|
p2.x = (int)((lua_getnumber(x2Obj) + 1) * 320);
|
|
p2.y = (int)((1 - lua_getnumber(y2Obj)) * 240);
|
|
}
|
|
|
|
//int layer = 2;
|
|
if (lua_istable(tableObj)) {
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("color");
|
|
lua_Object colorObj = lua_gettable();
|
|
if (lua_isuserdata(colorObj) && lua_tag(colorObj) == MKTAG('C','O','L','R')) {
|
|
color = getcolor(colorObj);
|
|
}
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("layer");
|
|
lua_Object layerObj = lua_gettable();
|
|
if (lua_isnumber(layerObj))
|
|
/*layer = (int)*/lua_getnumber(layerObj);
|
|
}
|
|
|
|
PrimitiveObject *p = new PrimitiveObject();
|
|
p->createLine(p1, p2, color); // TODO Add layer support
|
|
lua_pushusertag(p->getId(), MKTAG('P','R','I','M'));
|
|
}
|
|
|
|
void Lua_V1::ChangePrimitive() {
|
|
lua_Object param1 = lua_getparam(1);
|
|
if (!lua_isuserdata(param1) || lua_tag(param1) != MKTAG('P','R','I','M'))
|
|
return;
|
|
|
|
lua_Object tableObj = lua_getparam(2);
|
|
if (!lua_istable(tableObj))
|
|
return;
|
|
|
|
PrimitiveObject *pmodify = getprimitive(param1);
|
|
assert(pmodify);
|
|
|
|
Color color;
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("color");
|
|
lua_Object colorObj = lua_gettable();
|
|
if (lua_isuserdata(colorObj) && lua_tag(colorObj) == MKTAG('C','O','L','R')) {
|
|
color = getcolor(colorObj);
|
|
pmodify->setColor(color);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("layer");
|
|
lua_Object layer = lua_gettable();
|
|
if (lua_isnumber(layer)) {
|
|
// TODO pmodify->setLayer(lua_getnumber(layer));
|
|
warning("Not implemented: PrimitiveObject::setLayer. Layer: %d", (int)lua_getnumber(layer));
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("xoffset");
|
|
lua_Object xObj = lua_gettable();
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("yoffset");
|
|
lua_Object yObj = lua_gettable();
|
|
if (lua_isnumber(xObj) || lua_isnumber(yObj)) {
|
|
//int x = 0;
|
|
//int y = 0;
|
|
if (lua_isnumber(xObj))
|
|
/*x = (int)*/lua_getnumber(xObj);
|
|
if (lua_isnumber(yObj))
|
|
/*y = (int)*/lua_getnumber(yObj);
|
|
// TODO pmodify->setOffets(x, y);
|
|
assert(0);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("x");
|
|
xObj = lua_gettable();
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("y");
|
|
yObj = lua_gettable();
|
|
if (lua_isnumber(xObj) || lua_isnumber(yObj)) {
|
|
int x = -1;
|
|
int y = -1;
|
|
if (lua_isnumber(xObj)) {
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
x = (int)lua_getnumber(xObj);
|
|
else
|
|
x = (int)((lua_getnumber(xObj) + 1) * 320);
|
|
}
|
|
if (lua_isnumber(yObj)) {
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
y = (int)lua_getnumber(yObj);
|
|
else
|
|
y = (int)((1 - lua_getnumber(yObj)) * 240);
|
|
}
|
|
pmodify->setPos(x, y);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("x2");
|
|
xObj = lua_gettable();
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("y2");
|
|
yObj = lua_gettable();
|
|
if (lua_isnumber(xObj) || lua_isnumber(yObj)) {
|
|
int x = -1;
|
|
int y = -1;
|
|
if (lua_isnumber(xObj)) {
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
x = (int)lua_getnumber(xObj);
|
|
else
|
|
x = (int)((lua_getnumber(xObj) + 1) * 320);
|
|
}
|
|
if (lua_isnumber(yObj)) {
|
|
if (g_grim->getGameType() == GType_GRIM)
|
|
y = (int)lua_getnumber(yObj);
|
|
else
|
|
y = (int)((1 - lua_getnumber(yObj)) * 240);
|
|
}
|
|
pmodify->setEndpoint(x, y);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("width");
|
|
lua_Object width = lua_gettable();
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("height");
|
|
lua_Object height = lua_gettable();
|
|
if (lua_isnumber(width) || lua_isnumber(height)) {
|
|
//int x = -1;
|
|
//int y = -1;
|
|
if (lua_isnumber(width))
|
|
/*x = (int)*/lua_getnumber(width);
|
|
if (lua_isnumber(height))
|
|
/*y = (int)*/lua_getnumber(height);
|
|
// TODO pmodify->setSize(x, y);
|
|
}
|
|
}
|
|
|
|
void Lua_V1::DrawRectangle() {
|
|
Common::Point p1, p2;
|
|
Color color;
|
|
lua_Object x1Obj = lua_getparam(1);
|
|
lua_Object y1Obj = lua_getparam(2);
|
|
lua_Object x2Obj = lua_getparam(3);
|
|
lua_Object y2Obj = lua_getparam(4);
|
|
lua_Object tableObj = lua_getparam(5);
|
|
|
|
if (!lua_isnumber(x1Obj) || !lua_isnumber(y1Obj) || !lua_isnumber(x2Obj) || !lua_isnumber(y2Obj)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
|
|
if (g_grim->getGameType() == GType_GRIM) {
|
|
p1.x = (int)lua_getnumber(x1Obj);
|
|
p1.y = (int)lua_getnumber(y1Obj);
|
|
p2.x = (int)lua_getnumber(x2Obj);
|
|
p2.y = (int)lua_getnumber(y2Obj);
|
|
} else {
|
|
p1.x = (int)((lua_getnumber(x1Obj) + 1) * 320);
|
|
p1.y = (int)((1 - lua_getnumber(y1Obj)) * 240);
|
|
p2.x = (int)((lua_getnumber(x2Obj) + 1) * 320);
|
|
p2.y = (int)((1 - lua_getnumber(y2Obj)) * 240);
|
|
}
|
|
bool filled = false;
|
|
|
|
if (lua_istable(tableObj)) {
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("color");
|
|
lua_Object colorObj = lua_gettable();
|
|
if (lua_isuserdata(colorObj) && lua_tag(colorObj) == MKTAG('C','O','L','R')) {
|
|
color = getcolor(colorObj);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("filled");
|
|
lua_Object objFilled = lua_gettable();
|
|
if (!lua_isnil(objFilled))
|
|
filled = true;
|
|
}
|
|
|
|
PrimitiveObject *p = new PrimitiveObject();
|
|
p->createRectangle(p1, p2, color, filled);
|
|
lua_pushusertag(p->getId(), MKTAG('P','R','I','M')); // FIXME: we use PRIM usetag here
|
|
}
|
|
|
|
void Lua_V1::BlastRect() {
|
|
Common::Point p1, p2;
|
|
Color color;
|
|
lua_Object x1Obj = lua_getparam(1);
|
|
lua_Object y1Obj = lua_getparam(2);
|
|
lua_Object x2Obj = lua_getparam(3);
|
|
lua_Object y2Obj = lua_getparam(4);
|
|
lua_Object tableObj = lua_getparam(5);
|
|
|
|
if (!lua_isnumber(x1Obj) || !lua_isnumber(y1Obj) || !lua_isnumber(x2Obj) || !lua_isnumber(y2Obj)) {
|
|
lua_pushnil();
|
|
return;
|
|
}
|
|
if (g_grim->getGameType() == GType_GRIM) {
|
|
p1.x = (int)lua_getnumber(x1Obj);
|
|
p1.y = (int)lua_getnumber(y1Obj);
|
|
p2.x = (int)lua_getnumber(x2Obj);
|
|
p2.y = (int)lua_getnumber(y2Obj);
|
|
} else {
|
|
p1.x = (int)((lua_getnumber(x1Obj) + 1) * 320);
|
|
p1.y = (int)((1 - lua_getnumber(y1Obj)) * 240);
|
|
p2.x = (int)((lua_getnumber(x2Obj) + 1) * 320);
|
|
p2.y = (int)((1 - lua_getnumber(y2Obj)) * 240);
|
|
}
|
|
bool filled = false;
|
|
|
|
if (lua_istable(tableObj)) {
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("color");
|
|
lua_Object colorObj = lua_gettable();
|
|
if (lua_isuserdata(colorObj) && lua_tag(colorObj) == MKTAG('C','O','L','R')) {
|
|
color = getcolor(colorObj);
|
|
}
|
|
|
|
lua_pushobject(tableObj);
|
|
lua_pushstring("filled");
|
|
lua_Object objFilled = lua_gettable();
|
|
if (!lua_isnil(objFilled))
|
|
filled = true;
|
|
}
|
|
|
|
PrimitiveObject *p = new PrimitiveObject();
|
|
p->createRectangle(p1, p2, color, filled);
|
|
p->draw();
|
|
delete p;
|
|
}
|
|
|
|
void Lua_V1::KillPrimitive() {
|
|
lua_Object primObj = lua_getparam(1);
|
|
|
|
if (!lua_isuserdata(primObj) || lua_tag(primObj) != MKTAG('P','R','I','M'))
|
|
return;
|
|
|
|
PrimitiveObject *prim = getprimitive(primObj);
|
|
delete prim;
|
|
}
|
|
|
|
void Lua_V1::DimScreen() {
|
|
g_driver->storeDisplay();
|
|
g_driver->dimScreen();
|
|
}
|
|
|
|
void Lua_V1::DimRegion() {
|
|
int x = (int)lua_getnumber(lua_getparam(1));
|
|
int y = (int)lua_getnumber(lua_getparam(2));
|
|
int w = (int)lua_getnumber(lua_getparam(3));
|
|
int h = (int)lua_getnumber(lua_getparam(4));
|
|
float level = lua_getnumber(lua_getparam(5));
|
|
g_driver->dimRegion(x, y, w, h, level);
|
|
}
|
|
|
|
void Lua_V1::ScreenShot() {
|
|
int width = (int)lua_getnumber(lua_getparam(1));
|
|
int height = (int)lua_getnumber(lua_getparam(2));
|
|
GrimEngine::EngineMode mode = g_grim->getMode();
|
|
g_grim->setMode(GrimEngine::NormalMode);
|
|
g_grim->updateDisplayScene();
|
|
Bitmap *screenshot = g_driver->getScreenshot(width, height, false);
|
|
g_grim->setMode(mode);
|
|
if (screenshot) {
|
|
lua_pushusertag(screenshot->getId(), MKTAG('V','B','U','F'));
|
|
} else {
|
|
lua_pushnil();
|
|
}
|
|
}
|
|
|
|
void Lua_V1::SetGamma() {
|
|
lua_Object levelObj = lua_getparam(1);
|
|
|
|
if (!lua_isnumber(levelObj))
|
|
return;
|
|
double level = lua_getnumber(levelObj);
|
|
|
|
// FIXME: func(level)
|
|
warning("Lua_V1::SetGamma, implement opcode, level: %f", level);
|
|
}
|
|
|
|
void Lua_V1::Display() {
|
|
if (g_grim->getFlipEnable()) {
|
|
g_driver->flipBuffer();
|
|
}
|
|
}
|
|
|
|
void Lua_V1::EngineDisplay() {
|
|
// it enable/disable updating display
|
|
g_grim->setFlipEnable((bool)lua_getnumber(lua_getparam(1)));
|
|
}
|
|
|
|
void Lua_V1::ForceRefresh() {
|
|
// Nothing to do, no off-screen buffers
|
|
}
|
|
|
|
void Lua_V1::RenderModeUser() {
|
|
lua_Object param1 = lua_getparam(1);
|
|
if (!lua_isnil(param1) && g_grim->getMode() != GrimEngine::DrawMode) {
|
|
g_grim->setPreviousMode(g_grim->getMode());
|
|
g_movie->pause(true);
|
|
g_grim->setMode(GrimEngine::DrawMode);
|
|
} else if (lua_isnil(param1) && g_grim->getMode() == GrimEngine::DrawMode) {
|
|
g_movie->pause(false);
|
|
g_grim->setMode(g_grim->getPreviousMode());
|
|
}
|
|
}
|
|
|
|
void Lua_V1::IrisUp() {
|
|
lua_Object xObj = lua_getparam(1);
|
|
lua_Object yObj = lua_getparam(2);
|
|
lua_Object timeObj = lua_getparam(3);
|
|
|
|
g_grim->playIrisAnimation(Iris::Open, (int)lua_getnumber(xObj), (int)lua_getnumber(yObj), (int)lua_getnumber(timeObj));
|
|
}
|
|
|
|
void Lua_V1::IrisDown() {
|
|
lua_Object xObj = lua_getparam(1);
|
|
lua_Object yObj = lua_getparam(2);
|
|
lua_Object timeObj = lua_getparam(3);
|
|
|
|
g_grim->playIrisAnimation(Iris::Close, (int)lua_getnumber(xObj), (int)lua_getnumber(yObj), (int)lua_getnumber(timeObj));
|
|
}
|
|
|
|
void Lua_V1::PreRender() {
|
|
g_driver->renderBitmaps(getbool(1));
|
|
g_driver->renderZBitmaps(getbool(2));
|
|
}
|
|
|
|
void Lua_V1::ResetTextures() {
|
|
// nothing to implement here
|
|
}
|
|
|
|
} // end of namespace Grim
|