<commit_before>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextc.h"
#include "context.h"
#include "logging.h"

using namespace ContextProvider;

QStringList *serviceKeyList = NULL;
QString *serviceBusName = NULL;
QDBusConnection::BusType serviceBusType;

/// Initializes the service for the given \a name. \a is_system
/// (1 or 0) specifies if this is a system service (system bus or session bus). The \a keys
/// is a pointer to an array of pointers to NULL-terminated strings with key names.
/// \a key_count specifies the number of items in the \a keys array.
/// Returns 1 on success, 0 otherwise. This function internally calls Context::initService.
int context_init_service (int is_system, const char *name, const char** keys, int key_count)
{
    // Build the key string list
    QStringList keyList;
    int i;
    for (i = 0; i < key_count; i++)
        keyList.append(QString(keys[i]));

    QDBusConnection::BusType busType = (is_system) ? QDBusConnection::SystemBus : QDBusConnection::SessionBus;
    return Context::initService(busType, QString(name), keyList);
}

/// Creates a new ContextPtr handle that can be used to set/get values. \a name
/// is a NULL-terminated key name. Returns the new allocated ContextPtr. Will never
/// return NULL. You're supposed to destroy the ContextPtr with context_free.
ContextPtr* context_new (const char *name)
{
    Context *c = new Context(name);
    return (ContextPtr*) c;
}

/// Frees the provided \a ptr. Note that destroying the ContextPtr will not destroy
/// the key itself. It's just the handle that's being destroyed.
void context_free (ContextPtr *ptr)
{
    delete ((Context*) ptr);
}

/// Returns the key that this context property \a ptr represents.
/// The caller is responsible for freeing the returned string when
/// it's not needed anymore.
char* context_get_key (ContextPtr *ptr)
{
    QString key = ((Context*) ptr)->getKey();
    return qstrdup(key.toUtf8().constData());
}

/// Returns 1 if the context property referenced by \a ptr is valid.
int context_is_valid (ContextPtr *ptr)
{
    return ((Context*) ptr)->isValid();
}

/// Returns 1 if the context property referenced by \a ptr is set (it's not undetermined).
int context_is_set (ContextPtr *ptr)
{
    return ((Context*) ptr)->isSet();
}

/// Unsets the context property referenced by \a ptr. The property value
/// becomes undeterined.
void context_unset (ContextPtr *ptr)
{
    ((Context*) ptr)->unset();
}

/// Sets the context property referenced by \a ptr to a new integer value \a v.
/// Calls Context::set internally.
void context_set_int(ContextPtr *ptr, int v)
{
    ((Context*) ptr)->set(QVariant(v));
}

/// Sets the context property referenced by \a ptr to a new bool value \a v.
/// Calls Context::set internally.
void context_set_bool(ContextPtr *ptr, int v)
{
    ((Context*) ptr)->set(QVariant((bool) v));
}

/// Sets the context property referenced by \a ptr to a new double value \a v.
/// Calls Context::set internally.
void context_set_double(ContextPtr *ptr, double v)
{
    ((Context*) ptr)->set(QVariant(v));
}

/// Sets the context property referenced by \a ptr to a new string value \a v.
/// Calls Context::set internally.
void context_set_string(ContextPtr *ptr, const char *v)
{
    ((Context*) ptr)->set(QVariant(QString(v)));
}

/// Obtains the integer value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new integer value.
int context_get_int (ContextPtr *ptr, int *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toInt();
        return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the string value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a point to a new NULL-terminated string. You have to \b destroy this string
/// when done dealing with it.
int context_get_string (ContextPtr *ptr, char **v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        QString str = var.toString();
        *v = qstrdup(str.toUtf8().constData());
    return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the bool value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new bool (1, 0) value.
int context_get_bool (ContextPtr *ptr, int *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toBool();
        return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the double value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new double value.
int context_get_double (ContextPtr *ptr, double *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toDouble();
        return 1;
    } else {
        *v = 0.0;
        return 0;
    }
}

static int reinitialize_service()
{
    if (serviceKeyList->length() > 0)
        Context::stopService(*serviceBusName);

    return Context::initService(serviceBusType, *serviceBusName, *serviceKeyList);
}

int context_provider_init (DBusBusType bus_type, const char* bus_name)
{
    if (serviceBusName != NULL) {
    contextCritical() << "Service already initialize. You can only initialize one service with C API";
        return 0;
    }

    serviceBusType = (bus_type == DBUS_BUS_SESSION) ?
                      QDBusConnection::SessionBus   :
                      QDBusConnection::SystemBus;
    serviceBusName = new QString(bus_name);
    serviceKeyList = new QStringList();

    return reinitialize_service();
}

void context_provider_stop (void)
{
    contextDebug() << "Stopping service";

    if (serviceBusName) {
        Context::stopService(*serviceBusName);
    }

    delete serviceBusName; serviceBusName = NULL;
    delete serviceKeyList; serviceKeyList = NULL;
}

void context_provider_install_key (const char* key,
                                   int clear_values_on_subscribe,
                                   ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                   void* subscription_changed_cb_target)
{
    if (! serviceKeyList) {
        contextCritical() << "Can't install key:" << key << "because no service started.";
        return;
    }

    if (serviceKeyList->contains(QString(key))) {
        contextCritical() << "Key:" << key << "is already installed";
        return;
    }

    serviceKeyList->append(key);
    reinitialize_service();

    // FIXME Does not register the callback yet
}

<commit_msg>context_provider_install_key works like advertised.<commit_after>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextc.h"
#include "context.h"
#include "logging.h"
#include "sconnect.h"

using namespace ContextProvider;
class ContextListener;

QStringList *serviceKeyList = NULL;
QString *serviceBusName = NULL;
QDBusConnection::BusType serviceBusType;
QList<ContextListener*> *contextListeners = NULL;

class ContextListener : public QObject
{
    Q_OBJECT

public:
    ContextListener(const QString &k, ContextProviderSubscriptionChangedCallback cb, void *dt) :
        callback(cb), user_data(dt), key(k)
    {
        sconnect(&key, SIGNAL(firstSubscriberAppeared()), this, SLOT(onFirstSubscriberAppeared));
        sconnect(&key, SIGNAL(lastSubscriberDisappeared()), this, SLOT(onLastSubscriberDisappeared));
    }

public slots:
    void onFirstSubscriberAppeared()
    {
        callback(1, user_data);
    }

    void onLastSubscriberDisappeared()
    {
        callback(0, user_data);
    }

private:
    ContextProviderSubscriptionChangedCallback callback;
    void *user_data;
    Context key;
};

/// Initializes the service for the given \a name. \a is_system
/// (1 or 0) specifies if this is a system service (system bus or session bus). The \a keys
/// is a pointer to an array of pointers to NULL-terminated strings with key names.
/// \a key_count specifies the number of items in the \a keys array.
/// Returns 1 on success, 0 otherwise. This function internally calls Context::initService.
int context_init_service (int is_system, const char *name, const char** keys, int key_count)
{
    // Build the key string list
    QStringList keyList;
    int i;
    for (i = 0; i < key_count; i++)
        keyList.append(QString(keys[i]));

    QDBusConnection::BusType busType = (is_system) ? QDBusConnection::SystemBus : QDBusConnection::SessionBus;
    return Context::initService(busType, QString(name), keyList);
}

/// Creates a new ContextPtr handle that can be used to set/get values. \a name
/// is a NULL-terminated key name. Returns the new allocated ContextPtr. Will never
/// return NULL. You're supposed to destroy the ContextPtr with context_free.
ContextPtr* context_new (const char *name)
{
    Context *c = new Context(name);
    return (ContextPtr*) c;
}

/// Frees the provided \a ptr. Note that destroying the ContextPtr will not destroy
/// the key itself. It's just the handle that's being destroyed.
void context_free (ContextPtr *ptr)
{
    delete ((Context*) ptr);
}

/// Returns the key that this context property \a ptr represents.
/// The caller is responsible for freeing the returned string when
/// it's not needed anymore.
char* context_get_key (ContextPtr *ptr)
{
    QString key = ((Context*) ptr)->getKey();
    return qstrdup(key.toUtf8().constData());
}

/// Returns 1 if the context property referenced by \a ptr is valid.
int context_is_valid (ContextPtr *ptr)
{
    return ((Context*) ptr)->isValid();
}

/// Returns 1 if the context property referenced by \a ptr is set (it's not undetermined).
int context_is_set (ContextPtr *ptr)
{
    return ((Context*) ptr)->isSet();
}

/// Unsets the context property referenced by \a ptr. The property value
/// becomes undeterined.
void context_unset (ContextPtr *ptr)
{
    ((Context*) ptr)->unset();
}

/// Sets the context property referenced by \a ptr to a new integer value \a v.
/// Calls Context::set internally.
void context_set_int(ContextPtr *ptr, int v)
{
    ((Context*) ptr)->set(QVariant(v));
}

/// Sets the context property referenced by \a ptr to a new bool value \a v.
/// Calls Context::set internally.
void context_set_bool(ContextPtr *ptr, int v)
{
    ((Context*) ptr)->set(QVariant((bool) v));
}

/// Sets the context property referenced by \a ptr to a new double value \a v.
/// Calls Context::set internally.
void context_set_double(ContextPtr *ptr, double v)
{
    ((Context*) ptr)->set(QVariant(v));
}

/// Sets the context property referenced by \a ptr to a new string value \a v.
/// Calls Context::set internally.
void context_set_string(ContextPtr *ptr, const char *v)
{
    ((Context*) ptr)->set(QVariant(QString(v)));
}

/// Obtains the integer value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new integer value.
int context_get_int (ContextPtr *ptr, int *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toInt();
        return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the string value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a point to a new NULL-terminated string. You have to \b destroy this string
/// when done dealing with it.
int context_get_string (ContextPtr *ptr, char **v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        QString str = var.toString();
        *v = qstrdup(str.toUtf8().constData());
    return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the bool value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new bool (1, 0) value.
int context_get_bool (ContextPtr *ptr, int *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toBool();
        return 1;
    } else {
        *v = 0;
        return 0;
    }
}

/// Obtains the double value of the context property described by the \a ptr.
/// Returns 1 if the property retrieval was succesful. 0 when property
/// value is \b Undetermined. When retrieval was succesful, \a v is set
/// to a new double value.
int context_get_double (ContextPtr *ptr, double *v)
{
    QVariant var = ((Context*) ptr)->get();
    if (var.isValid()) {
        *v = var.toDouble();
        return 1;
    } else {
        *v = 0.0;
        return 0;
    }
}

static int reinitialize_service()
{
    if (serviceKeyList->length() > 0)
        Context::stopService(*serviceBusName);

    return Context::initService(serviceBusType, *serviceBusName, *serviceKeyList);
}

int context_provider_init (DBusBusType bus_type, const char* bus_name)
{
    if (serviceBusName != NULL) {
    contextCritical() << "Service already initialize. You can only initialize one service with C API";
        return 0;
    }

    serviceBusType = (bus_type == DBUS_BUS_SESSION) ?
                      QDBusConnection::SessionBus   :
                      QDBusConnection::SystemBus;
    serviceBusName = new QString(bus_name);
    serviceKeyList = new QStringList();
    contextListeners = new QList<ContextListener*>;

    return reinitialize_service();
}

void context_provider_stop (void)
{
    contextDebug() << "Stopping service";

    if (serviceBusName) {
        Context::stopService(*serviceBusName);
    }

    // Delete all listeners
    foreach (ContextListener *listener, *contextListeners)
        delete listener;

    delete serviceBusName; serviceBusName = NULL;
    delete serviceKeyList; serviceKeyList = NULL;
    delete contextListeners; contextListeners = NULL;
}

void context_provider_install_key (const char* key,
                                   int clear_values_on_subscribe,
                                   ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                   void* subscription_changed_cb_target)
{
    if (! serviceKeyList) {
        contextCritical() << "Can't install key:" << key << "because no service started.";
        return;
    }

    if (serviceKeyList->contains(QString(key))) {
        contextCritical() << "Key:" << key << "is already installed";
        return;
    }

    serviceKeyList->append(key);
    reinitialize_service();

    contextListeners->append(new ContextListener(key, subscription_changed_cb, subscription_changed_cb_target));
}

<|endoftext|>


<commit_before>/*
 * Copyright 2000-2008, François Revol, <revol@free.fr>. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include "Utils.h"
#include <FindDirectory.h>
#include <Path.h>
#include <String.h>
#include <BeBuild.h>

#include <malloc.h>
#include <stdio.h>

// some private font information structs
namespace BPrivate {

typedef struct font_folder_info {
	//char name[256];
	char *name;
	uint32 flags;
} font_folder_info;

typedef struct font_file_info {
	char *name;
	uint32 flags;
	font_family family;
	font_style style;
	uint32 dummy;
} font_file_info;

};

using namespace BPrivate;

// this is PRIVATE to libbe and NOT in R5!!!
extern long _count_font_folders_(void);
extern long _count_font_files_(long);
extern status_t _get_nth_font_file_(long, font_file_info **);
extern status_t _get_nth_font_folder_(long, font_folder_info **);

status_t
find_font_file(entry_ref *to, font_family family, font_style style, float size)
{
#ifdef B_BEOS_VERSION_DANO
	status_t err = ENOENT;
	long i, fontcount, foldercount;
	font_file_info *ffi;
	font_folder_info *fdi;
	bool found = false;
	(void)size;

	fontcount = _count_font_files_(0);
	for (i = 0; i < fontcount; i++) {
		err = _get_nth_font_file_(i, &ffi);
		if (err)
			continue;
		if (strcmp(ffi->family, family) || strcmp(ffi->style, style))
			continue;
		found = true;
		break;
	}
	if (!found)
		return ENOENT;
	foldercount = _count_font_folders_();
	for (i = 0; i < fontcount; i++) {
		err = _get_nth_font_folder_(i, &fdi);
		if (err)
			continue;
		BPath ffile(fdi->name);
		ffile.Append(ffi->name);
		printf("find_font_file: looking for '%s' in '%s'\n", ffi->name, fdi->name);
		BEntry ent(ffile.Path());
		if (ent.InitCheck())
			continue;
		printf("find_font_file: found\n.");
		return ent.GetRef(to);
	}
#endif
	return ENOENT;
}

#define _BORK(_t) \
	err = find_directory(_t, &path); \
	if (!err && (s = dir->FindFirst(path.Path())) >= 0) { \
		printf("found %s\n", #_t); \
		dir->Remove(s, strlen(path.Path()) - s); \
		BString tok(#_t); \
		tok.Prepend("${"); \
		tok.Append("}"); \
		dir->Insert(tok, s); \
		return B_OK; \
	} \

status_t
escape_find_directory(BString *dir)
{
	status_t err;
	BPath path;
	int32 s;

	_BORK(B_DESKTOP_DIRECTORY);
	_BORK(B_TRASH_DIRECTORY);
	//_BORK(B_ROOT_DIRECTORY);

	//_BORK(B_BEOS_BOOT_DIRECTORY);
	_BORK(B_BEOS_FONTS_DIRECTORY);
	_BORK(B_BEOS_LIB_DIRECTORY);
	_BORK(B_BEOS_SERVERS_DIRECTORY);
	_BORK(B_BEOS_APPS_DIRECTORY);
	_BORK(B_BEOS_BIN_DIRECTORY);
	_BORK(B_BEOS_ETC_DIRECTORY);
	_BORK(B_BEOS_DOCUMENTATION_DIRECTORY);
	_BORK(B_BEOS_PREFERENCES_DIRECTORY);
	_BORK(B_BEOS_TRANSLATORS_DIRECTORY);
	_BORK(B_BEOS_MEDIA_NODES_DIRECTORY);
	_BORK(B_BEOS_SOUNDS_DIRECTORY);
	// not in the declared order, so others are picked first
	_BORK(B_BEOS_ADDONS_DIRECTORY);
	_BORK(B_BEOS_SYSTEM_DIRECTORY);
	_BORK(B_BEOS_DIRECTORY);

	_BORK(B_USER_BOOT_DIRECTORY);
	_BORK(B_USER_FONTS_DIRECTORY);
	_BORK(B_USER_LIB_DIRECTORY);
	_BORK(B_USER_SETTINGS_DIRECTORY);
	_BORK(B_USER_DESKBAR_DIRECTORY);
	_BORK(B_USER_PRINTERS_DIRECTORY);
	_BORK(B_USER_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_SOUNDS_DIRECTORY);
	//
	_BORK(B_USER_ADDONS_DIRECTORY);
	_BORK(B_USER_CONFIG_DIRECTORY);
	_BORK(B_USER_DIRECTORY);

	// same for the whole block, prefer user over common
	_BORK(B_COMMON_BOOT_DIRECTORY);
	_BORK(B_COMMON_FONTS_DIRECTORY);
	_BORK(B_COMMON_LIB_DIRECTORY);
	_BORK(B_COMMON_SERVERS_DIRECTORY);
	_BORK(B_COMMON_BIN_DIRECTORY);
	_BORK(B_COMMON_ETC_DIRECTORY);
	_BORK(B_COMMON_DOCUMENTATION_DIRECTORY);
	_BORK(B_COMMON_SETTINGS_DIRECTORY);
	_BORK(B_COMMON_DEVELOP_DIRECTORY);
	_BORK(B_COMMON_LOG_DIRECTORY);
	_BORK(B_COMMON_SPOOL_DIRECTORY);
	_BORK(B_COMMON_TEMP_DIRECTORY);
	_BORK(B_COMMON_VAR_DIRECTORY);
	_BORK(B_COMMON_TRANSLATORS_DIRECTORY);
	_BORK(B_COMMON_MEDIA_NODES_DIRECTORY);
	_BORK(B_COMMON_SOUNDS_DIRECTORY);
	//
	_BORK(B_COMMON_ADDONS_DIRECTORY);
	_BORK(B_COMMON_SYSTEM_DIRECTORY);
	_BORK(B_COMMON_DIRECTORY);

	_BORK(B_APPS_DIRECTORY);
	_BORK(B_PREFERENCES_DIRECTORY);
	_BORK(B_UTILITIES_DIRECTORY);

	return B_OK;
}
#undef _BORK

#define _BORK(_t) \
	if (tok == #_t) { \
		err = find_directory(_t, &path); \
		if (err) return err; \
		dir->Remove(s, e - s + 1); \
		dir->Insert(path.Path(), s); \
		return B_OK; \
	} \

status_t
unescape_find_directory(BString *dir)
{
	status_t err = B_ERROR;
	int32 s, e;
	BString tok;
	BPath path;
	s = dir->FindFirst("${");
	if (s < 0)
		return B_OK;
	e = dir->FindFirst("}", s);
	if (e < 0)
		return B_OK;
	dir->CopyInto(tok, s + 2, e - s - 2);
	//printf("tok '%s'\n", tok.String());

	_BORK(B_DESKTOP_DIRECTORY);
	_BORK(B_TRASH_DIRECTORY);
#ifdef B_BEOS_VERSION_DANO
	_BORK(B_ROOT_DIRECTORY);
#endif

	_BORK(B_BEOS_DIRECTORY);
	_BORK(B_BEOS_SYSTEM_DIRECTORY);
	_BORK(B_BEOS_ADDONS_DIRECTORY);
	_BORK(B_BEOS_BOOT_DIRECTORY);
	_BORK(B_BEOS_FONTS_DIRECTORY);
	_BORK(B_BEOS_LIB_DIRECTORY);
	_BORK(B_BEOS_SERVERS_DIRECTORY);
	_BORK(B_BEOS_APPS_DIRECTORY);
	_BORK(B_BEOS_BIN_DIRECTORY);
	_BORK(B_BEOS_ETC_DIRECTORY);
	_BORK(B_BEOS_DOCUMENTATION_DIRECTORY);
	_BORK(B_BEOS_PREFERENCES_DIRECTORY);
	_BORK(B_BEOS_TRANSLATORS_DIRECTORY);
	_BORK(B_BEOS_MEDIA_NODES_DIRECTORY);
	_BORK(B_BEOS_SOUNDS_DIRECTORY);

	_BORK(B_COMMON_DIRECTORY);
	_BORK(B_COMMON_SYSTEM_DIRECTORY);
	_BORK(B_COMMON_ADDONS_DIRECTORY);
	_BORK(B_COMMON_BOOT_DIRECTORY);
	_BORK(B_COMMON_FONTS_DIRECTORY);
	_BORK(B_COMMON_LIB_DIRECTORY);
	_BORK(B_COMMON_SERVERS_DIRECTORY);
	_BORK(B_COMMON_BIN_DIRECTORY);
	_BORK(B_COMMON_ETC_DIRECTORY);
	_BORK(B_COMMON_DOCUMENTATION_DIRECTORY);
	_BORK(B_COMMON_SETTINGS_DIRECTORY);
	_BORK(B_COMMON_DEVELOP_DIRECTORY);
	_BORK(B_COMMON_LOG_DIRECTORY);
	_BORK(B_COMMON_SPOOL_DIRECTORY);
	_BORK(B_COMMON_TEMP_DIRECTORY);
	_BORK(B_COMMON_VAR_DIRECTORY);
	_BORK(B_COMMON_TRANSLATORS_DIRECTORY);
	_BORK(B_COMMON_MEDIA_NODES_DIRECTORY);
	_BORK(B_COMMON_SOUNDS_DIRECTORY);

	_BORK(B_USER_DIRECTORY);
	_BORK(B_USER_CONFIG_DIRECTORY);
	_BORK(B_USER_ADDONS_DIRECTORY);
	_BORK(B_USER_BOOT_DIRECTORY);
	_BORK(B_USER_FONTS_DIRECTORY);
	_BORK(B_USER_LIB_DIRECTORY);
	_BORK(B_USER_SETTINGS_DIRECTORY);
	_BORK(B_USER_DESKBAR_DIRECTORY);
	_BORK(B_USER_PRINTERS_DIRECTORY);
	_BORK(B_USER_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_SOUNDS_DIRECTORY);

	_BORK(B_APPS_DIRECTORY);
	_BORK(B_PREFERENCES_DIRECTORY);
	_BORK(B_UTILITIES_DIRECTORY);

	return B_OK;
}
#undef _BORK

// copy a file including its attributes
#define BUFF_SZ 1024*1024
status_t
copy_file(entry_ref *ref, const char *to)
{
	char *buff;
	status_t err = B_OK;
	//off_t off;
	//size_t got;
	(void)ref; (void)to;

	buff = (char *)malloc(BUFF_SZ);
	// XXX: TODO

	free(buff);
	return err;
}

int
testhook()
{
	status_t err;
	BString str("/boot/home/config/fonts/ttfonts/toto.ttf");
	err = escape_find_directory(&str);
	printf("error 0x%08lx %s\n", err, str.String());
	err = unescape_find_directory(&str);
	printf("error 0x%08lx %s\n", err, str.String());
	return 0;
}

status_t
FindRGBColor(BMessage &message, const char *name, int32 index, rgb_color *c)
{
#ifdef B_BEOS_VERSION_DANO
	return message.FindRGBColor(name, index, c);
#else
	const void *data;
	ssize_t len;
	status_t err;
	err = message.FindData(name, B_RGB_COLOR_TYPE, index, &data, &len);
	if (err < B_OK)
		return err;
	if (len > (ssize_t)sizeof(*c))
		return E2BIG;
	// Hack
	memcpy((void *)c, data, len);
	return B_OK;
#endif
}

status_t
AddRGBColor(BMessage &message, const char *name, rgb_color a_color, type_code type)
{
#ifdef B_BEOS_VERSION_DANO
	return message.AddRGBColor(name, a_color, type);
#else
	return message.AddData(name, type, &a_color, sizeof(a_color));
#endif
}

status_t
FindFont(BMessage &message, const char *name, int32 index, BFont *f)
{
#ifdef B_BEOS_VERSION_DANO
	return message.FindFlat(name, index, f);
#else
	const void *data;
	ssize_t len;
	status_t err = message.FindData(name, 'FONt', index, &data, &len);
#define DERR(e) { PRINT(("%s: err: %s\n", __FUNCTION__, strerror(e))); }
	if (err < B_OK)
		return err;
	if (len > (ssize_t)sizeof(*f))
		return E2BIG;
	// Hack: only Dano has BFont : public BFlattenable
	memcpy((void *)f, data, len);
	return B_OK;
#endif
}

status_t
AddFont(BMessage &message, const char *name, BFont *f, int32 count)
{
#ifdef B_BEOS_VERSION_DANO
	return message.AddFlat(name, f, count);
#else
	return message.AddData(name, 'FONt', (void *)&f, sizeof(f), true, count);
#endif
}

<commit_msg>Sync directory listing to that in FindDirectory.h<commit_after>/*
 * Copyright 2000-2008, François Revol, <revol@free.fr>. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include "Utils.h"
#include <FindDirectory.h>
#include <Path.h>
#include <String.h>
#include <BeBuild.h>

#include <malloc.h>
#include <stdio.h>

// some private font information structs
namespace BPrivate {

typedef struct font_folder_info {
	//char name[256];
	char *name;
	uint32 flags;
} font_folder_info;

typedef struct font_file_info {
	char *name;
	uint32 flags;
	font_family family;
	font_style style;
	uint32 dummy;
} font_file_info;

};

using namespace BPrivate;

// this is PRIVATE to libbe and NOT in R5!!!
extern long _count_font_folders_(void);
extern long _count_font_files_(long);
extern status_t _get_nth_font_file_(long, font_file_info **);
extern status_t _get_nth_font_folder_(long, font_folder_info **);

status_t
find_font_file(entry_ref *to, font_family family, font_style style, float size)
{
#ifdef B_BEOS_VERSION_DANO
	status_t err = ENOENT;
	long i, fontcount, foldercount;
	font_file_info *ffi;
	font_folder_info *fdi;
	bool found = false;
	(void)size;

	fontcount = _count_font_files_(0);
	for (i = 0; i < fontcount; i++) {
		err = _get_nth_font_file_(i, &ffi);
		if (err)
			continue;
		if (strcmp(ffi->family, family) || strcmp(ffi->style, style))
			continue;
		found = true;
		break;
	}
	if (!found)
		return ENOENT;
	foldercount = _count_font_folders_();
	for (i = 0; i < fontcount; i++) {
		err = _get_nth_font_folder_(i, &fdi);
		if (err)
			continue;
		BPath ffile(fdi->name);
		ffile.Append(ffi->name);
		printf("find_font_file: looking for '%s' in '%s'\n", ffi->name, fdi->name);
		BEntry ent(ffile.Path());
		if (ent.InitCheck())
			continue;
		printf("find_font_file: found\n.");
		return ent.GetRef(to);
	}
#endif
	return ENOENT;
}

#define _BORK(_t) \
	err = find_directory(_t, &path); \
	if (!err && (s = dir->FindFirst(path.Path())) >= 0) { \
		printf("found %s\n", #_t); \
		dir->Remove(s, strlen(path.Path()) - s); \
		BString tok(#_t); \
		tok.Prepend("${"); \
		tok.Append("}"); \
		dir->Insert(tok, s); \
		return B_OK; \
	} \

status_t
escape_find_directory(BString *dir)
{
	status_t err;
	BPath path;
	int32 s;

	/* This is just the entire directory_which from FindDirectory.h */
	_BORK(B_DESKTOP_DIRECTORY);
	_BORK(B_TRASH_DIRECTORY);

	_BORK(B_BEOS_BOOT_DIRECTORY);
	_BORK(B_BEOS_FONTS_DIRECTORY);
	_BORK(B_BEOS_LIB_DIRECTORY);
	_BORK(B_BEOS_SERVERS_DIRECTORY);
	_BORK(B_BEOS_APPS_DIRECTORY);
	_BORK(B_BEOS_BIN_DIRECTORY);
	_BORK(B_BEOS_ETC_DIRECTORY);
	_BORK(B_BEOS_DOCUMENTATION_DIRECTORY);
	_BORK(B_BEOS_PREFERENCES_DIRECTORY);
	_BORK(B_BEOS_TRANSLATORS_DIRECTORY);
	_BORK(B_BEOS_MEDIA_NODES_DIRECTORY);
	_BORK(B_BEOS_SOUNDS_DIRECTORY);
	_BORK(B_BEOS_DATA_DIRECTORY);
	// not in the declared order, so others are picked first
	_BORK(B_BEOS_ADDONS_DIRECTORY);
	_BORK(B_BEOS_SYSTEM_DIRECTORY);
	_BORK(B_BEOS_DIRECTORY);

	_BORK(B_USER_BOOT_DIRECTORY);
	_BORK(B_USER_FONTS_DIRECTORY);
	_BORK(B_USER_LIB_DIRECTORY);
	_BORK(B_USER_SETTINGS_DIRECTORY);
	_BORK(B_USER_DESKBAR_DIRECTORY);
	_BORK(B_USER_PRINTERS_DIRECTORY);
	_BORK(B_USER_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_SOUNDS_DIRECTORY);

	// Out of order again.
	_BORK(B_USER_DIRECTORY);
	_BORK(B_USER_CONFIG_DIRECTORY);
	_BORK(B_USER_ADDONS_DIRECTORY);

	_BORK(B_USER_DATA_DIRECTORY);
	_BORK(B_USER_CACHE_DIRECTORY);
	_BORK(B_USER_PACKAGES_DIRECTORY);
	_BORK(B_USER_HEADERS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_ADDONS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_BIN_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DATA_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_FONTS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_SOUNDS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DOCUMENTATION_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_LIB_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_HEADERS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DEVELOP_DIRECTORY);
	_BORK(B_USER_DEVELOP_DIRECTORY);
	_BORK(B_USER_DOCUMENTATION_DIRECTORY);
	_BORK(B_USER_SERVERS_DIRECTORY);
	_BORK(B_USER_APPS_DIRECTORY);
	_BORK(B_USER_BIN_DIRECTORY);
	_BORK(B_USER_PREFERENCES_DIRECTORY);
	_BORK(B_USER_ETC_DIRECTORY);
	_BORK(B_USER_LOG_DIRECTORY);
	_BORK(B_USER_SPOOL_DIRECTORY);
	_BORK(B_USER_VAR_DIRECTORY);

	_BORK(B_APPS_DIRECTORY);
	_BORK(B_PREFERENCES_DIRECTORY);
	_BORK(B_UTILITIES_DIRECTORY);
	_BORK(B_PACKAGE_LINKS_DIRECTORY);

	return B_OK;
}
#undef _BORK

#define _BORK(_t) \
	if (tok == #_t) { \
		err = find_directory(_t, &path); \
		if (err) return err; \
		dir->Remove(s, e - s + 1); \
		dir->Insert(path.Path(), s); \
		return B_OK; \
	} \

status_t
unescape_find_directory(BString *dir)
{
	status_t err = B_ERROR;
	int32 s, e;
	BString tok;
	BPath path;
	s = dir->FindFirst("${");
	if (s < 0)
		return B_OK;
	e = dir->FindFirst("}", s);
	if (e < 0)
		return B_OK;
	dir->CopyInto(tok, s + 2, e - s - 2);
	//printf("tok '%s'\n", tok.String());

	/* This is just the entire directory_which from FindDirectory.h */
	_BORK(B_DESKTOP_DIRECTORY);
	_BORK(B_TRASH_DIRECTORY);

	_BORK(B_BEOS_BOOT_DIRECTORY);
	_BORK(B_BEOS_FONTS_DIRECTORY);
	_BORK(B_BEOS_LIB_DIRECTORY);
	_BORK(B_BEOS_SERVERS_DIRECTORY);
	_BORK(B_BEOS_APPS_DIRECTORY);
	_BORK(B_BEOS_BIN_DIRECTORY);
	_BORK(B_BEOS_ETC_DIRECTORY);
	_BORK(B_BEOS_DOCUMENTATION_DIRECTORY);
	_BORK(B_BEOS_PREFERENCES_DIRECTORY);
	_BORK(B_BEOS_TRANSLATORS_DIRECTORY);
	_BORK(B_BEOS_MEDIA_NODES_DIRECTORY);
	_BORK(B_BEOS_SOUNDS_DIRECTORY);
	_BORK(B_BEOS_DATA_DIRECTORY);
	// not in the declared order, so others are picked first
	_BORK(B_BEOS_ADDONS_DIRECTORY);
	_BORK(B_BEOS_SYSTEM_DIRECTORY);
	_BORK(B_BEOS_DIRECTORY);

	_BORK(B_USER_BOOT_DIRECTORY);
	_BORK(B_USER_FONTS_DIRECTORY);
	_BORK(B_USER_LIB_DIRECTORY);
	_BORK(B_USER_SETTINGS_DIRECTORY);
	_BORK(B_USER_DESKBAR_DIRECTORY);
	_BORK(B_USER_PRINTERS_DIRECTORY);
	_BORK(B_USER_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_SOUNDS_DIRECTORY);

	// Out of order again.
	_BORK(B_USER_DIRECTORY);
	_BORK(B_USER_CONFIG_DIRECTORY);
	_BORK(B_USER_ADDONS_DIRECTORY);

	_BORK(B_USER_DATA_DIRECTORY);
	_BORK(B_USER_CACHE_DIRECTORY);
	_BORK(B_USER_PACKAGES_DIRECTORY);
	_BORK(B_USER_HEADERS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_ADDONS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_TRANSLATORS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_MEDIA_NODES_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_BIN_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DATA_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_FONTS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_SOUNDS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DOCUMENTATION_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_LIB_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_HEADERS_DIRECTORY);
	_BORK(B_USER_NONPACKAGED_DEVELOP_DIRECTORY);
	_BORK(B_USER_DEVELOP_DIRECTORY);
	_BORK(B_USER_DOCUMENTATION_DIRECTORY);
	_BORK(B_USER_SERVERS_DIRECTORY);
	_BORK(B_USER_APPS_DIRECTORY);
	_BORK(B_USER_BIN_DIRECTORY);
	_BORK(B_USER_PREFERENCES_DIRECTORY);
	_BORK(B_USER_ETC_DIRECTORY);
	_BORK(B_USER_LOG_DIRECTORY);
	_BORK(B_USER_SPOOL_DIRECTORY);
	_BORK(B_USER_VAR_DIRECTORY);

	_BORK(B_APPS_DIRECTORY);
	_BORK(B_PREFERENCES_DIRECTORY);
	_BORK(B_UTILITIES_DIRECTORY);
	_BORK(B_PACKAGE_LINKS_DIRECTORY);

	return B_OK;
}
#undef _BORK

// copy a file including its attributes
#define BUFF_SZ 1024*1024
status_t
copy_file(entry_ref *ref, const char *to)
{
	char *buff;
	status_t err = B_OK;
	//off_t off;
	//size_t got;
	(void)ref; (void)to;

	buff = (char *)malloc(BUFF_SZ);
	// XXX: TODO

	free(buff);
	return err;
}

int
testhook()
{
	status_t err;
	BString str("/boot/home/config/fonts/ttfonts/toto.ttf");
	err = escape_find_directory(&str);
	printf("error 0x%08lx %s\n", err, str.String());
	err = unescape_find_directory(&str);
	printf("error 0x%08lx %s\n", err, str.String());
	return 0;
}

status_t
FindRGBColor(BMessage &message, const char *name, int32 index, rgb_color *c)
{
#ifdef B_BEOS_VERSION_DANO
	return message.FindRGBColor(name, index, c);
#else
	const void *data;
	ssize_t len;
	status_t err;
	err = message.FindData(name, B_RGB_COLOR_TYPE, index, &data, &len);
	if (err < B_OK)
		return err;
	if (len > (ssize_t)sizeof(*c))
		return E2BIG;
	// Hack
	memcpy((void *)c, data, len);
	return B_OK;
#endif
}

status_t
AddRGBColor(BMessage &message, const char *name, rgb_color a_color, type_code type)
{
#ifdef B_BEOS_VERSION_DANO
	return message.AddRGBColor(name, a_color, type);
#else
	return message.AddData(name, type, &a_color, sizeof(a_color));
#endif
}

status_t
FindFont(BMessage &message, const char *name, int32 index, BFont *f)
{
#ifdef B_BEOS_VERSION_DANO
	return message.FindFlat(name, index, f);
#else
	const void *data;
	ssize_t len;
	status_t err = message.FindData(name, 'FONt', index, &data, &len);
#define DERR(e) { PRINT(("%s: err: %s\n", __FUNCTION__, strerror(e))); }
	if (err < B_OK)
		return err;
	if (len > (ssize_t)sizeof(*f))
		return E2BIG;
	// Hack: only Dano has BFont : public BFlattenable
	memcpy((void *)f, data, len);
	return B_OK;
#endif
}

status_t
AddFont(BMessage &message, const char *name, BFont *f, int32 count)
{
#ifdef B_BEOS_VERSION_DANO
	return message.AddFlat(name, f, count);
#else
	return message.AddData(name, 'FONt', (void *)&f, sizeof(f), true, count);
#endif
}

<|endoftext|>


<commit_before>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008, 2009 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

Redistribution and use of HOOMD-blue, in source and binary forms, with or
without modification, are permitted, provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions, and the following disclaimer in the documentation and/or
other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of HOOMD-blue's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR
ANY WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT 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.
*/

// $Id$
// $URL$
// Maintainer: joaander

/*! \file PDBDumpWriter.cc
    \brief Defines the PDBDumpWriter class
*/

// this file was written from scratch, but some error checks and PDB snprintf strings were copied from VMD's molfile plugin
// it is used under the following license

// University of Illinois Open Source License
// Copyright 2003 Theoretical and Computational Biophysics Group,
// All rights reserved.

// Developed by:       Theoretical and Computational Biophysics Group
//             University of Illinois at Urbana-Champaign
//            http://www.ks.uiuc.edu/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;

#include <string>
#include <fstream>
#include <iomanip>
#include <stdio.h>

#include "PDBDumpWriter.h"
#include "BondData.h"

using namespace std;
using namespace boost;

/*! \param sysdef System definition containing particle data to write
    \param base_fname Base filename to expand with **timestep**.pdb when writing
*/
PDBDumpWriter::PDBDumpWriter(boost::shared_ptr<SystemDefinition> sysdef, std::string base_fname)
        : Analyzer(sysdef), m_base_fname(base_fname), m_output_bond(false)
    {
    }

/*! \param timestep Current time step of the simulation

    analzye() constructs af file name m_base_fname.**timestep**.pdb and writes out the
    current state of the system to that file.
*/
void PDBDumpWriter::analyze(unsigned int timestep)
    {
    if (m_prof)
        m_prof->push("Dump PDB");

    ostringstream full_fname;
    string filetype = ".pdb";

    // Generate a filename with the timestep padded to ten zeros
    full_fname << m_base_fname << "." << setfill('0') << setw(10) << timestep << filetype;
    // then write the file
    writeFile(full_fname.str());

    if (m_prof)
        m_prof->pop();
    }

/*! \param fname File name to write

    Writes the current state of the system to the pdb file \a fname
*/
void PDBDumpWriter::writeFile(std::string fname)
    {
    // open the file for writing
    ofstream f(fname.c_str());
    f.exceptions ( ifstream::eofbit | ifstream::failbit | ifstream::badbit );

    if (!f.good())
        {
        cerr << endl << "***Error! Unable to open dump file for writing: " << fname << endl << endl;
        throw runtime_error("Error writting pdb dump file");
        }

    // acquire the particle data
    ParticleDataArraysConst arrays = m_pdata->acquireReadOnly();

    // get the box dimensions
    Scalar Lx,Ly,Lz;
    BoxDim box = m_pdata->getBox();
    Lx=Scalar(box.xhi-box.xlo);
    Ly=Scalar(box.yhi-box.ylo);
    Lz=Scalar(box.zhi-box.zlo);

    // start writing the heinous PDB format
    const int linesize = 82;
    char buf[linesize];

    // output the box dimensions
    snprintf(buf, linesize, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1           1\n", Lx,Ly,Lz, 90.0, 90.0, 90.0);
    f << buf;

    // write out all the atoms
    for (unsigned int i = 0; i < m_pdata->getN(); i++)
        {
        // first check that everything will fit into the PDB output
        if (arrays.x[i] < -999.9994f || arrays.x[i] > 9999.9994f || arrays.y[i] < -999.9994f || arrays.y[i] > 9999.9994f || arrays.z[i] < -999.9994f || arrays.z[i] > 9999.9994f)
            {
            cerr << "***Error! Coordinate " << arrays.x[i] << " " << arrays.y[i] << " " << arrays.z[i] << " is out of range for PDB writing" << endl << endl;
            throw runtime_error("Error writing PDB file");
            }
        // check the length of the type name
        const string &type_name = m_pdata->getNameByType(arrays.type[i]);
        if (type_name.size() > 4)
            {
            cerr << "***Error! Type " << type_name << " is too long for PDB writing" << endl << endl;
            throw runtime_error("Error writing PDB file");
            }

        // start preparing the stuff to write (copied from VMD's molfile plugin)
        char indexbuf[32];
        char residbuf[32];
        char segnamebuf[5];
        char altlocchar;

        /* XXX                                                          */
        /* if the atom or residue indices exceed the legal PDB spec, we */
        /* start emitting asterisks or hexadecimal strings rather than  */
        /* aborting.  This is not really legal, but is an accepted hack */
        /* among various other programs that deal with large PDB files  */
        /* If we run out of hexadecimal indices, then we just print     */
        /* asterisks.                                                   */
        if (i < 100000)
            {
            sprintf(indexbuf, "%5d", i);
            }
        else if (i < 1048576)
            {
            sprintf(indexbuf, "%05x", i);
            }
        else
            {
            sprintf(indexbuf, "*****");
            }

        /*if (resid < 10000) {
        sprintf(residbuf, "%4d", resid);
        } else if (resid < 65536) {
        sprintf(residbuf, "%04x", resid);
        } else {
        sprintf(residbuf, "****");
        }*/
        sprintf(residbuf, "%4d", 1);

        //altlocchar = altloc[0];
        //if (altlocchar == '\0') {
        altlocchar = ' ';
        //}

        /* make sure the segname does not overflow the format */
        sprintf(segnamebuf, "SEG ");

        snprintf(buf, linesize, "%-6s%5s %4s%c%-4s%c%4s%c   %8.3f%8.3f%8.3f%6.2f%6.2f      %-4s%2s\n", "ATOM  ", indexbuf, type_name.c_str(), altlocchar, "RES", ' ',
                 residbuf, ' ', arrays.x[i], arrays.y[i], arrays.z[i], 0.0f, 0.0f, segnamebuf, "  ");
        f << buf;
        }

    if (m_output_bond)
        {
        // error check: pdb files cannot contain bonds with 100,000 or more atom records
        if (m_pdata->getN() >= 100000)
            {
            cerr << endl << "***Error! PDB files with bonds cannot hold more than 99,999 atoms!" << endl << endl;
            throw runtime_error("Error dumping PDB file");
            }

        // grab the bond data
        shared_ptr<BondData> bond_data = m_sysdef->getBondData();
        for (unsigned int i = 0; i < bond_data->getNumBonds(); i++)
            {
            Bond bond = bond_data->getBond(i);
            snprintf(buf, linesize, "CONECT%1$5d%2$5d\n", bond.a, bond.b);
            f << buf;
            }
        }

    // release the particle data
    m_pdata->release();
    }

void export_PDBDumpWriter()
    {
    class_<PDBDumpWriter, boost::shared_ptr<PDBDumpWriter>, bases<Analyzer>, boost::noncopyable>
    ("PDBDumpWriter", init< boost::shared_ptr<SystemDefinition>, std::string >())
    .def("setOutputBond", &PDBDumpWriter::setOutputBond)
    .def("writeFile", &PDBDumpWriter::writeFile)
    ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<commit_msg>Fixed a bug where dump.pdb wrote coordinates in the wrong order. Ticket #422<commit_after>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008, 2009 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

Redistribution and use of HOOMD-blue, in source and binary forms, with or
without modification, are permitted, provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions, and the following disclaimer in the documentation and/or
other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of HOOMD-blue's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR
ANY WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT 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.
*/

// $Id$
// $URL$
// Maintainer: joaander

/*! \file PDBDumpWriter.cc
    \brief Defines the PDBDumpWriter class
*/

// this file was written from scratch, but some error checks and PDB snprintf strings were copied from VMD's molfile plugin
// it is used under the following license

// University of Illinois Open Source License
// Copyright 2003 Theoretical and Computational Biophysics Group,
// All rights reserved.

// Developed by:       Theoretical and Computational Biophysics Group
//             University of Illinois at Urbana-Champaign
//            http://www.ks.uiuc.edu/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;

#include <string>
#include <fstream>
#include <iomanip>
#include <stdio.h>

#include "PDBDumpWriter.h"
#include "BondData.h"

using namespace std;
using namespace boost;

/*! \param sysdef System definition containing particle data to write
    \param base_fname Base filename to expand with **timestep**.pdb when writing
*/
PDBDumpWriter::PDBDumpWriter(boost::shared_ptr<SystemDefinition> sysdef, std::string base_fname)
        : Analyzer(sysdef), m_base_fname(base_fname), m_output_bond(false)
    {
    }

/*! \param timestep Current time step of the simulation

    analzye() constructs af file name m_base_fname.**timestep**.pdb and writes out the
    current state of the system to that file.
*/
void PDBDumpWriter::analyze(unsigned int timestep)
    {
    if (m_prof)
        m_prof->push("Dump PDB");

    ostringstream full_fname;
    string filetype = ".pdb";

    // Generate a filename with the timestep padded to ten zeros
    full_fname << m_base_fname << "." << setfill('0') << setw(10) << timestep << filetype;
    // then write the file
    writeFile(full_fname.str());

    if (m_prof)
        m_prof->pop();
    }

/*! \param fname File name to write

    Writes the current state of the system to the pdb file \a fname
*/
void PDBDumpWriter::writeFile(std::string fname)
    {
    // open the file for writing
    ofstream f(fname.c_str());
    f.exceptions ( ifstream::eofbit | ifstream::failbit | ifstream::badbit );

    if (!f.good())
        {
        cerr << endl << "***Error! Unable to open dump file for writing: " << fname << endl << endl;
        throw runtime_error("Error writting pdb dump file");
        }

    // acquire the particle data
    ParticleDataArraysConst arrays = m_pdata->acquireReadOnly();

    // get the box dimensions
    Scalar Lx,Ly,Lz;
    BoxDim box = m_pdata->getBox();
    Lx=Scalar(box.xhi-box.xlo);
    Ly=Scalar(box.yhi-box.ylo);
    Lz=Scalar(box.zhi-box.zlo);

    // start writing the heinous PDB format
    const int linesize = 82;
    char buf[linesize];

    // output the box dimensions
    snprintf(buf, linesize, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1           1\n", Lx,Ly,Lz, 90.0, 90.0, 90.0);
    f << buf;

    // write out all the atoms
    for (unsigned int j = 0; j < m_pdata->getN(); j++)
        {
        int i;
        i= arrays.rtag[j];

        // first check that everything will fit into the PDB output
        if (arrays.x[i] < -999.9994f || arrays.x[i] > 9999.9994f || arrays.y[i] < -999.9994f || arrays.y[i] > 9999.9994f || arrays.z[i] < -999.9994f || arrays.z[i] > 9999.9994f)
            {
            cerr << "***Error! Coordinate " << arrays.x[i] << " " << arrays.y[i] << " " << arrays.z[i] << " is out of range for PDB writing" << endl << endl;
            throw runtime_error("Error writing PDB file");
            }
        // check the length of the type name
        const string &type_name = m_pdata->getNameByType(arrays.type[i]);
        if (type_name.size() > 4)
            {
            cerr << "***Error! Type " << type_name << " is too long for PDB writing" << endl << endl;
            throw runtime_error("Error writing PDB file");
            }

        // start preparing the stuff to write (copied from VMD's molfile plugin)
        char indexbuf[32];
        char residbuf[32];
        char segnamebuf[5];
        char altlocchar;

        /* XXX                                                          */
        /* if the atom or residue indices exceed the legal PDB spec, we */
        /* start emitting asterisks or hexadecimal strings rather than  */
        /* aborting.  This is not really legal, but is an accepted hack */
        /* among various other programs that deal with large PDB files  */
        /* If we run out of hexadecimal indices, then we just print     */
        /* asterisks.                                                   */
        if (i < 100000)
            {
            sprintf(indexbuf, "%5d", i);
            }
        else if (i < 1048576)
            {
            sprintf(indexbuf, "%05x", i);
            }
        else
            {
            sprintf(indexbuf, "*****");
            }

        /*if (resid < 10000) {
        sprintf(residbuf, "%4d", resid);
        } else if (resid < 65536) {
        sprintf(residbuf, "%04x", resid);
        } else {
        sprintf(residbuf, "****");
        }*/
        sprintf(residbuf, "%4d", 1);

        //altlocchar = altloc[0];
        //if (altlocchar == '\0') {
        altlocchar = ' ';
        //}

        /* make sure the segname does not overflow the format */
        sprintf(segnamebuf, "SEG ");

        snprintf(buf, linesize, "%-6s%5s %4s%c%-4s%c%4s%c   %8.3f%8.3f%8.3f%6.2f%6.2f      %-4s%2s\n", "ATOM  ", indexbuf, type_name.c_str(), altlocchar, "RES", ' ',
                 residbuf, ' ', arrays.x[i], arrays.y[i], arrays.z[i], 0.0f, 0.0f, segnamebuf, "  ");
        f << buf;
        }

    if (m_output_bond)
        {
        // error check: pdb files cannot contain bonds with 100,000 or more atom records
        if (m_pdata->getN() >= 100000)
            {
            cerr << endl << "***Error! PDB files with bonds cannot hold more than 99,999 atoms!" << endl << endl;
            throw runtime_error("Error dumping PDB file");
            }

        // grab the bond data
        shared_ptr<BondData> bond_data = m_sysdef->getBondData();
        for (unsigned int i = 0; i < bond_data->getNumBonds(); i++)
            {
            Bond bond = bond_data->getBond(i);
            snprintf(buf, linesize, "CONECT%1$5d%2$5d\n", bond.a, bond.b);
            f << buf;
            }
        }

    // release the particle data
    m_pdata->release();
    }

void export_PDBDumpWriter()
    {
    class_<PDBDumpWriter, boost::shared_ptr<PDBDumpWriter>, bases<Analyzer>, boost::noncopyable>
    ("PDBDumpWriter", init< boost::shared_ptr<SystemDefinition>, std::string >())
    .def("setOutputBond", &PDBDumpWriter::setOutputBond)
    .def("writeFile", &PDBDumpWriter::writeFile)
    ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/bencode.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;
using boost::filesystem::create_directory;
using namespace libtorrent;
using boost::tuples::ignore;

// test the maximum transfer rate
void test_rate()
{
	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48575, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49575, 50000));

	torrent_handle tor1;
	torrent_handle tor2;

	create_directory("./tmp1_transfer");
	std::ofstream file("./tmp1_transfer/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 4 * 1024 * 1024, 50);
	file.close();

	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
		, true, false, true, "_transfer", 0, &t);

	ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);

	ptime start = time_now();

	for (int i = 0; i < 40; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "up: \033[33m" << st1.upload_payload_rate / 1000000.f << "MB/s "
			<< " down: \033[32m" << st2.download_payload_rate / 1000000.f << "MB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< std::endl;

		if (st1.paused) break;
		if (tor2.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());

	time_duration dt = time_now() - start;

	std::cerr << "downloaded " << t->total_size() << " bytes "
		"in " << (total_milliseconds(dt) / 1000.f) << " seconds" << std::endl;

	std::cerr << "average download rate: " << (t->total_size() / total_milliseconds(dt))
		<< " kB/s" << std::endl;

}

void test_transfer()
{
	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48075, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49075, 50000));

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;

	create_directory("./tmp1_transfer");
	std::ofstream file("./tmp1_transfer/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024);
	file.close();

	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
		, true, false, true, "_transfer", 8 * 1024, &t);

	// set half of the pieces to priority 0
	int num_pieces = tor2.get_torrent_info().num_pieces();
	std::vector<int> priorities(num_pieces, 1);
	std::fill(priorities.begin(), priorities.begin() + num_pieces / 2, 0);
	tor2.prioritize_pieces(priorities);

	ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st1.progress * 100) << "% "
			<< st1.num_peers
			<< ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers
			<< std::endl;

		if (tor2.is_finished()) break;

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::downloading);

		test_sleep(1000);
	}

	TEST_CHECK(!tor2.is_seed());
	std::cerr << "torrent is finished (50% complete)" << std::endl;

	tor2.pause();
	alert const* a = ses2.wait_for_alert(seconds(10));
	while (a)
	{
		std::auto_ptr<alert> holder = ses2.pop_alert();
		std::cerr << "ses2: " << a->message() << std::endl;
		if (dynamic_cast<torrent_paused_alert const*>(a)) break;
		a = ses2.wait_for_alert(seconds(10));
	}

	tor2.save_resume_data();

	std::vector<char> resume_data;
	a = ses2.wait_for_alert(seconds(10));
	while (a)
	{
		std::auto_ptr<alert> holder = ses2.pop_alert();
		std::cerr << "ses2: " << a->message() << std::endl;
		if (dynamic_cast<save_resume_data_alert const*>(a))
		{
			bencode(std::back_inserter(resume_data)
				, *dynamic_cast<save_resume_data_alert const*>(a)->resume_data);
			break;
		}
		a = ses2.wait_for_alert(seconds(10));
	}

	std::cerr << "saved resume data" << std::endl;

	ses2.remove_torrent(tor2);

	std::cerr << "removed" << std::endl;

	test_sleep(1000);

	std::cout << "re-adding" << std::endl;
	add_torrent_params p;
	p.ti = t;
	p.save_path = "./tmp2_transfer";
	p.resume_data = &resume_data;
	tor2 = ses2.add_torrent(p);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	tor2.prioritize_pieces(priorities);
	std::cout << "resetting priorities" << std::endl;
	tor2.resume();

	test_sleep(1000);

	for (int i = 0; i < 5; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::finished);

		test_sleep(1000);
	}

	TEST_CHECK(!tor2.is_seed());

	std::fill(priorities.begin(), priorities.end(), 1);
	tor2.prioritize_pieces(priorities);
	std::cout << "setting priorities to 1" << std::endl;

	for (int i = 0; i < 130; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st1.progress * 100) << "% "
			<< st1.num_peers
			<< ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers
			<< std::endl;

		if (tor2.is_finished()) break;

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::downloading);

		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}

#ifdef NDEBUG
	// test rate only makes sense in release mode
	test_rate();

	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}
#endif

	test_transfer();

	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}

	return 0;
}

<commit_msg>added test that exposes bug where priorities are cleared when a torrent is force-rechecked<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/bencode.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;
using boost::filesystem::create_directory;
using namespace libtorrent;
using boost::tuples::ignore;

// test the maximum transfer rate
void test_rate()
{
	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48575, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49575, 50000));

	torrent_handle tor1;
	torrent_handle tor2;

	create_directory("./tmp1_transfer");
	std::ofstream file("./tmp1_transfer/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 4 * 1024 * 1024, 50);
	file.close();

	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
		, true, false, true, "_transfer", 0, &t);

	ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);

	ptime start = time_now();

	for (int i = 0; i < 40; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "up: \033[33m" << st1.upload_payload_rate / 1000000.f << "MB/s "
			<< " down: \033[32m" << st2.download_payload_rate / 1000000.f << "MB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< std::endl;

		if (st1.paused) break;
		if (tor2.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());

	time_duration dt = time_now() - start;

	std::cerr << "downloaded " << t->total_size() << " bytes "
		"in " << (total_milliseconds(dt) / 1000.f) << " seconds" << std::endl;

	std::cerr << "average download rate: " << (t->total_size() / total_milliseconds(dt))
		<< " kB/s" << std::endl;

}

void test_transfer()
{
	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48075, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49075, 50000));

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;

	create_directory("./tmp1_transfer");
	std::ofstream file("./tmp1_transfer/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024);
	file.close();

	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
		, true, false, true, "_transfer", 8 * 1024, &t);

	// set half of the pieces to priority 0
	int num_pieces = tor2.get_torrent_info().num_pieces();
	std::vector<int> priorities(num_pieces, 1);
	std::fill(priorities.begin(), priorities.begin() + num_pieces / 2, 0);
	tor2.prioritize_pieces(priorities);

	ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st1.progress * 100) << "% "
			<< st1.num_peers
			<< ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers
			<< std::endl;

		if (tor2.is_finished()) break;

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::downloading);

		test_sleep(1000);
	}

	TEST_CHECK(!tor2.is_seed());
	std::cerr << "torrent is finished (50% complete)" << std::endl;

	tor2.force_recheck();

	for (int i = 0; i < 10; ++i)
	{
		test_sleep(1000);
		print_alerts(ses2, "ses2");
		torrent_status st2 = tor2.status();
		std::cerr << "\033[0m" << int(st2.progress * 100) << "% " << std::endl;
		if (st2.state != torrent_status::checking_files) break;
	}

	std::vector<int> priorities2 = tor2.piece_priorities();
	TEST_CHECK(std::equal(priorities.begin(), priorities.end(), priorities2.begin()));

	for (int i = 0; i < 5; ++i)
	{
		print_alerts(ses2, "ses2");
		torrent_status st2 = tor2.status();
		std::cerr << "\033[0m" << int(st2.progress * 100) << "% " << std::endl;
		TEST_CHECK(st2.state == torrent_status::finished);
		test_sleep(1000);
	}

	tor2.pause();
	alert const* a = ses2.wait_for_alert(seconds(10));
	while (a)
	{
		std::auto_ptr<alert> holder = ses2.pop_alert();
		std::cerr << "ses2: " << a->message() << std::endl;
		if (dynamic_cast<torrent_paused_alert const*>(a)) break;
		a = ses2.wait_for_alert(seconds(10));
	}

	tor2.save_resume_data();

	std::vector<char> resume_data;
	a = ses2.wait_for_alert(seconds(10));
	while (a)
	{
		std::auto_ptr<alert> holder = ses2.pop_alert();
		std::cerr << "ses2: " << a->message() << std::endl;
		if (dynamic_cast<save_resume_data_alert const*>(a))
		{
			bencode(std::back_inserter(resume_data)
				, *dynamic_cast<save_resume_data_alert const*>(a)->resume_data);
			break;
		}
		a = ses2.wait_for_alert(seconds(10));
	}

	std::cerr << "saved resume data" << std::endl;

	ses2.remove_torrent(tor2);

	std::cerr << "removed" << std::endl;

	test_sleep(1000);

	std::cout << "re-adding" << std::endl;
	add_torrent_params p;
	p.ti = t;
	p.save_path = "./tmp2_transfer";
	p.resume_data = &resume_data;
	tor2 = ses2.add_torrent(p);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	tor2.prioritize_pieces(priorities);
	std::cout << "resetting priorities" << std::endl;
	tor2.resume();

	test_sleep(1000);

	for (int i = 0; i < 5; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::finished);

		test_sleep(1000);
	}

	TEST_CHECK(!tor2.is_seed());

	std::fill(priorities.begin(), priorities.end(), 1);
	tor2.prioritize_pieces(priorities);
	std::cout << "setting priorities to 1" << std::endl;

	for (int i = 0; i < 130; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st1.progress * 100) << "% "
			<< st1.num_peers
			<< ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers
			<< std::endl;

		if (tor2.is_finished()) break;

		TEST_CHECK(st1.state == torrent_status::seeding);
		TEST_CHECK(st2.state == torrent_status::downloading);

		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}

#ifdef NDEBUG
	// test rate only makes sense in release mode
	test_rate();

	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}
#endif

	test_transfer();

	try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
	try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}

	return 0;
}

<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Prime Generator
// Use Sieve Eratosthenes - can find all the primes within range (1, 2^31 - 1)
// Reference: http://zobayer.blogspot.in/2009/09/segmented-sieve.html
////////////////////////////////////////////////////////////////////////////////

#include <string.h>
#include <iostream>
#include <cassert>

using namespace std;

template <int MAX_RANGE>
class PrimeGenerator {
#define SQ(x) ((x)*(x))
#define MSET(x,v) memset(x,v,sizeof(x))
#define CHKC(x,n) (x[n>>6]&(1<<((n>>1)&31)))
#define SETC(x,n) (x[n>>6]|=(1<<((n>>1)&31)))
    static const int MAX = 46656; // around sqrt(2^31 - 1)
    static const int LMT = 216;  // sqrt(MAX)
    static const int LEN = 4830; // number of necessary primes (primes under MAX)
    unsigned base[MAX/64], segment[MAX_RANGE/64], primes[LEN];
public:
    PrimeGenerator() { internal_sieve(); }
    // Returns the number of primes within range [a, b] and marks them in segment[]
    // Stores all the primes in the user array (it not NULL)
    int segmented_sieve(int a, int b, int *user) {
        unsigned i, j, k, cnt = (a <= 2 && 2 <= b)? 1 : 0;
        if (a <= 2 && 2 <= b) { if (user) user[cnt - 1] = 2; }
        if (b < 2) return 0;
        if (a < 3) a = 3;
        if (a % 2 == 0) a++;
        MSET(segment, 0);
        for (i = 0; SQ(primes[i]) <= b; i++) {
            j = primes[i] * ((a + primes[i] - 1) / primes[i]);
            if (j % 2 == 0) j += primes[i];
            for (k = primes[i]<<1; j <= b; j += k)
                if (j != primes[i]) SETC(segment, (j - a));
        }
        if (b - a >= 0) {
            for(i = 0; i <= b - a; i += 2) {
                if(!CHKC(segment, i)) {
                    if (user) user[cnt] = i + a;
                    cnt++;
                }
            }
        }
        return cnt;
    }
private:
    // Generates all the necessary prime numbers and marks them in base[]
    void internal_sieve() {
        unsigned i, j, k;
        for (i = 3; i < LMT; i += 2)
            if (!CHKC(base, i)) for (j = i * i, k = i<<1; j < MAX; j += k) SETC(base, j);
        for (i = 3, j = 0; i < MAX; i += 2) if (!CHKC(base, i)) primes[j++] = i;
    }
};

int primes[1000005];

int main() {
    PrimeGenerator<10000000> pg; // range 10,000,000 for testing - might wanna change it
    int num_p = pg.segmented_sieve(1, 100, primes);
    for (int i = 0; i < num_p; i++) {
        cout << primes[i] << endl;
    }
    cout << "count: " << num_p << endl;

    assert(pg.segmented_sieve(2, 2, NULL) == 1);
    assert(pg.segmented_sieve(2, 3, NULL) == 2);
    assert(pg.segmented_sieve(5, 10, NULL) == 2);
    assert(pg.segmented_sieve(1, 10, NULL) == 4);
    assert(pg.segmented_sieve(1, 50, NULL) == 15);
    assert(pg.segmented_sieve(1, 100, NULL) == 25);
    assert(pg.segmented_sieve(1, 1000000, NULL) == 78498);
    assert(pg.segmented_sieve(1, 10000000, NULL) == 664579);
    assert(pg.segmented_sieve(2000000000, 2000000000, NULL) == 0);
    assert(pg.segmented_sieve(2000000001, 2000000001, NULL) == 0);
    assert(pg.segmented_sieve(2000000000, 2010000000, NULL) == 467612);

    return 0;
}
<commit_msg>Deleted prime generator.<commit_after><|endoftext|>


<commit_before>#include "GMSAssetResourceManagementSystem.hpp"

#include "../AssetResources/TextureInfoResource.hpp"
#include "../AssetResources/GMSRoomResource.hpp"
#include "../AssetResources/GMSObjectResource.hpp"

#include "../CommandLineOptions.hpp"

#include <KEngine/Events/OtherGraphicsEvents.hpp>
#include <KEngine/App.hpp>
#include <KEngine/Utility/FileSystemHelper.hpp>
#include <KEngine/Log/Log.hpp>

#include <SFML/Graphics/Image.hpp>

#include <algorithm>
#include <execution>
#include <filesystem>
#include <fstream>
#include <limits>
#include <utility>

namespace fs = std::filesystem;

namespace
{
    ke::Colour gmsColourStrToColour(const ke::String & p_colourStr)
    {
        assert(p_colourStr.length() == 9);
        assert(p_colourStr[0] == '#');
        ke::Colour roomColour = {
            // assume colour is in hex RGBA starting with the '#' symbol.
            static_cast<uint8_t>(std::stol(p_colourStr.substr(1, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(3, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(5, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(7, 2), nullptr, 16)) };
        return roomColour;
    }
}

namespace pf
{

    bool GMSAssetResourceManagementSystem::initialise()
    {
        ke::Log::instance()->info("Scanning assets...");
        this->loadTextureAssets();
        this->loadRoomAssets();
        this->loadObjectAssets();
        ke::Log::instance()->info("Scanning assets... DONE");
        return true;
    }

    void GMSAssetResourceManagementSystem::shutdown()
    {
    }

    void GMSAssetResourceManagementSystem::update(ke::Time elapsedTime)
    {
        KE_UNUSED(elapsedTime);
    }

    void GMSAssetResourceManagementSystem::loadTextureAssets(void)
    {
        ke::Log::instance()->info("Scanning texture assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto texturesRootDirPath = fs::path{ assetDirPath } / "textures";
        sf::Image tempImage;
        std::hash<ke::String> hasher;
        for (const auto & texDirPath : ke::FileSystemHelper::getChildPaths(texturesRootDirPath))
        {
            ke::Log::instance()->info("Discovered texture asset: {}", texDirPath.string());

            auto textureFilePaths = ke::FileSystemHelper::getFilePaths(texDirPath);
            if (textureFilePaths.size() == 1)
            {
                auto texPath = textureFilePaths[0];
                auto textureResource = std::make_shared<TextureInfoResource>();
                textureResource->setName(texPath.stem().string());
                textureResource->setTextureId(hasher(textureResource->getName()));
                textureResource->setSourcePath(texPath.string());

                // retrieve size
                bool ret = tempImage.loadFromFile(texPath.string());
                assert(ret);
                TextureInfoResource::DimensionType dimension;
                dimension.width = tempImage.getSize().x;
                dimension.height = tempImage.getSize().y;
                textureResource->setTextureSize(dimension);

                ke::App::instance()->getResourceManager()->registerResource(textureResource);
            }
            else
            {
                // ignore when there're multiple texture files in a single dir for now.
            }

        }
    }

    void GMSAssetResourceManagementSystem::loadRoomAssets(void)
    {
        ke::Log::instance()->info("Scanning GM:S room assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto gmsRoomsRootDirPath = fs::path{ assetDirPath } / "rooms";
        const auto gmsRoomPaths = ke::FileSystemHelper::getFilePaths(gmsRoomsRootDirPath);
        std::hash<ke::String> hasher;
        std::for_each(std::execution::par_unseq, std::begin(gmsRoomPaths), std::end(gmsRoomPaths), [&](const auto & gmsRoomPath) {
            ke::Log::instance()->info("Discovered GM:S room asset: {}", gmsRoomPath.string());
            auto roomResource = std::make_shared<GMSRoomResource>();
            roomResource->setName(gmsRoomPath.stem().string());
            roomResource->setSourcePath(gmsRoomPath.string());

            std::ifstream roomFileStream{ gmsRoomPath };
            ke::json roomJson;
            roomFileStream >> roomJson;

            //
            // Load general room info.
            //
            GMSRoomResource::SizeType roomSize;
            roomSize.width  = roomJson["size"]["width"].get<unsigned>();
            roomSize.height = roomJson["size"]["height"].get<unsigned>();
            roomResource->setSize(roomSize);
            roomResource->setSpeed(roomJson["speed"].get<int>());
            auto roomColourStr = roomJson["colour"].get<ke::String>();
            roomResource->setColour(::gmsColourStrToColour(roomColourStr));

            //
            // load background info
            //
            const auto & roomBackgroundsJson = roomJson["bgs"];
            for (const auto & backgroundJson : roomBackgroundsJson)
            {
                GMSRoomBackgroundInfo backgroundInfo;
                backgroundInfo.enabled    = backgroundJson["enabled"].get<bool>();
                backgroundInfo.foreground = backgroundJson["foreground"].get<bool>();
                backgroundInfo.pos        = { backgroundJson["pos"]["x"].get<int>(), -backgroundJson["pos"]["y"].get<int>() };
                backgroundInfo.tilex      = backgroundJson["tilex"].get<bool>();
                backgroundInfo.tiley      = backgroundJson["tiley"].get<bool>();
                backgroundInfo.speed      = { backgroundJson["speed"]["x"].get<int>(), -backgroundJson["speed"]["y"].get<int>() };
                backgroundInfo.stretch    = backgroundJson["stretch"].get<bool>();
                backgroundInfo.bg         = backgroundJson.value("bg", "");
                backgroundInfo.bg_hash    = hasher(backgroundInfo.bg);
                roomResource->addBackgroundInfo(backgroundInfo);
            }

            //
            // load tile instances
            //
            const auto & roomTilesJson = roomJson["tiles"];
            roomResource->tiles.reserve(roomTilesJson.size());
            for (const auto & tileJson : roomTilesJson)
            {
                pf::GMSRoomTileInstance newTile;
                newTile.instanceid = tileJson["instanceid"].get<unsigned>();

                // Here we make sure to convert the GM:S room coordinates to KEngine's world coordinates.
                // I.e. y-down to y-up.
                // Texture coordinates are the same at the moment at y-down. I.e. (0,0) at top left.
                newTile.pos       = { tileJson["pos"]["x"].get<int>(), -tileJson["pos"]["y"].get<int>() };
                newTile.bg        = tileJson["bg"].get<ke::String>();
                newTile.bg_hash   = hasher(newTile.bg);
                newTile.sourcepos = { tileJson["sourcepos"]["x"].get<int>(), tileJson["sourcepos"]["y"].get<int>() }; // sourcepos is y-down local image coordinates.
                newTile.size      = { tileJson["size"]["width"].get<int>(), tileJson["size"]["height"].get<int>() };
                newTile.scale     = { tileJson["scale"]["x"].get<float>(), tileJson["scale"]["y"].get<float>() };
                newTile.colour    = ::gmsColourStrToColour(tileJson["colour"].get<ke::String>());

                // Here convert GM:S' depth system to KEngine's depth system.
                // GM:S depth value: larger == further back.
                // KEngine depth value: larger == further in front.
                newTile.tiledepth = -tileJson["tiledepth"].get<ke::graphics::DepthType>();

                roomResource->addTile(newTile);
            }

            //
            // load object instances
            //
            const auto & roomObjsJson = roomJson["objs"];
            roomResource->objects.reserve(roomObjsJson.size());
            for (const auto & objJson : roomObjsJson)
            {
                pf::GMSRoomObjectInstance obj;
                obj.instanceid = objJson["instanceid"].get<ke::EntityId>();
                obj.obj        = objJson["obj"].get<ke::String>();
                obj.pos        = { objJson["pos"]["x"].get<int>(), -objJson["pos"]["y"].get<int>() };
                obj.scale      = { objJson["scale"]["x"].get<float>(), objJson["scale"]["y"].get<float>() };
                obj.rotation   = objJson["rotation"].get<float>();
                obj.colour     = ::gmsColourStrToColour(objJson["colour"].get<ke::String>());

                roomResource->addObject(obj);
            }

            ke::App::instance()->getResourceManager()->registerResource(roomResource);
        });
    }

    void GMSAssetResourceManagementSystem::loadObjectAssets(void)
    {
        ke::Log::instance()->info("Scanning GM:S object assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto gmsObjectRootDirPath = fs::path{ assetDirPath } / "object";
        const auto gmsObjectPaths = ke::FileSystemHelper::getFilePaths(gmsObjectRootDirPath);
        std::for_each(std::execution::par_unseq, std::begin(gmsObjectPaths), std::end(gmsObjectPaths), [&](const auto & gmsObjectPath) {
            ke::Log::instance()->info("Discovered GM:S object asset: {}", gmsObjectPath.string());

            std::ifstream objectFileStream{ gmsObjectPath };
            ke::json objectJson;
            objectFileStream >> objectJson;

            auto objectResource = std::make_shared<pf::GMSObjectResource>(fs::path(gmsObjectPath).stem().string(), gmsObjectPath.string());
            objectResource->sprite   = objectJson["sprite"].get<ke::String>();
            objectResource->visible  = objectJson["visible"].get<bool>();
            objectResource->solid    = objectJson["solid"].get<bool>();
            objectResource->depth    = objectJson["depth"].get<decltype(objectResource->depth)>();
            objectResource->persist  = objectJson["persist"].get<bool>();
            objectResource->sensor   = objectJson["sensor"].get<bool>();
            objectResource->colshape = objectJson["colshape"].get<ke::String>();

            ke::App::instance()->getResourceManager()->registerResource(objectResource);
        });
    }
}<commit_msg>handle allowing non-directory path when loading textures.<commit_after>#include "GMSAssetResourceManagementSystem.hpp"

#include "../AssetResources/TextureInfoResource.hpp"
#include "../AssetResources/GMSRoomResource.hpp"
#include "../AssetResources/GMSObjectResource.hpp"

#include "../CommandLineOptions.hpp"

#include <KEngine/Events/OtherGraphicsEvents.hpp>
#include <KEngine/App.hpp>
#include <KEngine/Utility/FileSystemHelper.hpp>
#include <KEngine/Log/Log.hpp>

#include <SFML/Graphics/Image.hpp>

#include <algorithm>
#include <execution>
#include <filesystem>
#include <fstream>
#include <limits>
#include <utility>

namespace fs = std::filesystem;

namespace
{
    ke::Colour gmsColourStrToColour(const ke::String & p_colourStr)
    {
        assert(p_colourStr.length() == 9);
        assert(p_colourStr[0] == '#');
        ke::Colour roomColour = {
            // assume colour is in hex RGBA starting with the '#' symbol.
            static_cast<uint8_t>(std::stol(p_colourStr.substr(1, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(3, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(5, 2), nullptr, 16)),
            static_cast<uint8_t>(std::stol(p_colourStr.substr(7, 2), nullptr, 16)) };
        return roomColour;
    }
}

namespace pf
{

    bool GMSAssetResourceManagementSystem::initialise()
    {
        ke::Log::instance()->info("Scanning assets...");
        this->loadTextureAssets();
        this->loadRoomAssets();
        this->loadObjectAssets();
        ke::Log::instance()->info("Scanning assets... DONE");
        return true;
    }

    void GMSAssetResourceManagementSystem::shutdown()
    {
    }

    void GMSAssetResourceManagementSystem::update(ke::Time elapsedTime)
    {
        KE_UNUSED(elapsedTime);
    }

    void GMSAssetResourceManagementSystem::loadTextureAssets(void)
    {
        ke::Log::instance()->info("Scanning texture assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto texturesRootDirPath = fs::path{ assetDirPath } / "textures";
        sf::Image tempImage;
        std::hash<ke::String> hasher;
        for (const auto & texDirPath : ke::FileSystemHelper::getChildPaths(texturesRootDirPath))
        {
            if (fs::is_directory(texDirPath))
            {
                ke::Log::instance()->info("Discovered texture asset: {}", texDirPath.string());

                auto textureFilePaths = ke::FileSystemHelper::getFilePaths(texDirPath);
                if (textureFilePaths.size() == 1)
                {
                    auto texPath = textureFilePaths[0];
                    auto textureResource = std::make_shared<TextureInfoResource>();
                    textureResource->setName(texPath.stem().string());
                    textureResource->setTextureId(hasher(textureResource->getName()));
                    textureResource->setSourcePath(texPath.string());

                    // retrieve size
                    bool ret = tempImage.loadFromFile(texPath.string());
                    assert(ret);
                    TextureInfoResource::DimensionType dimension;
                    dimension.width = tempImage.getSize().x;
                    dimension.height = tempImage.getSize().y;
                    textureResource->setTextureSize(dimension);

                    ke::App::instance()->getResourceManager()->registerResource(textureResource);
                }
                else
                {
                    // ignore when there're multiple texture files in a single dir for now.
                }
            }
        }
    }

    void GMSAssetResourceManagementSystem::loadRoomAssets(void)
    {
        ke::Log::instance()->info("Scanning GM:S room assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto gmsRoomsRootDirPath = fs::path{ assetDirPath } / "rooms";
        const auto gmsRoomPaths = ke::FileSystemHelper::getFilePaths(gmsRoomsRootDirPath);
        std::hash<ke::String> hasher;
        std::for_each(std::execution::par_unseq, std::begin(gmsRoomPaths), std::end(gmsRoomPaths), [&](const auto & gmsRoomPath) {
            ke::Log::instance()->info("Discovered GM:S room asset: {}", gmsRoomPath.string());
            auto roomResource = std::make_shared<GMSRoomResource>();
            roomResource->setName(gmsRoomPath.stem().string());
            roomResource->setSourcePath(gmsRoomPath.string());

            std::ifstream roomFileStream{ gmsRoomPath };
            ke::json roomJson;
            roomFileStream >> roomJson;

            //
            // Load general room info.
            //
            GMSRoomResource::SizeType roomSize;
            roomSize.width  = roomJson["size"]["width"].get<unsigned>();
            roomSize.height = roomJson["size"]["height"].get<unsigned>();
            roomResource->setSize(roomSize);
            roomResource->setSpeed(roomJson["speed"].get<int>());
            auto roomColourStr = roomJson["colour"].get<ke::String>();
            roomResource->setColour(::gmsColourStrToColour(roomColourStr));

            //
            // load background info
            //
            const auto & roomBackgroundsJson = roomJson["bgs"];
            for (const auto & backgroundJson : roomBackgroundsJson)
            {
                GMSRoomBackgroundInfo backgroundInfo;
                backgroundInfo.enabled    = backgroundJson["enabled"].get<bool>();
                backgroundInfo.foreground = backgroundJson["foreground"].get<bool>();
                backgroundInfo.pos        = { backgroundJson["pos"]["x"].get<int>(), -backgroundJson["pos"]["y"].get<int>() };
                backgroundInfo.tilex      = backgroundJson["tilex"].get<bool>();
                backgroundInfo.tiley      = backgroundJson["tiley"].get<bool>();
                backgroundInfo.speed      = { backgroundJson["speed"]["x"].get<int>(), -backgroundJson["speed"]["y"].get<int>() };
                backgroundInfo.stretch    = backgroundJson["stretch"].get<bool>();
                backgroundInfo.bg         = backgroundJson.value("bg", "");
                backgroundInfo.bg_hash    = hasher(backgroundInfo.bg);
                roomResource->addBackgroundInfo(backgroundInfo);
            }

            //
            // load tile instances
            //
            const auto & roomTilesJson = roomJson["tiles"];
            roomResource->tiles.reserve(roomTilesJson.size());
            for (const auto & tileJson : roomTilesJson)
            {
                pf::GMSRoomTileInstance newTile;
                newTile.instanceid = tileJson["instanceid"].get<unsigned>();

                // Here we make sure to convert the GM:S room coordinates to KEngine's world coordinates.
                // I.e. y-down to y-up.
                // Texture coordinates are the same at the moment at y-down. I.e. (0,0) at top left.
                newTile.pos       = { tileJson["pos"]["x"].get<int>(), -tileJson["pos"]["y"].get<int>() };
                newTile.bg        = tileJson["bg"].get<ke::String>();
                newTile.bg_hash   = hasher(newTile.bg);
                newTile.sourcepos = { tileJson["sourcepos"]["x"].get<int>(), tileJson["sourcepos"]["y"].get<int>() }; // sourcepos is y-down local image coordinates.
                newTile.size      = { tileJson["size"]["width"].get<int>(), tileJson["size"]["height"].get<int>() };
                newTile.scale     = { tileJson["scale"]["x"].get<float>(), tileJson["scale"]["y"].get<float>() };
                newTile.colour    = ::gmsColourStrToColour(tileJson["colour"].get<ke::String>());

                // Here convert GM:S' depth system to KEngine's depth system.
                // GM:S depth value: larger == further back.
                // KEngine depth value: larger == further in front.
                newTile.tiledepth = -tileJson["tiledepth"].get<ke::graphics::DepthType>();

                roomResource->addTile(newTile);
            }

            //
            // load object instances
            //
            const auto & roomObjsJson = roomJson["objs"];
            roomResource->objects.reserve(roomObjsJson.size());
            for (const auto & objJson : roomObjsJson)
            {
                pf::GMSRoomObjectInstance obj;
                obj.instanceid = objJson["instanceid"].get<ke::EntityId>();
                obj.obj        = objJson["obj"].get<ke::String>();
                obj.pos        = { objJson["pos"]["x"].get<int>(), -objJson["pos"]["y"].get<int>() };
                obj.scale      = { objJson["scale"]["x"].get<float>(), objJson["scale"]["y"].get<float>() };
                obj.rotation   = objJson["rotation"].get<float>();
                obj.colour     = ::gmsColourStrToColour(objJson["colour"].get<ke::String>());

                roomResource->addObject(obj);
            }

            ke::App::instance()->getResourceManager()->registerResource(roomResource);
        });
    }

    void GMSAssetResourceManagementSystem::loadObjectAssets(void)
    {
        ke::Log::instance()->info("Scanning GM:S object assets...");
        const auto assetDirPath = ke::App::getCommandLineArgValue(pf::cli::ExecAssetsPath).as<ke::String>();
        const auto gmsObjectRootDirPath = fs::path{ assetDirPath } / "object";
        const auto gmsObjectPaths = ke::FileSystemHelper::getFilePaths(gmsObjectRootDirPath);
        std::for_each(std::execution::par_unseq, std::begin(gmsObjectPaths), std::end(gmsObjectPaths), [&](const auto & gmsObjectPath) {
            ke::Log::instance()->info("Discovered GM:S object asset: {}", gmsObjectPath.string());

            std::ifstream objectFileStream{ gmsObjectPath };
            ke::json objectJson;
            objectFileStream >> objectJson;

            auto objectResource = std::make_shared<pf::GMSObjectResource>(fs::path(gmsObjectPath).stem().string(), gmsObjectPath.string());
            objectResource->sprite   = objectJson["sprite"].get<ke::String>();
            objectResource->visible  = objectJson["visible"].get<bool>();
            objectResource->solid    = objectJson["solid"].get<bool>();
            objectResource->depth    = objectJson["depth"].get<decltype(objectResource->depth)>();
            objectResource->persist  = objectJson["persist"].get<bool>();
            objectResource->sensor   = objectJson["sensor"].get<bool>();
            objectResource->colshape = objectJson["colshape"].get<ke::String>();

            ke::App::instance()->getResourceManager()->registerResource(objectResource);
        });
    }
}<|endoftext|>