<commit_before>// $RCSfile: $
// $Revision: $ $Date: $
// Auth: Samson Bonfante (bonfante@steptools.com)
//
// Copyright (c) 1991-2016 by STEP Tools Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "nodeMachineState.h"
#include "nodeUtils.h"
#include <stp_schema.h>
#include <future>

StixSimGeomType GeomTypeFromString(char* typ)
{
    if (!strcmp(typ, "MESH"))
    {
	return STIXSIM_GT_MESH;
    }
    if (!strcmp(typ, "POLYLINE"))
    {
	return STIXSIM_GT_POLYLINE;
    }
    else
    {
	return STIXSIM_GT_NULL;
    }
}

NAN_METHOD(machineState::New)
{

    if (info.IsConstructCall()){
    	if (info.Length() == 0){
    	    return;
    	}
        else if(info.Length() == 1 || (info.Length() == 2 && info[1]->BooleanValue() == false)){
        	char * b;
        	v8StringToChar(info[0], b);
        	machineState * ms = new machineState();
        	ms->_ms = MachineState::InitializeState(b);
        	delete[] b;
        	ms->Wrap(info.This());
        	info.GetReturnValue().Set(info.This());
        }
        else if(info.Length() == 2){
            char * b;
            v8StringToChar(info[0], b);
            bool c = info[1]->BooleanValue();
            machineState * ms = new machineState();
            ms->_ms = MachineState::InitializeState(b, c);
            delete[] b;
            ms->Wrap(info.This());
            info.GetReturnValue().Set(info.This());
        }
        else{
            return;
        }
    }
    else{
	   return;
    }
}

NAN_METHOD(machineState::AdvanceState)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int rtnval = ms->_ms->AdvanceState();
    v8::Local<v8::Int32> rtn = Nan::New(rtnval);
    v8::Local<v8::Promise::Resolver> pmise = v8::Promise::Resolver::New(info.GetIsolate());
    std::async(std::launch::async, [pmise, rtn, ms]() {ms->_ms->WaitForStateUpdate(); pmise->Resolve(rtn); });
    info.GetReturnValue().Set(pmise);
}

NAN_METHOD(machineState::GetGeometryJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    //This function has a 2 argument and a no argument version.
    if (info.Length() == 0)
    {
	RoseStringObject rtn;
	ms->_ms->GetGeometryJSON(rtn);
	info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
	return;
    }
    else
    {
	if (info.Length() != 2) return; // invalid number of arguments
	if (info[0]->IsUndefined()) return; //No Given ID
	if (info[1]->IsUndefined()) return; //No Given Typ
	if (!info[0]->IsString()) return; //ID is not valid
	if (!info[1]->IsString()) return; //Typ is not valid
	char * id;
	v8StringToChar(info[0], id);
	RoseObject * obj = ms->_ms->FindObjectByID(id);
	if (!obj)
	{
	    delete[] id;
	    return; //No Object Associated with given ID
	}
	char * typ;
	v8StringToChar(info[1], typ);
	RoseStringObject rtn;
	ms->_ms->GetGeometryJSON(rtn, id, obj, GeomTypeFromString(typ));
	info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
	delete[] id;
	delete[] typ;
	return;
    }
}

NAN_METHOD(machineState::GetDeltaGeometryJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
	return;
    if (!info[0]->IsNumber()) { // argument of wrong type
	return;
    }
    RoseStringObject rtn;
    ms->_ms->GetDynamicGeometryJSON(rtn,Nan::To<int32_t>(info[0]).FromJust());
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GetDeltaStateJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    RoseStringObject rtn;
    ms->_ms->GetStateJSON(rtn,false);
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GetKeyStateJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    RoseStringObject rtn;
    ms->_ms->GetStateJSON(rtn,true);
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GoToWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
    return;
    RoseObject * obj = ms->_ms->design()->findByEntityId(Nan::To<int32_t>(info[0]).FromJust());
    if (!obj)
    return;
    stp_machining_workingstep * ws = ROSE_CAST(stp_machining_workingstep, obj);
    if(!ws)
    return;
    int rtnval = ms->_ms->GoToWS(ws);
    info.GetReturnValue().Set(rtnval);
    return;
}

NAN_METHOD(machineState::GetEIDfromUUID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
    return;
    if(!info[0]->IsString())
    return;
    char * uuid = 0;
    v8StringToChar(info[0], uuid);
    RoseObject * obj = ms->_ms->FindObjectByID(uuid);
    if (!obj)
    return;
    int rtnval = obj->entity_id();
    info.GetReturnValue().Set(Nan::New(rtnval));
    return;
}

NAN_METHOD(machineState::LoadMachine)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info[0]->IsUndefined()) return;
    char * b;
    v8StringToChar(info[0], b);
    bool success = ms->_ms->SetMachine(b);
    info.GetReturnValue().Set(success);
    delete[] b;

    return;
}

NAN_METHOD(machineState::NextWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    ms->_ms->GoToNextWS();
    return;
}

NAN_METHOD(machineState::PrevWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    ms->_ms->GoToPrevWS();
    return;
}

NAN_METHOD(machineState::GetPrevWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetPrevWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetNextWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetNextWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetCurrentFeedrate)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    double feed = ms->_ms->GetCurrentFeedrate();
    info.GetReturnValue().Set(feed);
    return;
}

NAN_METHOD(machineState::GetCurrentSpindleSpeed)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    double speed = ms->_ms->GetCurrentSpindleSpeed();
    info.GetReturnValue().Set(speed);
    return;
}

NAN_METHOD(machineState::SetToolPosition)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 6) return;    // bad input

    double xyz[3];
    double ijk[3];

    for (int i=0; i<3; i++) {
	if (info[i]->IsUndefined()) return; // throw ex
	if (!info[i]->IsNumber()) return; // needs to be a number

	Nan::Maybe<double> num = Nan::To<double>(info[i]);

	xyz[i] = num.FromJust();
    }

    for (int i=3; i<6; i++) {
	if (info[i]->IsUndefined()) return; // bad input
	if (!info[i]->IsNumber()) return; // check for number

	Nan::Maybe<double> num = Nan::To<double>(info[i]);

	ijk[i - 3] = num.FromJust();
    }

    ms->_ms->SetToolPosition(xyz, ijk);
    return;
}

NAN_MODULE_INIT(machineState::Init)
{
    v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
    tpl->SetClassName(Nan::New("machineState").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    Nan::SetPrototypeMethod(tpl, "AdvanceState", AdvanceState);
    Nan::SetPrototypeMethod(tpl, "GetGeometryJSON", GetGeometryJSON);
    Nan::SetPrototypeMethod(tpl, "GetDeltaGeometryJSON", GetDeltaGeometryJSON);
    Nan::SetPrototypeMethod(tpl, "GetDeltaStateJSON", GetDeltaStateJSON);
    Nan::SetPrototypeMethod(tpl, "GetKeyStateJSON", GetKeyStateJSON);
    Nan::SetPrototypeMethod(tpl, "GoToWS", GoToWS);
    Nan::SetPrototypeMethod(tpl, "GetEIDfromUUID", GetEIDfromUUID);
    Nan::SetPrototypeMethod(tpl, "LoadMachine", LoadMachine);
    Nan::SetPrototypeMethod(tpl, "NextWS", NextWS);
    Nan::SetPrototypeMethod(tpl, "PrevWS", PrevWS);
    Nan::SetPrototypeMethod(tpl, "GetPrevWSID", GetPrevWSID);
    Nan::SetPrototypeMethod(tpl, "GetWSID", GetWSID);
    Nan::SetPrototypeMethod(tpl, "GetNextWSID", GetNextWSID);
    Nan::SetPrototypeMethod(tpl, "GetCurrentFeedrate", GetCurrentFeedrate);
    Nan::SetPrototypeMethod(tpl, "GetCurrentSpindleSpeed", GetCurrentSpindleSpeed);
    Nan::SetPrototypeMethod(tpl, "SetToolPosition", SetToolPosition);
    constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
    Nan::Set(target, Nan::New("machineState").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());

    MachineState::init();
}
<commit_msg>Added promise to SetToolPosition, as it needed it.<commit_after>// $RCSfile: $
// $Revision: $ $Date: $
// Auth: Samson Bonfante (bonfante@steptools.com)
//
// Copyright (c) 1991-2016 by STEP Tools Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "nodeMachineState.h"
#include "nodeUtils.h"
#include <stp_schema.h>
#include <future>

StixSimGeomType GeomTypeFromString(char* typ)
{
    if (!strcmp(typ, "MESH"))
    {
	return STIXSIM_GT_MESH;
    }
    if (!strcmp(typ, "POLYLINE"))
    {
	return STIXSIM_GT_POLYLINE;
    }
    else
    {
	return STIXSIM_GT_NULL;
    }
}

NAN_METHOD(machineState::New)
{

    if (info.IsConstructCall()){
    	if (info.Length() == 0){
    	    return;
    	}
        else if(info.Length() == 1 || (info.Length() == 2 && info[1]->BooleanValue() == false)){
        	char * b;
        	v8StringToChar(info[0], b);
        	machineState * ms = new machineState();
        	ms->_ms = MachineState::InitializeState(b);
        	delete[] b;
        	ms->Wrap(info.This());
        	info.GetReturnValue().Set(info.This());
        }
        else if(info.Length() == 2){
            char * b;
            v8StringToChar(info[0], b);
            bool c = info[1]->BooleanValue();
            machineState * ms = new machineState();
            ms->_ms = MachineState::InitializeState(b, c);
            delete[] b;
            ms->Wrap(info.This());
            info.GetReturnValue().Set(info.This());
        }
        else{
            return;
        }
    }
    else{
	   return;
    }
}

NAN_METHOD(machineState::AdvanceState)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int rtnval = ms->_ms->AdvanceState();
    v8::Local<v8::Int32> rtn = Nan::New(rtnval);
    v8::Local<v8::Promise::Resolver> pmise = v8::Promise::Resolver::New(info.GetIsolate());
    std::async(std::launch::async, [pmise, rtn, ms]() {ms->_ms->WaitForStateUpdate(); pmise->Resolve(rtn); });
    info.GetReturnValue().Set(pmise);
}

NAN_METHOD(machineState::GetGeometryJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    //This function has a 2 argument and a no argument version.
    if (info.Length() == 0)
    {
	RoseStringObject rtn;
	ms->_ms->GetGeometryJSON(rtn);
	info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
	return;
    }
    else
    {
	if (info.Length() != 2) return; // invalid number of arguments
	if (info[0]->IsUndefined()) return; //No Given ID
	if (info[1]->IsUndefined()) return; //No Given Typ
	if (!info[0]->IsString()) return; //ID is not valid
	if (!info[1]->IsString()) return; //Typ is not valid
	char * id;
	v8StringToChar(info[0], id);
	RoseObject * obj = ms->_ms->FindObjectByID(id);
	if (!obj)
	{
	    delete[] id;
	    return; //No Object Associated with given ID
	}
	char * typ;
	v8StringToChar(info[1], typ);
	RoseStringObject rtn;
	ms->_ms->GetGeometryJSON(rtn, id, obj, GeomTypeFromString(typ));
	info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
	delete[] id;
	delete[] typ;
	return;
    }
}

NAN_METHOD(machineState::GetDeltaGeometryJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
	return;
    if (!info[0]->IsNumber()) { // argument of wrong type
	return;
    }
    RoseStringObject rtn;
    ms->_ms->GetDynamicGeometryJSON(rtn,Nan::To<int32_t>(info[0]).FromJust());
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GetDeltaStateJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    RoseStringObject rtn;
    ms->_ms->GetStateJSON(rtn,false);
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GetKeyStateJSON)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    RoseStringObject rtn;
    ms->_ms->GetStateJSON(rtn,true);
    info.GetReturnValue().Set(CharTov8String(rtn.as_const()));
    return;
}

NAN_METHOD(machineState::GoToWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
    return;
    RoseObject * obj = ms->_ms->design()->findByEntityId(Nan::To<int32_t>(info[0]).FromJust());
    if (!obj)
    return;
    stp_machining_workingstep * ws = ROSE_CAST(stp_machining_workingstep, obj);
    if(!ws)
    return;
    int rtnval = ms->_ms->GoToWS(ws);
    info.GetReturnValue().Set(rtnval);
    return;
}

NAN_METHOD(machineState::GetEIDfromUUID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 1) //Function takes one argument
    return;
    if(!info[0]->IsString())
    return;
    char * uuid = 0;
    v8StringToChar(info[0], uuid);
    RoseObject * obj = ms->_ms->FindObjectByID(uuid);
    if (!obj)
    return;
    int rtnval = obj->entity_id();
    info.GetReturnValue().Set(Nan::New(rtnval));
    return;
}

NAN_METHOD(machineState::LoadMachine)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info[0]->IsUndefined()) return;
    char * b;
    v8StringToChar(info[0], b);
    bool success = ms->_ms->SetMachine(b);
    info.GetReturnValue().Set(success);
    delete[] b;

    return;
}

NAN_METHOD(machineState::NextWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    ms->_ms->GoToNextWS();
    return;
}

NAN_METHOD(machineState::PrevWS)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    ms->_ms->GoToPrevWS();
    return;
}

NAN_METHOD(machineState::GetPrevWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetPrevWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetNextWSID)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    int id = ms->_ms->GetNextWSID();
    info.GetReturnValue().Set(id);
    return;
}

NAN_METHOD(machineState::GetCurrentFeedrate)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    double feed = ms->_ms->GetCurrentFeedrate();
    info.GetReturnValue().Set(feed);
    return;
}

NAN_METHOD(machineState::GetCurrentSpindleSpeed)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (!info[0]->IsUndefined()) return; //This function takes no arguments.
    double speed = ms->_ms->GetCurrentSpindleSpeed();
    info.GetReturnValue().Set(speed);
    return;
}

NAN_METHOD(machineState::SetToolPosition)
{
    machineState * ms = Nan::ObjectWrap::Unwrap<machineState>(info.This());
    if (!ms || !(ms->_ms)) return;
    if (info.Length() != 6) return;    // bad input

    double xyz[3];
    double ijk[3];

    for (int i=0; i<3; i++) {
	if (info[i]->IsUndefined()) return; // throw ex
	if (!info[i]->IsNumber()) return; // needs to be a number

	Nan::Maybe<double> num = Nan::To<double>(info[i]);

	xyz[i] = num.FromJust();
    }

    for (int i=3; i<6; i++) {
	if (info[i]->IsUndefined()) return; // bad input
	if (!info[i]->IsNumber()) return; // check for number

	Nan::Maybe<double> num = Nan::To<double>(info[i]);

	ijk[i - 3] = num.FromJust();
    }

    ms->_ms->SetToolPosition(xyz, ijk);
    v8::Local<v8::Promise::Resolver> pmise = v8::Promise::Resolver::New(info.GetIsolate());
    std::async(std::launch::async, [pmise, ms]() {ms->_ms->WaitForStateUpdate(); pmise->Resolve(Nan::Null()); });
    info.GetReturnValue().Set(pmise);
    return;
}

NAN_MODULE_INIT(machineState::Init)
{
    v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
    tpl->SetClassName(Nan::New("machineState").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    Nan::SetPrototypeMethod(tpl, "AdvanceState", AdvanceState);
    Nan::SetPrototypeMethod(tpl, "GetGeometryJSON", GetGeometryJSON);
    Nan::SetPrototypeMethod(tpl, "GetDeltaGeometryJSON", GetDeltaGeometryJSON);
    Nan::SetPrototypeMethod(tpl, "GetDeltaStateJSON", GetDeltaStateJSON);
    Nan::SetPrototypeMethod(tpl, "GetKeyStateJSON", GetKeyStateJSON);
    Nan::SetPrototypeMethod(tpl, "GoToWS", GoToWS);
    Nan::SetPrototypeMethod(tpl, "GetEIDfromUUID", GetEIDfromUUID);
    Nan::SetPrototypeMethod(tpl, "LoadMachine", LoadMachine);
    Nan::SetPrototypeMethod(tpl, "NextWS", NextWS);
    Nan::SetPrototypeMethod(tpl, "PrevWS", PrevWS);
    Nan::SetPrototypeMethod(tpl, "GetPrevWSID", GetPrevWSID);
    Nan::SetPrototypeMethod(tpl, "GetWSID", GetWSID);
    Nan::SetPrototypeMethod(tpl, "GetNextWSID", GetNextWSID);
    Nan::SetPrototypeMethod(tpl, "GetCurrentFeedrate", GetCurrentFeedrate);
    Nan::SetPrototypeMethod(tpl, "GetCurrentSpindleSpeed", GetCurrentSpindleSpeed);
    Nan::SetPrototypeMethod(tpl, "SetToolPosition", SetToolPosition);
    constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
    Nan::Set(target, Nan::New("machineState").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());

    MachineState::init();
}
<|endoftext|>


<commit_before>#include "binding.hpp"
#include "wktreader.hpp"
#include "wktwriter.hpp"
#include "geometry.hpp"
#include "geometryfactory.hpp"
#include "precisionmodel.hpp"
#include "quadtree.hpp"
#include "geojsonwriter.hpp"

extern "C"
void init (Handle<Object> target) {
    HandleScope scope;

    target->Set(String::NewSymbol("geosversion"), String::NewSymbol(geos::geom::geosversion().data()));
    target->Set(String::NewSymbol("jtsport"), String::NewSymbol(geos::geom::jtsport().data()));

    Geometry::Initialize(target);
    WKTReader::Initialize(target);
    WKTWriter::Initialize(target);
    GeometryFactory::Initialize(target);
    PrecisionModel::Initialize(target);
    Quadtree::Initialize(target);
    GeoJSONWriter::Initialize(target);

}
<commit_msg>Add GeoJSONReader to the binding<commit_after>#include "binding.hpp"
#include "wktreader.hpp"
#include "wktwriter.hpp"
#include "geometry.hpp"
#include "geometryfactory.hpp"
#include "precisionmodel.hpp"
#include "quadtree.hpp"
#include "geojsonwriter.hpp"
#include "geojsonreader.hpp"

extern "C"
void init (Handle<Object> target) {
    HandleScope scope;

    target->Set(String::NewSymbol("geosversion"), String::NewSymbol(geos::geom::geosversion().data()));
    target->Set(String::NewSymbol("jtsport"), String::NewSymbol(geos::geom::jtsport().data()));

    Geometry::Initialize(target);
    WKTReader::Initialize(target);
    WKTWriter::Initialize(target);
    GeometryFactory::Initialize(target);
    PrecisionModel::Initialize(target);
    Quadtree::Initialize(target);
    GeoJSONWriter::Initialize(target);
    GeoJSONReader::Initialize(target);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2001-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

#ifndef __EXETRACE_HH__
#define __EXETRACE_HH__

#include <cstring>
#include <fstream>
#include <vector>

#include "base/trace.hh"
#include "cpu/inst_seq.hh"	// for InstSeqNum
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "sim/host.hh"

class ThreadContext;

namespace Trace {

class InstRecord
{
  protected:
    typedef TheISA::IntRegFile IntRegFile;

    Tick when;

    // The following fields are initialized by the constructor and
    // thus guaranteed to be valid.
    ThreadContext *thread;
    // need to make this ref-counted so it doesn't go away before we
    // dump the record
    StaticInstPtr staticInst;
    Addr PC;
    bool misspeculating;

    // The remaining fields are only valid for particular instruction
    // types (e.g, addresses for memory ops) or when particular
    // options are enabled (e.g., tracing full register contents).
    // Each data field has an associated valid flag to indicate
    // whether the data field is valid.
    Addr addr;
    bool addr_valid;

    union {
        uint64_t as_int;
        double as_double;
    } data;
    enum {
        DataInvalid = 0,
        DataInt8 = 1,	// set to equal number of bytes
        DataInt16 = 2,
        DataInt32 = 4,
        DataInt64 = 8,
        DataDouble = 3
    } data_status;

    InstSeqNum fetch_seq;
    bool fetch_seq_valid;

    InstSeqNum cp_seq;
    bool cp_seq_valid;

    struct iRegFile {
        IntRegFile regs;
    };
    iRegFile *iregs;
    bool regs_valid;

  public:
    InstRecord(Tick _when, ThreadContext *_thread,
               const StaticInstPtr &_staticInst,
               Addr _pc, bool spec)
        : when(_when), thread(_thread),
          staticInst(_staticInst), PC(_pc),
          misspeculating(spec)
    {
        data_status = DataInvalid;
        addr_valid = false;
        regs_valid = false;

        fetch_seq_valid = false;
        cp_seq_valid = false;
    }

    ~InstRecord() { }

    void setAddr(Addr a) { addr = a; addr_valid = true; }

    void setData(uint64_t d) { data.as_int = d; data_status = DataInt64; }
    void setData(uint32_t d) { data.as_int = d; data_status = DataInt32; }
    void setData(uint16_t d) { data.as_int = d; data_status = DataInt16; }
    void setData(uint8_t d) { data.as_int = d; data_status = DataInt8; }

    void setData(int64_t d) { setData((uint64_t)d); }
    void setData(int32_t d) { setData((uint32_t)d); }
    void setData(int16_t d) { setData((uint16_t)d); }
    void setData(int8_t d)  { setData((uint8_t)d); }

    void setData(double d) { data.as_double = d; data_status = DataDouble; }

    void setFetchSeq(InstSeqNum seq)
    { fetch_seq = seq; fetch_seq_valid = true; }

    void setCPSeq(InstSeqNum seq)
    { cp_seq = seq; cp_seq_valid = true; }

    void setRegs(const IntRegFile &regs);

    void dump();
};

inline void
InstRecord::setRegs(const IntRegFile &regs)
{
    if (!iregs)
      iregs = new iRegFile;

    std::memcpy(&iregs->regs, &regs, sizeof(IntRegFile));
    regs_valid = true;
}

inline InstRecord *
getInstRecord(Tick when, ThreadContext *tc, const StaticInstPtr staticInst,
              Addr pc)
{
    if (!IsOn(ExecEnable))
        return NULL;

    if (!Trace::enabled)
        return NULL;

    if (!IsOn(ExecSpeculative) && tc->misspeculating())
        return NULL;

    return new InstRecord(when, tc, staticInst, pc, tc->misspeculating());
}

/* namespace Trace */ }

#endif // __EXETRACE_HH__
<commit_msg>Add setData functions for the new Twin??_t types.<commit_after>/*
 * Copyright (c) 2001-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

#ifndef __EXETRACE_HH__
#define __EXETRACE_HH__

#include <cstring>
#include <fstream>
#include <vector>

#include "base/trace.hh"
#include "cpu/inst_seq.hh"	// for InstSeqNum
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "sim/host.hh"

class ThreadContext;

namespace Trace {

class InstRecord
{
  protected:
    typedef TheISA::IntRegFile IntRegFile;

    Tick when;

    // The following fields are initialized by the constructor and
    // thus guaranteed to be valid.
    ThreadContext *thread;
    // need to make this ref-counted so it doesn't go away before we
    // dump the record
    StaticInstPtr staticInst;
    Addr PC;
    bool misspeculating;

    // The remaining fields are only valid for particular instruction
    // types (e.g, addresses for memory ops) or when particular
    // options are enabled (e.g., tracing full register contents).
    // Each data field has an associated valid flag to indicate
    // whether the data field is valid.
    Addr addr;
    bool addr_valid;

    union {
        uint64_t as_int;
        double as_double;
    } data;
    enum {
        DataInvalid = 0,
        DataInt8 = 1,	// set to equal number of bytes
        DataInt16 = 2,
        DataInt32 = 4,
        DataInt64 = 8,
        DataDouble = 3
    } data_status;

    InstSeqNum fetch_seq;
    bool fetch_seq_valid;

    InstSeqNum cp_seq;
    bool cp_seq_valid;

    struct iRegFile {
        IntRegFile regs;
    };
    iRegFile *iregs;
    bool regs_valid;

  public:
    InstRecord(Tick _when, ThreadContext *_thread,
               const StaticInstPtr &_staticInst,
               Addr _pc, bool spec)
        : when(_when), thread(_thread),
          staticInst(_staticInst), PC(_pc),
          misspeculating(spec)
    {
        data_status = DataInvalid;
        addr_valid = false;
        regs_valid = false;

        fetch_seq_valid = false;
        cp_seq_valid = false;
    }

    ~InstRecord() { }

    void setAddr(Addr a) { addr = a; addr_valid = true; }

    void setData(Twin64_t d) { data.as_int = d.a; data_status = DataInt64; }
    void setData(Twin32_t d) { data.as_int = d.a; data_status = DataInt32; }
    void setData(uint64_t d) { data.as_int = d; data_status = DataInt64; }
    void setData(uint32_t d) { data.as_int = d; data_status = DataInt32; }
    void setData(uint16_t d) { data.as_int = d; data_status = DataInt16; }
    void setData(uint8_t d) { data.as_int = d; data_status = DataInt8; }

    void setData(int64_t d) { setData((uint64_t)d); }
    void setData(int32_t d) { setData((uint32_t)d); }
    void setData(int16_t d) { setData((uint16_t)d); }
    void setData(int8_t d)  { setData((uint8_t)d); }

    void setData(double d) { data.as_double = d; data_status = DataDouble; }

    void setFetchSeq(InstSeqNum seq)
    { fetch_seq = seq; fetch_seq_valid = true; }

    void setCPSeq(InstSeqNum seq)
    { cp_seq = seq; cp_seq_valid = true; }

    void setRegs(const IntRegFile &regs);

    void dump();
};

inline void
InstRecord::setRegs(const IntRegFile &regs)
{
    if (!iregs)
      iregs = new iRegFile;

    std::memcpy(&iregs->regs, &regs, sizeof(IntRegFile));
    regs_valid = true;
}

inline InstRecord *
getInstRecord(Tick when, ThreadContext *tc, const StaticInstPtr staticInst,
              Addr pc)
{
    if (!IsOn(ExecEnable))
        return NULL;

    if (!Trace::enabled)
        return NULL;

    if (!IsOn(ExecSpeculative) && tc->misspeculating())
        return NULL;

    return new InstRecord(when, tc, staticInst, pc, tc->misspeculating());
}

/* namespace Trace */ }

#endif // __EXETRACE_HH__
<|endoftext|>


<commit_before>// Copyright (c) 2011-2012, Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <map>
#include <stack>
#include <string>

#include "amxdebuginfo.h"
#include "amxpathfinder.h"
#include "amxstacktrace.h"
#include "amxutils.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "os.h"
#include "stacktrace.h"

#include "amx/amx.h"
#include "amx/amxaux.h" // for amx_StrError()

#define AMX_EXEC_GDK (-10)

static const char *kLogMessagePrefix = "[debug] ";
static const char *kServerConfig = "server.cfg";

bool crashdetect::errorCaught_ = false;
std::stack<crashdetect::NPCall> crashdetect::npCalls_;
ConfigReader crashdetect::serverCfg(kServerConfig);
crashdetect::InstanceMap crashdetect::instances_;

// static
crashdetect *crashdetect::GetInstance(AMX *amx) {
	InstanceMap::const_iterator iterator = instances_.find(amx);
	if (iterator == instances_.end()) {
		crashdetect *c = new crashdetect(amx);
		instances_.insert(std::make_pair(amx, c));
		return c;
	}
	return iterator->second;
}

// static
void crashdetect::DestroyInstance(AMX *amx) {
	instances_.erase(amx);
}

// static
void crashdetect::SystemException(void *context) {
	if (!npCalls_.empty()) {
		AMX *amx = npCalls_.top().amx();
		GetInstance(amx)->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void crashdetect::SystemInterrupt() {
	if (!npCalls_.empty()) {
		AMX *amx = npCalls_.top().amx();
		GetInstance(amx)->HandleInterrupt();
	} else {
		logprintf("Server recieved an interrupt signal");
	}
	PrintSystemBacktrace();
}

// static
void crashdetect::DieOrContinue() {
	if (serverCfg.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

crashdetect::crashdetect(AMX *amx)
	: amx_(amx)
	, amxhdr_(reinterpret_cast<AMX_HEADER*>(amx->base))
{
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amxPath_ = pathFinder.FindAMX(amx);
	amxName_ = fileutils::GetFileName(amxPath_);

	if (!amxPath_.empty()) {
		uint16_t flags;
		amx_Flags(amx_, &flags);
		if ((flags & AMX_FLAG_DEBUG) != 0) {
			debugInfo_.Load(amxPath_);
		}
	}

	amx_->sysreq_d = 0;
	prevCallback_ = amx_->callback;
}

int crashdetect::DoAmxCallback(cell index, cell *result, cell *params) {
	npCalls_.push(NPCall(NPCall::NATIVE, amx_, index, amx_->frm, amx_->cip));
	int retcode = prevCallback_(amx_, index, result, params);
	npCalls_.pop();
	return retcode;
}

int crashdetect::DoAmxExec(cell *retval, int index) {
	npCalls_.push(NPCall(NPCall::PUBLIC, amx_, index, amx_->frm, amx_->cip));

	int retcode = ::amx_Exec(amx_, retval, index);
	if (retcode != AMX_ERR_NONE && !errorCaught_) {
		amx_Error(amx_, index, retcode);
	} else {
		errorCaught_ = false;
	}

	npCalls_.pop();
	return retcode;
}

int crashdetect::DoAmxRelease(cell amx_addr, void *releaser) {
	if (amx_addr < amx_->hlw || amx_addr >= amx_->stk) {
		HandleReleaseError(amx_addr, releaser);
	}
	return amx_Release(amx_, amx_addr);
}

void crashdetect::HandleExecError(int index, int error) {
	crashdetect::errorCaught_ = true;

	if (error == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		error = AMX_ERR_NONE;
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, aux_StrError(error));

	switch (error) {
		case AMX_ERR_BOUNDS: {
			cell bound = *(reinterpret_cast<cell*>(amx_->cip + amx_->base + amxhdr_->cod + sizeof(cell)));
			cell index = amx_->pri;
			if (index < 0) {
				logprintf(" Accessing element at negative index %d", index);
			} else {
				logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = reinterpret_cast<AMX_FUNCSTUBNT*>(amx_->base + amxhdr_->natives);
			int numNatives = 0;
			amx_NumNatives(amx_, &numNatives);
			for (int i = 0; i < numNatives; ++i) {
				if (natives[i].address == 0) {
					char *name = reinterpret_cast<char*>(natives[i].nameofs + amx_->base);
					logprintf(" %s", name);
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_->stk, amx_->hea);
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_->stk, amx_->stp);
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_->hea, amx_->hlw);
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_->cip + amx_->base + amxhdr_->cod));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_->cip);
			break;
		}
	}

	if (error != AMX_ERR_NOTFOUND &&
		error != AMX_ERR_INDEX &&
		error != AMX_ERR_CALLBACK &&
		error != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = serverCfg.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void crashdetect::HandleException() {
	logprintf("Server crashed while executing %s", amxName_.c_str());
	PrintAmxBacktrace();
}

void crashdetect::HandleInterrupt() {
	logprintf("Server recieved interrupt signal while executing %s", amxName_.c_str());
	PrintAmxBacktrace();
}

void crashdetect::HandleReleaseError(cell address, void *releaser) {
	std::string plugin = fileutils::GetFileName(os::GetModulePath(releaser));
	if (plugin.empty()) {
		plugin.assign("<unknown>");
	}
	logprintf("Bad heap release detected:");
	logprintf(" %s [%08x] releases memory at %08x which is out of heap", plugin.c_str(), releaser, address);
	PrintSystemBacktrace();
}

// static
void crashdetect::PrintAmxBacktrace() {
	if (npCalls_.empty()) {
		return;
	}

	std::stack<NPCall> npCallStack = npCalls_;
	cell frm = npCallStack.top().amx()->frm;
	cell cip = npCallStack.top().amx()->cip;
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	while (!npCallStack.empty() && cip != 0) {
		NPCall call = npCallStack.top();

		if (call.amx() != npCallStack.top().amx()) {
			assert(level != 0);
			break;
		}

		if (call.type() == NPCall::NATIVE) {
			AMX_NATIVE address = amxutils::GetNativeAddress(call.amx(), call.index());
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePath((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amxutils::GetNativeName(call.amx(), call.index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call.type() == NPCall::PUBLIC) {
			AMXDebugInfo &debugInfo = instances_[call.amx()]->debugInfo_;

			amxutils::PushStack(call.amx(), cip); // push return address
			amxutils::PushStack(call.amx(), frm); // push frame pointer
			frm = call.amx()->stk;

			AMXStackTrace trace(call.amx(), debugInfo, frm);
			std::deque<AMXStackFrame> frames = trace.GetFrames();

			frm = amxutils::PopStack(call.amx()); // pop frame pointer
			cip = amxutils::PopStack(call.amx()); // pop return address

			if (frames.empty()) {
				ucell epAddr = amxutils::GetPublicAddress(call.amx(), call.index());
				frames.push_front(AMXStackFrame(call.amx(), frm, 0, epAddr, debugInfo));
			} else {
				if (!debugInfo.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(call.amx(),
						bottom.GetFrameAddress(),
						bottom.GetReturnAddress(),
						amxutils::GetPublicAddress(call.amx(), call.index()),
						debugInfo);
				}
			}

			std::string &amxName = instances_[call.amx()]->amxName_;
			for (size_t i = 0; i < frames.size(); i++) {
				std::string from = " from " + amxName;
				if (amxName.empty() || debugInfo.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, frames[i].GetString().c_str(), from.c_str());
			}

			frm = call.frm();
			cip = call.cip();
		}
		npCallStack.pop();
	}
}

// static
void crashdetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(0, 0, context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePath(frame.GetReturnAddress());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.GetString().c_str(), from.c_str());
	}
}

// static
void crashdetect::logprintf(const char *format, ...) {
	std::va_list args;
	va_start(args, format);

	std::string prefixed_format;
	prefixed_format.append(kLogMessagePrefix);
	prefixed_format.append(format);

	::vlogprintf(prefixed_format.c_str(), args);

	va_end(args);
}
<commit_msg>Minor fix<commit_after>// Copyright (c) 2011-2012, Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <map>
#include <stack>
#include <string>

#include "amxdebuginfo.h"
#include "amxpathfinder.h"
#include "amxstacktrace.h"
#include "amxutils.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "os.h"
#include "stacktrace.h"

#include "amx/amx.h"
#include "amx/amxaux.h" // for amx_StrError()

#define AMX_EXEC_GDK (-10)

static const char *kLogMessagePrefix = "[debug] ";
static const char *kServerConfig = "server.cfg";

bool crashdetect::errorCaught_ = false;
std::stack<crashdetect::NPCall> crashdetect::npCalls_;
ConfigReader crashdetect::serverCfg(kServerConfig);
crashdetect::InstanceMap crashdetect::instances_;

// static
crashdetect *crashdetect::GetInstance(AMX *amx) {
	InstanceMap::const_iterator iterator = instances_.find(amx);
	if (iterator == instances_.end()) {
		crashdetect *c = new crashdetect(amx);
		instances_.insert(std::make_pair(amx, c));
		return c;
	}
	return iterator->second;
}

// static
void crashdetect::DestroyInstance(AMX *amx) {
	instances_.erase(amx);
}

// static
void crashdetect::SystemException(void *context) {
	if (!npCalls_.empty()) {
		AMX *amx = npCalls_.top().amx();
		GetInstance(amx)->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void crashdetect::SystemInterrupt() {
	if (!npCalls_.empty()) {
		AMX *amx = npCalls_.top().amx();
		GetInstance(amx)->HandleInterrupt();
	} else {
		logprintf("Server recieved interrupt signal");
	}
	PrintSystemBacktrace();
}

// static
void crashdetect::DieOrContinue() {
	if (serverCfg.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

crashdetect::crashdetect(AMX *amx)
	: amx_(amx)
	, amxhdr_(reinterpret_cast<AMX_HEADER*>(amx->base))
{
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amxPath_ = pathFinder.FindAMX(amx);
	amxName_ = fileutils::GetFileName(amxPath_);

	if (!amxPath_.empty()) {
		uint16_t flags;
		amx_Flags(amx_, &flags);
		if ((flags & AMX_FLAG_DEBUG) != 0) {
			debugInfo_.Load(amxPath_);
		}
	}

	amx_->sysreq_d = 0;
	prevCallback_ = amx_->callback;
}

int crashdetect::DoAmxCallback(cell index, cell *result, cell *params) {
	npCalls_.push(NPCall(NPCall::NATIVE, amx_, index, amx_->frm, amx_->cip));
	int retcode = prevCallback_(amx_, index, result, params);
	npCalls_.pop();
	return retcode;
}

int crashdetect::DoAmxExec(cell *retval, int index) {
	npCalls_.push(NPCall(NPCall::PUBLIC, amx_, index, amx_->frm, amx_->cip));

	int retcode = ::amx_Exec(amx_, retval, index);
	if (retcode != AMX_ERR_NONE && !errorCaught_) {
		amx_Error(amx_, index, retcode);
	} else {
		errorCaught_ = false;
	}

	npCalls_.pop();
	return retcode;
}

int crashdetect::DoAmxRelease(cell amx_addr, void *releaser) {
	if (amx_addr < amx_->hlw || amx_addr >= amx_->stk) {
		HandleReleaseError(amx_addr, releaser);
	}
	return amx_Release(amx_, amx_addr);
}

void crashdetect::HandleExecError(int index, int error) {
	crashdetect::errorCaught_ = true;

	if (error == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		error = AMX_ERR_NONE;
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, aux_StrError(error));

	switch (error) {
		case AMX_ERR_BOUNDS: {
			cell bound = *(reinterpret_cast<cell*>(amx_->cip + amx_->base + amxhdr_->cod + sizeof(cell)));
			cell index = amx_->pri;
			if (index < 0) {
				logprintf(" Accessing element at negative index %d", index);
			} else {
				logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = reinterpret_cast<AMX_FUNCSTUBNT*>(amx_->base + amxhdr_->natives);
			int numNatives = 0;
			amx_NumNatives(amx_, &numNatives);
			for (int i = 0; i < numNatives; ++i) {
				if (natives[i].address == 0) {
					char *name = reinterpret_cast<char*>(natives[i].nameofs + amx_->base);
					logprintf(" %s", name);
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_->stk, amx_->hea);
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_->stk, amx_->stp);
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_->hea, amx_->hlw);
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_->cip + amx_->base + amxhdr_->cod));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_->cip);
			break;
		}
	}

	if (error != AMX_ERR_NOTFOUND &&
		error != AMX_ERR_INDEX &&
		error != AMX_ERR_CALLBACK &&
		error != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = serverCfg.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void crashdetect::HandleException() {
	logprintf("Server crashed while executing %s", amxName_.c_str());
	PrintAmxBacktrace();
}

void crashdetect::HandleInterrupt() {
	logprintf("Server recieved interrupt signal while executing %s", amxName_.c_str());
	PrintAmxBacktrace();
}

void crashdetect::HandleReleaseError(cell address, void *releaser) {
	std::string plugin = fileutils::GetFileName(os::GetModulePath(releaser));
	if (plugin.empty()) {
		plugin.assign("<unknown>");
	}
	logprintf("Bad heap release detected:");
	logprintf(" %s [%08x] releases memory at %08x which is out of heap", plugin.c_str(), releaser, address);
	PrintSystemBacktrace();
}

// static
void crashdetect::PrintAmxBacktrace() {
	if (npCalls_.empty()) {
		return;
	}

	std::stack<NPCall> npCallStack = npCalls_;
	cell frm = npCallStack.top().amx()->frm;
	cell cip = npCallStack.top().amx()->cip;
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	while (!npCallStack.empty() && cip != 0) {
		NPCall call = npCallStack.top();

		if (call.amx() != npCallStack.top().amx()) {
			assert(level != 0);
			break;
		}

		if (call.type() == NPCall::NATIVE) {
			AMX_NATIVE address = amxutils::GetNativeAddress(call.amx(), call.index());
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePath((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amxutils::GetNativeName(call.amx(), call.index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call.type() == NPCall::PUBLIC) {
			AMXDebugInfo &debugInfo = instances_[call.amx()]->debugInfo_;

			amxutils::PushStack(call.amx(), cip); // push return address
			amxutils::PushStack(call.amx(), frm); // push frame pointer
			frm = call.amx()->stk;

			AMXStackTrace trace(call.amx(), debugInfo, frm);
			std::deque<AMXStackFrame> frames = trace.GetFrames();

			frm = amxutils::PopStack(call.amx()); // pop frame pointer
			cip = amxutils::PopStack(call.amx()); // pop return address

			if (frames.empty()) {
				ucell epAddr = amxutils::GetPublicAddress(call.amx(), call.index());
				frames.push_front(AMXStackFrame(call.amx(), frm, 0, epAddr, debugInfo));
			} else {
				if (!debugInfo.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(call.amx(),
						bottom.GetFrameAddress(),
						bottom.GetReturnAddress(),
						amxutils::GetPublicAddress(call.amx(), call.index()),
						debugInfo);
				}
			}

			std::string &amxName = instances_[call.amx()]->amxName_;
			for (size_t i = 0; i < frames.size(); i++) {
				std::string from = " from " + amxName;
				if (amxName.empty() || debugInfo.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, frames[i].GetString().c_str(), from.c_str());
			}

			frm = call.frm();
			cip = call.cip();
		}
		npCallStack.pop();
	}
}

// static
void crashdetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(0, 0, context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePath(frame.GetReturnAddress());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.GetString().c_str(), from.c_str());
	}
}

// static
void crashdetect::logprintf(const char *format, ...) {
	std::va_list args;
	va_start(args, format);

	std::string prefixed_format;
	prefixed_format.append(kLogMessagePrefix);
	prefixed_format.append(format);

	::vlogprintf(prefixed_format.c_str(), args);

	va_end(args);
}
<|endoftext|>


<commit_before>#include "common.h"

#include "hexutil/basics/error.h"
#include "hexutil/basics/hexgrid.h"

#include "hexgame/game/game.h"
#include "hexgame/game/movement/movement.h"

namespace hex {

MovementModel::MovementModel(Game *game): game(game), target_pos(-1, -1) { }

MovementModel::MovementModel(Game *game, const Point& target_pos): game(game), target_pos(target_pos) { }

bool MovementModel::can_enter(const UnitStack& party, const Point& tile_pos) const {
    if (party.units.size() == 0)
        return false;

    Tile &tile = game->level.tiles[tile_pos];

    UnitStack::pointer target_stack = tile.stack;
    if (target_stack) {
        // Can't move through enemy stacks
        if (tile_pos != target_pos && target_stack->owner != party.owner)
            return false;

        //TODO can't move through a structure defended by a neighbouring enemy, either

        if (target_stack->units.size() + party.units.size() > MAX_UNITS)
            return false;

        //TODO make sure there will not be two transports in the new stack
    }

    return true;
}

int MovementModel::cost_to(const UnitStack& party, const Point& tile_pos) const {
    if (!can_enter(party, tile_pos))
        return INT_MAX;

    //TODO if one unit is a transport, then the others move for free

    int total_cost = 0;
    for (auto iter = party.units.begin(); iter != party.units.end(); iter++) {
        Unit& unit = **iter;
        int cost = cost_to(unit, tile_pos);
        if (cost == INT_MAX) {
            total_cost = INT_MAX;
            break;
        }
        total_cost += cost;
    }

    return total_cost;
}

int MovementModel::cost_to(const Unit& unit, const Point& tile_pos) const {
    Tile &tile = game->level.tiles[tile_pos];

    if (tile.has_property(Walkable) && unit.has_property(Walking)) {
        if (tile.has_property(Road))
            return 3;
        else if (tile.has_property(SlowWalking))
            return 5;
        else
            return 4;
    }

    if (tile.has_property(Swimmable) && unit.has_property(Swimming)) {
        return 4;
    }

    if (tile.has_property(Sailable) && unit.has_property(Sailing)) {
        return 4;
    }

    if (tile.has_property(Forest) && unit.has_property(Forestry)) {
        return 3;
    }

    if (tile.has_property(Flyable) && unit.has_property(Flying)) {
        if (tile.has_property(SlowFlying))
            return 5;
        else
            return 4;
    }

    return INT_MAX;
}

int MovementModel::admits(const UnitType& unit_type, const Tile& tile) const {
    if (unit_type.has_property(Walking) && tile.has_property(Walkable))
        return true;
    else if (unit_type.has_property(Swimming) && tile.has_property(Swimmable))
        return true;
    else if (unit_type.has_property(Sailing) && tile.has_property(Sailable))
        return true;
    if (unit_type.has_property(Flying) && tile.has_property(Flyable))
        return true;
    return false;
}

void MovementModel::move(UnitStack& party, const IntSet& selected_units, const Point& tile_pos) const {
    for (unsigned int i = 0; i < party.units.size(); i++) {
        if (selected_units.contains(i)) {
            Unit& unit = *party.units[i];
            int cost = cost_to(unit, tile_pos);
            unit.properties.increment<int>(Moves, -cost);

            //TODO if unit is on transport, does it affect the terrain?
            if (unit.has_property(PathOfLife)) {
                alter_terrain(tile_pos, PathOfLife);
            } else if (unit.has_property(PathOfDeath)) {
                alter_terrain(tile_pos, PathOfDeath);
            } else if (unit.has_property(PathOfIce)) {
                alter_terrain(tile_pos, PathOfIce);
            }

            Tile& tile = game->level.tiles[tile_pos];
            if (tile.structure) {
                Structure& structure = *tile.structure;
                if (structure.has_property(Capturable) && structure.owner != party.owner) {
                    structure.owner = party.owner;
                }
            }
        }
    }
}

void MovementModel::alter_terrain(Point pos, Atom type) const {
    std::vector<Point> points = get_circle_points(pos, 1, game->level.width, game->level.height);
    TileType::pointer new_tile_type;
    if (type == PathOfLife)
        new_tile_type = game->tile_types.get("grass");
    else if (type == PathOfDeath)
        new_tile_type = game->tile_types.get("wasteland");
    else
        new_tile_type = game->tile_types.get("snow");
    for (auto iter = points.begin(); iter != points.end(); iter++) {
        if (!game->level.contains(*iter))
            continue;
        Tile& tile = game->level.tiles[*iter];
        if (tile.type->name == "grass" || tile.type->name == "desert" || tile.type->name == "snow" || tile.type->name == "steppe")
            tile.type = new_tile_type;
    }
}

int MovementModel::check_path(const UnitStack& stack, const Path& path) const {
    StackMovePoints points(stack);
    unsigned int step_num = 0;
    Point current_position = stack.position;
    while (step_num < path.size()) {
        Point next_position = path[step_num];
        if (distance_between(current_position, next_position) != 1)
            break;
        if (!check_step(stack, &points, path, step_num))
            break;
        current_position = next_position;
        step_num++;
    }
    return step_num;
}

bool MovementModel::check_step(const UnitStack& stack, StackMovePoints *points, const Path& path, int step_num) const {
    Point next_step = path[step_num];

    if (!can_enter(stack, next_step))
        return false;

    for (unsigned int i = 0; i < stack.units.size(); i++) {
        Unit& unit = *stack.units[i];
        int cost = cost_to(unit, next_step);
        points->points[i] -= cost;
    }

    return !points->exhausted();
}

};
<commit_msg>PathOfIce turns water into ice.<commit_after>#include "common.h"

#include "hexutil/basics/error.h"
#include "hexutil/basics/hexgrid.h"

#include "hexgame/game/game.h"
#include "hexgame/game/movement/movement.h"

namespace hex {

MovementModel::MovementModel(Game *game): game(game), target_pos(-1, -1) { }

MovementModel::MovementModel(Game *game, const Point& target_pos): game(game), target_pos(target_pos) { }

bool MovementModel::can_enter(const UnitStack& party, const Point& tile_pos) const {
    if (party.units.size() == 0)
        return false;

    Tile &tile = game->level.tiles[tile_pos];

    UnitStack::pointer target_stack = tile.stack;
    if (target_stack) {
        // Can't move through enemy stacks
        if (tile_pos != target_pos && target_stack->owner != party.owner)
            return false;

        //TODO can't move through a structure defended by a neighbouring enemy, either

        if (target_stack->units.size() + party.units.size() > MAX_UNITS)
            return false;

        //TODO make sure there will not be two transports in the new stack
    }

    return true;
}

int MovementModel::cost_to(const UnitStack& party, const Point& tile_pos) const {
    if (!can_enter(party, tile_pos))
        return INT_MAX;

    //TODO if one unit is a transport, then the others move for free

    int total_cost = 0;
    for (auto iter = party.units.begin(); iter != party.units.end(); iter++) {
        Unit& unit = **iter;
        int cost = cost_to(unit, tile_pos);
        if (cost == INT_MAX) {
            total_cost = INT_MAX;
            break;
        }
        total_cost += cost;
    }

    return total_cost;
}

int MovementModel::cost_to(const Unit& unit, const Point& tile_pos) const {
    Tile &tile = game->level.tiles[tile_pos];

    if (tile.has_property(Walkable) && unit.has_property(Walking)) {
        if (tile.has_property(Road))
            return 3;
        else if (tile.has_property(SlowWalking))
            return 5;
        else
            return 4;
    }

    if (tile.has_property(Swimmable) && unit.has_property(Swimming)) {
        return 4;
    }

    if (tile.has_property(Sailable) && unit.has_property(Sailing)) {
        return 4;
    }

    if (tile.has_property(Forest) && unit.has_property(Forestry)) {
        return 3;
    }

    if (tile.has_property(Flyable) && unit.has_property(Flying)) {
        if (tile.has_property(SlowFlying))
            return 5;
        else
            return 4;
    }

    return INT_MAX;
}

int MovementModel::admits(const UnitType& unit_type, const Tile& tile) const {
    if (unit_type.has_property(Walking) && tile.has_property(Walkable))
        return true;
    else if (unit_type.has_property(Swimming) && tile.has_property(Swimmable))
        return true;
    else if (unit_type.has_property(Sailing) && tile.has_property(Sailable))
        return true;
    if (unit_type.has_property(Flying) && tile.has_property(Flyable))
        return true;
    return false;
}

void MovementModel::move(UnitStack& party, const IntSet& selected_units, const Point& tile_pos) const {
    for (unsigned int i = 0; i < party.units.size(); i++) {
        if (selected_units.contains(i)) {
            Unit& unit = *party.units[i];
            int cost = cost_to(unit, tile_pos);
            unit.properties.increment<int>(Moves, -cost);

            //TODO if unit is on transport, does it affect the terrain?
            if (unit.has_property(PathOfLife)) {
                alter_terrain(tile_pos, PathOfLife);
            } else if (unit.has_property(PathOfDeath)) {
                alter_terrain(tile_pos, PathOfDeath);
            } else if (unit.has_property(PathOfIce)) {
                alter_terrain(tile_pos, PathOfIce);
            }

            Tile& tile = game->level.tiles[tile_pos];
            if (tile.structure) {
                Structure& structure = *tile.structure;
                if (structure.has_property(Capturable) && structure.owner != party.owner) {
                    structure.owner = party.owner;
                }
            }
        }
    }
}

void MovementModel::alter_terrain(Point pos, Atom type) const {
    std::vector<Point> points = get_circle_points(pos, 1, game->level.width, game->level.height);
    TileType::pointer new_tile_type;
    TileType::pointer new_water_tile_type;
    if (type == PathOfLife)
        new_tile_type = game->tile_types.get("grass");
    else if (type == PathOfDeath)
        new_tile_type = game->tile_types.get("wasteland");
    else {
        new_tile_type = game->tile_types.get("snow");
        new_water_tile_type = game->tile_types.get("ice");
        //TODO ice is only temporary!  Maybe we need a terrain type for "temporary ice"
    }
    for (auto iter = points.begin(); iter != points.end(); iter++) {
        if (!game->level.contains(*iter))
            continue;
        Tile& tile = game->level.tiles[*iter];
        if (tile.type->name == "grass" || tile.type->name == "desert" || tile.type->name == "snow" || tile.type->name == "steppe")
            tile.type = new_tile_type;
        else if (tile.type->name == "water" && new_water_tile_type)
            tile.type = new_water_tile_type;
    }
}

int MovementModel::check_path(const UnitStack& stack, const Path& path) const {
    StackMovePoints points(stack);
    unsigned int step_num = 0;
    Point current_position = stack.position;
    while (step_num < path.size()) {
        Point next_position = path[step_num];
        if (distance_between(current_position, next_position) != 1)
            break;
        if (!check_step(stack, &points, path, step_num))
            break;
        current_position = next_position;
        step_num++;
    }
    return step_num;
}

bool MovementModel::check_step(const UnitStack& stack, StackMovePoints *points, const Path& path, int step_num) const {
    Point next_step = path[step_num];

    if (!can_enter(stack, next_step))
        return false;

    for (unsigned int i = 0; i < stack.units.size(); i++) {
        Unit& unit = *stack.units[i];
        int cost = cost_to(unit, next_step);
        points->points[i] -= cost;
    }

    return !points->exhausted();
}

};
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2022 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "Request.hxx"
#include "translation/Protocol.hxx"
#include "util/StaticVector.hxx"

#include <cstddef>
#include
#include <string>

#include <stddef.h>

struct pool;

struct TranslationInvalidateRequest : TranslateRequest {
	const char *site = nullptr;

	StaticVector<TranslationCommand, 32> commands;

	std::string ToString() const noexcept;
};

/**
 * Throws on error.
 */
TranslationInvalidateRequest
ParseTranslationInvalidateRequest(struct pool &pool,
				  std::span<const std::byte> p);
<commit_msg>translation/InvalidateParser: include cleanup<commit_after>/*
 * Copyright 2007-2022 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "Request.hxx"
#include "translation/Protocol.hxx"
#include "util/StaticVector.hxx"

#include <cstddef>
#include
#include <string>

struct pool;

struct TranslationInvalidateRequest : TranslateRequest {
	const char *site = nullptr;

	StaticVector<TranslationCommand, 32> commands;

	std::string ToString() const noexcept;
};

/**
 * Throws on error.
 */
TranslationInvalidateRequest
ParseTranslationInvalidateRequest(struct pool &pool,
				  std::span<const std::byte> p);
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libvisio project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "VSDMetaData.h"
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <unicode/ucnv.h>
#include <ctime>

libvisio::VSDMetaData::VSDMetaData()
  : m_idsAndOffsets(), m_typedPropertyValues(), m_metaData()
{
}

libvisio::VSDMetaData::~VSDMetaData()
{
}

enum PIDDSI
{
  PIDDSI_CODEPAGE          = 0x00000001,
  PIDDSI_CATEGORY          = 0x00000002,
  PIDDSI_PRESFORMAT        = 0x00000003,
  PIDDSI_BYTECOUNT         = 0x00000004,
  PIDDSI_LINECOUNT         = 0x00000005,
  PIDDSI_PARACOUNT         = 0x00000006,
  PIDDSI_SLIDECOUNT        = 0x00000007,
  PIDDSI_NOTECOUNT         = 0x00000008,
  PIDDSI_HIDDENCOUNT       = 0x00000009,
  PIDDSI_MMCLIPCOUNT       = 0x0000000A,
  PIDDSI_SCALE             = 0x0000000B,
  PIDDSI_HEADINGPAIR       = 0x0000000C,
  PIDDSI_DOCPARTS          = 0x0000000D,
  PIDDSI_MANAGER           = 0x0000000E,
  PIDDSI_COMPANY           = 0x0000000F,
  PIDDSI_LINKSDIRTY        = 0x00000010,
  PIDDSI_CCHWITHSPACES     = 0x00000011,
  PIDDSI_SHAREDDOC         = 0x00000013,
  PIDDSI_LINKBASE          = 0x00000014,
  PIDDSI_HLINKS            = 0x00000015,
  PIDDSI_HYPERLINKSCHANGED = 0x00000016,
  PIDDSI_VERSION           = 0x00000017,
  PIDDSI_DIGSIG            = 0x00000018,
  PIDDSI_CONTENTTYPE       = 0x0000001A,
  PIDDSI_CONTENTSTATUS     = 0x0000001B,
  PIDDSI_LANGUAGE          = 0x0000001C,
  PIDDSI_DOCVERSION        = 0x0000001D
};

enum PIDSI
{
  CODEPAGE_PROPERTY_IDENTIFIER = 0x00000001,
  PIDSI_TITLE                  = 0x00000002,
  PIDSI_SUBJECT                = 0x00000003,
  PIDSI_AUTHOR                 = 0x00000004,
  PIDSI_KEYWORDS               = 0x00000005,
  PIDSI_COMMENTS               = 0x00000006,
  PIDSI_TEMPLATE               = 0x00000007,
  PIDSI_LASTAUTHOR             = 0x00000008,
  PIDSI_REVNUMBER              = 0x00000009,
  PIDSI_EDITTIME               = 0x0000000A,
  PIDSI_LASTPRINTED            = 0x0000000B,
  PIDSI_CREATE_DTM             = 0x0000000C,
  PIDSI_LASTSAVE_DTM           = 0x0000000D,
  PIDSI_PAGECOUNT              = 0x0000000E,
  PIDSI_WORDCOUNT              = 0x0000000F,
  PIDSI_CHARCOUNT              = 0x00000010,
  PIDSI_THUMBNAIL              = 0x00000011,
  PIDSI_APPNAME                = 0x00000012,
  PIDSI_DOC_SECURITY           = 0x00000013
};

bool libvisio::VSDMetaData::parse(librevenge::RVNGInputStream *input)
{
  if (!input)
    return false;

  readPropertySetStream(input);

  return true;
}

void libvisio::VSDMetaData::readPropertySetStream(librevenge::RVNGInputStream *input)
{
  // ByteOrder
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // Version
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // SystemIdentifier
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // CLSID
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  // NumPropertySets
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // FMTID0
  //input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t data1 = readU32(input);
  uint16_t data2 = readU16(input);
  uint16_t data3 = readU16(input);
  uint8_t data4[8];
  for (int i = 0; i < 8; i++)
  {
    data4[i] = readU8(input);
  }
  // Pretty-printed GUID is 36 bytes + the terminating null-character.
  char FMTID0[37];
  sprintf(FMTID0, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", data1, data2, data3,
          data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]);

  uint32_t offset0 = readU32(input);
  readPropertySet(input, offset0, FMTID0);
}

void libvisio::VSDMetaData::readPropertySet(librevenge::RVNGInputStream *input, uint32_t offset, char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);

  // Size
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  uint32_t numProperties = readU32(input);
  for (uint32_t i = 0; i < numProperties; ++i)
    readPropertyIdentifierAndOffset(input);
  for (uint32_t i = 0; i < numProperties; ++i)
  {
    if (i >= m_idsAndOffsets.size())
      break;
    readTypedPropertyValue(input, i, offset + m_idsAndOffsets[i].second, FMTID);
  }
}

uint32_t libvisio::VSDMetaData::getCodePage()
{
  for (size_t i = 0; i < m_idsAndOffsets.size(); ++i)
  {
    if (m_idsAndOffsets[i].first == PIDSI::CODEPAGE_PROPERTY_IDENTIFIER)
    {
      if (i >= m_typedPropertyValues.size())
        break;
      return m_typedPropertyValues[i];
    }
  }

  return 0;
}

void libvisio::VSDMetaData::readPropertyIdentifierAndOffset(librevenge::RVNGInputStream *input)
{
  uint32_t propertyIdentifier = readU32(input);
  uint32_t offset = readU32(input);
  m_idsAndOffsets.push_back(std::make_pair(propertyIdentifier, offset));
}

#define VT_I2 0x0002
#define VT_LPSTR 0x001E

void libvisio::VSDMetaData::readTypedPropertyValue(librevenge::RVNGInputStream *input,
                                                   uint32_t index,
                                                   uint32_t offset,
                                                   char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);
  uint16_t type = readU16(input);
  // Padding
  input->seek(2, librevenge::RVNG_SEEK_CUR);

  if (type == VT_I2)
  {
    uint16_t value = readU16(input);
    m_typedPropertyValues[index] = value;
  }
  else if (type == VT_LPSTR)
  {
    librevenge::RVNGString string = readCodePageString(input);
    if (!string.empty())
    {
      if (index >= m_idsAndOffsets.size())
        return;

      if (!strcmp(FMTID, "f29f85e0-4ff9-1068-ab91-08002b27b3d9"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDSI::PIDSI_TITLE:
          m_metaData.insert("dc:title", string);
          break;
        case PIDSI::PIDSI_SUBJECT:
          m_metaData.insert("dc:subject", string);
          break;
        case PIDSI::PIDSI_AUTHOR:
          m_metaData.insert("meta:initial-creator", string);
          m_metaData.insert("dc:creator", string);
          break;
        case PIDSI::PIDSI_KEYWORDS:
          m_metaData.insert("meta:keyword", string);
          break;
        case PIDSI::PIDSI_COMMENTS:
          m_metaData.insert("dc:description", string);
          break;
        case PIDSI::PIDSI_TEMPLATE:
          std::string templateHref(string.cstr());
          size_t found = templateHref.find_last_of("/\\");
          if (found != std::string::npos)
            string = librevenge::RVNGString(templateHref.substr(found+1).c_str());
          m_metaData.insert("librevenge:template", string);
          break;
        }
      }
      else if (!strcmp(FMTID,"d5cdd502-2e9c-101b-9397-08002b2cf9ae"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDDSI::PIDDSI_CATEGORY:
          m_metaData.insert("librevenge:category", string);
          break;
        case PIDDSI::PIDDSI_LINECOUNT:
          // this should actually be PIDDSI::PIDDSI_COMPANY but this
          // is what company is mapped to
          m_metaData.insert("librevenge:company", string);
          break;
        case PIDDSI::PIDDSI_LANGUAGE:
          m_metaData.insert("dc:language", string);
          break;
        }
      }
    }
  }
}

librevenge::RVNGString libvisio::VSDMetaData::readCodePageString(librevenge::RVNGInputStream *input)
{
  uint32_t size = readU32(input);

  std::vector<unsigned char> characters;
  for (uint32_t i = 0; i < size; ++i)
    characters.push_back(readU8(input));

  uint32_t codepage = getCodePage();
  librevenge::RVNGString string;

  if (codepage == 65001)
  {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130%28v=vs.85%29.aspx
    // says this is UTF-8.
    for (std::vector<unsigned char>::const_iterator i = characters.begin(); i != characters.end(); ++i)
      string.append((const char)*i);
  }
  else
  {
    UErrorCode status = U_ZERO_ERROR;
    UConverter *conv = 0;

    switch (codepage)
    {
    case 1252:
      // http://msdn.microsoft.com/en-us/goglobal/bb964654
      conv = ucnv_open("windows-1252", &status);
      break;
    }

    if (U_SUCCESS(status) && conv)
    {
      const char *src = (const char *)&characters[0];
      const char *srcLimit = (const char *)src + characters.size();
      while (src < srcLimit)
      {
        UChar32 ucs4Character = ucnv_getNextUChar(conv, &src, srcLimit, &status);
        if (U_SUCCESS(status) && U_IS_UNICODE_CHAR(ucs4Character))
          appendUCS4(string, ucs4Character);
      }
    }

    if (conv)
      ucnv_close(conv);
  }

  return string;
}

bool libvisio::VSDMetaData::parseTimes(librevenge::RVNGInputStream *input)
{
  // Parse the header
  // HeaderSignature: 8 bytes
  // HeaderCLSID: 16 bytes
  // MinorVersion: 2 bytes
  // MajorVersion: 2 bytes
  // ByteOrder: 2 bytes
  input->seek(30, librevenge::RVNG_SEEK_CUR);
  uint16_t sectorShift = readU16(input);
  // MiniSectorShift: 2 bytes
  // Reserved: 6 bytes
  // NumDirectorySectors: 4 bytes
  // NumFATSectors: 4 bytes
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t firstDirSectorLocation = readU32(input);

  // Seek to the Root Directory Entry
  size_t sectorSize = std::pow(2, sectorShift);
  input->seek((firstDirSectorLocation + 1) * sectorSize, librevenge::RVNG_SEEK_SET);
  // DirectoryEntryName: 64 bytes
  // DirectoryEntryNameLength: 2 bytes
  // ObjectType: 1 byte
  // ColorFlag: 1 byte
  // LeftSiblingID: 4 bytes
  // RightSiblingID: 4 bytes
  // ChildID: 4 bytes
  // CLSID: 16 bytes
  // StateBits: 4 bytes
  // CreationTime: 8 bytes
  input->seek(108, librevenge::RVNG_SEEK_CUR);
  uint64_t modifiedTime = readU64(input);

  // modifiedTime is number of 100ns since Jan 1 1601
  const uint64_t epoch = uint64_t(116444736UL) * 100;
  time_t sec = (modifiedTime / 10000000) - epoch;
  const struct tm *time = localtime(&sec);
  if (time)
  {
    static const int MAX_BUFFER = 1024;
    char buffer[MAX_BUFFER];
    strftime(&buffer[0], MAX_BUFFER-1, "%Y-%m-%dT%H:%M:%SZ", time);
    librevenge::RVNGString result;
    result.append(buffer);
    // Visio UI uses modifiedTime for both purposes.
    m_metaData.insert("meta:creation-date", result);
    m_metaData.insert("dc:date", result);
    return true;
  }
  return false;
}

const librevenge::RVNGPropertyList &libvisio::VSDMetaData::getMetaData()
{
  return m_metaData;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>C++11 syntax is not allowed<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libvisio project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "VSDMetaData.h"
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <unicode/ucnv.h>
#include <ctime>

libvisio::VSDMetaData::VSDMetaData()
  : m_idsAndOffsets(), m_typedPropertyValues(), m_metaData()
{
}

libvisio::VSDMetaData::~VSDMetaData()
{
}

enum PIDDSI
{
  PIDDSI_CODEPAGE          = 0x00000001,
  PIDDSI_CATEGORY          = 0x00000002,
  PIDDSI_PRESFORMAT        = 0x00000003,
  PIDDSI_BYTECOUNT         = 0x00000004,
  PIDDSI_LINECOUNT         = 0x00000005,
  PIDDSI_PARACOUNT         = 0x00000006,
  PIDDSI_SLIDECOUNT        = 0x00000007,
  PIDDSI_NOTECOUNT         = 0x00000008,
  PIDDSI_HIDDENCOUNT       = 0x00000009,
  PIDDSI_MMCLIPCOUNT       = 0x0000000A,
  PIDDSI_SCALE             = 0x0000000B,
  PIDDSI_HEADINGPAIR       = 0x0000000C,
  PIDDSI_DOCPARTS          = 0x0000000D,
  PIDDSI_MANAGER           = 0x0000000E,
  PIDDSI_COMPANY           = 0x0000000F,
  PIDDSI_LINKSDIRTY        = 0x00000010,
  PIDDSI_CCHWITHSPACES     = 0x00000011,
  PIDDSI_SHAREDDOC         = 0x00000013,
  PIDDSI_LINKBASE          = 0x00000014,
  PIDDSI_HLINKS            = 0x00000015,
  PIDDSI_HYPERLINKSCHANGED = 0x00000016,
  PIDDSI_VERSION           = 0x00000017,
  PIDDSI_DIGSIG            = 0x00000018,
  PIDDSI_CONTENTTYPE       = 0x0000001A,
  PIDDSI_CONTENTSTATUS     = 0x0000001B,
  PIDDSI_LANGUAGE          = 0x0000001C,
  PIDDSI_DOCVERSION        = 0x0000001D
};

enum PIDSI
{
  CODEPAGE_PROPERTY_IDENTIFIER = 0x00000001,
  PIDSI_TITLE                  = 0x00000002,
  PIDSI_SUBJECT                = 0x00000003,
  PIDSI_AUTHOR                 = 0x00000004,
  PIDSI_KEYWORDS               = 0x00000005,
  PIDSI_COMMENTS               = 0x00000006,
  PIDSI_TEMPLATE               = 0x00000007,
  PIDSI_LASTAUTHOR             = 0x00000008,
  PIDSI_REVNUMBER              = 0x00000009,
  PIDSI_EDITTIME               = 0x0000000A,
  PIDSI_LASTPRINTED            = 0x0000000B,
  PIDSI_CREATE_DTM             = 0x0000000C,
  PIDSI_LASTSAVE_DTM           = 0x0000000D,
  PIDSI_PAGECOUNT              = 0x0000000E,
  PIDSI_WORDCOUNT              = 0x0000000F,
  PIDSI_CHARCOUNT              = 0x00000010,
  PIDSI_THUMBNAIL              = 0x00000011,
  PIDSI_APPNAME                = 0x00000012,
  PIDSI_DOC_SECURITY           = 0x00000013
};

bool libvisio::VSDMetaData::parse(librevenge::RVNGInputStream *input)
{
  if (!input)
    return false;

  readPropertySetStream(input);

  return true;
}

void libvisio::VSDMetaData::readPropertySetStream(librevenge::RVNGInputStream *input)
{
  // ByteOrder
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // Version
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // SystemIdentifier
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // CLSID
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  // NumPropertySets
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // FMTID0
  //input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t data1 = readU32(input);
  uint16_t data2 = readU16(input);
  uint16_t data3 = readU16(input);
  uint8_t data4[8];
  for (int i = 0; i < 8; i++)
  {
    data4[i] = readU8(input);
  }
  // Pretty-printed GUID is 36 bytes + the terminating null-character.
  char FMTID0[37];
  sprintf(FMTID0, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", data1, data2, data3,
          data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]);

  uint32_t offset0 = readU32(input);
  readPropertySet(input, offset0, FMTID0);
}

void libvisio::VSDMetaData::readPropertySet(librevenge::RVNGInputStream *input, uint32_t offset, char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);

  // Size
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  uint32_t numProperties = readU32(input);
  for (uint32_t i = 0; i < numProperties; ++i)
    readPropertyIdentifierAndOffset(input);
  for (uint32_t i = 0; i < numProperties; ++i)
  {
    if (i >= m_idsAndOffsets.size())
      break;
    readTypedPropertyValue(input, i, offset + m_idsAndOffsets[i].second, FMTID);
  }
}

uint32_t libvisio::VSDMetaData::getCodePage()
{
  for (size_t i = 0; i < m_idsAndOffsets.size(); ++i)
  {
    if (m_idsAndOffsets[i].first == CODEPAGE_PROPERTY_IDENTIFIER)
    {
      if (i >= m_typedPropertyValues.size())
        break;
      return m_typedPropertyValues[i];
    }
  }

  return 0;
}

void libvisio::VSDMetaData::readPropertyIdentifierAndOffset(librevenge::RVNGInputStream *input)
{
  uint32_t propertyIdentifier = readU32(input);
  uint32_t offset = readU32(input);
  m_idsAndOffsets.push_back(std::make_pair(propertyIdentifier, offset));
}

#define VT_I2 0x0002
#define VT_LPSTR 0x001E

void libvisio::VSDMetaData::readTypedPropertyValue(librevenge::RVNGInputStream *input,
                                                   uint32_t index,
                                                   uint32_t offset,
                                                   char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);
  uint16_t type = readU16(input);
  // Padding
  input->seek(2, librevenge::RVNG_SEEK_CUR);

  if (type == VT_I2)
  {
    uint16_t value = readU16(input);
    m_typedPropertyValues[index] = value;
  }
  else if (type == VT_LPSTR)
  {
    librevenge::RVNGString string = readCodePageString(input);
    if (!string.empty())
    {
      if (index >= m_idsAndOffsets.size())
        return;

      if (!strcmp(FMTID, "f29f85e0-4ff9-1068-ab91-08002b27b3d9"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDSI_TITLE:
          m_metaData.insert("dc:title", string);
          break;
        case PIDSI_SUBJECT:
          m_metaData.insert("dc:subject", string);
          break;
        case PIDSI_AUTHOR:
          m_metaData.insert("meta:initial-creator", string);
          m_metaData.insert("dc:creator", string);
          break;
        case PIDSI_KEYWORDS:
          m_metaData.insert("meta:keyword", string);
          break;
        case PIDSI_COMMENTS:
          m_metaData.insert("dc:description", string);
          break;
        case PIDSI_TEMPLATE:
          std::string templateHref(string.cstr());
          size_t found = templateHref.find_last_of("/\\");
          if (found != std::string::npos)
            string = librevenge::RVNGString(templateHref.substr(found+1).c_str());
          m_metaData.insert("librevenge:template", string);
          break;
        }
      }
      else if (!strcmp(FMTID,"d5cdd502-2e9c-101b-9397-08002b2cf9ae"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDDSI::PIDDSI_CATEGORY:
          m_metaData.insert("librevenge:category", string);
          break;
        case PIDDSI::PIDDSI_LINECOUNT:
          // this should actually be PIDDSI::PIDDSI_COMPANY but this
          // is what company is mapped to
          m_metaData.insert("librevenge:company", string);
          break;
        case PIDDSI::PIDDSI_LANGUAGE:
          m_metaData.insert("dc:language", string);
          break;
        }
      }
    }
  }
}

librevenge::RVNGString libvisio::VSDMetaData::readCodePageString(librevenge::RVNGInputStream *input)
{
  uint32_t size = readU32(input);

  std::vector<unsigned char> characters;
  for (uint32_t i = 0; i < size; ++i)
    characters.push_back(readU8(input));

  uint32_t codepage = getCodePage();
  librevenge::RVNGString string;

  if (codepage == 65001)
  {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130%28v=vs.85%29.aspx
    // says this is UTF-8.
    for (std::vector<unsigned char>::const_iterator i = characters.begin(); i != characters.end(); ++i)
      string.append((const char)*i);
  }
  else
  {
    UErrorCode status = U_ZERO_ERROR;
    UConverter *conv = 0;

    switch (codepage)
    {
    case 1252:
      // http://msdn.microsoft.com/en-us/goglobal/bb964654
      conv = ucnv_open("windows-1252", &status);
      break;
    }

    if (U_SUCCESS(status) && conv)
    {
      const char *src = (const char *)&characters[0];
      const char *srcLimit = (const char *)src + characters.size();
      while (src < srcLimit)
      {
        UChar32 ucs4Character = ucnv_getNextUChar(conv, &src, srcLimit, &status);
        if (U_SUCCESS(status) && U_IS_UNICODE_CHAR(ucs4Character))
          appendUCS4(string, ucs4Character);
      }
    }

    if (conv)
      ucnv_close(conv);
  }

  return string;
}

bool libvisio::VSDMetaData::parseTimes(librevenge::RVNGInputStream *input)
{
  // Parse the header
  // HeaderSignature: 8 bytes
  // HeaderCLSID: 16 bytes
  // MinorVersion: 2 bytes
  // MajorVersion: 2 bytes
  // ByteOrder: 2 bytes
  input->seek(30, librevenge::RVNG_SEEK_CUR);
  uint16_t sectorShift = readU16(input);
  // MiniSectorShift: 2 bytes
  // Reserved: 6 bytes
  // NumDirectorySectors: 4 bytes
  // NumFATSectors: 4 bytes
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t firstDirSectorLocation = readU32(input);

  // Seek to the Root Directory Entry
  size_t sectorSize = std::pow(2, sectorShift);
  input->seek((firstDirSectorLocation + 1) * sectorSize, librevenge::RVNG_SEEK_SET);
  // DirectoryEntryName: 64 bytes
  // DirectoryEntryNameLength: 2 bytes
  // ObjectType: 1 byte
  // ColorFlag: 1 byte
  // LeftSiblingID: 4 bytes
  // RightSiblingID: 4 bytes
  // ChildID: 4 bytes
  // CLSID: 16 bytes
  // StateBits: 4 bytes
  // CreationTime: 8 bytes
  input->seek(108, librevenge::RVNG_SEEK_CUR);
  uint64_t modifiedTime = readU64(input);

  // modifiedTime is number of 100ns since Jan 1 1601
  const uint64_t epoch = uint64_t(116444736UL) * 100;
  time_t sec = (modifiedTime / 10000000) - epoch;
  const struct tm *time = localtime(&sec);
  if (time)
  {
    static const int MAX_BUFFER = 1024;
    char buffer[MAX_BUFFER];
    strftime(&buffer[0], MAX_BUFFER-1, "%Y-%m-%dT%H:%M:%SZ", time);
    librevenge::RVNGString result;
    result.append(buffer);
    // Visio UI uses modifiedTime for both purposes.
    m_metaData.insert("meta:creation-date", result);
    m_metaData.insert("dc:date", result);
    return true;
  }
  return false;
}

const librevenge::RVNGPropertyList &libvisio::VSDMetaData::getMetaData()
{
  return m_metaData;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<|endoftext|>