<commit_before>#include <iostream>
#include <fstream>
#include <memory>

#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "LLVM_Output.h"
#include "RemoveTrivialForLoops.h"

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

// Replace the parameter objects of loads/stores witha new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  There's a huge hack
            // here, in that the scalars must be last for the scalar
            // arguments to shadow the symbols of the buffer.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX
                // vector. Generally, they should be page aligned ION
                // buffers, so this should be a reasonable
                // requirement.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                // sizeof(pointer) will always fit into int32
                arg_sizes.push_back(Expr((int32_t)sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                // sizeof(scalar-type) will always fit into int32
                arg_sizes.push_back(Expr((int32_t)i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((int32_t)0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions().empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        device_code.compile(Outputs().bitcode("hex.bc"));

        string hex_command = "${HL_HEXAGON_TOOLS}/bin/hexagon-clang hex.bc -fPIC -O3 -Wno-override-module -shared -o hex.so";
        if (device_code.target().has_feature(Target::HVX_v62)) {
            hex_command += " -mv62";
        }
        if (device_code.target().has_feature(Target::HVX_128)) {
            hex_command += " -mhvx-double";
        }
        int result = system(hex_command.c_str());
        internal_assert(result == 0) << "hexagon-clang failed\n";

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr((uint64_t) code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target(Target::NoOS, Target::Hexagon, 32);
    for (Target::Feature i : {Target::Debug, Target::NoAsserts, Target::HVX_64, Target::HVX_128, Target::HVX_v62}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<commit_msg>arg_sizes need to be identical types, so use uint64_t everywhere<commit_after>#include <iostream>
#include <fstream>
#include <memory>

#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "LLVM_Output.h"
#include "RemoveTrivialForLoops.h"

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

// Replace the parameter objects of loads/stores witha new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  There's a huge hack
            // here, in that the scalars must be last for the scalar
            // arguments to shadow the symbols of the buffer.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX
                // vector. Generally, they should be page aligned ION
                // buffers, so this should be a reasonable
                // requirement.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((uint64_t) sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                // sizeof(scalar-type) will always fit into int32
                arg_sizes.push_back(Expr((uint64_t) i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((uint64_t) 0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions().empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        device_code.compile(Outputs().bitcode("hex.bc"));

        string hex_command = "${HL_HEXAGON_TOOLS}/bin/hexagon-clang hex.bc -fPIC -O3 -Wno-override-module -shared -o hex.so";
        if (device_code.target().has_feature(Target::HVX_v62)) {
            hex_command += " -mv62";
        }
        if (device_code.target().has_feature(Target::HVX_128)) {
            hex_command += " -mhvx-double";
        }
        int result = system(hex_command.c_str());
        internal_assert(result == 0) << "hexagon-clang failed\n";

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr((uint64_t) code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target(Target::NoOS, Target::Hexagon, 32);
    for (Target::Feature i : {Target::Debug, Target::NoAsserts, Target::HVX_64, Target::HVX_128, Target::HVX_v62}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#include <fstream>

#include <sys/stat.h>

#include "cpp_utils/stop_watch.hpp"

#include "trainer/rbm_training_context.hpp"
#include "layer_traits.hpp"
#include "dbn_traits.hpp"

namespace dll {

template <typename R>
struct default_rbm_watcher {
    cpp::stop_watch<std::chrono::seconds> watch;

    template <typename RBM = R>
    void training_begin(const RBM& rbm) {
        using rbm_t = std::decay_t<RBM>;

        std::cout << "Train RBM with \"" << RBM::desc::template trainer_t<RBM, false>::name() << "\"" << std::endl;

        rbm.display();

        std::cout << "With parameters:" << std::endl;

        if(std::is_same<typename rbm_t::weight, float>::value){
            std::cout << "   single-precision" << std::endl;
        } else if(std::is_same<typename rbm_t::weight, double>::value){
            std::cout << "   double-precision" << std::endl;
        } else {
            std::cout << "   unknown-precision (something is wrong...)" << std::endl;
        }

        std::cout << "   learning_rate=" << rbm.learning_rate << std::endl;
        std::cout << "   batch_size=" << get_batch_size(rbm) << std::endl;

        if (rbm_layer_traits<RBM>::has_momentum()) {
            std::cout << "   momentum=" << rbm.momentum << std::endl;
        }

        if (rbm_layer_traits<RBM>::has_clip_gradients()) {
            std::cout << "   gradient clip=" << rbm.gradient_clip << std::endl;
        }

        if (w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1 || w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L1)=" << rbm.l1_weight_cost << std::endl;
        }

        if (w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L2 || w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L2)=" << rbm.l2_weight_cost << std::endl;
        }

        if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::LEE) {
            std::cout << "   Sparsity (Lee): pbias=" << rbm.pbias << std::endl;
            std::cout << "   Sparsity (Lee): pbias_lambda=" << rbm.pbias_lambda << std::endl;
        } else if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::GLOBAL_TARGET) {
            std::cout << "   sparsity_target(Global)=" << rbm.sparsity_target << std::endl;
        } else if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::LOCAL_TARGET) {
            std::cout << "   sparsity_target(Local)=" << rbm.sparsity_target << std::endl;
        }
    }

    template <typename RBM = R>
    void epoch_end(std::size_t epoch, const rbm_training_context& context, const RBM& /*rbm*/) {
        char formatted[1024];
        if (rbm_layer_traits<RBM>::free_energy()) {
            snprintf(formatted, 1024, "epoch %ld - Reconstruction error: %.5f - Free energy: %.3f - Sparsity: %.5f", epoch,
                     context.reconstruction_error, context.free_energy, context.sparsity);
        } else {
            snprintf(formatted, 1024, "epoch %ld - Reconstruction error: %.5f - Sparsity: %.5f", epoch, context.reconstruction_error, context.sparsity);
        }
        std::cout << formatted << std::endl;
    }

    template <typename RBM = R>
    void batch_end(const RBM& /* rbm */, const rbm_training_context& context, std::size_t batch, std::size_t batches) {
        char formatted[1024];
        sprintf(formatted, "Batch %ld/%ld - Reconstruction error: %.5f - Sparsity: %.5f", batch, batches,
                context.batch_error, context.batch_sparsity);
        std::cout << formatted << std::endl;
    }

    template <typename RBM = R>
    void training_end(const RBM&) {
        std::cout << "Training took " << watch.elapsed() << "s" << std::endl;
    }
};

template <typename DBN>
struct default_dbn_watcher {
    static constexpr bool ignore_sub  = false;
    static constexpr bool replace_sub = false;

    size_t ft_max_epochs = 0;
    dll::stop_timer ft_epoch_timer;
    dll::stop_timer ft_batch_timer;

    cpp::stop_watch<std::chrono::seconds> watch;

    void pretraining_begin(const DBN& /*dbn*/, std::size_t max_epochs) {
        std::cout << "DBN: Pretraining begin for " << max_epochs << " epochs" << std::endl;
    }

    template <typename RBM>
    void pretrain_layer(const DBN& /*dbn*/, std::size_t I, const RBM& rbm, std::size_t input_size) {
        if (input_size) {
            std::cout << "DBN: Pretrain layer " << I << " (" << rbm.to_short_string() << ") with " << input_size << " entries" << std::endl;
        } else {
            std::cout << "DBN: Pretrain layer " << I << " (" << rbm.to_short_string() << ")" << std::endl;
        }
    }

    void pretraining_end(const DBN& /*dbn*/) {
        std::cout << "DBN: Pretraining finished after " << watch.elapsed() << "s" << std::endl;
    }

    void pretraining_batch(const DBN& /*dbn*/, std::size_t batch) {
        std::cout << "DBN: Pretraining batch " << batch << std::endl;
    }

    /*!
     * \brief Fine-tuning of the given network just started
     * \param dbn The DBN that is being trained
     * \param max_epochs The maximum number of epochs to train the network
     */
    void fine_tuning_begin(const DBN& dbn, size_t max_epochs) {
        std::cout << "Train the network with \"" << DBN::desc::template trainer_t<DBN>::name() << "\"" << std::endl;
        std::cout << "With parameters:" << std::endl;
        std::cout << "          epochs=" << max_epochs << std::endl;
        std::cout << "      batch_size=" << DBN::batch_size << std::endl;
        std::cout << "   learning_rate=" << dbn.learning_rate << std::endl;

        if (dbn_traits<DBN>::has_momentum()) {
            std::cout << "   momentum=" << dbn.momentum << std::endl;
        }

        if (w_decay(dbn_traits<DBN>::decay()) == decay_type::L1 || w_decay(dbn_traits<DBN>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L1)=" << dbn.l1_weight_cost << std::endl;
        }

        if (w_decay(dbn_traits<DBN>::decay()) == decay_type::L2 || w_decay(dbn_traits<DBN>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L2)=" << dbn.l2_weight_cost << std::endl;
        }

        if (dbn_traits<DBN>::lr_driver() == lr_driver_type::BOLD) {
            std::cout << "   lr_driver(BOLD)=" << dbn.lr_bold_inc << ":" << dbn.lr_bold_dec << std::endl;
        }

        if (dbn_traits<DBN>::lr_driver() == lr_driver_type::STEP) {
            std::cout << "   lr_driver(STEP)=" << dbn.lr_step_size << ":" << dbn.lr_step_gamma << std::endl;
        }

        ft_max_epochs = max_epochs;
    }

    /*!
     * \brief One fine-tuning epoch is starting
     * \param epoch The current epoch
     * \param dbn The network being trained
     */
    void ft_epoch_start(std::size_t epoch, const DBN& dbn) {
        cpp_unused(epoch);
        cpp_unused(dbn);
        ft_epoch_timer.start();
    }

    /*!
     * \brief One fine-tuning epoch is over
     * \param epoch The current epoch
     * \param error The current error
     * \param loss The current loss
     * \param dbn The network being trained
     */
    void ft_epoch_end(std::size_t epoch, double error, double loss, const DBN& dbn) {
        cpp_unused(dbn);
        auto duration = ft_epoch_timer.stop();

        if /*constexpr*/ (dbn_traits<DBN>::error_on_epoch()){
            printf("Epoch %3ld/%ld - Classification error: %.5f Loss: %.5f Time %ldms \n", epoch, ft_max_epochs, error, loss, duration);
        } else {
            printf("Epoch %3ld/%ld - Loss: %.5f Time %ldms \n", epoch, ft_max_epochs, loss, duration);
        }

        std::cout.flush();
    }

    void ft_batch_start(size_t epoch, const DBN& dbn) {
        cpp_unused(epoch);
        cpp_unused(dbn);
        ft_epoch_timer.start();
    }

    void ft_batch_end(size_t epoch, size_t batch, size_t batches, double batch_error, double batch_loss, const DBN&) {
        auto duration = ft_epoch_timer.stop();
        printf("Epoch %3ld:%ld/%ld- B. Error: %.5f B. Loss: %.5f Time %ldms\n", epoch, batch, batches, batch_error, batch_loss, duration);
        std::cout.flush();
    }

    void ft_batch_end(size_t epoch, double batch_error, double batch_loss, const DBN&) {
        auto duration = ft_epoch_timer.stop();
        printf("Epoch %3ld - B.Error: %.5f B.Loss: %.5f Time %ldms\n", epoch, batch_error, batch_loss, duration);
        std::cout.flush();
    }

    void lr_adapt(const DBN& dbn) {
        printf("driver: learning rate adapted to %.5f \n", dbn.learning_rate);
        std::cout.flush();
    }

    void fine_tuning_end(const DBN&) {
        std::cout << "Training took " << watch.elapsed() << "s" << std::endl;
    }
};

template <typename DBN>
struct silent_dbn_watcher : default_dbn_watcher<DBN> {
    static constexpr bool ignore_sub  = true;
    static constexpr bool replace_sub = false;
};

template <typename DBN>
struct mute_dbn_watcher {
    static constexpr bool ignore_sub  = true;
    static constexpr bool replace_sub = false;

    void pretraining_begin(const DBN& /*dbn*/, std::size_t /*max_epochs*/) {}

    template <typename RBM>
    void pretrain_layer(const DBN& /*dbn*/, std::size_t /*I*/, const RBM& /*rbm*/, std::size_t /*input_size*/) {}

    void pretraining_end(const DBN& /*dbn*/) {}

    void pretraining_batch(const DBN& /*dbn*/, std::size_t /*batch*/) {}

    void fine_tuning_begin(const DBN& /*dbn*/, size_t /*max_epochs*/) {}

    void ft_epoch_start(std::size_t /*epoch*/, const DBN& /*dbn*/) {}

    void ft_epoch_end(std::size_t /*epoch*/, double /*error*/, const DBN& /*dbn*/) {}

    void ft_batch_start(size_t /*epoch*/, const DBN&) {}
    void ft_batch_end(size_t /*epoch*/, size_t /*batch*/, size_t /*batches*/, double /*batch_error*/, const DBN& /*dbn*/) {}
    void ft_batch_end(size_t /*epoch*/, double /*batch_error*/, const DBN& /*dbn*/) {}

    void lr_adapt(const DBN& /*dbn*/) {}

    void fine_tuning_end(const DBN& /*dbn*/) {}
};

//TODO This is currently useless

template <typename R>
struct histogram_watcher {
    default_rbm_watcher<R> parent;

    template <typename RBM = R>
    void training_begin(const RBM& rbm) {
        parent.training_begin(rbm);
    }

    template <typename RBM = R>
    void epoch_end(std::size_t epoch, double error, double /*free_energy*/, const RBM& rbm) {
        parent.epoch_end(epoch, error, rbm);
    }

    template <typename RBM = R>
    void batch_end(const RBM& rbm, const rbm_training_context& context, std::size_t batch, std::size_t batches) {
        parent.batch_end(rbm, context, batch, batches);
    }

    template <typename RBM = R>
    void training_end(const RBM& rbm) {
        parent.training_end(rbm);
    }

    void generate_hidden_images(std::size_t epoch, const R& rbm) {
        mkdir("reports", 0777);

        auto folder = "reports/epoch_" + std::to_string(epoch);
        mkdir(folder.c_str(), 0777);

        for (std::size_t j = 0; j < R::num_hidden; ++j) {
            auto path = folder + "/h_" + std::to_string(j) + ".dat";
            std::ofstream file(path, std::ios::out);

            if (!file) {
                std::cout << "Could not open file " << path << std::endl;
            } else {
                std::size_t i = R::num_visible;
                while (i > 0) {
                    --i;

                    auto value = rbm.w(i, j);
                    file << static_cast<std::size_t>(value > 0 ? static_cast<std::size_t>(value * 255.0) << 8 : static_cast<std::size_t>(-value * 255.0) << 16) << " ";
                }

                file << std::endl;
                file.close();
            }
        }
    }

    void generate_histograms(std::size_t epoch, const R& rbm) {
        mkdir("reports", 0777);

        auto folder = "reports/epoch_" + std::to_string(epoch);
        mkdir(folder.c_str(), 0777);

        generate_histogram(folder + "/weights.dat", rbm.w);
        generate_histogram(folder + "/visibles.dat", rbm.a);
        generate_histogram(folder + "/hiddens.dat", rbm.b);
    }

    template <typename Container>
    void generate_histogram(const std::string& path, const Container& weights) {
        std::ofstream file(path, std::ios::out);

        if (!file) {
            std::cout << "Could not open file " << path << std::endl;
        } else {
            for (auto& weight : weights) {
                file << weight << std::endl;
            }

            file << std::endl;
            file.close();
        }
    }
};

} //end of dll namespace
<commit_msg>Fix batch timings...<commit_after>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#include <fstream>

#include <sys/stat.h>

#include "cpp_utils/stop_watch.hpp"

#include "trainer/rbm_training_context.hpp"
#include "layer_traits.hpp"
#include "dbn_traits.hpp"

namespace dll {

template <typename R>
struct default_rbm_watcher {
    cpp::stop_watch<std::chrono::seconds> watch;

    template <typename RBM = R>
    void training_begin(const RBM& rbm) {
        using rbm_t = std::decay_t<RBM>;

        std::cout << "Train RBM with \"" << RBM::desc::template trainer_t<RBM, false>::name() << "\"" << std::endl;

        rbm.display();

        std::cout << "With parameters:" << std::endl;

        if(std::is_same<typename rbm_t::weight, float>::value){
            std::cout << "   single-precision" << std::endl;
        } else if(std::is_same<typename rbm_t::weight, double>::value){
            std::cout << "   double-precision" << std::endl;
        } else {
            std::cout << "   unknown-precision (something is wrong...)" << std::endl;
        }

        std::cout << "   learning_rate=" << rbm.learning_rate << std::endl;
        std::cout << "   batch_size=" << get_batch_size(rbm) << std::endl;

        if (rbm_layer_traits<RBM>::has_momentum()) {
            std::cout << "   momentum=" << rbm.momentum << std::endl;
        }

        if (rbm_layer_traits<RBM>::has_clip_gradients()) {
            std::cout << "   gradient clip=" << rbm.gradient_clip << std::endl;
        }

        if (w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1 || w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L1)=" << rbm.l1_weight_cost << std::endl;
        }

        if (w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L2 || w_decay(rbm_layer_traits<RBM>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L2)=" << rbm.l2_weight_cost << std::endl;
        }

        if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::LEE) {
            std::cout << "   Sparsity (Lee): pbias=" << rbm.pbias << std::endl;
            std::cout << "   Sparsity (Lee): pbias_lambda=" << rbm.pbias_lambda << std::endl;
        } else if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::GLOBAL_TARGET) {
            std::cout << "   sparsity_target(Global)=" << rbm.sparsity_target << std::endl;
        } else if (rbm_layer_traits<RBM>::sparsity_method() == sparsity_method::LOCAL_TARGET) {
            std::cout << "   sparsity_target(Local)=" << rbm.sparsity_target << std::endl;
        }
    }

    template <typename RBM = R>
    void epoch_end(std::size_t epoch, const rbm_training_context& context, const RBM& /*rbm*/) {
        char formatted[1024];
        if (rbm_layer_traits<RBM>::free_energy()) {
            snprintf(formatted, 1024, "epoch %ld - Reconstruction error: %.5f - Free energy: %.3f - Sparsity: %.5f", epoch,
                     context.reconstruction_error, context.free_energy, context.sparsity);
        } else {
            snprintf(formatted, 1024, "epoch %ld - Reconstruction error: %.5f - Sparsity: %.5f", epoch, context.reconstruction_error, context.sparsity);
        }
        std::cout << formatted << std::endl;
    }

    template <typename RBM = R>
    void batch_end(const RBM& /* rbm */, const rbm_training_context& context, std::size_t batch, std::size_t batches) {
        char formatted[1024];
        sprintf(formatted, "Batch %ld/%ld - Reconstruction error: %.5f - Sparsity: %.5f", batch, batches,
                context.batch_error, context.batch_sparsity);
        std::cout << formatted << std::endl;
    }

    template <typename RBM = R>
    void training_end(const RBM&) {
        std::cout << "Training took " << watch.elapsed() << "s" << std::endl;
    }
};

template <typename DBN>
struct default_dbn_watcher {
    static constexpr bool ignore_sub  = false;
    static constexpr bool replace_sub = false;

    size_t ft_max_epochs = 0;
    dll::stop_timer ft_epoch_timer;
    dll::stop_timer ft_batch_timer;

    cpp::stop_watch<std::chrono::seconds> watch;

    void pretraining_begin(const DBN& /*dbn*/, std::size_t max_epochs) {
        std::cout << "DBN: Pretraining begin for " << max_epochs << " epochs" << std::endl;
    }

    template <typename RBM>
    void pretrain_layer(const DBN& /*dbn*/, std::size_t I, const RBM& rbm, std::size_t input_size) {
        if (input_size) {
            std::cout << "DBN: Pretrain layer " << I << " (" << rbm.to_short_string() << ") with " << input_size << " entries" << std::endl;
        } else {
            std::cout << "DBN: Pretrain layer " << I << " (" << rbm.to_short_string() << ")" << std::endl;
        }
    }

    void pretraining_end(const DBN& /*dbn*/) {
        std::cout << "DBN: Pretraining finished after " << watch.elapsed() << "s" << std::endl;
    }

    void pretraining_batch(const DBN& /*dbn*/, std::size_t batch) {
        std::cout << "DBN: Pretraining batch " << batch << std::endl;
    }

    /*!
     * \brief Fine-tuning of the given network just started
     * \param dbn The DBN that is being trained
     * \param max_epochs The maximum number of epochs to train the network
     */
    void fine_tuning_begin(const DBN& dbn, size_t max_epochs) {
        std::cout << "Train the network with \"" << DBN::desc::template trainer_t<DBN>::name() << "\"" << std::endl;
        std::cout << "With parameters:" << std::endl;
        std::cout << "          epochs=" << max_epochs << std::endl;
        std::cout << "      batch_size=" << DBN::batch_size << std::endl;
        std::cout << "   learning_rate=" << dbn.learning_rate << std::endl;

        if (dbn_traits<DBN>::has_momentum()) {
            std::cout << "   momentum=" << dbn.momentum << std::endl;
        }

        if (w_decay(dbn_traits<DBN>::decay()) == decay_type::L1 || w_decay(dbn_traits<DBN>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L1)=" << dbn.l1_weight_cost << std::endl;
        }

        if (w_decay(dbn_traits<DBN>::decay()) == decay_type::L2 || w_decay(dbn_traits<DBN>::decay()) == decay_type::L1L2) {
            std::cout << "   weight_cost(L2)=" << dbn.l2_weight_cost << std::endl;
        }

        if (dbn_traits<DBN>::lr_driver() == lr_driver_type::BOLD) {
            std::cout << "   lr_driver(BOLD)=" << dbn.lr_bold_inc << ":" << dbn.lr_bold_dec << std::endl;
        }

        if (dbn_traits<DBN>::lr_driver() == lr_driver_type::STEP) {
            std::cout << "   lr_driver(STEP)=" << dbn.lr_step_size << ":" << dbn.lr_step_gamma << std::endl;
        }

        ft_max_epochs = max_epochs;
    }

    /*!
     * \brief One fine-tuning epoch is starting
     * \param epoch The current epoch
     * \param dbn The network being trained
     */
    void ft_epoch_start(std::size_t epoch, const DBN& dbn) {
        cpp_unused(epoch);
        cpp_unused(dbn);
        ft_epoch_timer.start();
    }

    /*!
     * \brief One fine-tuning epoch is over
     * \param epoch The current epoch
     * \param error The current error
     * \param loss The current loss
     * \param dbn The network being trained
     */
    void ft_epoch_end(std::size_t epoch, double error, double loss, const DBN& dbn) {
        cpp_unused(dbn);
        auto duration = ft_epoch_timer.stop();

        if /*constexpr*/ (dbn_traits<DBN>::error_on_epoch()){
            printf("Epoch %3ld/%ld - Classification error: %.5f Loss: %.5f Time %ldms \n", epoch, ft_max_epochs, error, loss, duration);
        } else {
            printf("Epoch %3ld/%ld - Loss: %.5f Time %ldms \n", epoch, ft_max_epochs, loss, duration);
        }

        std::cout.flush();
    }

    void ft_batch_start(size_t epoch, const DBN& dbn) {
        cpp_unused(epoch);
        cpp_unused(dbn);
        ft_batch_timer.start();
    }

    void ft_batch_end(size_t epoch, size_t batch, size_t batches, double batch_error, double batch_loss, const DBN&) {
        auto duration = ft_batch_timer.stop();
        printf("Epoch %3ld:%ld/%ld- B. Error: %.5f B. Loss: %.5f Time %ldms\n", epoch, batch, batches, batch_error, batch_loss, duration);
        std::cout.flush();
    }

    void ft_batch_end(size_t epoch, double batch_error, double batch_loss, const DBN&) {
        auto duration = ft_batch_timer.stop();
        printf("Epoch %3ld - B.Error: %.5f B.Loss: %.5f Time %ldms\n", epoch, batch_error, batch_loss, duration);
        std::cout.flush();
    }

    void lr_adapt(const DBN& dbn) {
        printf("driver: learning rate adapted to %.5f \n", dbn.learning_rate);
        std::cout.flush();
    }

    void fine_tuning_end(const DBN&) {
        std::cout << "Training took " << watch.elapsed() << "s" << std::endl;
    }
};

template <typename DBN>
struct silent_dbn_watcher : default_dbn_watcher<DBN> {
    static constexpr bool ignore_sub  = true;
    static constexpr bool replace_sub = false;
};

template <typename DBN>
struct mute_dbn_watcher {
    static constexpr bool ignore_sub  = true;
    static constexpr bool replace_sub = false;

    void pretraining_begin(const DBN& /*dbn*/, std::size_t /*max_epochs*/) {}

    template <typename RBM>
    void pretrain_layer(const DBN& /*dbn*/, std::size_t /*I*/, const RBM& /*rbm*/, std::size_t /*input_size*/) {}

    void pretraining_end(const DBN& /*dbn*/) {}

    void pretraining_batch(const DBN& /*dbn*/, std::size_t /*batch*/) {}

    void fine_tuning_begin(const DBN& /*dbn*/, size_t /*max_epochs*/) {}

    void ft_epoch_start(std::size_t /*epoch*/, const DBN& /*dbn*/) {}

    void ft_epoch_end(std::size_t /*epoch*/, double /*error*/, const DBN& /*dbn*/) {}

    void ft_batch_start(size_t /*epoch*/, const DBN&) {}
    void ft_batch_end(size_t /*epoch*/, size_t /*batch*/, size_t /*batches*/, double /*batch_error*/, const DBN& /*dbn*/) {}
    void ft_batch_end(size_t /*epoch*/, double /*batch_error*/, const DBN& /*dbn*/) {}

    void lr_adapt(const DBN& /*dbn*/) {}

    void fine_tuning_end(const DBN& /*dbn*/) {}
};

//TODO This is currently useless

template <typename R>
struct histogram_watcher {
    default_rbm_watcher<R> parent;

    template <typename RBM = R>
    void training_begin(const RBM& rbm) {
        parent.training_begin(rbm);
    }

    template <typename RBM = R>
    void epoch_end(std::size_t epoch, double error, double /*free_energy*/, const RBM& rbm) {
        parent.epoch_end(epoch, error, rbm);
    }

    template <typename RBM = R>
    void batch_end(const RBM& rbm, const rbm_training_context& context, std::size_t batch, std::size_t batches) {
        parent.batch_end(rbm, context, batch, batches);
    }

    template <typename RBM = R>
    void training_end(const RBM& rbm) {
        parent.training_end(rbm);
    }

    void generate_hidden_images(std::size_t epoch, const R& rbm) {
        mkdir("reports", 0777);

        auto folder = "reports/epoch_" + std::to_string(epoch);
        mkdir(folder.c_str(), 0777);

        for (std::size_t j = 0; j < R::num_hidden; ++j) {
            auto path = folder + "/h_" + std::to_string(j) + ".dat";
            std::ofstream file(path, std::ios::out);

            if (!file) {
                std::cout << "Could not open file " << path << std::endl;
            } else {
                std::size_t i = R::num_visible;
                while (i > 0) {
                    --i;

                    auto value = rbm.w(i, j);
                    file << static_cast<std::size_t>(value > 0 ? static_cast<std::size_t>(value * 255.0) << 8 : static_cast<std::size_t>(-value * 255.0) << 16) << " ";
                }

                file << std::endl;
                file.close();
            }
        }
    }

    void generate_histograms(std::size_t epoch, const R& rbm) {
        mkdir("reports", 0777);

        auto folder = "reports/epoch_" + std::to_string(epoch);
        mkdir(folder.c_str(), 0777);

        generate_histogram(folder + "/weights.dat", rbm.w);
        generate_histogram(folder + "/visibles.dat", rbm.a);
        generate_histogram(folder + "/hiddens.dat", rbm.b);
    }

    template <typename Container>
    void generate_histogram(const std::string& path, const Container& weights) {
        std::ofstream file(path, std::ios::out);

        if (!file) {
            std::cout << "Could not open file " << path << std::endl;
        } else {
            for (auto& weight : weights) {
                file << weight << std::endl;
            }

            file << std::endl;
            file.close();
        }
    }
};

} //end of dll namespace
<|endoftext|>


<commit_before>#include "Game.h"

#include "Device.h"

namespace fs = boost::filesystem;

using namespace RetroEnd::Model;
using namespace std;

// The Name of the table into the DB for this Model
string Game::table = "games";

// Model Class Constructor
Game::Game() : BaseModel()
{
	//default values
	DeviceId = 0;
	TimesPlayed = 0;
	Favorite = false;
}

// Model Class Constructor from DB record
Game::Game( sqlite3_stmt* record ) : BaseModel()
{
	id			= sqlite3_column_int(record, 0);
	Title		= (char*)sqlite3_column_text(record,1);
	DeviceId	= sqlite3_column_int(record,2);
	Description = (char*)sqlite3_column_text(record,3);
	Developer	= (char*)sqlite3_column_text(record,4);
	Publisher	= (char*)sqlite3_column_text(record,5);
	ESRB		= (char*)sqlite3_column_text(record,6);
	MaxPlayers	= (char*)sqlite3_column_text(record,7);
	CoOp		= (char*)sqlite3_column_text(record,8);
	ReleaseDate = (char*)sqlite3_column_text(record,9);
	TGDBId		= (char*)sqlite3_column_text(record,10);
	TGDBRating	= (char*)sqlite3_column_text(record,11);
	ImageFront	= (char*)sqlite3_column_text(record,12);
	ImageBack	= (char*)sqlite3_column_text(record,13);
	ImageScreen	= (char*)sqlite3_column_text(record,14);
	ImageLogo	= (char*)sqlite3_column_text(record,15);
	GameFile	= (char*)sqlite3_column_text(record,16);
	FileCRC		= (char*)sqlite3_column_text(record,17);
	TimesPlayed	= sqlite3_column_int(record, 18);
	Favorite	= sqlite3_column_int(record, 19) == 1;
}

// Model Class Constructor from TGDB result
Game::Game ( pugi::xml_node gameNode, sqlite3_int64 deviceId) : BaseModel()
{
	//default values
	TimesPlayed = 0;
	Favorite = false;

	DeviceId = deviceId;

	Title = gameNode.child("GameTitle").text().get();
	Description = gameNode.child("Overview").text().get();
	ESRB = gameNode.child("ESRB").text().get();
	MaxPlayers = gameNode.child("Players").text().get();
	CoOp = gameNode.child("Co-op").text().get();
	Publisher = gameNode.child("Publisher").text().get();
	Developer = gameNode.child("Developer").text().get();
	TGDBRating = gameNode.child("Rating").text().get();
	ReleaseDate = gameNode.child("ReleaseDate").text().get();
	TGDBId = gameNode.child("id").text().get();
}

// Model Class Distructor
Game::~Game()
{

}

// Build the query for update the DB record
string Game::getUpdateQuery()
{
	string query = "UPDATE " + table + " SET";

	query += " DEVICE_ID = "	+	to_string( DeviceId )+",";
	query += " TITLE = '"		+	sqlEscape( Title )+"',";
	query += " DESCRIPTION = '"	+	sqlEscape( Description )+"',";
	query += " DEVELOPER = '"	+	sqlEscape( Developer )+"',";
	query += " PUBLISHER = '"	+	sqlEscape( Publisher )+"',";
	query += " ESRB = '"		+	sqlEscape( ESRB )+"',";
	query += " PLAYERS = '"		+	sqlEscape( MaxPlayers )+"',";
	query += " CO_OP = '"		+	sqlEscape( CoOp )+"',";
	query += " RELEASE_DATE = '"+	sqlEscape( ReleaseDate )+"',";
	query += " TGDB_ID = '"		+	sqlEscape( TGDBId )+"',";
	query += " TGDB_RATING = '"	+	sqlEscape( TGDBRating )+"',";
	query += " IMAGE_FRONT = '"	+	sqlEscape( ImageFront )+"',";
	query += " IMAGE_BACK = '"	+	sqlEscape( ImageBack )+"',";
	query += " IMAGE_SCREEN = '"+	sqlEscape( ImageScreen )+"',";
	query += " IMAGE_LOGO = '"	+	sqlEscape( ImageLogo )+"',";
	query += " GAME_FILE = '"	+	sqlEscape( GameFile )+"' ,";
	query += " FILE_CRC = '"	+	sqlEscape( FileCRC )+"' ,";
	query += " TIMES_PLAYED = "	+	to_string( TimesPlayed )+" ,";
	query += " FAVORITE = "		+	to_string( Favorite? 1 : 0 ) ;

	query += " WHERE id="+to_string( id )+";";

	return query;
}

// Build the query for delete the DB record
string Game::getDeleteQuery()
{
	return "DELETE FROM " + table + " WHERE id="+to_string( id )+";";
}

// Build the query for create the DB record
string Game::getInsertQuery()
{
	string query = "INSERT into " + table + " (TITLE,DEVICE_ID,DESCRIPTION,DEVELOPER,PUBLISHER,ESRB,PLAYERS,CO_OP,RELEASE_DATE,TGDB_ID,TGDB_RATING,IMAGE_FRONT,IMAGE_BACK,IMAGE_SCREEN,IMAGE_LOGO, GAME_FILE, FILE_CRC, TIMES_PLAYED, FAVORITE)";
	query += "values ('" + sqlEscape( Title ) + "','" + to_string(DeviceId) + "','" + sqlEscape( Description ) + "','" + sqlEscape( Developer ) + "','" + sqlEscape( Publisher ) + "','" + sqlEscape( ESRB ) + "','" + sqlEscape( MaxPlayers ) + "','" + sqlEscape( CoOp ) + "','" + sqlEscape( ReleaseDate ) + "','" + sqlEscape( TGDBId ) + "','" + sqlEscape( TGDBRating ) + "','" + sqlEscape( ImageFront ) + "','" + sqlEscape( ImageBack ) + "','" + sqlEscape( ImageScreen ) + "','" + sqlEscape( ImageLogo ) + "','" + sqlEscape( GameFile ) + "','" + sqlEscape( FileCRC ) + "', "+to_string(TimesPlayed)+", " + to_string(Favorite? 1 : 0) + ")";
	return query;
}

// Build the query for create the table into the SQLite DB
// Sould be called only on first run of the app
void Game::init()
{
	//open db connection
	sqlite3* db;
	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;
	vector<string> queries;

	//THE CREATE PART - MUST BE ALWAYS RUN (if table already exists we simply have an error)
	string create = "CREATE TABLE IF NOT EXISTS " + Game::table + " (";
	create += "ID INTEGER PRIMARY KEY, TITLE TEXT, DEVICE_ID INTEGER, DESCRIPTION TEXT, DEVELOPER TEXT, PUBLISHER TEXT, ESRB TEXT, PLAYERS TEXT, ";
	create += "CO_OP TEXT, RELEASE_DATE TEXT, TGDB_ID TEXT, TGDB_RATING TEXT, ";
	create += "IMAGE_FRONT TEXT, IMAGE_BACK TEXT, IMAGE_SCREEN TEXT, IMAGE_LOGO TEXT);";
	queries.push_back(create);

	//THE UPDATE PART  - MUST BE ALWAYS RUN (if colums already exist we simply have an error)
	//new colums bust be added in the update part so when the app is updated we can add those to the DB
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN GAME_FILE TEXT DEFAULT '';" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN FILE_CRC TEXT DEFAULT '';" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN TIMES_PLAYED INTEGER DEFAULT 0;" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN FAVORITE INTEGER DEFAULT 0;" );

	for (vector<string>::iterator it = queries.begin() ; it != queries.end(); ++it) {
		string query = *it;

		//execute query
		if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
		{
			sqlite3_step(statement);
			sqlite3_finalize(statement);
		}
		else
		{
			string message = sqlite3_errmsg(db);

			//IF COLUMN ALREADY AXISTS IS NOT AN ERROR
			if( strncmp(message.c_str(), "duplicate column", strlen("duplicate column")) != 0)
				LOG(LogLevel::Error, "Game init : " + message);
		}
	}

	//close db concction
	sqlite3_close(db);
}

// Static method for read all the items from DB
vector<Game> Game::getAllGames()
{
	vector<Game> games;

	string query = "SELECT * FROM " + Game::table;

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		while (result == SQLITE_ROW)
		{
			//create and insert the new item
			games.push_back(Game(statement));

			//go to  next record
			result = sqlite3_step(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getAllGames : " + message);
	}

	sqlite3_close(db);

	return games;
}

// Static method for read an item specified by "id" from DB
Game Game::getGameById(sqlite3_int64 id)
{
	Game* game = NULL;

	string query = "SELECT * FROM " + Game::table + " WHERE id = " + to_string(id);

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		if(result == SQLITE_ROW)
		{
			game = new Game(statement);
		}
		else
		{
			game = new Game();
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGameById : " + message);
	}

	sqlite3_close(db);

	return *game;
}

Game* Game::getGameByTGDBId(string& TGDBId)
{
	Game* game = NULL;

	string query = "SELECT * FROM " + Game::table + " WHERE TGDB_ID = '" + TGDBId +"' LIMIT 1";

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		if(result == SQLITE_ROW)
		{
			game = new Game(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGameById : " + message);
	}

	sqlite3_close(db);

	return game;
}

string Game::getReleaseYear()
{
	if(ReleaseDate.empty())
		return "";
	else
	{
		return ReleaseDate.substr(ReleaseDate.find_last_of("/") + 1, string::npos) + ", ";
	}
}

string Game::getCleanFileName()
{
	string result = GameFile;

	regex rx1("\\(.+\\)");
	regex rx2("\\[.+\\]");

	result = tr1::regex_replace(result, rx1, "");
	result = tr1::regex_replace(result, rx2, "");
	result = result.substr(0, result.find_last_of('.'));
	result = trim(result);
	return result;
}

// Static method for read all the items from DB for a specified Device
vector<Game> Game::getGamesForDevice(sqlite3_int64 deviceId)
{
	vector<Game> games;

	string query = "SELECT * FROM " + Game::table + " WHERE DEVICE_ID = " + to_string(deviceId) + " ORDER BY TITLE ";

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		while (result == SQLITE_ROW)
		{
			//create and insert the new item
			games.push_back(Game(statement));

			//go to  next record
			result = sqlite3_step(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGamesForDevice : " + message);
	}

	sqlite3_close(db);

	return games;
}

void Game::remove()
{
	//remove from DB
	BaseModel::remove();

	if(DeviceId != 0 && ! GameFile.empty())
	{
		//get GameFile path
		string romPath = Device::getDeviceById(DeviceId).getRomsPath();
		//get Images path

		//delete rom file
		fs::path file = romPath / fs::path(GameFile);
		if( fs::exists(file)) fs::remove(file);
	}

	if( ! ImageFront.empty() && fs::exists(ImageFront) ) fs::remove(ImageFront);
	if( ! ImageBack.empty() && fs::exists(ImageBack) ) fs::remove(ImageBack);
	if( ! ImageLogo.empty() && fs::exists(ImageLogo) ) fs::remove(ImageLogo);
	if( ! ImageScreen.empty() && fs::exists(ImageScreen) ) fs::remove(ImageScreen);
}<commit_msg>Going to Raspberry PI<commit_after>#include "Game.h"

#include "Device.h"

namespace fs = boost::filesystem;

using namespace RetroEnd::Model;
using namespace std;

// The Name of the table into the DB for this Model
string Game::table = "games";

// Model Class Constructor
Game::Game() : BaseModel()
{
	//default values
	DeviceId = 0;
	TimesPlayed = 0;
	Favorite = false;
}

// Model Class Constructor from DB record
Game::Game( sqlite3_stmt* record ) : BaseModel()
{
	id			= sqlite3_column_int(record, 0);
	Title		= (char*)sqlite3_column_text(record,1);
	DeviceId	= sqlite3_column_int(record,2);
	Description = (char*)sqlite3_column_text(record,3);
	Developer	= (char*)sqlite3_column_text(record,4);
	Publisher	= (char*)sqlite3_column_text(record,5);
	ESRB		= (char*)sqlite3_column_text(record,6);
	MaxPlayers	= (char*)sqlite3_column_text(record,7);
	CoOp		= (char*)sqlite3_column_text(record,8);
	ReleaseDate = (char*)sqlite3_column_text(record,9);
	TGDBId		= (char*)sqlite3_column_text(record,10);
	TGDBRating	= (char*)sqlite3_column_text(record,11);
	ImageFront	= (char*)sqlite3_column_text(record,12);
	ImageBack	= (char*)sqlite3_column_text(record,13);
	ImageScreen	= (char*)sqlite3_column_text(record,14);
	ImageLogo	= (char*)sqlite3_column_text(record,15);
	GameFile	= (char*)sqlite3_column_text(record,16);
	FileCRC		= (char*)sqlite3_column_text(record,17);
	TimesPlayed	= sqlite3_column_int(record, 18);
	Favorite	= sqlite3_column_int(record, 19) == 1;
}

// Model Class Constructor from TGDB result
Game::Game ( pugi::xml_node gameNode, sqlite3_int64 deviceId) : BaseModel()
{
	//default values
	TimesPlayed = 0;
	Favorite = false;

	DeviceId = deviceId;

	Title = gameNode.child("GameTitle").text().get();
	Description = gameNode.child("Overview").text().get();
	ESRB = gameNode.child("ESRB").text().get();
	MaxPlayers = gameNode.child("Players").text().get();
	CoOp = gameNode.child("Co-op").text().get();
	Publisher = gameNode.child("Publisher").text().get();
	Developer = gameNode.child("Developer").text().get();
	TGDBRating = gameNode.child("Rating").text().get();
	ReleaseDate = gameNode.child("ReleaseDate").text().get();
	TGDBId = gameNode.child("id").text().get();
}

// Model Class Distructor
Game::~Game()
{

}

// Build the query for update the DB record
string Game::getUpdateQuery()
{
	string query = "UPDATE " + table + " SET";

	query += " DEVICE_ID = "	+	to_string( DeviceId )+",";
	query += " TITLE = '"		+	sqlEscape( Title )+"',";
	query += " DESCRIPTION = '"	+	sqlEscape( Description )+"',";
	query += " DEVELOPER = '"	+	sqlEscape( Developer )+"',";
	query += " PUBLISHER = '"	+	sqlEscape( Publisher )+"',";
	query += " ESRB = '"		+	sqlEscape( ESRB )+"',";
	query += " PLAYERS = '"		+	sqlEscape( MaxPlayers )+"',";
	query += " CO_OP = '"		+	sqlEscape( CoOp )+"',";
	query += " RELEASE_DATE = '"+	sqlEscape( ReleaseDate )+"',";
	query += " TGDB_ID = '"		+	sqlEscape( TGDBId )+"',";
	query += " TGDB_RATING = '"	+	sqlEscape( TGDBRating )+"',";
	query += " IMAGE_FRONT = '"	+	sqlEscape( ImageFront )+"',";
	query += " IMAGE_BACK = '"	+	sqlEscape( ImageBack )+"',";
	query += " IMAGE_SCREEN = '"+	sqlEscape( ImageScreen )+"',";
	query += " IMAGE_LOGO = '"	+	sqlEscape( ImageLogo )+"',";
	query += " GAME_FILE = '"	+	sqlEscape( GameFile )+"' ,";
	query += " FILE_CRC = '"	+	sqlEscape( FileCRC )+"' ,";
	query += " TIMES_PLAYED = "	+	to_string( TimesPlayed )+" ,";
	query += " FAVORITE = "		+	to_string( Favorite? 1 : 0 ) ;

	query += " WHERE id="+to_string( id )+";";

	return query;
}

// Build the query for delete the DB record
string Game::getDeleteQuery()
{
	return "DELETE FROM " + table + " WHERE id="+to_string( id )+";";
}

// Build the query for create the DB record
string Game::getInsertQuery()
{
	string query = "INSERT into " + table + " (TITLE,DEVICE_ID,DESCRIPTION,DEVELOPER,PUBLISHER,ESRB,PLAYERS,CO_OP,RELEASE_DATE,TGDB_ID,TGDB_RATING,IMAGE_FRONT,IMAGE_BACK,IMAGE_SCREEN,IMAGE_LOGO, GAME_FILE, FILE_CRC, TIMES_PLAYED, FAVORITE)";
	query += "values ('" + sqlEscape( Title ) + "','" + to_string(DeviceId) + "','" + sqlEscape( Description ) + "','" + sqlEscape( Developer ) + "','" + sqlEscape( Publisher ) + "','" + sqlEscape( ESRB ) + "','" + sqlEscape( MaxPlayers ) + "','" + sqlEscape( CoOp ) + "','" + sqlEscape( ReleaseDate ) + "','" + sqlEscape( TGDBId ) + "','" + sqlEscape( TGDBRating ) + "','" + sqlEscape( ImageFront ) + "','" + sqlEscape( ImageBack ) + "','" + sqlEscape( ImageScreen ) + "','" + sqlEscape( ImageLogo ) + "','" + sqlEscape( GameFile ) + "','" + sqlEscape( FileCRC ) + "', "+to_string(TimesPlayed)+", " + to_string(Favorite? 1 : 0) + ")";
	return query;
}

// Build the query for create the table into the SQLite DB
// Sould be called only on first run of the app
void Game::init()
{
	//open db connection
	sqlite3* db;
	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;
	vector<string> queries;

	//THE CREATE PART - MUST BE ALWAYS RUN (if table already exists we simply have an error)
	string create = "CREATE TABLE IF NOT EXISTS " + Game::table + " (";
	create += "ID INTEGER PRIMARY KEY, TITLE TEXT, DEVICE_ID INTEGER, DESCRIPTION TEXT, DEVELOPER TEXT, PUBLISHER TEXT, ESRB TEXT, PLAYERS TEXT, ";
	create += "CO_OP TEXT, RELEASE_DATE TEXT, TGDB_ID TEXT, TGDB_RATING TEXT, ";
	create += "IMAGE_FRONT TEXT, IMAGE_BACK TEXT, IMAGE_SCREEN TEXT, IMAGE_LOGO TEXT);";
	queries.push_back(create);

	//THE UPDATE PART  - MUST BE ALWAYS RUN (if colums already exist we simply have an error)
	//new colums bust be added in the update part so when the app is updated we can add those to the DB
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN GAME_FILE TEXT DEFAULT '';" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN FILE_CRC TEXT DEFAULT '';" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN TIMES_PLAYED INTEGER DEFAULT 0;" );
	queries.push_back( "ALTER TABLE " +Game::table + " ADD COLUMN FAVORITE INTEGER DEFAULT 0;" );

	for (vector<string>::iterator it = queries.begin() ; it != queries.end(); ++it) {
		string query = *it;

		//execute query
		if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
		{
			sqlite3_step(statement);
			sqlite3_finalize(statement);
		}
		else
		{
			string message = sqlite3_errmsg(db);

			//IF COLUMN ALREADY AXISTS IS NOT AN ERROR
			if( strncmp(message.c_str(), "duplicate column", strlen("duplicate column")) != 0)
				LOG(LogLevel::Error, "Game init : " + message);
		}
	}

	//close db concction
	sqlite3_close(db);
}

// Static method for read all the items from DB
vector<Game> Game::getAllGames()
{
	vector<Game> games;

	string query = "SELECT * FROM " + Game::table;

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		while (result == SQLITE_ROW)
		{
			//create and insert the new item
			games.push_back(Game(statement));

			//go to  next record
			result = sqlite3_step(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getAllGames : " + message);
	}

	sqlite3_close(db);

	return games;
}

// Static method for read an item specified by "id" from DB
Game Game::getGameById(sqlite3_int64 id)
{
	Game* game = NULL;

	string query = "SELECT * FROM " + Game::table + " WHERE id = " + to_string(id);

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		if(result == SQLITE_ROW)
		{
			game = new Game(statement);
		}
		else
		{
			game = new Game();
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGameById : " + message);
	}

	sqlite3_close(db);

	return *game;
}

Game* Game::getGameByTGDBId(string& TGDBId)
{
	Game* game = NULL;

	string query = "SELECT * FROM " + Game::table + " WHERE TGDB_ID = '" + TGDBId +"' LIMIT 1";

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		if(result == SQLITE_ROW)
		{
			game = new Game(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGameById : " + message);
	}

	sqlite3_close(db);

	return game;
}

string Game::getReleaseYear()
{
	if(ReleaseDate.empty())
		return "";
	else
	{
		return ReleaseDate.substr(ReleaseDate.find_last_of("/") + 1, string::npos) + ", ";
	}
}

string Game::getCleanFileName()
{
	string result = GameFile;

	regex rx1("\\(.+\\)");
	regex rx2("\\[.+\\]");

	result = regex_replace(result, rx1, "");
	result = regex_replace(result, rx2, "");
	result = result.substr(0, result.find_last_of('.'));
	result = trim(result);
	return result;
}

// Static method for read all the items from DB for a specified Device
vector<Game> Game::getGamesForDevice(sqlite3_int64 deviceId)
{
	vector<Game> games;

	string query = "SELECT * FROM " + Game::table + " WHERE DEVICE_ID = " + to_string(deviceId) + " ORDER BY TITLE ";

	sqlite3* db;

	sqlite3_open(RetroEnd::DB_NAME.c_str(), &db);

	sqlite3_stmt *statement;

	if(sqlite3_prepare_v2(db, query.c_str(), -1, &statement, 0) == SQLITE_OK)
	{
		int result = sqlite3_step(statement);

		while (result == SQLITE_ROW)
		{
			//create and insert the new item
			games.push_back(Game(statement));

			//go to  next record
			result = sqlite3_step(statement);
		}

		sqlite3_finalize(statement);
	}
	else
	{
		string message = sqlite3_errmsg(db);
		LOG(Error, "Game getGamesForDevice : " + message);
	}

	sqlite3_close(db);

	return games;
}

void Game::remove()
{
	//remove from DB
	BaseModel::remove();

	if(DeviceId != 0 && ! GameFile.empty())
	{
		//get GameFile path
		string romPath = Device::getDeviceById(DeviceId).getRomsPath();
		//get Images path

		//delete rom file
		fs::path file = romPath / fs::path(GameFile);
		if( fs::exists(file)) fs::remove(file);
	}

	if( ! ImageFront.empty() && fs::exists(ImageFront) ) fs::remove(ImageFront);
	if( ! ImageBack.empty() && fs::exists(ImageBack) ) fs::remove(ImageBack);
	if( ! ImageLogo.empty() && fs::exists(ImageLogo) ) fs::remove(ImageLogo);
	if( ! ImageScreen.empty() && fs::exists(ImageScreen) ) fs::remove(ImageScreen);
}<|endoftext|>


<commit_before>#pragma once

#include <lion/filesystem.hpp>

#include <atma/intrusive_ptr.hpp>
#include <atma/vector.hpp>
#include <atma/atomic.hpp>
#include <atma/handle_table.hpp>
#include <atma/enable_if.hpp>

#include <tuple>
#include <type_traits>

// forward declares
namespace lion
{
	struct asset_t;
	struct asset_library_t;
	template <typename> struct asset_handle_t;
	template <typename> struct asset_weak_handle_t;
}

// asset_t
namespace lion
{
	struct asset_t
	{
		auto path() const -> path_t const& { return path_; }
		virtual ~asset_t() {}
	private:
		path_t path_;
	};
}

// asset_handle_t / asset_weak_handle_t
namespace lion
{
	template <typename T>
	struct asset_handle_t
	{
		constexpr asset_handle_t();
		asset_handle_t(asset_handle_t const& rhs);
		asset_handle_t(asset_handle_t&& rhs);
		~asset_handle_t();

		explicit asset_handle_t(asset_weak_handle_t<T> const&);

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		asset_handle_t(asset_handle_t<Y> const& rhs);
		template <typename Y, typename = atma::enable_if<std::is_convertible<Y*, T*>>>
		asset_handle_t(asset_handle_t<Y>&& rhs);

		auto operator = (asset_handle_t const& rhs) -> asset_handle_t&;
		auto operator = (asset_handle_t&& rhs) -> asset_handle_t&;

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_handle_t<Y> const& rhs) -> asset_handle_t&;
		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_handle_t<Y>&& rhs) -> asset_handle_t&;

		auto operator -> () const -> T const* { return (T const*)library_->find(id_)->asset.get(); }
		auto operator -> () -> T*             { return (T      *)library_->find(id_)->asset.get(); }
		auto operator *() const -> T const&   { return *this->operator ->(); }
		auto operator *() -> T&               { return *this->operator ->(); }

		operator bool() const { return id_ != 0; }

		auto library() const -> asset_library_t* { return library_; }
		auto id() const -> uint32 { return id_; }

	private:
		asset_handle_t(asset_library_t* library, uint32 id)
			: library_{library}, id_{id}
		{}

	private:
		asset_library_t* library_ = nullptr;
		uint32 id_ = 0;

		friend struct asset_library_t;
		template <typename> friend struct asset_handle_t;
		template <typename> friend struct asset_weak_handle_t;

		template <typename Y, typename Y2>
		friend auto polymorphic_asset_cast(asset_handle_t<Y2> const&) -> asset_handle_t<Y>;
	};

	using base_asset_handle_t = asset_handle_t<asset_t>;
}

namespace lion
{
	template <typename T>
	struct asset_weak_handle_t
	{
		constexpr asset_weak_handle_t();
		asset_weak_handle_t(asset_weak_handle_t const&);
		asset_weak_handle_t(asset_weak_handle_t&&);
		~asset_weak_handle_t();

		asset_weak_handle_t(asset_handle_t<T> const&);

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		asset_weak_handle_t(asset_weak_handle_t<Y> const& rhs);
		template <typename Y, typename = atma::enable_if<std::is_convertible<Y*, T*>>>
		asset_weak_handle_t(asset_weak_handle_t<Y>&& rhs);

		auto operator = (asset_weak_handle_t const&) -> asset_weak_handle_t&;
		auto operator = (asset_weak_handle_t&&) -> asset_weak_handle_t&;

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_weak_handle_t<Y> const& rhs) -> asset_weak_handle_t&;
		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_weak_handle_t<Y>&& rhs) -> asset_weak_handle_t&;

		auto library() const -> asset_library_t* { return library_; }
		auto id() const -> uint32 { return id_; }

		auto expired() const -> bool;
		auto lock() const -> asset_handle_t<T>;

	private:
		asset_library_t* library_ = nullptr;
		uint32 id_ = 0;
	};
}

namespace lion
{
	template <typename T>
	inline constexpr asset_handle_t<T>::asset_handle_t()
	{}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.retain(id_);
	}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_weak_handle_t<T> const& rhs)
		: library_{rhs.library_}
		, id_{rhs.expired() ? 0 : rhs.id_}
	{
		library_->table_.release(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t<Y> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		//static_assert(std::is_convertible<Y*, T*>::value, "bad cast");
		library_->table_.retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t<Y>&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_handle_t<T>::~asset_handle_t()
	{
		library_->table_.release(id_);
	}

	template <typename T>
	inline auto asset_handle_t<T>::operator = (asset_handle_t const& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{rhs};
		return *this;
	}

	template <typename T>
	inline auto asset_handle_t<T>::operator = (asset_handle_t&& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_handle_t<T>::operator = (asset_handle_t<Y> const& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{rhs};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_handle_t<T>::operator = (asset_handle_t<Y>&& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename Y, typename T>
	inline auto polymorphic_asset_cast(asset_handle_t<T> const& x) -> asset_handle_t<Y>
	{
		static_assert(std::is_base_of<T, Y>::value, "bad cast");
		ATMA_ASSERT(nullptr != dynamic_cast<Y const*>(&*x), "bad cast");
		return asset_handle_t<Y>{x.library_, x.id_};
	}
}

namespace lion
{
	template <typename T>
	inline constexpr asset_weak_handle_t<T>::asset_weak_handle_t()
	{}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_handle_t<T> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t<Y> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		static_assert(std::is_convertible<Y*, T*>::value, "bad cast");
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t<Y>&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_weak_handle_t<T>::~asset_weak_handle_t()
	{
		library_->table_.release(id_);
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t const& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{rhs};
		return *this;
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t&& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t<Y> const& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{rhs};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t<Y>&& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::expired() const -> bool
	{
		return library_->table_.expired(id_);
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::lock() const -> asset_handle_t<T>
	{
		if (expired())
			return asset_handle_t<T>{library_, 0};
		else
			return asset_handle_t<T>{library_, id_};
	}
}

// asset_library_t
namespace lion
{
	struct asset_library_t
	{
		asset_library_t();
		asset_library_t(vfs_t*);

		auto register_loader(atma::string const& regex) -> void;

		auto store(asset_t*) -> base_asset_handle_t;

	private: // table management
		struct storage_t;

		auto find(uint32) -> storage_t*;
		auto find(uint32) const -> storage_t const*;

		atma::handle_table_t<storage_t> table_;

	private: // vfs
		vfs_t* vfs_ = nullptr;

	private:
		template <typename> friend struct asset_handle_t;
		template <typename> friend struct asset_weak_handle_t;
	};

	struct asset_library_t::storage_t
	{
		storage_t(asset_t* a)
			: asset{a}
			, generation{}
		{}

		std::unique_ptr<asset_t> asset;
		uint8 generation;
	};

}
<commit_msg>prototypes for future functionality<commit_after>#pragma once

#include <lion/filesystem.hpp>

#include <atma/intrusive_ptr.hpp>
#include <atma/vector.hpp>
#include <atma/atomic.hpp>
#include <atma/handle_table.hpp>
#include <atma/enable_if.hpp>

#include <tuple>
#include <type_traits>

// forward declares
namespace lion
{
	struct asset_t;
	struct asset_library_t;
	template <typename> struct asset_handle_t;
	template <typename> struct asset_weak_handle_t;
}

// asset_t
namespace lion
{
	struct asset_t
	{
		auto path() const -> path_t const& { return path_; }
		virtual ~asset_t() {}
	private:
		path_t path_;
	};
}

// asset_handle_t / asset_weak_handle_t
namespace lion
{
	template <typename T>
	struct asset_handle_t
	{
		constexpr asset_handle_t();
		asset_handle_t(asset_handle_t const& rhs);
		asset_handle_t(asset_handle_t&& rhs);
		~asset_handle_t();

		explicit asset_handle_t(asset_weak_handle_t<T> const&);

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		asset_handle_t(asset_handle_t<Y> const& rhs);
		template <typename Y, typename = atma::enable_if<std::is_convertible<Y*, T*>>>
		asset_handle_t(asset_handle_t<Y>&& rhs);

		auto operator = (asset_handle_t const& rhs) -> asset_handle_t&;
		auto operator = (asset_handle_t&& rhs) -> asset_handle_t&;

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_handle_t<Y> const& rhs) -> asset_handle_t&;
		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_handle_t<Y>&& rhs) -> asset_handle_t&;

		auto operator -> () const -> T const* { return (T const*)library_->find(id_)->asset.get(); }
		auto operator -> () -> T*             { return (T      *)library_->find(id_)->asset.get(); }
		auto operator *() const -> T const&   { return *this->operator ->(); }
		auto operator *() -> T&               { return *this->operator ->(); }

		operator bool() const { return id_ != 0; }

		auto library() const -> asset_library_t* { return library_; }
		auto id() const -> uint32 { return id_; }

	private:
		asset_handle_t(asset_library_t* library, uint32 id)
			: library_{library}, id_{id}
		{}

	private:
		asset_library_t* library_ = nullptr;
		uint32 id_ = 0;

		friend struct asset_library_t;
		template <typename> friend struct asset_handle_t;
		template <typename> friend struct asset_weak_handle_t;

		template <typename Y, typename Y2>
		friend auto polymorphic_asset_cast(asset_handle_t<Y2> const&) -> asset_handle_t<Y>;
	};

	using base_asset_handle_t = asset_handle_t<asset_t>;
}

namespace lion
{
	template <typename T>
	struct asset_weak_handle_t
	{
		constexpr asset_weak_handle_t();
		asset_weak_handle_t(asset_weak_handle_t const&);
		asset_weak_handle_t(asset_weak_handle_t&&);
		~asset_weak_handle_t();

		asset_weak_handle_t(asset_handle_t<T> const&);

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		asset_weak_handle_t(asset_weak_handle_t<Y> const& rhs);
		template <typename Y, typename = atma::enable_if<std::is_convertible<Y*, T*>>>
		asset_weak_handle_t(asset_weak_handle_t<Y>&& rhs);

		auto operator = (asset_weak_handle_t const&) -> asset_weak_handle_t&;
		auto operator = (asset_weak_handle_t&&) -> asset_weak_handle_t&;

		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_weak_handle_t<Y> const& rhs) -> asset_weak_handle_t&;
		template <typename Y, typename = std::enable_if_t<std::is_convertible<Y*, T*>::value>>
		auto operator = (asset_weak_handle_t<Y>&& rhs) -> asset_weak_handle_t&;

		auto library() const -> asset_library_t* { return library_; }
		auto id() const -> uint32 { return id_; }

		auto expired() const -> bool;
		auto lock() const -> asset_handle_t<T>;

	private:
		asset_library_t* library_ = nullptr;
		uint32 id_ = 0;
	};
}

namespace lion
{
	template <typename T>
	inline constexpr asset_handle_t<T>::asset_handle_t()
	{}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.retain(id_);
	}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_handle_t<T>::asset_handle_t(asset_weak_handle_t<T> const& rhs)
		: library_{rhs.library_}
		, id_{rhs.expired() ? 0 : rhs.id_}
	{
		library_->table_.release(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t<Y> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		//static_assert(std::is_convertible<Y*, T*>::value, "bad cast");
		library_->table_.retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_handle_t<T>::asset_handle_t(asset_handle_t<Y>&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_handle_t<T>::~asset_handle_t()
	{
		library_->table_.release(id_);
	}

	template <typename T>
	inline auto asset_handle_t<T>::operator = (asset_handle_t const& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{rhs};
		return *this;
	}

	template <typename T>
	inline auto asset_handle_t<T>::operator = (asset_handle_t&& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_handle_t<T>::operator = (asset_handle_t<Y> const& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{rhs};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_handle_t<T>::operator = (asset_handle_t<Y>&& rhs) -> asset_handle_t&
	{
		this->~asset_handle_t();
		new (this) asset_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename Y, typename T>
	inline auto polymorphic_asset_cast(asset_handle_t<T> const& x) -> asset_handle_t<Y>
	{
		static_assert(std::is_base_of<T, Y>::value, "bad cast");
		ATMA_ASSERT(nullptr != dynamic_cast<Y const*>(&*x), "bad cast");
		return asset_handle_t<Y>{x.library_, x.id_};
	}
}

namespace lion
{
	template <typename T>
	inline constexpr asset_weak_handle_t<T>::asset_weak_handle_t()
	{}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_handle_t<T> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t<Y> const& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		static_assert(std::is_convertible<Y*, T*>::value, "bad cast");
		library_->table_.weak_retain(id_);
	}

	template <typename T>
	template <typename Y, typename>
	inline asset_weak_handle_t<T>::asset_weak_handle_t(asset_weak_handle_t<Y>&& rhs)
		: library_{rhs.library_}, id_{rhs.id_}
	{
		rhs.id_ = 0;
	}

	template <typename T>
	inline asset_weak_handle_t<T>::~asset_weak_handle_t()
	{
		library_->table_.release(id_);
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t const& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{rhs};
		return *this;
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t&& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t<Y> const& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{rhs};
		return *this;
	}

	template <typename T>
	template <typename Y, typename>
	inline auto asset_weak_handle_t<T>::operator = (asset_weak_handle_t<Y>&& rhs) -> asset_weak_handle_t&
	{
		this->~asset_weak_handle_t();
		new (this) asset_weak_handle_t{std::move(rhs)};
		return *this;
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::expired() const -> bool
	{
		return library_->table_.expired(id_);
	}

	template <typename T>
	inline auto asset_weak_handle_t<T>::lock() const -> asset_handle_t<T>
	{
		if (expired())
			return asset_handle_t<T>{library_, 0};
		else
			return asset_handle_t<T>{library_, id_};
	}
}

// asset_library_t
namespace lion
{
	struct asset_library_t
	{
		asset_library_t();
		asset_library_t(vfs_t*);

		auto register_loader(atma::string const& regex) -> void;

		auto store(asset_t*) -> base_asset_handle_t;

		//auto load(atma::string const& path) -> base_asset_handle_t;
		//auto load(asset_collection_t, atma::string const& path) -> base_asset_handle_t;

	private: // table management
		struct storage_t;

		auto find(uint32) -> storage_t*;
		auto find(uint32) const -> storage_t const*;

		atma::handle_table_t<storage_t> table_;

	private: // vfs
		vfs_t* vfs_ = nullptr;

	private:
		template <typename> friend struct asset_handle_t;
		template <typename> friend struct asset_weak_handle_t;
	};

	struct asset_library_t::storage_t
	{
		storage_t(asset_t* a)
			: asset{a}
			, generation{}
		{}

		std::unique_ptr<asset_t> asset;
		uint8 generation;
	};

}
<|endoftext|>