mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 03:12:36 +01:00
Initial Script Machine implementation + some ops
This commit is contained in:
parent
e0a0c4882f
commit
c3e2172f3b
@ -18,6 +18,7 @@ struct DynamicObjectData;
|
||||
struct WeaponData;
|
||||
class GameWorld;
|
||||
class TextureAtlas;
|
||||
class SCMFile;
|
||||
|
||||
/**
|
||||
* @brief Stores simple data about Textures such as transparency flags.
|
||||
@ -104,6 +105,8 @@ public:
|
||||
|
||||
void loadHandling(const std::string& path);
|
||||
|
||||
SCMFile* loadSCM(const std::string& path);
|
||||
|
||||
/**
|
||||
* Loads water level data
|
||||
*/
|
||||
|
@ -17,6 +17,8 @@ class VehicleObject;
|
||||
|
||||
class WeaponScan;
|
||||
|
||||
class ScriptMachine;
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
@ -78,6 +80,8 @@ public:
|
||||
*/
|
||||
bool defineItems(const std::string& name);
|
||||
|
||||
void runScript(const std::string& name);
|
||||
|
||||
/**
|
||||
* Loads an IPL into the game.
|
||||
* @param name The name of the IPL as it appears in the games' gta.dat
|
||||
@ -214,6 +218,8 @@ public:
|
||||
* Work related
|
||||
*/
|
||||
WorkContext* _work;
|
||||
|
||||
ScriptMachine* script;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
12
rwengine/include/script/Opcodes3.hpp
Normal file
12
rwengine/include/script/Opcodes3.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#ifndef _OPCODES_3_HPP_
|
||||
#define _OPCODES_3_HPP_
|
||||
#include <script/ScriptTypes.hpp>
|
||||
|
||||
struct Opcodes3 : SCMOpcodes
|
||||
{
|
||||
Opcodes3();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,12 +1,9 @@
|
||||
#pragma once
|
||||
#ifndef _SCMFILE_HPP_
|
||||
#define _SCMFILE_HPP_
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
typedef uint16_t SCMOpcode;
|
||||
typedef char SCMByte;
|
||||
#include <script/ScriptTypes.hpp>
|
||||
|
||||
/**
|
||||
* @brief Handles in-memory SCM file data including section offsets.
|
||||
|
@ -1,10 +1,16 @@
|
||||
#pragma once
|
||||
#ifndef _SCRIPTMACHINE_HPP_
|
||||
#define _SCRIPTMACHINE_HPP_
|
||||
#include <script/ScriptTypes.hpp>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#define SCM_CONDITIONAL_MASK 0xF000
|
||||
#define SCM_THREAD_LOCAL_SIZE 256
|
||||
|
||||
class SCMFile;
|
||||
|
||||
struct SCMException
|
||||
{
|
||||
@ -16,16 +22,18 @@ struct IllegalInstruction : SCMException
|
||||
{
|
||||
SCMOpcode opcode;
|
||||
unsigned int offset;
|
||||
std::string thread;
|
||||
|
||||
IllegalInstruction(SCMOpcode opcode, unsigned int offset)
|
||||
: opcode(opcode), offset(offset) { }
|
||||
IllegalInstruction(SCMOpcode opcode, unsigned int offset, const std::string& thread)
|
||||
: opcode(opcode), offset(offset), thread(thread) { }
|
||||
|
||||
std::string what() const {
|
||||
std::stringstream ss;
|
||||
ss << "Illegal Instruction " <<
|
||||
std::setfill('0') << std::setw(4) << std::hex << opcode <<
|
||||
" encountered at offset " <<
|
||||
std::setfill('0') << std::setw(4) << std::hex << offset;
|
||||
std::setfill('0') << std::setw(4) << std::hex << offset <<
|
||||
" on thread " << thread;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
@ -34,54 +42,56 @@ struct UnknownType : SCMException
|
||||
{
|
||||
SCMByte type;
|
||||
unsigned int offset;
|
||||
std::string thread;
|
||||
|
||||
UnknownType(SCMByte type, unsigned int offset)
|
||||
: type(type), offset(offset) {}
|
||||
UnknownType(SCMByte type, unsigned int offset, const std::string& thread)
|
||||
: type(type), offset(offset), thread(thread) {}
|
||||
|
||||
std::string what() const {
|
||||
std::stringstream ss;
|
||||
ss << "Unkown data type " <<
|
||||
std::setfill('0') << std::hex << static_cast<unsigned int>(type) <<
|
||||
" encountered at offset " <<
|
||||
std::setfill('0') << std::hex << offset;
|
||||
std::setfill('0') << std::hex << offset <<
|
||||
" on thread " << thread;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct SCMMicrocode {
|
||||
static SCMMicrocodeTable knownOps;
|
||||
|
||||
struct SCMThread
|
||||
{
|
||||
typedef unsigned int pc_t;
|
||||
|
||||
std::string name;
|
||||
int parameters;
|
||||
pc_t programCounter;
|
||||
/** Number of MS until the thread should be waked (-1 = yeilded) */
|
||||
int wakeCounter;
|
||||
SCMByte locals[SCM_THREAD_LOCAL_SIZE];
|
||||
};
|
||||
|
||||
typedef std::map<SCMOpcode, SCMMicrocode> SCMMicrocodeTable;
|
||||
class ScriptMachine
|
||||
{
|
||||
SCMFile* _file;
|
||||
SCMOpcodes* _ops;
|
||||
|
||||
SCMMicrocodeTable knownOps;
|
||||
std::vector<SCMThread> _activeThreads;
|
||||
|
||||
enum SCMType {
|
||||
EndOfArgList = 0x00,
|
||||
TInt32 = 0x01,
|
||||
TGlobal = 0x02,
|
||||
TLocal = 0x03,
|
||||
TInt8 = 0x04,
|
||||
TInt16 = 0x05,
|
||||
TFloat16 = 0x06,
|
||||
TString = 0x09,
|
||||
};
|
||||
void executeThread(SCMThread& t, int msPassed);
|
||||
|
||||
struct SCMTypeInfo {
|
||||
uint8_t size;
|
||||
};
|
||||
public:
|
||||
ScriptMachine(SCMFile* file, SCMOpcodes* ops);
|
||||
~ScriptMachine();
|
||||
|
||||
typedef std::map<SCMType, SCMTypeInfo> SCMTypeInfoTable;
|
||||
void startThread(SCMThread::pc_t start);
|
||||
|
||||
SCMTypeInfoTable typeData = {
|
||||
{TInt8, {1}},
|
||||
{TInt16, {2}},
|
||||
{TInt32, {4}},
|
||||
{TGlobal, {2}},
|
||||
{TLocal, {2}},
|
||||
{TFloat16,{2}},
|
||||
{EndOfArgList, {0}},
|
||||
SCMByte* getGlobals();
|
||||
|
||||
/**
|
||||
* @brief executes threads until they are all in waiting state.
|
||||
*/
|
||||
void execute(float dt);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
70
rwengine/include/script/ScriptTypes.hpp
Normal file
70
rwengine/include/script/ScriptTypes.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#ifndef _SCRIPTTYPES_HPP_
|
||||
#define _SCRIPTTYPES_HPP_
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
class ScriptMachine;
|
||||
class SCMThread;
|
||||
|
||||
typedef uint16_t SCMOpcode;
|
||||
typedef char SCMByte;
|
||||
|
||||
enum SCMType {
|
||||
EndOfArgList = 0x00,
|
||||
TInt32 = 0x01,
|
||||
TGlobal = 0x02,
|
||||
TLocal = 0x03,
|
||||
TInt8 = 0x04,
|
||||
TInt16 = 0x05,
|
||||
TFloat16 = 0x06,
|
||||
TString = 0x09,
|
||||
};
|
||||
|
||||
struct SCMTypeInfo {
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
typedef std::map<SCMType, SCMTypeInfo> SCMTypeInfoTable;
|
||||
|
||||
static SCMTypeInfoTable typeData = {
|
||||
{TInt8, {1}},
|
||||
{TInt16, {2}},
|
||||
{TInt32, {4}},
|
||||
{TGlobal, {2}},
|
||||
{TLocal, {2}},
|
||||
{TFloat16,{2}},
|
||||
{EndOfArgList, {0}},
|
||||
};
|
||||
|
||||
|
||||
struct SCMOpcodeParameter {
|
||||
SCMType type;
|
||||
union {
|
||||
int integer;
|
||||
float real;
|
||||
char string[8];
|
||||
void* globalPtr;
|
||||
int* globalInteger;
|
||||
float* globalReal;
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::vector<SCMOpcodeParameter> SCMParams;
|
||||
|
||||
struct SCMMicrocode {
|
||||
std::string name;
|
||||
int parameters;
|
||||
std::function<void (ScriptMachine*, SCMThread*, std::vector<SCMOpcodeParameter>*)> func;
|
||||
};
|
||||
|
||||
typedef std::map<SCMOpcode, SCMMicrocode> SCMMicrocodeTable;
|
||||
|
||||
struct SCMOpcodes
|
||||
{
|
||||
std::map<SCMOpcode, SCMMicrocode> codes;
|
||||
};
|
||||
|
||||
#endif
|
@ -7,6 +7,7 @@
|
||||
#include <loaders/LoaderCOL.hpp>
|
||||
#include <data/ObjectData.hpp>
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
@ -347,6 +348,25 @@ void GameData::loadHandling(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
SCMFile *GameData::loadSCM(const std::string &path)
|
||||
{
|
||||
std::ifstream f(datpath + "/" + path);
|
||||
|
||||
if(! f.is_open() ) return nullptr;
|
||||
|
||||
f.seekg(0, std::ios_base::end);
|
||||
unsigned int size = f.tellg();
|
||||
f.seekg(0);
|
||||
|
||||
char* buff = new char[size];
|
||||
f.read(buff, size);
|
||||
SCMFile* scm = new SCMFile;
|
||||
scm->loadFile(buff, size);
|
||||
delete buff;
|
||||
|
||||
return scm;
|
||||
}
|
||||
|
||||
void GameData::loadWaterpro(const std::string& path)
|
||||
{
|
||||
std::ifstream ifstr(path.c_str());
|
||||
|
@ -7,6 +7,9 @@
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <WorkContext.hpp>
|
||||
|
||||
#include <script/Opcodes3.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
|
||||
// 3 isn't enough to cause a factory.
|
||||
#include <objects/CharacterObject.hpp>
|
||||
#include <objects/InstanceObject.hpp>
|
||||
@ -67,7 +70,7 @@ public:
|
||||
|
||||
GameWorld::GameWorld(const std::string& path)
|
||||
: gameTime(0.f), gameData(path), renderer(this), randomEngine(rand()),
|
||||
_work( new WorkContext( this ) )
|
||||
_work( new WorkContext( this ) ), script(nullptr)
|
||||
{
|
||||
gameData.engine = this;
|
||||
}
|
||||
@ -167,6 +170,19 @@ bool GameWorld::defineItems(const std::string& name)
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameWorld::runScript(const std::string &name)
|
||||
{
|
||||
SCMFile* f = gameData.loadSCM(name);
|
||||
if( f ) {
|
||||
if( script ) delete script;
|
||||
|
||||
script = new ScriptMachine(f, new Opcodes3);
|
||||
}
|
||||
else {
|
||||
logError("Failed to load SCM: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameWorld::placeItems(const std::string& name)
|
||||
{
|
||||
auto i = gameData.iplLocations.find(name);
|
||||
|
94
rwengine/src/script/Opcodes3.cpp
Normal file
94
rwengine/src/script/Opcodes3.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include <script/Opcodes3.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
|
||||
SCMMicrocodeTable ops_global = {
|
||||
{ 0x0002,
|
||||
{
|
||||
"Jump",
|
||||
1,
|
||||
[](ScriptMachine*, SCMThread* t, SCMParams* p)
|
||||
{
|
||||
t->programCounter = p->at(0).integer;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ 0x0004,
|
||||
{
|
||||
"Set Global Integer",
|
||||
2,
|
||||
[](ScriptMachine*, SCMThread*, SCMParams* p)
|
||||
{
|
||||
*p->at(0).globalInteger = p->at(1).integer;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ 0x0005,
|
||||
{
|
||||
"Set Global Float",
|
||||
2,
|
||||
[](ScriptMachine*, SCMThread*, SCMParams* p)
|
||||
{
|
||||
*p->at(0).globalReal = p->at(1).real;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ 0x0015,
|
||||
{
|
||||
"Divide Global by Float",
|
||||
2,
|
||||
[](ScriptMachine*, SCMThread*, SCMParams* p)
|
||||
{
|
||||
*p->at(0).globalReal /= p->at(1).real;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ 0x0086,
|
||||
{
|
||||
"Set Global Float To Global",
|
||||
2,
|
||||
[](ScriptMachine*, SCMThread*, SCMParams* p)
|
||||
{
|
||||
*p->at(0).globalReal = *p->at(1).globalReal;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ 0x03A4,
|
||||
{
|
||||
"Name Thread",
|
||||
1,
|
||||
[](ScriptMachine*, SCMThread* t, SCMParams* p)
|
||||
{
|
||||
t->name = p->at(0).string;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
#define SCM_FUNC(c) [](ScriptMachine* m, SCMThread* t, SCMParams* p) c
|
||||
|
||||
#include <iostream>
|
||||
|
||||
SCMMicrocodeTable ops_game = {
|
||||
|
||||
{ 0x042C,
|
||||
{ "Set Total Missions", 1,
|
||||
SCM_FUNC({
|
||||
std::cout << "Total Missions: " << p->at(0).integer << std::endl;
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Opcodes3::Opcodes3()
|
||||
{
|
||||
codes.insert(ops_global.begin(), ops_global.end());
|
||||
codes.insert(ops_game.begin(), ops_game.end());
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
|
||||
void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
{
|
||||
if( t.wakeCounter > 0 ) {
|
||||
t.wakeCounter = std::max( t.wakeCounter - msPassed, 0 );
|
||||
}
|
||||
if( t.wakeCounter > 0 ) return;
|
||||
|
||||
while( t.wakeCounter == 0 ) {
|
||||
auto opcode = _file->read<SCMOpcode>(t.programCounter);
|
||||
auto it = _ops->codes.find(opcode);
|
||||
if( it == _ops->codes.end() ) throw IllegalInstruction(opcode, t.programCounter, t.name);
|
||||
t.programCounter += sizeof(SCMOpcode);
|
||||
|
||||
SCMMicrocode& code = it->second;
|
||||
|
||||
SCMParams parameters;
|
||||
|
||||
for( int p = 0; p < code.parameters; ++p ) {
|
||||
auto type_r = _file->read<SCMByte>(t.programCounter);
|
||||
auto type = static_cast<SCMType>(type_r);
|
||||
|
||||
if( type_r > 42 ) {
|
||||
// for implicit strings, we need the byte we just read.
|
||||
type = TString;
|
||||
}
|
||||
else {
|
||||
t.programCounter += sizeof(SCMByte);
|
||||
}
|
||||
|
||||
parameters.push_back(SCMOpcodeParameter { type, 0 });
|
||||
switch(type) {
|
||||
case TInt8:
|
||||
parameters.back().integer = _file->read<std::uint8_t>(t.programCounter);
|
||||
t.programCounter += sizeof(SCMByte);
|
||||
break;
|
||||
case TInt16:
|
||||
parameters.back().integer = _file->read<std::uint16_t>(t.programCounter);
|
||||
t.programCounter += sizeof(SCMByte) * 2;
|
||||
break;
|
||||
case TGlobal: {
|
||||
auto v = _file->read<std::uint16_t>(t.programCounter);
|
||||
parameters.back().globalPtr = getGlobals() + v;
|
||||
t.programCounter += sizeof(SCMByte) * 2;
|
||||
}
|
||||
break;
|
||||
case TInt32:
|
||||
parameters.back().integer = _file->read<std::uint32_t>(t.programCounter);
|
||||
t.programCounter += sizeof(SCMByte) * 4;
|
||||
break;
|
||||
case TString:
|
||||
std::copy(_file->data()+t.programCounter, _file->data()+t.programCounter+8,
|
||||
parameters.back().string);
|
||||
t.programCounter += sizeof(SCMByte) * 8;
|
||||
break;
|
||||
case TFloat16:
|
||||
parameters.back().real = (float)(_file->read<std::uint16_t>(t.programCounter)) / 16.f;
|
||||
t.programCounter += sizeof(SCMByte) * 2;
|
||||
break;
|
||||
default:
|
||||
throw UnknownType(type, t.programCounter, t.name);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
code.func(this, &t, ¶meters);
|
||||
}
|
||||
|
||||
if( t.wakeCounter == -1 ) {
|
||||
t.wakeCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptMachine::ScriptMachine(SCMFile *file, SCMOpcodes *ops)
|
||||
: _file(file), _ops(ops)
|
||||
{
|
||||
startThread(0);
|
||||
}
|
||||
|
||||
ScriptMachine::~ScriptMachine()
|
||||
{
|
||||
delete _file;
|
||||
delete _ops;
|
||||
}
|
||||
|
||||
void ScriptMachine::startThread(SCMThread::pc_t start)
|
||||
{
|
||||
SCMThread t;
|
||||
for(int i = 0; i < SCM_THREAD_LOCAL_SIZE; ++i) {
|
||||
t.locals[i] = 0;
|
||||
}
|
||||
t.name = "THREAD";
|
||||
t.programCounter = start;
|
||||
t.wakeCounter = 0;
|
||||
_activeThreads.push_back(t);
|
||||
}
|
||||
|
||||
SCMByte *ScriptMachine::getGlobals()
|
||||
{
|
||||
return _file->data() + _file->getGlobalSection();
|
||||
}
|
||||
|
||||
void ScriptMachine::execute(float dt)
|
||||
{
|
||||
int ms = dt * 1000.f;
|
||||
for(auto& thread : _activeThreads) {
|
||||
executeThread( thread, ms );
|
||||
}
|
||||
}
|
@ -7,9 +7,20 @@
|
||||
#include <objects/ItemPickup.hpp>
|
||||
#include <render/Model.hpp>
|
||||
#include <items/WeaponItem.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
|
||||
IngameState::IngameState()
|
||||
IngameState::IngameState(bool test)
|
||||
: _player(nullptr), _playerCharacter(nullptr)
|
||||
{
|
||||
if( test ) {
|
||||
startTest();
|
||||
}
|
||||
else {
|
||||
getWorld()->runScript("data/main.scm");
|
||||
}
|
||||
}
|
||||
|
||||
void IngameState::startTest()
|
||||
{
|
||||
_playerCharacter = getWorld()->createPedestrian(1, {-1000.f, -990.f, 13.f});
|
||||
_player = new PlayerController(_playerCharacter);
|
||||
@ -72,17 +83,7 @@ void IngameState::spawnPlayerVehicle()
|
||||
}
|
||||
}
|
||||
|
||||
void IngameState::enter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IngameState::exit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IngameState::tick(float dt)
|
||||
void IngameState::updateView()
|
||||
{
|
||||
float qpi = glm::half_pi<float>();
|
||||
|
||||
@ -153,6 +154,34 @@ void IngameState::tick(float dt)
|
||||
setViewParameters( viewPos + localview * viewFraction, {localX, _lookAngles.y} );
|
||||
}
|
||||
|
||||
void IngameState::enter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IngameState::exit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IngameState::tick(float dt)
|
||||
{
|
||||
if( _player ) {
|
||||
updateView();
|
||||
}
|
||||
|
||||
if( getWorld()->script ) {
|
||||
try {
|
||||
getWorld()->script->execute(dt);
|
||||
}
|
||||
catch( SCMException& ex ) {
|
||||
std::cerr << ex.what() << std::endl;
|
||||
getWorld()->logError( ex.what() );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IngameState::handleEvent(const sf::Event &event)
|
||||
{
|
||||
switch(event.type) {
|
||||
|
@ -13,10 +13,13 @@ class IngameState : public State
|
||||
glm::vec2 _lookAngles;
|
||||
glm::vec3 _movement;
|
||||
public:
|
||||
IngameState();
|
||||
IngameState(bool test = false);
|
||||
|
||||
void startTest();
|
||||
void spawnPlayerVehicle();
|
||||
|
||||
void updateView();
|
||||
|
||||
virtual void enter();
|
||||
virtual void exit();
|
||||
|
||||
|
@ -6,7 +6,8 @@ MenuState::MenuState()
|
||||
{
|
||||
Menu *m = new Menu(getFont());
|
||||
m->offset = glm::vec2(50.f, 100.f);
|
||||
m->addEntry(Menu::lambda("Test", [] { StateManager::get().enter(new IngameState); }));
|
||||
m->addEntry(Menu::lambda("Start", [] { StateManager::get().enter(new IngameState); }));
|
||||
m->addEntry(Menu::lambda("Test", [] { StateManager::get().enter(new IngameState(true)); }));
|
||||
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));
|
||||
m->addEntry(Menu::lambda("Exit", [] { getWindow().close(); }));
|
||||
this->enterMenu(m);
|
||||
|
@ -49,7 +49,7 @@ void dumpOpcodes(SCMFile* scm, unsigned int offset, unsigned int size)
|
||||
|
||||
// If we don't know the size of the operator's parameters we can't jump over it.
|
||||
if( opit == knownOps.end() ) {
|
||||
throw IllegalInstruction(op, i);
|
||||
throw IllegalInstruction(op, i, "");
|
||||
}
|
||||
|
||||
std::cout << std::hex << std::setfill('0') << std::right <<
|
||||
@ -67,7 +67,7 @@ void dumpOpcodes(SCMFile* scm, unsigned int offset, unsigned int size)
|
||||
auto typeit = typeData.find(static_cast<SCMType>(datatype));
|
||||
if( typeit == typeData.end()) {
|
||||
if( datatype < 0x06 ) {
|
||||
throw UnknownType(datatype, i);
|
||||
throw UnknownType(datatype, i, "");
|
||||
}
|
||||
else {
|
||||
datatype = TString;
|
||||
|
29
tests/test_scriptmachine.cpp
Normal file
29
tests/test_scriptmachine.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_globals.hpp"
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
|
||||
SCMByte data[] = {
|
||||
0x02,0x00,0x01,0x08,0x00,0x00,0x00,0x00,
|
||||
0x02,0x00,0x01,0x18,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x02,0x00,0x01,0x28,0x00,0x00,0x00,0x00,
|
||||
0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ScriptMachineTests)
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scmfile_test)
|
||||
{
|
||||
SCMFile f;
|
||||
f.loadFile(data, sizeof(data));
|
||||
|
||||
BOOST_CHECK_EQUAL( f.getModelSection(), 0x10 );
|
||||
BOOST_CHECK_EQUAL( f.getMissionSection(), 0x20 );
|
||||
BOOST_CHECK_EQUAL( f.getCodeSection(), 0x28 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user