Fixes invalid text pasting, Adds API to VM, allow program loading in Lua and assigning to update function

This commit is contained in:
Simon Robertshaw
2012-09-14 22:03:14 +01:00
parent 685be24ffa
commit 486b34ebe5
7 changed files with 276 additions and 40 deletions

View File

@@ -20,6 +20,7 @@
#include "dialogues/TextPrompt.h" #include "dialogues/TextPrompt.h"
#include "dialogues/ConfirmPrompt.h" #include "dialogues/ConfirmPrompt.h"
#include "simulation/Simulation.h" #include "simulation/Simulation.h"
#include "virtualmachine/VirtualMachine.h"
#include "game/GameModel.h" #include "game/GameModel.h"
#include "LuaScriptHelper.h" #include "LuaScriptHelper.h"
#include "client/HTTP.h" #include "client/HTTP.h"
@@ -63,6 +64,7 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m):
initInterfaceAPI(); initInterfaceAPI();
initRendererAPI(); initRendererAPI();
initElementsAPI(); initElementsAPI();
initVirtualMachineAPI();
//Old TPT API //Old TPT API
int i = 0, j; int i = 0, j;
@@ -651,6 +653,42 @@ void LuaScriptInterface::initElementsAPI()
} }
} }
vm::VirtualMachine * LuaScriptInterface::updateVirtualMachines[PT_NUM];
int LuaScriptInterface::updateVM(UPDATE_FUNC_ARGS)
{
vm::VirtualMachine * vMachine = updateVirtualMachines[parts[i].type];
vm::word w;
int argAddr = 0, argCount = 5;
vMachine->sim = sim;
/* Set up call. */
vMachine->OpPUSH(w); //Pointless null in stack
w.int4 = (argCount + 2) * sizeof(vm::word);
vMachine->OpENTER(w);
argAddr = 8;
//Arguments
w.int4 = i; vMachine->Marshal(argAddr, w); argAddr += 4;
w.int4 = x; vMachine->Marshal(argAddr, w); argAddr += 4;
w.int4 = y; vMachine->Marshal(argAddr, w); argAddr += 4;
w.int4 = nt; vMachine->Marshal(argAddr, w); argAddr += 4;
w.int4 = surround_space; vMachine->Marshal(argAddr, w); argAddr += 4;
w.int4 = 0;
vMachine->Push(w);
vMachine->OpCALL(w);
vMachine->Run();
w.int4 = (argCount + 2) * sizeof(vm::word);
vMachine->OpLEAVE(w);
vMachine->OpPOP(w); //Pop pointless null
vMachine->End();
return 0;
}
int LuaScriptInterface::elements_loadDefault(lua_State * l) int LuaScriptInterface::elements_loadDefault(lua_State * l)
{ {
int args = lua_gettop(l); int args = lua_gettop(l);
@@ -957,6 +995,11 @@ int LuaScriptInterface::elements_property(lua_State * l)
lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX); lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX);
luacon_sim->elements[id].Update = &luacon_elementReplacement; luacon_sim->elements[id].Update = &luacon_elementReplacement;
} }
else if(lua_type(l, 3) == LUA_TLIGHTUSERDATA)
{
updateVirtualMachines[id] = (vm::VirtualMachine*)lua_touserdata(l, 3);
luacon_sim->elements[id].Update = &updateVM;
}
else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, 3)) else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, 3))
{ {
lua_el_func[id] = 0; lua_el_func[id] = 0;
@@ -1061,6 +1104,41 @@ int LuaScriptInterface::elements_free(lua_State * l)
return 0; return 0;
} }
void LuaScriptInterface::initVirtualMachineAPI()
{
//Methods
struct luaL_reg vmAPIMethods [] = {
{"loadProgram", virtualMachine_loadProgram},
{NULL, NULL}
};
luaL_register(l, "virtualMachine", vmAPIMethods);
//elem shortcut
lua_getglobal(l, "virtualMachine");
lua_setglobal(l, "vm");
int vmAPI = lua_gettop(l);
}
int LuaScriptInterface::virtualMachine_loadProgram(lua_State * l)
{
luaL_checktype(l, 1, LUA_TSTRING);
vm::VirtualMachine * newVM = new vm::VirtualMachine(1);
try
{
const char * tempString = lua_tostring(l, 1);
int tempStringLength = lua_strlen(l, 1);
std::vector<char> programData(tempString, tempString+tempStringLength);
newVM->LoadProgram(programData);
}
catch(std::exception & e)
{
return luaL_error(l, "Unable to load program");
}
lua_pushlightuserdata(l, newVM);
return 1;
}
bool LuaScriptInterface::OnBrushChanged(int brushType, int rx, int ry) bool LuaScriptInterface::OnBrushChanged(int brushType, int rx, int ry)
{ {

View File

@@ -23,6 +23,12 @@ namespace ui
class Window; class Window;
} }
namespace vm
{
class VirtualMachine;
}
//Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :( //Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :(
#define LOCAL_LUA_DIR "Lua" #define LOCAL_LUA_DIR "Lua"
@@ -59,6 +65,9 @@ class LuaScriptInterface: public CommandInterface {
static int renderer_colourMode(lua_State * l); static int renderer_colourMode(lua_State * l);
//Elements //Elements
static vm::VirtualMachine * updateVirtualMachines[PT_NUM];
static int updateVM(UPDATE_FUNC_ARGS);
//
void initElementsAPI(); void initElementsAPI();
static int elements_allocate(lua_State * l); static int elements_allocate(lua_State * l);
static int elements_element(lua_State * l); static int elements_element(lua_State * l);
@@ -71,6 +80,10 @@ class LuaScriptInterface: public CommandInterface {
static int interface_showWindow(lua_State * l); static int interface_showWindow(lua_State * l);
static int interface_closeWindow(lua_State * l); static int interface_closeWindow(lua_State * l);
static int interface_addComponent(lua_State * l); static int interface_addComponent(lua_State * l);
//VM
void initVirtualMachineAPI();
static int virtualMachine_loadProgram(lua_State * l);
public: public:
ui::Window * Window; ui::Window * Window;
lua_State *l; lua_State *l;

View File

@@ -191,6 +191,21 @@ void Textbox::pasteIntoSelection()
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound(); cursor = getLowerSelectionBound();
} }
for(std::string::iterator iter = newText.begin(), end = newText.end(); iter != end; ++iter)
{
if(!CharacterValid(*iter))
{
if(inputType == All)
{
if(*iter == '\n' || *iter == '\r')
*iter = ' ';
else
*iter = '?';
}
else
*iter = '0';
}
}
backingText.insert(cursor, newText); backingText.insert(cursor, newText);
cursor = cursor+newText.length(); cursor = cursor+newText.length();
ClearSelection(); ClearSelection();

View File

@@ -86,4 +86,15 @@ namespace vm
} }
~OutOfMemoryException() throw() {}; ~OutOfMemoryException() throw() {};
}; };
class InvalidProgramException: public RuntimeException
{
public:
InvalidProgramException() {}
const char * what() const throw()
{
return "Could not load program";
}
~InvalidProgramException() throw() {};
};
} }

View File

@@ -61,9 +61,9 @@ namespace vm
TRAPDEF(partCreate) TRAPDEF(partCreate)
{ {
//Push<int4_t>(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4)); printf("%d, %d, %d, %d\n", ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4);
printf("create_part(%d, %d, %d, %d)\n", ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4);
Push<int4_t>(0); Push<int4_t>(0);
//Push<int4_t>(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4));
} }
TRAPDEF(partChangeType) TRAPDEF(partChangeType)

View File

@@ -74,86 +74,87 @@ namespace vm
} }
/* Read one octet from file. */ /* Read one octet from file. */
int VirtualMachine::readByte(FILE *qvmfile) int VirtualMachine::readByte(std::istream & input)
{ {
int o; int o;
o = fgetc(qvmfile); o = input.get();
if (o < 0) o = 0; /* EOF (hack) */ if (o < 0) o = 0; /* EOF (hack) */
return o; return o;
} }
/* Read little-endian 32-bit integer from file. */ /* Read little-endian 32-bit integer from file. */
int VirtualMachine::readInt(FILE *qvmfile) int VirtualMachine::readInt(std::istream & input)
{ {
int a, b, c, d, n; int a, b, c, d, n;
a = readByte(qvmfile); a = readByte(input);
b = readByte(qvmfile); b = readByte(input);
c = readByte(qvmfile); c = readByte(input);
d = readByte(qvmfile); d = readByte(input);
n = (a) | (b << 8) | (c << 16) | (d << 24); n = (a) | (b << 8) | (c << 16) | (d << 24);
return n; return n;
} }
int VirtualMachine::LoadProgram(char * filename) int VirtualMachine::readProgram(std::istream & input)
{ {
FILE * qvmfile = fopen(filename, "rb");
qvm_header_t qvminfo; qvm_header_t qvminfo;
int i, n; int i, n;
uint1_t x[4]; uint1_t x[4];
word w; word w;
DEBUGTRACE("Loading file...\n"); DEBUGTRACE("Loading file...\n");
qvminfo.magic = readInt(qvmfile); /* magic. */ qvminfo.magic = readInt(input); /* magic. */
if (qvminfo.magic != QVM_MAGIC) if (qvminfo.magic != QVM_MAGIC)
{ {
DEBUGTRACE("Invalid magic"); DEBUGTRACE("Invalid magic");
//q3vm_error("Does not appear to be a QVM file."); throw InvalidProgramException();
/* XXX: option to force continue. */ //q3vm_error("Does not appear to be a QVM file.");
return 0; /* XXX: option to force continue. */
} return 0;
}
DEBUGTRACE("Magic OK\n"); DEBUGTRACE("Magic OK\n");
/* variable-length instructions mean instruction count != code length */ /* variable-length instructions mean instruction count != code length */
qvminfo.inscount = readInt(qvmfile); qvminfo.inscount = readInt(input);
qvminfo.codeoff = readInt(qvmfile); qvminfo.codeoff = readInt(input);
qvminfo.codelen = readInt(qvmfile); qvminfo.codelen = readInt(input);
qvminfo.dataoff = readInt(qvmfile); qvminfo.dataoff = readInt(input);
qvminfo.datalen = readInt(qvmfile); qvminfo.datalen = readInt(input);
qvminfo.litlen = readInt(qvmfile); qvminfo.litlen = readInt(input);
qvminfo.bsslen = readInt(qvmfile); qvminfo.bsslen = readInt(input);
/* Code segment should follow... */ /* Code segment should follow... */
/* XXX: use fseek with SEEK_CUR? */ /* XXX: use fseek with SEEK_CUR? */
DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile)); DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, input.tellg());
// rom = (q3vm_rom_t*)(hunk); /* ROM-in-hunk */ // rom = (q3vm_rom_t*)(hunk); /* ROM-in-hunk */
rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0])); rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
while (ftell(qvmfile) < qvminfo.codeoff) while (input.tellg() < qvminfo.codeoff)
readByte(qvmfile); readByte(input);
while (romSize < qvminfo.inscount) while (romSize < qvminfo.inscount)
{ {
n = readByte(qvmfile); n = readByte(input);
w.int4 = 0; w.int4 = 0;
if ((i = opcodeParameterSize(n))) if ((i = opcodeParameterSize(n)))
{ {
x[0] = x[1] = x[2] = x[3] = 0; x[0] = x[1] = x[2] = x[3] = 0;
fread(&x, 1, i, qvmfile); input.readsome((char*)x, 4);
w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
} }
rom[romSize].Operation = n; rom[romSize].Operation = n;
rom[romSize].Parameter = w; rom[romSize].Parameter = w;
romSize++; romSize++;
} }
DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen); DEBUGTRACE("After loading code: at %d, should be %d\n", input.tellg(), qvminfo.codeoff + qvminfo.codelen);
/* Then data segment. */ /* Then data segment. */
// ram = hunk + ((romlen + 3) & ~3); /* RAM-in-hunk */ // ram = hunk + ((romlen + 3) & ~3); /* RAM-in-hunk */
ram = hunk; ram = hunk;
DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile)); DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, input.tellg());
while (ftell(qvmfile) < qvminfo.dataoff) while (input.tellg() < qvminfo.dataoff)
readByte(qvmfile); readByte(input);
for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++) for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
{ {
i = fread(&x, 1, sizeof(x), qvmfile); i = input.readsome((char*)x, sizeof(x));
w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
*((word*)(ram + ramSize)) = w; *((word*)(ram + ramSize)) = w;
ramSize += sizeof(word); ramSize += sizeof(word);
@@ -164,7 +165,7 @@ namespace vm
DEBUGTRACE("Loading .lit\n"); DEBUGTRACE("Loading .lit\n");
for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++) for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
{ {
i = fread(&x, 1, sizeof(x), qvmfile); i = input.readsome((char*)x, sizeof(x));
memcpy(&(w.uint1), &x, sizeof(x)); /* no byte-swapping. */ memcpy(&(w.uint1), &x, sizeof(x)); /* no byte-swapping. */
*((word*)(ram + ramSize)) = w; *((word*)(ram + ramSize)) = w;
ramSize += sizeof(word); ramSize += sizeof(word);
@@ -187,8 +188,8 @@ namespace vm
{ {
int stacksize = 0x10000; int stacksize = 0x10000;
dataStack = ramSize - (stacksize / 2); dataStack = ramSize - (stacksize / 2);
//returnStack = ramSize; returnStack = ramSize;
returnStack = dataStack+4; //returnStack = dataStack+4;
RP = returnStack; RP = returnStack;
DP = dataStack; DP = dataStack;
} }
@@ -201,6 +202,122 @@ namespace vm
return 1; return 1;
} }
int VirtualMachine::LoadProgram(std::vector<char> data)
{
/*class vectorwrapbuf : public std::basic_streambuf<char, std::char_traits<char> >
{
public:
vectorwrapbuf(std::vector<char> &vec) {
setg(vec.data(), vec.data(), vec.data() + vec.size());
}
};
vectorwrapbuf databuf(data);
std::istream is(&databuf);
return readProgram(is);*/
std::stringstream ss(std::string(data.begin(), data.end()));
return readProgram((std::istream &)ss);
}
int VirtualMachine::LoadProgram(char * filename)
{
/*FILE * qvmfile = fopen(filename, "rb");
qvm_header_t qvminfo;
int i, n;
uint1_t x[4];
word w;
DEBUGTRACE("Loading file...\n");
qvminfo.magic = readInt(qvmfile);
if (qvminfo.magic != QVM_MAGIC)
{
DEBUGTRACE("Invalid magic");
return 0;
}
DEBUGTRACE("Magic OK\n");
qvminfo.inscount = readInt(qvmfile);
qvminfo.codeoff = readInt(qvmfile);
qvminfo.codelen = readInt(qvmfile);
qvminfo.dataoff = readInt(qvmfile);
qvminfo.datalen = readInt(qvmfile);
qvminfo.litlen = readInt(qvmfile);
qvminfo.bsslen = readInt(qvmfile);
DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile));
rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
while (ftell(qvmfile) < qvminfo.codeoff)
readByte(qvmfile);
while (romSize < qvminfo.inscount)
{
n = readByte(qvmfile);
w.int4 = 0;
if ((i = opcodeParameterSize(n)))
{
x[0] = x[1] = x[2] = x[3] = 0;
fread(&x, 1, i, qvmfile);
w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
}
rom[romSize].Operation = n;
rom[romSize].Parameter = w;
romSize++;
}
DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen);
ram = hunk;
DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile));
while (ftell(qvmfile) < qvminfo.dataoff)
readByte(qvmfile);
for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
{
i = fread(&x, 1, sizeof(x), qvmfile);
w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
*((word*)(ram + ramSize)) = w;
ramSize += sizeof(word);
}
DEBUGTRACE("Loading .lit\n");
for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
{
i = fread(&x, 1, sizeof(x), qvmfile);
memcpy(&(w.uint1), &x, sizeof(x));
*((word*)(ram + ramSize)) = w;
ramSize += sizeof(word);
}
DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen);
ramSize += qvminfo.bsslen;
hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4);
DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize);
if (ramSize > hunkSize)
{
throw OutOfMemoryException();
return 0;
}
{
int stacksize = 0x10000;
dataStack = ramSize - (stacksize / 2);
//returnStack = ramSize;
returnStack = dataStack+4;
RP = returnStack;
DP = dataStack;
}
PC = romSize + 1;
ramMask = ramSize;
return 1;*/
}
void VirtualMachine::End() void VirtualMachine::End()
{ {
PC = romSize+1; PC = romSize+1;

View File

@@ -117,8 +117,9 @@ namespace vm
}; };
#undef OPDEF #undef OPDEF
int readByte(FILE *qvmfile); int readProgram(std::istream & input);
int readInt(FILE *qvmfile); int readByte(std::istream & input);
int readInt(std::istream & input);
int opcodeParameterSize(int opcode); int opcodeParameterSize(int opcode);
int syscall(int programCounter); int syscall(int programCounter);
@@ -153,6 +154,7 @@ public:
virtual ~VirtualMachine(); virtual ~VirtualMachine();
int LoadProgram(char * filename); int LoadProgram(char * filename);
int LoadProgram(std::vector<char> fileData);
int Run(); int Run();
int CallInterpreted(int address); int CallInterpreted(int address);
void End(); void End();