<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "rpcsend.h"
#include "rpcsend_private.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/network/rpcnetwork.h>
#include <vespa/messagebus/tracelevel.h>
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/errorcode.h>
#include <vespa/fnet/channel.h>
#include <vespa/fnet/frt/reflection.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/lambdatask.h>

#include <vespa/vespalib/data/slime/cursor.h>

using vespalib::make_string;
using vespalib::makeLambdaTask;

namespace mbus {

using network::internal::ReplyContext;
using network::internal::SendContext;

namespace {

class FillByCopy final : public PayLoadFiller
{
public:
    FillByCopy(BlobRef payload) : _payload(payload) { }
    void fill(FRT_Values & v) const override {
        v.AddData(_payload.data(), _payload.size());
    }
    void fill(const vespalib::Memory & name, vespalib::slime::Cursor & v) const override {
        v.setData(name, vespalib::Memory(_payload.data(), _payload.size()));
    }
private:
    BlobRef _payload;
};

class FillByHandover final : public PayLoadFiller
{
public:
    FillByHandover(Blob payload) : _payload(std::move(payload)) { }
    void fill(FRT_Values & v) const override {
        size_t sz = _payload.size();
        v.AddData(std::move(_payload.payload()), sz);
    }
    void fill(const vespalib::Memory & name, vespalib::slime::Cursor & v) const override {
        v.setData(name, vespalib::Memory(_payload.data(), _payload.size()));
    }
private:
    mutable Blob _payload;
};

}

RPCSend::RPCSend() :
    _net(nullptr),
    _clientIdent("client"),
    _serverIdent("server")
{ }

RPCSend::~RPCSend() {}

void
RPCSend::attach(RPCNetwork &net)
{
    _net = &net;
    const string &prefix = _net->getIdentity().getServicePrefix();
    if (!prefix.empty()) {
        _clientIdent = make_string("'%s'", prefix.c_str());
        _serverIdent = _clientIdent;
    }

    FRT_ReflectionBuilder builder(&_net->getSupervisor());
    build(builder);
}

void
RPCSend::replyError(FRT_RPCRequest *req, const vespalib::Version &version, uint32_t traceLevel, const Error &err)
{
    Reply::UP reply(new EmptyReply());
    reply->setContext(Context(new ReplyContext(*req, version)));
    reply->getTrace().setLevel(traceLevel);
    reply->addError(err);
    handleReply(std::move(reply));
}

void
RPCSend::handleDiscard(Context ctx)
{
    ReplyContext::UP tmp(static_cast<ReplyContext*>(ctx.value.PTR));
    FRT_RPCRequest &req = tmp->getRequest();
    FNET_Channel *chn = req.GetContext()._value.CHANNEL;
    req.SubRef();
    chn->Free();
}

void
RPCSend::sendByHandover(RoutingNode &recipient, const vespalib::Version &version, Blob payload, uint64_t timeRemaining)
{
    send(recipient, version, FillByHandover(std::move(payload)), timeRemaining);
}

void
RPCSend::send(RoutingNode &recipient, const vespalib::Version &version, BlobRef payload, uint64_t timeRemaining)
{
    send(recipient, version, FillByCopy(payload), timeRemaining);
}

void
RPCSend::send(RoutingNode &recipient, const vespalib::Version &version,
              const PayLoadFiller & payload, uint64_t timeRemaining)
{
    SendContext::UP ctx(new SendContext(recipient, timeRemaining));
    RPCServiceAddress &address = static_cast<RPCServiceAddress&>(recipient.getServiceAddress());
    const Message &msg = recipient.getMessage();
    Route route = recipient.getRoute();
    Hop hop = route.removeHop(0);

    FRT_RPCRequest *req = _net->allocRequest();
    encodeRequest(*req, version, route, address, msg, recipient.getTrace().getLevel(),  payload, timeRemaining);

    if (ctx->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        ctx->getTrace().trace(TraceLevel::SEND_RECEIVE,
                              make_string("Sending message (version %s) from %s to '%s' with %.2f seconds timeout.",
                                          version.toString().c_str(), _clientIdent.c_str(),
                                          address.getServiceName().c_str(), ctx->getTimeout()));
    }

    if (hop.getIgnoreResult()) {
        address.getTarget().getFRTTarget().InvokeVoid(req);
        if (ctx->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
            ctx->getTrace().trace(TraceLevel::SEND_RECEIVE,
                                  make_string("Not waiting for a reply from '%s'.", address.getServiceName().c_str()));
        }
        Reply::UP reply(new EmptyReply());
        reply->getTrace().swap(ctx->getTrace());
        _net->getOwner().deliverReply(std::move(reply), recipient);
    } else {
        SendContext *ptr = ctx.release();
        req->SetContext(FNET_Context(ptr));
        address.getTarget().getFRTTarget().InvokeAsync(req, ptr->getTimeout(), this);
    }
}

void
RPCSend::RequestDone(FRT_RPCRequest *req)
{
    _net->getExecutor().execute(makeLambdaTask([this, req]() { doRequestDone(req);}));
}

void
RPCSend::doRequestDone(FRT_RPCRequest *req) {
    SendContext::UP ctx(static_cast<SendContext*>(req->GetContext()._value.VOIDP));
    const string &serviceName = static_cast<RPCServiceAddress&>(ctx->getRecipient().getServiceAddress()).getServiceName();
    Reply::UP reply;
    Error error;
    Trace & trace = ctx->getTrace();
    if (!req->CheckReturnTypes(getReturnSpec())) {
        reply.reset(new EmptyReply());
        switch (req->GetErrorCode()) {
            case FRTE_RPC_TIMEOUT:
                error = Error(ErrorCode::TIMEOUT,
                              make_string("A timeout occured while waiting for '%s' (%g seconds expired); %s",
                                          serviceName.c_str(), ctx->getTimeout(), req->GetErrorMessage()));
                break;
            case FRTE_RPC_CONNECTION:
                error = Error(ErrorCode::CONNECTION_ERROR,
                              make_string("A connection error occured for '%s'; %s",
                                          serviceName.c_str(), req->GetErrorMessage()));
                break;
            default:
                error = Error(ErrorCode::NETWORK_ERROR,
                              make_string("A network error occured for '%s'; %s",
                                          serviceName.c_str(), req->GetErrorMessage()));
        }
    } else {
        FRT_Values &ret = *req->GetReturn();
        reply = createReply(ret, serviceName, error, trace.getRoot());
    }
    if (trace.shouldTrace(TraceLevel::SEND_RECEIVE)) {
        trace.trace(TraceLevel::SEND_RECEIVE,
                    make_string("Reply (type %d) received at %s.", reply->getType(), _clientIdent.c_str()));
    }
    reply->getTrace().swap(trace);
    if (error.getCode() != ErrorCode::NONE) {
        reply->addError(error);
    }
    _net->getOwner().deliverReply(std::move(reply), ctx->getRecipient());
    req->SubRef();
}

std::unique_ptr<Reply>
RPCSend::decode(vespalib::stringref protocolName, const vespalib::Version & version, BlobRef payload, Error & error) const
{
    Reply::UP reply;
    IProtocol * protocol = _net->getOwner().getProtocol(protocolName);
    if (protocol != nullptr) {
        Routable::UP routable = protocol->decode(version, payload);
        if (routable) {
            if (routable->isReply()) {
                reply.reset(static_cast<Reply*>(routable.release()));
            } else {
                error = Error(ErrorCode::DECODE_ERROR, "Payload decoded to a message when expecting a reply.");
            }
        } else {
            error = Error(ErrorCode::DECODE_ERROR,
                          make_string("Protocol '%s' failed to decode routable.", protocolName.c_str()));
        }

    } else {
        error = Error(ErrorCode::UNKNOWN_PROTOCOL,
                      make_string("Protocol '%s' is not known by %s.", protocolName.c_str(), _serverIdent.c_str()));
    }
    return reply;
}

void
RPCSend::handleReply(Reply::UP reply)
{
    _net->getExecutor().execute(makeLambdaTask([this, reply = std::move(reply)]() mutable {
        doHandleReply(std::move(reply));
    }));
}

void
RPCSend::doHandleReply(Reply::UP reply) {
    ReplyContext::UP ctx(static_cast<ReplyContext*>(reply->getContext().value.PTR));
    FRT_RPCRequest &req = ctx->getRequest();
    string version = ctx->getVersion().toString();
    if (reply->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        reply->getTrace().trace(TraceLevel::SEND_RECEIVE, make_string("Sending reply (version %s) from %s.",
                                                                      version.c_str(), _serverIdent.c_str()));
    }
    Blob payload(0);
    if (reply->getType() != 0) {
        payload = _net->getOwner().getProtocol(reply->getProtocol())->encode(ctx->getVersion(), *reply);
        if (payload.size() == 0) {
            reply->addError(Error(ErrorCode::ENCODE_ERROR, "An error occured while encoding the reply, see log."));
        }
    }
    FRT_Values &ret = *req.GetReturn();
    createResponse(ret, version, *reply, std::move(payload));
    req.Return();
}

void
RPCSend::invoke(FRT_RPCRequest *req)
{
    req->Detach();
    auto rejected = _net->getExecutor().execute(makeLambdaTask([this, req]() { doRequest(req);}));
    assert(!rejected);
}

void
RPCSend::doRequest(FRT_RPCRequest *req)
{
    FRT_Values &args = *req->GetParams();

    std::unique_ptr<Params> params = toParams(args);
    IProtocol * protocol = _net->getOwner().getProtocol(params->getProtocol());
    if (protocol == nullptr) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::UNKNOWN_PROTOCOL,
                         make_string("Protocol '%s' is not known by %s.", params->getProtocol().c_str(), _serverIdent.c_str())));
        return;
    }
    Routable::UP routable = protocol->decode(params->getVersion(), params->getPayload());
    req->DiscardBlobs();
    if ( ! routable ) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::DECODE_ERROR,
                         make_string("Protocol '%s' failed to decode routable.", params->getProtocol().c_str())));
        return;
    }
    if (routable->isReply()) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::DECODE_ERROR, "Payload decoded to a reply when expecting a mesage."));
        return;
    }
    Message::UP msg(static_cast<Message*>(routable.release()));
    vespalib::stringref route = params->getRoute();
    if (!route.empty()) {
        msg->setRoute(Route::parse(route));
    }
    msg->setContext(Context(new ReplyContext(*req, params->getVersion())));
    msg->pushHandler(*this, *this);
    msg->setRetryEnabled(params->useRetry());
    msg->setRetry(params->getRetries());
    msg->setTimeReceivedNow();
    msg->setTimeRemaining(params->getRemainingTime());
    msg->getTrace().setLevel(params->getTraceLevel());
    if (msg->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        msg->getTrace().trace(TraceLevel::SEND_RECEIVE,
                              make_string("Message (type %d) received at %s for session '%s'.",
                                          msg->getType(), _serverIdent.c_str(), string(params->getSession()).c_str()));
    }
    _net->getOwner().deliverMessage(std::move(msg), params->getSession());
}

} // namespace mbus
<commit_msg>Assert that task is accepted.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "rpcsend.h"
#include "rpcsend_private.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/network/rpcnetwork.h>
#include <vespa/messagebus/tracelevel.h>
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/errorcode.h>
#include <vespa/fnet/channel.h>
#include <vespa/fnet/frt/reflection.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/lambdatask.h>

#include <vespa/vespalib/data/slime/cursor.h>

using vespalib::make_string;
using vespalib::makeLambdaTask;

namespace mbus {

using network::internal::ReplyContext;
using network::internal::SendContext;

namespace {

class FillByCopy final : public PayLoadFiller
{
public:
    FillByCopy(BlobRef payload) : _payload(payload) { }
    void fill(FRT_Values & v) const override {
        v.AddData(_payload.data(), _payload.size());
    }
    void fill(const vespalib::Memory & name, vespalib::slime::Cursor & v) const override {
        v.setData(name, vespalib::Memory(_payload.data(), _payload.size()));
    }
private:
    BlobRef _payload;
};

class FillByHandover final : public PayLoadFiller
{
public:
    FillByHandover(Blob payload) : _payload(std::move(payload)) { }
    void fill(FRT_Values & v) const override {
        size_t sz = _payload.size();
        v.AddData(std::move(_payload.payload()), sz);
    }
    void fill(const vespalib::Memory & name, vespalib::slime::Cursor & v) const override {
        v.setData(name, vespalib::Memory(_payload.data(), _payload.size()));
    }
private:
    mutable Blob _payload;
};

}

RPCSend::RPCSend() :
    _net(nullptr),
    _clientIdent("client"),
    _serverIdent("server")
{ }

RPCSend::~RPCSend() {}

void
RPCSend::attach(RPCNetwork &net)
{
    _net = &net;
    const string &prefix = _net->getIdentity().getServicePrefix();
    if (!prefix.empty()) {
        _clientIdent = make_string("'%s'", prefix.c_str());
        _serverIdent = _clientIdent;
    }

    FRT_ReflectionBuilder builder(&_net->getSupervisor());
    build(builder);
}

void
RPCSend::replyError(FRT_RPCRequest *req, const vespalib::Version &version, uint32_t traceLevel, const Error &err)
{
    Reply::UP reply(new EmptyReply());
    reply->setContext(Context(new ReplyContext(*req, version)));
    reply->getTrace().setLevel(traceLevel);
    reply->addError(err);
    handleReply(std::move(reply));
}

void
RPCSend::handleDiscard(Context ctx)
{
    ReplyContext::UP tmp(static_cast<ReplyContext*>(ctx.value.PTR));
    FRT_RPCRequest &req = tmp->getRequest();
    FNET_Channel *chn = req.GetContext()._value.CHANNEL;
    req.SubRef();
    chn->Free();
}

void
RPCSend::sendByHandover(RoutingNode &recipient, const vespalib::Version &version, Blob payload, uint64_t timeRemaining)
{
    send(recipient, version, FillByHandover(std::move(payload)), timeRemaining);
}

void
RPCSend::send(RoutingNode &recipient, const vespalib::Version &version, BlobRef payload, uint64_t timeRemaining)
{
    send(recipient, version, FillByCopy(payload), timeRemaining);
}

void
RPCSend::send(RoutingNode &recipient, const vespalib::Version &version,
              const PayLoadFiller & payload, uint64_t timeRemaining)
{
    SendContext::UP ctx(new SendContext(recipient, timeRemaining));
    RPCServiceAddress &address = static_cast<RPCServiceAddress&>(recipient.getServiceAddress());
    const Message &msg = recipient.getMessage();
    Route route = recipient.getRoute();
    Hop hop = route.removeHop(0);

    FRT_RPCRequest *req = _net->allocRequest();
    encodeRequest(*req, version, route, address, msg, recipient.getTrace().getLevel(),  payload, timeRemaining);

    if (ctx->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        ctx->getTrace().trace(TraceLevel::SEND_RECEIVE,
                              make_string("Sending message (version %s) from %s to '%s' with %.2f seconds timeout.",
                                          version.toString().c_str(), _clientIdent.c_str(),
                                          address.getServiceName().c_str(), ctx->getTimeout()));
    }

    if (hop.getIgnoreResult()) {
        address.getTarget().getFRTTarget().InvokeVoid(req);
        if (ctx->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
            ctx->getTrace().trace(TraceLevel::SEND_RECEIVE,
                                  make_string("Not waiting for a reply from '%s'.", address.getServiceName().c_str()));
        }
        Reply::UP reply(new EmptyReply());
        reply->getTrace().swap(ctx->getTrace());
        _net->getOwner().deliverReply(std::move(reply), recipient);
    } else {
        SendContext *ptr = ctx.release();
        req->SetContext(FNET_Context(ptr));
        address.getTarget().getFRTTarget().InvokeAsync(req, ptr->getTimeout(), this);
    }
}

void
RPCSend::RequestDone(FRT_RPCRequest *req)
{
    auto task = _net->getExecutor().execute(makeLambdaTask([this, req]() { doRequestDone(req);}));
    assert(!task);
}

void
RPCSend::doRequestDone(FRT_RPCRequest *req) {
    SendContext::UP ctx(static_cast<SendContext*>(req->GetContext()._value.VOIDP));
    const string &serviceName = static_cast<RPCServiceAddress&>(ctx->getRecipient().getServiceAddress()).getServiceName();
    Reply::UP reply;
    Error error;
    Trace & trace = ctx->getTrace();
    if (!req->CheckReturnTypes(getReturnSpec())) {
        reply.reset(new EmptyReply());
        switch (req->GetErrorCode()) {
            case FRTE_RPC_TIMEOUT:
                error = Error(ErrorCode::TIMEOUT,
                              make_string("A timeout occured while waiting for '%s' (%g seconds expired); %s",
                                          serviceName.c_str(), ctx->getTimeout(), req->GetErrorMessage()));
                break;
            case FRTE_RPC_CONNECTION:
                error = Error(ErrorCode::CONNECTION_ERROR,
                              make_string("A connection error occured for '%s'; %s",
                                          serviceName.c_str(), req->GetErrorMessage()));
                break;
            default:
                error = Error(ErrorCode::NETWORK_ERROR,
                              make_string("A network error occured for '%s'; %s",
                                          serviceName.c_str(), req->GetErrorMessage()));
        }
    } else {
        FRT_Values &ret = *req->GetReturn();
        reply = createReply(ret, serviceName, error, trace.getRoot());
    }
    if (trace.shouldTrace(TraceLevel::SEND_RECEIVE)) {
        trace.trace(TraceLevel::SEND_RECEIVE,
                    make_string("Reply (type %d) received at %s.", reply->getType(), _clientIdent.c_str()));
    }
    reply->getTrace().swap(trace);
    if (error.getCode() != ErrorCode::NONE) {
        reply->addError(error);
    }
    _net->getOwner().deliverReply(std::move(reply), ctx->getRecipient());
    req->SubRef();
}

std::unique_ptr<Reply>
RPCSend::decode(vespalib::stringref protocolName, const vespalib::Version & version, BlobRef payload, Error & error) const
{
    Reply::UP reply;
    IProtocol * protocol = _net->getOwner().getProtocol(protocolName);
    if (protocol != nullptr) {
        Routable::UP routable = protocol->decode(version, payload);
        if (routable) {
            if (routable->isReply()) {
                reply.reset(static_cast<Reply*>(routable.release()));
            } else {
                error = Error(ErrorCode::DECODE_ERROR, "Payload decoded to a message when expecting a reply.");
            }
        } else {
            error = Error(ErrorCode::DECODE_ERROR,
                          make_string("Protocol '%s' failed to decode routable.", protocolName.c_str()));
        }

    } else {
        error = Error(ErrorCode::UNKNOWN_PROTOCOL,
                      make_string("Protocol '%s' is not known by %s.", protocolName.c_str(), _serverIdent.c_str()));
    }
    return reply;
}

void
RPCSend::handleReply(Reply::UP reply)
{
    _net->getExecutor().execute(makeLambdaTask([this, reply = std::move(reply)]() mutable {
        doHandleReply(std::move(reply));
    }));
}

void
RPCSend::doHandleReply(Reply::UP reply) {
    ReplyContext::UP ctx(static_cast<ReplyContext*>(reply->getContext().value.PTR));
    FRT_RPCRequest &req = ctx->getRequest();
    string version = ctx->getVersion().toString();
    if (reply->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        reply->getTrace().trace(TraceLevel::SEND_RECEIVE, make_string("Sending reply (version %s) from %s.",
                                                                      version.c_str(), _serverIdent.c_str()));
    }
    Blob payload(0);
    if (reply->getType() != 0) {
        payload = _net->getOwner().getProtocol(reply->getProtocol())->encode(ctx->getVersion(), *reply);
        if (payload.size() == 0) {
            reply->addError(Error(ErrorCode::ENCODE_ERROR, "An error occured while encoding the reply, see log."));
        }
    }
    FRT_Values &ret = *req.GetReturn();
    createResponse(ret, version, *reply, std::move(payload));
    req.Return();
}

void
RPCSend::invoke(FRT_RPCRequest *req)
{
    req->Detach();
    auto rejected = _net->getExecutor().execute(makeLambdaTask([this, req]() { doRequest(req);}));
    assert(!rejected);
}

void
RPCSend::doRequest(FRT_RPCRequest *req)
{
    FRT_Values &args = *req->GetParams();

    std::unique_ptr<Params> params = toParams(args);
    IProtocol * protocol = _net->getOwner().getProtocol(params->getProtocol());
    if (protocol == nullptr) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::UNKNOWN_PROTOCOL,
                         make_string("Protocol '%s' is not known by %s.", params->getProtocol().c_str(), _serverIdent.c_str())));
        return;
    }
    Routable::UP routable = protocol->decode(params->getVersion(), params->getPayload());
    req->DiscardBlobs();
    if ( ! routable ) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::DECODE_ERROR,
                         make_string("Protocol '%s' failed to decode routable.", params->getProtocol().c_str())));
        return;
    }
    if (routable->isReply()) {
        replyError(req, params->getVersion(), params->getTraceLevel(),
                   Error(ErrorCode::DECODE_ERROR, "Payload decoded to a reply when expecting a mesage."));
        return;
    }
    Message::UP msg(static_cast<Message*>(routable.release()));
    vespalib::stringref route = params->getRoute();
    if (!route.empty()) {
        msg->setRoute(Route::parse(route));
    }
    msg->setContext(Context(new ReplyContext(*req, params->getVersion())));
    msg->pushHandler(*this, *this);
    msg->setRetryEnabled(params->useRetry());
    msg->setRetry(params->getRetries());
    msg->setTimeReceivedNow();
    msg->setTimeRemaining(params->getRemainingTime());
    msg->getTrace().setLevel(params->getTraceLevel());
    if (msg->getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
        msg->getTrace().trace(TraceLevel::SEND_RECEIVE,
                              make_string("Message (type %d) received at %s for session '%s'.",
                                          msg->getType(), _serverIdent.c_str(), string(params->getSession()).c_str()));
    }
    _net->getOwner().deliverMessage(std::move(msg), params->getSession());
}

} // namespace mbus
<|endoftext|>


<commit_before>/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "feedback.h"

/* MySQL functions/variables not declared in mysql_priv.h */
int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_status(THD *thd, TABLE_LIST *tables, COND *cond);
extern ST_SCHEMA_TABLE schema_tables[];

namespace feedback {

char server_uid_buf[SERVER_UID_SIZE+1]; ///< server uid will be written here

/* backing store for system variables */
static char *server_uid= server_uid_buf, *url;
char *user_info;
ulong send_timeout, send_retry_wait;

/**
  these three are used to communicate the shutdown signal to the
  background thread
*/
pthread_mutex_t sleep_mutex;
pthread_cond_t sleep_condition;
volatile bool shutdown_plugin;
static pthread_t sender_thread;

Url **urls;             ///< list of urls to send the report to
uint url_count;

ST_SCHEMA_TABLE *i_s_feedback; ///< table descriptor for our I_S table

/*
  the column names *must* match column names in GLOBAL_VARIABLES and
  GLOBAL_STATUS tables otherwise condition pushdown below will not work
*/
static ST_FIELD_INFO feedback_fields[] =
{
  {"VARIABLE_NAME",   255, MYSQL_TYPE_STRING, 0, 0, 0, 0},
  {"VARIABLE_VALUE", 1024, MYSQL_TYPE_STRING, 0, 0, 0, 0},
  {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
};

static COND * const OOM= (COND*)1;

/**
  Generate the COND tree for the condition pushdown

  This function takes a list of strings and generates an Item tree
  corresponding to the following expression:

    field LIKE str1 OR field LIKE str2 OR field LIKE str3 OR ...

  where 'field' is the first field in the table - VARIABLE_NAME field -
  and str1, str2... are strings from the list.

  This condition is used to filter the selected rows, emulating

    SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE ...
*/
static COND* make_cond(THD *thd, TABLE_LIST *tables, LEX_STRING *filter)
{
  Item_cond_or *res= NULL;
  Name_resolution_context nrc;
  const char *db= tables->db, *table= tables->alias,
             *field= tables->table->field[0]->field_name;
  CHARSET_INFO *cs= &my_charset_latin1;

  if (!filter->str)
    return 0;

  nrc.init();
  nrc.resolve_in_table_list_only(tables);

  res= new Item_cond_or();
  if (!res)
    return OOM;

  for (; filter->str; filter++)
  {
    Item_field  *fld= new Item_field(&nrc, db, table, field);
    Item_string *pattern= new Item_string(filter->str, filter->length, cs);
    Item_string *escape= new Item_string("\\", 1, cs);

    if (!fld || !pattern || !escape)
      return OOM;

    Item_func_like *like= new Item_func_like(fld, pattern, escape, 0);

    if (!like)
      return OOM;

    res->add(like);
  }

  if (res->fix_fields(thd, (Item**)&res))
    return OOM;

  return res;
}

/**
  System variables that we want to see in the feedback report
*/
static LEX_STRING vars_filter[]= {
  {C_STRING_WITH_LEN("auto\\_increment%")},
  {C_STRING_WITH_LEN("binlog\\_format")},
  {C_STRING_WITH_LEN("character\\_set\\_%")},
  {C_STRING_WITH_LEN("collation%")},
  {C_STRING_WITH_LEN("engine\\_condition\\_pushdown")},
  {C_STRING_WITH_LEN("event\\_scheduler")},
  {C_STRING_WITH_LEN("feedback\\_%")},
  {C_STRING_WITH_LEN("ft\\_m%")},
  {C_STRING_WITH_LEN("have\\_%")},
  {C_STRING_WITH_LEN("%\\_size")},
  {C_STRING_WITH_LEN("%\\_length%")},
  {C_STRING_WITH_LEN("%\\_timeout")},
  {C_STRING_WITH_LEN("large\\_%")},
  {C_STRING_WITH_LEN("lc_time_names")},
  {C_STRING_WITH_LEN("log")},
  {C_STRING_WITH_LEN("log_bin")},
  {C_STRING_WITH_LEN("log_output")},
  {C_STRING_WITH_LEN("log_slow_queries")},
  {C_STRING_WITH_LEN("log_slow_time")},
  {C_STRING_WITH_LEN("lower_case%")},
  {C_STRING_WITH_LEN("max_allowed_packet")},
  {C_STRING_WITH_LEN("max_connections")},
  {C_STRING_WITH_LEN("max_prepared_stmt_count")},
  {C_STRING_WITH_LEN("max_sp_recursion_depth")},
  {C_STRING_WITH_LEN("max_user_connections")},
  {C_STRING_WITH_LEN("max_write_lock_count")},
  {C_STRING_WITH_LEN("myisam_recover_options")},
  {C_STRING_WITH_LEN("myisam_repair_threads")},
  {C_STRING_WITH_LEN("myisam_stats_method")},
  {C_STRING_WITH_LEN("myisam_use_mmap")},
  {C_STRING_WITH_LEN("net\\_%")},
  {C_STRING_WITH_LEN("new")},
  {C_STRING_WITH_LEN("old%")},
  {C_STRING_WITH_LEN("optimizer%")},
  {C_STRING_WITH_LEN("profiling")},
  {C_STRING_WITH_LEN("query_cache%")},
  {C_STRING_WITH_LEN("secure_auth")},
  {C_STRING_WITH_LEN("slow_launch_time")},
  {C_STRING_WITH_LEN("sql%")},
  {C_STRING_WITH_LEN("storage_engine")},
  {C_STRING_WITH_LEN("sync_binlog")},
  {C_STRING_WITH_LEN("table_definition_cache")},
  {C_STRING_WITH_LEN("table_open_cache")},
  {C_STRING_WITH_LEN("thread_handling")},
  {C_STRING_WITH_LEN("time_zone")},
  {C_STRING_WITH_LEN("timed_mutexes")},
  {C_STRING_WITH_LEN("version%")},
  {0, 0}
};

/**
  Status variables that we want to see in the feedback report

  (empty list = no WHERE condition)
*/
static LEX_STRING status_filter[]= {{0, 0}};

/**
  Fill our I_S table with data

  This function works by invoking fill_variables() and
  fill_status() of the corresponding I_S tables - to have
  their data UNION-ed in the same target table.
  After that it invokes our own fill_* functions
  from the utils.cc - to get the data that aren't available in the
  I_S.GLOBAL_VARIABLES and I_S.GLOBAL_STATUS.
*/
int fill_feedback(THD *thd, TABLE_LIST *tables, COND *unused)
{
  int res;
  COND *cond;

  tables->schema_table= schema_tables + SCH_GLOBAL_VARIABLES;
  cond= make_cond(thd, tables, vars_filter);
  res= (cond == OOM) ? 1 : fill_variables(thd, tables, cond);

  tables->schema_table= schema_tables + SCH_GLOBAL_STATUS;
  if (!res)
  {
    cond= make_cond(thd, tables, status_filter);
    res= (cond == OOM) ? 1 : fill_status(thd, tables, cond);
  }

  tables->schema_table= i_s_feedback;
  res= res || fill_plugin_version(thd, tables)
           || fill_misc_data(thd, tables)
           || fill_linux_info(thd, tables);

  return res;
}

/**
   plugin initialization function
*/
static int init(void *p)
{
  i_s_feedback= (ST_SCHEMA_TABLE*) p;
  /* initialize the I_S descriptor structure */
  i_s_feedback->fields_info= feedback_fields; ///< field descriptor
  i_s_feedback->fill_table= fill_feedback;    ///< how to fill the I_S table
  i_s_feedback->idx_field1 = 0;               ///< virtual index on the 1st col

  if (calculate_server_uid(server_uid_buf))
    return 1;

  prepare_linux_info();

  url_count= 0;
  if (*url)
  {
    // now we split url on spaces and store them in Url objects
    int slot;
    char *s, *e;

    for (s= url, url_count= 1; *s; s++)
      if (*s == ' ')
        url_count++;

    urls= (Url **)my_malloc(url_count*sizeof(Url*), MYF(MY_WME));
    if (!urls)
      return 1;

    for (s= url, e = url+1, slot= 0; e[-1]; e++)
      if (*e == 0 || *e == ' ')
      {
        if (e > s && (urls[slot]= Url::create(s, e - s)))
          slot++;
        else
        {
          if (e > s)
            sql_print_error("feedback plugin: invalid url '%.*s'", (int)(e-s), s);
          url_count--;
        }
        s= e + 1;
      }

    // create a background thread to handle urls, if any
    if (url_count)
    {
      pthread_mutex_init(&sleep_mutex, 0);
      pthread_cond_init(&sleep_condition, 0);
      shutdown_plugin= false;

      pthread_attr_t attr;
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
      if (pthread_create(&sender_thread, &attr, background_thread, 0) != 0)
      {
        sql_print_error("feedback plugin: failed to start a background thread");
        return 1;
      }
    }
    else
      my_free(urls, MYF(0));
  }

  return 0;
}

/**
   plugin deinitialization function
*/
static int free(void *p)
{
  if (url_count)
  {
    pthread_mutex_lock(&sleep_mutex);
    shutdown_plugin= true;
    pthread_cond_signal(&sleep_condition);
    pthread_mutex_unlock(&sleep_mutex);
    pthread_join(sender_thread, NULL);

    pthread_mutex_destroy(&sleep_mutex);
    pthread_cond_destroy(&sleep_condition);

    for (uint i= 0; i < url_count; i++)
      delete urls[i];
    my_free(urls, MYF(0));
  }
  return 0;
}

#ifdef HAVE_OPENSSL
#define DEFAULT_PROTO "https://"
#else
#define DEFAULT_PROTO "http://"
#endif

static MYSQL_SYSVAR_STR(server_uid, server_uid,
       PLUGIN_VAR_READONLY | PLUGIN_VAR_NOCMDOPT,
       "Automatically calculated server unique id hash.", NULL, NULL, 0);
static MYSQL_SYSVAR_STR(user_info, user_info,
       PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
       "User specified string that will be included in the feedback report.",
       NULL, NULL, "");
static MYSQL_SYSVAR_STR(url, url, PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
       "Space separated URLs to send the feedback report to.", NULL, NULL,
       DEFAULT_PROTO "mariadb.org/feedback_plugin/post");
static MYSQL_SYSVAR_ULONG(send_timeout, send_timeout, PLUGIN_VAR_RQCMDARG,
       "Timeout (in seconds) for the sending the report.",
       NULL, NULL, 60, 1, 60*60*24, 1);
static MYSQL_SYSVAR_ULONG(send_retry_wait, send_retry_wait, PLUGIN_VAR_RQCMDARG,
       "Wait this many seconds before retrying a failed send.",
       NULL, NULL, 60, 1, 60*60*24, 1);

static struct st_mysql_sys_var* settings[] = {
  MYSQL_SYSVAR(server_uid),
  MYSQL_SYSVAR(user_info),
  MYSQL_SYSVAR(url),
  MYSQL_SYSVAR(send_timeout),
  MYSQL_SYSVAR(send_retry_wait),
  NULL
};

static struct st_mysql_information_schema feedback =
{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };

} // namespace feedback

mysql_declare_plugin(feedback)
{
  MYSQL_INFORMATION_SCHEMA_PLUGIN,
  &feedback::feedback,
  "FEEDBACK",
  "Sergei Golubchik",
  "MariaDB User Feedback Plugin",
  PLUGIN_LICENSE_GPL,
  feedback::init,
  feedback::free,
  0x0100,
  NULL,
  feedback::settings,
  NULL
}
mysql_declare_plugin_end;

<commit_msg>fix feedback plugin for 5.2<commit_after>/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "feedback.h"

/* MySQL functions/variables not declared in mysql_priv.h */
int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_status(THD *thd, TABLE_LIST *tables, COND *cond);
extern ST_SCHEMA_TABLE schema_tables[];

namespace feedback {

char server_uid_buf[SERVER_UID_SIZE+1]; ///< server uid will be written here

/* backing store for system variables */
static char *server_uid= server_uid_buf, *url;
char *user_info;
ulong send_timeout, send_retry_wait;

/**
  these three are used to communicate the shutdown signal to the
  background thread
*/
pthread_mutex_t sleep_mutex;
pthread_cond_t sleep_condition;
volatile bool shutdown_plugin;
static pthread_t sender_thread;

Url **urls;             ///< list of urls to send the report to
uint url_count;

ST_SCHEMA_TABLE *i_s_feedback; ///< table descriptor for our I_S table

/*
  the column names *must* match column names in GLOBAL_VARIABLES and
  GLOBAL_STATUS tables otherwise condition pushdown below will not work
*/
static ST_FIELD_INFO feedback_fields[] =
{
  {"VARIABLE_NAME",   255, MYSQL_TYPE_STRING, 0, 0, 0, 0},
  {"VARIABLE_VALUE", 1024, MYSQL_TYPE_STRING, 0, 0, 0, 0},
  {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
};

static COND * const OOM= (COND*)1;

/**
  Generate the COND tree for the condition pushdown

  This function takes a list of strings and generates an Item tree
  corresponding to the following expression:

    field LIKE str1 OR field LIKE str2 OR field LIKE str3 OR ...

  where 'field' is the first field in the table - VARIABLE_NAME field -
  and str1, str2... are strings from the list.

  This condition is used to filter the selected rows, emulating

    SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE ...
*/
static COND* make_cond(THD *thd, TABLE_LIST *tables, LEX_STRING *filter)
{
  Item_cond_or *res= NULL;
  Name_resolution_context nrc;
  const char *db= tables->db, *table= tables->alias,
             *field= tables->table->field[0]->field_name;
  CHARSET_INFO *cs= &my_charset_latin1;

  if (!filter->str)
    return 0;

  nrc.init();
  nrc.resolve_in_table_list_only(tables);

  res= new Item_cond_or();
  if (!res)
    return OOM;

  for (; filter->str; filter++)
  {
    Item_field  *fld= new Item_field(&nrc, db, table, field);
    Item_string *pattern= new Item_string(filter->str, filter->length, cs);
    Item_string *escape= new Item_string("\\", 1, cs);

    if (!fld || !pattern || !escape)
      return OOM;

    Item_func_like *like= new Item_func_like(fld, pattern, escape, 0);

    if (!like)
      return OOM;

    res->add(like);
  }

  if (res->fix_fields(thd, (Item**)&res))
    return OOM;

  return res;
}

/**
  System variables that we want to see in the feedback report
*/
static LEX_STRING vars_filter[]= {
  {C_STRING_WITH_LEN("auto\\_increment%")},
  {C_STRING_WITH_LEN("binlog\\_format")},
  {C_STRING_WITH_LEN("character\\_set\\_%")},
  {C_STRING_WITH_LEN("collation%")},
  {C_STRING_WITH_LEN("engine\\_condition\\_pushdown")},
  {C_STRING_WITH_LEN("event\\_scheduler")},
  {C_STRING_WITH_LEN("feedback\\_%")},
  {C_STRING_WITH_LEN("ft\\_m%")},
  {C_STRING_WITH_LEN("have\\_%")},
  {C_STRING_WITH_LEN("%\\_size")},
  {C_STRING_WITH_LEN("%\\_length%")},
  {C_STRING_WITH_LEN("%\\_timeout")},
  {C_STRING_WITH_LEN("large\\_%")},
  {C_STRING_WITH_LEN("lc_time_names")},
  {C_STRING_WITH_LEN("log")},
  {C_STRING_WITH_LEN("log_bin")},
  {C_STRING_WITH_LEN("log_output")},
  {C_STRING_WITH_LEN("log_slow_queries")},
  {C_STRING_WITH_LEN("log_slow_time")},
  {C_STRING_WITH_LEN("lower_case%")},
  {C_STRING_WITH_LEN("max_allowed_packet")},
  {C_STRING_WITH_LEN("max_connections")},
  {C_STRING_WITH_LEN("max_prepared_stmt_count")},
  {C_STRING_WITH_LEN("max_sp_recursion_depth")},
  {C_STRING_WITH_LEN("max_user_connections")},
  {C_STRING_WITH_LEN("max_write_lock_count")},
  {C_STRING_WITH_LEN("myisam_recover_options")},
  {C_STRING_WITH_LEN("myisam_repair_threads")},
  {C_STRING_WITH_LEN("myisam_stats_method")},
  {C_STRING_WITH_LEN("myisam_use_mmap")},
  {C_STRING_WITH_LEN("net\\_%")},
  {C_STRING_WITH_LEN("new")},
  {C_STRING_WITH_LEN("old%")},
  {C_STRING_WITH_LEN("optimizer%")},
  {C_STRING_WITH_LEN("profiling")},
  {C_STRING_WITH_LEN("query_cache%")},
  {C_STRING_WITH_LEN("secure_auth")},
  {C_STRING_WITH_LEN("slow_launch_time")},
  {C_STRING_WITH_LEN("sql%")},
  {C_STRING_WITH_LEN("storage_engine")},
  {C_STRING_WITH_LEN("sync_binlog")},
  {C_STRING_WITH_LEN("table_definition_cache")},
  {C_STRING_WITH_LEN("table_open_cache")},
  {C_STRING_WITH_LEN("thread_handling")},
  {C_STRING_WITH_LEN("time_zone")},
  {C_STRING_WITH_LEN("timed_mutexes")},
  {C_STRING_WITH_LEN("version%")},
  {0, 0}
};

/**
  Status variables that we want to see in the feedback report

  (empty list = no WHERE condition)
*/
static LEX_STRING status_filter[]= {{0, 0}};

/**
  Fill our I_S table with data

  This function works by invoking fill_variables() and
  fill_status() of the corresponding I_S tables - to have
  their data UNION-ed in the same target table.
  After that it invokes our own fill_* functions
  from the utils.cc - to get the data that aren't available in the
  I_S.GLOBAL_VARIABLES and I_S.GLOBAL_STATUS.
*/
int fill_feedback(THD *thd, TABLE_LIST *tables, COND *unused)
{
  int res;
  COND *cond;

  tables->schema_table= schema_tables + SCH_GLOBAL_VARIABLES;
  cond= make_cond(thd, tables, vars_filter);
  res= (cond == OOM) ? 1 : fill_variables(thd, tables, cond);

  tables->schema_table= schema_tables + SCH_GLOBAL_STATUS;
  if (!res)
  {
    cond= make_cond(thd, tables, status_filter);
    res= (cond == OOM) ? 1 : fill_status(thd, tables, cond);
  }

  tables->schema_table= i_s_feedback;
  res= res || fill_plugin_version(thd, tables)
           || fill_misc_data(thd, tables)
           || fill_linux_info(thd, tables);

  return res;
}

/**
   plugin initialization function
*/
static int init(void *p)
{
  i_s_feedback= (ST_SCHEMA_TABLE*) p;
  /* initialize the I_S descriptor structure */
  i_s_feedback->fields_info= feedback_fields; ///< field descriptor
  i_s_feedback->fill_table= fill_feedback;    ///< how to fill the I_S table
  i_s_feedback->idx_field1 = 0;               ///< virtual index on the 1st col

  if (calculate_server_uid(server_uid_buf))
    return 1;

  prepare_linux_info();

  url_count= 0;
  if (*url)
  {
    // now we split url on spaces and store them in Url objects
    int slot;
    char *s, *e;

    for (s= url, url_count= 1; *s; s++)
      if (*s == ' ')
        url_count++;

    urls= (Url **)my_malloc(url_count*sizeof(Url*), MYF(MY_WME));
    if (!urls)
      return 1;

    for (s= url, e = url+1, slot= 0; e[-1]; e++)
      if (*e == 0 || *e == ' ')
      {
        if (e > s && (urls[slot]= Url::create(s, e - s)))
          slot++;
        else
        {
          if (e > s)
            sql_print_error("feedback plugin: invalid url '%.*s'", (int)(e-s), s);
          url_count--;
        }
        s= e + 1;
      }

    // create a background thread to handle urls, if any
    if (url_count)
    {
      pthread_mutex_init(&sleep_mutex, 0);
      pthread_cond_init(&sleep_condition, 0);
      shutdown_plugin= false;

      pthread_attr_t attr;
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
      if (pthread_create(&sender_thread, &attr, background_thread, 0) != 0)
      {
        sql_print_error("feedback plugin: failed to start a background thread");
        return 1;
      }
    }
    else
      my_free(urls, MYF(0));
  }

  return 0;
}

/**
   plugin deinitialization function
*/
static int free(void *p)
{
  if (url_count)
  {
    pthread_mutex_lock(&sleep_mutex);
    shutdown_plugin= true;
    pthread_cond_signal(&sleep_condition);
    pthread_mutex_unlock(&sleep_mutex);
    pthread_join(sender_thread, NULL);

    pthread_mutex_destroy(&sleep_mutex);
    pthread_cond_destroy(&sleep_condition);

    for (uint i= 0; i < url_count; i++)
      delete urls[i];
    my_free(urls, MYF(0));
  }
  return 0;
}

#ifdef HAVE_OPENSSL
#define DEFAULT_PROTO "https://"
#else
#define DEFAULT_PROTO "http://"
#endif

static MYSQL_SYSVAR_STR(server_uid, server_uid,
       PLUGIN_VAR_READONLY | PLUGIN_VAR_NOCMDOPT,
       "Automatically calculated server unique id hash.", NULL, NULL, 0);
static MYSQL_SYSVAR_STR(user_info, user_info,
       PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
       "User specified string that will be included in the feedback report.",
       NULL, NULL, "");
static MYSQL_SYSVAR_STR(url, url, PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
       "Space separated URLs to send the feedback report to.", NULL, NULL,
       DEFAULT_PROTO "mariadb.org/feedback_plugin/post");
static MYSQL_SYSVAR_ULONG(send_timeout, send_timeout, PLUGIN_VAR_RQCMDARG,
       "Timeout (in seconds) for the sending the report.",
       NULL, NULL, 60, 1, 60*60*24, 1);
static MYSQL_SYSVAR_ULONG(send_retry_wait, send_retry_wait, PLUGIN_VAR_RQCMDARG,
       "Wait this many seconds before retrying a failed send.",
       NULL, NULL, 60, 1, 60*60*24, 1);

static struct st_mysql_sys_var* settings[] = {
  MYSQL_SYSVAR(server_uid),
  MYSQL_SYSVAR(user_info),
  MYSQL_SYSVAR(url),
  MYSQL_SYSVAR(send_timeout),
  MYSQL_SYSVAR(send_retry_wait),
  NULL
};

static struct st_mysql_information_schema feedback =
{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };

} // namespace feedback

mysql_declare_plugin(feedback)
{
  MYSQL_INFORMATION_SCHEMA_PLUGIN,
  &feedback::feedback,
  "FEEDBACK",
  "Sergei Golubchik",
  "MariaDB User Feedback Plugin",
  PLUGIN_LICENSE_GPL,
  feedback::init,
  feedback::free,
  0x0100,
  NULL,
  feedback::settings,
  NULL
}
mysql_declare_plugin_end;
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
maria_declare_plugin(feedback)
{
  MYSQL_INFORMATION_SCHEMA_PLUGIN,
  &feedback::feedback,
  "FEEDBACK",
  "Sergei Golubchik",
  "MariaDB User Feedback Plugin",
  PLUGIN_LICENSE_GPL,
  feedback::init,
  feedback::free,
  0x0100,
  NULL,
  feedback::settings,
  "1.0",
  MariaDB_PLUGIN_MATURITY_BETA
}
mysql_declare_plugin_end;
#endif
<|endoftext|>


<commit_before>// This file was automatically generated by Nazara

/*
	Nazara Engine - Core module

	Copyright (C) 2012 Jrme "Lynix" Leclercq (Lynix680@gmail.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
	of the Software, and to permit persons to whom the Software is furnished to do
	so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

#pragma once

#include <Nazara/Core/ByteArray.hpp>
#include <Nazara/Core/Clock.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Core/ConditionVariable.hpp>
#include <Nazara/Core/Config.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/Core/Directory.hpp>
#include <Nazara/Core/DynLib.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/Format.hpp>
#include <Nazara/Core/Functor.hpp>
#include <Nazara/Core/Hash.hpp>
#include <Nazara/Core/Hashable.hpp>
#include <Nazara/Core/HashDigest.hpp>
#include <Nazara/Core/HashImpl.hpp>
#include <Nazara/Core/Initializer.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Core/LockGuard.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/MemoryStream.hpp>
#include <Nazara/Core/Mutex.hpp>
#include <Nazara/Core/NonCopyable.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceListener.hpp>
#include <Nazara/Core/ResourceLoader.hpp>
#include <Nazara/Core/Semaphore.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Core/Thread.hpp>
#include <Nazara/Core/Tuple.hpp>
#include <Nazara/Core/Unicode.hpp>
<commit_msg>Added Stream include to Core global include<commit_after>// This file was automatically generated by Nazara

/*
	Nazara Engine - Core module

	Copyright (C) 2012 Jrme "Lynix" Leclercq (Lynix680@gmail.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
	of the Software, and to permit persons to whom the Software is furnished to do
	so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

#pragma once

#include <Nazara/Core/ByteArray.hpp>
#include <Nazara/Core/Clock.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Core/ConditionVariable.hpp>
#include <Nazara/Core/Config.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/Core/Directory.hpp>
#include <Nazara/Core/DynLib.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/Format.hpp>
#include <Nazara/Core/Functor.hpp>
#include <Nazara/Core/Hash.hpp>
#include <Nazara/Core/Hashable.hpp>
#include <Nazara/Core/HashDigest.hpp>
#include <Nazara/Core/HashImpl.hpp>
#include <Nazara/Core/Initializer.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Core/LockGuard.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/MemoryStream.hpp>
#include <Nazara/Core/Mutex.hpp>
#include <Nazara/Core/NonCopyable.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceListener.hpp>
#include <Nazara/Core/ResourceLoader.hpp>
#include <Nazara/Core/Semaphore.hpp>
#include <Nazara/Core/Stream.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Core/Thread.hpp>
#include <Nazara/Core/Tuple.hpp>
#include <Nazara/Core/Unicode.hpp>
<|endoftext|>


<commit_before>#ifndef RPTUI_GROUPS_SORTING_HXX
#define RPTUI_GROUPS_SORTING_HXX
/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: GroupsSorting.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2007-07-09 11:56:30 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_FLOATWIN_HXX
#include <vcl/floatwin.hxx>
#endif
#ifndef _FIXED_HXX //autogen
#include <vcl/fixed.hxx>
#endif
#ifndef _SV_LSTBOX_HXX
#include <vcl/lstbox.hxx>
#endif
#ifndef _SV_EDIT_HXX
#include <vcl/edit.hxx>
#endif
#ifndef _SV_FIELD_HXX
#include <vcl/field.hxx>
#endif
#ifndef _SV_BUTTON_HXX
#include <vcl/imagebtn.hxx>
#endif
#ifndef _COM_SUN_STAR_REPORT_XGROUPS_HPP_
#include <com/sun/star/report/XGroups.hpp>
#endif
#ifndef _COM_SUN_STAR_REPORT_XGROUP_HPP_
#include <com/sun/star/report/XGroup.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef RPT_SHARED_GROUPS_PROPERTIES_HXX
#include "GroupProperties.hxx"
#endif
#ifndef _COMPHELPER_PROPERTY_MULTIPLEX_HXX_
#include <comphelper/propmultiplex.hxx>
#endif
#ifndef _CPPUHELPER_BASEMUTEX_HXX_
#include "cppuhelper/basemutex.hxx"
#endif
#ifndef _SVEDIT_HXX
#include <svtools/svmedit.hxx>
#endif
#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif

#include <vector>

namespace comphelper
{
    class OPropertyChangeMultiplexer;
}
namespace rptui
{
class OFieldExpressionControl;
class OReportController;
/*************************************************************************
|*
|* Groups and Sorting dialog
|*
\************************************************************************/
class OGroupsSortingDialog :    public FloatingWindow
                            ,   public ::cppu::BaseMutex
                            ,   public ::comphelper::OPropertyChangeListener
{
    friend class OFieldExpressionControl;

    FixedLine                               m_aFL2;
    FixedText                               m_aMove;
    ImageButton                             m_aUp;
    ImageButton                             m_aDown;
    FixedLine                               m_aFL3;
    FixedText                               m_aOrder;
    ListBox                                 m_aOrderLst;
    FixedText                               m_aHeader;
    ListBox                                 m_aHeaderLst;
    FixedText                               m_aFooter;
    ListBox                                 m_aFooterLst;
    FixedText                               m_aGroupOn;
    ListBox                                 m_aGroupOnLst;
    FixedText                               m_aGroupInterval;
    NumericField                            m_aGroupIntervalEd;
    FixedText                               m_aKeepTogether;
    ListBox                                 m_aKeepTogetherLst;
    FixedLine                               m_aFL;
    FixedText                               m_aHelpWindow;

    OFieldExpressionControl*                m_pFieldExpression;
    ::rptui::OReportController*             m_pController;
    ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>                       m_pCurrentGroupListener;
    ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>                       m_pReportListener;
    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroups>            m_xGroups;
    ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >    m_xColumns;
    ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent >          m_xHoldAlive;
    sal_Bool                                m_bReadOnly;
private:
    DECL_LINK( OnControlFocusLost, Control* );
    DECL_LINK( OnControlFocusGot, Control* );
    DECL_LINK( LBChangeHdl, ListBox* );
    DECL_LINK( ClickHdl, ImageButton* );

    /** returns the groups
        @return the groups which now have to check which one changes
    */
    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroups>& getGroups() { return m_xGroups; }

    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup> getGroup(sal_Int32 _nPos)
    {
        OSL_ENSURE(_nPos >= 0 && _nPos < m_xGroups->getCount(),"Invalid count!");
        return ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup>(m_xGroups->getByIndex(_nPos),::com::sun::star::uno::UNO_QUERY);
    }

    /** updates the listboxes with the new group properties
        @param  _nRow   the new group pos
    */
    void DisplayData( sal_Int32 _nRow );

    /** saves the values from the listboxes into the group at position _nRow
        @param  _nRow   the group pos to store in
    */
    void SaveData( sal_Int32 _nRow );

    /** returns <TRUE/> when the dialog should be read only
    */
    sal_Bool isReadOnly( ) const;

    /** returns the data type for the given column name
        @param _sColumnName
    */
    sal_Int32 getColumnDataType(const ::rtl::OUString& _sColumnName);

    /** shows the text given by the id in the multiline edit
        @param  _nResId the string id
    */
    void showHelpText(USHORT _nResId);
    /** display the group props
        @param  _xGroup the group to display
    */
    void displayGroup(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup>& _xGroup);

    /** enables or diables the up and down button
        @param  _nRow   the row which will be active
    */
    void checkButtons(sal_Int32 _nRow);

    /** clears the m_xColumns member and reset the fields
    *
    */
    void fillColumns();
    OGroupsSortingDialog(OGroupsSortingDialog&);
    void operator =(OGroupsSortingDialog&);
protected:
    // window
    virtual void    Resize();
    // OPropertyChangeListener
    virtual void    _propertyChanged(const ::com::sun::star::beans::PropertyChangeEvent& _rEvent) throw( ::com::sun::star::uno::RuntimeException);
public:
    OGroupsSortingDialog( Window* pParent
                        ,sal_Bool _bReadOnly
                        ,::rptui::OReportController* _pController);
    virtual ~OGroupsSortingDialog();

    /** sets the newe columns at the groups dialog.
        @param  _xColumns the new columns
    */
    void setColumns(const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _xColumns);

    /* updates the current view
    */
    void UpdateData( );
};
// =============================================================================
} // namespace rptui
// =============================================================================
#endif // RPTUI_GROUPS_SORTING_HXX
<commit_msg>INTEGRATION: CWS changefileheader (1.2.104); FILE MERGED 2008/04/01 15:23:41 thb 1.2.104.3: #i85898# Stripping all external header guards 2008/04/01 12:33:13 thb 1.2.104.2: #i85898# Stripping all external header guards 2008/03/31 13:32:17 rt 1.2.104.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: GroupsSorting.hxx,v $
 * $Revision: 1.3 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef RPTUI_GROUPS_SORTING_HXX
#define RPTUI_GROUPS_SORTING_HXX

#include <vcl/floatwin.hxx>
#ifndef _FIXED_HXX //autogen
#include <vcl/fixed.hxx>
#endif
#include <vcl/lstbox.hxx>
#include <vcl/edit.hxx>
#include <vcl/field.hxx>
#include <vcl/imagebtn.hxx>
#include <com/sun/star/report/XGroups.hpp>
#include <com/sun/star/report/XGroup.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include "GroupProperties.hxx"
#include <comphelper/propmultiplex.hxx>
#include "cppuhelper/basemutex.hxx"
#include <svtools/svmedit.hxx>
#include <rtl/ref.hxx>

#include <vector>

namespace comphelper
{
    class OPropertyChangeMultiplexer;
}
namespace rptui
{
class OFieldExpressionControl;
class OReportController;
/*************************************************************************
|*
|* Groups and Sorting dialog
|*
\************************************************************************/
class OGroupsSortingDialog :    public FloatingWindow
                            ,   public ::cppu::BaseMutex
                            ,   public ::comphelper::OPropertyChangeListener
{
    friend class OFieldExpressionControl;

    FixedLine                               m_aFL2;
    FixedText                               m_aMove;
    ImageButton                             m_aUp;
    ImageButton                             m_aDown;
    FixedLine                               m_aFL3;
    FixedText                               m_aOrder;
    ListBox                                 m_aOrderLst;
    FixedText                               m_aHeader;
    ListBox                                 m_aHeaderLst;
    FixedText                               m_aFooter;
    ListBox                                 m_aFooterLst;
    FixedText                               m_aGroupOn;
    ListBox                                 m_aGroupOnLst;
    FixedText                               m_aGroupInterval;
    NumericField                            m_aGroupIntervalEd;
    FixedText                               m_aKeepTogether;
    ListBox                                 m_aKeepTogetherLst;
    FixedLine                               m_aFL;
    FixedText                               m_aHelpWindow;

    OFieldExpressionControl*                m_pFieldExpression;
    ::rptui::OReportController*             m_pController;
    ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>                       m_pCurrentGroupListener;
    ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>                       m_pReportListener;
    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroups>            m_xGroups;
    ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >    m_xColumns;
    ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent >          m_xHoldAlive;
    sal_Bool                                m_bReadOnly;
private:
    DECL_LINK( OnControlFocusLost, Control* );
    DECL_LINK( OnControlFocusGot, Control* );
    DECL_LINK( LBChangeHdl, ListBox* );
    DECL_LINK( ClickHdl, ImageButton* );

    /** returns the groups
        @return the groups which now have to check which one changes
    */
    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroups>& getGroups() { return m_xGroups; }

    ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup> getGroup(sal_Int32 _nPos)
    {
        OSL_ENSURE(_nPos >= 0 && _nPos < m_xGroups->getCount(),"Invalid count!");
        return ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup>(m_xGroups->getByIndex(_nPos),::com::sun::star::uno::UNO_QUERY);
    }

    /** updates the listboxes with the new group properties
        @param  _nRow   the new group pos
    */
    void DisplayData( sal_Int32 _nRow );

    /** saves the values from the listboxes into the group at position _nRow
        @param  _nRow   the group pos to store in
    */
    void SaveData( sal_Int32 _nRow );

    /** returns <TRUE/> when the dialog should be read only
    */
    sal_Bool isReadOnly( ) const;

    /** returns the data type for the given column name
        @param _sColumnName
    */
    sal_Int32 getColumnDataType(const ::rtl::OUString& _sColumnName);

    /** shows the text given by the id in the multiline edit
        @param  _nResId the string id
    */
    void showHelpText(USHORT _nResId);
    /** display the group props
        @param  _xGroup the group to display
    */
    void displayGroup(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XGroup>& _xGroup);

    /** enables or diables the up and down button
        @param  _nRow   the row which will be active
    */
    void checkButtons(sal_Int32 _nRow);

    /** clears the m_xColumns member and reset the fields
    *
    */
    void fillColumns();
    OGroupsSortingDialog(OGroupsSortingDialog&);
    void operator =(OGroupsSortingDialog&);
protected:
    // window
    virtual void    Resize();
    // OPropertyChangeListener
    virtual void    _propertyChanged(const ::com::sun::star::beans::PropertyChangeEvent& _rEvent) throw( ::com::sun::star::uno::RuntimeException);
public:
    OGroupsSortingDialog( Window* pParent
                        ,sal_Bool _bReadOnly
                        ,::rptui::OReportController* _pController);
    virtual ~OGroupsSortingDialog();

    /** sets the newe columns at the groups dialog.
        @param  _xColumns the new columns
    */
    void setColumns(const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _xColumns);

    /* updates the current view
    */
    void UpdateData( );
};
// =============================================================================
} // namespace rptui
// =============================================================================
#endif // RPTUI_GROUPS_SORTING_HXX
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
//
// Kroniax2-Mapeditor
// Copyright (C) 2015 Alexander Weinrauch (alexander.weinrauch@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// License information at https://github.com/AlexAUT/Kroniax2-Mapeditor/blob/master/LICENSE
//
////////////////////////////////////////////////////////////

#include "application.hpp"

#include "aw/utilities/log.hpp"

#include <exception>

#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Image.hpp>

#include <SFGUI/Button.hpp>
#include <SFGUI/Frame.hpp>
#include <SFGUI/Image.hpp>
#include <SFGUI/Scale.hpp>
#include <SFGUI/Window.hpp>
#include <SFGUI/Entry.hpp>
#include <SFGUI/RadioButton.hpp>
#include <SFGUI/Box.hpp>
#include <SFGUI/Label.hpp>
#include <SFGUI/Notebook.hpp>
#include <SFGUI/ToggleButton.hpp>

Application::Application() :
  mGui(*this),
  mTilesetController(mTilesetManager, mGui.getDesktop()),
  mLayerController(mLayerManager, mGui.getDesktop()),
  mSelectionController(mSelectionManager, mGui.getDesktop())
{
  initMainWindow();
}

int Application::run()
{
  while (mWindow.isOpen())
  {
    while (mWindow.pollEvent(mEvent))
    {
      if (!mGui.handleEvent(mEvent))
      {
        if (mEvent.type == sf::Event::Closed)
          mWindow.close();
      }
    }

    mGui.update(0.016f);
    mTilesetController.update();

    mWindow.clear(sf::Color(200, 200, 200));
    mGui.display();
    mWindow.display();
  }

  return 0;
}

void Application::initMainWindow()
{
  mWindow.create({ 1200, 750 }, "Kroniax2-Mapeditor");
  mWindow.setVerticalSyncEnabled(true);
}

<commit_msg>Forgot to call the non-default constructor of SelectionManager in Application<commit_after>////////////////////////////////////////////////////////////
//
// Kroniax2-Mapeditor
// Copyright (C) 2015 Alexander Weinrauch (alexander.weinrauch@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// License information at https://github.com/AlexAUT/Kroniax2-Mapeditor/blob/master/LICENSE
//
////////////////////////////////////////////////////////////

#include "application.hpp"

#include "aw/utilities/log.hpp"

#include <exception>

#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Image.hpp>

#include <SFGUI/Button.hpp>
#include <SFGUI/Frame.hpp>
#include <SFGUI/Image.hpp>
#include <SFGUI/Scale.hpp>
#include <SFGUI/Window.hpp>
#include <SFGUI/Entry.hpp>
#include <SFGUI/RadioButton.hpp>
#include <SFGUI/Box.hpp>
#include <SFGUI/Label.hpp>
#include <SFGUI/Notebook.hpp>
#include <SFGUI/ToggleButton.hpp>

Application::Application() :
  mGui(*this),
  mTilesetController(mTilesetManager, mGui.getDesktop()),
  mLayerController(mLayerManager, mGui.getDesktop()),
  mSelectionManager(mTilesetManager),
  mSelectionController(mSelectionManager, mGui.getDesktop())
{
  initMainWindow();
}

int Application::run()
{
  while (mWindow.isOpen())
  {
    while (mWindow.pollEvent(mEvent))
    {
      if (!mGui.handleEvent(mEvent))
      {
        if (mEvent.type == sf::Event::Closed)
          mWindow.close();
      }
    }

    mGui.update(0.016f);
    mTilesetController.update();

    mWindow.clear(sf::Color(200, 200, 200));
    mGui.display();
    mWindow.display();
  }

  return 0;
}

void Application::initMainWindow()
{
  mWindow.create({ 1200, 750 }, "Kroniax2-Mapeditor");
  mWindow.setVerticalSyncEnabled(true);
}

<|endoftext|>


<commit_before>/* util.cpp

Copyright (c) 2014, tuxuser. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

*/

#include <QtEndian>
#include <QDirIterator>
#include <QDateTime>
#include <QUuid>
#include <plist/Plist.hpp>
#include "ffs/kextconvert.h"
#include "dsdt2bios/Dsdt2Bios.h"
#include "../ffs.h"
#include "util.h"

/* General stuff */

UINT8 fileOpen(QString path, QByteArray & buf)
{
    QFileInfo fileInfo(path);

    if (!fileInfo.exists())
        return ERR_FILE_NOT_FOUND;

    QFile inputFile;
    inputFile.setFileName(path);

    if (!inputFile.open(QFile::ReadOnly))
        return ERR_FILE_OPEN;

    buf.clear();

    buf.append(inputFile.readAll());
    inputFile.close();

    return ERR_SUCCESS;
}

UINT8 fileWrite(QString path, QByteArray & buf)
{
    QFileInfo fileInfo(path);

    if (fileInfo.exists())
        printf("Warning: File already exists! Overwriting it...\n");

    QFile writeFile;
    writeFile.setFileName(path);

    if (!writeFile.open(QFile::WriteOnly))
        return ERR_FILE_OPEN;

    if(writeFile.write(buf) != buf.size())
        return ERR_FILE_WRITE;

    writeFile.close();

    return ERR_SUCCESS;
}

BOOLEAN fileExists(QString path)
{
    QFileInfo fileInfo = QFileInfo(path);

    return fileInfo.exists();
}

UINT8 dirCreate(QString path)
{
    QDir dir;
    if (dir.cd(path))
        return ERR_DIR_ALREADY_EXIST;

    if (!dir.mkpath(path))
        return ERR_DIR_CREATE;

    return ERR_SUCCESS;
}

BOOLEAN dirExists(QString path)
{
    QDir dir(path);

    return dir.exists();
}

QString pathConcatenate(QString path, QString filename)
{
    return QDir(path).filePath(filename);
}

UINT32 getDateTime()
{
    QDateTime dateTime = QDateTime::currentDateTime();
    return dateTime.toTime_t();
}

UINT16 getUInt16(QByteArray & buf, UINT32 start, bool fromBE)
{
    UINT16 tmp = 0;

    tmp = (tmp << 8) + buf.at(start+0);
    tmp = (tmp << 8) + buf.at(start+1);

    if(fromBE)
        return qFromBigEndian(tmp);
    else
        return tmp;
}

UINT32 getUInt32(QByteArray & buf, UINT32 start, bool fromBE)
{
    UINT32 tmp = 0;

    tmp = (tmp << 8) + buf.at(start+0);
    tmp = (tmp << 8) + buf.at(start+1);
    tmp = (tmp << 8) + buf.at(start+2);
    tmp = (tmp << 8) + buf.at(start+3);

    if(fromBE)
        return qFromBigEndian(tmp);
    else
        return tmp;
}

/* Specific stuff */

UINT8 getGUIDfromFile(QByteArray object, QString & name)
{
    QByteArray header;
    EFI_GUID* guid;
    header = object.left(sizeof(EFI_GUID));
    guid = (EFI_GUID*)(header.constData());

    // Get info
    name = guidToQString(*guid);
    return ERR_SUCCESS;
}

UINT8 plistReadExecName(QByteArray plist, QString & name)
{
    static const std::string execIdentifier = "CFBundleExecutable";

    QString plistExec;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(execIdentifier) > 0)
        plistExec = boost::any_cast<const std::string&>(dict.find(execIdentifier)->second).c_str();

    if(plistExec.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank. Aborting!\n");
        return ERR_ERROR;
    }

    name = plistExec;

    return ERR_SUCCESS;
}

UINT8 plistReadBundlenameAndVersion(QByteArray plist, QString & name, QString & version)
{
    static const std::string nameIdentifier = "CFBundleName";
    static const std::string versionIdentifier = "CFBundleShortVersionString";

    QString plistName;
    QString plistVersion;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(nameIdentifier) > 0)
        plistName = boost::any_cast<const std::string&>(dict.find(nameIdentifier)->second).c_str();

    if(dict.count(versionIdentifier) > 0)
        plistVersion = boost::any_cast<const std::string&>(dict.find(versionIdentifier)->second).c_str();

    if(plistName.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank. Aborting!\n");
        return ERR_ERROR;
    }

    name = plistName;
    version = plistVersion;

    return ERR_SUCCESS;
}

UINT8 plistWriteNewBasename(QByteArray plist, QString newName, QByteArray & out)
{
    std::vector<char> data;
    static const std::string nameIdentifier = "CFBundleName";

    QString plistName;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(nameIdentifier) > 0)
        plistName = boost::any_cast<const std::string&>(dict.find(nameIdentifier)->second).c_str();

    if(plistName.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank, so cannot be modified. Aborting!\n");
        return ERR_ERROR;
    }

    // Assign new value for CFBundleName
    dict[nameIdentifier] = std::string(newName.toUtf8().constData());

    Plist::writePlistXML(data, dict);

    out.clear();
    out.append(data.data(), data.size());

    return ERR_SUCCESS;
}

UINT8 checkAggressivityLevel(int aggressivity) {
    QString level;

    switch(aggressivity) {
    case RUN_AS_IS:
        level = "Do nothing - Inject as-is";
        break;
    case RUN_COMPRESS:
         level = "Compress CORE_DXE";
         break;
    case RUN_DELETE:
         level = "Delete network stuff from BIOS";
         break;
    case RUN_DEL_OZM_NREQ:
         level = "Delete non-required Ozmosis files";
         break;
    default:
        printf("Warning: Invalid aggressivity level set!\n");
        return ERR_ERROR;
    }

    printf("Info: Aggressivity level set to '%s'...\n", qPrintable(level));
    return ERR_SUCCESS;
}

UINT8 convertOzmPlist(QString input, QByteArray & out)
{
    UINT8 ret;
    QByteArray plist;

    KextConvert *kext = new KextConvert();

    ret = fileOpen(input, plist);
    if(ret) {
        printf("ERROR: Open failed: %s\n", qPrintable(input));
        return ERR_ERROR;
    }

    ret = kext->createFFS(ozmSectionName, ozmPlistGUID, plist, out);
    if(ret) {
        printf("ERROR: KEXT2FFS failed on '%s'\n", qPrintable(ozmDefaultsFilename));
        return ERR_ERROR;
    }

    return ERR_SUCCESS;
}

UINT8 convertKext(QString input, int kextIndex, QByteArray & out)
{
    UINT8 ret;
    UINT8 nullterminator = 0;

    QString sectionName, guid;
    QString bundleName, bundleVersion, execName;
    QDir dir;

    QFileInfo binaryPath;
    QFileInfo plistPath;

    QByteArray plistbuf;
    QByteArray binarybuf;
    QByteArray toConvertBinary;

    KextConvert *kext = new KextConvert();

    // Check all folders in input-dir

    if(kextIndex > 0xF) {
        printf("ERROR: Invalid kextIndex '%i' supplied!\n", kextIndex);
        return ERR_ERROR;
    }

    dir.setPath(input);
    dir = dir.filePath("Contents");
    plistPath.setFile(dir,"Info.plist");
    dir = dir.filePath("MacOS");

    if (!dir.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/MacOS/ missing!\n");
        return ERR_ERROR;
    }

    if (!plistPath.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/Info.plist missing!\n");
        return ERR_ERROR;
    }

    ret = fileOpen(plistPath.filePath(), plistbuf);
    if(ret) {
        printf("ERROR: Opening '%s' failed!\n", qPrintable(plistPath.filePath()));
        return ret;
    }

    ret = plistReadExecName(plistbuf, execName);
    if(ret) {
        printf("ERROR: Failed to get executableName Info.plist\n");
        return ret;
    }

    binaryPath.setFile(dir, execName);

    if (!binaryPath.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/MacOS/%s missing!\n",
               qPrintable(execName));
        return ERR_ERROR;
    }

    ret = plistReadBundlenameAndVersion(plistbuf, bundleName, bundleVersion);
    if (ret) {
        printf("ERROR: Failed to get bundleName and/or version from Info.plist\n");
        return ret;
    }

    ret = fileOpen(binaryPath.filePath(), binarybuf);
    if (ret) {
        printf("ERROR: Opening '%s' failed!\n", qPrintable(binaryPath.filePath()));
        return ERR_ERROR;
    }

    if(bundleVersion.isEmpty())
        bundleVersion = "?";

    sectionName.sprintf("%s-%s",qPrintable(bundleName), qPrintable(bundleVersion));

    guid = kextGUID.arg(kextIndex, 0, 16);

    toConvertBinary.append(plistbuf);
    toConvertBinary.append(nullterminator);
    toConvertBinary.append(binarybuf);

//    ret = kext->createFFS(sectionName, guid, toConvertBinary, out);
    ret = customFFScreate(toConvertBinary, guid, sectionName, out);
    if(ret) {
        printf("ERROR: KEXT2FFS failed on '%s'\n", qPrintable(sectionName));
        return ERR_ERROR;
    }

    return ERR_SUCCESS;
}

UINT8 customFFScreate(QByteArray body, QString guid, QString sectionName, QByteArray & out)
{
    QByteArray bufSectionName;
    QByteArray fileBody, header;

    /* FFS PE32 Section */
    header.fill(0, sizeof(EFI_COMMON_SECTION_HEADER));
    EFI_COMMON_SECTION_HEADER* PE32SectionHeader = (EFI_COMMON_SECTION_HEADER*)header.data();

    uint32ToUint24(sizeof(EFI_COMMON_SECTION_HEADER)+body.size(), PE32SectionHeader->Size);
    PE32SectionHeader->Type = EFI_SECTION_PE32;

    fileBody.append(header, sizeof(EFI_COMMON_SECTION_HEADER));
    fileBody.append(body);

    /* FFS User Interface */
    header.clear();
    header.fill(0, sizeof(EFI_USER_INTERFACE_SECTION));
    EFI_USER_INTERFACE_SECTION* UserInterfaceSection = (EFI_USER_INTERFACE_SECTION*)header.data();

    /* Convert sectionName to unicode data */
    bufSectionName.append((const char*) (sectionName.utf16()), sectionName.size() * 2);

    uint32ToUint24(sizeof(EFI_USER_INTERFACE_SECTION)+bufSectionName.size(), UserInterfaceSection->Size);
    UserInterfaceSection->Type = EFI_SECTION_USER_INTERFACE;

    fileBody.append(header, sizeof(EFI_USER_INTERFACE_SECTION));
    fileBody.append(bufSectionName);

    /* FFS File */
    const static UINT8 revision = 0;
    const static UINT8 erasePolarity = 1;
    const static UINT32 size = fileBody.size();

    QUuid uuid = QUuid(guid);

    header.clear();
    header.fill(0, sizeof(EFI_FFS_FILE_HEADER));
    EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data();

    uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER)+size, fileHeader->Size);
    fileHeader->Attributes = 0x00;
    fileHeader->Attributes |= (erasePolarity == ERASE_POLARITY_TRUE) ? '\xFF' : '\x00';
    fileHeader->Type = EFI_FV_FILETYPE_FREEFORM;
    fileHeader->State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
    // Invert state bits if erase polarity is true
    if (erasePolarity == ERASE_POLARITY_TRUE)
        fileHeader->State = ~fileHeader->State;

    memcpy(&fileHeader->Name, &uuid.data1, sizeof(EFI_GUID));

    // Calculate header checksum
    fileHeader->IntegrityCheck.Checksum.Header = 0;
    fileHeader->IntegrityCheck.Checksum.File = 0;
    fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER)-1);

    // Set data checksum
    if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM)
        fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((UINT8*)fileBody.constData(), fileBody.size());
    else if (revision == 1)
        fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
    else
        fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2;

    out.clear();
    out.append(header, sizeof(EFI_FFS_FILE_HEADER));
    out.append(fileBody);

    return ERR_SUCCESS;
}
<commit_msg>Duh.. was missing alignment<commit_after>/* util.cpp

Copyright (c) 2014, tuxuser. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

*/

#include <QtEndian>
#include <QDirIterator>
#include <QDateTime>
#include <QUuid>
#include <plist/Plist.hpp>
#include "ffs/kextconvert.h"
#include "dsdt2bios/Dsdt2Bios.h"
#include "../ffs.h"
#include "util.h"

/* General stuff */

UINT8 fileOpen(QString path, QByteArray & buf)
{
    QFileInfo fileInfo(path);

    if (!fileInfo.exists())
        return ERR_FILE_NOT_FOUND;

    QFile inputFile;
    inputFile.setFileName(path);

    if (!inputFile.open(QFile::ReadOnly))
        return ERR_FILE_OPEN;

    buf.clear();

    buf.append(inputFile.readAll());
    inputFile.close();

    return ERR_SUCCESS;
}

UINT8 fileWrite(QString path, QByteArray & buf)
{
    QFileInfo fileInfo(path);

    if (fileInfo.exists())
        printf("Warning: File already exists! Overwriting it...\n");

    QFile writeFile;
    writeFile.setFileName(path);

    if (!writeFile.open(QFile::WriteOnly))
        return ERR_FILE_OPEN;

    if(writeFile.write(buf) != buf.size())
        return ERR_FILE_WRITE;

    writeFile.close();

    return ERR_SUCCESS;
}

BOOLEAN fileExists(QString path)
{
    QFileInfo fileInfo = QFileInfo(path);

    return fileInfo.exists();
}

UINT8 dirCreate(QString path)
{
    QDir dir;
    if (dir.cd(path))
        return ERR_DIR_ALREADY_EXIST;

    if (!dir.mkpath(path))
        return ERR_DIR_CREATE;

    return ERR_SUCCESS;
}

BOOLEAN dirExists(QString path)
{
    QDir dir(path);

    return dir.exists();
}

QString pathConcatenate(QString path, QString filename)
{
    return QDir(path).filePath(filename);
}

UINT32 getDateTime()
{
    QDateTime dateTime = QDateTime::currentDateTime();
    return dateTime.toTime_t();
}

UINT16 getUInt16(QByteArray & buf, UINT32 start, bool fromBE)
{
    UINT16 tmp = 0;

    tmp = (tmp << 8) + buf.at(start+0);
    tmp = (tmp << 8) + buf.at(start+1);

    if(fromBE)
        return qFromBigEndian(tmp);
    else
        return tmp;
}

UINT32 getUInt32(QByteArray & buf, UINT32 start, bool fromBE)
{
    UINT32 tmp = 0;

    tmp = (tmp << 8) + buf.at(start+0);
    tmp = (tmp << 8) + buf.at(start+1);
    tmp = (tmp << 8) + buf.at(start+2);
    tmp = (tmp << 8) + buf.at(start+3);

    if(fromBE)
        return qFromBigEndian(tmp);
    else
        return tmp;
}

/* Specific stuff */

UINT8 getGUIDfromFile(QByteArray object, QString & name)
{
    QByteArray header;
    EFI_GUID* guid;
    header = object.left(sizeof(EFI_GUID));
    guid = (EFI_GUID*)(header.constData());

    // Get info
    name = guidToQString(*guid);
    return ERR_SUCCESS;
}

UINT8 plistReadExecName(QByteArray plist, QString & name)
{
    static const std::string execIdentifier = "CFBundleExecutable";

    QString plistExec;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(execIdentifier) > 0)
        plistExec = boost::any_cast<const std::string&>(dict.find(execIdentifier)->second).c_str();

    if(plistExec.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank. Aborting!\n");
        return ERR_ERROR;
    }

    name = plistExec;

    return ERR_SUCCESS;
}

UINT8 plistReadBundlenameAndVersion(QByteArray plist, QString & name, QString & version)
{
    static const std::string nameIdentifier = "CFBundleName";
    static const std::string versionIdentifier = "CFBundleShortVersionString";

    QString plistName;
    QString plistVersion;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(nameIdentifier) > 0)
        plistName = boost::any_cast<const std::string&>(dict.find(nameIdentifier)->second).c_str();

    if(dict.count(versionIdentifier) > 0)
        plistVersion = boost::any_cast<const std::string&>(dict.find(versionIdentifier)->second).c_str();

    if(plistName.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank. Aborting!\n");
        return ERR_ERROR;
    }

    name = plistName;
    version = plistVersion;

    return ERR_SUCCESS;
}

UINT8 plistWriteNewBasename(QByteArray plist, QString newName, QByteArray & out)
{
    std::vector<char> data;
    static const std::string nameIdentifier = "CFBundleName";

    QString plistName;

    std::map<std::string, boost::any> dict;
    Plist::readPlist(plist.data(), plist.size(), dict);

    if(dict.count(nameIdentifier) > 0)
        plistName = boost::any_cast<const std::string&>(dict.find(nameIdentifier)->second).c_str();

    if(plistName.isEmpty()) {
        printf("ERROR: CFBundleName in plist is blank, so cannot be modified. Aborting!\n");
        return ERR_ERROR;
    }

    // Assign new value for CFBundleName
    dict[nameIdentifier] = std::string(newName.toUtf8().constData());

    Plist::writePlistXML(data, dict);

    out.clear();
    out.append(data.data(), data.size());

    return ERR_SUCCESS;
}

UINT8 checkAggressivityLevel(int aggressivity) {
    QString level;

    switch(aggressivity) {
    case RUN_AS_IS:
        level = "Do nothing - Inject as-is";
        break;
    case RUN_COMPRESS:
         level = "Compress CORE_DXE";
         break;
    case RUN_DELETE:
         level = "Delete network stuff from BIOS";
         break;
    case RUN_DEL_OZM_NREQ:
         level = "Delete non-required Ozmosis files";
         break;
    default:
        printf("Warning: Invalid aggressivity level set!\n");
        return ERR_ERROR;
    }

    printf("Info: Aggressivity level set to '%s'...\n", qPrintable(level));
    return ERR_SUCCESS;
}

UINT8 convertOzmPlist(QString input, QByteArray & out)
{
    UINT8 ret;
    QByteArray plist;

    KextConvert *kext = new KextConvert();

    ret = fileOpen(input, plist);
    if(ret) {
        printf("ERROR: Open failed: %s\n", qPrintable(input));
        return ERR_ERROR;
    }

    ret = kext->createFFS(ozmSectionName, ozmPlistGUID, plist, out);
    if(ret) {
        printf("ERROR: KEXT2FFS failed on '%s'\n", qPrintable(ozmDefaultsFilename));
        return ERR_ERROR;
    }

    return ERR_SUCCESS;
}

UINT8 convertKext(QString input, int kextIndex, QByteArray & out)
{
    UINT8 ret;
    UINT8 nullterminator = 0;

    QString sectionName, guid;
    QString bundleName, bundleVersion, execName;
    QDir dir;

    QFileInfo binaryPath;
    QFileInfo plistPath;

    QByteArray plistbuf;
    QByteArray binarybuf;
    QByteArray toConvertBinary;

    KextConvert *kext = new KextConvert();

    // Check all folders in input-dir

    if(kextIndex > 0xF) {
        printf("ERROR: Invalid kextIndex '%i' supplied!\n", kextIndex);
        return ERR_ERROR;
    }

    dir.setPath(input);
    dir = dir.filePath("Contents");
    plistPath.setFile(dir,"Info.plist");
    dir = dir.filePath("MacOS");

    if (!dir.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/MacOS/ missing!\n");
        return ERR_ERROR;
    }

    if (!plistPath.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/Info.plist missing!\n");
        return ERR_ERROR;
    }

    ret = fileOpen(plistPath.filePath(), plistbuf);
    if(ret) {
        printf("ERROR: Opening '%s' failed!\n", qPrintable(plistPath.filePath()));
        return ret;
    }

    ret = plistReadExecName(plistbuf, execName);
    if(ret) {
        printf("ERROR: Failed to get executableName Info.plist\n");
        return ret;
    }

    binaryPath.setFile(dir, execName);

    if (!binaryPath.exists()) {
        printf("ERROR: Kext-dir invalid: */Contents/MacOS/%s missing!\n",
               qPrintable(execName));
        return ERR_ERROR;
    }

    ret = plistReadBundlenameAndVersion(plistbuf, bundleName, bundleVersion);
    if (ret) {
        printf("ERROR: Failed to get bundleName and/or version from Info.plist\n");
        return ret;
    }

    ret = fileOpen(binaryPath.filePath(), binarybuf);
    if (ret) {
        printf("ERROR: Opening '%s' failed!\n", qPrintable(binaryPath.filePath()));
        return ERR_ERROR;
    }

    if(bundleVersion.isEmpty())
        bundleVersion = "?";

    sectionName.sprintf("%s-%s",qPrintable(bundleName), qPrintable(bundleVersion));

    guid = kextGUID.arg(kextIndex, 0, 16);

    toConvertBinary.append(plistbuf);
    toConvertBinary.append(nullterminator);
    toConvertBinary.append(binarybuf);

//    ret = kext->createFFS(sectionName, guid, toConvertBinary, out);
    ret = customFFScreate(toConvertBinary, guid, sectionName, out);
    if(ret) {
        printf("ERROR: KEXT2FFS failed on '%s'\n", qPrintable(sectionName));
        return ERR_ERROR;
    }

    return ERR_SUCCESS;
}

UINT8 customFFScreate(QByteArray body, QString guid, QString sectionName, QByteArray & out)
{
    QByteArray bufSectionName;
    QByteArray fileBody, header;

    /* FFS PE32 Section */
    header.fill(0, sizeof(EFI_COMMON_SECTION_HEADER));
    EFI_COMMON_SECTION_HEADER* PE32SectionHeader = (EFI_COMMON_SECTION_HEADER*)header.data();

    uint32ToUint24(sizeof(EFI_COMMON_SECTION_HEADER)+body.size(), PE32SectionHeader->Size);
    PE32SectionHeader->Type = EFI_SECTION_PE32;

    fileBody.append(header, sizeof(EFI_COMMON_SECTION_HEADER));
    fileBody.append(body);

    /* FFS User Interface */
    header.clear();
    header.fill(0, sizeof(EFI_USER_INTERFACE_SECTION));
    EFI_USER_INTERFACE_SECTION* UserInterfaceSection = (EFI_USER_INTERFACE_SECTION*)header.data();

    /* Convert sectionName to unicode data */
    bufSectionName.append((const char*) (sectionName.utf16()), sectionName.size() * 2);

    uint32ToUint24(sizeof(EFI_USER_INTERFACE_SECTION)+bufSectionName.size(), UserInterfaceSection->Size);
    UserInterfaceSection->Type = EFI_SECTION_USER_INTERFACE;

    /* Align for next section */
    UINT8 alignment = fileBody.size() % 4;
    if (alignment) {
        alignment = 4 - alignment;
        fileBody.append(QByteArray(alignment, '\x00'));
    }

    fileBody.append(header, sizeof(EFI_USER_INTERFACE_SECTION));
    fileBody.append(bufSectionName);

    /* FFS File */
    const static UINT8 revision = 0;
    const static UINT8 erasePolarity = 1;
    const static UINT32 size = fileBody.size();

    QUuid uuid = QUuid(guid);

    header.clear();
    header.fill(0, sizeof(EFI_FFS_FILE_HEADER));
    EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data();

    uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER)+size, fileHeader->Size);
    fileHeader->Attributes = 0x00;
    fileHeader->Attributes |= (erasePolarity == ERASE_POLARITY_TRUE) ? '\xFF' : '\x00';
    fileHeader->Type = EFI_FV_FILETYPE_FREEFORM;
    fileHeader->State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
    // Invert state bits if erase polarity is true
    if (erasePolarity == ERASE_POLARITY_TRUE)
        fileHeader->State = ~fileHeader->State;

    memcpy(&fileHeader->Name, &uuid.data1, sizeof(EFI_GUID));

    // Calculate header checksum
    fileHeader->IntegrityCheck.Checksum.Header = 0;
    fileHeader->IntegrityCheck.Checksum.File = 0;
    fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER)-1);

    // Set data checksum
    if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM)
        fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((UINT8*)fileBody.constData(), fileBody.size());
    else if (revision == 1)
        fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
    else
        fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2;

    out.clear();
    out.append(header, sizeof(EFI_FFS_FILE_HEADER));
    out.append(fileBody);

    return ERR_SUCCESS;
}
<|endoftext|>