<commit_before>// gui.cpp
// GUI for mancala game, using gtkmm
// Copyright Matthew Chandler 2012

#include <iostream>

#include <gdkmm/general.h>
#include <glibmm/fileutils.h>
#include <gtkmm/messagedialog.h>

#include "gui.h"

Mancala_draw::Mancala_draw(Mancala_win * Win): win(Win)
{
    add_events(Gdk::BUTTON_PRESS_MASK);
    signal_button_press_event().connect(sigc::mem_fun(*this, &Mancala_draw::mouse_down));
    try
    {
        bg_store = Gdk::Pixbuf::create_from_file("img/bg_store.png");
    }
    catch(const Glib::FileError& ex)
    {
        std::cerr<<"FileError: "<<ex.what()<<std::endl;
    }
    catch(const Gdk::PixbufError& ex)
    {
        std::cerr<<"PixbufError: "<< ex.what()<<std::endl;
    }
}

bool Mancala_draw::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
    Gtk::Allocation alloc = get_allocation();

    cr->save();
    cr->scale(alloc.get_width() / (double)bg_store->get_width() / (win->b.num_bowls + 2), alloc.get_height() / (double)bg_store->get_height());
    Gdk::Cairo::set_source_pixbuf(cr, bg_store);
    cr->paint();
    cr->restore();

    cr->save();
    cr->translate(alloc.get_width() * (1.0 - 1.0 / (win->b.num_bowls + 2)), 0);
    cr->scale(alloc.get_width() / (double)bg_store->get_width() / (win->b.num_bowls + 2), alloc.get_height() / (double)bg_store->get_height());
    Gdk::Cairo::set_source_pixbuf(cr, bg_store);
    cr->paint();
    cr->restore();

    cr->set_source_rgb(1.0, 0.0, 0.0);

    for(int i = 1; i < win->b.num_bowls + 2; ++i)
    {
        cr->move_to(alloc.get_width() * (i / (double)(win->b.num_bowls + 2)), 0.0);
        cr->line_to(alloc.get_width() * (i / (double)(win->b.num_bowls + 2)), alloc.get_height());
        cr->stroke();
    }

    cr->move_to(1.0 / (win->b.num_bowls + 2) * alloc.get_width(), alloc.get_height() * .5);
    cr->line_to((1.0 -  1.0 / (win->b.num_bowls + 2)) * alloc.get_width(), alloc.get_height() * .5);
    cr->stroke();

    Pango::FontDescription font("Monospace");
    font.set_size(std::min(alloc.get_width(), alloc.get_height()) * .1 * Pango::SCALE);
    int tex_w, tex_h;
    cr->set_source_rgb(0.0, 0.0, 0.0);

    // draw # for left store
    std::ostringstream l_store_str;
    l_store_str<<win->b.bowls[win->b.p2_store].count;
    Glib::RefPtr<Pango::Layout> l_store_txt = create_pango_layout(l_store_str.str());
    l_store_txt->set_font_description(font);
    l_store_txt->get_pixel_size(tex_w, tex_h);
    cr->move_to(alloc.get_width() * 1.0 / (2.0 * (win->b.num_bowls + 2)) - tex_w * .5, (alloc.get_height() - tex_h) * .5);
    l_store_txt->show_in_cairo_context(cr);

    // draw # for right store
    std::ostringstream r_store_str;
    r_store_str<<win->b.bowls[win->b.p1_store].count;
    Glib::RefPtr<Pango::Layout> r_store_txt = create_pango_layout(r_store_str.str());
    r_store_txt->set_font_description(font);
    r_store_txt->get_pixel_size(tex_w, tex_h);
    cr->move_to(alloc.get_width() * (1.0 - 1.0 / (2.0 * (win->b.num_bowls + 2))) - tex_w * .5, (alloc.get_height() - tex_h) * .5);
    r_store_txt->show_in_cairo_context(cr);

    // draw #s for bowls
    for(int i = 0; i < win->b.num_bowls; ++i)
    {
        //upper row
        std::ostringstream upper_str;
        upper_str<<win->b.bowls[win->b.bowls[win->b.p1_start + i].across].count;
        Glib::RefPtr<Pango::Layout> upper_txt = create_pango_layout(upper_str.str());
        upper_txt->set_font_description(font);
        upper_txt->get_pixel_size(tex_w, tex_h);
        cr->move_to(alloc.get_width() * (2 * i + 3) / (2.0 * (win->b.num_bowls + 2)) - tex_w * .5, alloc.get_height() * .25 - tex_h * .5);
        upper_txt->show_in_cairo_context(cr);

        //lower row
        std::ostringstream lower_str;
        lower_str<<win->b.bowls[win->b.p1_start + i].count;
        Glib::RefPtr<Pango::Layout> lower_txt = create_pango_layout(lower_str.str());
        lower_txt->set_font_description(font);
        lower_txt->get_pixel_size(tex_w, tex_h);
        cr->move_to(alloc.get_width() * (2 * i + 3) / (2.0 * (win->b.num_bowls + 2)) - tex_w * .5, alloc.get_height() * .75 - tex_h * .5);
        lower_txt->show_in_cairo_context(cr);
    }

    return true;
}

bool Mancala_draw::mouse_down(GdkEventButton * event)
{
    Gtk::Allocation alloc = get_allocation();

    int grid_x = (int)((win->b.num_bowls + 2) * event->x / alloc.get_width());
    int grid_y = (event->y / alloc.get_height() <= .5)? 0 : 1;

    if(grid_x > 0 && grid_x < win->b.num_bowls + 1 && !win->b.finished())
    {
        if(win->player == 1 && grid_y == 1)
        {
            win->move(grid_x - 1);
        }

        if(win->player == 2 && grid_y == 0);
    }

    return true;
}

Mancala_win::Mancala_win():
    main_box(Gtk::ORIENTATION_VERTICAL),
    hint_box(Gtk::ORIENTATION_HORIZONTAL),
    new_game_box(Gtk::ORIENTATION_HORIZONTAL),
    hint_b("Hint"),
    new_game_b("New Game"),
    draw(this),
    player(1)
{
    // set window properties
    set_border_width(10);
    set_default_size(800,400);
    set_title("Mancala");

    // add widgets to contatiners
    add(main_box);
    main_box.pack_start(player_label, Gtk::PACK_SHRINK);

    main_box.pack_start(draw);

    main_box.pack_end(new_game_box, Gtk::PACK_SHRINK);
    new_game_box.pack_start(new_game_b, Gtk::PACK_EXPAND_PADDING);

    main_box.pack_end(hint_box, Gtk::PACK_SHRINK);
    hint_box.pack_start(hint_b, Gtk::PACK_EXPAND_PADDING);

    hint_b.signal_clicked().connect(sigc::mem_fun(*this, &Mancala_win::hint));

    new_game_b.signal_clicked().connect(sigc::mem_fun(*this, &Mancala_win::new_game));

    // set all labels for number of seeds
    update_board();

    show_all_children();
}

// make a move (called by button signals)
void Mancala_win::move(const int i)
{
    if(b.bowls[b.p1_start + i].count <= 0)
        return;

    if(!b.move(i))
    {
        //player = (player == 1)? 2 : 1;
        b.swapsides();

        int ai_move = 0;
        do
        {
            if(b.finished())
                break;
            ai_move = choosemove(b);
        }
        while(b.move(ai_move));

        b.swapsides();
    }

    update_board();

    // check to see if the game is over
    if(b.finished())
    {
        Glib::ustring msg;
        // check for a tie
        if(b.bowls[b.p1_store].count == b.bowls[b.p2_store].count)
            msg = "Tie";

        // determine the current player, and then see if they won or lost
        else if(player == 1)
            if(b.bowls[b.p1_store].count > b.bowls[b.p2_store].count)
                msg = "Player 1 wins";
            else
                msg = "Player 2 wins";
        else
            if(b.bowls[b.p1_store].count > b.bowls[b.p2_store].count)
                msg = "Player 2 wins";
            else
                msg = "Player 1 wins";

        // was the win full of win?
        if(abs(b.bowls[b.p1_store].count - b.bowls[b.p2_store].count) >= 10)
            msg += "\nFATALITY";

        //create and show a dialog announcing the winner
        Gtk::MessageDialog dlg(*this, "Game Over");
        dlg.set_secondary_text(msg);
        dlg.run();

        //make the game unplayable
        hint_b.set_state(Gtk::STATE_INSENSITIVE);
    }
}

// get a hint, will highlight a bowl
void Mancala_win::hint()
{
    // use AI function to find best move
    int bestmove = choosemove(b);
}

// start a new game
void Mancala_win::new_game()
{
    hint_b.set_state(Gtk::STATE_NORMAL);
    b = Board();
    update_board();
}

// update the numbers for each bowl / store
void Mancala_win::update_board()
{
    // Show who's turn it is TODO: ugly
    if(player == 1)
        player_label.set_text("Player 1");
    else
        player_label.set_text("Player 2");
    draw.queue_draw();
}
<commit_msg>cleaned and optimized drawing scaling math<commit_after>// gui.cpp
// GUI for mancala game, using gtkmm
// Copyright Matthew Chandler 2012

#include <iostream>

#include <gdkmm/general.h>
#include <glibmm/fileutils.h>
#include <gtkmm/messagedialog.h>

#include "gui.h"

Mancala_draw::Mancala_draw(Mancala_win * Win): win(Win)
{
    add_events(Gdk::BUTTON_PRESS_MASK);
    signal_button_press_event().connect(sigc::mem_fun(*this, &Mancala_draw::mouse_down));
    try
    {
        bg_store = Gdk::Pixbuf::create_from_file("img/bg_store.png");
    }
    catch(const Glib::FileError& ex)
    {
        std::cerr<<"FileError: "<<ex.what()<<std::endl;
    }
    catch(const Gdk::PixbufError& ex)
    {
        std::cerr<<"PixbufError: "<< ex.what()<<std::endl;
    }
}

bool Mancala_draw::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
    int num_cells = win->b.num_bowls + 2;
    double inv_num_cells = 1.0 / num_cells;

    Gtk::Allocation alloc = get_allocation();

    cr->save();
    cr->scale(alloc.get_width() / (double)bg_store->get_width() * inv_num_cells, alloc.get_height() / (double)bg_store->get_height());
    Gdk::Cairo::set_source_pixbuf(cr, bg_store);
    cr->paint();
    cr->restore();

    cr->save();
    cr->translate(alloc.get_width() * (1.0 - inv_num_cells), 0);
    cr->scale(alloc.get_width() / (double)bg_store->get_width() * inv_num_cells, alloc.get_height() / (double)bg_store->get_height());
    Gdk::Cairo::set_source_pixbuf(cr, bg_store);
    cr->paint();
    cr->restore();

    cr->set_source_rgb(1.0, 0.0, 0.0);

    for(int i = 1; i < num_cells; ++i)
    {
        cr->move_to(alloc.get_width() * (i * inv_num_cells), 0.0);
        cr->line_to(alloc.get_width() * (i * inv_num_cells), alloc.get_height());
        cr->stroke();
    }

    cr->move_to(inv_num_cells * alloc.get_width(), alloc.get_height() * .5);
    cr->line_to((1.0 -  inv_num_cells) * alloc.get_width(), alloc.get_height() * .5);
    cr->stroke();

    Pango::FontDescription font("Monospace");
    font.set_size(std::min(alloc.get_width(), alloc.get_height()) * .1 * Pango::SCALE);
    int tex_w, tex_h;
    cr->set_source_rgb(0.0, 0.0, 0.0);

    // draw # for left store
    std::ostringstream l_store_str;
    l_store_str<<win->b.bowls[win->b.p2_store].count;
    Glib::RefPtr<Pango::Layout> l_store_txt = create_pango_layout(l_store_str.str());
    l_store_txt->set_font_description(font);
    l_store_txt->get_pixel_size(tex_w, tex_h);
    cr->move_to(alloc.get_width() * .5 * inv_num_cells - tex_w * .5, (alloc.get_height() - tex_h) * .5);
    l_store_txt->show_in_cairo_context(cr);

    // draw # for right store
    std::ostringstream r_store_str;
    r_store_str<<win->b.bowls[win->b.p1_store].count;
    Glib::RefPtr<Pango::Layout> r_store_txt = create_pango_layout(r_store_str.str());
    r_store_txt->set_font_description(font);
    r_store_txt->get_pixel_size(tex_w, tex_h);
    cr->move_to(alloc.get_width() * (1.0 - .5 * inv_num_cells) - tex_w * .5, (alloc.get_height() - tex_h) * .5);
    r_store_txt->show_in_cairo_context(cr);

    // draw #s for bowls
    for(int i = 0; i < win->b.num_bowls; ++i)
    {
        //upper row
        std::ostringstream upper_str;
        upper_str<<win->b.bowls[win->b.bowls[win->b.p1_start + i].across].count;
        Glib::RefPtr<Pango::Layout> upper_txt = create_pango_layout(upper_str.str());
        upper_txt->set_font_description(font);
        upper_txt->get_pixel_size(tex_w, tex_h);
        cr->move_to(alloc.get_width() * (2 * i + 3) * .5 * inv_num_cells - tex_w * .5, alloc.get_height() * .25 - tex_h * .5);
        upper_txt->show_in_cairo_context(cr);

        //lower row
        std::ostringstream lower_str;
        lower_str<<win->b.bowls[win->b.p1_start + i].count;
        Glib::RefPtr<Pango::Layout> lower_txt = create_pango_layout(lower_str.str());
        lower_txt->set_font_description(font);
        lower_txt->get_pixel_size(tex_w, tex_h);
        cr->move_to(alloc.get_width() * (2 * i + 3) * .5 * inv_num_cells - tex_w * .5, alloc.get_height() * .75 - tex_h * .5);
        lower_txt->show_in_cairo_context(cr);
    }

    return true;
}

bool Mancala_draw::mouse_down(GdkEventButton * event)
{
    Gtk::Allocation alloc = get_allocation();

    int grid_x = (int)((win->b.num_bowls + 2) * event->x / alloc.get_width());
    int grid_y = (event->y / alloc.get_height() <= .5)? 0 : 1;

    if(grid_x > 0 && grid_x < win->b.num_bowls + 1 && !win->b.finished())
    {
        if(win->player == 1 && grid_y == 1)
        {
            win->move(grid_x - 1);
        }

        if(win->player == 2 && grid_y == 0);
    }

    return true;
}

Mancala_win::Mancala_win():
    main_box(Gtk::ORIENTATION_VERTICAL),
    hint_box(Gtk::ORIENTATION_HORIZONTAL),
    new_game_box(Gtk::ORIENTATION_HORIZONTAL),
    hint_b("Hint"),
    new_game_b("New Game"),
    draw(this),
    player(1)
{
    // set window properties
    set_border_width(10);
    set_default_size(800,400);
    set_title("Mancala");

    // add widgets to contatiners
    add(main_box);
    main_box.pack_start(player_label, Gtk::PACK_SHRINK);

    main_box.pack_start(draw);

    main_box.pack_end(new_game_box, Gtk::PACK_SHRINK);
    new_game_box.pack_start(new_game_b, Gtk::PACK_EXPAND_PADDING);

    main_box.pack_end(hint_box, Gtk::PACK_SHRINK);
    hint_box.pack_start(hint_b, Gtk::PACK_EXPAND_PADDING);

    hint_b.signal_clicked().connect(sigc::mem_fun(*this, &Mancala_win::hint));

    new_game_b.signal_clicked().connect(sigc::mem_fun(*this, &Mancala_win::new_game));

    // set all labels for number of seeds
    update_board();

    show_all_children();
}

// make a move (called by button signals)
void Mancala_win::move(const int i)
{
    if(b.bowls[b.p1_start + i].count <= 0)
        return;

    if(!b.move(i))
    {
        //player = (player == 1)? 2 : 1;
        b.swapsides();

        int ai_move = 0;
        do
        {
            if(b.finished())
                break;
            ai_move = choosemove(b);
        }
        while(b.move(ai_move));

        b.swapsides();
    }

    update_board();

    // check to see if the game is over
    if(b.finished())
    {
        Glib::ustring msg;
        // check for a tie
        if(b.bowls[b.p1_store].count == b.bowls[b.p2_store].count)
            msg = "Tie";

        // determine the current player, and then see if they won or lost
        else if(player == 1)
            if(b.bowls[b.p1_store].count > b.bowls[b.p2_store].count)
                msg = "Player 1 wins";
            else
                msg = "Player 2 wins";
        else
            if(b.bowls[b.p1_store].count > b.bowls[b.p2_store].count)
                msg = "Player 2 wins";
            else
                msg = "Player 1 wins";

        // was the win full of win?
        if(abs(b.bowls[b.p1_store].count - b.bowls[b.p2_store].count) >= 10)
            msg += "\nFATALITY";

        //create and show a dialog announcing the winner
        Gtk::MessageDialog dlg(*this, "Game Over");
        dlg.set_secondary_text(msg);
        dlg.run();

        //make the game unplayable
        hint_b.set_state(Gtk::STATE_INSENSITIVE);
    }
}

// get a hint, will highlight a bowl
void Mancala_win::hint()
{
    // use AI function to find best move
    int bestmove = choosemove(b);
}

// start a new game
void Mancala_win::new_game()
{
    hint_b.set_state(Gtk::STATE_NORMAL);
    b = Board();
    update_board();
}

// update the numbers for each bowl / store
void Mancala_win::update_board()
{
    // Show who's turn it is TODO: ugly
    if(player == 1)
        player_label.set_text("Player 1");
    else
        player_label.set_text("Player 2");
    draw.queue_draw();
}
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                     The Descent map loader
//
// NAME         : Hog
// PURPOSE      : Providing a decoder and wrapper for the Descent .HOG format.
// COPYRIGHT    : (c) 2011 Sean Donnellan. All Rights Reserved.
// AUTHORS      : Sean Donnellan (darkdonno@gmail.com)
// DESCRIPTION  : A decoder for the HOG file format that is used by Parallax
//                Software in the computer game, Descent.
//
//                The file format is as follows:
//
//                - Magic number which is 3 bytes and corresponding to the
//                  string "DHF"

//                - A series of files which are preceded by a short header,
//                  which describes the name of the file and the numer of bytes.
//
//                 | "DHF" - 3 bytes
//                 |---------------- Start of the first file
//                 | filename - 13 bytes
//                 | size - 4 bytes
//                 | data - the size of this part is by the size before it.
//                 |---------------- The next header/file comes straight after.
//                 | filename - 13 bytes
//                 | size - 4 bytes
//                 | data - the size of this part is by the size before it.
//
//===----------------------------------------------------------------------===//

/////
// Developer notes
// Different file formats: http://www.descent2.com/ddn/kb/files/
//
/////

#include "cube.hpp"
#include "hogreader.hpp"
#include "hogiterator.hpp"
#include "rdl.hpp"

#include <memory>
#include <iterator>
#include <utility>
#include <vector>

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _MSC_VER
static_assert(sizeof(uint8_t) == 1,
              "The size of uint8_t is incorrect it must be 1-byte");
static_assert(sizeof(uint32_t) == 4,
              "The size of uint32_t is incorrect it must be 4-bytes");
static_assert(sizeof(uint8_t) == sizeof(char),
              "The size of a char must be 1-byte");
#endif

// The 3-byte MAGIC number at the start of the file format used to identifiy the
// file as being a Descent HOG file.
static uint8_t magic[3] = { 'D', 'H', 'F' };

HogReader::iterator HogReader::begin()
{
  // Sync back up to the start just after the magic number.
  if (IsValid())
  {
    fseek(myFile, sizeof(magic), SEEK_SET);
    if (fread(&myChildFile.name, 13, 1, myFile) != 1) HogReaderIterator();
    if (fread(&myChildFile.size, 4, 1, myFile) != 1) HogReaderIterator();
  }

  return HogReaderIterator(*this);
}

HogReader::iterator HogReader::end()
{
  return HogReaderIterator();
}

HogReader::HogReader(const char* filename) : myFile(nullptr), hasReadFile(false)
{
  myFile = fopen(filename, "rb");
  myChildFile.name[0] = '\0';
  myChildFile.size = 0;
  if (!myFile) return;

  const size_t count = 1;
  if (fread(myHeader, sizeof(myHeader), count, myFile) != count)
  {
    myHeader[0] = '\0'; // Failed to load.
    return;
  }

  // Read in the header for the first file.
  if (IsValid())
  {
    if (fread(&myChildFile.name, 13, 1, myFile) != 1) return;
    if (fread(&myChildFile.size, 4, 1, myFile) != 1) return;
  }
}

HogReader::~HogReader()
{
  if (myFile) fclose(myFile);
}

bool HogReader::IsValid() const
{
  if (!myFile) return false;
  return memcmp(myHeader, magic, 3) == 0;
}

bool HogReader::NextFile()
{
  // Skip the current file.
  if (feof(myFile)) return false;

  if (!hasReadFile)
  {
    // The data for the current file has not been read so skip over the data
    // section
    // for the file.

    if (fseek(myFile, myChildFile.size, SEEK_CUR) != 0) return false;
  }

  // Read in the header for the next file.
  if (fread(&myChildFile.name, 13, 1, myFile) != 1) return false;
  if (fread(&myChildFile.size, 4, 1, myFile) != 1) return false;

  hasReadFile = false; // We haven't read the next file yet.
  return true;
}

const char* HogReader::CurrentFileName() const
{
  return myChildFile.name;
}

unsigned int HogReader::CurrentFileSize() const
{
  return myChildFile.size;
}

std::vector<uint8_t> HogReader::CurrentFile()
{
  std::vector<uint8_t> fileData;

  // TODO: Allow this to seek-back and read it again.
  //
  // For now ensure the current file hasn't been read yet.
  assert(!hasReadFile);

  // Skip the current file.
  if (feof(myFile)) return fileData;

  const unsigned int size = CurrentFileSize(); // Size in bytes
  fileData.resize(size);

  if (fread(fileData.data(), size, 1, myFile) != 1)
  {
    fileData.clear();
  }

  return fileData;
}

#include <algorithm>
#include <iostream>
#include <string>

struct Quad
{
  size_t a;
  size_t b;
  size_t c;
  size_t d;
};

void Quads(const Cube& cube, std::vector<Quad>* quads)
{
  const uint16_t* const vertices = cube.vertices;

  // Vertices:
  // 0 - left, front, top
  // 1 - left, front, bottom
  // 2 - right, front, bottom
  // 3 - right, front, top
  // 4 - left, back, top
  // 5 - left, back, bottom
  // 6 - right, back, bottom
  // 7 - right, back, top

  // Neighbours:
  enum Neighbour
  {
    Left,
    Top,
    Right,
    Bottom,
    Back,
    Front
  };

  if (cube.neighbors[Left] == -1)
  {
    const Quad quad = { vertices[0], vertices[1], vertices[5], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Top] == -1)
  {
    const Quad quad = { vertices[0], vertices[3], vertices[7], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Right] == -1)
  {
    const Quad quad = { vertices[2], vertices[3], vertices[7], vertices[6] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Bottom] == -1)
  {
    const Quad quad = { vertices[1], vertices[2], vertices[6], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Front] == -1)
  {
    const Quad quad = { vertices[0], vertices[1], vertices[2], vertices[3] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Back] == -1)
  {
    const Quad quad = { vertices[4], vertices[5], vertices[6], vertices[7] };
    quads->push_back(quad);
  }
}

std::vector<Quad> Quads(const std::vector<Cube>& Cubes)
{
  // Generate quads from the sides of the cubes.
  std::vector<Quad> quads;
  for (auto cube = Cubes.cbegin(), cubeEnd = Cubes.cend(); cube != cubeEnd;
       ++cube)
  {
    Quads(*cube, &quads);
  }
  return quads;
}

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    printf("usage: %s filename\n", argv[0]);
    return 1;
  }

  HogReader reader(argv[1]);
  if (!reader.IsValid())
  {
    fprintf(stderr, "error to open the hog file");
    return 1;
  }

  enum Mode
  {
    ListAllFiles,
    ExportToPly,
    Debug // Performs some other task during development.
  };

  const Mode mode = ExportToPly;
  if (mode == ListAllFiles)
  {
    printf("%-13s Size\n", "Name");
    printf("=====================\n");
    std::for_each(reader.begin(), reader.end(),
                  [](HogReader::iterator::value_type item)
    { printf("%-13s %d\n", item.name, item.size); });
  }
  else if (mode == ExportToPly)
  {
    auto file = std::find_if(reader.begin(), reader.end(),
                             [](const HogReader::iterator::value_type & v)->bool
    { return strcmp(v.name, "level02.rdl") == 0; });

    // TODO: Provide a way to get to the file data from the iterator.
    const auto data = reader.CurrentFile();
    RdlReader rdlReader(data);
    const auto vertices = rdlReader.Vertices();
    const auto cubes = rdlReader.Cubes();
    const auto quads = Quads(cubes);

    const bool verticesOnly = false;

    const size_t faceCount = 0;
    std::cout << "ply" << std::endl;
    std::cout << "format ascii 1.0" << std::endl;
    std::cout << "comment An exported Descent 1 level (" << (*file).name << ")"
              << std::endl;

    std::cout << "element vertex " << vertices.size() << std::endl;
    std::cout << "property float x" << std::endl;
    std::cout << "property float y" << std::endl;
    std::cout << "property float z" << std::endl;
    if (!verticesOnly)
    {
      std::cout << "element face " << quads.size() << std::endl;
      std::cout << "property list uchar int vertex_index" << std::endl;
    }
    std::cout << "end_header" << std::endl;

    std::for_each(vertices.begin(), vertices.end(), [](Vertex v)
    { printf("%f %f %f\n", v.x, v.y, v.z); });

    if (!verticesOnly)
    {
      std::for_each(quads.begin(), quads.end(), [](const Quad& quad)
      { printf("4 %d %d %d %d\n", quad.a, quad.b, quad.c, quad.d); });
    }
  }
  else
  {
    // Iterate over the file list and list all the files which do not
    // end in the 'rdl' file extension.
    const std::string extentionRdl(".rdl");
    std::for_each(reader.begin(), reader.end(),
                  [&reader, &extentionRdl](HogReader::iterator::value_type n)
    {
      const std::string filename(n.name);
      if (!std::equal(extentionRdl.rbegin(), extentionRdl.rend(),
                      filename.rbegin()))
        return;

      printf("File: %s Size: %d\n", n.name, reader.CurrentFileSize());
      const auto data = reader.CurrentFile();
      RdlReader rdlReader(data);

      if (!rdlReader.IsValid()) return;

      // Print out the vertices.
      const auto vertices = rdlReader.Vertices();
      printf("Vertex count: %d\n", vertices.size());
      std::for_each(vertices.begin(), vertices.end(),
                    [](Vertex v)
                    { printf("%16f %16f %16f\n", v.x, v.y, v.z); });
    });
  }
  return 0;
}
<commit_msg>Removed an unused variable.<commit_after>//===----------------------------------------------------------------------===//
//
//                     The Descent map loader
//
// NAME         : Hog
// PURPOSE      : Providing a decoder and wrapper for the Descent .HOG format.
// COPYRIGHT    : (c) 2011 Sean Donnellan. All Rights Reserved.
// AUTHORS      : Sean Donnellan (darkdonno@gmail.com)
// DESCRIPTION  : A decoder for the HOG file format that is used by Parallax
//                Software in the computer game, Descent.
//
//                The file format is as follows:
//
//                - Magic number which is 3 bytes and corresponding to the
//                  string "DHF"

//                - A series of files which are preceded by a short header,
//                  which describes the name of the file and the numer of bytes.
//
//                 | "DHF" - 3 bytes
//                 |---------------- Start of the first file
//                 | filename - 13 bytes
//                 | size - 4 bytes
//                 | data - the size of this part is by the size before it.
//                 |---------------- The next header/file comes straight after.
//                 | filename - 13 bytes
//                 | size - 4 bytes
//                 | data - the size of this part is by the size before it.
//
//===----------------------------------------------------------------------===//

/////
// Developer notes
// Different file formats: http://www.descent2.com/ddn/kb/files/
//
/////

#include "cube.hpp"
#include "hogreader.hpp"
#include "hogiterator.hpp"
#include "rdl.hpp"

#include <memory>
#include <iterator>
#include <utility>
#include <vector>

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _MSC_VER
static_assert(sizeof(uint8_t) == 1,
              "The size of uint8_t is incorrect it must be 1-byte");
static_assert(sizeof(uint32_t) == 4,
              "The size of uint32_t is incorrect it must be 4-bytes");
static_assert(sizeof(uint8_t) == sizeof(char),
              "The size of a char must be 1-byte");
#endif

// The 3-byte MAGIC number at the start of the file format used to identifiy the
// file as being a Descent HOG file.
static uint8_t magic[3] = { 'D', 'H', 'F' };

HogReader::iterator HogReader::begin()
{
  // Sync back up to the start just after the magic number.
  if (IsValid())
  {
    fseek(myFile, sizeof(magic), SEEK_SET);
    if (fread(&myChildFile.name, 13, 1, myFile) != 1) HogReaderIterator();
    if (fread(&myChildFile.size, 4, 1, myFile) != 1) HogReaderIterator();
  }

  return HogReaderIterator(*this);
}

HogReader::iterator HogReader::end()
{
  return HogReaderIterator();
}

HogReader::HogReader(const char* filename) : myFile(nullptr), hasReadFile(false)
{
  myFile = fopen(filename, "rb");
  myChildFile.name[0] = '\0';
  myChildFile.size = 0;
  if (!myFile) return;

  const size_t count = 1;
  if (fread(myHeader, sizeof(myHeader), count, myFile) != count)
  {
    myHeader[0] = '\0'; // Failed to load.
    return;
  }

  // Read in the header for the first file.
  if (IsValid())
  {
    if (fread(&myChildFile.name, 13, 1, myFile) != 1) return;
    if (fread(&myChildFile.size, 4, 1, myFile) != 1) return;
  }
}

HogReader::~HogReader()
{
  if (myFile) fclose(myFile);
}

bool HogReader::IsValid() const
{
  if (!myFile) return false;
  return memcmp(myHeader, magic, 3) == 0;
}

bool HogReader::NextFile()
{
  // Skip the current file.
  if (feof(myFile)) return false;

  if (!hasReadFile)
  {
    // The data for the current file has not been read so skip over the data
    // section
    // for the file.

    if (fseek(myFile, myChildFile.size, SEEK_CUR) != 0) return false;
  }

  // Read in the header for the next file.
  if (fread(&myChildFile.name, 13, 1, myFile) != 1) return false;
  if (fread(&myChildFile.size, 4, 1, myFile) != 1) return false;

  hasReadFile = false; // We haven't read the next file yet.
  return true;
}

const char* HogReader::CurrentFileName() const
{
  return myChildFile.name;
}

unsigned int HogReader::CurrentFileSize() const
{
  return myChildFile.size;
}

std::vector<uint8_t> HogReader::CurrentFile()
{
  std::vector<uint8_t> fileData;

  // TODO: Allow this to seek-back and read it again.
  //
  // For now ensure the current file hasn't been read yet.
  assert(!hasReadFile);

  // Skip the current file.
  if (feof(myFile)) return fileData;

  const unsigned int size = CurrentFileSize(); // Size in bytes
  fileData.resize(size);

  if (fread(fileData.data(), size, 1, myFile) != 1)
  {
    fileData.clear();
  }

  return fileData;
}

#include <algorithm>
#include <iostream>
#include <string>

struct Quad
{
  size_t a;
  size_t b;
  size_t c;
  size_t d;
};

void Quads(const Cube& cube, std::vector<Quad>* quads)
{
  const uint16_t* const vertices = cube.vertices;

  // Vertices:
  // 0 - left, front, top
  // 1 - left, front, bottom
  // 2 - right, front, bottom
  // 3 - right, front, top
  // 4 - left, back, top
  // 5 - left, back, bottom
  // 6 - right, back, bottom
  // 7 - right, back, top

  // Neighbours:
  enum Neighbour
  {
    Left,
    Top,
    Right,
    Bottom,
    Back,
    Front
  };

  if (cube.neighbors[Left] == -1)
  {
    const Quad quad = { vertices[0], vertices[1], vertices[5], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Top] == -1)
  {
    const Quad quad = { vertices[0], vertices[3], vertices[7], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Right] == -1)
  {
    const Quad quad = { vertices[2], vertices[3], vertices[7], vertices[6] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Bottom] == -1)
  {
    const Quad quad = { vertices[1], vertices[2], vertices[6], vertices[4] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Front] == -1)
  {
    const Quad quad = { vertices[0], vertices[1], vertices[2], vertices[3] };
    quads->push_back(quad);
  }

  if (cube.neighbors[Back] == -1)
  {
    const Quad quad = { vertices[4], vertices[5], vertices[6], vertices[7] };
    quads->push_back(quad);
  }
}

std::vector<Quad> Quads(const std::vector<Cube>& Cubes)
{
  // Generate quads from the sides of the cubes.
  std::vector<Quad> quads;
  for (auto cube = Cubes.cbegin(), cubeEnd = Cubes.cend(); cube != cubeEnd;
       ++cube)
  {
    Quads(*cube, &quads);
  }
  return quads;
}

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    printf("usage: %s filename\n", argv[0]);
    return 1;
  }

  HogReader reader(argv[1]);
  if (!reader.IsValid())
  {
    fprintf(stderr, "error to open the hog file");
    return 1;
  }

  enum Mode
  {
    ListAllFiles,
    ExportToPly,
    Debug // Performs some other task during development.
  };

  const Mode mode = ExportToPly;
  if (mode == ListAllFiles)
  {
    printf("%-13s Size\n", "Name");
    printf("=====================\n");
    std::for_each(reader.begin(), reader.end(),
                  [](HogReader::iterator::value_type item)
    { printf("%-13s %d\n", item.name, item.size); });
  }
  else if (mode == ExportToPly)
  {
    auto file = std::find_if(reader.begin(), reader.end(),
                             [](const HogReader::iterator::value_type & v)->bool
    { return strcmp(v.name, "level02.rdl") == 0; });

    // TODO: Provide a way to get to the file data from the iterator.
    const auto data = reader.CurrentFile();
    RdlReader rdlReader(data);
    const auto vertices = rdlReader.Vertices();
    const auto cubes = rdlReader.Cubes();
    const auto quads = Quads(cubes);

    const bool verticesOnly = false;

    std::cout << "ply" << std::endl;
    std::cout << "format ascii 1.0" << std::endl;
    std::cout << "comment An exported Descent 1 level (" << (*file).name << ")"
              << std::endl;

    std::cout << "element vertex " << vertices.size() << std::endl;
    std::cout << "property float x" << std::endl;
    std::cout << "property float y" << std::endl;
    std::cout << "property float z" << std::endl;
    if (!verticesOnly)
    {
      std::cout << "element face " << quads.size() << std::endl;
      std::cout << "property list uchar int vertex_index" << std::endl;
    }
    std::cout << "end_header" << std::endl;

    std::for_each(vertices.begin(), vertices.end(), [](Vertex v)
    { printf("%f %f %f\n", v.x, v.y, v.z); });

    if (!verticesOnly)
    {
      std::for_each(quads.begin(), quads.end(), [](const Quad& quad)
      { printf("4 %d %d %d %d\n", quad.a, quad.b, quad.c, quad.d); });
    }
  }
  else
  {
    // Iterate over the file list and list all the files which do not
    // end in the 'rdl' file extension.
    const std::string extentionRdl(".rdl");
    std::for_each(reader.begin(), reader.end(),
                  [&reader, &extentionRdl](HogReader::iterator::value_type n)
    {
      const std::string filename(n.name);
      if (!std::equal(extentionRdl.rbegin(), extentionRdl.rend(),
                      filename.rbegin()))
        return;

      printf("File: %s Size: %d\n", n.name, reader.CurrentFileSize());
      const auto data = reader.CurrentFile();
      RdlReader rdlReader(data);

      if (!rdlReader.IsValid()) return;

      // Print out the vertices.
      const auto vertices = rdlReader.Vertices();
      printf("Vertex count: %d\n", vertices.size());
      std::for_each(vertices.begin(), vertices.end(),
                    [](Vertex v)
                    { printf("%16f %16f %16f\n", v.x, v.y, v.z); });
    });
  }
  return 0;
}
<|endoftext|>


<commit_before>#include "assets_editor_window.h"
#include "halley/tools/project/project.h"
#include "halley/core/resources/resource_locator.h"
#include "halley/core/resources/standard_resources.h"
#include "halley/ui/widgets/ui_label.h"
#include "halley/ui/widgets/ui_list.h"
#include "animation_editor.h"
#include "metadata_editor.h"
#include "prefab_editor.h"

using namespace Halley;

AssetsEditorWindow::AssetsEditorWindow(UIFactory& factory, Project& project, EditorRootStage& stage)
	: UIWidget("assets_editor", {}, UISizer())
	, factory(factory)
	, project(project)
	, stage(stage)
	, curSrcPath(".")
{
	loadResources();
	makeUI();
	setAssetSrcMode(true);
}

void AssetsEditorWindow::loadResources()
{
	project.addAssetReloadCallback([=] (const std::vector<String>& assets)
	{
		refreshAssets(assets);
	});
}

void AssetsEditorWindow::makeUI()
{
	UIWidget::add(factory.makeUI("ui/halley/assets_editor_window"), 1);

	assetList = getWidgetAs<UIList>("assetList");
	assetList->setSingleClickAccept(false);
	metadataEditor = getWidgetAs<MetadataEditor>("metadataEditor");
	content = getWidgetAs<UIPagedPane>("content");
	contentList = getWidgetAs<UIList>("contentList");
	contentListDropdown = getWidgetAs<UIDropdown>("contentListDropdown");
	contentListDropdownLabel = getWidgetAs<UILabel>("contentListDropdownLabel");

	setHandle(UIEventType::ListSelectionChanged, "contentList", [=] (const UIEvent& event)
	{
		content->setPage(event.getStringData().toInteger());
	});

	setHandle(UIEventType::DropboxSelectionChanged, "contentListDropdown", [=](const UIEvent& event)
	{
		loadAsset(loadedAsset, false, false);
	});

	setHandle(UIEventType::ListSelectionChanged, "assetType", [=] (const UIEvent& event)
	{
		listAssets(fromString<AssetType>(event.getStringData()));
	});

	setHandle(UIEventType::ListSelectionChanged, "assetList", [=] (const UIEvent& event)
	{
		loadAsset(event.getStringData(), false, true);
	});

	setHandle(UIEventType::ListAccept, "assetList", [=] (const UIEvent& event)
	{
		loadAsset(event.getStringData(), true, true);
	});

	setHandle(UIEventType::TextChanged, "assetSearch", [=] (const UIEvent& event)
	{
		setFilter(event.getStringData());
	});
}

void AssetsEditorWindow::setAssetSrcMode(bool enabled)
{
	assetSrcMode = enabled;
	getWidget("assetType")->setActive(!assetSrcMode);
	if (assetSrcMode) {
		listAssetSources();
	} else {
		listAssets(AssetType::Sprite);
	}
}

void AssetsEditorWindow::listAssetSources()
{
	if (!assetNames) {
		assetNames = project.getAssetSrcList();
		std::sort(assetNames->begin(), assetNames->end()); // Is this even needed?
	}

	if (filter.isEmpty()) {
		setListContents(assetNames.value(), curSrcPath, false);
	} else {
		std::vector<String> filteredList;
		for (auto& a: assetNames.value()) {
			if (a.asciiLower().contains(filter)) {
				filteredList.push_back(a);
			}
		}
		setListContents(filteredList, curSrcPath, true);
	}
}

void AssetsEditorWindow::listAssets(AssetType type)
{
	curType = type;
	if (curPaths.find(type) == curPaths.end()) {
		curPaths[type] = Path(".");
	}
	const auto curPath = curPaths[type];

	auto assets = project.getGameResources().ofType(type).enumerate();
	std::sort(assets.begin(), assets.end());

	setListContents(assets, curPath, false);
}

void AssetsEditorWindow::setListContents(std::vector<String> assets, const Path& curPath, bool flat)
{
	assetList->clear();
	if (flat) {
		for (auto& a: assets) {
			assetList->addTextItem(a, LocalisedString::fromUserString(Path(a).getFilename().toString()));
		}
	} else {
		std::set<String> dirs;
		std::vector<std::pair<String, String>> files;

		for (auto& a: assets) {
			auto relPath = Path("./" + a).makeRelativeTo(curPath);
			if (relPath.getNumberPaths() == 1) {
				files.emplace_back(a, relPath.toString());
			} else {
				auto start = relPath.getFront(1);
				dirs.insert(start.toString());
			}
		}

		for (auto& dir: dirs) {
			assetList->addTextItem(dir + "/.", LocalisedString::fromUserString("[" + dir + "]"));
		}
		for (auto& file: files) {
			assetList->addTextItem(file.first, LocalisedString::fromUserString(file.second));
		}
	}
}

void AssetsEditorWindow::refreshList()
{
	if (assetSrcMode) {
		listAssetSources();
	} else {
		listAssets(curType);
	}
}

void AssetsEditorWindow::setFilter(const String& f)
{
	if (filter != f) {
		filter = f.asciiLower();
		refreshList();
	}
}

void AssetsEditorWindow::loadAsset(const String& name, bool doubleClick, bool clearDropdown)
{
	auto& curPath = assetSrcMode ? curSrcPath : curPaths[curType];
	if (name.endsWith("/.")) {
		if (doubleClick) {
			curPath = curPath / name;
			refreshList();
		}
	} else {
		loadedAsset = name;

		content->clear();
		contentList->clear();
		curEditors.clear();

		if (clearDropdown) {
			contentListDropdown->clear();
			contentListDropdown->setActive(false);
			contentListDropdownLabel->setActive(false);
		}

		if (assetSrcMode) {
			auto assets = project.getAssetsFromFile(Path(name));

			std::sort(assets.begin(), assets.end(), [] (decltype(assets)::const_reference a, decltype(assets)::const_reference b) -> bool
			{
				return b.first < a.first;
			});

			auto useDropdown = false;
			std::vector<String> assetNames;
			if (int(assets.size()) > 3)
			{

				for (const auto& asset : assets)
				{
					if (asset.first == AssetType::Animation) {
						assetNames.push_back(asset.second);
					}
				}

				if(assetNames.size() > 0)
				{
					useDropdown = true;
					contentListDropdown->setActive(true);
					contentListDropdownLabel->setActive(true);
				}

				if (clearDropdown) {
					contentListDropdown->setOptions(assetNames, 0);
				}
			}

			for (auto& asset: assets) {
				if (!useDropdown || asset.second == contentListDropdown->getSelectedOptionId() || std::find(assetNames.begin(), assetNames.end(), asset.second) == assetNames.end()) {
					createEditorTab(asset.first, asset.second);
				}
			}

			if (assets.empty()) {
				metadataEditor->clear();
			} else {
				const auto type = assets.at(0).first;
				auto effectiveMeta = project.getImportMetadata(type, assets.at(0).second);
				metadataEditor->setResource(project, type, Path(name), std::move(effectiveMeta));
			}
		} else {
			metadataEditor->clear();
			createEditorTab(curType, name);
		}
	}
}

void AssetsEditorWindow::refreshAssets(const std::vector<String>& assets)
{
	assetNames.reset();
	refreshList();

	for (auto& editor: curEditors) {
		editor->reload();
	}
}

std::shared_ptr<AssetEditor> AssetsEditorWindow::makeEditor(AssetType type, const String& name)
{
	switch (type) {
	case AssetType::Sprite:
	case AssetType::Animation:
	case AssetType::Texture:
		return std::make_shared<AnimationEditor>(factory, project.getGameResources(), type, project);
	case AssetType::Prefab:
	case AssetType::Scene:
		return std::make_shared<PrefabEditor>(factory, project.getGameResources(), type, project, stage);
	}
	return {};
}

void AssetsEditorWindow::createEditorTab(AssetType type, const String& name)
{
	auto editor = makeEditor(type, name);
	if (editor) {
		editor->setResource(name);
		auto n = content->getNumberOfPages();
		content->addPage();
		content->getPage(n)->add(editor, 1);
		auto typeSprite = Sprite().setImage(factory.getResources(), Path("ui") / "assetTypes" / toString(type) + ".png");
		const auto image = std::make_shared<UIImage>(typeSprite);
		const auto text = std::make_shared<UILabel>(name + "_" + toString(type) + ":label", contentList->getStyle().getTextRenderer("label"), LocalisedString::fromUserString(name));

		auto item = std::make_shared<UISizer>();
		item->add(image);
		item->add(text, 1.0f, {}, UISizerAlignFlags::CentreVertical);

		contentList->addItem(toString(n), item);
	}
}

AssetEditor::AssetEditor(UIFactory& factory, Resources& resources, Project& project, AssetType type)
	: UIWidget("assetEditor", {}, UISizer())
	, factory(factory)
	, project(project)
	, resources(resources)
	, assetType(type)
{
}

void AssetEditor::setResource(const String& id)
{
	assetId = id;
	if (assetType == AssetType::Animation) {
		resource = resources.get<Animation>(assetId);
	} else if (assetType == AssetType::Sprite) {
		resource = resources.get<SpriteResource>(assetId);
	} else if (assetType == AssetType::Texture) {
		resource = resources.get<Texture>(assetId);
	}
	reload();
}

void AssetEditor::clearResource()
{
	assetId = "";
	resource.reset();
	reload();
}

void AssetEditor::reload()
{
}
<commit_msg>set dropdown inactive unless needed<commit_after>#include "assets_editor_window.h"
#include "halley/tools/project/project.h"
#include "halley/core/resources/resource_locator.h"
#include "halley/core/resources/standard_resources.h"
#include "halley/ui/widgets/ui_label.h"
#include "halley/ui/widgets/ui_list.h"
#include "animation_editor.h"
#include "metadata_editor.h"
#include "prefab_editor.h"

using namespace Halley;

AssetsEditorWindow::AssetsEditorWindow(UIFactory& factory, Project& project, EditorRootStage& stage)
	: UIWidget("assets_editor", {}, UISizer())
	, factory(factory)
	, project(project)
	, stage(stage)
	, curSrcPath(".")
{
	loadResources();
	makeUI();
	setAssetSrcMode(true);
}

void AssetsEditorWindow::loadResources()
{
	project.addAssetReloadCallback([=] (const std::vector<String>& assets)
	{
		refreshAssets(assets);
	});
}

void AssetsEditorWindow::makeUI()
{
	UIWidget::add(factory.makeUI("ui/halley/assets_editor_window"), 1);

	assetList = getWidgetAs<UIList>("assetList");
	assetList->setSingleClickAccept(false);
	metadataEditor = getWidgetAs<MetadataEditor>("metadataEditor");
	content = getWidgetAs<UIPagedPane>("content");
	contentList = getWidgetAs<UIList>("contentList");
	contentListDropdown = getWidgetAs<UIDropdown>("contentListDropdown");
	contentListDropdownLabel = getWidgetAs<UILabel>("contentListDropdownLabel");

	setHandle(UIEventType::ListSelectionChanged, "contentList", [=] (const UIEvent& event)
	{
		content->setPage(event.getStringData().toInteger());
	});

	setHandle(UIEventType::DropboxSelectionChanged, "contentListDropdown", [=](const UIEvent& event)
	{
		loadAsset(loadedAsset, false, false);
	});

	setHandle(UIEventType::ListSelectionChanged, "assetType", [=] (const UIEvent& event)
	{
		listAssets(fromString<AssetType>(event.getStringData()));
	});

	setHandle(UIEventType::ListSelectionChanged, "assetList", [=] (const UIEvent& event)
	{
		loadAsset(event.getStringData(), false, true);
	});

	setHandle(UIEventType::ListAccept, "assetList", [=] (const UIEvent& event)
	{
		loadAsset(event.getStringData(), true, true);
	});

	setHandle(UIEventType::TextChanged, "assetSearch", [=] (const UIEvent& event)
	{
		setFilter(event.getStringData());
	});
}

void AssetsEditorWindow::setAssetSrcMode(bool enabled)
{
	assetSrcMode = enabled;
	getWidget("assetType")->setActive(!assetSrcMode);
	if (assetSrcMode) {
		listAssetSources();
	} else {
		listAssets(AssetType::Sprite);
	}
}

void AssetsEditorWindow::listAssetSources()
{
	if (!assetNames) {
		assetNames = project.getAssetSrcList();
		std::sort(assetNames->begin(), assetNames->end()); // Is this even needed?
	}

	if (filter.isEmpty()) {
		setListContents(assetNames.value(), curSrcPath, false);
	} else {
		std::vector<String> filteredList;
		for (auto& a: assetNames.value()) {
			if (a.asciiLower().contains(filter)) {
				filteredList.push_back(a);
			}
		}
		setListContents(filteredList, curSrcPath, true);
	}
}

void AssetsEditorWindow::listAssets(AssetType type)
{
	curType = type;
	if (curPaths.find(type) == curPaths.end()) {
		curPaths[type] = Path(".");
	}
	const auto curPath = curPaths[type];

	auto assets = project.getGameResources().ofType(type).enumerate();
	std::sort(assets.begin(), assets.end());

	setListContents(assets, curPath, false);
}

void AssetsEditorWindow::setListContents(std::vector<String> assets, const Path& curPath, bool flat)
{
	assetList->clear();
	if (flat) {
		for (auto& a: assets) {
			assetList->addTextItem(a, LocalisedString::fromUserString(Path(a).getFilename().toString()));
		}
	} else {
		std::set<String> dirs;
		std::vector<std::pair<String, String>> files;

		for (auto& a: assets) {
			auto relPath = Path("./" + a).makeRelativeTo(curPath);
			if (relPath.getNumberPaths() == 1) {
				files.emplace_back(a, relPath.toString());
			} else {
				auto start = relPath.getFront(1);
				dirs.insert(start.toString());
			}
		}

		for (auto& dir: dirs) {
			assetList->addTextItem(dir + "/.", LocalisedString::fromUserString("[" + dir + "]"));
		}
		for (auto& file: files) {
			assetList->addTextItem(file.first, LocalisedString::fromUserString(file.second));
		}
	}
}

void AssetsEditorWindow::refreshList()
{
	if (assetSrcMode) {
		listAssetSources();
	} else {
		listAssets(curType);
	}
}

void AssetsEditorWindow::setFilter(const String& f)
{
	if (filter != f) {
		filter = f.asciiLower();
		refreshList();
	}
}

void AssetsEditorWindow::loadAsset(const String& name, bool doubleClick, bool clearDropdown)
{
	if (clearDropdown) {
		contentListDropdown->clear();
		contentListDropdown->setActive(false);
		contentListDropdownLabel->setActive(false);
	}

	auto& curPath = assetSrcMode ? curSrcPath : curPaths[curType];
	if (name.endsWith("/.")) {
		if (doubleClick) {
			curPath = curPath / name;
			refreshList();
		}
	} else {
		loadedAsset = name;

		content->clear();
		contentList->clear();
		curEditors.clear();

		if (assetSrcMode) {
			auto assets = project.getAssetsFromFile(Path(name));

			std::sort(assets.begin(), assets.end(), [] (decltype(assets)::const_reference a, decltype(assets)::const_reference b) -> bool
			{
				return b.first < a.first;
			});

			auto useDropdown = false;
			std::vector<String> assetNames;
			if (int(assets.size()) > 3)
			{

				for (const auto& asset : assets)
				{
					if (asset.first == AssetType::Animation) {
						assetNames.push_back(asset.second);
					}
				}

				if(assetNames.size() > 0)
				{
					useDropdown = true;
					contentListDropdown->setActive(true);
					contentListDropdownLabel->setActive(true);
				}

				if (clearDropdown) {
					contentListDropdown->setOptions(assetNames, 0);
				}
			}

			for (auto& asset: assets) {
				if (!useDropdown || asset.second == contentListDropdown->getSelectedOptionId() || std::find(assetNames.begin(), assetNames.end(), asset.second) == assetNames.end()) {
					createEditorTab(asset.first, asset.second);
				}
			}

			if (assets.empty()) {
				metadataEditor->clear();
			} else {
				const auto type = assets.at(0).first;
				auto effectiveMeta = project.getImportMetadata(type, assets.at(0).second);
				metadataEditor->setResource(project, type, Path(name), std::move(effectiveMeta));
			}
		} else {
			metadataEditor->clear();
			createEditorTab(curType, name);
		}
	}
}

void AssetsEditorWindow::refreshAssets(const std::vector<String>& assets)
{
	assetNames.reset();
	refreshList();

	for (auto& editor: curEditors) {
		editor->reload();
	}
}

std::shared_ptr<AssetEditor> AssetsEditorWindow::makeEditor(AssetType type, const String& name)
{
	switch (type) {
	case AssetType::Sprite:
	case AssetType::Animation:
	case AssetType::Texture:
		return std::make_shared<AnimationEditor>(factory, project.getGameResources(), type, project);
	case AssetType::Prefab:
	case AssetType::Scene:
		return std::make_shared<PrefabEditor>(factory, project.getGameResources(), type, project, stage);
	}
	return {};
}

void AssetsEditorWindow::createEditorTab(AssetType type, const String& name)
{
	auto editor = makeEditor(type, name);
	if (editor) {
		editor->setResource(name);
		auto n = content->getNumberOfPages();
		content->addPage();
		content->getPage(n)->add(editor, 1);
		auto typeSprite = Sprite().setImage(factory.getResources(), Path("ui") / "assetTypes" / toString(type) + ".png");
		const auto image = std::make_shared<UIImage>(typeSprite);
		const auto text = std::make_shared<UILabel>(name + "_" + toString(type) + ":label", contentList->getStyle().getTextRenderer("label"), LocalisedString::fromUserString(name));

		auto item = std::make_shared<UISizer>();
		item->add(image);
		item->add(text, 1.0f, {}, UISizerAlignFlags::CentreVertical);

		contentList->addItem(toString(n), item);
	}
}

AssetEditor::AssetEditor(UIFactory& factory, Resources& resources, Project& project, AssetType type)
	: UIWidget("assetEditor", {}, UISizer())
	, factory(factory)
	, project(project)
	, resources(resources)
	, assetType(type)
{
}

void AssetEditor::setResource(const String& id)
{
	assetId = id;
	if (assetType == AssetType::Animation) {
		resource = resources.get<Animation>(assetId);
	} else if (assetType == AssetType::Sprite) {
		resource = resources.get<SpriteResource>(assetId);
	} else if (assetType == AssetType::Texture) {
		resource = resources.get<Texture>(assetId);
	}
	reload();
}

void AssetEditor::clearResource()
{
	assetId = "";
	resource.reset();
	reload();
}

void AssetEditor::reload()
{
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/
//$Id$

// boost
#include <boost/python/suite/indexing/indexing_suite.hpp>
#include <boost/python/iterator.hpp>
#include <boost/python/call_method.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python.hpp>
#include <boost/scoped_array.hpp>
// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/datasource.hpp>

mapnik::geometry2d & (mapnik::Feature::*get_geom1)(unsigned) = &mapnik::Feature::get_geometry;

namespace boost { namespace python {
struct value_converter : public boost::static_visitor<PyObject*>
{
    PyObject * operator() (int val) const
    {
	return ::PyInt_FromLong(val);
    }

    PyObject * operator() (double val) const
    {
	return ::PyFloat_FromDouble(val);
    }

    PyObject * operator() (UnicodeString const& s) const
    {
	std::string buffer;
	mapnik::to_utf8(s,buffer);
	PyObject *obj = Py_None;
	obj = ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
	return obj;
    }

    PyObject * operator() (mapnik::value_null const& s) const
    {
	return NULL;
    }
};

struct mapnik_value_to_python
{
    static PyObject* convert(mapnik::value const& v)
    {
	return boost::apply_visitor(value_converter(),v.base());
    }
};

// Forward declaration
template <class Container, bool NoProxy, class DerivedPolicies>
class map_indexing_suite2;

namespace detail
{
template <class Container, bool NoProxy>
class final_map_derived_policies
    : public map_indexing_suite2<Container,
				 NoProxy, final_map_derived_policies<Container, NoProxy> > {};
}

template <
class Container,
bool NoProxy = false,
class DerivedPolicies
= detail::final_map_derived_policies<Container, NoProxy> >
class map_indexing_suite2
    : public indexing_suite<
Container
, DerivedPolicies
, NoProxy
, true
, typename Container::value_type::second_type
, typename Container::key_type
, typename Container::key_type
>
{
public:

    typedef typename Container::value_type value_type;
    typedef typename Container::value_type::second_type data_type;
    typedef typename Container::key_type key_type;
    typedef typename Container::key_type index_type;
    typedef typename Container::size_type size_type;
    typedef typename Container::difference_type difference_type;

    template <class Class>
    static void
    extension_def(Class& cl)
    {

    }

    static data_type&
    get_item(Container& container, index_type i_)
    {
	typename Container::iterator i = container.props().find(i_);
	if (i == container.end())
	{
	    PyErr_SetString(PyExc_KeyError, "Invalid key");
	    throw_error_already_set();
	}
	return i->second;
    }

    static void
    set_item(Container& container, index_type i, data_type const& v)
    {
	container[i] = v;
    }

    static void
    delete_item(Container& container, index_type i)
    {
	container.props().erase(i);
    }

    static size_t
    size(Container& container)
    {
	return container.props().size();
    }

    static bool
    contains(Container& container, key_type const& key)
    {
	return container.props().find(key) != container.end();
    }

    static bool
    compare_index(Container& container, index_type a, index_type b)
    {
	return container.props().key_comp()(a, b);
    }

    static index_type
    convert_index(Container& /*container*/, PyObject* i_)
    {
	extract<key_type const&> i(i_);
	if (i.check())
	{
	    return i();
	}
	else
	{
	    extract<key_type> i(i_);
	    if (i.check())
		return i();
	}

	PyErr_SetString(PyExc_TypeError, "Invalid index type");
	throw_error_already_set();
	return index_type();
    }
};

template <typename T1, typename T2>
struct std_pair_to_tuple
{
    static PyObject* convert(std::pair<T1, T2> const& p)
    {
	return boost::python::incref(
	    boost::python::make_tuple(p.first, p.second).ptr());
    }
};

template <typename T1, typename T2>
struct std_pair_to_python_converter
{
    std_pair_to_python_converter()
    {
	boost::python::to_python_converter<
	std::pair<T1, T2>,
	    std_pair_to_tuple<T1, T2> >();
    }
};
}
}

mapnik::feature_ptr create_feature_(int id)
{
    return mapnik::feature_ptr(new mapnik::Feature(id));
}

struct UnicodeString_from_python_str
{
    UnicodeString_from_python_str()
    {
	boost::python::converter::registry::push_back(
	    &convertible,
	    &construct,
	    boost::python::type_id<UnicodeString>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
	if (!PyString_Check(obj_ptr)) return 0;
	return obj_ptr;
    }

    static void construct(
	PyObject* obj_ptr,
	boost::python::converter::rvalue_from_python_stage1_data* data)
    {
	const char* value = PyString_AsString(obj_ptr);
	if (value == 0) boost::python::throw_error_already_set();
	void* storage = (
	    (boost::python::converter::rvalue_from_python_storage<UnicodeString>*)
	    data)->storage.bytes;
	new (storage) UnicodeString(value);
	data->convertible = storage;
    }
};

void export_feature()
{
    using namespace boost::python;
    using mapnik::Feature;

    implicitly_convertible<int,mapnik::value>();
    implicitly_convertible<double,mapnik::value>();
    implicitly_convertible<UnicodeString,mapnik::value>();
    implicitly_convertible<bool,mapnik::value>();

    std_pair_to_python_converter<std::string const,mapnik::value>();
    to_python_converter<mapnik::value,mapnik_value_to_python>();
    UnicodeString_from_python_str();

    class_<Feature,boost::shared_ptr<Feature>,
	boost::noncopyable>("Feature",no_init)
	.def("id",&Feature::id)
	.def("__str__",&Feature::to_string)
//	.add_property("properties",
//		      make_function(&Feature::props,return_value_policy<reference_existing_object>()))
    //      .def("add_geometry", // TODO define more mapnik::Feature methods
	.def("num_geometries",&Feature::num_geometries)
	.def("get_geometry", make_function(get_geom1,return_value_policy<reference_existing_object>()))
	.def("envelope", &Feature::envelope)
	.def("create",create_feature_)
	.staticmethod("create")
	.def(map_indexing_suite2<Feature, true >())
	.def("iteritems",iterator<Feature> ())
	;

//def("Feature", &create_feature_);

//class_<std::map<std::string, mapnik::value> >("Properties")
//   .def(map_indexing_suite2<std::map<std::string, mapnik::value>, true >())
//  .def("iteritems",iterator<std::map<std::string,mapnik::value> > ())
//     ;
}
<commit_msg>comment out new unicode stuff in mapnik_feature due to #505<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/
//$Id$

// boost
#include <boost/python/suite/indexing/indexing_suite.hpp>
#include <boost/python/iterator.hpp>
#include <boost/python/call_method.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python.hpp>
#include <boost/scoped_array.hpp>
// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/datasource.hpp>

mapnik::geometry2d & (mapnik::Feature::*get_geom1)(unsigned) = &mapnik::Feature::get_geometry;

namespace boost { namespace python {
struct value_converter : public boost::static_visitor<PyObject*>
{
    PyObject * operator() (int val) const
    {
	return ::PyInt_FromLong(val);
    }

    PyObject * operator() (double val) const
    {
	return ::PyFloat_FromDouble(val);
    }

    PyObject * operator() (UnicodeString const& s) const
    {
	std::string buffer;
	mapnik::to_utf8(s,buffer);
	PyObject *obj = Py_None;
	obj = ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
	return obj;
    }

    PyObject * operator() (mapnik::value_null const& s) const
    {
	return NULL;
    }
};

struct mapnik_value_to_python
{
    static PyObject* convert(mapnik::value const& v)
    {
	return boost::apply_visitor(value_converter(),v.base());
    }
};

// Forward declaration
template <class Container, bool NoProxy, class DerivedPolicies>
class map_indexing_suite2;

namespace detail
{
template <class Container, bool NoProxy>
class final_map_derived_policies
    : public map_indexing_suite2<Container,
				 NoProxy, final_map_derived_policies<Container, NoProxy> > {};
}

template <
class Container,
bool NoProxy = false,
class DerivedPolicies
= detail::final_map_derived_policies<Container, NoProxy> >
class map_indexing_suite2
    : public indexing_suite<
Container
, DerivedPolicies
, NoProxy
, true
, typename Container::value_type::second_type
, typename Container::key_type
, typename Container::key_type
>
{
public:

    typedef typename Container::value_type value_type;
    typedef typename Container::value_type::second_type data_type;
    typedef typename Container::key_type key_type;
    typedef typename Container::key_type index_type;
    typedef typename Container::size_type size_type;
    typedef typename Container::difference_type difference_type;

    template <class Class>
    static void
    extension_def(Class& cl)
    {

    }

    static data_type&
    get_item(Container& container, index_type i_)
    {
	typename Container::iterator i = container.props().find(i_);
	if (i == container.end())
	{
	    PyErr_SetString(PyExc_KeyError, "Invalid key");
	    throw_error_already_set();
	}
	return i->second;
    }

    static void
    set_item(Container& container, index_type i, data_type const& v)
    {
	container[i] = v;
    }

    static void
    delete_item(Container& container, index_type i)
    {
	container.props().erase(i);
    }

    static size_t
    size(Container& container)
    {
	return container.props().size();
    }

    static bool
    contains(Container& container, key_type const& key)
    {
	return container.props().find(key) != container.end();
    }

    static bool
    compare_index(Container& container, index_type a, index_type b)
    {
	return container.props().key_comp()(a, b);
    }

    static index_type
    convert_index(Container& /*container*/, PyObject* i_)
    {
	extract<key_type const&> i(i_);
	if (i.check())
	{
	    return i();
	}
	else
	{
	    extract<key_type> i(i_);
	    if (i.check())
		return i();
	}

	PyErr_SetString(PyExc_TypeError, "Invalid index type");
	throw_error_already_set();
	return index_type();
    }
};

template <typename T1, typename T2>
struct std_pair_to_tuple
{
    static PyObject* convert(std::pair<T1, T2> const& p)
    {
	return boost::python::incref(
	    boost::python::make_tuple(p.first, p.second).ptr());
    }
};

template <typename T1, typename T2>
struct std_pair_to_python_converter
{
    std_pair_to_python_converter()
    {
	boost::python::to_python_converter<
	std::pair<T1, T2>,
	    std_pair_to_tuple<T1, T2> >();
    }
};
}
}

/*
mapnik::feature_ptr create_feature_(int id)
{
    return mapnik::feature_ptr(new mapnik::Feature(id));
}

struct UnicodeString_from_python_str
{
    UnicodeString_from_python_str()
    {
	boost::python::converter::registry::push_back(
	    &convertible,
	    &construct,
	    boost::python::type_id<UnicodeString>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
	if (!PyString_Check(obj_ptr)) return 0;
	return obj_ptr;
    }

    static void construct(
	PyObject* obj_ptr,
	boost::python::converter::rvalue_from_python_stage1_data* data)
    {
	const char* value = PyString_AsString(obj_ptr);
	if (value == 0) boost::python::throw_error_already_set();
	void* storage = (
	    (boost::python::converter::rvalue_from_python_storage<UnicodeString>*)
	    data)->storage.bytes;
	new (storage) UnicodeString(value);
	data->convertible = storage;
    }
};
*/

void export_feature()
{
    using namespace boost::python;
    using mapnik::Feature;

    implicitly_convertible<int,mapnik::value>();
    implicitly_convertible<double,mapnik::value>();
    //implicitly_convertible<UnicodeString,mapnik::value>();
    implicitly_convertible<bool,mapnik::value>();

    std_pair_to_python_converter<std::string const,mapnik::value>();
    to_python_converter<mapnik::value,mapnik_value_to_python>();
    //UnicodeString_from_python_str();

    class_<Feature,boost::shared_ptr<Feature>,
	boost::noncopyable>("Feature",no_init)
	.def("id",&Feature::id)
	.def("__str__",&Feature::to_string)
//	.add_property("properties",
//		      make_function(&Feature::props,return_value_policy<reference_existing_object>()))
    //      .def("add_geometry", // TODO define more mapnik::Feature methods
	.def("num_geometries",&Feature::num_geometries)
	.def("get_geometry", make_function(get_geom1,return_value_policy<reference_existing_object>()))
	.def("envelope", &Feature::envelope)
	//.def("create",create_feature_)
	//.staticmethod("create")
	.def(map_indexing_suite2<Feature, true >())
	.def("iteritems",iterator<Feature> ())
	;

//def("Feature", &create_feature_);

//class_<std::map<std::string, mapnik::value> >("Properties")
//   .def(map_indexing_suite2<std::map<std::string, mapnik::value>, true >())
//  .def("iteritems",iterator<std::map<std::string,mapnik::value> > ())
//     ;
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <threading/Formatter.h>
#include "MetronJSON.h"

using metron::formatter::MetronJSON;
using threading::formatter::JSON;
using threading::MsgThread;
using threading::Field;
using threading::Value;

MetronJSON::MetronJSON(string sn, MsgThread* t, TimeFormat tf)
    : JSON(t, tf)
    , stream_name(sn)
{
}

MetronJSON::~MetronJSON() {}

bool MetronJSON::Describe(ODesc* desc, int num_fields,
    const Field* const* fields, Value** vals) const
{
    desc->AddRaw("{");

    // prepend the stream name
    desc->AddRaw("\"");
    desc->AddRaw(stream_name);
    desc->AddRaw("\", ");

    // append the JSON formatted log record itself
    JSON::Describe(desc, num_fields, fields, vals);

    desc->AddRaw("}");
    return true;
}
<commit_msg>METRON-52: Bro Plugin Header has comma instead of colon (dlyle65535 via cestella) closes apache/incubator-metron#31<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <threading/Formatter.h>
#include "MetronJSON.h"

using metron::formatter::MetronJSON;
using threading::formatter::JSON;
using threading::MsgThread;
using threading::Field;
using threading::Value;

MetronJSON::MetronJSON(string sn, MsgThread* t, TimeFormat tf)
    : JSON(t, tf)
    , stream_name(sn)
{
}

MetronJSON::~MetronJSON() {}

bool MetronJSON::Describe(ODesc* desc, int num_fields,
    const Field* const* fields, Value** vals) const
{
    desc->AddRaw("{");

    // prepend the stream name
    desc->AddRaw("\"");
    desc->AddRaw(stream_name);
    desc->AddRaw("\": ");

    // append the JSON formatted log record itself
    JSON::Describe(desc, num_fields, fields, vals);

    desc->AddRaw("}");
    return true;
}
<|endoftext|>


<commit_before>#include "jsonmerge_tests.h"
#include "../Common/defines.h"
#include "../../xpiks-qt/Helpers/jsonhelper.h"
#include "../../xpiks-qt/Helpers/localconfig.h"

void JsonMergeTests::initTestCase() {
    LOG_DEBUG << "App path: " << QDir::currentPath();
}

void JsonMergeTests::mergeTwoFilesObjects() {
    QFileInfo infoOld("jsons-for-tests/old.json");
    QFileInfo infoNew("jsons-for-tests/new.json");
    QFileInfo infoGold("jsons-for-tests/gold.json");

    QString pathTo = infoOld.absoluteFilePath();
    QString pathWith = infoNew.absoluteFilePath();
    QString pathGold = infoGold.absoluteFilePath();

    QVERIFY(infoOld.exists());
    QVERIFY(infoNew.exists());
    QVERIFY(infoGold.exists());

    Helpers::LocalConfig localConfigWith(pathWith);
    Helpers::LocalConfig localConfigTo(pathTo);
    Helpers::LocalConfig localConfigGold(pathGold);

    localConfigWith.initConfig();
    localConfigTo.initConfig();
    localConfigGold.initConfig();

    Helpers::mergeJson(localConfigTo.getConfig(), localConfigWith.getConfig(), 0, *this);

    QVERIFY(localConfigGold.getConfig() == localConfigTo.getConfig());
}

void JsonMergeTests::mergeTwoFilesStrings() {
    QFileInfo infoOld("jsons-for-tests/oldS.json");
    QFileInfo infoNew("jsons-for-tests/newS.json");
    QFileInfo infoGold("jsons-for-tests/goldS.json");

    QString pathTo = infoOld.absoluteFilePath();
    QString pathWith = infoNew.absoluteFilePath();
    QString pathGold = infoGold.absoluteFilePath();

    QVERIFY(infoOld.exists());
    QVERIFY(infoNew.exists());
    QVERIFY(infoGold.exists());

    Helpers::LocalConfig localConfigWith(pathWith);
    Helpers::LocalConfig localConfigTo(pathTo);
    Helpers::LocalConfig localConfigGold(pathGold);

    localConfigWith.initConfig();
    localConfigTo.initConfig();
    localConfigGold.initConfig();

    Helpers::mergeJson(localConfigTo.getConfig(), localConfigWith.getConfig(), 0, *this);

    QVERIFY(localConfigGold.getConfig() == localConfigTo.getConfig());
}
<commit_msg>Fixed relative include path<commit_after>#include "jsonmerge_tests.h"
#include "../../xpiks-qt/Common/defines.h"
#include "../../xpiks-qt/Helpers/jsonhelper.h"
#include "../../xpiks-qt/Helpers/localconfig.h"

void JsonMergeTests::initTestCase() {
    LOG_DEBUG << "App path: " << QDir::currentPath();
}

void JsonMergeTests::mergeTwoFilesObjects() {
    QFileInfo infoOld("jsons-for-tests/old.json");
    QFileInfo infoNew("jsons-for-tests/new.json");
    QFileInfo infoGold("jsons-for-tests/gold.json");

    QString pathTo = infoOld.absoluteFilePath();
    QString pathWith = infoNew.absoluteFilePath();
    QString pathGold = infoGold.absoluteFilePath();

    QVERIFY(infoOld.exists());
    QVERIFY(infoNew.exists());
    QVERIFY(infoGold.exists());

    Helpers::LocalConfig localConfigWith(pathWith);
    Helpers::LocalConfig localConfigTo(pathTo);
    Helpers::LocalConfig localConfigGold(pathGold);

    localConfigWith.initConfig();
    localConfigTo.initConfig();
    localConfigGold.initConfig();

    Helpers::mergeJson(localConfigTo.getConfig(), localConfigWith.getConfig(), 0, *this);

    QVERIFY(localConfigGold.getConfig() == localConfigTo.getConfig());
}

void JsonMergeTests::mergeTwoFilesStrings() {
    QFileInfo infoOld("jsons-for-tests/oldS.json");
    QFileInfo infoNew("jsons-for-tests/newS.json");
    QFileInfo infoGold("jsons-for-tests/goldS.json");

    QString pathTo = infoOld.absoluteFilePath();
    QString pathWith = infoNew.absoluteFilePath();
    QString pathGold = infoGold.absoluteFilePath();

    QVERIFY(infoOld.exists());
    QVERIFY(infoNew.exists());
    QVERIFY(infoGold.exists());

    Helpers::LocalConfig localConfigWith(pathWith);
    Helpers::LocalConfig localConfigTo(pathTo);
    Helpers::LocalConfig localConfigGold(pathGold);

    localConfigWith.initConfig();
    localConfigTo.initConfig();
    localConfigGold.initConfig();

    Helpers::mergeJson(localConfigTo.getConfig(), localConfigWith.getConfig(), 0, *this);

    QVERIFY(localConfigGold.getConfig() == localConfigTo.getConfig());
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "test-harness.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <map>
#include <cstring>
#include <testcase.h>

namespace TestHarness
{

typedef std::map<int32_t, TestCase> RunningTestCases;

const char* basename(const char* path)
{
  const char* ptr=path;
  const char* slash=NULL;
  for( ; *ptr != '\0' ; ++ptr )
  {
    if(*ptr == '/') slash=ptr;
  }
  if(slash != NULL) ++slash;
  return slash;
}

int32_t RunTestCase( struct ::testcase_s& testCase )
{
  int32_t result = EXIT_STATUS_TESTCASE_FAILED;

// dont want to catch exception as we want to be able to get
// gdb stack trace from the first error
// by default tests should all always pass with no exceptions
  if( testCase.startup )
  {
    testCase.startup();
  }
  try
  {
    result = testCase.function();
  }
  catch( const char* )
  {
    // just catch test fail exception, return is already set to EXIT_STATUS_TESTCASE_FAILED
  }
  if( testCase.cleanup )
  {
    testCase.cleanup();
  }

  return result;
}

int32_t RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput )
{
  int32_t testResult = EXIT_STATUS_TESTCASE_FAILED;

  int32_t pid = fork();
  if( pid == 0 ) // Child process
  {
    if( suppressOutput )
    {
      close(STDOUT_FILENO);
      close(STDERR_FILENO);
    }
    else
    {
      printf("\n");
      for(int32_t i=0; i<80; ++i) printf("#");
      printf("\nTC: %s\n", testCase.name);
      fflush(stdout);
    }

    int32_t status = RunTestCase( testCase );

    if( ! suppressOutput )
    {
      fflush(stdout);
      fflush(stderr);
      fclose(stdout);
      fclose(stderr);
    }
    exit( status );
  }
  else if(pid == -1)
  {
    perror("fork");
    exit(EXIT_STATUS_FORK_FAILED);
  }
  else // Parent process
  {
    int32_t status = 0;
    int32_t childPid = waitpid(pid, &status, 0);
    if( childPid == -1 )
    {
      perror("waitpid");
      exit(EXIT_STATUS_WAITPID_FAILED);
    }
    if( WIFEXITED(status) )
    {
      if( childPid > 0 )
      {
        testResult = WEXITSTATUS(status);
        if( testResult )
        {
          printf("Test case %s failed: %d\n", testCase.name, testResult);
        }
      }
    }
    else if(WIFSIGNALED(status) )
    {
      int32_t signal = WTERMSIG(status);
      testResult = EXIT_STATUS_TESTCASE_ABORTED;
      if( signal == SIGABRT )
      {
        printf("Test case %s failed: test case asserted\n", testCase.name );
      }
      else
      {
        printf("Test case %s failed: exit with signal %s\n", testCase.name, strsignal(WTERMSIG(status)));
      }
    }
    else if(WIFSTOPPED(status))
    {
      printf("Test case %s failed: stopped with signal %s\n", testCase.name, strsignal(WSTOPSIG(status)));
    }
  }
  fflush(stdout);
  fflush(stderr);
  return testResult;
}

void OutputStatistics( const char* processName, int32_t numPasses, int32_t numFailures )
{
  FILE* fp=fopen("summary.xml", "a");
  if( fp != NULL )
  {
    fprintf( fp,
             "  <suite name=\"%s\">\n"
             "    <total_case>%d</total_case>\n"
             "    <pass_case>%d</pass_case>\n"
             "    <pass_rate>%5.2f</pass_rate>\n"
             "    <fail_case>%d</fail_case>\n"
             "    <fail_rate>%5.2f</fail_rate>\n"
             "    <block_case>0</block_case>\n"
             "    <block_rate>0.00</block_rate>\n"
             "    <na_case>0</na_case>\n"
             "    <na_rate>0.00</na_rate>\n"
             "  </suite>\n",
             basename(processName),
             numPasses+numFailures,
             numPasses,
             (float)numPasses/(numPasses+numFailures),
             numFailures,
             (float)numFailures/(numPasses+numFailures) );
    fclose(fp);
  }
}

int32_t RunAll( const char* processName, ::testcase tc_array[] )
{
  int32_t numFailures = 0;
  int32_t numPasses = 0;

  // Run test cases in child process( to kill output/handle signals ), but run serially.
  for( uint32_t i=0; tc_array[i].name; i++)
  {
    int32_t result = RunTestCaseInChildProcess( tc_array[i], false );
    if( result == 0 )
    {
      numPasses++;
    }
    else
    {
      numFailures++;
    }
  }

  OutputStatistics( processName, numPasses, numFailures);

  return numFailures;
}

// Constantly runs up to MAX_NUM_CHILDREN processes
int32_t RunAllInParallel(  const char* processName, ::testcase tc_array[], bool reRunFailed)
{
  int32_t numFailures = 0;
  int32_t numPasses = 0;

  RunningTestCases children;
  std::vector<int32_t> failedTestCases;

  // Fork up to MAX_NUM_CHILDREN processes, then
  // wait. As soon as a proc completes, fork the next.

  int32_t nextTestCase = 0;
  int32_t numRunningChildren = 0;

  while( tc_array[nextTestCase].name || numRunningChildren > 0)
  {
    // Create more children (up to the max number or til the end of the array)
    while( numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name )
    {
      int32_t pid = fork();
      if( pid == 0 ) // Child process
      {
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        exit( RunTestCase( tc_array[nextTestCase] ) );
      }
      else if(pid == -1)
      {
        perror("fork");
        exit(EXIT_STATUS_FORK_FAILED);
      }
      else // Parent process
      {
        TestCase tc(nextTestCase, tc_array[nextTestCase].name);
        children[pid] = tc;
        nextTestCase++;
        numRunningChildren++;
      }
    }

    // Wait for the next child to finish

    int32_t status=0;
    int32_t childPid = waitpid(-1, &status, 0);
    if( childPid == -1 )
    {
      perror("waitpid");
      exit(EXIT_STATUS_WAITPID_FAILED);
    }

    if( WIFEXITED(status) )
    {
      if( childPid > 0 )
      {
        int32_t testResult = WEXITSTATUS(status);
        if( testResult )
        {
          printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult);
          failedTestCases.push_back(children[childPid].testCase);
          numFailures++;
        }
        else
        {
          numPasses++;
        }
        numRunningChildren--;
      }
    }

    else if( WIFSIGNALED(status) || WIFSTOPPED(status))
    {
      status = WIFSIGNALED(status)?WTERMSIG(status):WSTOPSIG(status);

      if( childPid > 0 )
      {
        RunningTestCases::iterator iter = children.find(childPid);
        if( iter != children.end() )
        {
          printf("Test case %s exited with signal %s\n", iter->second.testCaseName, strsignal(status));
          failedTestCases.push_back(iter->second.testCase);
        }
        else
        {
          printf("Unknown child process: %d signaled %s\n", childPid, strsignal(status));
        }

        numFailures++;
        numRunningChildren--;
      }
    }
  }

  OutputStatistics( processName, numPasses, numFailures );

  if( reRunFailed )
  {
    for( uint32_t i=0; i<failedTestCases.size(); i++)
    {
      char* testCaseStrapline;
      int32_t numChars = asprintf(&testCaseStrapline, "Test case %s", tc_array[failedTestCases[i]].name );
      printf("\n%s\n", testCaseStrapline);
      for(int32_t j=0; j<numChars; j++)
      {
        printf("=");
      }
      printf("\n");
      RunTestCaseInChildProcess( tc_array[failedTestCases[i] ], false );
    }
  }

  return numFailures;
}

int32_t FindAndRunTestCase(::testcase tc_array[], const char* testCaseName)
{
  int32_t result = EXIT_STATUS_TESTCASE_NOT_FOUND;

  for( int32_t i = 0; tc_array[i].name; i++ )
  {
    if( !strcmp(testCaseName, tc_array[i].name) )
    {
      return RunTestCase( tc_array[i] );
    }
  }

  printf("Unknown testcase name: \"%s\"\n", testCaseName);
  return result;
}

void Usage(const char* program)
{
  printf("Usage: \n"
         "   %s <testcase name>\t\t Execute a test case\n"
         "   %s \t\t Execute all test cases in parallel\n"
         "   %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n"
         "   %s -s\t\t Execute all test cases serially\n",
         program, program, program, program);
}

} // namespace
<commit_msg>Fix random crash in TCT when stderr is closed but we try to write to it<commit_after>/*
 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "test-harness.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <map>
#include <cstring>
#include <testcase.h>
#include <fcntl.h>

namespace TestHarness
{

typedef std::map<int32_t, TestCase> RunningTestCases;

const char* basename(const char* path)
{
  const char* ptr=path;
  const char* slash=NULL;
  for( ; *ptr != '\0' ; ++ptr )
  {
    if(*ptr == '/') slash=ptr;
  }
  if(slash != NULL) ++slash;
  return slash;
}

void SuppressLogOutput()
{
  // Close stdout and stderr to suppress the log output
  close(STDOUT_FILENO); // File descriptor number for stdout is 1
  close(STDERR_FILENO); // File descriptor number for stderr is 2

  // The POSIX specification requires that /dev/null must be provided,
  // The open function always chooses the lowest unused file descriptor
  // It is sufficient for stdout to be writable.
  open("/dev/null", O_WRONLY); // Redirect file descriptor number 1 (i.e. stdout) to /dev/null
  // When stderr is opened it must be both readable and writable.
  open("/dev/null", O_RDWR); // Redirect file descriptor number 2 (i.e. stderr) to /dev/null
}

int32_t RunTestCase( struct ::testcase_s& testCase )
{
  int32_t result = EXIT_STATUS_TESTCASE_FAILED;

// dont want to catch exception as we want to be able to get
// gdb stack trace from the first error
// by default tests should all always pass with no exceptions
  if( testCase.startup )
  {
    testCase.startup();
  }
  try
  {
    result = testCase.function();
  }
  catch( const char* )
  {
    // just catch test fail exception, return is already set to EXIT_STATUS_TESTCASE_FAILED
  }
  if( testCase.cleanup )
  {
    testCase.cleanup();
  }

  return result;
}

int32_t RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput )
{
  int32_t testResult = EXIT_STATUS_TESTCASE_FAILED;

  int32_t pid = fork();
  if( pid == 0 ) // Child process
  {
    if( suppressOutput )
    {
      SuppressLogOutput();
    }
    else
    {
      printf("\n");
      for(int32_t i=0; i<80; ++i) printf("#");
      printf("\nTC: %s\n", testCase.name);
      fflush(stdout);
    }

    int32_t status = RunTestCase( testCase );

    if( ! suppressOutput )
    {
      fflush(stdout);
      fflush(stderr);
      fclose(stdout);
      fclose(stderr);
    }
    exit( status );
  }
  else if(pid == -1)
  {
    perror("fork");
    exit(EXIT_STATUS_FORK_FAILED);
  }
  else // Parent process
  {
    int32_t status = 0;
    int32_t childPid = waitpid(pid, &status, 0);
    if( childPid == -1 )
    {
      perror("waitpid");
      exit(EXIT_STATUS_WAITPID_FAILED);
    }
    if( WIFEXITED(status) )
    {
      if( childPid > 0 )
      {
        testResult = WEXITSTATUS(status);
        if( testResult )
        {
          printf("Test case %s failed: %d\n", testCase.name, testResult);
        }
      }
    }
    else if(WIFSIGNALED(status) )
    {
      int32_t signal = WTERMSIG(status);
      testResult = EXIT_STATUS_TESTCASE_ABORTED;
      if( signal == SIGABRT )
      {
        printf("Test case %s failed: test case asserted\n", testCase.name );
      }
      else
      {
        printf("Test case %s failed: exit with signal %s\n", testCase.name, strsignal(WTERMSIG(status)));
      }
    }
    else if(WIFSTOPPED(status))
    {
      printf("Test case %s failed: stopped with signal %s\n", testCase.name, strsignal(WSTOPSIG(status)));
    }
  }
  fflush(stdout);
  fflush(stderr);
  return testResult;
}

void OutputStatistics( const char* processName, int32_t numPasses, int32_t numFailures )
{
  FILE* fp=fopen("summary.xml", "a");
  if( fp != NULL )
  {
    fprintf( fp,
             "  <suite name=\"%s\">\n"
             "    <total_case>%d</total_case>\n"
             "    <pass_case>%d</pass_case>\n"
             "    <pass_rate>%5.2f</pass_rate>\n"
             "    <fail_case>%d</fail_case>\n"
             "    <fail_rate>%5.2f</fail_rate>\n"
             "    <block_case>0</block_case>\n"
             "    <block_rate>0.00</block_rate>\n"
             "    <na_case>0</na_case>\n"
             "    <na_rate>0.00</na_rate>\n"
             "  </suite>\n",
             basename(processName),
             numPasses+numFailures,
             numPasses,
             (float)numPasses/(numPasses+numFailures),
             numFailures,
             (float)numFailures/(numPasses+numFailures) );
    fclose(fp);
  }
}

int32_t RunAll( const char* processName, ::testcase tc_array[] )
{
  int32_t numFailures = 0;
  int32_t numPasses = 0;

  // Run test cases in child process( to kill output/handle signals ), but run serially.
  for( uint32_t i=0; tc_array[i].name; i++)
  {
    int32_t result = RunTestCaseInChildProcess( tc_array[i], false );
    if( result == 0 )
    {
      numPasses++;
    }
    else
    {
      numFailures++;
    }
  }

  OutputStatistics( processName, numPasses, numFailures);

  return numFailures;
}

// Constantly runs up to MAX_NUM_CHILDREN processes
int32_t RunAllInParallel(  const char* processName, ::testcase tc_array[], bool reRunFailed)
{
  int32_t numFailures = 0;
  int32_t numPasses = 0;

  RunningTestCases children;
  std::vector<int32_t> failedTestCases;

  // Fork up to MAX_NUM_CHILDREN processes, then
  // wait. As soon as a proc completes, fork the next.

  int32_t nextTestCase = 0;
  int32_t numRunningChildren = 0;

  while( tc_array[nextTestCase].name || numRunningChildren > 0)
  {
    // Create more children (up to the max number or til the end of the array)
    while( numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name )
    {
      int32_t pid = fork();
      if( pid == 0 ) // Child process
      {
        SuppressLogOutput();
        exit( RunTestCase( tc_array[nextTestCase] ) );
      }
      else if(pid == -1)
      {
        perror("fork");
        exit(EXIT_STATUS_FORK_FAILED);
      }
      else // Parent process
      {
        TestCase tc(nextTestCase, tc_array[nextTestCase].name);
        children[pid] = tc;
        nextTestCase++;
        numRunningChildren++;
      }
    }

    // Wait for the next child to finish

    int32_t status=0;
    int32_t childPid = waitpid(-1, &status, 0);
    if( childPid == -1 )
    {
      perror("waitpid");
      exit(EXIT_STATUS_WAITPID_FAILED);
    }

    if( WIFEXITED(status) )
    {
      if( childPid > 0 )
      {
        int32_t testResult = WEXITSTATUS(status);
        if( testResult )
        {
          printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult);
          failedTestCases.push_back(children[childPid].testCase);
          numFailures++;
        }
        else
        {
          numPasses++;
        }
        numRunningChildren--;
      }
    }

    else if( WIFSIGNALED(status) || WIFSTOPPED(status))
    {
      status = WIFSIGNALED(status)?WTERMSIG(status):WSTOPSIG(status);

      if( childPid > 0 )
      {
        RunningTestCases::iterator iter = children.find(childPid);
        if( iter != children.end() )
        {
          printf("Test case %s exited with signal %s\n", iter->second.testCaseName, strsignal(status));
          failedTestCases.push_back(iter->second.testCase);
        }
        else
        {
          printf("Unknown child process: %d signaled %s\n", childPid, strsignal(status));
        }

        numFailures++;
        numRunningChildren--;
      }
    }
  }

  OutputStatistics( processName, numPasses, numFailures );

  if( reRunFailed )
  {
    for( uint32_t i=0; i<failedTestCases.size(); i++)
    {
      char* testCaseStrapline;
      int32_t numChars = asprintf(&testCaseStrapline, "Test case %s", tc_array[failedTestCases[i]].name );
      printf("\n%s\n", testCaseStrapline);
      for(int32_t j=0; j<numChars; j++)
      {
        printf("=");
      }
      printf("\n");
      RunTestCaseInChildProcess( tc_array[failedTestCases[i] ], false );
    }
  }

  return numFailures;
}

int32_t FindAndRunTestCase(::testcase tc_array[], const char* testCaseName)
{
  int32_t result = EXIT_STATUS_TESTCASE_NOT_FOUND;

  for( int32_t i = 0; tc_array[i].name; i++ )
  {
    if( !strcmp(testCaseName, tc_array[i].name) )
    {
      return RunTestCase( tc_array[i] );
    }
  }

  printf("Unknown testcase name: \"%s\"\n", testCaseName);
  return result;
}

void Usage(const char* program)
{
  printf("Usage: \n"
         "   %s <testcase name>\t\t Execute a test case\n"
         "   %s \t\t Execute all test cases in parallel\n"
         "   %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n"
         "   %s -s\t\t Execute all test cases serially\n",
         program, program, program, program);
}

} // namespace
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include <functional>

namespace warbler {

    enum ConsoleArgType
    {
        STRING,
        INT,
        FLOAT
    };

    struct ConsoleArg
    {
        ConsoleArg() : intValue(0), floatValue(0.0) {}

        ConsoleArgType type;
        int intValue;
        double floatValue;
        std::string stringValue;
    };

    typedef std::vector<const ConsoleArg> t_consoleArgs;
    typedef std::shared_ptr<t_consoleArgs> t_consoleArgs_ptr;
    typedef std::function<void(t_consoleArgs_ptr)> t_commandHandler;
    typedef std::vector<ConsoleArgType> t_consoleArgTypes;
    typedef std::shared_ptr<t_consoleArgTypes> t_consoleArgTypes_ptr;

    struct ConsoleCommand
    {
        ConsoleCommand() : handler(nullptr), argTypes(nullptr) {}
        ConsoleCommand(t_commandHandler handler, t_consoleArgTypes_ptr argTypes) : handler(handler), argTypes(argTypes)
        {
        }

        t_commandHandler handler;
        t_consoleArgTypes_ptr argTypes;
    };

    struct ConsoleCommandSignature
    {
        ConsoleCommandSignature(): argCount(0) {}

        std::string name;
        int argCount;
    };

    typedef std::vector<const ConsoleCommand> t_commandHandlers;
    typedef std::shared_ptr<t_commandHandlers> t_commandHandlers_ptr;
    typedef std::unordered_map<std::string, t_commandHandlers_ptr> t_commandHandlerMap;

    class Console
    {
    public:
        Console();
        Console(Console& rhs);
        virtual ~Console();

        void registerCommand(const std::string &name, t_commandHandler, t_consoleArgTypes_ptr argTypes);
        void executeCommand(const std::string command) const;

    private:
        t_commandHandlers_ptr _getHandlersByName(const std::string &name) const;
        ConsoleCommandSignature _getCommandSignature(const std::string &input) const;
        ConsoleCommand _getConsoleCommand(const ConsoleCommandSignature &signature) const;
        t_consoleArgs_ptr _getConsoleArgs(const std::string &input, const ConsoleCommand command) const;

        t_commandHandlerMap _commandHandlerMap = t_commandHandlerMap();

        static bool _isNumber(const std::string &input);
        static bool _isInteger(const std::string &input);
    };
}<commit_msg>Remove unnecessary initializer.<commit_after>#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include <functional>

namespace warbler {

    enum ConsoleArgType
    {
        STRING,
        INT,
        FLOAT
    };

    struct ConsoleArg
    {
        ConsoleArg() : intValue(0), floatValue(0.0) {}

        ConsoleArgType type;
        int intValue;
        double floatValue;
        std::string stringValue;
    };

    typedef std::vector<const ConsoleArg> t_consoleArgs;
    typedef std::shared_ptr<t_consoleArgs> t_consoleArgs_ptr;
    typedef std::function<void(t_consoleArgs_ptr)> t_commandHandler;
    typedef std::vector<ConsoleArgType> t_consoleArgTypes;
    typedef std::shared_ptr<t_consoleArgTypes> t_consoleArgTypes_ptr;

    struct ConsoleCommand
    {
        ConsoleCommand() : handler(nullptr), argTypes(nullptr) {}
        ConsoleCommand(t_commandHandler handler, t_consoleArgTypes_ptr argTypes) : handler(handler), argTypes(argTypes)
        {
        }

        t_commandHandler handler;
        t_consoleArgTypes_ptr argTypes;
    };

    struct ConsoleCommandSignature
    {
        ConsoleCommandSignature(): argCount(0) {}

        std::string name;
        int argCount;
    };

    typedef std::vector<const ConsoleCommand> t_commandHandlers;
    typedef std::shared_ptr<t_commandHandlers> t_commandHandlers_ptr;
    typedef std::unordered_map<std::string, t_commandHandlers_ptr> t_commandHandlerMap;

    class Console
    {
    public:
        Console();
        Console(Console& rhs);
        virtual ~Console();

        void registerCommand(const std::string &name, t_commandHandler, t_consoleArgTypes_ptr argTypes);
        void executeCommand(const std::string command) const;

    private:
        t_commandHandlers_ptr _getHandlersByName(const std::string &name) const;
        ConsoleCommandSignature _getCommandSignature(const std::string &input) const;
        ConsoleCommand _getConsoleCommand(const ConsoleCommandSignature &signature) const;
        t_consoleArgs_ptr _getConsoleArgs(const std::string &input, const ConsoleCommand command) const;

        t_commandHandlerMap _commandHandlerMap;

        static bool _isNumber(const std::string &input);
        static bool _isInteger(const std::string &input);
    };
}<|endoftext|>