<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <algorithm>
#include <iomanip>
#include <queue>
#include "nextpnr.h"
#include "cells.h"

#include "log.h"

#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())

NEXTPNR_NAMESPACE_BEGIN

static std::string get_quad_name(GlobalQuadrant quad)
{
    switch (quad) {
    case QUAD_UL:
        return "UL";
    case QUAD_UR:
        return "UR";
    case QUAD_LL:
        return "LL";
    case QUAD_LR:
        return "LR";
    }
    return "";
}

class Ecp5GlobalRouter
{
  public:
    Ecp5GlobalRouter(Context *ctx) : ctx(ctx){};

  private:
    bool is_clock_port(const PortRef &user)
    {
        if (user.cell->type == ctx->id("TRELLIS_LC") && user.port == ctx->id("CLK"))
            return true;
        return false;
    }

    std::vector<NetInfo *> get_clocks()
    {
        std::unordered_map<IdString, int> clockCount;
        for (auto &net : ctx->nets) {
            NetInfo *ni = net.second.get();
            clockCount[ni->name] = 0;
            for (const auto &user : ni->users) {
                if (is_clock_port(user))
                    clockCount[ni->name]++;
            }
        }
        std::vector<NetInfo *> clocks;
        while (clocks.size() < 16) {
            auto max = std::max_element(clockCount.begin(), clockCount.end(),
                                        [](const decltype(clockCount)::value_type &a,
                                           const decltype(clockCount)::value_type &b) { return a.second < b.second; });
            if (max == clockCount.end() || max->second < 3)
                break;
            clocks.push_back(ctx->nets.at(max->first).get());
            clockCount.erase(max->first);
        }
        return clocks;
    }

    PipId find_tap_pip(WireId tile_glb)
    {
        std::string wireName = ctx->getWireBasename(tile_glb).str(ctx);
        std::string glbName = wireName.substr(2);
        TapDirection td = ctx->globalInfoAtLoc(tile_glb.location).tap_dir;
        WireId tap_wire;
        Location tap_loc;
        tap_loc.x = ctx->globalInfoAtLoc(tile_glb.location).tap_col;
        tap_loc.y = tile_glb.location.y;
        if (td == TAP_DIR_LEFT) {
            tap_wire = ctx->getWireByLocAndBasename(tap_loc, "L_" + glbName);
        } else {
            tap_wire = ctx->getWireByLocAndBasename(tap_loc, "R_" + glbName);
        }
        return *(ctx->getPipsUphill(tap_wire).begin());
    }

    void route_logic_tile_global(NetInfo *net, int global_index, PortRef user)
    {
        WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port));
        WireId globalWire;
        IdString global_name = ctx->id(fmt_str("G_HPBX" << std::setw(2) << std::setfill('0') << global_index << "00"));
        std::queue<WireId> upstream;
        std::unordered_map<WireId, PipId> backtrace;
        upstream.push(userWire);
        bool already_routed = false;
        // Search back from the pin until we reach the global network
        while (true) {
            WireId next = upstream.front();
            upstream.pop();

            if (ctx->getBoundWireNet(next) == net) {
                already_routed = true;
                globalWire = next;
                break;
            }

            if (ctx->getWireBasename(next) == global_name) {
                globalWire = next;
                break;
            }
            if (ctx->checkWireAvail(next)) {
                for (auto pip : ctx->getPipsUphill(next)) {
                    WireId src = ctx->getPipSrcWire(pip);
                    backtrace[src] = pip;
                    upstream.push(src);
                }
            }
            if (upstream.size() > 3000) {
                log_error("failed to route HPBX%02d00 to %s.%s\n", global_index,
                          ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx));
            }
        }
        // Set all the pips we found along the way
        WireId cursor = userWire;
        while (true) {
            auto fnd = backtrace.find(cursor);
            if (fnd == backtrace.end())
                break;
            ctx->bindPip(fnd->second, net, STRENGTH_LOCKED);
            cursor = ctx->getPipSrcWire(fnd->second);
        }
        // If the global network inside the tile isn't already set up,
        // we also need to bind the buffers along the way
        if (!already_routed) {
            ctx->bindWire(cursor, net, STRENGTH_LOCKED);
            PipId tap_pip = find_tap_pip(cursor);
            NetInfo *tap_net = ctx->getBoundPipNet(tap_pip);
            if (tap_net == nullptr) {
                ctx->bindPip(tap_pip, net, STRENGTH_LOCKED);
                // TODO: SPINE
            } else {
                NPNR_ASSERT(tap_net == net);
            }
        }
    }

    bool is_global_io(CellInfo *io, std::string &glb_name)
    {
        std::string func_name = ctx->getPioFunctionName(io->bel);
        if (func_name.substr(0, 5) == "PCLKT") {
            func_name.erase(func_name.find('_'), 1);
            glb_name = "G_" + func_name;
            return true;
        }
        return false;
    }

    WireId get_global_wire(GlobalQuadrant quad, int network)
    {
        return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network));
    }

    void simple_router(NetInfo *net, WireId src, WireId dst)
    {
        std::queue<WireId> visit;
        std::unordered_map<WireId, PipId> backtrace;
        visit.push(src);
        WireId cursor;
        while (true) {
            if (visit.empty() || visit.size() > 50000) {
                log_error("cannot route global from %s to %s.\n", ctx->getWireName(src).c_str(ctx),
                          ctx->getWireName(dst).c_str(ctx));
            }
            cursor = visit.back();
            visit.pop();
            NetInfo *bound = ctx->getBoundWireNet(cursor);
            if (bound == net) {
                break;
            } else if (bound != nullptr) {
                continue;
            }
            if (cursor == dst)
                break;
            for (auto dh : ctx->getPipsDownhill(cursor)) {
                WireId pipDst = ctx->getPipDstWire(dh);
                if (backtrace.count(pipDst))
                    continue;
                backtrace[pipDst] = dh;
                visit.push(pipDst);
            }
        }
        while (true) {
            auto fnd = backtrace.find(cursor);
            if (fnd == backtrace.end())
                break;
            ctx->bindPip(fnd->second, net, STRENGTH_LOCKED);
            cursor = ctx->getPipSrcWire(fnd->second);
        }
    }

    void route_onto_global(NetInfo *net, int network)
    {
        WireId glb_src;
        if (net->driver.cell->type == ctx->id("TRELLIS_IO")) {
            std::string ioglb;
            if (!is_global_io(net->driver.cell, ioglb))
                goto non_dedicated;
            glb_src = ctx->getWireByLocAndBasename(Location(0, 0), ioglb);
        }
        for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) {
            WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network);
            simple_router(net, glb_src, glb_dst);
        }
        if (false) {
        non_dedicated:
            log_error("FIXME: currenly global networks can only be driven by dedicated global input pins");
        }
    }

    // Insert a DCC into a net to promote it to a global
    NetInfo *insert_dcc(NetInfo *net)
    {
        auto dcc = create_ecp5_cell(ctx, ctx->id("DCCA"), "$gbuf$" + net->name.str(ctx));

        std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo);
        glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx));
        glbnet->driver.cell = dcc.get();
        glbnet->driver.port = ctx->id("CLKO");

        for (auto user : net->users) {
            user.cell->ports.at(user.port).net = glbnet.get();
        }
        net->users.clear();

        dcc->ports[ctx->id("CLKI")].net = net;
        PortRef clki_pr;
        clki_pr.port = ctx->id("CLKI");
        clki_pr.cell = dcc.get();
        net->users.push_back(clki_pr);

        ctx->cells[dcc->name] = std::move(dcc);
        NetInfo *glbptr = glbnet.get();
        ctx->nets[glbnet->name] = std::move(glbnet);
        return glbptr;
    }

    Context *ctx;
};

void route_ecp5_globals(Context *ctx);

NEXTPNR_NAMESPACE_END
<commit_msg>ecp5: Add SPINE routing to global router<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <algorithm>
#include <iomanip>
#include <queue>
#include "nextpnr.h"
#include "cells.h"

#include "log.h"

#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())

NEXTPNR_NAMESPACE_BEGIN

static std::string get_quad_name(GlobalQuadrant quad)
{
    switch (quad) {
    case QUAD_UL:
        return "UL";
    case QUAD_UR:
        return "UR";
    case QUAD_LL:
        return "LL";
    case QUAD_LR:
        return "LR";
    }
    return "";
}

class Ecp5GlobalRouter
{
  public:
    Ecp5GlobalRouter(Context *ctx) : ctx(ctx){};

  private:
    bool is_clock_port(const PortRef &user)
    {
        if (user.cell->type == ctx->id("TRELLIS_LC") && user.port == ctx->id("CLK"))
            return true;
        return false;
    }

    std::vector<NetInfo *> get_clocks()
    {
        std::unordered_map<IdString, int> clockCount;
        for (auto &net : ctx->nets) {
            NetInfo *ni = net.second.get();
            clockCount[ni->name] = 0;
            for (const auto &user : ni->users) {
                if (is_clock_port(user))
                    clockCount[ni->name]++;
            }
        }
        std::vector<NetInfo *> clocks;
        while (clocks.size() < 16) {
            auto max = std::max_element(clockCount.begin(), clockCount.end(),
                                        [](const decltype(clockCount)::value_type &a,
                                           const decltype(clockCount)::value_type &b) { return a.second < b.second; });
            if (max == clockCount.end() || max->second < 3)
                break;
            clocks.push_back(ctx->nets.at(max->first).get());
            clockCount.erase(max->first);
        }
        return clocks;
    }

    PipId find_tap_pip(WireId tile_glb)
    {
        std::string wireName = ctx->getWireBasename(tile_glb).str(ctx);
        std::string glbName = wireName.substr(2);
        TapDirection td = ctx->globalInfoAtLoc(tile_glb.location).tap_dir;
        WireId tap_wire;
        Location tap_loc;
        tap_loc.x = ctx->globalInfoAtLoc(tile_glb.location).tap_col;
        tap_loc.y = tile_glb.location.y;
        if (td == TAP_DIR_LEFT) {
            tap_wire = ctx->getWireByLocAndBasename(tap_loc, "L_" + glbName);
        } else {
            tap_wire = ctx->getWireByLocAndBasename(tap_loc, "R_" + glbName);
        }
        return *(ctx->getPipsUphill(tap_wire).begin());
    }

    PipId find_spine_pip(WireId tap_wire)
    {
        std::string wireName = ctx->getWireBasename(tap_wire).str(ctx);
        Location spine_loc;
        spine_loc.x = ctx->globalInfoAtLoc(tap_wire.location).spine_col;
        spine_loc.y = ctx->globalInfoAtLoc(tap_wire.location).spine_row;
        WireId spine_wire = ctx->getWireByLocAndBasename(spine_loc, wireName);
        return *(ctx->getPipsUphill(spine_wire).begin());
    }

    void route_logic_tile_global(NetInfo *net, int global_index, PortRef user)
    {
        WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port));
        WireId globalWire;
        IdString global_name = ctx->id(fmt_str("G_HPBX" << std::setw(2) << std::setfill('0') << global_index << "00"));
        std::queue<WireId> upstream;
        std::unordered_map<WireId, PipId> backtrace;
        upstream.push(userWire);
        bool already_routed = false;
        // Search back from the pin until we reach the global network
        while (true) {
            WireId next = upstream.front();
            upstream.pop();

            if (ctx->getBoundWireNet(next) == net) {
                already_routed = true;
                globalWire = next;
                break;
            }

            if (ctx->getWireBasename(next) == global_name) {
                globalWire = next;
                break;
            }
            if (ctx->checkWireAvail(next)) {
                for (auto pip : ctx->getPipsUphill(next)) {
                    WireId src = ctx->getPipSrcWire(pip);
                    backtrace[src] = pip;
                    upstream.push(src);
                }
            }
            if (upstream.size() > 3000) {
                log_error("failed to route HPBX%02d00 to %s.%s\n", global_index,
                          ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx));
            }
        }
        // Set all the pips we found along the way
        WireId cursor = userWire;
        while (true) {
            auto fnd = backtrace.find(cursor);
            if (fnd == backtrace.end())
                break;
            ctx->bindPip(fnd->second, net, STRENGTH_LOCKED);
            cursor = ctx->getPipSrcWire(fnd->second);
        }
        // If the global network inside the tile isn't already set up,
        // we also need to bind the buffers along the way
        if (!already_routed) {
            ctx->bindWire(cursor, net, STRENGTH_LOCKED);
            PipId tap_pip = find_tap_pip(cursor);
            NetInfo *tap_net = ctx->getBoundPipNet(tap_pip);
            if (tap_net == nullptr) {
                ctx->bindPip(tap_pip, net, STRENGTH_LOCKED);
                PipId spine_pip = find_spine_pip(ctx->getPipSrcWire(tap_pip));
                NetInfo *spine_net = ctx->getBoundPipNet(spine_pip);
                if (spine_net == nullptr) {
                    ctx->bindPip(spine_pip, net, STRENGTH_LOCKED);
                } else {
                    NPNR_ASSERT(spine_net == net);
                }
            } else {
                NPNR_ASSERT(tap_net == net);
            }
        }
    }

    bool is_global_io(CellInfo *io, std::string &glb_name)
    {
        std::string func_name = ctx->getPioFunctionName(io->bel);
        if (func_name.substr(0, 5) == "PCLKT") {
            func_name.erase(func_name.find('_'), 1);
            glb_name = "G_" + func_name;
            return true;
        }
        return false;
    }

    WireId get_global_wire(GlobalQuadrant quad, int network)
    {
        return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network));
    }

    void simple_router(NetInfo *net, WireId src, WireId dst)
    {
        std::queue<WireId> visit;
        std::unordered_map<WireId, PipId> backtrace;
        visit.push(src);
        WireId cursor;
        while (true) {
            if (visit.empty() || visit.size() > 50000) {
                log_error("cannot route global from %s to %s.\n", ctx->getWireName(src).c_str(ctx),
                          ctx->getWireName(dst).c_str(ctx));
            }
            cursor = visit.back();
            visit.pop();
            NetInfo *bound = ctx->getBoundWireNet(cursor);
            if (bound == net) {
                break;
            } else if (bound != nullptr) {
                continue;
            }
            if (cursor == dst)
                break;
            for (auto dh : ctx->getPipsDownhill(cursor)) {
                WireId pipDst = ctx->getPipDstWire(dh);
                if (backtrace.count(pipDst))
                    continue;
                backtrace[pipDst] = dh;
                visit.push(pipDst);
            }
        }
        while (true) {
            auto fnd = backtrace.find(cursor);
            if (fnd == backtrace.end())
                break;
            ctx->bindPip(fnd->second, net, STRENGTH_LOCKED);
            cursor = ctx->getPipSrcWire(fnd->second);
        }
    }

    void route_onto_global(NetInfo *net, int network)
    {
        WireId glb_src;
        if (net->driver.cell->type == ctx->id("TRELLIS_IO")) {
            std::string ioglb;
            if (!is_global_io(net->driver.cell, ioglb))
                goto non_dedicated;
            glb_src = ctx->getWireByLocAndBasename(Location(0, 0), ioglb);
        }
        for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) {
            WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network);
            simple_router(net, glb_src, glb_dst);
        }
        if (false) {
        non_dedicated:
            log_error("FIXME: currenly global networks can only be driven by dedicated global input pins");
        }
    }

    // Insert a DCC into a net to promote it to a global
    NetInfo *insert_dcc(NetInfo *net)
    {
        auto dcc = create_ecp5_cell(ctx, ctx->id("DCCA"), "$gbuf$" + net->name.str(ctx));

        std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo);
        glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx));
        glbnet->driver.cell = dcc.get();
        glbnet->driver.port = ctx->id("CLKO");

        for (auto user : net->users) {
            user.cell->ports.at(user.port).net = glbnet.get();
        }
        net->users.clear();

        dcc->ports[ctx->id("CLKI")].net = net;
        PortRef clki_pr;
        clki_pr.port = ctx->id("CLKI");
        clki_pr.cell = dcc.get();
        net->users.push_back(clki_pr);

        ctx->cells[dcc->name] = std::move(dcc);
        NetInfo *glbptr = glbnet.get();
        ctx->nets[glbnet->name] = std::move(glbnet);
        return glbptr;
    }

    Context *ctx;
};

void route_ecp5_globals(Context *ctx);

NEXTPNR_NAMESPACE_END
<|endoftext|>


<commit_before>//**********************************************************************;
// Copyright (c) 2015, Intel Corporation
// 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.
//
// 3. Neither the name of Intel Corporation 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 HOLDER 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.
//**********************************************************************;

#ifdef _WIN32
#include "stdafx.h"
#else
#include <stdarg.h>
#endif

#ifndef UNICODE
#define UNICODE 1
#endif

#ifdef _WIN32
// link with Ws2_32.lib
#pragma comment(lib,"Ws2_32.lib")

#include <winsock2.h>
#include <ws2tcpip.h>
#else
#define sprintf_s   snprintf
#define sscanf_s    sscanf
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <getopt.h>

#include <tss2/tpm20.h>
#include <tcti/tcti_socket.h>
#include "common.h"

int debugLevel = 0;
BYTE *msg = NULL;
UINT16 msgLen = 0;
TPM2B_DIGEST msgHash;

int verifySignature(TPMI_DH_OBJECT keyHandle, int D_flag, TPMI_ALG_HASH halg, TPMT_SIGNATURE *signature, const char *outFilePath)
{
    UINT32 rval;
    TPMT_TK_VERIFIED validation;

    TPMS_AUTH_RESPONSE sessionDataOut;
    TSS2_SYS_RSP_AUTHS sessionsDataOut;
    TPMS_AUTH_RESPONSE *sessionDataOutArray[1];

    sessionDataOutArray[0] = &sessionDataOut;
    sessionsDataOut.rspAuths = &sessionDataOutArray[0];
    sessionsDataOut.rspAuthsCount = 1;

    printf("\nTPM2_VerifySignature TESTS:\n");

    if(D_flag == 0)
    {
        if(computeDataHash(msg, msgLen, halg, &msgHash))
        {
            printf("Compute message hash failed !\n");
            return -1;
        }
        printf("\nVerifySignature: computing message hash succeeded!\n");
    }

    printf("\nmsgHash(hex type):\n ");
    for(UINT16 i = 0; i < msgHash.t.size; i++)
        printf("%02x ", msgHash.t.buffer[i]);
    printf("\n");

    rval = Tss2_Sys_VerifySignature(sysContext, keyHandle, NULL, &msgHash, signature, &validation, &sessionsDataOut);
    if(rval != TPM_RC_SUCCESS)
    {
        printf("tpm2_verifysignature failed, error code: 0x%x\n", rval);
        return -2;
    }
    printf("\ntpm2_verifysignature succ.\n\n");

    if(saveDataToFile(outFilePath, (UINT8 *)&validation, sizeof(validation)))
    {
        printf("failed to save validation into %s\n", outFilePath);
        return -3;
    }

    return 0;
}

void showHelp(const char *name)
{
    printf("\n%s  [options]\n"
        "\n"
        "-h, --help               Display command tool usage info;\n"
        "-v, --version            Display command tool version info\n"
        "-k, --keyHandle<hexHandle>  handle of public key that will be used in the validation\n"
        "-c, --keyContext <filename>  filename of the key context used for the operation\n"
        "-g, --halg     <hexAlg>     the hash algorithm used to digest the message \n"
        "\t0x0004  TPM_ALG_SHA1\n"
        "\t0x000B  TPM_ALG_SHA256\n"
        "\t0x000C  TPM_ALG_SHA384\n"
        "\t0x000D  TPM_ALG_SHA512\n"
        "\t0x0012  TPM_ALG_SM3_256\n"
        "-m, --msg      <filePath>   the input message file, containning the content to be digested\n"
        "-D, --digest   <filePath>   the input hash file, containning the hash of the message\n"
        "\tif this argument been chosed, the argument '-m(--msg)' and '-g(--halg)' is no need\n"
        "-s, --sig      <filePath>   the input signature file, containning the signature to be tested\n"
        "-r, --raw                   set the input signature file to raw type, default TPMT_SIGNATURE, optional\n"
        "-t, --ticket   <filePath>   the ticket file, record the validation structure\n"
        "-p, --port   <port number>  The Port number, default is %d, optional\n"
        "-d, --debugLevel <0|1|2|3>  The level of debug message, default is 0, optional\n"
        "\t0 (high level test results)\n"
        "\t1 (test app send/receive byte streams)\n"
        "\t2 (resource manager send/receive byte streams)\n"
        "\t3 (resource manager tables)\n"
    "\n"
        "Example:\n"
        "%s -k 0x81010001 -g 0x000B -m <filePath> -s <filePath> -t <filePath>\n"
        "%s -k 0x81010001 -D <filePath> -s <filePath> -t <filePath>\n"
        ,name, DEFAULT_RESMGR_TPM_PORT, name, name);
}

int main(int argc, char* argv[])
{
    char hostName[200] = DEFAULT_HOSTNAME;
    int port = DEFAULT_RESMGR_TPM_PORT;

    TPMI_DH_OBJECT keyHandle;
    long fileSize = 0;
    UINT16 size;

    TPMI_ALG_HASH halg = TPM_ALG_SHA256;
    TPMT_SIGNATURE  signature;
    char inSigFilePath[PATH_MAX] = {0};
    char outFilePath[PATH_MAX] = {0};
    char inMsgFilePath[PATH_MAX] = {0};
    char *contextKeyFile = NULL;

    setbuf(stdout, NULL);
    setvbuf (stdout, NULL, _IONBF, BUFSIZ);

    int opt = -1;
    const char *optstring = "hvk:g:m:D:rs:t:p:d:c:";
    static struct option long_options[] = {
      {"help",0,NULL,'h'},
      {"version",0,NULL,'v'},
      {"keyHandle",1,NULL,'k'},
      {"digest",1,NULL,'D'},
      {"halg",1,NULL,'g'},
      {"msg",1,NULL,'m'},
      {"raw",0,NULL,'r'},
      {"sig",1,NULL,'s'},
      {"ticket",1,NULL,'t'},
      {"port",1,NULL,'p'},
      {"debugLevel",1,NULL,'d'},
      {"keyContext",1,NULL,'c'},
      {0,0,0,0}
    };

    int returnVal = 0;
    int flagCnt = 0;
    int h_flag = 0,
        v_flag = 0,
        k_flag = 0,
        g_flag = 0,
        m_flag = 0,
        D_flag = 0,
        r_flag = 0,
        s_flag = 0,
        c_flag = 0,
        t_flag = 0;

    if(argc == 1)
    {
        showHelp(argv[0]);
        return 0;
    }

    while((opt = getopt_long(argc,argv,optstring,long_options,NULL)) != -1)
    {
        switch(opt)
        {
        case 'h':
            h_flag = 1;
            break;
        case 'v':
            v_flag = 1;
            break;
        case 'k':
            if(getSizeUint32Hex(optarg,&keyHandle) != 0)
            {
                returnVal = -1;
                break;
            }
            k_flag = 1;
            break;
        case 'g':
            if(getSizeUint16Hex(optarg,&halg) != 0)
            {
                showArgError(optarg, argv[0]);
                returnVal = -2;
                break;
            }
            printf("halg = 0x%4.4x\n", halg);
            g_flag = 1;
            break;
        case 'm':
            safeStrNCpy(inMsgFilePath, optarg, sizeof(inMsgFilePath));
            m_flag = 1;
            break;
        case 'D':
            size = sizeof(msgHash);
            if(loadDataFromFile(optarg, (UINT8 *)&msgHash, &size) != 0)
            {
                returnVal = -3;
                break;
            }
            D_flag = 1;
            break;
        case 'r':
            r_flag = 1;
            break;
        case 's':
            safeStrNCpy(inSigFilePath, optarg, sizeof(inSigFilePath));
            s_flag = 1;
            break;
        case 't':
            safeStrNCpy(outFilePath, optarg, sizeof(outFilePath));
            if(checkOutFile(outFilePath) != 0)
            {
                returnVal = -4;
                break;
            }
            t_flag = 1;
            break;
        case 'p':
            if( getPort(optarg, &port) )
            {
                printf("Incorrect port number.\n");
                returnVal = -5;
            }
            break;
        case 'd':
            if( getDebugLevel(optarg, &debugLevel) )
            {
                printf("Incorrect debug level.\n");
                returnVal = -6;
            }
            break;
        case 'c':
            contextKeyFile = optarg;
            if(contextKeyFile == NULL || contextKeyFile[0] == '\0')
            {
                returnVal = -7;
                break;
            }
            printf("contextKeyFile = %s\n", contextKeyFile);
            c_flag = 1;
            break;
        case ':':
//              printf("Argument %c needs a value!\n",optopt);
            returnVal = -8;
            break;
        case '?':
//              printf("Unknown Argument: %c\n",optopt);
            returnVal = -9;
            break;
        //default:
        //  break;
        }
        if(returnVal)
            break;
    };

    if(returnVal != 0)
        goto end;

    if(m_flag)
    {
        if(getFileSize(inMsgFilePath, &fileSize) != 0)
        {
            returnVal = -10;
            goto end;
        }
        if(fileSize == 0)
        {
            printf("the message file is empty !\n");
            returnVal = -11;
            goto end;
        }
        if(fileSize > 0xffff)
        {
            printf("the message file was too long !\n");
            returnVal = -12;
            goto end;
        }
#if 0
        printf("\nmsg length: %d\n",length);
        printf("msg content: ");
        for(int i = 0; i < length; i++)
        {
            printf("%02x ", msg[i]);
        }
        printf("\n");
        return -1;
#endif
        msg = (BYTE*)malloc(fileSize);
        if(msg == NULL)
        {
            returnVal = -13;
            goto end;
        }
        memset(msg, 0, fileSize);

        msgLen = fileSize;
        if(loadDataFromFile(inMsgFilePath, msg, &msgLen) != 0)
        {
            returnVal = -14;
            goto end;
        }
    }

    if(D_flag == 1 && (m_flag == 1 || g_flag == 1))
    {
        showArgMismatch(argv[0]);
        returnVal = -15;
        goto end;
    }
    if(D_flag == 1)
    {
        printf("\nVerifySignature: using the input hash file!\n");
    }

    if(s_flag ==1)  //construct the signature
    {
        if(r_flag == 0)
        {
            UINT16 size = sizeof(signature);
            if(loadDataFromFile(inSigFilePath, (UINT8 *)&signature, &size))
            {
                returnVal = -16;
                goto end;
            }
            printf("VerifySignature: using the input signature file as sig structure!\n");
        }
        else
        {
            signature.sigAlg = TPM_ALG_RSASSA;
            signature.signature.rsassa.hash = halg;
            signature.signature.rsassa.sig.t.size = sizeof(signature.signature.rsassa.sig) - 2;
            if(loadDataFromFile(inSigFilePath, signature.signature.rsassa.sig.t.buffer,
                        &signature.signature.rsassa.sig.t.size))
            {
                returnVal = -17;
                goto end;
            }
            printf("VerifySignature: using the input signature file as raw data!\n");
        }
    }

    flagCnt = h_flag + v_flag + k_flag + g_flag + m_flag + D_flag + s_flag + t_flag + c_flag;

    if(flagCnt == 1)
    {
        if(h_flag == 1)
            showHelp(argv[0]);
        else if(v_flag == 1)
            showVersion(argv[0]);
        else
        {
            showArgMismatch(argv[0]);
            returnVal = -18;
        }
    }
    else if(((flagCnt==4 && D_flag==1) || (flagCnt==5 && D_flag==0)) &&
            (h_flag==0  && v_flag==0) &&
            ((k_flag==1 || c_flag == 1) && s_flag==1 && t_flag==1) )
    {
        prepareTest(hostName, port, debugLevel);

        if(c_flag)
            returnVal = loadTpmContextFromFile(sysContext, &keyHandle, contextKeyFile);
        if(returnVal == 0)
            returnVal = verifySignature(keyHandle, D_flag, halg, &signature, outFilePath);

        finishTest();

        if(returnVal)
            returnVal = -19;
    }
    else
    {
        showArgMismatch(argv[0]);
        returnVal = -20;
    }

end:
    if(msg)
        free(msg);
    return returnVal;
}
<commit_msg>tpm2_verifysignature: fix one more TPM2B failure<commit_after>//**********************************************************************;
// Copyright (c) 2015, Intel Corporation
// 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.
//
// 3. Neither the name of Intel Corporation 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 HOLDER 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.
//**********************************************************************;

#ifdef _WIN32
#include "stdafx.h"
#else
#include <stdarg.h>
#endif

#ifndef UNICODE
#define UNICODE 1
#endif

#ifdef _WIN32
// link with Ws2_32.lib
#pragma comment(lib,"Ws2_32.lib")

#include <winsock2.h>
#include <ws2tcpip.h>
#else
#define sprintf_s   snprintf
#define sscanf_s    sscanf
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <getopt.h>

#include <tss2/tpm20.h>
#include <tcti/tcti_socket.h>
#include "common.h"

int debugLevel = 0;
BYTE *msg = NULL;
UINT16 msgLen = 0;
TPM2B_DIGEST msgHash = { { sizeof(TPM2B_DIGEST)-2, } };

int verifySignature(TPMI_DH_OBJECT keyHandle, int D_flag, TPMI_ALG_HASH halg, TPMT_SIGNATURE *signature, const char *outFilePath)
{
    UINT32 rval;
    TPMT_TK_VERIFIED validation;

    TPMS_AUTH_RESPONSE sessionDataOut;
    TSS2_SYS_RSP_AUTHS sessionsDataOut;
    TPMS_AUTH_RESPONSE *sessionDataOutArray[1];

    sessionDataOutArray[0] = &sessionDataOut;
    sessionsDataOut.rspAuths = &sessionDataOutArray[0];
    sessionsDataOut.rspAuthsCount = 1;

    printf("\nTPM2_VerifySignature TESTS:\n");

    if(D_flag == 0)
    {
        if(computeDataHash(msg, msgLen, halg, &msgHash))
        {
            printf("Compute message hash failed !\n");
            return -1;
        }
        printf("\nVerifySignature: computing message hash succeeded!\n");
    }

    printf("\nmsgHash(hex type):\n ");
    for(UINT16 i = 0; i < msgHash.t.size; i++)
        printf("%02x ", msgHash.t.buffer[i]);
    printf("\n");

    rval = Tss2_Sys_VerifySignature(sysContext, keyHandle, NULL, &msgHash, signature, &validation, &sessionsDataOut);
    if(rval != TPM_RC_SUCCESS)
    {
        printf("tpm2_verifysignature failed, error code: 0x%x\n", rval);
        return -2;
    }
    printf("\ntpm2_verifysignature succ.\n\n");

    if(saveDataToFile(outFilePath, (UINT8 *)&validation, sizeof(validation)))
    {
        printf("failed to save validation into %s\n", outFilePath);
        return -3;
    }

    return 0;
}

void showHelp(const char *name)
{
    printf("\n%s  [options]\n"
        "\n"
        "-h, --help               Display command tool usage info;\n"
        "-v, --version            Display command tool version info\n"
        "-k, --keyHandle<hexHandle>  handle of public key that will be used in the validation\n"
        "-c, --keyContext <filename>  filename of the key context used for the operation\n"
        "-g, --halg     <hexAlg>     the hash algorithm used to digest the message \n"
        "\t0x0004  TPM_ALG_SHA1\n"
        "\t0x000B  TPM_ALG_SHA256\n"
        "\t0x000C  TPM_ALG_SHA384\n"
        "\t0x000D  TPM_ALG_SHA512\n"
        "\t0x0012  TPM_ALG_SM3_256\n"
        "-m, --msg      <filePath>   the input message file, containning the content to be digested\n"
        "-D, --digest   <filePath>   the input hash file, containning the hash of the message\n"
        "\tif this argument been chosed, the argument '-m(--msg)' and '-g(--halg)' is no need\n"
        "-s, --sig      <filePath>   the input signature file, containning the signature to be tested\n"
        "-r, --raw                   set the input signature file to raw type, default TPMT_SIGNATURE, optional\n"
        "-t, --ticket   <filePath>   the ticket file, record the validation structure\n"
        "-p, --port   <port number>  The Port number, default is %d, optional\n"
        "-d, --debugLevel <0|1|2|3>  The level of debug message, default is 0, optional\n"
        "\t0 (high level test results)\n"
        "\t1 (test app send/receive byte streams)\n"
        "\t2 (resource manager send/receive byte streams)\n"
        "\t3 (resource manager tables)\n"
    "\n"
        "Example:\n"
        "%s -k 0x81010001 -g 0x000B -m <filePath> -s <filePath> -t <filePath>\n"
        "%s -k 0x81010001 -D <filePath> -s <filePath> -t <filePath>\n"
        ,name, DEFAULT_RESMGR_TPM_PORT, name, name);
}

int main(int argc, char* argv[])
{
    char hostName[200] = DEFAULT_HOSTNAME;
    int port = DEFAULT_RESMGR_TPM_PORT;

    TPMI_DH_OBJECT keyHandle;
    long fileSize = 0;
    UINT16 size;

    TPMI_ALG_HASH halg = TPM_ALG_SHA256;
    TPMT_SIGNATURE  signature;
    char inSigFilePath[PATH_MAX] = {0};
    char outFilePath[PATH_MAX] = {0};
    char inMsgFilePath[PATH_MAX] = {0};
    char *contextKeyFile = NULL;

    setbuf(stdout, NULL);
    setvbuf (stdout, NULL, _IONBF, BUFSIZ);

    int opt = -1;
    const char *optstring = "hvk:g:m:D:rs:t:p:d:c:";
    static struct option long_options[] = {
      {"help",0,NULL,'h'},
      {"version",0,NULL,'v'},
      {"keyHandle",1,NULL,'k'},
      {"digest",1,NULL,'D'},
      {"halg",1,NULL,'g'},
      {"msg",1,NULL,'m'},
      {"raw",0,NULL,'r'},
      {"sig",1,NULL,'s'},
      {"ticket",1,NULL,'t'},
      {"port",1,NULL,'p'},
      {"debugLevel",1,NULL,'d'},
      {"keyContext",1,NULL,'c'},
      {0,0,0,0}
    };

    int returnVal = 0;
    int flagCnt = 0;
    int h_flag = 0,
        v_flag = 0,
        k_flag = 0,
        g_flag = 0,
        m_flag = 0,
        D_flag = 0,
        r_flag = 0,
        s_flag = 0,
        c_flag = 0,
        t_flag = 0;

    if(argc == 1)
    {
        showHelp(argv[0]);
        return 0;
    }

    while((opt = getopt_long(argc,argv,optstring,long_options,NULL)) != -1)
    {
        switch(opt)
        {
        case 'h':
            h_flag = 1;
            break;
        case 'v':
            v_flag = 1;
            break;
        case 'k':
            if(getSizeUint32Hex(optarg,&keyHandle) != 0)
            {
                returnVal = -1;
                break;
            }
            k_flag = 1;
            break;
        case 'g':
            if(getSizeUint16Hex(optarg,&halg) != 0)
            {
                showArgError(optarg, argv[0]);
                returnVal = -2;
                break;
            }
            printf("halg = 0x%4.4x\n", halg);
            g_flag = 1;
            break;
        case 'm':
            safeStrNCpy(inMsgFilePath, optarg, sizeof(inMsgFilePath));
            m_flag = 1;
            break;
        case 'D':
            size = sizeof(msgHash);
            if(loadDataFromFile(optarg, (UINT8 *)&msgHash, &size) != 0)
            {
                returnVal = -3;
                break;
            }
            D_flag = 1;
            break;
        case 'r':
            r_flag = 1;
            break;
        case 's':
            safeStrNCpy(inSigFilePath, optarg, sizeof(inSigFilePath));
            s_flag = 1;
            break;
        case 't':
            safeStrNCpy(outFilePath, optarg, sizeof(outFilePath));
            if(checkOutFile(outFilePath) != 0)
            {
                returnVal = -4;
                break;
            }
            t_flag = 1;
            break;
        case 'p':
            if( getPort(optarg, &port) )
            {
                printf("Incorrect port number.\n");
                returnVal = -5;
            }
            break;
        case 'd':
            if( getDebugLevel(optarg, &debugLevel) )
            {
                printf("Incorrect debug level.\n");
                returnVal = -6;
            }
            break;
        case 'c':
            contextKeyFile = optarg;
            if(contextKeyFile == NULL || contextKeyFile[0] == '\0')
            {
                returnVal = -7;
                break;
            }
            printf("contextKeyFile = %s\n", contextKeyFile);
            c_flag = 1;
            break;
        case ':':
//              printf("Argument %c needs a value!\n",optopt);
            returnVal = -8;
            break;
        case '?':
//              printf("Unknown Argument: %c\n",optopt);
            returnVal = -9;
            break;
        //default:
        //  break;
        }
        if(returnVal)
            break;
    };

    if(returnVal != 0)
        goto end;

    if(m_flag)
    {
        if(getFileSize(inMsgFilePath, &fileSize) != 0)
        {
            returnVal = -10;
            goto end;
        }
        if(fileSize == 0)
        {
            printf("the message file is empty !\n");
            returnVal = -11;
            goto end;
        }
        if(fileSize > 0xffff)
        {
            printf("the message file was too long !\n");
            returnVal = -12;
            goto end;
        }
#if 0
        printf("\nmsg length: %d\n",length);
        printf("msg content: ");
        for(int i = 0; i < length; i++)
        {
            printf("%02x ", msg[i]);
        }
        printf("\n");
        return -1;
#endif
        msg = (BYTE*)malloc(fileSize);
        if(msg == NULL)
        {
            returnVal = -13;
            goto end;
        }
        memset(msg, 0, fileSize);

        msgLen = fileSize;
        if(loadDataFromFile(inMsgFilePath, msg, &msgLen) != 0)
        {
            returnVal = -14;
            goto end;
        }
    }

    if(D_flag == 1 && (m_flag == 1 || g_flag == 1))
    {
        showArgMismatch(argv[0]);
        returnVal = -15;
        goto end;
    }
    if(D_flag == 1)
    {
        printf("\nVerifySignature: using the input hash file!\n");
    }

    if(s_flag ==1)  //construct the signature
    {
        if(r_flag == 0)
        {
            UINT16 size = sizeof(signature);
            if(loadDataFromFile(inSigFilePath, (UINT8 *)&signature, &size))
            {
                returnVal = -16;
                goto end;
            }
            printf("VerifySignature: using the input signature file as sig structure!\n");
        }
        else
        {
            signature.sigAlg = TPM_ALG_RSASSA;
            signature.signature.rsassa.hash = halg;
            signature.signature.rsassa.sig.t.size = sizeof(signature.signature.rsassa.sig) - 2;
            if(loadDataFromFile(inSigFilePath, signature.signature.rsassa.sig.t.buffer,
                        &signature.signature.rsassa.sig.t.size))
            {
                returnVal = -17;
                goto end;
            }
            printf("VerifySignature: using the input signature file as raw data!\n");
        }
    }

    flagCnt = h_flag + v_flag + k_flag + g_flag + m_flag + D_flag + s_flag + t_flag + c_flag;

    if(flagCnt == 1)
    {
        if(h_flag == 1)
            showHelp(argv[0]);
        else if(v_flag == 1)
            showVersion(argv[0]);
        else
        {
            showArgMismatch(argv[0]);
            returnVal = -18;
        }
    }
    else if(((flagCnt==4 && D_flag==1) || (flagCnt==5 && D_flag==0)) &&
            (h_flag==0  && v_flag==0) &&
            ((k_flag==1 || c_flag == 1) && s_flag==1 && t_flag==1) )
    {
        prepareTest(hostName, port, debugLevel);

        if(c_flag)
            returnVal = loadTpmContextFromFile(sysContext, &keyHandle, contextKeyFile);
        if(returnVal == 0)
            returnVal = verifySignature(keyHandle, D_flag, halg, &signature, outFilePath);

        finishTest();

        if(returnVal)
            returnVal = -19;
    }
    else
    {
        showArgMismatch(argv[0]);
        returnVal = -20;
    }

end:
    if(msg)
        free(msg);
    return returnVal;
}
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, Inc.
 *  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 Willow Garage, Inc. 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.
 *
 * Author: Nico Blodow (blodow@cs.tum.edu)
 *         Christian Potthast (potthast@usc.edu)
 */

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/time.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#define BOOST_FILESYSTEM_VERSION 3
#include <boost/filesystem.hpp>
#include <pcl/visualization/pcl_visualizer.h>

using namespace pcl::console;
using namespace boost::filesystem;

template<typename PointType>
class OpenNIGrabFrame
{
  typedef pcl::PointCloud<PointType> Cloud;
  typedef typename Cloud::ConstPtr CloudConstPtr;
  public:
    OpenNIGrabFrame ()
    : visualizer_ (new pcl::visualization::PCLVisualizer ("OpenNI Viewer"))
    , writer_ ()
    , quit_ (false)
    , trigger_ (false)
    , continuous_ (false)
    , file_name_ ("")
    , dir_name_ ("")
    , format_ (4)
    {
    }

    void
    cloud_cb_ (const CloudConstPtr& cloud)
    {
      if (quit_)
        return;

      boost::mutex::scoped_lock lock (cloud_mutex_);
        cloud_ = cloud;

      if (continuous_ || trigger_)
        saveCloud ();

      trigger_ = false;
    }

    void
    keyboard_callback (const pcl::visualization::KeyboardEvent& event, void*)
    {
      if (event.keyUp ())
      {
        switch (event.getKeyCode ())
        {
          case 27:
          case 'Q':
          case 'q': quit_ = true; visualizer_->close ();
            break;
          case ' ': continuous_ = !continuous_;
            break;
        }
      }
    }

    void
    mouse_callback (const pcl::visualization::MouseEvent& mouse_event, void*)
    {
      if (mouse_event.getType() == pcl::visualization::MouseEvent::MouseButtonPress && mouse_event.getButton() == pcl::visualization::MouseEvent::LeftButton)
      {
        trigger_ = true;
      }
    }

    CloudConstPtr
    getLatestCloud ()
    {
      //lock while we swap our cloud and reset it.
      boost::mutex::scoped_lock lock(cloud_mutex_);
      CloudConstPtr temp_cloud;
      temp_cloud.swap (cloud_); //here we set cloud_ to null, so that
      //it is safe to set it again from our
      //callback
      return (temp_cloud);
    }

    void saveCloud ()
    {
      std::stringstream ss;
      ss << dir_name_ << "/" << file_name_ << "_" << boost::posix_time::to_iso_string (boost::posix_time::microsec_clock::local_time ()) << ".pcd";

      if (format_ & 1)
      {
        writer_.writeBinary<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY format to " << ss.str () << std::endl;
      }

      if (format_ & 2)
      {
        writer_.writeBinaryCompressed<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY COMPRESSED format to " << ss.str () << std::endl;
      }

      if (format_ & 4)
      {
        writer_.writeBinaryCompressed<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY COMPRESSED format to " << ss.str () << std::endl;
      }
    }

    void
    run ()
    {
      // register the keyboard and mouse callback for the visualizer
      visualizer_->registerMouseCallback (&OpenNIGrabFrame::mouse_callback, *this);
      visualizer_->registerKeyboardCallback(&OpenNIGrabFrame::keyboard_callback, *this);

      // create a new grabber for OpenNI devices
      pcl::Grabber* interface = new pcl::OpenNIGrabber ();

      // make callback function from member function
      boost::function<void (const CloudConstPtr&)> f =
        boost::bind (&OpenNIGrabFrame::cloud_cb_, this, _1);

      // connect callback function for desired signal. In this case its a point cloud with color values
      boost::signals2::connection c = interface->registerCallback (f);

      // start receiving point clouds
      interface->start ();

      // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
      while (!visualizer_->wasStopped())
      {
        visualizer_->spinOnce ();
        if (cloud_)
        {
          CloudConstPtr cloud = getLatestCloud ();
          if (!visualizer_->updatePointCloud (cloud, "OpenNICloud"))
          {
            visualizer_->addPointCloud (cloud, "OpenNICloud");
            visualizer_->resetCameraViewpoint ("OpenNICloud");
          }
        }
        boost::this_thread::sleep (boost::posix_time::microseconds (100));
      }

      //while (!quit_)
        //boost::this_thread::sleep (boost::posix_time::seconds (1));

      // stop the grabber
      interface->stop ();
    }

    void
    setOptions (std::string filename, std::string pcd_format, bool paused)
    {
      boost::filesystem::path path(filename);

      if (filename.empty ())
      {
        dir_name_ = ".";
        file_name_ = "frame";
      }
      else
      {
        dir_name_ = path.parent_path ().string ();

        if (!dir_name_.empty () && !boost::filesystem::exists (path.parent_path ()))
        {
          std::cerr << "directory \"" << path.parent_path () << "\" does not exist!\n";
          exit (1);
        }
        file_name_ = path.stem ().string ();
      }

      std::cout << "dir: " << dir_name_ << " :: " << path.parent_path () << std::endl;
      std::cout << "file: " << file_name_ << " :: " << path.stem (). string () << std::endl;

      if (pcd_format == "b" || pcd_format == "all")
        format_ |= 1;
      else if (pcd_format == "ascii" || pcd_format == "all")
        format_ |= 2;
      else if (pcd_format == "bc" || pcd_format == "all")
        format_ |= 4;

      continuous_ = !paused;
    }

    boost::shared_ptr<pcl::visualization::PCLVisualizer> visualizer_;
    pcl::PCDWriter writer_;
    bool quit_;
    bool continuous_;
    bool trigger_;
    std::string file_name_;
    std::string dir_name_;
    unsigned format_;
    CloudConstPtr cloud_;
    mutable boost::mutex cloud_mutex_;
};

void
usage (char ** argv)
{
  std::cout << "usage: " << argv[0] << " <filename> <options>\n\n";

  print_info ("  filename: if no filename is provided a generic timestamp will be set as filename\n\n");
  print_info ("  where options are:\n");
  print_info ("                    -format = PCD file format (b=binary; bc=binary compressed; ascii=ascii; all=all) (default: bc)\n");
  print_info ("                    -XYZ  = store just a XYZ cloud\n");
  print_info ("                    -paused = start grabber in paused mode. Toggle pause by pressing the space bar\n");
  print_info ("                              or grab single frames by just pressing the left mouse button.\n");
}

int
main (int argc, char** argv)
{
  std::string arg;
  if (argc > 1)
    arg = std::string (argv[1]);

  if (arg == "--help" || arg == "-h")
  {
    usage (argv);
    return 1;
  }

  std::string format = "bc";
  std::string filename;
  bool paused = false;
  bool xyz = false;
  if (argc > 1)
  {
    // Parse the command line arguments for .pcd file
    std::vector<int> p_file_indices;
    p_file_indices = parse_file_extension_argument (argc, argv, ".pcd");
    if (p_file_indices.size () > 0)
      filename = argv[p_file_indices[0]];

    std::cout << "fname: " << filename << std::endl;
    // Command line parsing
    parse_argument (argc, argv, "-format", format);
    xyz = find_switch (argc, argv, "-XYZ");
    paused = find_switch (argc, argv, "-paused");
  }

  if (xyz)
  {
    OpenNIGrabFrame<pcl::PointXYZ> grab_frame;
    grab_frame.setOptions (filename, format, paused);
    grab_frame.run ();
  }
  else
  {
    OpenNIGrabFrame<pcl::PointXYZRGBA> grab_frame;
    grab_frame.setOptions (filename, format, paused);
    grab_frame.run ();
  }
  return (0);
}

<commit_msg>Add compatibility code for boost filesystem version 2 to openni_grab_frame<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, Inc.
 *  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 Willow Garage, Inc. 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.
 *
 * Author: Nico Blodow (blodow@cs.tum.edu)
 *         Christian Potthast (potthast@usc.edu)
 */

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/time.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#include <boost/filesystem.hpp>
#include <pcl/visualization/pcl_visualizer.h>

using namespace pcl::console;
using namespace boost::filesystem;

template<typename PointType>
class OpenNIGrabFrame
{
  typedef pcl::PointCloud<PointType> Cloud;
  typedef typename Cloud::ConstPtr CloudConstPtr;
  public:
    OpenNIGrabFrame ()
    : visualizer_ (new pcl::visualization::PCLVisualizer ("OpenNI Viewer"))
    , writer_ ()
    , quit_ (false)
    , trigger_ (false)
    , continuous_ (false)
    , file_name_ ("")
    , dir_name_ ("")
    , format_ (4)
    {
    }

    void
    cloud_cb_ (const CloudConstPtr& cloud)
    {
      if (quit_)
        return;

      boost::mutex::scoped_lock lock (cloud_mutex_);
        cloud_ = cloud;

      if (continuous_ || trigger_)
        saveCloud ();

      trigger_ = false;
    }

    void
    keyboard_callback (const pcl::visualization::KeyboardEvent& event, void*)
    {
      if (event.keyUp ())
      {
        switch (event.getKeyCode ())
        {
          case 27:
          case 'Q':
          case 'q': quit_ = true; visualizer_->close ();
            break;
          case ' ': continuous_ = !continuous_;
            break;
        }
      }
    }

    void
    mouse_callback (const pcl::visualization::MouseEvent& mouse_event, void*)
    {
      if (mouse_event.getType() == pcl::visualization::MouseEvent::MouseButtonPress && mouse_event.getButton() == pcl::visualization::MouseEvent::LeftButton)
      {
        trigger_ = true;
      }
    }

    CloudConstPtr
    getLatestCloud ()
    {
      //lock while we swap our cloud and reset it.
      boost::mutex::scoped_lock lock(cloud_mutex_);
      CloudConstPtr temp_cloud;
      temp_cloud.swap (cloud_); //here we set cloud_ to null, so that
      //it is safe to set it again from our
      //callback
      return (temp_cloud);
    }

    void saveCloud ()
    {
      std::stringstream ss;
      ss << dir_name_ << "/" << file_name_ << "_" << boost::posix_time::to_iso_string (boost::posix_time::microsec_clock::local_time ()) << ".pcd";

      if (format_ & 1)
      {
        writer_.writeBinary<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY format to " << ss.str () << std::endl;
      }

      if (format_ & 2)
      {
        writer_.writeBinaryCompressed<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY COMPRESSED format to " << ss.str () << std::endl;
      }

      if (format_ & 4)
      {
        writer_.writeBinaryCompressed<PointType> (ss.str (), *cloud_);
        std::cerr << "Data saved in BINARY COMPRESSED format to " << ss.str () << std::endl;
      }
    }

    void
    run ()
    {
      // register the keyboard and mouse callback for the visualizer
      visualizer_->registerMouseCallback (&OpenNIGrabFrame::mouse_callback, *this);
      visualizer_->registerKeyboardCallback(&OpenNIGrabFrame::keyboard_callback, *this);

      // create a new grabber for OpenNI devices
      pcl::Grabber* interface = new pcl::OpenNIGrabber ();

      // make callback function from member function
      boost::function<void (const CloudConstPtr&)> f =
        boost::bind (&OpenNIGrabFrame::cloud_cb_, this, _1);

      // connect callback function for desired signal. In this case its a point cloud with color values
      boost::signals2::connection c = interface->registerCallback (f);

      // start receiving point clouds
      interface->start ();

      // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
      while (!visualizer_->wasStopped())
      {
        visualizer_->spinOnce ();
        if (cloud_)
        {
          CloudConstPtr cloud = getLatestCloud ();
          if (!visualizer_->updatePointCloud (cloud, "OpenNICloud"))
          {
            visualizer_->addPointCloud (cloud, "OpenNICloud");
            visualizer_->resetCameraViewpoint ("OpenNICloud");
          }
        }
        boost::this_thread::sleep (boost::posix_time::microseconds (100));
      }

      //while (!quit_)
        //boost::this_thread::sleep (boost::posix_time::seconds (1));

      // stop the grabber
      interface->stop ();
    }

    void
    setOptions (std::string filename, std::string pcd_format, bool paused)
    {
      boost::filesystem::path path(filename);

      if (filename.empty ())
      {
        dir_name_ = ".";
        file_name_ = "frame";
      }
      else
      {
        dir_name_ = path.parent_path ().string ();

        if (!dir_name_.empty () && !boost::filesystem::exists (path.parent_path ()))
        {
          std::cerr << "directory \"" << path.parent_path () << "\" does not exist!\n";
          exit (1);
        }
#if BOOST_FILESYSTEM_VERSION == 3
        file_name_ = path.stem ().string ();
#else
        file_name_ = path.stem ();
#endif
      }

      std::cout << "dir: " << dir_name_ << " :: " << path.parent_path () << std::endl;
#if BOOST_FILESYSTEM_VERSION == 3
      std::cout << "file: " << file_name_ << " :: " << path.stem (). string () << std::endl;
#else
      std::cout << "file: " << file_name_ << " :: " << path.stem () << std::endl;
#endif

      if (pcd_format == "b" || pcd_format == "all")
        format_ |= 1;
      else if (pcd_format == "ascii" || pcd_format == "all")
        format_ |= 2;
      else if (pcd_format == "bc" || pcd_format == "all")
        format_ |= 4;

      continuous_ = !paused;
    }

    boost::shared_ptr<pcl::visualization::PCLVisualizer> visualizer_;
    pcl::PCDWriter writer_;
    bool quit_;
    bool continuous_;
    bool trigger_;
    std::string file_name_;
    std::string dir_name_;
    unsigned format_;
    CloudConstPtr cloud_;
    mutable boost::mutex cloud_mutex_;
};

void
usage (char ** argv)
{
  std::cout << "usage: " << argv[0] << " <filename> <options>\n\n";

  print_info ("  filename: if no filename is provided a generic timestamp will be set as filename\n\n");
  print_info ("  where options are:\n");
  print_info ("                    -format = PCD file format (b=binary; bc=binary compressed; ascii=ascii; all=all) (default: bc)\n");
  print_info ("                    -XYZ  = store just a XYZ cloud\n");
  print_info ("                    -paused = start grabber in paused mode. Toggle pause by pressing the space bar\n");
  print_info ("                              or grab single frames by just pressing the left mouse button.\n");
}

int
main (int argc, char** argv)
{
  std::string arg;
  if (argc > 1)
    arg = std::string (argv[1]);

  if (arg == "--help" || arg == "-h")
  {
    usage (argv);
    return 1;
  }

  std::string format = "bc";
  std::string filename;
  bool paused = false;
  bool xyz = false;
  if (argc > 1)
  {
    // Parse the command line arguments for .pcd file
    std::vector<int> p_file_indices;
    p_file_indices = parse_file_extension_argument (argc, argv, ".pcd");
    if (p_file_indices.size () > 0)
      filename = argv[p_file_indices[0]];

    std::cout << "fname: " << filename << std::endl;
    // Command line parsing
    parse_argument (argc, argv, "-format", format);
    xyz = find_switch (argc, argv, "-XYZ");
    paused = find_switch (argc, argv, "-paused");
  }

  if (xyz)
  {
    OpenNIGrabFrame<pcl::PointXYZ> grab_frame;
    grab_frame.setOptions (filename, format, paused);
    grab_frame.run ();
  }
  else
  {
    OpenNIGrabFrame<pcl::PointXYZRGBA> grab_frame;
    grab_frame.setOptions (filename, format, paused);
    grab_frame.run ();
  }
  return (0);
}

<|endoftext|>


<commit_before>#include "acmacs-base/acmacsd.hh"
#include "acmacs-base/argv.hh"
#include "acmacs-base/read-file.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-map-draw/draw.hh"
#include "acmacs-map-draw/mod-applicator.hh"
#include "acmacs-map-draw/setup-dbs.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str>       apply{*this, "apply", desc{"json array to use as \"apply\", e.g. [\"all_grey\",\"egg\",\"clades\",\"labels\"]"}};
    option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    option<bool>      clade{*this, "clade"};
    option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    option<bool>      flip_ew{*this, "flip-ew"};
    option<bool>      flip_ns{*this, "flip-ns"};
    option<str_array> settings_files{*this, 's'};
    option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};
    option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0UL}};
    option<bool>      open{*this, "open"};
    option<bool>      ql{*this, "ql"};
    option<bool>      verbose{*this, 'v', "verbose"};

    argument<str> chart{*this, arg_name{"chart.ace"}, mandatory};
    argument<str> output_pdf{*this, arg_name{"output.pdf"}};
};

static int draw(const Options& opt);

int main(int argc, char* const argv[])
{
    int exit_code = 1;
    try {
        Options opt(argc, argv);
        exit_code = draw(opt);
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

int draw(const Options& opt)
{
    using namespace std::string_view_literals;

    setup_dbs(opt.db_dir, opt.verbose);

    ChartDraw chart_draw(std::make_shared<acmacs::chart::ChartModify>(acmacs::chart::import_from_file(opt.chart)), opt.projection);
    if (!opt.previous->empty())
        chart_draw.previous_chart(acmacs::chart::import_from_file(opt.previous));

    rjson::value settings{rjson::object{{"apply", rjson::array{"title"}}}};
    for (const auto& settings_file_name : {"acmacs-map-draw.json"sv}) {
        if (const auto filename = fmt::format("{}/share/conf/{}", acmacs::acmacsd_root(), settings_file_name); fs::exists(filename))
            settings.update(rjson::parse_file(filename, rjson::remove_comments::no));
        else
            fmt::print(stderr, "WARNING: cannot load \"{}\": file not found\n", filename);
    }

    bool settings_loaded = false;
    for (auto fn : *opt.settings_files) {
        if (opt.verbose)
            fmt::print(stderr, "DEBUG: reading settings from {}\n", fn);
        try {
            settings.update(rjson::parse_file(fn, rjson::remove_comments::no));
        }
        catch (std::exception& err) {
            throw std::runtime_error(string::concat(fn, ':', err.what()));
        }
        settings_loaded = true;
    }
    // std::cerr << "DEBUG: loaded settings\n" << settings.to_json_pp() << '\n';

    if (!opt.apply->empty() || !opt.apply_from->empty()) {
        const auto new_apply = !opt.apply->empty() ? rjson::parse_string(opt.apply) : rjson::parse_string(acmacs::file::ifstream(opt.apply_from).read());
        if (opt.verbose)
            fmt::print(stderr, "DEBUG: new apply {}\n", new_apply);
        if (settings_loaded) {
            // if (const auto& old_apply_const = settings["apply"]; !old_apply_const.is_null()) {
            //     auto& old_apply = const_cast<rjson::value&>(old_apply_const);
            //     if (opt.verbose)
            //         std::cerr << "DEBUG: old apply " << old_apply << '\n';
            //     rjson::for_each(new_apply, [&old_apply](const rjson::value& element) { old_apply.append(element); });
            // }
            // else
                settings["apply"] = new_apply;
        }
        else {
            settings["apply"] = new_apply;
        }
    }
    else if (opt.clade) {
        settings["apply"] = rjson::array{"size_reset", "all_grey", "egg", "clades", "vaccines", "title"};
    }

    if (opt.point_scale.has_value()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "point_scale"}, {"scale", static_cast<double>(opt.point_scale)}, {"outline_scale", 1.0}});
    }

    if (opt.flip_ew) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ew"}});
    }
    if (opt.flip_ns) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ns"}});
    }
    if (opt.rotate_degrees.has_value()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(opt.rotate_degrees)}});
    }

    try {
        apply_mods(chart_draw, settings["apply"], settings);
    }
    catch (std::exception& err) {
        // throw std::runtime_error{"Cannot apply " + rjson::to_string(settings["apply"]) + ": " + err.what() + "\n settings:\n" + rjson::pretty(settings, rjson::emacs_indent::no) + '\n'};
        const auto msg = rjson::to_string(settings["apply"]).substr(0, 200);
        const auto add_double_quotes = (std::count(std::begin(msg), std::end(msg), '"') % 2) == 1 ? "\"" : "";
        throw std::runtime_error{fmt::format(": {}   cannot apply: {} ... {}", err.what(), msg, add_double_quotes)};
    }

    chart_draw.calculate_viewport();

    if (opt.output_pdf->empty()) {
        acmacs::file::temp output(static_cast<std::string>(fs::path(*opt.chart).stem()) + "--p" + std::to_string(opt.projection), ".pdf");
        chart_draw.draw(output, 800, report_time::yes);
        acmacs::quicklook(output, 2);
    }
    else if (*opt.output_pdf == "/dev/null" || opt.output_pdf == "/") { // do not generate pdf
    }
    else {
        chart_draw.draw(opt.output_pdf, 800, report_time::yes);
        acmacs::open_or_quicklook(opt.open, opt.ql, opt.output_pdf, 2);
    }

    if (!opt.save->empty())
        chart_draw.save(opt.save, opt.program_name());

    return 0;

} // draw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>minor fix<commit_after>#include "acmacs-base/acmacsd.hh"
#include "acmacs-base/argv.hh"
#include "acmacs-base/read-file.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-map-draw/draw.hh"
#include "acmacs-map-draw/mod-applicator.hh"
#include "acmacs-map-draw/setup-dbs.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str>       apply{*this, "apply", desc{"json array to use as \"apply\", e.g. [\"all_grey\",\"egg\",\"clades\",\"labels\"]"}};
    option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    option<bool>      clade{*this, "clade"};
    option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    option<bool>      flip_ew{*this, "flip-ew"};
    option<bool>      flip_ns{*this, "flip-ns"};
    option<str_array> settings_files{*this, 's'};
    option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};
    option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0UL}};
    option<bool>      open{*this, "open"};
    option<bool>      ql{*this, "ql"};
    option<bool>      verbose{*this, 'v', "verbose"};

    argument<str> chart{*this, arg_name{"chart.ace"}, mandatory};
    argument<str> output_pdf{*this, arg_name{"output.pdf"}};
};

static int draw(const Options& opt);

int main(int argc, char* const argv[])
{
    int exit_code = 1;
    try {
        Options opt(argc, argv);
        exit_code = draw(opt);
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

int draw(const Options& opt)
{
    using namespace std::string_view_literals;

    setup_dbs(opt.db_dir, opt.verbose);

    ChartDraw chart_draw(std::make_shared<acmacs::chart::ChartModify>(acmacs::chart::import_from_file(opt.chart)), opt.projection);
    if (!opt.previous->empty())
        chart_draw.previous_chart(acmacs::chart::import_from_file(opt.previous));

    rjson::value settings{rjson::object{{"apply", rjson::array{"title"}}}};
    for (const auto& settings_file_name : {"acmacs-map-draw.json"sv}) {
        if (const auto filename = fmt::format("{}/share/conf/{}", acmacs::acmacsd_root(), settings_file_name); fs::exists(filename))
            settings.update(rjson::parse_file(filename, rjson::remove_comments::no));
        else
            fmt::print(stderr, "WARNING: cannot load \"{}\": file not found\n", filename);
    }

    bool settings_loaded = false;
    for (auto fn : *opt.settings_files) {
        if (opt.verbose)
            fmt::print(stderr, "DEBUG: reading settings from {}\n", fn);
        try {
            settings.update(rjson::parse_file(fn, rjson::remove_comments::no));
        }
        catch (std::exception& err) {
            throw std::runtime_error(string::concat(fn, ':', err.what()));
        }
        settings_loaded = true;
    }
    // std::cerr << "DEBUG: loaded settings\n" << settings.to_json_pp() << '\n';

    if (!opt.apply->empty() || !opt.apply_from->empty()) {
        const auto new_apply = !opt.apply->empty() ? rjson::parse_string(opt.apply) : rjson::parse_string(acmacs::file::ifstream(opt.apply_from).read());
        if (opt.verbose)
            fmt::print(stderr, "DEBUG: new apply {}\n", new_apply);
        if (settings_loaded) {
            // if (const auto& old_apply_const = settings["apply"]; !old_apply_const.is_null()) {
            //     auto& old_apply = const_cast<rjson::value&>(old_apply_const);
            //     if (opt.verbose)
            //         std::cerr << "DEBUG: old apply " << old_apply << '\n';
            //     rjson::for_each(new_apply, [&old_apply](const rjson::value& element) { old_apply.append(element); });
            // }
            // else
                settings["apply"] = new_apply;
        }
        else {
            settings["apply"] = new_apply;
        }
    }
    else if (opt.clade) {
        settings["apply"] = rjson::array{"size_reset", "all_grey", "egg", "clades", "vaccines", "title"};
    }

    if (opt.point_scale.has_value()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "point_scale"}, {"scale", static_cast<double>(opt.point_scale)}, {"outline_scale", 1.0}});
    }

    if (opt.flip_ew) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ew"}});
    }
    if (opt.flip_ns) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ns"}});
    }
    if (opt.rotate_degrees.has_value()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(opt.rotate_degrees)}});
    }

    try {
        apply_mods(chart_draw, settings["apply"], settings);
    }
    catch (std::exception& err) {
        // throw std::runtime_error{"Cannot apply " + rjson::to_string(settings["apply"]) + ": " + err.what() + "\n settings:\n" + rjson::pretty(settings, rjson::emacs_indent::no) + '\n'};
        const auto msg = rjson::to_string(settings["apply"]).substr(0, 200);
        const auto add_double_quotes = (std::count(std::begin(msg), std::end(msg), '"') % 2) == 1 ? "\"" : "";
        throw std::runtime_error{fmt::format(": {}   cannot apply: {} ... {}", err.what(), msg, add_double_quotes)};
    }

    chart_draw.calculate_viewport();

    if (opt.output_pdf->empty()) {
        acmacs::file::temp output(static_cast<std::string>(fs::path(*opt.chart).stem()) + "--p" + std::to_string(opt.projection), ".pdf");
        chart_draw.draw(output, 800, report_time::yes);
        acmacs::quicklook(output, 2);
    }
    else if (*opt.output_pdf == "/dev/null" || opt.output_pdf == "/") { // do not generate pdf
    }
    else {
        chart_draw.draw(opt.output_pdf, 800, report_time::yes);
        acmacs::open_or_quicklook(opt.open, opt.ql, opt.output_pdf);
    }

    if (!opt.save->empty())
        chart_draw.save(opt.save, opt.program_name());

    return 0;

} // draw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "Sofa_test.h"

#include "SofaLoader/MeshVTKLoader.h"

namespace sofa {

struct MeshVTKLoader_test : public Sofa_test<>, public component::loader::MeshVTKLoader
{

    MeshVTKLoader_test()
    {
    }

};

TEST_F(MeshVTKLoader_test, detectFileType)
{
    ASSERT_EQ(component::loader::MeshVTKLoader::LEGACY, this->detectFileType(sofa::helper::system::DataRepository.getFile("mesh/liver.vtk").c_str()));
    ASSERT_EQ(component::loader::MeshVTKLoader::XML, this->detectFileType(sofa::helper::system::DataRepository.getFile("mesh/Armadillo_Tetra_4406.vtu").c_str()));
}

}// namespace sofa
<commit_msg>test legacy and XML loader<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "Sofa_test.h"

#include "SofaLoader/MeshVTKLoader.h"

namespace sofa {

struct MeshVTKLoader_test : public Sofa_test<>, public component::loader::MeshVTKLoader
{

    MeshVTKLoader_test()
    {}

    void test_load(std::string const& filename, unsigned nbPoints, unsigned nbEdges, unsigned nbTriangles, unsigned nbQuads, unsigned nbPolygons, unsigned nbTetrahedra, unsigned nbHexahedra)
    {
        setFilename(filename);
        load();
        EXPECT_EQ(nbPoints, positions.getValue().size());
        EXPECT_EQ(nbEdges, edges.getValue().size());
        EXPECT_EQ(nbTriangles, triangles.getValue().size());
        EXPECT_EQ(nbQuads, quads.getValue().size());
        EXPECT_EQ(nbPolygons, polygons.getValue().size());
        EXPECT_EQ(nbTetrahedra, tetrahedra.getValue().size());
        EXPECT_EQ(nbHexahedra, hexahedra.getValue().size());
    }

};

TEST_F(MeshVTKLoader_test, detectFileType)
{
    ASSERT_EQ(component::loader::MeshVTKLoader::LEGACY, detectFileType(sofa::helper::system::DataRepository.getFile("mesh/liver.vtk").c_str()));
    ASSERT_EQ(component::loader::MeshVTKLoader::XML, detectFileType(sofa::helper::system::DataRepository.getFile("mesh/Armadillo_Tetra_4406.vtu").c_str()));
}

TEST_F(MeshVTKLoader_test, loadLegacy)
{
    test_load(sofa::helper::system::DataRepository.getFile("mesh/liver.vtk"), 5008, 0, 10000, 0, 0, 0, 0);
}

TEST_F(MeshVTKLoader_test, loadXML)
{
    test_load(sofa::helper::system::DataRepository.getFile("mesh/Armadillo_Tetra_4406.vtu"), 1446, 0, 0, 0, 0, 4406, 0);
}

}// namespace sofa
<|endoftext|>