<commit_before>#include <iostream>
#include <set>
#include <sstream>
#include <algorithm>

#include "Lower.h"

#include "AddImageChecks.h"
#include "AddParameterChecks.h"
#include "AllocationBoundsInference.h"
#include "Bounds.h"
#include "BoundsInference.h"
#include "CSE.h"
#include "Debug.h"
#include "DebugToFile.h"
#include "DeepCopy.h"
#include "Deinterleave.h"
#include "EarlyFree.h"
#include "FindCalls.h"
#include "Function.h"
#include "FuseGPUThreadLoops.h"
#include "HexagonOffload.h"
#include "InjectHostDevBufferCopies.h"
#include "InjectImageIntrinsics.h"
#include "InjectOpenGLIntrinsics.h"
#include "Inline.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "IRPrinter.h"
#include "LoopCarry.h"
#include "Memoization.h"
#include "PartitionLoops.h"
#include "Profiling.h"
#include "Qualify.h"
#include "RealizationOrder.h"
#include "RemoveDeadAllocations.h"
#include "RemoveTrivialForLoops.h"
#include "RemoveUndef.h"
#include "ScheduleFunctions.h"
#include "SelectGPUAPI.h"
#include "SkipStages.h"
#include "SlidingWindow.h"
#include "Simplify.h"
#include "SimplifySpecializations.h"
#include "StorageFlattening.h"
#include "StorageFolding.h"
#include "Substitute.h"
#include "Tracing.h"
#include "TrimNoOps.h"
#include "UnifyDuplicateLets.h"
#include "UniquifyVariableNames.h"
#include "UnrollLoops.h"
#include "VaryingAttributes.h"
#include "VectorizeLoops.h"
#include "WrapCalls.h"

namespace Halide {
namespace Internal {

using std::set;
using std::ostringstream;
using std::string;
using std::vector;
using std::map;

Stmt lower(vector<Function> outputs, const string &pipeline_name, const Target &t, const vector<IRMutator *> &custom_passes) {

    // Compute an environment
    map<string, Function> env;
    for (Function f : outputs) {
        map<string, Function> more_funcs = find_transitive_calls(f);
        env.insert(more_funcs.begin(), more_funcs.end());
    }

    // Create a deep-copy of the entire graph of Funcs.
    std::tie(outputs, env) = deep_copy(outputs, env);

    // Substitute in wrapper Funcs
    env = wrap_func_calls(env);

    // Compute a realization order
    vector<string> order = realization_order(outputs, env);

    // Try to simplify the RHS/LHS of a function definition by propagating its
    // specializations' conditions
    simplify_specializations(env);

    bool any_memoized = false;

    debug(1) << "Creating initial loop nests...\n";
    Stmt s = schedule_functions(outputs, order, env, t, any_memoized);
    debug(2) << "Lowering after creating initial loop nests:\n" << s << '\n';

    if (any_memoized) {
        debug(1) << "Injecting memoization...\n";
        s = inject_memoization(s, env, pipeline_name, outputs);
        debug(2) << "Lowering after injecting memoization:\n" << s << '\n';
    } else {
        debug(1) << "Skipping injecting memoization...\n";
    }

    debug(1) << "Injecting tracing...\n";
    s = inject_tracing(s, pipeline_name, env, outputs);
    debug(2) << "Lowering after injecting tracing:\n" << s << '\n';

    debug(1) << "Adding checks for parameters\n";
    s = add_parameter_checks(s, t);
    debug(2) << "Lowering after injecting parameter checks:\n" << s << '\n';

    // Compute the maximum and minimum possible value of each
    // function. Used in later bounds inference passes.
    debug(1) << "Computing bounds of each function's value\n";
    FuncValueBounds func_bounds = compute_function_value_bounds(order, env);

    // The checks will be in terms of the symbols defined by bounds
    // inference.
    debug(1) << "Adding checks for images\n";
    s = add_image_checks(s, outputs, t, order, env, func_bounds);
    debug(2) << "Lowering after injecting image checks:\n" << s << '\n';

    // This pass injects nested definitions of variable names, so we
    // can't simplify statements from here until we fix them up. (We
    // can still simplify Exprs).
    debug(1) << "Performing computation bounds inference...\n";
    s = bounds_inference(s, outputs, order, env, func_bounds);
    debug(2) << "Lowering after computation bounds inference:\n" << s << '\n';

    debug(1) << "Performing sliding window optimization...\n";
    s = sliding_window(s, env);
    debug(2) << "Lowering after sliding window:\n" << s << '\n';

    debug(1) << "Performing allocation bounds inference...\n";
    s = allocation_bounds_inference(s, env, func_bounds);
    debug(2) << "Lowering after allocation bounds inference:\n" << s << '\n';

    debug(1) << "Removing code that depends on undef values...\n";
    s = remove_undef(s);
    debug(2) << "Lowering after removing code that depends on undef values:\n" << s << "\n\n";

    // This uniquifies the variable names, so we're good to simplify
    // after this point. This lets later passes assume syntactic
    // equivalence means semantic equivalence.
    debug(1) << "Uniquifying variable names...\n";
    s = uniquify_variable_names(s);
    debug(2) << "Lowering after uniquifying variable names:\n" << s << "\n\n";

    debug(1) << "Performing storage folding optimization...\n";
    s = storage_folding(s, env);
    debug(2) << "Lowering after storage folding:\n" << s << '\n';

    debug(1) << "Injecting debug_to_file calls...\n";
    s = debug_to_file(s, outputs, env);
    debug(2) << "Lowering after injecting debug_to_file calls:\n" << s << '\n';

    debug(1) << "Simplifying...\n"; // without removing dead lets, because storage flattening needs the strides
    s = simplify(s, false);
    debug(2) << "Lowering after first simplification:\n" << s << "\n\n";

    debug(1) << "Dynamically skipping stages...\n";
    s = skip_stages(s, order);
    debug(2) << "Lowering after dynamically skipping stages:\n" << s << "\n\n";

    if (t.has_feature(Target::OpenGL) || t.has_feature(Target::Renderscript)) {
        debug(1) << "Injecting image intrinsics...\n";
        s = inject_image_intrinsics(s, env);
        debug(2) << "Lowering after image intrinsics:\n" << s << "\n\n";
    }

    debug(1) << "Performing storage flattening...\n";
    s = storage_flattening(s, outputs, env, t);
    debug(2) << "Lowering after storage flattening:\n" << s << "\n\n";

    if (any_memoized) {
        debug(1) << "Rewriting memoized allocations...\n";
        s = rewrite_memoized_allocations(s, env);
        debug(2) << "Lowering after rewriting memoized allocations:\n" << s << "\n\n";
    } else {
        debug(1) << "Skipping rewriting memoized allocations...\n";
    }

    if (t.has_gpu_feature() ||
        t.has_feature(Target::OpenGLCompute) ||
        t.has_feature(Target::OpenGL) ||
        t.has_feature(Target::Renderscript) ||
        (t.arch != Target::Hexagon && (t.features_any_of({Target::HVX_64, Target::HVX_128})))) {
        debug(1) << "Selecting a GPU API for GPU loops...\n";
        s = select_gpu_api(s, t);
        debug(2) << "Lowering after selecting a GPU API:\n" << s << "\n\n";

        debug(1) << "Injecting host <-> dev buffer copies...\n";
        s = inject_host_dev_buffer_copies(s, t);
        debug(2) << "Lowering after injecting host <-> dev buffer copies:\n" << s << "\n\n";
    }

    if (t.has_feature(Target::OpenGL)) {
        debug(1) << "Injecting OpenGL texture intrinsics...\n";
        s = inject_opengl_intrinsics(s);
        debug(2) << "Lowering after OpenGL intrinsics:\n" << s << "\n\n";
    }

    if (t.has_gpu_feature() ||
        t.has_feature(Target::OpenGLCompute) ||
        t.has_feature(Target::Renderscript)) {
        debug(1) << "Injecting per-block gpu synchronization...\n";
        s = fuse_gpu_thread_loops(s);
        debug(2) << "Lowering after injecting per-block gpu synchronization:\n" << s << "\n\n";
    }

    debug(1) << "Simplifying...\n";
    s = simplify(s);
    s = unify_duplicate_lets(s);
    s = remove_trivial_for_loops(s);
    debug(2) << "Lowering after second simplifcation:\n" << s << "\n\n";

    debug(1) << "Unrolling...\n";
    s = unroll_loops(s);
    s = simplify(s);
    debug(2) << "Lowering after unrolling:\n" << s << "\n\n";

    debug(1) << "Vectorizing...\n";
    s = vectorize_loops(s);
    s = simplify(s);
    debug(0) << "Lowering after vectorizing:\n" << s << "\n\n";

    debug(1) << "Detecting vector interleavings...\n";
    s = rewrite_interleavings(s);
    s = simplify(s);
    debug(2) << "Lowering after rewriting vector interleavings:\n" << s << "\n\n";

    debug(1) << "Partitioning loops to simplify boundary conditions...\n";
    s = partition_loops(s);
    s = simplify(s);
    debug(2) << "Lowering after partitioning loops:\n" << s << "\n\n";

    debug(1) << "Trimming loops to the region over which they do something...\n";
    s = trim_no_ops(s);
    debug(2) << "Lowering after loop trimming:\n" << s << "\n\n";

    debug(1) << "Injecting early frees...\n";
    s = inject_early_frees(s);
    debug(2) << "Lowering after injecting early frees:\n" << s << "\n\n";

    if (t.has_feature(Target::Profile)) {
        debug(1) << "Injecting profiling...\n";
        s = inject_profiling(s, pipeline_name);
        debug(2) << "Lowering after injecting profiling:\n" << s << '\n';
    }

    debug(1) << "Simplifying...\n";
    s = common_subexpression_elimination(s);

    if (t.has_feature(Target::OpenGL)) {
        debug(1) << "Detecting varying attributes...\n";
        s = find_linear_expressions(s);
        debug(2) << "Lowering after detecting varying attributes:\n" << s << "\n\n";

        debug(1) << "Moving varying attribute expressions out of the shader...\n";
        s = setup_gpu_vertex_buffer(s);
        debug(2) << "Lowering after removing varying attributes:\n" << s << "\n\n";
    }

    s = remove_dead_allocations(s);
    s = remove_trivial_for_loops(s);
    s = simplify(s);
    debug(1) << "Lowering after final simplification:\n" << s << "\n\n";

    debug(1) << "Splitting off Hexagon offload...\n";
    s = inject_hexagon_rpc(s, t);
    debug(2) << "Lowering after splitting off Hexagon offload:\n" << s << '\n';

    if (!custom_passes.empty()) {
        for (size_t i = 0; i < custom_passes.size(); i++) {
            debug(1) << "Running custom lowering pass " << i << "...\n";
            s = custom_passes[i]->mutate(s);
            debug(1) << "Lowering after custom pass " << i << ":\n" << s << "\n\n";
        }
    }

    return s;
}

}
}
<commit_msg>disabled debug print<commit_after>#include <iostream>
#include <set>
#include <sstream>
#include <algorithm>

#include "Lower.h"

#include "AddImageChecks.h"
#include "AddParameterChecks.h"
#include "AllocationBoundsInference.h"
#include "Bounds.h"
#include "BoundsInference.h"
#include "CSE.h"
#include "Debug.h"
#include "DebugToFile.h"
#include "DeepCopy.h"
#include "Deinterleave.h"
#include "EarlyFree.h"
#include "FindCalls.h"
#include "Function.h"
#include "FuseGPUThreadLoops.h"
#include "HexagonOffload.h"
#include "InjectHostDevBufferCopies.h"
#include "InjectImageIntrinsics.h"
#include "InjectOpenGLIntrinsics.h"
#include "Inline.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "IRPrinter.h"
#include "LoopCarry.h"
#include "Memoization.h"
#include "PartitionLoops.h"
#include "Profiling.h"
#include "Qualify.h"
#include "RealizationOrder.h"
#include "RemoveDeadAllocations.h"
#include "RemoveTrivialForLoops.h"
#include "RemoveUndef.h"
#include "ScheduleFunctions.h"
#include "SelectGPUAPI.h"
#include "SkipStages.h"
#include "SlidingWindow.h"
#include "Simplify.h"
#include "SimplifySpecializations.h"
#include "StorageFlattening.h"
#include "StorageFolding.h"
#include "Substitute.h"
#include "Tracing.h"
#include "TrimNoOps.h"
#include "UnifyDuplicateLets.h"
#include "UniquifyVariableNames.h"
#include "UnrollLoops.h"
#include "VaryingAttributes.h"
#include "VectorizeLoops.h"
#include "WrapCalls.h"

namespace Halide {
namespace Internal {

using std::set;
using std::ostringstream;
using std::string;
using std::vector;
using std::map;

Stmt lower(vector<Function> outputs, const string &pipeline_name, const Target &t, const vector<IRMutator *> &custom_passes) {

    // Compute an environment
    map<string, Function> env;
    for (Function f : outputs) {
        map<string, Function> more_funcs = find_transitive_calls(f);
        env.insert(more_funcs.begin(), more_funcs.end());
    }

    // Create a deep-copy of the entire graph of Funcs.
    std::tie(outputs, env) = deep_copy(outputs, env);

    // Substitute in wrapper Funcs
    env = wrap_func_calls(env);

    // Compute a realization order
    vector<string> order = realization_order(outputs, env);

    // Try to simplify the RHS/LHS of a function definition by propagating its
    // specializations' conditions
    simplify_specializations(env);

    bool any_memoized = false;

    debug(1) << "Creating initial loop nests...\n";
    Stmt s = schedule_functions(outputs, order, env, t, any_memoized);
    debug(2) << "Lowering after creating initial loop nests:\n" << s << '\n';

    if (any_memoized) {
        debug(1) << "Injecting memoization...\n";
        s = inject_memoization(s, env, pipeline_name, outputs);
        debug(2) << "Lowering after injecting memoization:\n" << s << '\n';
    } else {
        debug(1) << "Skipping injecting memoization...\n";
    }

    debug(1) << "Injecting tracing...\n";
    s = inject_tracing(s, pipeline_name, env, outputs);
    debug(2) << "Lowering after injecting tracing:\n" << s << '\n';

    debug(1) << "Adding checks for parameters\n";
    s = add_parameter_checks(s, t);
    debug(2) << "Lowering after injecting parameter checks:\n" << s << '\n';

    // Compute the maximum and minimum possible value of each
    // function. Used in later bounds inference passes.
    debug(1) << "Computing bounds of each function's value\n";
    FuncValueBounds func_bounds = compute_function_value_bounds(order, env);

    // The checks will be in terms of the symbols defined by bounds
    // inference.
    debug(1) << "Adding checks for images\n";
    s = add_image_checks(s, outputs, t, order, env, func_bounds);
    debug(2) << "Lowering after injecting image checks:\n" << s << '\n';

    // This pass injects nested definitions of variable names, so we
    // can't simplify statements from here until we fix them up. (We
    // can still simplify Exprs).
    debug(1) << "Performing computation bounds inference...\n";
    s = bounds_inference(s, outputs, order, env, func_bounds);
    debug(2) << "Lowering after computation bounds inference:\n" << s << '\n';

    debug(1) << "Performing sliding window optimization...\n";
    s = sliding_window(s, env);
    debug(2) << "Lowering after sliding window:\n" << s << '\n';

    debug(1) << "Performing allocation bounds inference...\n";
    s = allocation_bounds_inference(s, env, func_bounds);
    debug(2) << "Lowering after allocation bounds inference:\n" << s << '\n';

    debug(1) << "Removing code that depends on undef values...\n";
    s = remove_undef(s);
    debug(2) << "Lowering after removing code that depends on undef values:\n" << s << "\n\n";

    // This uniquifies the variable names, so we're good to simplify
    // after this point. This lets later passes assume syntactic
    // equivalence means semantic equivalence.
    debug(1) << "Uniquifying variable names...\n";
    s = uniquify_variable_names(s);
    debug(2) << "Lowering after uniquifying variable names:\n" << s << "\n\n";

    debug(1) << "Performing storage folding optimization...\n";
    s = storage_folding(s, env);
    debug(2) << "Lowering after storage folding:\n" << s << '\n';

    debug(1) << "Injecting debug_to_file calls...\n";
    s = debug_to_file(s, outputs, env);
    debug(2) << "Lowering after injecting debug_to_file calls:\n" << s << '\n';

    debug(1) << "Simplifying...\n"; // without removing dead lets, because storage flattening needs the strides
    s = simplify(s, false);
    debug(2) << "Lowering after first simplification:\n" << s << "\n\n";

    debug(1) << "Dynamically skipping stages...\n";
    s = skip_stages(s, order);
    debug(2) << "Lowering after dynamically skipping stages:\n" << s << "\n\n";

    if (t.has_feature(Target::OpenGL) || t.has_feature(Target::Renderscript)) {
        debug(1) << "Injecting image intrinsics...\n";
        s = inject_image_intrinsics(s, env);
        debug(2) << "Lowering after image intrinsics:\n" << s << "\n\n";
    }

    debug(1) << "Performing storage flattening...\n";
    s = storage_flattening(s, outputs, env, t);
    debug(2) << "Lowering after storage flattening:\n" << s << "\n\n";

    if (any_memoized) {
        debug(1) << "Rewriting memoized allocations...\n";
        s = rewrite_memoized_allocations(s, env);
        debug(2) << "Lowering after rewriting memoized allocations:\n" << s << "\n\n";
    } else {
        debug(1) << "Skipping rewriting memoized allocations...\n";
    }

    if (t.has_gpu_feature() ||
        t.has_feature(Target::OpenGLCompute) ||
        t.has_feature(Target::OpenGL) ||
        t.has_feature(Target::Renderscript) ||
        (t.arch != Target::Hexagon && (t.features_any_of({Target::HVX_64, Target::HVX_128})))) {
        debug(1) << "Selecting a GPU API for GPU loops...\n";
        s = select_gpu_api(s, t);
        debug(2) << "Lowering after selecting a GPU API:\n" << s << "\n\n";

        debug(1) << "Injecting host <-> dev buffer copies...\n";
        s = inject_host_dev_buffer_copies(s, t);
        debug(2) << "Lowering after injecting host <-> dev buffer copies:\n" << s << "\n\n";
    }

    if (t.has_feature(Target::OpenGL)) {
        debug(1) << "Injecting OpenGL texture intrinsics...\n";
        s = inject_opengl_intrinsics(s);
        debug(2) << "Lowering after OpenGL intrinsics:\n" << s << "\n\n";
    }

    if (t.has_gpu_feature() ||
        t.has_feature(Target::OpenGLCompute) ||
        t.has_feature(Target::Renderscript)) {
        debug(1) << "Injecting per-block gpu synchronization...\n";
        s = fuse_gpu_thread_loops(s);
        debug(2) << "Lowering after injecting per-block gpu synchronization:\n" << s << "\n\n";
    }

    debug(1) << "Simplifying...\n";
    s = simplify(s);
    s = unify_duplicate_lets(s);
    s = remove_trivial_for_loops(s);
    debug(2) << "Lowering after second simplifcation:\n" << s << "\n\n";

    debug(1) << "Unrolling...\n";
    s = unroll_loops(s);
    s = simplify(s);
    debug(2) << "Lowering after unrolling:\n" << s << "\n\n";

    debug(1) << "Vectorizing...\n";
    s = vectorize_loops(s);
    s = simplify(s);
    debug(2) << "Lowering after vectorizing:\n" << s << "\n\n";

    debug(1) << "Detecting vector interleavings...\n";
    s = rewrite_interleavings(s);
    s = simplify(s);
    debug(2) << "Lowering after rewriting vector interleavings:\n" << s << "\n\n";

    debug(1) << "Partitioning loops to simplify boundary conditions...\n";
    s = partition_loops(s);
    s = simplify(s);
    debug(2) << "Lowering after partitioning loops:\n" << s << "\n\n";

    debug(1) << "Trimming loops to the region over which they do something...\n";
    s = trim_no_ops(s);
    debug(2) << "Lowering after loop trimming:\n" << s << "\n\n";

    debug(1) << "Injecting early frees...\n";
    s = inject_early_frees(s);
    debug(2) << "Lowering after injecting early frees:\n" << s << "\n\n";

    if (t.has_feature(Target::Profile)) {
        debug(1) << "Injecting profiling...\n";
        s = inject_profiling(s, pipeline_name);
        debug(2) << "Lowering after injecting profiling:\n" << s << '\n';
    }

    debug(1) << "Simplifying...\n";
    s = common_subexpression_elimination(s);

    if (t.has_feature(Target::OpenGL)) {
        debug(1) << "Detecting varying attributes...\n";
        s = find_linear_expressions(s);
        debug(2) << "Lowering after detecting varying attributes:\n" << s << "\n\n";

        debug(1) << "Moving varying attribute expressions out of the shader...\n";
        s = setup_gpu_vertex_buffer(s);
        debug(2) << "Lowering after removing varying attributes:\n" << s << "\n\n";
    }

    s = remove_dead_allocations(s);
    s = remove_trivial_for_loops(s);
    s = simplify(s);
    debug(1) << "Lowering after final simplification:\n" << s << "\n\n";

    debug(1) << "Splitting off Hexagon offload...\n";
    s = inject_hexagon_rpc(s, t);
    debug(2) << "Lowering after splitting off Hexagon offload:\n" << s << '\n';

    if (!custom_passes.empty()) {
        for (size_t i = 0; i < custom_passes.size(); i++) {
            debug(1) << "Running custom lowering pass " << i << "...\n";
            s = custom_passes[i]->mutate(s);
            debug(1) << "Lowering after custom pass " << i << ":\n" << s << "\n\n";
        }
    }

    return s;
}

}
}
<|endoftext|>


<commit_before>#include <iostream>
#include "protojson.h"
#include "core/assert.h"
#include "core/str.h"

// todo: add proto-> json
// todo: add error path
// todo: fix naming

namespace
{
  std::string json2field(const rapidjson::Value* json, google::protobuf::Message* msg, const google::protobuf::FieldDescriptor *field)
  {
    const auto* ref = msg->GetReflection();
    const bool repeated = field->is_repeated();
    switch (field->cpp_type())
    {
      case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddInt32(msg, field, (int32_t) json->GetInt());
        }
        else
        {
          ref->SetInt32(msg, field, (int32_t) json->GetInt());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddUInt32(msg, field, json->GetUint());
        }
        else
        {
          ref->SetUInt32(msg, field, json->GetUint());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddInt64(msg, field, json->GetInt64());
        }
        else
        {
          ref->SetInt64(msg, field, json->GetInt64());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddUInt64(msg, field, json->GetUint64());
        }
        else
        {
          ref->SetUInt64(msg, field, json->GetUint64());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddDouble(msg, field, json->GetDouble());
        }
        else
        {
          ref->SetDouble(msg, field, json->GetDouble());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
      {
        if (json->GetType() != rapidjson::kNumberType)
        {
          return "Not a number";
        }
        if (repeated)
        {
          ref->AddFloat(msg, field, json->GetDouble());
        }
        else
        {
          ref->SetFloat(msg, field, json->GetDouble());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
      {
        if (json->GetType() != rapidjson::kTrueType && json->GetType() != rapidjson::kFalseType)
        {
          return "Not a bool";
        }
        bool v = json->GetBool();
        if (repeated)
        {
          ref->AddBool(msg, field, v);
        }
        else
        {
          ref->SetBool(msg, field, v);
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
      {
        if (json->GetType() != rapidjson::kStringType)
        {
          return "Not a string";
        }
        const char* value = json->GetString();
        uint32_t str_size = json->GetStringLength();
        std::string str_value(value, str_size);
        if (field->type() == google::protobuf::FieldDescriptor::TYPE_BYTES)
        {
          if (repeated)
          {
            Assert(false);
            // ref->AddString(msg, field, b64_decode(str_value));
          }
          else
          {
            Assert(false);
            // ref->SetString(msg, field, b64_decode(str_value));
          }
        }
        else
        {
          if (repeated)
          {
            ref->AddString(msg, field, str_value);
          }
          else
          {
            ref->SetString(msg, field, str_value);
          }
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
      {
        google::protobuf::Message *mf = (repeated) ? ref->AddMessage(msg, field) : ref->MutableMessage(msg, field);
        return protojson::ToProto(*json, mf);
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
      {
        const auto* ed = field->enum_type();
        const google::protobuf::EnumValueDescriptor* ev = nullptr;
        if (json->GetType() == rapidjson::kNumberType)
        {
          ev = ed->FindValueByNumber(json->GetInt());
        }
        else if (json->GetType() == rapidjson::kStringType)
        {
          ev = ed->FindValueByName(json->GetString());
        }
        else return "Not an integer or string";
        if (!ev)
          return "Enum value not found";
        if (repeated)
        {
          ref->AddEnum(msg, field, ev);
        }
        else
        {
          ref->SetEnum(msg, field, ev);
        }
        break;
      }
      default:
        break;
    }
    return "";
  }
}

namespace protojson
{
  std::string ToProto(const rapidjson::Value& json, google::protobuf::Message* msg)
  {
    if (json.GetType() != rapidjson::kObjectType)
    {
      return "JSON root is not a object";
    }
    const  auto* d = msg->GetDescriptor();
    const auto* ref = msg->GetReflection();
    if (d == nullptr || ref == nullptr)
    {
      return "Protobuf object has no reflection";
    }

    for (rapidjson::Value::ConstMemberIterator itr = json.MemberBegin(); itr != json.MemberEnd(); ++itr)
    {
      const char* name = itr->name.GetString();
      const auto* field = d->FindFieldByName(name);
      if (field == nullptr)
        field = ref->FindKnownExtensionByName(name);
      if (field == nullptr) {
        return Str() << "Failed to find json field " << name << " in proto";
      }
      if (itr->value.GetType() == rapidjson::kNullType) {
        ref->ClearField(msg, field);
        continue;
      }
      if (field->is_repeated())
      {
        if (itr->value.GetType() != rapidjson::kArrayType) {
          return "Protobuf is repeated but json didnt specify an array";
        }
        for (rapidjson::Value::ConstValueIterator ait = itr->value.Begin(); ait != itr->value.End(); ++ait)
        {
          const auto ret = json2field(ait, msg, field);
          if (!ret.empty())
          {
            return ret;
          }
        }
      }
      else
      {
        const auto ret = json2field(&(itr->value), msg, field);
        if (!ret.empty())
        {
          return ret;
        }
      }
    }
    return "";
  }
}
<commit_msg>improved json->protobuf errors<commit_after>#include <iostream>
#include <google/protobuf/descriptor.pb.h>
#include "protojson.h"
#include "core/assert.h"
#include "core/str.h"

// todo: add proto-> json
// todo: fix naming

namespace
{
const char* const TypeToString(rapidjson::Type type)
{
  switch(type)
  {
    case rapidjson::kNullType  : return "null"  ;
    case rapidjson::kFalseType : return "false" ;
    case rapidjson::kTrueType  : return "true"  ;
    case rapidjson::kObjectType: return "object";
    case rapidjson::kArrayType : return "array" ;
    case rapidjson::kStringType: return "string";
    case rapidjson::kNumberType: return "number";
    default: return "<unknown>";
  }
}

std::string ToString(const rapidjson::Value& val)
{
  // todo: add more printing
  return TypeToString(val.GetType());
}

  class JsonPath {
   public:
    JsonPath(const JsonPath* p, const rapidjson::Value& s)
      : parent(p), self(ToString(s))
    {
    }

    static void Print(const JsonPath* path, std::ostringstream& ss)
    {
      if(path != nullptr) {
        Print(path->parent, ss);
        ss << "/" << path->self;
      }
      else {
        // todo: print root sign?
        ss << "<root>";
      }
    }

   private:
    const JsonPath* parent;
    std::string self;
  };

  std::ostringstream& operator<<(std::ostringstream& o, const JsonPath& path)
  {
    JsonPath::Print(&path, o);
    return o;
  }

  std::string JsonToProtoField(const rapidjson::Value& json, google::protobuf::Message* msg, const google::protobuf::FieldDescriptor *field, const JsonPath* path)
  {
    const JsonPath self_path {path, json};
    const auto* ref = msg->GetReflection();
    const bool repeated = field->is_repeated();
    switch (field->cpp_type())
    {
      case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(int32), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddInt32(msg, field, (int32_t) json.GetInt());
        }
        else
        {
          ref->SetInt32(msg, field, (int32_t) json.GetInt());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(uint32), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddUInt32(msg, field, json.GetUint());
        }
        else
        {
          ref->SetUInt32(msg, field, json.GetUint());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(int64), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddInt64(msg, field, json.GetInt64());
        }
        else
        {
          ref->SetInt64(msg, field, json.GetInt64());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(uint64), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddUInt64(msg, field, json.GetUint64());
        }
        else
        {
          ref->SetUInt64(msg, field, json.GetUint64());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(double), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddDouble(msg, field, json.GetDouble());
        }
        else
        {
          ref->SetDouble(msg, field, json.GetDouble());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
      {
        if (json.GetType() != rapidjson::kNumberType)
        {
          return Str() << "Not a number(float), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddFloat(msg, field, json.GetDouble());
        }
        else
        {
          ref->SetFloat(msg, field, json.GetDouble());
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
      {
        if (json.GetType() != rapidjson::kTrueType && json.GetType() != rapidjson::kFalseType)
        {
          return Str() << "Not true or false, found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        bool v = json.GetBool();
        if (repeated)
        {
          ref->AddBool(msg, field, v);
        }
        else
        {
          ref->SetBool(msg, field, v);
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
      {
        if (json.GetType() != rapidjson::kStringType)
        {
          return Str() << "Not a string, found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        const char* value = json.GetString();
        uint32_t str_size = json.GetStringLength();
        std::string str_value(value, str_size);
        if (field->type() == google::protobuf::FieldDescriptor::TYPE_BYTES)
        {
          if (repeated)
          {
            Assert(false);
            // ref->AddString(msg, field, b64_decode(str_value));
          }
          else
          {
            Assert(false);
            // ref->SetString(msg, field, b64_decode(str_value));
          }
        }
        else
        {
          if (repeated)
          {
            ref->AddString(msg, field, str_value);
          }
          else
          {
            ref->SetString(msg, field, str_value);
          }
        }
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
      {
        google::protobuf::Message *mf = (repeated) ? ref->AddMessage(msg, field) : ref->MutableMessage(msg, field);
        return protojson::ToProto(json, mf);
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
      {
        const auto* ed = field->enum_type();
        const google::protobuf::EnumValueDescriptor* ev = nullptr;
        if (json.GetType() == rapidjson::kNumberType)
        {
          const auto value = json.GetInt();
          ev = ed->FindValueByNumber(value);
          if (!ev)
          {
            return Str() << "Enum value " << value << "not found, " << " at " << self_path;
          }
        }
        else if (json.GetType() == rapidjson::kStringType)
        {
          const std::string value = json.GetString();
          ev = ed->FindValueByName(value);
          if (!ev)
          {
            // todo: list all values, guess based on shortest edit distance?
            return Str() << "Enum value " << value << "not found, " << " at " << self_path;
          }
        }
        else
        {
          return Str() << "Neither integer or string(enum), found a " << TypeToString(json.GetType()) << " at " << self_path;
        }
        if (repeated)
        {
          ref->AddEnum(msg, field, ev);
        }
        else
        {
          ref->SetEnum(msg, field, ev);
        }
        break;
      }
      default:
        return Str() << "Unhandled json type, found a " << TypeToString(json.GetType()) << " at " << self_path;
    }
    return "";
  }
}

namespace protojson
{
  std::string ToProto(const rapidjson::Value& json, google::protobuf::Message* msg, JsonPath* path)
  {
    JsonPath self_path {path, json};
    if (json.GetType() != rapidjson::kObjectType)
    {
      return Str() << "JSON root is not a object at " << self_path;;
    }
    const  auto* d = msg->GetDescriptor();
    const auto* ref = msg->GetReflection();
    if (d == nullptr || ref == nullptr)
    {
      return Str() << "Protobuf object has no reflection at " << self_path;
    }

    for (rapidjson::Value::ConstMemberIterator itr = json.MemberBegin(); itr != json.MemberEnd(); ++itr)
    {
      const char* name = itr->name.GetString();
      const auto* field = d->FindFieldByName(name);
      if (field == nullptr)
        field = ref->FindKnownExtensionByName(name);
      if (field == nullptr) {
        // todo: list all fields
        return Str() << "Failed to find json field " << name << " in proto at " << self_path;;
      }
      if (itr->value.GetType() == rapidjson::kNullType) {
        ref->ClearField(msg, field);
        continue;
      }
      if (field->is_repeated())
      {
        if (itr->value.GetType() != rapidjson::kArrayType) {
          return Str() << "Protobuf is repeated but json didn't specify an array at " << self_path;
        }
        for (rapidjson::Value::ConstValueIterator ait = itr->value.Begin(); ait != itr->value.End(); ++ait)
        {
          const rapidjson::Value* val = ait;
          const auto ret = JsonToProtoField(*val, msg, field, &self_path);
          if (!ret.empty())
          {
            return ret;
          }
        }
      }
      else
      {
        const auto ret = JsonToProtoField(itr->value, msg, field, &self_path);
        if (!ret.empty())
        {
          return ret;
        }
      }
    }
    return "";
  }

  std::string ToProto(const rapidjson::Value& json, google::protobuf::Message* msg)
  {
    return ToProto(json, msg, nullptr);
  }
}
<|endoftext|>


<commit_before>/*
 * Photo.cpp
 *
 *  Created on: Aug 14, 2013
 *      Author: andresf
 */

#include <Photo.h>

namespace me {

Photo::Photo() {
	// TODO Auto-generated constructor stub
}

Photo::~Photo() {
	// TODO Auto-generated destructor stub
}

} /* namespace me */
<commit_msg>Photo.cpp * Deleted comments.<commit_after>/*
 * Photo.cpp
 *
 *  Created on: Aug 14, 2013
 *      Author: andresf
 */

#include <Photo.h>

namespace me {

Photo::Photo() {
}

Photo::~Photo() {
}

} /* namespace me */
<|endoftext|>


<commit_before>#include "accumulatedAttribs.cxx"
#include "ambientLight.cxx"
#include "bamFile.cxx"
#include "billboardEffect.cxx"
#include "binCullHandler.cxx"
#include "camera.cxx"
#include "clipPlaneAttrib.cxx"
#include "colorAttrib.cxx"
#include "colorBlendAttrib.cxx"
#include "colorScaleAttrib.cxx"
#include "colorWriteAttrib.cxx"
#include "compassEffect.cxx"
#include "config_pgraph.cxx"
#include "cullBin.cxx"
#include "cullBinAttrib.cxx"
#include "cullBinBackToFront.cxx"
#include "cullBinFixed.cxx"
#include "cullBinFrontToBack.cxx"
#include "cullBinManager.cxx"
#include "cullBinUnsorted.cxx"
#include "cullFaceAttrib.cxx"
#include "cullHandler.cxx"
#include "cullResult.cxx"
#include "cullTraverser.cxx"
#include "cullTraverserData.cxx"
#include "cullableObject.cxx"
#include "decalEffect.cxx"
#include "depthOffsetAttrib.cxx"
#include "depthTestAttrib.cxx"
#include "depthWriteAttrib.cxx"
#include "alphaTestAttrib.cxx"
#include "directionalLight.cxx"
#include "drawCullHandler.cxx"
#include "fadeLodNode.cxx"
#include "findApproxPath.cxx"
#include "findApproxLevelEntry.cxx"
#include "fog.cxx"
#include "fogAttrib.cxx"
#include "geomNode.cxx"
#include "geomTransformer.cxx"
<commit_msg>omitted file<commit_after>#include "accumulatedAttribs.cxx"
#include "ambientLight.cxx"
#include "auxSceneData.cxx"
#include "bamFile.cxx"
#include "billboardEffect.cxx"
#include "binCullHandler.cxx"
#include "camera.cxx"
#include "clipPlaneAttrib.cxx"
#include "colorAttrib.cxx"
#include "colorBlendAttrib.cxx"
#include "colorScaleAttrib.cxx"
#include "colorWriteAttrib.cxx"
#include "compassEffect.cxx"
#include "config_pgraph.cxx"
#include "cullBin.cxx"
#include "cullBinAttrib.cxx"
#include "cullBinBackToFront.cxx"
#include "cullBinFixed.cxx"
#include "cullBinFrontToBack.cxx"
#include "cullBinManager.cxx"
#include "cullBinUnsorted.cxx"
#include "cullFaceAttrib.cxx"
#include "cullHandler.cxx"
#include "cullResult.cxx"
#include "cullTraverser.cxx"
#include "cullTraverserData.cxx"
#include "cullableObject.cxx"
#include "decalEffect.cxx"
#include "depthOffsetAttrib.cxx"
#include "depthTestAttrib.cxx"
#include "depthWriteAttrib.cxx"
#include "alphaTestAttrib.cxx"
#include "directionalLight.cxx"
#include "drawCullHandler.cxx"
#include "fadeLodNode.cxx"
#include "findApproxPath.cxx"
#include "findApproxLevelEntry.cxx"
#include "fog.cxx"
#include "fogAttrib.cxx"
#include "geomNode.cxx"
#include "geomTransformer.cxx"
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    SbrCam.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include <math.h>
#include "SbrCam.hh"
#include "SbrRenW.hh"
#include "SbrRen.hh"

void rotate(int fd, float Cosine,float Sine, char axis)
{
  static float tform[4][4] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
  int i,j;

  for (i = 0; i < 3; i++)
    {
    for (j = 0; j < 3; j++)
      {
       if (j == i) tform[i][j] = 1.0;
       else tform[i][j] = 0.0;
      }
    }

  switch (axis)
    {
  case 'x' :
  case 'X' :
    tform[1][1] = Cosine;
    tform[2][2] = tform[1][1];
    tform[1][2] = Sine;
    tform[2][1] = -tform[1][2];
    break;
  case 'y' :
  case 'Y' :
    tform[0][0] = Cosine;
    tform[2][2] = tform[0][0];
    tform[2][0] = Sine;
    tform[0][2] = -tform[2][0];
    break;
  case 'z' :
  case 'Z' :
    tform[0][0] = Cosine;
    tform[1][1] = tform[0][0];
    tform[0][1] = Sine;
    tform[1][0] = -tform[0][1];
    break;
    }

  view_matrix3d(fd, tform, PRE_CONCAT_VW);
}

void translate(int fd, float x, float y, float z)
{
  static float tform[4][4] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};

  tform[3][0] = x;
  tform[3][1] = y;
  tform[3][2] = z;

  view_matrix3d(fd, tform, PRE_CONCAT_VW);
}

void kens_view_volume(int fd, float left, float right, float bottom,
		      float top, float nearz, float farz)
{
  static float tform[4][4] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};

  tform[0][0] = 2.0*nearz/(right-left);
  tform[1][1] = 2.0*nearz/(top-bottom);
  tform[2][0] = (right+left)/(right-left);
  tform[2][1] = (top+bottom)/(top-bottom);
  tform[2][2] = -0.5*(farz+nearz)/(farz-nearz) - 0.5;
  tform[2][3] = -1.0;
  tform[3][2] = -1.0*farz*nearz/(farz-nearz);
  tform[3][3] = 0.0;

  view_matrix3d(fd, tform, REPLACE_VW);
}

void lookat(int fd, float vx,float vy,float vz,float px,float py,float pz,
	    float twist)
{
  float mag;

  rotate(fd,cos(-1.0*twist),sin(-1.0*twist),'z');

  mag = sqrt((pz-vz)*(pz-vz) + (py-vy)*(py-vy) + (px-vx)*(px-vx));
  if (mag != 0)
    rotate(fd,sqrt((px-vx)*(px-vx) + (pz-vz)*(pz-vz))/mag,(vy-py)/mag,'x');

  mag = sqrt((pz-vz)*(pz-vz) + (px-vx)*(px-vx));
  if (mag != 0)
    {
    rotate(fd, (vz-pz)/mag, (px-vx)/mag, 'y');
    }
  else
    {
    if (py < vy)
      {
      rotate(fd, 1, 0, 'y');
      }
    else
      {
      rotate(fd, -1, 0, 'y');
      }
    }

  translate(fd,-vx,-vy,-vz);

  viewpoint(fd,POSITIONAL,vx,vy,vz);
}

/* bonus stereo perspective function - from manual */
void stereopersp(int fd, float fovy, float aspect, float near,
		 float far, float conv, float eye)
{
  float left, right, top, bottom;
  float gltan;

  eye = tan(eye*3.1415926/180.0)*conv;
  gltan = tan(fovy*M_PI/360.0);
  top = gltan*near;
  bottom = -top;

  gltan = tan(fovy*aspect*M_PI/360.0);
  left = bottom*aspect - eye/conv*near;
  right = top*aspect - eye/conv*near;

  kens_view_volume(fd,left,right,bottom,top,near,far);

  /* now translate */
  translate(fd, -eye, 0, 0);
}

// Description:
// Implement base class method.
void vtkSbrCamera::Render(vtkCamera *cam, vtkRenderer *ren)
{
  this->Render(cam, (vtkSbrRenderer *)ren);
}

// Description:
// Actual camera render method.
void vtkSbrCamera::Render(vtkCamera *cam, vtkSbrRenderer *ren)
{
  float aspect[3];
  float vaspect[2];
  float viewport[4];
  float *background;
  int	width,height;
  long clr;
  int left,right,bottom,top;
  long xmax,ymax,zmax;
  float twist;
  int stereo;
  int fd;
  int *size;
  int *screen_size;
  vtkSbrRenderWindow *rw;
  float trans[2];
  float old;
  float view_size[2];
  float vdc_vals[6];
  float *Position, *FocalPoint, *ClippingRange;
  fd = ren->GetFd();

  // get the background color
  background = ren->GetBackground();
  // get size info
  rw = (vtkSbrRenderWindow*)(ren->GetRenderWindow());
  size = rw->GetSize();
  screen_size = rw->GetScreenSize();
  // find out if we should stereo render
  stereo = rw->GetStereoRender();

  // set this renderer's viewport, must turn off z-buffering when changing
  // viewport
  hidden_surface(fd, FALSE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: False False\n");

  memcpy(viewport,ren->GetViewport(),sizeof(float)*4);

  // if were on a stereo renderer draw to special parts of screen
  if (stereo)
    {
    switch ((ren->GetRenderWindow())->GetStereoType())
      {
      case VTK_STEREO_CRYSTAL_EYES:
	if (cam->GetLeftEye())
	  {
	  viewport[1] = 0.5 + viewport[1]*0.5;
	  viewport[3] = 0.5 + viewport[3]*0.5;
	  }
	else
	  {
	  viewport[1] = viewport[1]*0.5;
	  viewport[3] = viewport[3]*0.5;
	  }
	break;
      }
    }

  view_size[0] = (viewport[2] - viewport[0])*size[0];
  view_size[1] = (viewport[3] - viewport[1])*size[1];
  vdc_vals[0] = -1.0 - viewport[0]*size[0]*2.0/view_size[0];
  vdc_vals[3] = vdc_vals[0] + 2.0*screen_size[0]/view_size[0];
  vdc_vals[4] = 1.0 + (1.0-viewport[3])*size[1]*2.0/view_size[1];
  vdc_vals[1] = vdc_vals[4] - 2.0*screen_size[1]/view_size[1];
  vdc_vals[2] = 0;
  vdc_vals[5] = 1.0;

  // make sure the aspect is up to date
  if (stereo)
    {
    switch ((ren->GetRenderWindow())->GetStereoType())
      {
      case VTK_STEREO_CRYSTAL_EYES:
	{
	aspect[0] = view_size[0]/(2.0*view_size[1]);
	aspect[1] = 1.0;
	}
	break;
      default:
	{
	aspect[0] = view_size[0]/view_size[1];
	aspect[1] = 1.0;
	}
      }
    }
  else
    {
    aspect[0] = view_size[0]/view_size[1];
    aspect[1] = 1.0;
    }
  ren->SetAspect(aspect);

  vdc_extent(fd,vdc_vals[0],vdc_vals[1],vdc_vals[2],
	     vdc_vals[3],vdc_vals[4],vdc_vals[5]);

  vtkDebugMacro(<< " screen_size " << screen_size[0] << " "
  << screen_size[1] << endl);
  vtkDebugMacro(<< " size " << size[0] << " " << size[1] << endl);
  vtkDebugMacro(<< " viewport " << viewport[0] << " " << viewport[1]
  << " " << viewport[2] << " " << viewport[3] << endl);

  // set viewport to clear entire window
  view_port(fd,-1.0,-1.0,1.0,1.0);
  hidden_surface(fd, TRUE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: True False\n");

  // Set the background color and clear the display.
  // Since clear control was set to clear z buffer, this is done here
  // also.
  background_color(fd, background[0], background[1], background[2]);

  // clear the view surface so the new background color takes effect
  if (ren->GetErase())
    {
    clear_view_surface(fd);
    vtkDebugMacro(<< " SB_clear_view_surface\n");
    }

  hidden_surface(fd, FALSE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: False False\n");

  // I think the z clipping is done before the divide by w
  vdc_extent(fd,vdc_vals[0],vdc_vals[1],vdc_vals[2],
	     vdc_vals[3],vdc_vals[4],vdc_vals[5]);

  view_port(fd,-1.0,-1.0,1.0,1.0);

  hidden_surface(fd, TRUE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: True False\n");

  twist = cam->GetTwist();

  ClippingRange = cam->GetClippingRange();
  Position = cam->GetPosition();
  FocalPoint = cam->GetFocalPoint();
  if (stereo)
    {
    if (cam->GetLeftEye())
      {
      stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		  ClippingRange[0],ClippingRange[1],
		  cam->GetDistance(),-1.0*cam->GetEyeAngle());
      }
    else
      {
      stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		  ClippingRange[0],ClippingRange[1],
		  cam->GetDistance(),1.0*cam->GetEyeAngle());
      }

    lookat(fd,Position[0], Position[1], Position[2],
	   FocalPoint[0], FocalPoint[1], FocalPoint[2],twist);
    }
  else
    {
    stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		ClippingRange[0],ClippingRange[1],
		cam->GetDistance(),0.0);

    lookat(fd,Position[0], Position[1], Position[2],
	   FocalPoint[0], FocalPoint[1], FocalPoint[2],twist);
    }

  clip_depth(fd,0.0,ClippingRange[1]);

  // if we have a stereo renderer, draw other eye next time
  if (stereo)
    {
    if (cam->GetLeftEye()) cam->SetLeftEye(0);
    else cam->SetLeftEye(1);
    }
}
<commit_msg>fixed up some warnings<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    SbrCam.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include <math.h>
#include "SbrCam.hh"
#include "SbrRenW.hh"
#include "SbrRen.hh"

void rotate(int fd, float Cosine,float Sine, char axis)
{
  static float tform[4][4] = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};
  int i,j;

  for (i = 0; i < 3; i++)
    {
    for (j = 0; j < 3; j++)
      {
       if (j == i) tform[i][j] = 1.0;
       else tform[i][j] = 0.0;
      }
    }

  switch (axis)
    {
  case 'x' :
  case 'X' :
    tform[1][1] = Cosine;
    tform[2][2] = tform[1][1];
    tform[1][2] = Sine;
    tform[2][1] = -tform[1][2];
    break;
  case 'y' :
  case 'Y' :
    tform[0][0] = Cosine;
    tform[2][2] = tform[0][0];
    tform[2][0] = Sine;
    tform[0][2] = -tform[2][0];
    break;
  case 'z' :
  case 'Z' :
    tform[0][0] = Cosine;
    tform[1][1] = tform[0][0];
    tform[0][1] = Sine;
    tform[1][0] = -tform[0][1];
    break;
    }

  view_matrix3d(fd, tform, PRE_CONCAT_VW);
}

void translate(int fd, float x, float y, float z)
{
  static float tform[4][4] = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};

  tform[3][0] = x;
  tform[3][1] = y;
  tform[3][2] = z;

  view_matrix3d(fd, tform, PRE_CONCAT_VW);
}

void kens_view_volume(int fd, float left, float right, float bottom,
		      float top, float nearz, float farz)
{
  static float tform[4][4] = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};

  tform[0][0] = 2.0*nearz/(right-left);
  tform[1][1] = 2.0*nearz/(top-bottom);
  tform[2][0] = (right+left)/(right-left);
  tform[2][1] = (top+bottom)/(top-bottom);
  tform[2][2] = -0.5*(farz+nearz)/(farz-nearz) - 0.5;
  tform[2][3] = -1.0;
  tform[3][2] = -1.0*farz*nearz/(farz-nearz);
  tform[3][3] = 0.0;

  view_matrix3d(fd, tform, REPLACE_VW);
}

void lookat(int fd, float vx,float vy,float vz,float px,float py,float pz,
	    float twist)
{
  float mag;

  rotate(fd,cos(-1.0*twist),sin(-1.0*twist),'z');

  mag = sqrt((pz-vz)*(pz-vz) + (py-vy)*(py-vy) + (px-vx)*(px-vx));
  if (mag != 0)
    rotate(fd,sqrt((px-vx)*(px-vx) + (pz-vz)*(pz-vz))/mag,(vy-py)/mag,'x');

  mag = sqrt((pz-vz)*(pz-vz) + (px-vx)*(px-vx));
  if (mag != 0)
    {
    rotate(fd, (vz-pz)/mag, (px-vx)/mag, 'y');
    }
  else
    {
    if (py < vy)
      {
      rotate(fd, 1, 0, 'y');
      }
    else
      {
      rotate(fd, -1, 0, 'y');
      }
    }

  translate(fd,-vx,-vy,-vz);

  viewpoint(fd,POSITIONAL,vx,vy,vz);
}

/* bonus stereo perspective function - from manual */
void stereopersp(int fd, float fovy, float aspect, float near,
		 float far, float conv, float eye)
{
  float left, right, top, bottom;
  float gltan;

  eye = tan(eye*3.1415926/180.0)*conv;
  gltan = tan(fovy*M_PI/360.0);
  top = gltan*near;
  bottom = -top;

  gltan = tan(fovy*aspect*M_PI/360.0);
  left = bottom*aspect - eye/conv*near;
  right = top*aspect - eye/conv*near;

  kens_view_volume(fd,left,right,bottom,top,near,far);

  /* now translate */
  translate(fd, -eye, 0, 0);
}

// Description:
// Implement base class method.
void vtkSbrCamera::Render(vtkCamera *cam, vtkRenderer *ren)
{
  this->Render(cam, (vtkSbrRenderer *)ren);
}

// Description:
// Actual camera render method.
void vtkSbrCamera::Render(vtkCamera *cam, vtkSbrRenderer *ren)
{
  float aspect[3];
  float viewport[4];
  float *background;
  float twist;
  int stereo;
  int fd;
  int *size;
  int *screen_size;
  vtkSbrRenderWindow *rw;
  float view_size[2];
  float vdc_vals[6];
  float *Position, *FocalPoint, *ClippingRange;
  fd = ren->GetFd();

  // get the background color
  background = ren->GetBackground();
  // get size info
  rw = (vtkSbrRenderWindow*)(ren->GetRenderWindow());
  size = rw->GetSize();
  screen_size = rw->GetScreenSize();
  // find out if we should stereo render
  stereo = rw->GetStereoRender();

  // set this renderer's viewport, must turn off z-buffering when changing
  // viewport
  hidden_surface(fd, FALSE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: False False\n");

  memcpy(viewport,ren->GetViewport(),sizeof(float)*4);

  // if were on a stereo renderer draw to special parts of screen
  if (stereo)
    {
    switch ((ren->GetRenderWindow())->GetStereoType())
      {
      case VTK_STEREO_CRYSTAL_EYES:
	if (cam->GetLeftEye())
	  {
	  viewport[1] = 0.5 + viewport[1]*0.5;
	  viewport[3] = 0.5 + viewport[3]*0.5;
	  }
	else
	  {
	  viewport[1] = viewport[1]*0.5;
	  viewport[3] = viewport[3]*0.5;
	  }
	break;
      }
    }

  view_size[0] = (viewport[2] - viewport[0])*size[0];
  view_size[1] = (viewport[3] - viewport[1])*size[1];
  vdc_vals[0] = -1.0 - viewport[0]*size[0]*2.0/view_size[0];
  vdc_vals[3] = vdc_vals[0] + 2.0*screen_size[0]/view_size[0];
  vdc_vals[4] = 1.0 + (1.0-viewport[3])*size[1]*2.0/view_size[1];
  vdc_vals[1] = vdc_vals[4] - 2.0*screen_size[1]/view_size[1];
  vdc_vals[2] = 0;
  vdc_vals[5] = 1.0;

  // make sure the aspect is up to date
  if (stereo)
    {
    switch ((ren->GetRenderWindow())->GetStereoType())
      {
      case VTK_STEREO_CRYSTAL_EYES:
	{
	aspect[0] = view_size[0]/(2.0*view_size[1]);
	aspect[1] = 1.0;
	}
	break;
      default:
	{
	aspect[0] = view_size[0]/view_size[1];
	aspect[1] = 1.0;
	}
      }
    }
  else
    {
    aspect[0] = view_size[0]/view_size[1];
    aspect[1] = 1.0;
    }
  ren->SetAspect(aspect);

  vdc_extent(fd,vdc_vals[0],vdc_vals[1],vdc_vals[2],
	     vdc_vals[3],vdc_vals[4],vdc_vals[5]);

  vtkDebugMacro(<< " screen_size " << screen_size[0] << " "
  << screen_size[1] << endl);
  vtkDebugMacro(<< " size " << size[0] << " " << size[1] << endl);
  vtkDebugMacro(<< " viewport " << viewport[0] << " " << viewport[1]
  << " " << viewport[2] << " " << viewport[3] << endl);

  // set viewport to clear entire window
  view_port(fd,-1.0,-1.0,1.0,1.0);
  hidden_surface(fd, TRUE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: True False\n");

  // Set the background color and clear the display.
  // Since clear control was set to clear z buffer, this is done here
  // also.
  background_color(fd, background[0], background[1], background[2]);

  // clear the view surface so the new background color takes effect
  if (ren->GetErase())
    {
    clear_view_surface(fd);
    vtkDebugMacro(<< " SB_clear_view_surface\n");
    }

  hidden_surface(fd, FALSE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: False False\n");

  // I think the z clipping is done before the divide by w
  vdc_extent(fd,vdc_vals[0],vdc_vals[1],vdc_vals[2],
	     vdc_vals[3],vdc_vals[4],vdc_vals[5]);

  view_port(fd,-1.0,-1.0,1.0,1.0);

  hidden_surface(fd, TRUE, FALSE);
  vtkDebugMacro(<< " SB_hidden_surface: True False\n");

  twist = cam->GetTwist();

  ClippingRange = cam->GetClippingRange();
  Position = cam->GetPosition();
  FocalPoint = cam->GetFocalPoint();
  if (stereo)
    {
    if (cam->GetLeftEye())
      {
      stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		  ClippingRange[0],ClippingRange[1],
		  cam->GetDistance(),-1.0*cam->GetEyeAngle());
      }
    else
      {
      stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		  ClippingRange[0],ClippingRange[1],
		  cam->GetDistance(),1.0*cam->GetEyeAngle());
      }

    lookat(fd,Position[0], Position[1], Position[2],
	   FocalPoint[0], FocalPoint[1], FocalPoint[2],twist);
    }
  else
    {
    stereopersp(fd,cam->GetViewAngle(), aspect[0] / aspect[1],
		ClippingRange[0],ClippingRange[1],
		cam->GetDistance(),0.0);

    lookat(fd,Position[0], Position[1], Position[2],
	   FocalPoint[0], FocalPoint[1], FocalPoint[2],twist);
    }

  clip_depth(fd,0.0,ClippingRange[1]);

  // if we have a stereo renderer, draw other eye next time
  if (stereo)
    {
    if (cam->GetLeftEye()) cam->SetLeftEye(0);
    else cam->SetLeftEye(1);
    }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// Provide BZFS with a list server connection

/* class header */
#include "ListServerConnection.h"

/* system implementation headers */
#include <string.h>
#include <string>
#include <math.h>
#include <errno.h>

/* common implementation headers */
#include "bzfio.h"
#include "version.h"
#include "TextUtils.h"
#include "Protocol.h"
#include "GameKeeper.h"

/* local implementation headers */
#include "CmdLineOptions.h"

// FIXME remove externs!
extern PingPacket getTeamCounts();
extern uint16_t curMaxPlayers;
extern void sendMessage(int playerIndex, PlayerId targetPlayer,
			const char *message);
extern void sendPlayerInfo(void);
extern void sendIPUpdate(int targetPlayer, int playerIndex);
extern CmdLineOptions *clOptions;

const int ListServerLink::NotConnected = -1;

ListServerLink::ListServerLink(std::string listServerURL,
			       std::string publicizedAddress,
			       std::string publicizedTitle)
{
  // parse url
  std::string protocol, _hostname, _pathname;
  int _port = 80;
  bool useDefault = false;

  // use default if it can't be parsed
  if (!BzfNetwork::parseURL(listServerURL, protocol, _hostname, _port,
			    _pathname))
    useDefault = true;

  // use default if wrong protocol or invalid port
  if ((protocol != "http") || (_port < 1) || (_port > 65535))
    useDefault = true;

  // use default if bad address
  Address _address = Address::getHostAddress(_hostname);
  if (_address.isAny())
    useDefault = true;

  // parse default list server URL if we need to; assume default works
  if (useDefault) {
    BzfNetwork::parseURL(DefaultListServerURL, protocol, _hostname, _port,
			 _pathname);
    DEBUG1("Provided list server URL (%s) is invalid.  Using default of %s.\n",
	   listServerURL.c_str(), DefaultListServerURL);
  }

  // add to list
  address          = _address;
  port             = _port;
  pathname         = _pathname;
  hostname         = _hostname;
  this->linkSocket = NotConnected;

  if (clOptions->pingInterface != "")
    localAddress = Address::getHostAddress(clOptions->pingInterface);
  publicizeAddress     = publicizedAddress;
  publicizeDescription = publicizedTitle;
  //if this c'tor is called, it's safe to publicize
  publicizeServer      = true;

  // schedule initial ADD message
  queueMessage(ListServerLink::ADD);
}

ListServerLink::ListServerLink()
{
  // does not create a usable link, so checks should be placed
  // in  all public member functions to ensure that nothing tries
  // to happen if publicizeServer is false
  this->linkSocket = NotConnected;
  this->publicizeServer = false;
}

ListServerLink::~ListServerLink()
{
  // now tell the list server that we're going away.  this can
  // take some time but we don't want to wait too long.  we do
  // our own multiplexing loop and wait for a maximum of 3 seconds
  // total.

  // if we aren't supposed to be publicizing, skip the whole thing
  // and don't waste 3 seconds.
  if (!publicizeServer)
    return;

  queueMessage(ListServerLink::REMOVE);
  TimeKeeper start = TimeKeeper::getCurrent();
  do {
    // compute timeout
    float waitTime = float(3.0 - (TimeKeeper::getCurrent() - start));
    if (waitTime <= 0.0f)
      break;
    if (!isConnected()) //queueMessage should have connected us
      break;
    // check for list server socket connection
    int fdMax = -1;
    fd_set write_set;
    fd_set read_set;
    FD_ZERO(&write_set);
    FD_ZERO(&read_set);
    if (phase == ListServerLink::CONNECTING) {
      FD_SET((unsigned int)linkSocket, &write_set);
    } else {
      FD_SET((unsigned int)linkSocket, &read_set);
    }
    fdMax = linkSocket;

    // wait for socket to connect or timeout
    struct timeval timeout;
    timeout.tv_sec = long(floorf(waitTime));
    timeout.tv_usec = long(1.0e+6f * (waitTime - floorf(waitTime)));
    int nfound = select(fdMax + 1, (fd_set*)&read_set, (fd_set*)&write_set,
			0, &timeout);
    if (nfound == 0)
      // Time has gone, close and go
      break;
    // check for connection to list server
    if (FD_ISSET(linkSocket, &write_set))
      sendQueuedMessages();
    else if (FD_ISSET(linkSocket, &read_set))
      read();
  } while (true);

  // stop list server communication
  closeLink();
}

void ListServerLink::closeLink()
{
  if (isConnected()) {
    close(linkSocket);
    DEBUG4("Closing List Server\n");
    linkSocket = NotConnected;
  }
}

void ListServerLink::read()
{
  if (isConnected()) {
    char buf[2048];
    int bytes = recv(linkSocket, buf, sizeof(buf)-1, 0);
    // TODO don't close unless we've got it all
    closeLink();
    buf[bytes]=0;
    char* base = buf;
    static char *tokGoodIdentifier = "TOKGOOD: ";
    static char *tokBadIdentifier = "TOKBAD: ";
    // walks entire reply including HTTP headers
    while (*base) {
      // find next newline
      char* scan = base;
      while (*scan && *scan != '\r' && *scan != '\n') scan++;
      // if no newline then no more complete replies
      if (*scan != '\r' && *scan != '\n') break;
      while (*scan && (*scan == '\r' || *scan == '\n')) *scan++ = '\0';
      DEBUG4("Got line: \"%s\"\n", base);
      // TODO don't do this if we don't want central logins
      if (strncmp(base, tokGoodIdentifier, strlen(tokGoodIdentifier)) == 0) {
	char *callsign, *group;
	callsign = base + strlen(tokGoodIdentifier);
	DEBUG3("Got: %s\n", base);
	group = callsign;
	while (*group && (*group != ':')) group++;
	while (*group && (*group == ':')) *group++ = 0;
	int playerIndex = GameKeeper::Player::getPlayerIDByName(callsign);
	if (playerIndex < curMaxPlayers) {
	  GameKeeper::Player *playerData
	    = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (playerData != NULL) {
	    // don't accept the global auth if there's a local account
	    // of the same name and the local account is not marked as
	    // being the same as the global account
	    if (!playerData->accessInfo.hasRealPassword()
		|| playerData->accessInfo.getUserInfo(callsign)
		.hasGroup("LOCAL.GLOBAL")) {
	      if (!playerData->accessInfo.isRegistered())
		playerData->accessInfo.storeInfo(NULL);
	      playerData->accessInfo.setPermissionRights();
	      while (*group) {
		char *nextgroup = group;
		while (*nextgroup && (*nextgroup != ':')) nextgroup++;
		while (*nextgroup && (*nextgroup == ':')) *nextgroup++ = 0;
		playerData->accessInfo.addGroup(group);
		group = nextgroup;
	      }
		  playerData->authentication.global(true);
	      sendMessage(ServerPlayer, playerIndex, "Global login approved!");
	      sendIPUpdate(playerIndex, -1);
	      sendPlayerInfo();
	    } else {
	      sendMessage(ServerPlayer, playerIndex, "Global login rejected. "
			  "This callsign is registered locally on this "
			  "server.");
	      sendMessage(ServerPlayer, playerIndex,
			  "If the local account is yours, "
			  "/identify, /deregister and reconnnect, "
			  "or ask an admin for the LOCAL.GLOBAL group.");
	      sendMessage(ServerPlayer, playerIndex,
			  "If it is not yours, please ask an admin "
			  "to deregister it so that you may use your global "
			  "callsign.");
	    }
	    playerData->setNeedThisHostbanChecked(true);
	    playerData->player.clearToken();
	  }
	}
      } else if (!strncmp(base, tokBadIdentifier, strlen(tokBadIdentifier))) {
	char *callsign;
	callsign = base + strlen(tokBadIdentifier);
	int playerIndex = GameKeeper::Player::getPlayerIDByName(callsign);
	DEBUG3("Got: [%d] %s\n", playerIndex, base);
	if (playerIndex < curMaxPlayers) {
	  GameKeeper::Player *playerData
	    = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  sendMessage(ServerPlayer, playerIndex,
		      "Global login rejected, bad token.");
	  if (playerData != NULL) {
	    playerData->setNeedThisHostbanChecked(true);
	    playerData->player.clearToken();
	  }
	}
      }

      // next reply
      base = scan;
    }
    if (nextMessageType != ListServerLink::NONE) {
      // There was a pending request arrived after we write:
      // we should redo all the stuff
      openLink();
    }
  }
}

void ListServerLink::openLink()
{
  // start opening connection if not already doing so
  if (!isConnected()) {
    linkSocket = socket(AF_INET, SOCK_STREAM, 0);
    DEBUG4("Opening List Server\n");
    if (!isConnected()) {
      return;
    }

    // set to non-blocking for connect
    if (BzfNetwork::setNonBlocking(linkSocket) < 0) {
      closeLink();
      return;
    }

    // Make our connection come from our serverAddress in case we have
    // multiple/masked IPs so the list server can verify us.
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr = localAddress;

    // assign the address to the socket
    if (bind(linkSocket, (CNCTType*)&addr, sizeof(addr)) < 0) {
      closeLink();
      return;
    }

    // connect.  this should fail with EINPROGRESS but check for
    // success just in case.
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(port);
    addr.sin_addr   = address;
    if (connect(linkSocket, (CNCTType*)&addr, sizeof(addr)) < 0) {
#if defined(_WIN32)
#undef EINPROGRESS
#define EINPROGRESS EWOULDBLOCK
#endif
      if (getErrno() != EINPROGRESS) {
	nerror("connecting to list server");
	// try to lookup dns name again in case it moved
	this->address = Address::getHostAddress(this->hostname);
	closeLink();
      } else {
	phase = CONNECTING;
      }
    } else {
      // shouldn't arrive here. Just in case, clean
      DEBUG3("list server connect and close?\n");
      closeLink();
    }
  }
}

void ListServerLink::queueMessage(MessageType type)
{
  // ignore if the server is not public
  if (!publicizeServer) return;

  // Open network connection only if closed
  if (!isConnected()) openLink();

  // record next message to send.
  nextMessageType = type;
}

void ListServerLink::sendQueuedMessages()
{
  if (!isConnected())
    return;

  if (nextMessageType == ListServerLink::ADD) {
    DEBUG3("Queuing ADD message to list server\n");
    addMe(getTeamCounts(), publicizeAddress,
	  TextUtils::url_encode(publicizeDescription));
    lastAddTime = TimeKeeper::getCurrent();
  } else if (nextMessageType == ListServerLink::REMOVE) {
    DEBUG3("Queuing REMOVE message to list server\n");
    removeMe(publicizeAddress);
  }
}

void ListServerLink::addMe(PingPacket pingInfo,
			   std::string publicizedAddress,
			   std::string publicizedTitle)
{
  std::string msg;
  std::string hdr;

  // encode ping reply as ascii hex digits plus NULL
  char gameInfo[PingPacketHexPackedSize + 1];
  pingInfo.packHex(gameInfo);

  // send ADD message (must send blank line)
  msg = TextUtils::format
    ("action=ADD&nameport=%s&version=%s&gameinfo=%s&build=%s",
    publicizedAddress.c_str(),
    getServerVersion(), gameInfo,
    getAppVersion());
  msg += "&checktokens=";
  // callsign1@ip1=token1%0D%0Acallsign2@ip2=token2%0D%0A
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    NetHandler *handler = playerData->netHandler;
    std::string encodedCallsign
      = TextUtils::url_encode(playerData->player.getCallSign());
    if (strlen(playerData->player.getCallSign())
	&& strlen(playerData->player.getToken())) {
      msg += encodedCallsign;
      Address addr = handler->getIPAddress();
      if (!addr.isPrivate()) {
	      msg += "@";
	      msg += handler->getTargetIP();
      }
      msg += "=";
      msg += playerData->player.getToken();
      msg += "%0D%0A";
    }
  }
  // *groups=GROUP0%0D%0AGROUP1%0D%0A
  msg += "&groups=";
  PlayerAccessMap::iterator itr = groupAccess.begin();
  for ( ; itr != groupAccess.end(); itr++) {
    if (itr->first.substr(0, 6) != "LOCAL.") {
      msg += itr->first.c_str();
      msg += "%0D%0A";
    }
  }

  msg += TextUtils::format("&title=%s\r\n", publicizedTitle.c_str());
  hdr = TextUtils::format(
      "POST %s HTTP/1.1\r\n"
      "User-Agent: bzfs %s\r\n"
      "Host: %s\r\n"
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Content-Type: application/x-www-form-urlencoded\r\n"
      "Content-Length: %d\r\n"
      "\r\n",
      pathname.c_str(), getAppVersion(), hostname.c_str(), msg.length());
  sendLSMessage(hdr+msg);
}

void ListServerLink::removeMe(std::string publicizedAddress)
{
  std::string msg;

  // send REMOVE (must send blank line)
  msg = TextUtils::format("action=REMOVE&nameport=%s\r\n",  publicizedAddress.c_str());
  sendLSMessage(TextUtils::format(
      "POST %s HTTP/1.1\r\n"
      "User-Agent: bzfs %s\r\n"
      "Host: %s\r\n"
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Content-Type: application/x-www-form-urlencoded\r\n"
      "Content-Length: %d\r\n"
      "\r\n",
      pathname.c_str(), getAppVersion(), hostname.c_str(), msg.length())   + msg);
}

void ListServerLink::sendLSMessage(std::string message)
{
  const int bufsize = 4096;
  char msg[bufsize];
  strncpy(msg, message.c_str(), bufsize);
  msg[bufsize - 1] = 0;
  if (strlen(msg) > 0) {
    DEBUG3("%s\n", msg);
    if (send(linkSocket, msg, strlen(msg), 0) == -1) {
      perror("List server send failed");
      DEBUG3("Unable to send to the list server!\n");
      closeLink();
    } else {
      nextMessageType = ListServerLink::NONE;
      phase	   = ListServerLink::WRITTEN;
    }
  } else {
    closeLink();
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>no need for "this->" when there is no shadow<commit_after>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// Provide BZFS with a list server connection

/* class header */
#include "ListServerConnection.h"

/* system implementation headers */
#include <string.h>
#include <string>
#include <math.h>
#include <errno.h>

/* common implementation headers */
#include "bzfio.h"
#include "version.h"
#include "TextUtils.h"
#include "Protocol.h"
#include "GameKeeper.h"

/* local implementation headers */
#include "CmdLineOptions.h"

// FIXME remove externs!
extern PingPacket getTeamCounts();
extern uint16_t curMaxPlayers;
extern void sendMessage(int playerIndex, PlayerId targetPlayer,
			const char *message);
extern void sendPlayerInfo(void);
extern void sendIPUpdate(int targetPlayer, int playerIndex);
extern CmdLineOptions *clOptions;

const int ListServerLink::NotConnected = -1;

ListServerLink::ListServerLink(std::string listServerURL,
			       std::string publicizedAddress,
			       std::string publicizedTitle)
{
  // parse url
  std::string protocol, _hostname, _pathname;
  int _port = 80;
  bool useDefault = false;

  // use default if it can't be parsed
  if (!BzfNetwork::parseURL(listServerURL, protocol, _hostname, _port,
			    _pathname))
    useDefault = true;

  // use default if wrong protocol or invalid port
  if ((protocol != "http") || (_port < 1) || (_port > 65535))
    useDefault = true;

  // use default if bad address
  Address _address = Address::getHostAddress(_hostname);
  if (_address.isAny())
    useDefault = true;

  // parse default list server URL if we need to; assume default works
  if (useDefault) {
    BzfNetwork::parseURL(DefaultListServerURL, protocol, _hostname, _port,
			 _pathname);
    DEBUG1("Provided list server URL (%s) is invalid.  Using default of %s.\n",
	   listServerURL.c_str(), DefaultListServerURL);
  }

  // add to list
  address          = _address;
  port             = _port;
  pathname         = _pathname;
  hostname         = _hostname;
  linkSocket       = NotConnected;

  if (clOptions->pingInterface != "")
    localAddress = Address::getHostAddress(clOptions->pingInterface);
  publicizeAddress     = publicizedAddress;
  publicizeDescription = publicizedTitle;
  //if this c'tor is called, it's safe to publicize
  publicizeServer      = true;

  // schedule initial ADD message
  queueMessage(ListServerLink::ADD);
}

ListServerLink::ListServerLink()
{
  // does not create a usable link, so checks should be placed
  // in  all public member functions to ensure that nothing tries
  // to happen if publicizeServer is false
  linkSocket      = NotConnected;
  publicizeServer = false;
}

ListServerLink::~ListServerLink()
{
  // now tell the list server that we're going away.  this can
  // take some time but we don't want to wait too long.  we do
  // our own multiplexing loop and wait for a maximum of 3 seconds
  // total.

  // if we aren't supposed to be publicizing, skip the whole thing
  // and don't waste 3 seconds.
  if (!publicizeServer)
    return;

  queueMessage(ListServerLink::REMOVE);
  TimeKeeper start = TimeKeeper::getCurrent();
  do {
    // compute timeout
    float waitTime = float(3.0 - (TimeKeeper::getCurrent() - start));
    if (waitTime <= 0.0f)
      break;
    if (!isConnected()) //queueMessage should have connected us
      break;
    // check for list server socket connection
    int fdMax = -1;
    fd_set write_set;
    fd_set read_set;
    FD_ZERO(&write_set);
    FD_ZERO(&read_set);
    if (phase == ListServerLink::CONNECTING) {
      FD_SET((unsigned int)linkSocket, &write_set);
    } else {
      FD_SET((unsigned int)linkSocket, &read_set);
    }
    fdMax = linkSocket;

    // wait for socket to connect or timeout
    struct timeval timeout;
    timeout.tv_sec = long(floorf(waitTime));
    timeout.tv_usec = long(1.0e+6f * (waitTime - floorf(waitTime)));
    int nfound = select(fdMax + 1, (fd_set*)&read_set, (fd_set*)&write_set,
			0, &timeout);
    if (nfound == 0)
      // Time has gone, close and go
      break;
    // check for connection to list server
    if (FD_ISSET(linkSocket, &write_set))
      sendQueuedMessages();
    else if (FD_ISSET(linkSocket, &read_set))
      read();
  } while (true);

  // stop list server communication
  closeLink();
}

void ListServerLink::closeLink()
{
  if (isConnected()) {
    close(linkSocket);
    DEBUG4("Closing List Server\n");
    linkSocket = NotConnected;
  }
}

void ListServerLink::read()
{
  if (isConnected()) {
    char buf[2048];
    int bytes = recv(linkSocket, buf, sizeof(buf)-1, 0);
    // TODO don't close unless we've got it all
    closeLink();
    buf[bytes]=0;
    char* base = buf;
    static char *tokGoodIdentifier = "TOKGOOD: ";
    static char *tokBadIdentifier = "TOKBAD: ";
    // walks entire reply including HTTP headers
    while (*base) {
      // find next newline
      char* scan = base;
      while (*scan && *scan != '\r' && *scan != '\n') scan++;
      // if no newline then no more complete replies
      if (*scan != '\r' && *scan != '\n') break;
      while (*scan && (*scan == '\r' || *scan == '\n')) *scan++ = '\0';
      DEBUG4("Got line: \"%s\"\n", base);
      // TODO don't do this if we don't want central logins
      if (strncmp(base, tokGoodIdentifier, strlen(tokGoodIdentifier)) == 0) {
	char *callsign, *group;
	callsign = base + strlen(tokGoodIdentifier);
	DEBUG3("Got: %s\n", base);
	group = callsign;
	while (*group && (*group != ':')) group++;
	while (*group && (*group == ':')) *group++ = 0;
	int playerIndex = GameKeeper::Player::getPlayerIDByName(callsign);
	if (playerIndex < curMaxPlayers) {
	  GameKeeper::Player *playerData
	    = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (playerData != NULL) {
	    // don't accept the global auth if there's a local account
	    // of the same name and the local account is not marked as
	    // being the same as the global account
	    if (!playerData->accessInfo.hasRealPassword()
		|| playerData->accessInfo.getUserInfo(callsign)
		.hasGroup("LOCAL.GLOBAL")) {
	      if (!playerData->accessInfo.isRegistered())
		playerData->accessInfo.storeInfo(NULL);
	      playerData->accessInfo.setPermissionRights();
	      while (*group) {
		char *nextgroup = group;
		while (*nextgroup && (*nextgroup != ':')) nextgroup++;
		while (*nextgroup && (*nextgroup == ':')) *nextgroup++ = 0;
		playerData->accessInfo.addGroup(group);
		group = nextgroup;
	      }
		  playerData->authentication.global(true);
	      sendMessage(ServerPlayer, playerIndex, "Global login approved!");
	      sendIPUpdate(playerIndex, -1);
	      sendPlayerInfo();
	    } else {
	      sendMessage(ServerPlayer, playerIndex, "Global login rejected. "
			  "This callsign is registered locally on this "
			  "server.");
	      sendMessage(ServerPlayer, playerIndex,
			  "If the local account is yours, "
			  "/identify, /deregister and reconnnect, "
			  "or ask an admin for the LOCAL.GLOBAL group.");
	      sendMessage(ServerPlayer, playerIndex,
			  "If it is not yours, please ask an admin "
			  "to deregister it so that you may use your global "
			  "callsign.");
	    }
	    playerData->setNeedThisHostbanChecked(true);
	    playerData->player.clearToken();
	  }
	}
      } else if (!strncmp(base, tokBadIdentifier, strlen(tokBadIdentifier))) {
	char *callsign;
	callsign = base + strlen(tokBadIdentifier);
	int playerIndex = GameKeeper::Player::getPlayerIDByName(callsign);
	DEBUG3("Got: [%d] %s\n", playerIndex, base);
	if (playerIndex < curMaxPlayers) {
	  GameKeeper::Player *playerData
	    = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  sendMessage(ServerPlayer, playerIndex,
		      "Global login rejected, bad token.");
	  if (playerData != NULL) {
	    playerData->setNeedThisHostbanChecked(true);
	    playerData->player.clearToken();
	  }
	}
      }

      // next reply
      base = scan;
    }
    if (nextMessageType != ListServerLink::NONE) {
      // There was a pending request arrived after we write:
      // we should redo all the stuff
      openLink();
    }
  }
}

void ListServerLink::openLink()
{
  // start opening connection if not already doing so
  if (!isConnected()) {
    linkSocket = socket(AF_INET, SOCK_STREAM, 0);
    DEBUG4("Opening List Server\n");
    if (!isConnected()) {
      return;
    }

    // set to non-blocking for connect
    if (BzfNetwork::setNonBlocking(linkSocket) < 0) {
      closeLink();
      return;
    }

    // Make our connection come from our serverAddress in case we have
    // multiple/masked IPs so the list server can verify us.
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr = localAddress;

    // assign the address to the socket
    if (bind(linkSocket, (CNCTType*)&addr, sizeof(addr)) < 0) {
      closeLink();
      return;
    }

    // connect.  this should fail with EINPROGRESS but check for
    // success just in case.
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(port);
    addr.sin_addr   = address;
    if (connect(linkSocket, (CNCTType*)&addr, sizeof(addr)) < 0) {
#if defined(_WIN32)
#undef EINPROGRESS
#define EINPROGRESS EWOULDBLOCK
#endif
      if (getErrno() != EINPROGRESS) {
	nerror("connecting to list server");
	// try to lookup dns name again in case it moved
	address = Address::getHostAddress(hostname);
	closeLink();
      } else {
	phase = CONNECTING;
      }
    } else {
      // shouldn't arrive here. Just in case, clean
      DEBUG3("list server connect and close?\n");
      closeLink();
    }
  }
}

void ListServerLink::queueMessage(MessageType type)
{
  // ignore if the server is not public
  if (!publicizeServer) return;

  // Open network connection only if closed
  if (!isConnected()) openLink();

  // record next message to send.
  nextMessageType = type;
}

void ListServerLink::sendQueuedMessages()
{
  if (!isConnected())
    return;

  if (nextMessageType == ListServerLink::ADD) {
    DEBUG3("Queuing ADD message to list server\n");
    addMe(getTeamCounts(), publicizeAddress,
	  TextUtils::url_encode(publicizeDescription));
    lastAddTime = TimeKeeper::getCurrent();
  } else if (nextMessageType == ListServerLink::REMOVE) {
    DEBUG3("Queuing REMOVE message to list server\n");
    removeMe(publicizeAddress);
  }
}

void ListServerLink::addMe(PingPacket pingInfo,
			   std::string publicizedAddress,
			   std::string publicizedTitle)
{
  std::string msg;
  std::string hdr;

  // encode ping reply as ascii hex digits plus NULL
  char gameInfo[PingPacketHexPackedSize + 1];
  pingInfo.packHex(gameInfo);

  // send ADD message (must send blank line)
  msg = TextUtils::format
    ("action=ADD&nameport=%s&version=%s&gameinfo=%s&build=%s",
    publicizedAddress.c_str(),
    getServerVersion(), gameInfo,
    getAppVersion());
  msg += "&checktokens=";
  // callsign1@ip1=token1%0D%0Acallsign2@ip2=token2%0D%0A
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    NetHandler *handler = playerData->netHandler;
    std::string encodedCallsign
      = TextUtils::url_encode(playerData->player.getCallSign());
    if (strlen(playerData->player.getCallSign())
	&& strlen(playerData->player.getToken())) {
      msg += encodedCallsign;
      Address addr = handler->getIPAddress();
      if (!addr.isPrivate()) {
	      msg += "@";
	      msg += handler->getTargetIP();
      }
      msg += "=";
      msg += playerData->player.getToken();
      msg += "%0D%0A";
    }
  }
  // *groups=GROUP0%0D%0AGROUP1%0D%0A
  msg += "&groups=";
  PlayerAccessMap::iterator itr = groupAccess.begin();
  for ( ; itr != groupAccess.end(); itr++) {
    if (itr->first.substr(0, 6) != "LOCAL.") {
      msg += itr->first.c_str();
      msg += "%0D%0A";
    }
  }

  msg += TextUtils::format("&title=%s\r\n", publicizedTitle.c_str());
  hdr = TextUtils::format(
      "POST %s HTTP/1.1\r\n"
      "User-Agent: bzfs %s\r\n"
      "Host: %s\r\n"
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Content-Type: application/x-www-form-urlencoded\r\n"
      "Content-Length: %d\r\n"
      "\r\n",
      pathname.c_str(), getAppVersion(), hostname.c_str(), msg.length());
  sendLSMessage(hdr+msg);
}

void ListServerLink::removeMe(std::string publicizedAddress)
{
  std::string msg;

  // send REMOVE (must send blank line)
  msg = TextUtils::format("action=REMOVE&nameport=%s\r\n",  publicizedAddress.c_str());
  sendLSMessage(TextUtils::format(
      "POST %s HTTP/1.1\r\n"
      "User-Agent: bzfs %s\r\n"
      "Host: %s\r\n"
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Content-Type: application/x-www-form-urlencoded\r\n"
      "Content-Length: %d\r\n"
      "\r\n",
      pathname.c_str(), getAppVersion(), hostname.c_str(), msg.length())   + msg);
}

void ListServerLink::sendLSMessage(std::string message)
{
  const int bufsize = 4096;
  char msg[bufsize];
  strncpy(msg, message.c_str(), bufsize);
  msg[bufsize - 1] = 0;
  if (strlen(msg) > 0) {
    DEBUG3("%s\n", msg);
    if (send(linkSocket, msg, strlen(msg), 0) == -1) {
      perror("List server send failed");
      DEBUG3("Unable to send to the list server!\n");
      closeLink();
    } else {
      nextMessageType = ListServerLink::NONE;
      phase	   = ListServerLink::WRITTEN;
    }
  } else {
    closeLink();
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>