<commit_before>/*
 * Copyright (c) 2010, Nicholas Campbell <nicholas.j.campbell@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the <ORGANIZATION> nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <node.h>
#include <node_version.h>

#include <string>
#include <cstring>

#include <openssl/rand.h>
#include <openssl/crypto.h>
#include "node_blf.h"

//pulled from node commit - 97cada0
#ifdef _WIN32
# include <windows.h>
#else
# include <pthread.h>
#endif

#define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4)))

using namespace v8;
using namespace node;

#ifdef _WIN32

static HANDLE* locks;

static void crypto_lock_init(void) {
  int i, n;

  n = CRYPTO_num_locks();
  locks = new HANDLE[n];

  for (i = 0; i < n; i++)
    if (!(locks[i] = CreateMutex(NULL, FALSE, NULL)))
      abort();
}

static void crypto_lock_cb(int mode, int n, const char* file, int line) {
  if (mode & CRYPTO_LOCK)
    WaitForSingleObject(locks[type], INFINITE);
  else
    ReleaseMutex(locks[type]);
}

static unsigned long crypto_id_cb(void) {
  return (unsigned long) GetCurrentThreadId();
}

#else /* !_WIN32 */

static pthread_rwlock_t* locks;

static void crypto_lock_init(void) {
  int i, n;

  n = CRYPTO_num_locks();
  locks = new pthread_rwlock_t[n];

  for (i = 0; i < n; i++)
    if (pthread_rwlock_init(locks + i, NULL))
      abort();
}

static void crypto_lock_cb(int mode, int n, const char* file, int line) {
  if (mode & CRYPTO_LOCK) {
    if (mode & CRYPTO_READ) pthread_rwlock_rdlock(locks + n);
    if (mode & CRYPTO_WRITE) pthread_rwlock_wrlock(locks + n);
  } else {
    pthread_rwlock_unlock(locks + n);
  }
}

static unsigned long crypto_id_cb(void) {
  return (unsigned long) pthread_self();
}

#endif /* !_WIN32 */

namespace {

struct baton_base {
    v8::Persistent<v8::Function> callback;
    std::string error;

    virtual ~baton_base() {
        callback.Dispose();
    }
};

struct salt_baton : baton_base {
    std::string salt;
    int rand_len;
    ssize_t rounds;
};

struct encrypt_baton : baton_base {
    std::string salt;
    std::string input;
    std::string output;
};

struct compare_baton : baton_base {
    std::string input;
    std::string encrypted;
    bool result;
};

int GetSeed(uint8_t* seed, int size) {
    switch (RAND_bytes((unsigned char *)seed, size)) {
        case -1:
        case 0:
            switch (RAND_pseudo_bytes(seed, size)) {
                case -1:
                    return -1;
                case 0:
                    return 0;
                default:
                    return 1;
            }
        default:
            return 1;
    }
}

bool ValidateSalt(const char* salt) {

    if (!salt || *salt != '$') {
        return false;
    }

    // discard $
    salt++;

    if (*salt > BCRYPT_VERSION) {
        return false;
    }

    if (salt[1] != '$') {
        switch (salt[1]) {
        case 'a':
            salt++;
            break;
        default:
            return false;
        }
    }

    // discard version + $
    salt += 2;

    if (salt[2] != '$') {
        return false;
    }

    int n = atoi(salt);
    if (n > 31 || n < 0) {
        return false;
    }

    if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) {
        return false;
    }

    salt += 3;
    if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
        return false;
    }

    return true;
}

/* SALT GENERATION */
#if NODE_LESS_THAN
int EIO_GenSalt(eio_req *req) {
#else
void EIO_GenSalt(eio_req *req) {
#endif

    salt_baton* baton = static_cast<salt_baton*>(req->data);

    char* salt = new char[_SALT_LEN];
    uint8_t* seed = new uint8_t[baton->rand_len];
    switch(GetSeed(seed, baton->rand_len)) {
        case -1:
            baton->error = "Rand operation not supported.";
        case 0:
            baton->error = "Rand operation did not generate a cryptographically sound seed.";
    }

    bcrypt_gensalt(baton->rounds, seed, salt);
    baton->salt = std::string(salt);

    delete[] salt;
    delete[] seed;

#if NODE_LESS_THAN
    return 0;
#endif
}

int EIO_GenSaltAfter(eio_req *req) {
    HandleScope scope;

    ev_unref(EV_DEFAULT_UC);
    salt_baton* baton = static_cast<salt_baton*>(req->data);

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    }
    else {
        argv[0] = Undefined();
        argv[1] = Encode(baton->salt.c_str(), baton->salt.size(), BINARY);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    delete baton;
    return 0;
}

Handle<Value> GenerateSalt(const Arguments &args) {
    HandleScope scope;

    const ssize_t rounds = args[0]->Int32Value();
    const int rand_len = args[1]->Int32Value();
    Local<Function> callback = Local<Function>::Cast(args[2]);

    salt_baton* baton = new salt_baton();

    baton->callback = Persistent<Function>::New(callback);
    baton->rand_len = rand_len;
    baton->rounds = rounds;

    eio_custom(EIO_GenSalt, EIO_PRI_DEFAULT, EIO_GenSaltAfter, baton);

    ev_ref(EV_DEFAULT_UC);

    return Undefined();
}

Handle<Value> GenerateSaltSync(const Arguments& args) {
    HandleScope scope;

    const ssize_t rounds = args[0]->Int32Value();
    const int size = args[1]->Int32Value();

    uint8_t* seed = new uint8_t[size];
    switch(GetSeed(seed, size)) {
        case -1:
            return ThrowException(Exception::Error(String::New("Rand operation not supported.")));
        case 0:
            return ThrowException(Exception::Error(String::New("Rand operation did not generate a cryptographically sound seed.")));
    }

    char salt[_SALT_LEN];
    bcrypt_gensalt(rounds, seed, salt);
    delete[] seed;

    return scope.Close(Encode(salt, strlen(salt), BINARY));
}

/* ENCRYPT DATA - USED TO BE HASHPW */
#if NODE_LESS_THAN
int EIO_Encrypt(eio_req *req) {
#else
void EIO_Encrypt(eio_req *req) {
#endif
    encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);

    if (!(ValidateSalt(baton->salt.c_str()))) {
        baton->error = "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue";
#if NODE_LESS_THAN
        return 1;
#else
        return;
#endif
    }

    char* bcrypted = new char[_PASSWORD_LEN];
    bcrypt(baton->input.c_str(), baton->salt.c_str(), bcrypted);
    baton->output = std::string(bcrypted);

    delete[] bcrypted;

#if NODE_LESS_THAN
    return 0;
#endif
}

int EIO_EncryptAfter(eio_req *req) {
    HandleScope scope;

    ev_unref(EV_DEFAULT_UC);
    encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    }
    else {
        argv[0] = Undefined();
        argv[1] = Encode(baton->output.c_str(), baton->output.size(), BINARY);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    delete baton;
    return 0;
}

Handle<Value> Encrypt(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value data(args[0]->ToString());
    String::Utf8Value salt(args[1]->ToString());
    Local<Function> callback = Local<Function>::Cast(args[2]);

    encrypt_baton* baton = new encrypt_baton();
    baton->callback = Persistent<Function>::New(callback);
    baton->input = std::string(*data);
    baton->salt = std::string(*salt);

    eio_custom(EIO_Encrypt, EIO_PRI_DEFAULT, EIO_EncryptAfter, baton);

    ev_ref(EV_DEFAULT_UC);

    return Undefined();
}

Handle<Value> EncryptSync(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value data(args[0]->ToString());
    String::Utf8Value salt(args[1]->ToString());

    if (!(ValidateSalt(*salt))) {
        return ThrowException(Exception::Error(String::New("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue")));
    }

    char bcrypted[_PASSWORD_LEN];
    bcrypt(*data, *salt, bcrypted);
    return scope.Close(Encode(bcrypted, strlen(bcrypted), BINARY));
}

/* COMPARATOR */
bool CompareStrings(const char* s1, const char* s2) {

    bool eq = true;
    int s1_len = strlen(s1);
    int s2_len = strlen(s2);

    if (s1_len != s2_len) {
        eq = false;
    }

    const int max_len = (s2_len < s1_len) ? s1_len : s2_len;

    // to prevent timing attacks, should check entire string
    // don't exit after found to be false
    for (int i = 0; i < max_len; ++i) {
      if (s1_len >= i && s2_len >= i && s1[i] != s2[i]) {
        eq = false;
      }
    }

    return eq;
}

#if NODE_LESS_THAN
int EIO_Compare(eio_req *req) {
#else
void EIO_Compare(eio_req *req) {
#endif
    compare_baton* baton = static_cast<compare_baton*>(req->data);

    char bcrypted[_PASSWORD_LEN];
    bcrypt(baton->input.c_str(), baton->encrypted.c_str(), bcrypted);
    baton->result = CompareStrings(bcrypted, baton->encrypted.c_str());

#if NODE_LESS_THAN
    return 0;
#endif
}

int EIO_CompareAfter(eio_req *req) {
    HandleScope scope;

    ev_unref(EV_DEFAULT_UC);

    compare_baton* baton = static_cast<compare_baton*>(req->data);

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    } else {
        argv[0] = Undefined();
        argv[1] = Boolean::New(baton->result);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    // done with the baton
    // free the memory and callback
    delete baton;

    return 0;
}

Handle<Value> Compare(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value input(args[0]->ToString());
    String::Utf8Value encrypted(args[1]->ToString());
    Local<Function> callback = Local<Function>::Cast(args[2]);

    compare_baton* baton = new compare_baton();
    baton->callback = Persistent<Function>::New(callback);
    baton->input = std::string(*input);
    baton->encrypted = std::string(*encrypted);

    eio_custom(EIO_Compare, EIO_PRI_DEFAULT, EIO_CompareAfter, baton);

    ev_ref(EV_DEFAULT_UC);

    return Undefined();
}

Handle<Value> CompareSync(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value pw(args[0]->ToString());
    String::Utf8Value hash(args[1]->ToString());

    char bcrypted[_PASSWORD_LEN];
    bcrypt(*pw, *hash, bcrypted);
    return Boolean::New(CompareStrings(bcrypted, *hash));
}

Handle<Value> GetRounds(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value hash(args[0]->ToString());
    u_int32_t rounds;
    if (!(rounds = bcrypt_get_rounds(*hash))) {
        return ThrowException(Exception::Error(String::New("invalid hash provided")));
    }

    return Integer::New(rounds);
}

} // anonymous namespace

// bind the bcrypt module
extern "C" void init(Handle<Object> target) {
    HandleScope scope;

    crypto_lock_init();
    CRYPTO_set_locking_callback(crypto_lock_cb);
    CRYPTO_set_id_callback(crypto_id_cb);

    NODE_SET_METHOD(target, "gen_salt_sync", GenerateSaltSync);
    NODE_SET_METHOD(target, "encrypt_sync", EncryptSync);
    NODE_SET_METHOD(target, "compare_sync", CompareSync);
    NODE_SET_METHOD(target, "get_rounds", GetRounds);
    NODE_SET_METHOD(target, "gen_salt", GenerateSalt);
    NODE_SET_METHOD(target, "encrypt", Encrypt);
    NODE_SET_METHOD(target, "compare", Compare);
};

<commit_msg>move to libuv<commit_after>/*
 * Copyright (c) 2010, Nicholas Campbell <nicholas.j.campbell@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the <ORGANIZATION> nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <node.h>
#include <node_version.h>

#include <string>
#include <cstring>

#include <openssl/rand.h>
#include <openssl/crypto.h>
#include "node_blf.h"

//pulled from node commit - 97cada0
#ifdef _WIN32
# include <windows.h>
#else
# include <pthread.h>
#endif

#define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4)))

using namespace v8;
using namespace node;

#ifdef _WIN32

static HANDLE* locks;

static void crypto_lock_init(void) {
  int i, n;

  n = CRYPTO_num_locks();
  locks = new HANDLE[n];

  for (i = 0; i < n; i++)
    if (!(locks[i] = CreateMutex(NULL, FALSE, NULL)))
      abort();
}

static void crypto_lock_cb(int mode, int n, const char* file, int line) {
  if (mode & CRYPTO_LOCK)
    WaitForSingleObject(locks[type], INFINITE);
  else
    ReleaseMutex(locks[type]);
}

static unsigned long crypto_id_cb(void) {
  return (unsigned long) GetCurrentThreadId();
}

#else /* !_WIN32 */

static pthread_rwlock_t* locks;

static void crypto_lock_init(void) {
  int i, n;

  n = CRYPTO_num_locks();
  locks = new pthread_rwlock_t[n];

  for (i = 0; i < n; i++)
    if (pthread_rwlock_init(locks + i, NULL))
      abort();
}

static void crypto_lock_cb(int mode, int n, const char* file, int line) {
  if (mode & CRYPTO_LOCK) {
    if (mode & CRYPTO_READ) pthread_rwlock_rdlock(locks + n);
    if (mode & CRYPTO_WRITE) pthread_rwlock_wrlock(locks + n);
  } else {
    pthread_rwlock_unlock(locks + n);
  }
}

static unsigned long crypto_id_cb(void) {
  return (unsigned long) pthread_self();
}

#endif /* !_WIN32 */

namespace {

struct baton_base {
    v8::Persistent<v8::Function> callback;
    std::string error;

    virtual ~baton_base() {
        callback.Dispose();
    }
};

struct salt_baton : baton_base {
    std::string salt;
    int rand_len;
    ssize_t rounds;
};

struct encrypt_baton : baton_base {
    std::string salt;
    std::string input;
    std::string output;
};

struct compare_baton : baton_base {
    std::string input;
    std::string encrypted;
    bool result;
};

int GetSeed(uint8_t* seed, int size) {
    switch (RAND_bytes((unsigned char *)seed, size)) {
        case -1:
        case 0:
            switch (RAND_pseudo_bytes(seed, size)) {
                case -1:
                    return -1;
                case 0:
                    return 0;
                default:
                    return 1;
            }
        default:
            return 1;
    }
}

bool ValidateSalt(const char* salt) {

    if (!salt || *salt != '$') {
        return false;
    }

    // discard $
    salt++;

    if (*salt > BCRYPT_VERSION) {
        return false;
    }

    if (salt[1] != '$') {
        switch (salt[1]) {
        case 'a':
            salt++;
            break;
        default:
            return false;
        }
    }

    // discard version + $
    salt += 2;

    if (salt[2] != '$') {
        return false;
    }

    int n = atoi(salt);
    if (n > 31 || n < 0) {
        return false;
    }

    if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) {
        return false;
    }

    salt += 3;
    if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
        return false;
    }

    return true;
}

/* SALT GENERATION */
void GenSaltAsync(uv_work_t* req) {

    salt_baton* baton = static_cast<salt_baton*>(req->data);

    char* salt = new char[_SALT_LEN];
    uint8_t* seed = new uint8_t[baton->rand_len];
    switch(GetSeed(seed, baton->rand_len)) {
        case -1:
            baton->error = "Rand operation not supported.";
        case 0:
            baton->error = "Rand operation did not generate a cryptographically sound seed.";
    }

    bcrypt_gensalt(baton->rounds, seed, salt);
    baton->salt = std::string(salt);

    delete[] salt;
    delete[] seed;
}

void GenSaltAsyncAfter(uv_work_t* req) {
    HandleScope scope;

    salt_baton* baton = static_cast<salt_baton*>(req->data);
    delete req;

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    }
    else {
        argv[0] = Undefined();
        argv[1] = Encode(baton->salt.c_str(), baton->salt.size(), BINARY);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    delete baton;
}

Handle<Value> GenerateSalt(const Arguments &args) {
    HandleScope scope;

    const ssize_t rounds = args[0]->Int32Value();
    const int rand_len = args[1]->Int32Value();
    Local<Function> callback = Local<Function>::Cast(args[2]);

    salt_baton* baton = new salt_baton();

    baton->callback = Persistent<Function>::New(callback);
    baton->rand_len = rand_len;
    baton->rounds = rounds;

    uv_work_t* req = new uv_work_t;
    req->data = baton;
    uv_queue_work(uv_default_loop(), req, GenSaltAsync, GenSaltAsyncAfter);

    return Undefined();
}

Handle<Value> GenerateSaltSync(const Arguments& args) {
    HandleScope scope;

    const ssize_t rounds = args[0]->Int32Value();
    const int size = args[1]->Int32Value();

    uint8_t* seed = new uint8_t[size];
    switch(GetSeed(seed, size)) {
        case -1:
            return ThrowException(Exception::Error(String::New("Rand operation not supported.")));
        case 0:
            return ThrowException(Exception::Error(String::New("Rand operation did not generate a cryptographically sound seed.")));
    }

    char salt[_SALT_LEN];
    bcrypt_gensalt(rounds, seed, salt);
    delete[] seed;

    return scope.Close(Encode(salt, strlen(salt), BINARY));
}

/* ENCRYPT DATA - USED TO BE HASHPW */
void EncryptAsync(uv_work_t* req) {
    encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);

    if (!(ValidateSalt(baton->salt.c_str()))) {
        baton->error = "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue";
    }

    char* bcrypted = new char[_PASSWORD_LEN];
    bcrypt(baton->input.c_str(), baton->salt.c_str(), bcrypted);
    baton->output = std::string(bcrypted);

    delete[] bcrypted;
}

void EncryptAsyncAfter(uv_work_t* req) {
    HandleScope scope;

    encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);
    delete req;

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    }
    else {
        argv[0] = Undefined();
        argv[1] = Encode(baton->output.c_str(), baton->output.size(), BINARY);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    delete baton;
}

Handle<Value> Encrypt(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value data(args[0]->ToString());
    String::Utf8Value salt(args[1]->ToString());
    Local<Function> callback = Local<Function>::Cast(args[2]);

    encrypt_baton* baton = new encrypt_baton();
    baton->callback = Persistent<Function>::New(callback);
    baton->input = std::string(*data);
    baton->salt = std::string(*salt);

    uv_work_t* req = new uv_work_t;
    req->data = baton;
    uv_queue_work(uv_default_loop(), req, EncryptAsync, EncryptAsyncAfter);

    return Undefined();
}

Handle<Value> EncryptSync(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value data(args[0]->ToString());
    String::Utf8Value salt(args[1]->ToString());

    if (!(ValidateSalt(*salt))) {
        return ThrowException(Exception::Error(String::New("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue")));
    }

    char bcrypted[_PASSWORD_LEN];
    bcrypt(*data, *salt, bcrypted);
    return scope.Close(Encode(bcrypted, strlen(bcrypted), BINARY));
}

/* COMPARATOR */
bool CompareStrings(const char* s1, const char* s2) {

    bool eq = true;
    int s1_len = strlen(s1);
    int s2_len = strlen(s2);

    if (s1_len != s2_len) {
        eq = false;
    }

    const int max_len = (s2_len < s1_len) ? s1_len : s2_len;

    // to prevent timing attacks, should check entire string
    // don't exit after found to be false
    for (int i = 0; i < max_len; ++i) {
      if (s1_len >= i && s2_len >= i && s1[i] != s2[i]) {
        eq = false;
      }
    }

    return eq;
}

void CompareAsync(uv_work_t* req) {
    compare_baton* baton = static_cast<compare_baton*>(req->data);

    char bcrypted[_PASSWORD_LEN];
    bcrypt(baton->input.c_str(), baton->encrypted.c_str(), bcrypted);
    baton->result = CompareStrings(bcrypted, baton->encrypted.c_str());
}

void CompareAsyncAfter(uv_work_t* req) {
    HandleScope scope;

    compare_baton* baton = static_cast<compare_baton*>(req->data);
    delete req;

    Handle<Value> argv[2];

    if (!baton->error.empty()) {
        argv[0] = Exception::Error(String::New(baton->error.c_str()));
        argv[1] = Undefined();
    } else {
        argv[0] = Undefined();
        argv[1] = Boolean::New(baton->result);
    }

    TryCatch try_catch; // don't quite see the necessity of this

    baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    // done with the baton
    // free the memory and callback
    delete baton;
}

Handle<Value> Compare(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value input(args[0]->ToString());
    String::Utf8Value encrypted(args[1]->ToString());
    Local<Function> callback = Local<Function>::Cast(args[2]);

    compare_baton* baton = new compare_baton();
    baton->callback = Persistent<Function>::New(callback);
    baton->input = std::string(*input);
    baton->encrypted = std::string(*encrypted);

    uv_work_t* req = new uv_work_t;
    req->data = baton;
    uv_queue_work(uv_default_loop(), req, CompareAsync, CompareAsyncAfter);

    return Undefined();
}

Handle<Value> CompareSync(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value pw(args[0]->ToString());
    String::Utf8Value hash(args[1]->ToString());

    char bcrypted[_PASSWORD_LEN];
    bcrypt(*pw, *hash, bcrypted);
    return Boolean::New(CompareStrings(bcrypted, *hash));
}

Handle<Value> GetRounds(const Arguments& args) {
    HandleScope scope;

    String::Utf8Value hash(args[0]->ToString());
    u_int32_t rounds;
    if (!(rounds = bcrypt_get_rounds(*hash))) {
        return ThrowException(Exception::Error(String::New("invalid hash provided")));
    }

    return Integer::New(rounds);
}

} // anonymous namespace

// bind the bcrypt module
extern "C" void init(Handle<Object> target) {
    HandleScope scope;

    crypto_lock_init();
    CRYPTO_set_locking_callback(crypto_lock_cb);
    CRYPTO_set_id_callback(crypto_id_cb);

    NODE_SET_METHOD(target, "gen_salt_sync", GenerateSaltSync);
    NODE_SET_METHOD(target, "encrypt_sync", EncryptSync);
    NODE_SET_METHOD(target, "compare_sync", CompareSync);
    NODE_SET_METHOD(target, "get_rounds", GetRounds);
    NODE_SET_METHOD(target, "gen_salt", GenerateSalt);
    NODE_SET_METHOD(target, "encrypt", Encrypt);
    NODE_SET_METHOD(target, "compare", Compare);
};

<|endoftext|>


<commit_before>/** accuracy.cc
    Jeremy Barnes, 16 December 2014
    Copyright (c) 2014 Datacratic Inc.  All rights reserved.

    This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

    Implementation of an ACCURACY algorithm for embedding of a dataset.
*/

#include "accuracy.h"
#include "matrix.h"
#include "mldb/server/mldb_server.h"
#include "mldb/core/dataset.h"
#include "mldb/server/bound_queries.h"
#include "mldb/sql/sql_expression.h"
#include "mldb/jml/stats/distribution.h"
#include <boost/multi_array.hpp>
#include "mldb/jml/utils/guard.h"
#include "mldb/jml/utils/worker_task.h"
#include "mldb/jml/utils/pair_utils.h"
#include "mldb/arch/timers.h"
#include "mldb/arch/simd_vector.h"
#include "mldb/jml/utils/vector_utils.h"
#include "mldb/types/basic_value_descriptions.h"
#include "mldb/ml/value_descriptions.h"
#include "mldb/ml/separation_stats.h"
#include "mldb/server/analytics.h"
#include "mldb/types/any_impl.h"
#include "mldb/server/per_thread_accumulator.h"
#include "mldb/types/optional_description.h"
#include "mldb/http/http_exception.h"
#include "mldb/plugins/sql_config_validator.h"
#include "mldb/plugins/sql_expression_extractors.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

DEFINE_STRUCTURE_DESCRIPTION(AccuracyConfig);

AccuracyConfigDescription::
AccuracyConfigDescription()
{
    Optional<PolyConfigT<Dataset> > optionalOutputDataset;
    optionalOutputDataset.emplace(PolyConfigT<Dataset>().
                                  withType(AccuracyConfig::defaultOutputDatasetType));

    addField("testingData", &AccuracyConfig::testingData,
             "Specification of the data for input to the accuracy procedure. "
             "The select expression must contain these sub-expressions: one scalar expression "
             "to identify the label and one scalar expression to identify the score. "
             "The type of the label expression must match "
             "that of the classifier mode from which the model was trained. "
             "Labels with a null value will have their row skipped. "
             "The expression to generate the score represents the output "
             "of whatever is having its accuracy tested.  This needs to be "
             "a number, and normally should be a floating point number that "
             "represents the degree of confidence in the prediction, not "
             "just the class. This is typically, the training function returned "
             "by a classifier.train procedure. "
             "The select expression can also contain an optional weight sub-expression. "
             "This expression generates the relative weight for each example.  In some "
             "circumstances it is necessary to calculate accuracy statistics "
             "with uneven weighting, for example to counteract the effect of "
             "non-uniform sampling in dataset.  By default, each class will "
             "get the same weight.  This value is relative to the other "
             "examples, in other words having all examples weighted 1 or all "
             "examples weighted 10 will have the same effect.  That being "
             "said, it is a good idea to keep the weights centered around 1 "
             "to avoid numeric errors in the calculations."
             "The select statement does not support groupby and having clauses.");
    addField("outputDataset", &AccuracyConfig::outputDataset,
             "Output dataset for scored examples. The score for the test "
             "example will be written to this dataset. Examples get grouped when "
              "they have the same score. Specifying a "
             "dataset is optional.", optionalOutputDataset);
    addParent<ProcedureConfig>();

        // the difference between included population of the current versus
            // last stats.stats represents the number of exemples included in the stats.
            // examples get grouped when they have the same score

    onPostValidate = validate<AccuracyConfig,
                              InputQuery,
                              NoGroupByHaving,
                              PlainColumnSelect,
                              ScoreLabelSelect>(&AccuracyConfig::testingData, "accuracy");

}

/*****************************************************************************/
/* ACCURACY PROCEDURE                                                         */
/*****************************************************************************/

AccuracyProcedure::
AccuracyProcedure(MldbServer * owner,
                 PolyConfig config,
                 const std::function<bool (const Json::Value &)> & onProgress)
    : Procedure(owner)
{
    this->accuracyConfig = config.params.convert<AccuracyConfig>();
    if (!accuracyConfig.testingData.stm)
        throw HttpReturnException(400, "Classifier testing procedure requires 'testingData' to be set",
                                  "config", this->accuracyConfig);
}

Any
AccuracyProcedure::
getStatus() const
{
    return Any();
}

RunOutput
AccuracyProcedure::
run(const ProcedureRunConfig & run,
    const std::function<bool (const Json::Value &)> & onProgress) const
{
    auto runAccuracyConf = applyRunConfOverProcConf(accuracyConfig, run);

    // 1.  Get the input dataset
    SqlExpressionMldbContext context(server);

    auto dataset = runAccuracyConf.testingData.stm->from->bind(context).dataset;

    // 3.  Create our aggregator function, which records the output of
    //     this row.

    PerThreadAccumulator<ScoredStats> accum;

    auto aggregator = [&] (const MatrixNamedRow & row,
                           const std::vector<ExpressionValue> & scoreLabelWeight)
        {
            //cerr << "got vals " << labelWeight << " " << score << endl;

            double score = scoreLabelWeight[0].toDouble();
            bool label = scoreLabelWeight[1].asBool();
            double weight = scoreLabelWeight[2].toDouble();

            accum.get().update(label, score, weight, row.rowName);

            return true;
        };

    // 5.  Run it
    auto score = extractNamedSubSelect("score", runAccuracyConf.testingData.stm->select)->expression;
    auto label = extractNamedSubSelect("label", runAccuracyConf.testingData.stm->select)->expression;
    auto weightSubSelect = extractNamedSubSelect("weight", runAccuracyConf.testingData.stm->select);
    shared_ptr<SqlExpression> weight = weightSubSelect ? weightSubSelect->expression : SqlExpression::ONE;

    std::vector<std::shared_ptr<SqlExpression> > calc = {
        score,
        label,
        weight
    };

    BoundSelectQuery({} /* select */, *dataset, "" /* table alias */,
                     runAccuracyConf.testingData.stm->when, runAccuracyConf.testingData.stm->where,
                     runAccuracyConf.testingData.stm->orderBy,
                     calc,
                     false /* implicit order by row hash */)
        .execute(aggregator, runAccuracyConf.testingData.stm->offset, runAccuracyConf.testingData.stm->limit,
                 nullptr /* progress */);

    // Now merge out stats together

    ScoredStats stats;

    accum.forEach([&] (ScoredStats * thrStats)
                  {
                      thrStats->sort();
                      stats.add(*thrStats);
                  });

    //stats.sort();
    stats.calculate();

    if(runAccuracyConf.outputDataset) {
        std::shared_ptr<Dataset> output;

        PolyConfigT<Dataset> outputDataset = *runAccuracyConf.outputDataset;
        if (outputDataset.type.empty())
            outputDataset.type = AccuracyConfig::defaultOutputDatasetType;

        output = createDataset(server, outputDataset, nullptr, true /*overwrite*/);

        Date recordDate = Date::now();

        int prevIncludedPop = 0;

        std::vector<std::pair<RowName, std::vector<std::tuple<ColumnName, CellValue, Date> > > > rows;

        for (unsigned i = 1, j = 0;  i < stats.stats.size();  ++i) {
            auto & bstats = stats.stats[i];
            auto & entry = stats.entries[j];

            // the difference between included population of the current versus
            // last stats.stats represents the number of exemples included in the stats.
            // examples get grouped when they have the same score
            j += (bstats.includedPopulation() - prevIncludedPop);
            prevIncludedPop = bstats.includedPopulation();

            ExcAssertEqual(bstats.threshold, entry.score);

            std::vector<std::tuple<RowName, CellValue, Date> > row;

            row.emplace_back(ColumnName("index"), i, recordDate);
            row.emplace_back(ColumnName("label"), entry.label, recordDate);
            row.emplace_back(ColumnName("score"), entry.score, recordDate);
            row.emplace_back(ColumnName("weight"), entry.weight, recordDate);
            row.emplace_back(ColumnName("truePositives"), bstats.truePositives(), recordDate);
            row.emplace_back(ColumnName("falsePositives"), bstats.falsePositives(), recordDate);
            row.emplace_back(ColumnName("trueNegatives"), bstats.trueNegatives(), recordDate);
            row.emplace_back(ColumnName("falseNegatives"), bstats.falseNegatives(), recordDate);
            row.emplace_back(ColumnName("precision"), bstats.precision(), recordDate);
            row.emplace_back(ColumnName("recall"), bstats.recall(), recordDate);
            row.emplace_back(ColumnName("truePositiveRate"), bstats.truePositiveRate(), recordDate);
            row.emplace_back(ColumnName("falsePositiveRate"), bstats.falsePositiveRate(), recordDate);

            rows.emplace_back(boost::any_cast<RowName>(entry.key), std::move(row));
            if (rows.size() > 1000) {
                output->recordRows(rows);
                rows.clear();
            }
        }

        output->recordRows(rows);

        output->commit();
    }

    cerr << "stats are " << endl;

    cerr << stats.toJson();

    cerr << stats.atPercentile(0.50).toJson();

    cerr << stats.atPercentile(0.20).toJson();

    cerr << stats.atPercentile(0.10).toJson();

    cerr << stats.atPercentile(0.05).toJson();

    cerr << stats.atPercentile(0.01).toJson();

    return Any(stats.toJson());
}

namespace {

RegisterProcedureType<AccuracyProcedure, AccuracyConfig>
regAccuracy(builtinPackage(),
            "classifier.test",
            "Calculate the accuracy of a classifier on held-out data",
            "procedures/Accuracy.md.html");

} // file scope

} // namespace MLDB
} // namespace Datacratic
<commit_msg>[MLDB-1206] Trivial<commit_after>/** accuracy.cc
    Jeremy Barnes, 16 December 2014
    Copyright (c) 2014 Datacratic Inc.  All rights reserved.

    This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

    Implementation of an ACCURACY algorithm for embedding of a dataset.
*/

#include "accuracy.h"
#include "matrix.h"
#include "mldb/server/mldb_server.h"
#include "mldb/core/dataset.h"
#include "mldb/server/bound_queries.h"
#include "mldb/sql/sql_expression.h"
#include "mldb/jml/stats/distribution.h"
#include <boost/multi_array.hpp>
#include "mldb/jml/utils/guard.h"
#include "mldb/jml/utils/worker_task.h"
#include "mldb/jml/utils/pair_utils.h"
#include "mldb/arch/timers.h"
#include "mldb/arch/simd_vector.h"
#include "mldb/jml/utils/vector_utils.h"
#include "mldb/types/basic_value_descriptions.h"
#include "mldb/ml/value_descriptions.h"
#include "mldb/ml/separation_stats.h"
#include "mldb/server/analytics.h"
#include "mldb/types/any_impl.h"
#include "mldb/server/per_thread_accumulator.h"
#include "mldb/types/optional_description.h"
#include "mldb/http/http_exception.h"
#include "mldb/plugins/sql_config_validator.h"
#include "mldb/plugins/sql_expression_extractors.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

DEFINE_STRUCTURE_DESCRIPTION(AccuracyConfig);

AccuracyConfigDescription::
AccuracyConfigDescription()
{
    Optional<PolyConfigT<Dataset> > optionalOutputDataset;
    optionalOutputDataset.emplace(PolyConfigT<Dataset>().
                                  withType(AccuracyConfig::defaultOutputDatasetType));

    addField("testingData", &AccuracyConfig::testingData,
             "Specification of the data for input to the accuracy procedure. "
             "The select expression must contain these sub-expressions: one scalar expression "
             "to identify the label and one scalar expression to identify the score. "
             "The type of the label expression must match "
             "that of the classifier mode from which the model was trained. "
             "Labels with a null value will have their row skipped. "
             "The expression to generate the score represents the output "
             "of whatever is having its accuracy tested.  This needs to be "
             "a number, and normally should be a floating point number that "
             "represents the degree of confidence in the prediction, not "
             "just the class. This is typically, the training function returned "
             "by a classifier.train procedure. "
             "The select expression can also contain an optional weight sub-expression. "
             "This expression generates the relative weight for each example.  In some "
             "circumstances it is necessary to calculate accuracy statistics "
             "with uneven weighting, for example to counteract the effect of "
             "non-uniform sampling in dataset.  By default, each class will "
             "get the same weight.  This value is relative to the other "
             "examples, in other words having all examples weighted 1 or all "
             "examples weighted 10 will have the same effect.  That being "
             "said, it is a good idea to keep the weights centered around 1 "
             "to avoid numeric errors in the calculations."
             "The select statement does not support groupby and having clauses.");
    addField("outputDataset", &AccuracyConfig::outputDataset,
             "Output dataset for scored examples. The score for the test "
             "example will be written to this dataset. Examples get grouped when "
              "they have the same score. Specifying a "
             "dataset is optional.", optionalOutputDataset);
    addParent<ProcedureConfig>();

    onPostValidate = validate<AccuracyConfig,
                              InputQuery,
                              NoGroupByHaving,
                              PlainColumnSelect,
                              ScoreLabelSelect>(&AccuracyConfig::testingData, "accuracy");

}

/*****************************************************************************/
/* ACCURACY PROCEDURE                                                         */
/*****************************************************************************/

AccuracyProcedure::
AccuracyProcedure(MldbServer * owner,
                 PolyConfig config,
                 const std::function<bool (const Json::Value &)> & onProgress)
    : Procedure(owner)
{
    this->accuracyConfig = config.params.convert<AccuracyConfig>();
    if (!accuracyConfig.testingData.stm)
        throw HttpReturnException(400, "Classifier testing procedure requires 'testingData' to be set",
                                  "config", this->accuracyConfig);
}

Any
AccuracyProcedure::
getStatus() const
{
    return Any();
}

RunOutput
AccuracyProcedure::
run(const ProcedureRunConfig & run,
    const std::function<bool (const Json::Value &)> & onProgress) const
{
    auto runAccuracyConf = applyRunConfOverProcConf(accuracyConfig, run);

    // 1.  Get the input dataset
    SqlExpressionMldbContext context(server);

    auto dataset = runAccuracyConf.testingData.stm->from->bind(context).dataset;

    // 3.  Create our aggregator function, which records the output of
    //     this row.

    PerThreadAccumulator<ScoredStats> accum;

    auto aggregator = [&] (const MatrixNamedRow & row,
                           const std::vector<ExpressionValue> & scoreLabelWeight)
        {
            //cerr << "got vals " << labelWeight << " " << score << endl;

            double score = scoreLabelWeight[0].toDouble();
            bool label = scoreLabelWeight[1].asBool();
            double weight = scoreLabelWeight[2].toDouble();

            accum.get().update(label, score, weight, row.rowName);

            return true;
        };

    // 5.  Run it
    auto score = extractNamedSubSelect("score", runAccuracyConf.testingData.stm->select)->expression;
    auto label = extractNamedSubSelect("label", runAccuracyConf.testingData.stm->select)->expression;
    auto weightSubSelect = extractNamedSubSelect("weight", runAccuracyConf.testingData.stm->select);
    shared_ptr<SqlExpression> weight = weightSubSelect ? weightSubSelect->expression : SqlExpression::ONE;

    std::vector<std::shared_ptr<SqlExpression> > calc = {
        score,
        label,
        weight
    };

    BoundSelectQuery({} /* select */, *dataset, "" /* table alias */,
                     runAccuracyConf.testingData.stm->when, runAccuracyConf.testingData.stm->where,
                     runAccuracyConf.testingData.stm->orderBy,
                     calc,
                     false /* implicit order by row hash */)
        .execute(aggregator, runAccuracyConf.testingData.stm->offset, runAccuracyConf.testingData.stm->limit,
                 nullptr /* progress */);

    // Now merge out stats together

    ScoredStats stats;

    accum.forEach([&] (ScoredStats * thrStats)
                  {
                      thrStats->sort();
                      stats.add(*thrStats);
                  });

    //stats.sort();
    stats.calculate();

    if(runAccuracyConf.outputDataset) {
        std::shared_ptr<Dataset> output;

        PolyConfigT<Dataset> outputDataset = *runAccuracyConf.outputDataset;
        if (outputDataset.type.empty())
            outputDataset.type = AccuracyConfig::defaultOutputDatasetType;

        output = createDataset(server, outputDataset, nullptr, true /*overwrite*/);

        Date recordDate = Date::now();

        int prevIncludedPop = 0;

        std::vector<std::pair<RowName, std::vector<std::tuple<ColumnName, CellValue, Date> > > > rows;

        for (unsigned i = 1, j = 0;  i < stats.stats.size();  ++i) {
            auto & bstats = stats.stats[i];
            auto & entry = stats.entries[j];

            // the difference between included population of the current versus
            // last stats.stats represents the number of exemples included in the stats.
            // examples get grouped when they have the same score
            j += (bstats.includedPopulation() - prevIncludedPop);
            prevIncludedPop = bstats.includedPopulation();

            ExcAssertEqual(bstats.threshold, entry.score);

            std::vector<std::tuple<RowName, CellValue, Date> > row;

            row.emplace_back(ColumnName("index"), i, recordDate);
            row.emplace_back(ColumnName("label"), entry.label, recordDate);
            row.emplace_back(ColumnName("score"), entry.score, recordDate);
            row.emplace_back(ColumnName("weight"), entry.weight, recordDate);
            row.emplace_back(ColumnName("truePositives"), bstats.truePositives(), recordDate);
            row.emplace_back(ColumnName("falsePositives"), bstats.falsePositives(), recordDate);
            row.emplace_back(ColumnName("trueNegatives"), bstats.trueNegatives(), recordDate);
            row.emplace_back(ColumnName("falseNegatives"), bstats.falseNegatives(), recordDate);
            row.emplace_back(ColumnName("precision"), bstats.precision(), recordDate);
            row.emplace_back(ColumnName("recall"), bstats.recall(), recordDate);
            row.emplace_back(ColumnName("truePositiveRate"), bstats.truePositiveRate(), recordDate);
            row.emplace_back(ColumnName("falsePositiveRate"), bstats.falsePositiveRate(), recordDate);

            rows.emplace_back(boost::any_cast<RowName>(entry.key), std::move(row));
            if (rows.size() > 1000) {
                output->recordRows(rows);
                rows.clear();
            }
        }

        output->recordRows(rows);

        output->commit();
    }

    cerr << "stats are " << endl;

    cerr << stats.toJson();

    cerr << stats.atPercentile(0.50).toJson();

    cerr << stats.atPercentile(0.20).toJson();

    cerr << stats.atPercentile(0.10).toJson();

    cerr << stats.atPercentile(0.05).toJson();

    cerr << stats.atPercentile(0.01).toJson();

    return Any(stats.toJson());
}

namespace {

RegisterProcedureType<AccuracyProcedure, AccuracyConfig>
regAccuracy(builtinPackage(),
            "classifier.test",
            "Calculate the accuracy of a classifier on held-out data",
            "procedures/Accuracy.md.html");

} // file scope

} // namespace MLDB
} // namespace Datacratic
<|endoftext|>


<commit_before>//
// Created by dar on 1/25/16.
//

#include <render/block/ChunkRender.h>
#include <render/entity/PlayerRender.h>
#include <render/entity/SimpleShapeRender.h>
#include "../RenderManager.h"
#include "GameRender.h"
#include "../../window/Game.h"
#include "../../core/Core.h"
#include <core/map/entity/EntityBullet.h>
#include <core/map/entity/EntityFather.h>
#include <core/map/entity/EntityMachinery.h>
#include <core/map/entity/EntityDoor.h>
#include <core/map/entity/EntityFurniture.h>
#include <core/map/entity/EntityTable.h>
#include <core/map/entity/EntityGlassDebris.h>
#include <core/map/entity/EntityCouch.h>
#include <core/map/entity/EntityWall.h>
#include <core/map/block/SimpleBlock.h>
#include <logging.h>
#include <render/entity/EntityMachineryRender.h>
#include <render/entity/EntityStaticRender.h>
#include <render/entity/EntityWideRender.h>
#include <render/entity/EntityWallRender.h>
#include <ApplicationContext.h>

GameRender::GameRender() : WindowRender() { }

void GameRender::init(RenderContext *const renderContext) {
    this->initRenders();
    fbo.init(3, renderContext->getWindowWidth(), renderContext->getWindowHeight(), new float[4]{0.9, 0.9, 0.9, 1.0}, "fboshader");
    fbo.bind();
    glClearColor(0.9, 0.9, 0.9, 0.0);
    fbo.unbind();
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    this->resize(renderContext);
}

void GameRender::resize(RenderContext *const renderContext) {
    viewMatrix = glm::lookAt(glm::vec3(0, 0, 0.0f), glm::vec3(0, 0, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    this->projectionMatrix = glm::ortho(0.0f, float(renderContext->getWindowWidth()), 0.0f, float(renderContext->getWindowHeight()));
    this->fbo.resize(renderContext->getWindowWidth(), renderContext->getWindowHeight());
    this->markedToRedraw = true;
}

void GameRender::render(Window *window, RenderContext *const renderContext) {
    Game *game = ((Game *) window);
    Core *core = game->getCore();

    unsigned int windowWidth = renderContext->getWindowWidth();
    unsigned int windowHeight = renderContext->getWindowHeight();
    double scale = core->getBlockSize() * core->getGeneralScale();

    double camX = core->getCamX() * (core->getBlockSize() * core->getGeneralScale());
    double camY = core->getCamY() * (core->getBlockSize() * core->getGeneralScale());

    glClear(GL_COLOR_BUFFER_BIT);
    fbo.bind();
    glClear(GL_COLOR_BUFFER_BIT);
    scale *= Chunk::size;
    for (Chunk *const chunk : core->getMap()->getChunks()) {
        if (this->markedToRedraw) {
            chunk->setRedrawn(false);
        }
        getChunkRender()->render(chunk, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            -(signed) windowWidth / 2.0f - camX,
            (signed) windowHeight / 2.0f - camY, 0.0f
        )), core->getBlockSize() * core->getGeneralScale());
        chunk->setRedrawn();
    }
    scale /= Chunk::size;
    int entitiesNum = 0;
    for (int i = core->getMap()->getEntities().size() - 1; i >= 0; i--) {
        Entity *entity = core->getMap()->getEntities().at(i);
        double maxSize = entity->getWidth() > entity->getHeight() ? entity->getWidth() : entity->getHeight();
        if (entity->getX() * scale > -(signed) windowWidth / 2.0f - camX &&
            (entity->getX() - 1 - maxSize) * scale < -(signed) windowWidth / 2.0f - camX + (signed) windowWidth &&
            entity->getY() * scale > -(signed) windowHeight / 2.0f - camY &&
            (entity->getY() - 1 - maxSize) * scale < -(signed) windowHeight / 2.0f - camY + (signed) windowHeight) {
            if (this->markedToRedraw) {
                entity->setRedrawn(false);
            }
            getEntityRender(entity)->render(entity, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
                -(signed) windowWidth / 2.0f - camX,
                (signed) windowHeight / 2.0f - camY,
                0.0f)), core->getBlockSize() * core->getGeneralScale());
            entity->setRedrawn();
            if (IEntityLighted *elighted = dynamic_cast<IEntityLighted *>(entity)) {
                fbo.getShaderProgram()->useProgram();
                if (entitiesNum < fbo.MAX_LIGHT_SRCS) {
                    char *uniform_name_formatted = new char[16 + (int) log((double) fbo.MAX_LIGHT_SRCS)];
                    sprintf(uniform_name_formatted, "lightPoints[%d]", entitiesNum);
                    fbo.getShaderProgram()->setUniform(uniform_name_formatted, glm::vec2(
                        camX + (entity->getX() - 1 + entity->getWidth() / 2) * scale + (double) windowWidth / 2,
                        -camY - (entity->getY() - 1 + entity->getHeight() / 2) * scale + (double) windowHeight / 2));
                    entitiesNum++;
                }
            }
        }
    }

#ifndef EDITOR
    //TODO
    static int grayout[][4] = {{20, 3, 4, 6},
                               {21, 9, 1, 1},
                               {25, 3, 5, 3},
                               {30, 3, 1, 1},
                               {31, 3, 4, 3},
                               {35, 4, 1, 1},
                               {34, 6, 1, 1},
                               {31, 7, 7, 11}};

    for (int i = 0; i < 8; i++) {
        this->voidRender->render(grayout[i][0], grayout[i][1], grayout[i][2], grayout[i][3], projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            -(signed) windowWidth / 2.0f - camX,
            (signed) windowHeight / 2.0f - camY,
            0.0f)), core->getBlockSize() * core->getGeneralScale());
    }
#endif //EDITOR

    fbo.unbind();
    fbo.getShaderProgram()->useProgram();
    fbo.getShaderProgram()->setUniform("colorfulness", (float) core->getPlayer()->getColorfulness());
    fbo.getShaderProgram()->setUniform("lightPointsNum", entitiesNum);
    fbo.getShaderProgram()->setUniform("scale", (float) (core->getBlockSize() * core->getGeneralScale()));
    fbo.render(0);

    for (GuiElement *guiElement : game->getGuiElements()) {
        renderContext->getGuiElementRender(guiElement)->render(guiElement, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            0,
            (int) ((signed) windowHeight),
            0.0f)), core->getGeneralScale());
    }

    this->markedToRedraw = false;
}

ChunkRender *GameRender::getChunkRender() const {
    return this->chunkRender;
}

EntityRender *GameRender::getEntityRender(const Entity *const entity) {
    EntityRender *r = entityRenders[typeid(*entity).name()];
    if (r == nullptr) {
        if (const EntityFurniture *const f = dynamic_cast<const EntityFurniture *const>(entity)) {
            return entityRenders[typeid(EntityFurniture).name()];
        } else {
            return nullptr;
        }
    }
    return r;
}

void GameRender::cleanRenders() {
    delete this->voidRender;
    delete this->chunkRender;

    for (std::pair<const char *, EntityRender *> renderPair : entityRenders) {
        delete renderPair.second;
    }
    entityRenders.clear();
}

void GameRender::initRenders() {
    this->voidRender = new VoidRender();
    this->chunkRender = new ChunkRender();

    entityRenders.insert(std::make_pair(typeid(EntityPlayer).name(), new PlayerRender()));
    EntityMachineryRender *machineryRender = new EntityMachineryRender();
    entityRenders.insert(std::make_pair(typeid(EntityTruck).name(), machineryRender));
    entityRenders.insert(std::make_pair(typeid(EntityBulldozer).name(), machineryRender));
    entityRenders.insert(std::make_pair(typeid(EntityHoover).name(), machineryRender));
    //entityRenders.insert(std::make_pair(typeid(EntityBullet).name(), new DefaultEntityRender("bullet", "shader")));
    EntityWideRender *wideRender = new EntityWideRender();
    entityRenders.insert(std::make_pair(typeid(EntityTable).name(), wideRender));
    entityRenders.insert(std::make_pair(typeid(EntityCoffeeTable).name(), wideRender));
    entityRenders.insert(std::make_pair(typeid(EntityCouch).name(), wideRender));
    EntityStaticRender *staticRender = new EntityStaticRender();
    entityRenders.insert(std::make_pair(typeid(EntityFurniture).name(), staticRender));
    entityRenders.insert(std::make_pair(typeid(EntityGlassDebris).name(), staticRender));
    entityRenders.insert(std::make_pair(typeid(SimpleShape).name(), new SimpleShapeRender()));
    entityRenders.insert(std::make_pair(typeid(EntityFather).name(), new DefaultEntityRender("parents", "shader")));
    entityRenders.insert(std::make_pair(typeid(EntityWall).name(), new EntityWallRender()));
}

GameRender::~GameRender() {
    cleanRenders();
}

<commit_msg>Commented out blackout void render<commit_after>//
// Created by dar on 1/25/16.
//

#include <render/block/ChunkRender.h>
#include <render/entity/PlayerRender.h>
#include <render/entity/SimpleShapeRender.h>
#include "../RenderManager.h"
#include "GameRender.h"
#include "../../window/Game.h"
#include "../../core/Core.h"
#include <core/map/entity/EntityBullet.h>
#include <core/map/entity/EntityFather.h>
#include <core/map/entity/EntityMachinery.h>
#include <core/map/entity/EntityDoor.h>
#include <core/map/entity/EntityFurniture.h>
#include <core/map/entity/EntityTable.h>
#include <core/map/entity/EntityGlassDebris.h>
#include <core/map/entity/EntityCouch.h>
#include <core/map/entity/EntityWall.h>
#include <core/map/block/SimpleBlock.h>
#include <logging.h>
#include <render/entity/EntityMachineryRender.h>
#include <render/entity/EntityStaticRender.h>
#include <render/entity/EntityWideRender.h>
#include <render/entity/EntityWallRender.h>
#include <ApplicationContext.h>

GameRender::GameRender() : WindowRender() { }

void GameRender::init(RenderContext *const renderContext) {
    this->initRenders();
    fbo.init(3, renderContext->getWindowWidth(), renderContext->getWindowHeight(), new float[4]{0.9, 0.9, 0.9, 1.0}, "fboshader");
    fbo.bind();
    glClearColor(0.9, 0.9, 0.9, 0.0);
    fbo.unbind();
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    this->resize(renderContext);
}

void GameRender::resize(RenderContext *const renderContext) {
    viewMatrix = glm::lookAt(glm::vec3(0, 0, 0.0f), glm::vec3(0, 0, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    this->projectionMatrix = glm::ortho(0.0f, float(renderContext->getWindowWidth()), 0.0f, float(renderContext->getWindowHeight()));
    this->fbo.resize(renderContext->getWindowWidth(), renderContext->getWindowHeight());
    this->markedToRedraw = true;
}

void GameRender::render(Window *window, RenderContext *const renderContext) {
    Game *game = ((Game *) window);
    Core *core = game->getCore();

    unsigned int windowWidth = renderContext->getWindowWidth();
    unsigned int windowHeight = renderContext->getWindowHeight();
    double scale = core->getBlockSize() * core->getGeneralScale();

    double camX = core->getCamX() * (core->getBlockSize() * core->getGeneralScale());
    double camY = core->getCamY() * (core->getBlockSize() * core->getGeneralScale());

    glClear(GL_COLOR_BUFFER_BIT);
    fbo.bind();
    glClear(GL_COLOR_BUFFER_BIT);
    scale *= Chunk::size;
    for (Chunk *const chunk : core->getMap()->getChunks()) {
        if (this->markedToRedraw) {
            chunk->setRedrawn(false);
        }
        getChunkRender()->render(chunk, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            -(signed) windowWidth / 2.0f - camX,
            (signed) windowHeight / 2.0f - camY, 0.0f
        )), core->getBlockSize() * core->getGeneralScale());
        chunk->setRedrawn();
    }
    scale /= Chunk::size;
    int entitiesNum = 0;
    for (int i = core->getMap()->getEntities().size() - 1; i >= 0; i--) {
        Entity *entity = core->getMap()->getEntities().at(i);
        double maxSize = entity->getWidth() > entity->getHeight() ? entity->getWidth() : entity->getHeight();
        if (entity->getX() * scale > -(signed) windowWidth / 2.0f - camX &&
            (entity->getX() - 1 - maxSize) * scale < -(signed) windowWidth / 2.0f - camX + (signed) windowWidth &&
            entity->getY() * scale > -(signed) windowHeight / 2.0f - camY &&
            (entity->getY() - 1 - maxSize) * scale < -(signed) windowHeight / 2.0f - camY + (signed) windowHeight) {
            if (this->markedToRedraw) {
                entity->setRedrawn(false);
            }
            getEntityRender(entity)->render(entity, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
                -(signed) windowWidth / 2.0f - camX,
                (signed) windowHeight / 2.0f - camY,
                0.0f)), core->getBlockSize() * core->getGeneralScale());
            entity->setRedrawn();
            if (IEntityLighted *elighted = dynamic_cast<IEntityLighted *>(entity)) {
                fbo.getShaderProgram()->useProgram();
                if (entitiesNum < fbo.MAX_LIGHT_SRCS) {
                    char *uniform_name_formatted = new char[16 + (int) log((double) fbo.MAX_LIGHT_SRCS)];
                    sprintf(uniform_name_formatted, "lightPoints[%d]", entitiesNum);
                    fbo.getShaderProgram()->setUniform(uniform_name_formatted, glm::vec2(
                        camX + (entity->getX() - 1 + entity->getWidth() / 2) * scale + (double) windowWidth / 2,
                        -camY - (entity->getY() - 1 + entity->getHeight() / 2) * scale + (double) windowHeight / 2));
                    entitiesNum++;
                }
            }
        }
    }

#ifndef EDITOR
    //TODO
    /*static int grayout[][4] = {{20, 3, 4, 6},
                               {21, 9, 1, 1},
                               {25, 3, 5, 3},
                               {30, 3, 1, 1},
                               {31, 3, 4, 3},
                               {35, 4, 1, 1},
                               {34, 6, 1, 1},
                               {31, 7, 7, 11}};

    for (int i = 0; i < 8; i++) {
        this->voidRender->render(grayout[i][0], grayout[i][1], grayout[i][2], grayout[i][3], projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            -(signed) windowWidth / 2.0f - camX,
            (signed) windowHeight / 2.0f - camY,
            0.0f)), core->getBlockSize() * core->getGeneralScale());
    }*/
#endif //EDITOR

    fbo.unbind();
    fbo.getShaderProgram()->useProgram();
    fbo.getShaderProgram()->setUniform("colorfulness", (float) core->getPlayer()->getColorfulness());
    fbo.getShaderProgram()->setUniform("lightPointsNum", entitiesNum);
    fbo.getShaderProgram()->setUniform("scale", (float) (core->getBlockSize() * core->getGeneralScale()));
    fbo.render(0);

    for (GuiElement *guiElement : game->getGuiElements()) {
        renderContext->getGuiElementRender(guiElement)->render(guiElement, projectionMatrix, glm::translate(viewMatrix, glm::vec3(
            0,
            (int) ((signed) windowHeight),
            0.0f)), core->getGeneralScale());
    }

    this->markedToRedraw = false;
}

ChunkRender *GameRender::getChunkRender() const {
    return this->chunkRender;
}

EntityRender *GameRender::getEntityRender(const Entity *const entity) {
    EntityRender *r = entityRenders[typeid(*entity).name()];
    if (r == nullptr) {
        if (const EntityFurniture *const f = dynamic_cast<const EntityFurniture *const>(entity)) {
            return entityRenders[typeid(EntityFurniture).name()];
        } else {
            return nullptr;
        }
    }
    return r;
}

void GameRender::cleanRenders() {
    delete this->voidRender;
    delete this->chunkRender;

    for (std::pair<const char *, EntityRender *> renderPair : entityRenders) {
        delete renderPair.second;
    }
    entityRenders.clear();
}

void GameRender::initRenders() {
    this->voidRender = new VoidRender();
    this->chunkRender = new ChunkRender();

    entityRenders.insert(std::make_pair(typeid(EntityPlayer).name(), new PlayerRender()));
    EntityMachineryRender *machineryRender = new EntityMachineryRender();
    entityRenders.insert(std::make_pair(typeid(EntityTruck).name(), machineryRender));
    entityRenders.insert(std::make_pair(typeid(EntityBulldozer).name(), machineryRender));
    entityRenders.insert(std::make_pair(typeid(EntityHoover).name(), machineryRender));
    //entityRenders.insert(std::make_pair(typeid(EntityBullet).name(), new DefaultEntityRender("bullet", "shader")));
    EntityWideRender *wideRender = new EntityWideRender();
    entityRenders.insert(std::make_pair(typeid(EntityTable).name(), wideRender));
    entityRenders.insert(std::make_pair(typeid(EntityCoffeeTable).name(), wideRender));
    entityRenders.insert(std::make_pair(typeid(EntityCouch).name(), wideRender));
    EntityStaticRender *staticRender = new EntityStaticRender();
    entityRenders.insert(std::make_pair(typeid(EntityFurniture).name(), staticRender));
    entityRenders.insert(std::make_pair(typeid(EntityGlassDebris).name(), staticRender));
    entityRenders.insert(std::make_pair(typeid(SimpleShape).name(), new SimpleShapeRender()));
    entityRenders.insert(std::make_pair(typeid(EntityFather).name(), new DefaultEntityRender("parents", "shader")));
    entityRenders.insert(std::make_pair(typeid(EntityWall).name(), new EntityWallRender()));
}

GameRender::~GameRender() {
    cleanRenders();
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.
 * License: https://github.com/taylor001/crown/blob/master/LICENSE
 */

#include "core/containers/array.h"
#include "core/containers/vector.h"
#include "core/filesystem/file.h"
#include "core/filesystem/filesystem.h"
#include "core/filesystem/path.h"
#include "core/guid.h"
#include "core/memory/temp_allocator.h"
#include "core/os.h"
#include "core/strings/dynamic_string.h"
#include "core/strings/string_stream.h"
#include "device/log.h"
#include "resource/compile_options.h"
#include "resource/data_compiler.h"

namespace crown
{
CompileOptions::CompileOptions(DataCompiler& dc, Filesystem& data_filesystem, DynamicString& source_path, Buffer& output, const char* platform)
	: _data_compiler(dc)
	, _data_filesystem(data_filesystem)
	, _source_path(source_path)
	, _output(output)
	, _platform(platform)
	, _dependencies(default_allocator())
{
}

void CompileOptions::error(const char* msg, va_list args)
{
	_data_compiler.error(msg, args);
}

void CompileOptions::error(const char* msg, ...)
{
	va_list args;
	va_start(args, msg);
	error(msg, args);
	va_end(args);
}

const char* CompileOptions::source_path()
{
	return _source_path.c_str();
}

bool CompileOptions::file_exists(const char* path)
{
	TempAllocator256 ta;
	DynamicString source_dir(ta);
	FilesystemDisk fs(ta);

	_data_compiler.source_dir(path, source_dir);
	fs.set_prefix(source_dir.c_str());

	return fs.exists(path);
}

bool CompileOptions::resource_exists(const char* type, const char* name)
{
	TempAllocator1024 ta;
	DynamicString path(ta);
	path += name;
	path += ".";
	path += type;
	return file_exists(path.c_str());
}

Buffer CompileOptions::read_temporary(const char* path)
{
	File* file = _data_filesystem.open(path, FileOpenMode::READ);
	u32 size = file->size();
	Buffer buf(default_allocator());
	array::resize(buf, size);
	file->read(array::begin(buf), size);
	_data_filesystem.close(*file);
	return buf;
}

void CompileOptions::write_temporary(const char* path, const char* data, u32 size)
{
	File* file = _data_filesystem.open(path, FileOpenMode::WRITE);
	file->write(data, size);
	_data_filesystem.close(*file);
}

void CompileOptions::write_temporary(const char* path, const Buffer& data)
{
	write_temporary(path, array::begin(data), array::size(data));
}

///
Buffer CompileOptions::read()
{
	return read(_source_path.c_str());
}

Buffer CompileOptions::read(const char* path)
{
	add_dependency(path);

	TempAllocator256 ta;
	DynamicString source_dir(ta);
	FilesystemDisk source_fs(ta);

	_data_compiler.source_dir(path, source_dir);
	source_fs.set_prefix(source_dir.c_str());

	File* file = source_fs.open(path, FileOpenMode::READ);
	u32 size = file->size();
	Buffer buf(default_allocator());
	array::resize(buf, size);
	file->read(array::begin(buf), size);
	source_fs.close(*file);
	return buf;
}

void CompileOptions::get_absolute_path(const char* path, DynamicString& abs)
{
	TempAllocator256 ta;
	DynamicString source_dir(ta);
	FilesystemDisk source_fs(ta);

	_data_compiler.source_dir(path, source_dir);
	source_fs.set_prefix(source_dir.c_str());

	source_fs.get_absolute_path(path, abs);
}

void CompileOptions::get_temporary_path(const char* suffix, DynamicString& abs)
{
	_data_filesystem.get_absolute_path(CROWN_TEMP_DIRECTORY, abs);

	TempAllocator64 ta;
	DynamicString prefix(ta);
	guid::to_string(guid::new_guid(), prefix);

	abs += '/';
	abs += prefix;
	abs += '.';
	abs += suffix;
}

void CompileOptions::delete_file(const char* path)
{
	_data_filesystem.delete_file(path);
}

void CompileOptions::write(const void* data, u32 size)
{
	array::push(_output, (const char*)data, size);
}

void CompileOptions::write(const Buffer& data)
{
	array::push(_output, array::begin(data), array::size(data));
}

const char* CompileOptions::platform() const
{
	return _platform;
}

const Vector<DynamicString>& CompileOptions::dependencies() const
{
	return _dependencies;
}

void CompileOptions::add_dependency(const char* path)
{
	TempAllocator256 ta;
	DynamicString dep(ta);
	dep += path;
	vector::push_back(_dependencies, dep);
}

int CompileOptions::run_external_compiler(const char* const* argv, StringStream& output)
{
	return os::execute_process(argv, output);
}

} // namespace crown
<commit_msg>Cleanup<commit_after>/*
 * Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.
 * License: https://github.com/taylor001/crown/blob/master/LICENSE
 */

#include "core/containers/array.h"
#include "core/containers/vector.h"
#include "core/filesystem/file.h"
#include "core/filesystem/filesystem.h"
#include "core/filesystem/path.h"
#include "core/guid.h"
#include "core/memory/temp_allocator.h"
#include "core/os.h"
#include "core/strings/dynamic_string.h"
#include "core/strings/string_stream.h"
#include "device/log.h"
#include "resource/compile_options.h"
#include "resource/data_compiler.h"

namespace crown
{
CompileOptions::CompileOptions(DataCompiler& dc, Filesystem& data_filesystem, DynamicString& source_path, Buffer& output, const char* platform)
	: _data_compiler(dc)
	, _data_filesystem(data_filesystem)
	, _source_path(source_path)
	, _output(output)
	, _platform(platform)
	, _dependencies(default_allocator())
{
}

void CompileOptions::error(const char* msg, va_list args)
{
	_data_compiler.error(msg, args);
}

void CompileOptions::error(const char* msg, ...)
{
	va_list args;
	va_start(args, msg);
	error(msg, args);
	va_end(args);
}

const char* CompileOptions::source_path()
{
	return _source_path.c_str();
}

bool CompileOptions::file_exists(const char* path)
{
	TempAllocator256 ta;
	DynamicString source_dir(ta);
	FilesystemDisk fs(ta);

	_data_compiler.source_dir(path, source_dir);
	fs.set_prefix(source_dir.c_str());

	return fs.exists(path);
}

bool CompileOptions::resource_exists(const char* type, const char* name)
{
	TempAllocator1024 ta;
	DynamicString path(ta);
	path += name;
	path += ".";
	path += type;
	return file_exists(path.c_str());
}

Buffer CompileOptions::read_temporary(const char* path)
{
	File* file = _data_filesystem.open(path, FileOpenMode::READ);
	u32 size = file->size();
	Buffer buf(default_allocator());
	array::resize(buf, size);
	file->read(array::begin(buf), size);
	_data_filesystem.close(*file);
	return buf;
}

void CompileOptions::write_temporary(const char* path, const char* data, u32 size)
{
	File* file = _data_filesystem.open(path, FileOpenMode::WRITE);
	file->write(data, size);
	_data_filesystem.close(*file);
}

void CompileOptions::write_temporary(const char* path, const Buffer& data)
{
	write_temporary(path, array::begin(data), array::size(data));
}

///
Buffer CompileOptions::read()
{
	return read(_source_path.c_str());
}

Buffer CompileOptions::read(const char* path)
{
	add_dependency(path);

	TempAllocator256 ta;
	DynamicString source_dir(ta);
	_data_compiler.source_dir(path, source_dir);

	FilesystemDisk source_filesystem(ta);
	source_filesystem.set_prefix(source_dir.c_str());

	File* file = source_filesystem.open(path, FileOpenMode::READ);
	const u32 size = file->size();
	Buffer buf(default_allocator());
	array::resize(buf, size);
	file->read(array::begin(buf), size);
	source_filesystem.close(*file);
	return buf;
}

void CompileOptions::get_absolute_path(const char* path, DynamicString& abs)
{
	TempAllocator256 ta;
	DynamicString source_dir(ta);
	_data_compiler.source_dir(path, source_dir);

	FilesystemDisk source_filesystem(ta);
	source_filesystem.set_prefix(source_dir.c_str());
	source_filesystem.get_absolute_path(path, abs);
}

void CompileOptions::get_temporary_path(const char* suffix, DynamicString& abs)
{
	_data_filesystem.get_absolute_path(CROWN_TEMP_DIRECTORY, abs);

	TempAllocator64 ta;
	DynamicString prefix(ta);
	guid::to_string(guid::new_guid(), prefix);

	abs += '/';
	abs += prefix;
	abs += '.';
	abs += suffix;
}

void CompileOptions::delete_file(const char* path)
{
	_data_filesystem.delete_file(path);
}

void CompileOptions::write(const void* data, u32 size)
{
	array::push(_output, (const char*)data, size);
}

void CompileOptions::write(const Buffer& data)
{
	array::push(_output, array::begin(data), array::size(data));
}

const char* CompileOptions::platform() const
{
	return _platform;
}

const Vector<DynamicString>& CompileOptions::dependencies() const
{
	return _dependencies;
}

void CompileOptions::add_dependency(const char* path)
{
	TempAllocator256 ta;
	DynamicString dep(ta);
	dep += path;
	vector::push_back(_dependencies, dep);
}

int CompileOptions::run_external_compiler(const char* const* argv, StringStream& output)
{
	return os::execute_process(argv, output);
}

} // namespace crown
<|endoftext|>


<commit_before>#include "FilterDevice.h"
#include "trace.h"
#include "FilterDevice.tmh"
#include "DeviceAccess.h"
#include "ControlDevice.h"

void CUsbDkChildDevice::Dump()
{
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Child device:");
    m_DeviceID->Dump();
    m_InstanceID->Dump();
}

class CUsbDkFilterDeviceInit : public CPreAllocatedDeviceInit
{
public:
    CUsbDkFilterDeviceInit(PWDFDEVICE_INIT DeviceInit)
    { Attach(DeviceInit); }

    NTSTATUS Configure(PFN_WDFDEVICE_WDM_IRP_PREPROCESS QDRPreProcessCallback);

    CUsbDkFilterDeviceInit(const CUsbDkFilterDeviceInit&) = delete;
    CUsbDkFilterDeviceInit& operator= (const CUsbDkFilterDeviceInit&) = delete;
};

NTSTATUS CUsbDkFilterDeviceInit::Configure(PFN_WDFDEVICE_WDM_IRP_PREPROCESS QDRPreProcessCallback)
{
    PAGED_CODE();

    SetFilter();
    return SetPreprocessCallback(QDRPreProcessCallback, IRP_MJ_PNP, IRP_MN_QUERY_DEVICE_RELATIONS);
}

NTSTATUS CUsbDkFilterDevice::QDRPreProcess(PIRP Irp)
{
    PDEVICE_OBJECT wdmDevice = WdfDeviceWdmGetDeviceObject(m_Device);
    PIO_STACK_LOCATION  irpStack = IoGetCurrentIrpStackLocation(Irp);

    if (BusRelations != irpStack->Parameters.QueryDeviceRelations.Type)
    {
        IoSkipCurrentIrpStackLocation(Irp);
    }
    else
    {
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutineEx(wdmDevice,
                                 Irp,
                                 (PIO_COMPLETION_ROUTINE) CUsbDkFilterDevice::QDRPostProcessWrap,
                                 this,
                                 TRUE, FALSE, FALSE);
    }

    return WdfDeviceWdmDispatchPreprocessedIrp(m_Device, Irp);
}

NTSTATUS CUsbDkFilterDevice::QDRPostProcess(PIRP Irp)
{
    if (Irp->PendingReturned)
    {
        IoMarkIrpPending(Irp);
    }

    ASSERT(m_QDRIrp == nullptr);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Queuing work item");

    m_QDRIrp = Irp;
    m_QDRCompletionWorkItem.Enqueue();

    return STATUS_MORE_PROCESSING_REQUIRED;
}

//////////////////////////////////////////////////////////////////////////
//TODO: TEMP
typedef struct _CONTROL_DEVICE_EXTENSION {
} PDO_CLONE_EXTENSION, *PPDO_CLONE_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_CLONE_EXTENSION, PdoCloneGetData)

static WDFDEVICE
UsbDkClonePdo(WDFDEVICE ParentDevice)
{
    WDFDEVICE              ClonePdo;
    NTSTATUS               status;
    PWDFDEVICE_INIT        DevInit;
    WDF_OBJECT_ATTRIBUTES  DevAttr;

    PAGED_CODE();

    DevInit = WdfPdoInitAllocate(ParentDevice);

    if (DevInit == NULL) {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! WdfPdoInitAllocate returned NULL");
        return WDF_NO_HANDLE;
    }

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DevAttr, PDO_CLONE_EXTENSION);

#define CLONE_HARDWARE_IDS      L"USB\\Vid_FEED&Pid_CAFE&Rev_0001\0USB\\Vid_FEED&Pid_CAFE\0"
#define CLONE_COMPATIBLE_IDS    L"USB\\Class_FF&SubClass_FF&Prot_FF\0USB\\Class_FF&SubClass_FF\0USB\\Class_FF\0"
#define CLONE_INSTANCE_ID       L"111222333"

    DECLARE_CONST_UNICODE_STRING(CloneHwId, CLONE_HARDWARE_IDS);
    DECLARE_CONST_UNICODE_STRING(CloneCompatId, CLONE_COMPATIBLE_IDS);
    DECLARE_CONST_UNICODE_STRING(CloneInstanceId, CLONE_INSTANCE_ID);

    //TODO: Check error codes
    WdfPdoInitAssignDeviceID(DevInit, &CloneHwId);
    WdfPdoInitAddCompatibleID(DevInit, &CloneCompatId);
    WdfPdoInitAddHardwareID(DevInit, &CloneCompatId);
    WdfPdoInitAssignInstanceID(DevInit, &CloneInstanceId);

    status = WdfDeviceCreate(&DevInit, &DevAttr, &ClonePdo);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! WdfDeviceCreate returned %!STATUS!", status);
        WdfDeviceInitFree(DevInit);
        return WDF_NO_HANDLE;
    }

    return ClonePdo;
}
///////////////////////////////////////////////////////////////////////////////////////////////

void CUsbDkFilterDevice::ClearChildrenList()
{
    m_ChildDevices.ForEachDetached([](CUsbDkChildDevice* Child) { delete Child; return true; });
}

void CUsbDkFilterDevice::QDRPostProcessWi()
{
    ASSERT(m_QDRIrp != NULL);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    ClearChildrenList();

    auto Relations = (PDEVICE_RELATIONS) m_QDRIrp->IoStatus.Information;

    if (Relations)
    {
        for (ULONG i = 0; i < Relations->Count; i++)
        {
            auto PDO = Relations->Objects[i];
            CWdmDeviceAccess pdoAccess(PDO);
            CObjHolder<CRegText> DevID(pdoAccess.GetDeviceID());

            if (!DevID || DevID->empty())
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! No Device IDs read");
                continue;
            }

            CObjHolder<CRegText> InstanceID(pdoAccess.GetInstanceID());

            if (!InstanceID || InstanceID->empty())
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! No Instance ID read");
                continue;
            }

            CUsbDkChildDevice *Device = new CUsbDkChildDevice(DevID, InstanceID, PDO);

            if (Device == nullptr)
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Cannot allocate child device instance");
                continue;
            }

            DevID.detach();
            InstanceID.detach();

            m_ChildDevices.PushBack(Device);
        }

        if (Relations->Count > 0)
        {
            m_ClonedPdo = Relations->Objects[0];

            WDFDEVICE PdoClone = UsbDkClonePdo(m_Device);

            if (PdoClone != WDF_NO_HANDLE)
            {
                Relations->Objects[0] = WdfDeviceWdmGetDeviceObject(PdoClone);
                ObReferenceObject(Relations->Objects[0]);

                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Replaced PDO 0x%p with 0x%p",
                    m_ClonedPdo, Relations->Objects[0]);

                //TODO: Temporary to allow normal USB hub driver unload
                ObDereferenceObject(m_ClonedPdo);
            }
            else
            {
                TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! Failed to create clone PDO");
            }
        }
    }

    auto QDRIrp = m_QDRIrp;
    m_QDRIrp = NULL;

    IoCompleteRequest(QDRIrp, IO_NO_INCREMENT);
}

CUsbDkFilterDevice::~CUsbDkFilterDevice()
{
    ClearChildrenList();
    if (m_ControlDevice != nullptr)
    {
        m_ControlDevice->UnregisterFilter(*this);
        CUsbDkControlDevice::Release();
    }
}

NTSTATUS CUsbDkFilterDevice::Create(PWDFDEVICE_INIT DevInit, WDFDRIVER Driver)
{
    PAGED_CODE();

    auto status = CreateFilterDevice(DevInit);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    m_ControlDevice = CUsbDkControlDevice::Reference(Driver);

    if (m_ControlDevice == nullptr)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    m_ControlDevice->RegisterFilter(*this);
    return STATUS_SUCCESS;
}

NTSTATUS CUsbDkFilterDevice::CreateFilterDevice(PWDFDEVICE_INIT DevInit)
{
    CUsbDkFilterDeviceInit DeviceInit(DevInit);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    auto status = DeviceInit.Configure(CUsbDkFilterDevice::QDRPreProcessWrap);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    WDF_OBJECT_ATTRIBUTES attr;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, USBDK_FILTER_DEVICE_EXTENSION);
    attr.EvtCleanupCallback = CUsbDkFilterDevice::ContextCleanup;

    status = CWdfDevice::Create(DeviceInit, attr);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    if (ShouldAttach())
    {
        auto deviceContext = UsbDkFilterGetContext(m_Device);
        deviceContext->UsbDkFilter = this;

        status = m_QDRCompletionWorkItem.Create(m_Device);

        ULONG traceLevel = NT_SUCCESS(status) ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_ERROR;
        TraceEvents(traceLevel, TRACE_FILTERDEVICE, "%!FUNC! Attachment status: %!STATUS!", status);
    }
    else
    {
        status = STATUS_NOT_SUPPORTED;
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Not attached");
    }

    return status;
}

void CUsbDkFilterDevice::ContextCleanup(_In_ WDFOBJECT DeviceObject)
{
    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    auto deviceContext = UsbDkFilterGetContext(DeviceObject);
    delete deviceContext->UsbDkFilter;
}

bool CUsbDkFilterDevice::ShouldAttach()
{
    PAGED_CODE();

    CWdfDeviceAccess devAccess(m_Device);
    CObjHolder<CRegText> hwIDs(devAccess.GetHardwareIdProperty());
    if (hwIDs)
    {
        hwIDs->Dump();
        return hwIDs->Match(L"USB\\ROOT_HUB") ||
                hwIDs->Match(L"USB\\ROOT_HUB20");
    }

    return false;
}
<commit_msg>UsbDk: Comment out PDO cloning logic<commit_after>#include "FilterDevice.h"
#include "trace.h"
#include "FilterDevice.tmh"
#include "DeviceAccess.h"
#include "ControlDevice.h"

void CUsbDkChildDevice::Dump()
{
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Child device:");
    m_DeviceID->Dump();
    m_InstanceID->Dump();
}

class CUsbDkFilterDeviceInit : public CPreAllocatedDeviceInit
{
public:
    CUsbDkFilterDeviceInit(PWDFDEVICE_INIT DeviceInit)
    { Attach(DeviceInit); }

    NTSTATUS Configure(PFN_WDFDEVICE_WDM_IRP_PREPROCESS QDRPreProcessCallback);

    CUsbDkFilterDeviceInit(const CUsbDkFilterDeviceInit&) = delete;
    CUsbDkFilterDeviceInit& operator= (const CUsbDkFilterDeviceInit&) = delete;
};

NTSTATUS CUsbDkFilterDeviceInit::Configure(PFN_WDFDEVICE_WDM_IRP_PREPROCESS QDRPreProcessCallback)
{
    PAGED_CODE();

    SetFilter();
    return SetPreprocessCallback(QDRPreProcessCallback, IRP_MJ_PNP, IRP_MN_QUERY_DEVICE_RELATIONS);
}

NTSTATUS CUsbDkFilterDevice::QDRPreProcess(PIRP Irp)
{
    PDEVICE_OBJECT wdmDevice = WdfDeviceWdmGetDeviceObject(m_Device);
    PIO_STACK_LOCATION  irpStack = IoGetCurrentIrpStackLocation(Irp);

    if (BusRelations != irpStack->Parameters.QueryDeviceRelations.Type)
    {
        IoSkipCurrentIrpStackLocation(Irp);
    }
    else
    {
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutineEx(wdmDevice,
                                 Irp,
                                 (PIO_COMPLETION_ROUTINE) CUsbDkFilterDevice::QDRPostProcessWrap,
                                 this,
                                 TRUE, FALSE, FALSE);
    }

    return WdfDeviceWdmDispatchPreprocessedIrp(m_Device, Irp);
}

NTSTATUS CUsbDkFilterDevice::QDRPostProcess(PIRP Irp)
{
    if (Irp->PendingReturned)
    {
        IoMarkIrpPending(Irp);
    }

    ASSERT(m_QDRIrp == nullptr);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Queuing work item");

    m_QDRIrp = Irp;
    m_QDRCompletionWorkItem.Enqueue();

    return STATUS_MORE_PROCESSING_REQUIRED;
}

//////////////////////////////////////////////////////////////////////////
//TODO: TEMP
typedef struct _CONTROL_DEVICE_EXTENSION {
} PDO_CLONE_EXTENSION, *PPDO_CLONE_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_CLONE_EXTENSION, PdoCloneGetData)

// static WDFDEVICE
// UsbDkClonePdo(WDFDEVICE ParentDevice)
// {
//     WDFDEVICE              ClonePdo;
//     NTSTATUS               status;
//     PWDFDEVICE_INIT        DevInit;
//     WDF_OBJECT_ATTRIBUTES  DevAttr;
//
//     PAGED_CODE();
//
//     DevInit = WdfPdoInitAllocate(ParentDevice);
//
//     if (DevInit == NULL) {
//         TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! WdfPdoInitAllocate returned NULL");
//         return WDF_NO_HANDLE;
//     }
//
//     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DevAttr, PDO_CLONE_EXTENSION);
//
// #define CLONE_HARDWARE_IDS      L"USB\\Vid_FEED&Pid_CAFE&Rev_0001\0USB\\Vid_FEED&Pid_CAFE\0"
// #define CLONE_COMPATIBLE_IDS    L"USB\\Class_FF&SubClass_FF&Prot_FF\0USB\\Class_FF&SubClass_FF\0USB\\Class_FF\0"
// #define CLONE_INSTANCE_ID       L"111222333"
//
//     DECLARE_CONST_UNICODE_STRING(CloneHwId, CLONE_HARDWARE_IDS);
//     DECLARE_CONST_UNICODE_STRING(CloneCompatId, CLONE_COMPATIBLE_IDS);
//     DECLARE_CONST_UNICODE_STRING(CloneInstanceId, CLONE_INSTANCE_ID);
//
//     //TODO: Check error codes
//     WdfPdoInitAssignDeviceID(DevInit, &CloneHwId);
//     WdfPdoInitAddCompatibleID(DevInit, &CloneCompatId);
//     WdfPdoInitAddHardwareID(DevInit, &CloneCompatId);
//     WdfPdoInitAssignInstanceID(DevInit, &CloneInstanceId);
//
//     status = WdfDeviceCreate(&DevInit, &DevAttr, &ClonePdo);
//     if (!NT_SUCCESS(status))
//     {
//         TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! WdfDeviceCreate returned %!STATUS!", status);
//         WdfDeviceInitFree(DevInit);
//         return WDF_NO_HANDLE;
//     }
//
//     return ClonePdo;
// }
// ///////////////////////////////////////////////////////////////////////////////////////////////

void CUsbDkFilterDevice::ClearChildrenList()
{
    m_ChildDevices.ForEachDetached([](CUsbDkChildDevice* Child) { delete Child; return true; });
}

void CUsbDkFilterDevice::QDRPostProcessWi()
{
    ASSERT(m_QDRIrp != NULL);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    ClearChildrenList();

    auto Relations = (PDEVICE_RELATIONS) m_QDRIrp->IoStatus.Information;

    if (Relations)
    {
        for (ULONG i = 0; i < Relations->Count; i++)
        {
            auto PDO = Relations->Objects[i];
            CWdmDeviceAccess pdoAccess(PDO);
            CObjHolder<CRegText> DevID(pdoAccess.GetDeviceID());

            if (!DevID || DevID->empty())
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! No Device IDs read");
                continue;
            }

            CObjHolder<CRegText> InstanceID(pdoAccess.GetInstanceID());

            if (!InstanceID || InstanceID->empty())
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! No Instance ID read");
                continue;
            }

            CUsbDkChildDevice *Device = new CUsbDkChildDevice(DevID, InstanceID, PDO);

            if (Device == nullptr)
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Cannot allocate child device instance");
                continue;
            }

            DevID.detach();
            InstanceID.detach();

            m_ChildDevices.PushBack(Device);
        }

//         if (Relations->Count > 0)
//         {
//             m_ClonedPdo = Relations->Objects[0];
//
//             WDFDEVICE PdoClone = UsbDkClonePdo(m_Device);
//
//             if (PdoClone != WDF_NO_HANDLE)
//             {
//                 Relations->Objects[0] = WdfDeviceWdmGetDeviceObject(PdoClone);
//                 ObReferenceObject(Relations->Objects[0]);
//
//                 TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Replaced PDO 0x%p with 0x%p",
//                     m_ClonedPdo, Relations->Objects[0]);
//
//                 //TODO: Temporary to allow normal USB hub driver unload
//                 ObDereferenceObject(m_ClonedPdo);
//             }
//             else
//             {
//                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! Failed to create clone PDO");
//             }
//         }
    }

    auto QDRIrp = m_QDRIrp;
    m_QDRIrp = NULL;

    IoCompleteRequest(QDRIrp, IO_NO_INCREMENT);
}

CUsbDkFilterDevice::~CUsbDkFilterDevice()
{
    ClearChildrenList();
    if (m_ControlDevice != nullptr)
    {
        m_ControlDevice->UnregisterFilter(*this);
        CUsbDkControlDevice::Release();
    }
}

NTSTATUS CUsbDkFilterDevice::Create(PWDFDEVICE_INIT DevInit, WDFDRIVER Driver)
{
    PAGED_CODE();

    auto status = CreateFilterDevice(DevInit);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    m_ControlDevice = CUsbDkControlDevice::Reference(Driver);

    if (m_ControlDevice == nullptr)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    m_ControlDevice->RegisterFilter(*this);
    return STATUS_SUCCESS;
}

NTSTATUS CUsbDkFilterDevice::CreateFilterDevice(PWDFDEVICE_INIT DevInit)
{
    CUsbDkFilterDeviceInit DeviceInit(DevInit);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    auto status = DeviceInit.Configure(CUsbDkFilterDevice::QDRPreProcessWrap);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    WDF_OBJECT_ATTRIBUTES attr;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, USBDK_FILTER_DEVICE_EXTENSION);
    attr.EvtCleanupCallback = CUsbDkFilterDevice::ContextCleanup;

    status = CWdfDevice::Create(DeviceInit, attr);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    if (ShouldAttach())
    {
        auto deviceContext = UsbDkFilterGetContext(m_Device);
        deviceContext->UsbDkFilter = this;

        status = m_QDRCompletionWorkItem.Create(m_Device);

        ULONG traceLevel = NT_SUCCESS(status) ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_ERROR;
        TraceEvents(traceLevel, TRACE_FILTERDEVICE, "%!FUNC! Attachment status: %!STATUS!", status);
    }
    else
    {
        status = STATUS_NOT_SUPPORTED;
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Not attached");
    }

    return status;
}

void CUsbDkFilterDevice::ContextCleanup(_In_ WDFOBJECT DeviceObject)
{
    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Entry");

    auto deviceContext = UsbDkFilterGetContext(DeviceObject);
    delete deviceContext->UsbDkFilter;
}

bool CUsbDkFilterDevice::ShouldAttach()
{
    PAGED_CODE();

    CWdfDeviceAccess devAccess(m_Device);
    CObjHolder<CRegText> hwIDs(devAccess.GetHardwareIdProperty());
    if (hwIDs)
    {
        hwIDs->Dump();
        return hwIDs->Match(L"USB\\ROOT_HUB") ||
                hwIDs->Match(L"USB\\ROOT_HUB20");
    }

    return false;
}
<|endoftext|>