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

#include <QProcess>
#include <QsLog.h>
#include <QFileInfo>
#include <QVector>

#include <trikNetwork/mailboxInterface.h>
#include <trikKernel/paths.h>
#include <trikKernel/exceptions/internalErrorException.h>
#include <trikScriptRunnerInterface.h>

#include "pythonEngineWorker.h"
#include <Python.h>
#include "PythonQtConversion.h"

using namespace trikScriptRunner;

QAtomicInt PythonEngineWorker::initCounter = 0;

static int quitFromPython(void*) {
	PyErr_SetInterrupt();
	return 0;
}

static void abortPythonInterpreter() {
	if(!Py_IsInitialized()) {
		return;
	}
	PythonQtGILScope _;
	Py_AddPendingCall(&quitFromPython, nullptr);
}

PythonEngineWorker::PythonEngineWorker(trikControl::BrickInterface &brick
		, trikNetwork::MailboxInterface * const mailbox
		)
	: mBrick(brick)
	, mScriptExecutionControl(new ScriptExecutionControl())
	, mMailbox(mailbox)
	, mState(ready)
{}

PythonEngineWorker::~PythonEngineWorker()
{
	stopScript();
	{
		// In python at least before 3.7 (3.5,3.6)
		// we need to make all pending calls before the context
		// is destroyed, otherwise python crashes
		PythonQtGILScope _;
		Py_MakePendingCalls();
		mMainContext = nullptr;
		if (mPyInterpreter) {
			Py_EndInterpreter(mPyInterpreter);
			mPyInterpreter = nullptr;
		}
	}

	if (--initCounter == 0) {
		Py_Finalize();
		PyMem_RawFree(mProgramName);
		PyMem_RawFree(mPythonPath);
		if (PythonQt::self()) {
			PythonQt::cleanup();
		}
	}
}

void PythonEngineWorker::init()
{
	if (initCounter++ == 0) {
		mProgramName = Py_DecodeLocale("trikPythonRuntime", nullptr);
		Py_SetProgramName(mProgramName);
		constexpr auto varName = "TRIK_PYTHONPATH";
		auto const &path = QProcessEnvironment::systemEnvironment().value(varName);
		if (path.isEmpty()) {
			auto const &e = QString("%1 must be set to correct value").arg(varName);
			QLOG_FATAL() << e;
			throw trikKernel::InternalErrorException(e);
		} else {
			QLOG_INFO() << varName << ":" << path;
		}

		/// TODO: Must point to local .zip file
		mPythonPath = Py_DecodeLocale(path.toStdString().data(), nullptr);
		Py_SetPath(mPythonPath);

/* uncomment for verbosity
		Py_VerboseFlag = 3;
		Py_InspectFlag = 1;
		Py_DebugFlag = 2;
// */
		Py_IsolatedFlag = 1;
		Py_BytesWarningFlag = 3;
		Py_DontWriteBytecodeFlag = 1;
		Py_NoSiteFlag = 1;
		Py_NoUserSiteDirectory = 1;

		Py_Initialize();
		PyEval_InitThreads(); // For Python < 3.7
	}

	if (!mPyInterpreter) {
//		mPyInterpreter = Py_NewInterpreter();
	}

	if (!PythonQt::self()) {
		PythonQt::setEnableThreadSupport(true);
		PythonQtGILScope _;
		PythonQt::init(PythonQt::RedirectStdOut | PythonQt::PythonAlreadyInitialized);
		connect(PythonQt::self(), &PythonQt::pythonStdErr, this, &PythonEngineWorker::updateErrorMessage);
		connect(PythonQt::self(), &PythonQt::pythonStdOut, this, &PythonEngineWorker::sendStdOutMessage);
		PythonQtRegisterListTemplateConverter(QVector, uint8_t)
		PythonQt_QtAll::init();
	}
	if (!mMainContext) {
		mMainContext = PythonQt::self()->getMainModule();
		recreateContext();
	}
	emit inited();
}

bool PythonEngineWorker::recreateContext()
{
	{
		PythonQtGILScope _;
		Py_MakePendingCalls();
		PyErr_CheckSignals();
		PyErr_Clear();
	}
	PythonQt::self()->clearError();
	return initTrik();
}

bool PythonEngineWorker::evalSystemPy()
{
	const QString systemPyPath = trikKernel::Paths::systemScriptsPath() + "system.py";

	if (!QFileInfo::exists(systemPyPath)) {
		QLOG_ERROR() << "system.py not found, path:" << systemPyPath;
		return false;
	}
	mMainContext.evalFile(systemPyPath);
	if (PythonQt::self()->hadError()) {
		QLOG_ERROR() << "Failed to eval system.py";
		return false;
	}
	return true;
}

bool PythonEngineWorker::initTrik()
{
	PythonQt_init_PyTrikControl(mMainContext);
	mMainContext.addObject("brick", &mBrick);
	mMainContext.addObject("script_cpp", mScriptExecutionControl.data());

	return evalSystemPy();
}

void PythonEngineWorker::resetBrick()
{
	QLOG_INFO() << "Stopping robot";

	if (mMailbox) {
		mMailbox->stopWaiting();
		mMailbox->clearQueue();
	}

	mBrick.reset();
}

void PythonEngineWorker::brickBeep()
{
	mBrick.playSound(trikKernel::Paths::mediaPath() + "media/beep_soft.wav");
}

void PythonEngineWorker::sendStdOutMessage(const QString &text)
{
	emit sendMessage(QString("print: %1").arg(text));
}

void PythonEngineWorker::stopScript()
{
	QMutexLocker locker(&mScriptStateMutex);

	if (mState == stopping) {
		// Already stopping, so we can do nothing.
		return;
	}

	if (mState == ready) {
		// Engine is ready for execution.
		return;
	}

	QLOG_INFO() << "PythonEngineWorker: stopping script";

	mState = stopping;

	if (QThread::currentThread() != thread()) {
		abortPythonInterpreter();
	} else {
		QLOG_FATAL() << "Attempt to abort Python from main thread.";
	}

	if (mMailbox) {
		mMailbox->stopWaiting();
	}

	mState = ready;

	/// @todo: is it actually stopped?

	QLOG_INFO() << "PythonEngineWorker: stopping complete";
}

QStringList PythonEngineWorker::knownNames() const
{
	QSet<QString> result = {"brick", "script", "threading"};
	TrikScriptRunnerInterface::Helper::collectMethodNames(result, &trikControl::BrickInterface::staticMetaObject);
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mScriptControl.metaObject());
	if (mMailbox) {
		result.insert("mailbox");
		TrikScriptRunnerInterface::Helper::collectMethodNames(result, mMailbox->metaObject());
	}
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mThreading.metaObject());
	return result.toList();
}

void PythonEngineWorker::run(const QString &script)
{
	QMutexLocker locker(&mScriptStateMutex);
	mState = starting;
	QMetaObject::invokeMethod(this, "doRun", Q_ARG(QString, script));
}

void PythonEngineWorker::doRun(const QString &script)
{
	emit startedScript("", 0);
	mErrorMessage.clear();
	/// When starting script execution (by any means), clear button states.
	mBrick.keys()->reset();
	mState = running;
	auto ok = recreateContext();
	if (!ok) {
		emit completed(mErrorMessage,0);
		return;
	}

	if (script.endsWith(".py")) {
		int fileNameStarts = script.lastIndexOf('/');
		QString pathToScript = script.left(fileNameStarts);
		mMainContext.evalScript("import sys; sys.append(" + pathToScript + ")");
	}
	mMainContext.evalScript(script);

	QLOG_INFO() << "PythonEngineWorker: evaluation ended";

	auto wasError = mState != ready && PythonQt::self()->hadError();
	mState = ready;
	if (wasError) {
		emit completed(mErrorMessage, 0);
	} else {
		emit completed("", 0);
	}
}

void PythonEngineWorker::runDirect(const QString &command)
{
	QMutexLocker locker(&mScriptStateMutex);
	QMetaObject::invokeMethod(this, "doRunDirect", Q_ARG(QString, command));
}

void PythonEngineWorker::doRunDirect(const QString &command)
{
	emit startedDirectScript(0);
	if (PythonQt::self()->hadError()) {
		PythonQt::self()->clearError();
		mErrorMessage.clear();
		recreateContext();
	}
	mMainContext.evalScript(command);
	emit completed(mErrorMessage, 0);
}

void PythonEngineWorker::updateErrorMessage(const QString &err)
{
	mErrorMessage += err;
}

void PythonEngineWorker::onScriptRequestingToQuit()
{
	throw std::logic_error("Not implemented");
}
<commit_msg>Use QFileInfo instead of primitive<commit_after>/* Copyright 2018 Iakov Kirilenko, CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <QProcess>
#include <QsLog.h>
#include <QDir>
#include <QFileInfo>
#include <QVector>

#include <trikNetwork/mailboxInterface.h>
#include <trikKernel/paths.h>
#include <trikKernel/exceptions/internalErrorException.h>
#include <trikScriptRunnerInterface.h>

#include "pythonEngineWorker.h"
#include <Python.h>
#include "PythonQtConversion.h"

using namespace trikScriptRunner;

QAtomicInt PythonEngineWorker::initCounter = 0;

static int quitFromPython(void*) {
	PyErr_SetInterrupt();
	return 0;
}

static void abortPythonInterpreter() {
	if(!Py_IsInitialized()) {
		return;
	}
	PythonQtGILScope _;
	Py_AddPendingCall(&quitFromPython, nullptr);
}

PythonEngineWorker::PythonEngineWorker(trikControl::BrickInterface &brick
		, trikNetwork::MailboxInterface * const mailbox
		)
	: mBrick(brick)
	, mScriptExecutionControl(new ScriptExecutionControl())
	, mMailbox(mailbox)
	, mState(ready)
{}

PythonEngineWorker::~PythonEngineWorker()
{
	stopScript();
	{
		// In python at least before 3.7 (3.5,3.6)
		// we need to make all pending calls before the context
		// is destroyed, otherwise python crashes
		PythonQtGILScope _;
		Py_MakePendingCalls();
		mMainContext = nullptr;
		if (mPyInterpreter) {
			Py_EndInterpreter(mPyInterpreter);
			mPyInterpreter = nullptr;
		}
	}

	if (--initCounter == 0) {
		Py_Finalize();
		PyMem_RawFree(mProgramName);
		PyMem_RawFree(mPythonPath);
		if (PythonQt::self()) {
			PythonQt::cleanup();
		}
	}
}

void PythonEngineWorker::init()
{
	if (initCounter++ == 0) {
		mProgramName = Py_DecodeLocale("trikPythonRuntime", nullptr);
		Py_SetProgramName(mProgramName);
		constexpr auto varName = "TRIK_PYTHONPATH";
		auto const &path = QProcessEnvironment::systemEnvironment().value(varName);
		if (path.isEmpty()) {
			auto const &e = QString("%1 must be set to correct value").arg(varName);
			QLOG_FATAL() << e;
			throw trikKernel::InternalErrorException(e);
		} else {
			QLOG_INFO() << varName << ":" << path;
		}

		/// TODO: Must point to local .zip file
		mPythonPath = Py_DecodeLocale(path.toStdString().data(), nullptr);
		Py_SetPath(mPythonPath);

/* uncomment for verbosity
		Py_VerboseFlag = 3;
		Py_InspectFlag = 1;
		Py_DebugFlag = 2;
// */
		Py_IsolatedFlag = 1;
		Py_BytesWarningFlag = 3;
		Py_DontWriteBytecodeFlag = 1;
		Py_NoSiteFlag = 1;
		Py_NoUserSiteDirectory = 1;

		Py_Initialize();
		PyEval_InitThreads(); // For Python < 3.7
	}

	if (!mPyInterpreter) {
//		mPyInterpreter = Py_NewInterpreter();
	}

	if (!PythonQt::self()) {
		PythonQt::setEnableThreadSupport(true);
		PythonQtGILScope _;
		PythonQt::init(PythonQt::RedirectStdOut | PythonQt::PythonAlreadyInitialized);
		connect(PythonQt::self(), &PythonQt::pythonStdErr, this, &PythonEngineWorker::updateErrorMessage);
		connect(PythonQt::self(), &PythonQt::pythonStdOut, this, &PythonEngineWorker::sendStdOutMessage);
		PythonQtRegisterListTemplateConverter(QVector, uint8_t)
		PythonQt_QtAll::init();
	}
	if (!mMainContext) {
		mMainContext = PythonQt::self()->getMainModule();
		recreateContext();
	}
	emit inited();
}

bool PythonEngineWorker::recreateContext()
{
	{
		PythonQtGILScope _;
		Py_MakePendingCalls();
		PyErr_CheckSignals();
		PyErr_Clear();
	}
	PythonQt::self()->clearError();
	return initTrik();
}

bool PythonEngineWorker::evalSystemPy()
{
	const QString systemPyPath = trikKernel::Paths::systemScriptsPath() + "system.py";

	if (!QFileInfo::exists(systemPyPath)) {
		QLOG_ERROR() << "system.py not found, path:" << systemPyPath;
		return false;
	}
	mMainContext.evalFile(systemPyPath);
	if (PythonQt::self()->hadError()) {
		QLOG_ERROR() << "Failed to eval system.py";
		return false;
	}
	return true;
}

bool PythonEngineWorker::initTrik()
{
	PythonQt_init_PyTrikControl(mMainContext);
	mMainContext.addObject("brick", &mBrick);
	mMainContext.addObject("script_cpp", mScriptExecutionControl.data());

	return evalSystemPy();
}

void PythonEngineWorker::resetBrick()
{
	QLOG_INFO() << "Stopping robot";

	if (mMailbox) {
		mMailbox->stopWaiting();
		mMailbox->clearQueue();
	}

	mBrick.reset();
}

void PythonEngineWorker::brickBeep()
{
	mBrick.playSound(trikKernel::Paths::mediaPath() + "media/beep_soft.wav");
}

void PythonEngineWorker::sendStdOutMessage(const QString &text)
{
	emit sendMessage(QString("print: %1").arg(text));
}

void PythonEngineWorker::stopScript()
{
	QMutexLocker locker(&mScriptStateMutex);

	if (mState == stopping) {
		// Already stopping, so we can do nothing.
		return;
	}

	if (mState == ready) {
		// Engine is ready for execution.
		return;
	}

	QLOG_INFO() << "PythonEngineWorker: stopping script";

	mState = stopping;

	if (QThread::currentThread() != thread()) {
		abortPythonInterpreter();
	} else {
		QLOG_FATAL() << "Attempt to abort Python from main thread.";
	}

	if (mMailbox) {
		mMailbox->stopWaiting();
	}

	mState = ready;

	/// @todo: is it actually stopped?

	QLOG_INFO() << "PythonEngineWorker: stopping complete";
}

QStringList PythonEngineWorker::knownNames() const
{
	QSet<QString> result = {"brick", "script", "threading"};
	TrikScriptRunnerInterface::Helper::collectMethodNames(result, &trikControl::BrickInterface::staticMetaObject);
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mScriptControl.metaObject());
	if (mMailbox) {
		result.insert("mailbox");
		TrikScriptRunnerInterface::Helper::collectMethodNames(result, mMailbox->metaObject());
	}
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mThreading.metaObject());
	return result.toList();
}

void PythonEngineWorker::run(const QString &script)
{
	QMutexLocker locker(&mScriptStateMutex);
	mState = starting;
	QMetaObject::invokeMethod(this, "doRun", Q_ARG(QString, script));
}

void PythonEngineWorker::doRun(const QString &script)
{
	emit startedScript("", 0);
	mErrorMessage.clear();
	/// When starting script execution (by any means), clear button states.
	mBrick.keys()->reset();
	mState = running;
	auto ok = recreateContext();
	if (!ok) {
		emit completed(mErrorMessage,0);
		return;
	}

	if (script.endsWith(".py")) {
		QFileInfo scriptInfo = QFileInfo(script);
		auto const & pathToScript = scriptInfo.dir();
		mMainContext.evalScript("import sys; sys.append(" + pathToScript.path() + ")");
	}
	mMainContext.evalScript(script);

	QLOG_INFO() << "PythonEngineWorker: evaluation ended";

	auto wasError = mState != ready && PythonQt::self()->hadError();
	mState = ready;
	if (wasError) {
		emit completed(mErrorMessage, 0);
	} else {
		emit completed("", 0);
	}
}

void PythonEngineWorker::runDirect(const QString &command)
{
	QMutexLocker locker(&mScriptStateMutex);
	QMetaObject::invokeMethod(this, "doRunDirect", Q_ARG(QString, command));
}

void PythonEngineWorker::doRunDirect(const QString &command)
{
	emit startedDirectScript(0);
	if (PythonQt::self()->hadError()) {
		PythonQt::self()->clearError();
		mErrorMessage.clear();
		recreateContext();
	}
	mMainContext.evalScript(command);
	emit completed(mErrorMessage, 0);
}

void PythonEngineWorker::updateErrorMessage(const QString &err)
{
	mErrorMessage += err;
}

void PythonEngineWorker::onScriptRequestingToQuit()
{
	throw std::logic_error("Not implemented");
}
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2020 Winlin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <srs_protocol_utility.hpp>

// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <stdlib.h>
#include <sstream>
using namespace std;

#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_consts.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_protocol_io.hpp>

/**
 * resolve the vhost in query string
 * @pram vhost, update the vhost if query contains the vhost.
 * @param app, may contains the vhost in query string format:
 *   app?vhost=request_vhost
 *   app...vhost...request_vhost
 * @param param, the query, for example, ?vhost=xxx
 */
void srs_vhost_resolve(string& vhost, string& app, string& param)
{
    // get original param
    size_t pos = 0;
    if ((pos = app.find("?")) != std::string::npos) {
        param = app.substr(pos);
    }

    // filter tcUrl
    app = srs_string_replace(app, ",", "?");
    app = srs_string_replace(app, "...", "?");
    app = srs_string_replace(app, "&&", "?");
    app = srs_string_replace(app, "&", "?");
    app = srs_string_replace(app, "=", "?");

    if (srs_string_ends_with(app, "/_definst_")) {
        app = srs_erase_last_substr(app, "/_definst_");
    }

    if ((pos = app.find("?")) != std::string::npos) {
        std::string query = app.substr(pos + 1);
        app = app.substr(0, pos);

        if ((pos = query.find("vhost?")) != std::string::npos) {
            query = query.substr(pos + 6);
            if (!query.empty()) {
                vhost = query;
            }
        }
    }

    // vhost with params.
    if ((pos = vhost.find("?")) != std::string::npos) {
        vhost = vhost.substr(0, pos);
    }

    /* others */
}

void srs_discovery_tc_url(string tcUrl, string& schema, string& host, string& vhost, string& app, string& stream, int& port, string& param)
{
    size_t pos = std::string::npos;
    std::string url = tcUrl;

    if ((pos = url.find("://")) != std::string::npos) {
        schema = url.substr(0, pos);
        url = url.substr(schema.length() + 3);
        srs_info("discovery schema=%s", schema.c_str());
    }

    if ((pos = url.find("/")) != std::string::npos) {
        host = url.substr(0, pos);
        url = url.substr(host.length() + 1);
        srs_info("discovery host=%s", host.c_str());
    }

    port = SRS_CONSTS_RTMP_DEFAULT_PORT;
    if ((pos = host.find(":")) != std::string::npos) {
        srs_parse_hostport(host, host, port);
        srs_info("discovery host=%s, port=%d", host.c_str(), port);
    }

    if (url.empty()) {
        app = SRS_CONSTS_RTMP_DEFAULT_APP;
    } else {
        app = url;
    }

    vhost = host;
    srs_vhost_resolve(vhost, app, param);
    srs_vhost_resolve(vhost, stream, param);

    // Ignore when the param only contains the default vhost.
    if (param == "?vhost="SRS_CONSTS_RTMP_DEFAULT_VHOST) {
        param = "";
    }
}

void srs_parse_query_string(string q, map<string,string>& query)
{
    // query string flags.
    static vector<string> flags;
    if (flags.empty()) {
        flags.push_back("=");
        flags.push_back(",");
        flags.push_back("&&");
        flags.push_back("&");
        flags.push_back(";");
    }

    vector<string> kvs = srs_string_split(q, flags);
    for (int i = 0; i < (int)kvs.size(); i+=2) {
        string k = kvs.at(i);
        string v = (i < (int)kvs.size() - 1)? kvs.at(i+1):"";

        query[k] = v;
    }
}

void srs_random_generate(char* bytes, int size)
{
    static bool _random_initialized = false;
    if (!_random_initialized) {
        srand(0);
        _random_initialized = true;
    }

    for (int i = 0; i < size; i++) {
        // the common value in [0x0f, 0xf0]
        bytes[i] = 0x0f + (rand() % (256 - 0x0f - 0x0f));
    }
}

string srs_generate_tc_url(string host, string vhost, string app, int port)
{
    string tcUrl = "rtmp://";

    if (vhost == SRS_CONSTS_RTMP_DEFAULT_VHOST) {
        tcUrl += host;
    } else {
        tcUrl += vhost;
    }

    if (port != SRS_CONSTS_RTMP_DEFAULT_PORT) {
        tcUrl += ":" + srs_int2str(port);
    }

    tcUrl += "/" + app;

    return tcUrl;
}

string srs_generate_stream_with_query(string host, string vhost, string stream, string param)
{
    string url = stream;
    string query = param;

    // If no vhost in param, try to append one.
    string guessVhost;
    if (query.find("vhost=") == string::npos) {
        if (vhost != SRS_CONSTS_RTMP_DEFAULT_VHOST) {
            guessVhost = vhost;
        } else if (!srs_is_ipv4(host)) {
            guessVhost = host;
        }
    }

    // Well, if vhost exists, always append in query string.
    if (!guessVhost.empty()) {
        query += "&vhost=" + guessVhost;
    }

    // Remove the start & when param is empty.
    query = srs_string_trim_start(query, "&");

    // Prefix query with ?.
    if (!query.empty() && !srs_string_starts_with(query, "?")) {
        url += "?";
    }

    // Append query to url.
    if (!query.empty()) {
        url += query;
    }

    return url;
}

template<typename T>
srs_error_t srs_do_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, T** ppmsg)
{
    srs_error_t err = srs_success;

    *ppmsg = NULL;
    T* msg = NULL;

    if (type == SrsFrameTypeAudio) {
        SrsMessageHeader header;
        header.initialize_audio(size, timestamp, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else if (type == SrsFrameTypeVideo) {
        SrsMessageHeader header;
        header.initialize_video(size, timestamp, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else if (type == SrsFrameTypeScript) {
        SrsMessageHeader header;
        header.initialize_amf0_script(size, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else {
        return srs_error_new(ERROR_STREAM_CASTER_FLV_TAG, "unknown tag=%#x", (uint8_t)type);
    }

    *ppmsg = msg;

    return err;
}

srs_error_t srs_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg)
{
    srs_error_t err = srs_success;

    // only when failed, we must free the data.
    if ((err = srs_do_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != srs_success) {
        srs_freepa(data);
        return srs_error_wrap(err, "create message");
    }

    return err;
}

srs_error_t srs_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, SrsCommonMessage** ppmsg)
{
    srs_error_t err = srs_success;

    // only when failed, we must free the data.
    if ((err = srs_do_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != srs_success) {
        srs_freepa(data);
        return srs_error_wrap(err, "create message");
    }

    return err;
}

string srs_generate_stream_url(string vhost, string app, string stream)
{
    std::string url = "";

    if (SRS_CONSTS_RTMP_DEFAULT_VHOST != vhost){
        url += vhost;
    }
    url += "/";
    url += app;
    url += "/";
    url += stream;

    return url;
}

void srs_parse_rtmp_url(string url, string& tcUrl, string& stream)
{
    size_t pos;

    if ((pos = url.rfind("/")) != string::npos) {
        stream = url.substr(pos + 1);
        tcUrl = url.substr(0, pos);
    } else {
        tcUrl = url;
    }
}

string srs_generate_rtmp_url(string server, int port, string host, string vhost, string app, string stream, string param)
{
    string tcUrl = "rtmp://" + server + ":" + srs_int2str(port) + "/"  + app;
    string streamWithQuery = srs_generate_stream_with_query(host, vhost, stream, param);
    string url = tcUrl + "/" + streamWithQuery;
    return url;
}

srs_error_t srs_write_large_iovs(ISrsProtocolReadWriter* skt, iovec* iovs, int size, ssize_t* pnwrite)
{
    srs_error_t err = srs_success;

    // the limits of writev iovs.
    // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32
    // for linux, generally it's 1024.
    static int limits = (int)sysconf(_SC_IOV_MAX);
#else
    static int limits = 1024;
#endif

    // send in a time.
    if (size < limits) {
        if ((err = skt->writev(iovs, size, pnwrite)) != srs_success) {
            return srs_error_wrap(err, "writev");
        }
        return err;
    }

    // send in multiple times.
    int cur_iov = 0;
    while (cur_iov < size) {
        int cur_count = srs_min(limits, size - cur_iov);
        if ((err = skt->writev(iovs + cur_iov, cur_count, pnwrite)) != srs_success) {
            return srs_error_wrap(err, "writev");
        }
        cur_iov += cur_count;
    }

    return err;
}

string srs_join_vector_string(vector<string>& vs, string separator)
{
    string str = "";

    for (int i = 0; i < (int)vs.size(); i++) {
        str += vs.at(i);
        if (i != (int)vs.size() - 1) {
            str += separator;
        }
    }

    return str;
}

bool srs_is_ipv4(string domain)
{
    for (int i = 0; i < (int)domain.length(); i++) {
        char ch = domain.at(i);
        if (ch == '.') {
            continue;
        }
        if (ch >= '0' && ch <= '9') {
            continue;
        }

        return false;
    }

    return true;
}

<commit_msg>修复srs_write_large_iovs中nwrite未累加的错误<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2020 Winlin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <srs_protocol_utility.hpp>

// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <stdlib.h>
#include <sstream>
using namespace std;

#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_consts.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_protocol_io.hpp>

/**
 * resolve the vhost in query string
 * @pram vhost, update the vhost if query contains the vhost.
 * @param app, may contains the vhost in query string format:
 *   app?vhost=request_vhost
 *   app...vhost...request_vhost
 * @param param, the query, for example, ?vhost=xxx
 */
void srs_vhost_resolve(string& vhost, string& app, string& param)
{
    // get original param
    size_t pos = 0;
    if ((pos = app.find("?")) != std::string::npos) {
        param = app.substr(pos);
    }

    // filter tcUrl
    app = srs_string_replace(app, ",", "?");
    app = srs_string_replace(app, "...", "?");
    app = srs_string_replace(app, "&&", "?");
    app = srs_string_replace(app, "&", "?");
    app = srs_string_replace(app, "=", "?");

    if (srs_string_ends_with(app, "/_definst_")) {
        app = srs_erase_last_substr(app, "/_definst_");
    }

    if ((pos = app.find("?")) != std::string::npos) {
        std::string query = app.substr(pos + 1);
        app = app.substr(0, pos);

        if ((pos = query.find("vhost?")) != std::string::npos) {
            query = query.substr(pos + 6);
            if (!query.empty()) {
                vhost = query;
            }
        }
    }

    // vhost with params.
    if ((pos = vhost.find("?")) != std::string::npos) {
        vhost = vhost.substr(0, pos);
    }

    /* others */
}

void srs_discovery_tc_url(string tcUrl, string& schema, string& host, string& vhost, string& app, string& stream, int& port, string& param)
{
    size_t pos = std::string::npos;
    std::string url = tcUrl;

    if ((pos = url.find("://")) != std::string::npos) {
        schema = url.substr(0, pos);
        url = url.substr(schema.length() + 3);
        srs_info("discovery schema=%s", schema.c_str());
    }

    if ((pos = url.find("/")) != std::string::npos) {
        host = url.substr(0, pos);
        url = url.substr(host.length() + 1);
        srs_info("discovery host=%s", host.c_str());
    }

    port = SRS_CONSTS_RTMP_DEFAULT_PORT;
    if ((pos = host.find(":")) != std::string::npos) {
        srs_parse_hostport(host, host, port);
        srs_info("discovery host=%s, port=%d", host.c_str(), port);
    }

    if (url.empty()) {
        app = SRS_CONSTS_RTMP_DEFAULT_APP;
    } else {
        app = url;
    }

    vhost = host;
    srs_vhost_resolve(vhost, app, param);
    srs_vhost_resolve(vhost, stream, param);

    // Ignore when the param only contains the default vhost.
    if (param == "?vhost="SRS_CONSTS_RTMP_DEFAULT_VHOST) {
        param = "";
    }
}

void srs_parse_query_string(string q, map<string,string>& query)
{
    // query string flags.
    static vector<string> flags;
    if (flags.empty()) {
        flags.push_back("=");
        flags.push_back(",");
        flags.push_back("&&");
        flags.push_back("&");
        flags.push_back(";");
    }

    vector<string> kvs = srs_string_split(q, flags);
    for (int i = 0; i < (int)kvs.size(); i+=2) {
        string k = kvs.at(i);
        string v = (i < (int)kvs.size() - 1)? kvs.at(i+1):"";

        query[k] = v;
    }
}

void srs_random_generate(char* bytes, int size)
{
    static bool _random_initialized = false;
    if (!_random_initialized) {
        srand(0);
        _random_initialized = true;
    }

    for (int i = 0; i < size; i++) {
        // the common value in [0x0f, 0xf0]
        bytes[i] = 0x0f + (rand() % (256 - 0x0f - 0x0f));
    }
}

string srs_generate_tc_url(string host, string vhost, string app, int port)
{
    string tcUrl = "rtmp://";

    if (vhost == SRS_CONSTS_RTMP_DEFAULT_VHOST) {
        tcUrl += host;
    } else {
        tcUrl += vhost;
    }

    if (port != SRS_CONSTS_RTMP_DEFAULT_PORT) {
        tcUrl += ":" + srs_int2str(port);
    }

    tcUrl += "/" + app;

    return tcUrl;
}

string srs_generate_stream_with_query(string host, string vhost, string stream, string param)
{
    string url = stream;
    string query = param;

    // If no vhost in param, try to append one.
    string guessVhost;
    if (query.find("vhost=") == string::npos) {
        if (vhost != SRS_CONSTS_RTMP_DEFAULT_VHOST) {
            guessVhost = vhost;
        } else if (!srs_is_ipv4(host)) {
            guessVhost = host;
        }
    }

    // Well, if vhost exists, always append in query string.
    if (!guessVhost.empty()) {
        query += "&vhost=" + guessVhost;
    }

    // Remove the start & when param is empty.
    query = srs_string_trim_start(query, "&");

    // Prefix query with ?.
    if (!query.empty() && !srs_string_starts_with(query, "?")) {
        url += "?";
    }

    // Append query to url.
    if (!query.empty()) {
        url += query;
    }

    return url;
}

template<typename T>
srs_error_t srs_do_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, T** ppmsg)
{
    srs_error_t err = srs_success;

    *ppmsg = NULL;
    T* msg = NULL;

    if (type == SrsFrameTypeAudio) {
        SrsMessageHeader header;
        header.initialize_audio(size, timestamp, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else if (type == SrsFrameTypeVideo) {
        SrsMessageHeader header;
        header.initialize_video(size, timestamp, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else if (type == SrsFrameTypeScript) {
        SrsMessageHeader header;
        header.initialize_amf0_script(size, stream_id);

        msg = new T();
        if ((err = msg->create(&header, data, size)) != srs_success) {
            srs_freep(msg);
            return srs_error_wrap(err, "create message");
        }
    } else {
        return srs_error_new(ERROR_STREAM_CASTER_FLV_TAG, "unknown tag=%#x", (uint8_t)type);
    }

    *ppmsg = msg;

    return err;
}

srs_error_t srs_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg)
{
    srs_error_t err = srs_success;

    // only when failed, we must free the data.
    if ((err = srs_do_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != srs_success) {
        srs_freepa(data);
        return srs_error_wrap(err, "create message");
    }

    return err;
}

srs_error_t srs_rtmp_create_msg(char type, uint32_t timestamp, char* data, int size, int stream_id, SrsCommonMessage** ppmsg)
{
    srs_error_t err = srs_success;

    // only when failed, we must free the data.
    if ((err = srs_do_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != srs_success) {
        srs_freepa(data);
        return srs_error_wrap(err, "create message");
    }

    return err;
}

string srs_generate_stream_url(string vhost, string app, string stream)
{
    std::string url = "";

    if (SRS_CONSTS_RTMP_DEFAULT_VHOST != vhost){
        url += vhost;
    }
    url += "/";
    url += app;
    url += "/";
    url += stream;

    return url;
}

void srs_parse_rtmp_url(string url, string& tcUrl, string& stream)
{
    size_t pos;

    if ((pos = url.rfind("/")) != string::npos) {
        stream = url.substr(pos + 1);
        tcUrl = url.substr(0, pos);
    } else {
        tcUrl = url;
    }
}

string srs_generate_rtmp_url(string server, int port, string host, string vhost, string app, string stream, string param)
{
    string tcUrl = "rtmp://" + server + ":" + srs_int2str(port) + "/"  + app;
    string streamWithQuery = srs_generate_stream_with_query(host, vhost, stream, param);
    string url = tcUrl + "/" + streamWithQuery;
    return url;
}

srs_error_t srs_write_large_iovs(ISrsProtocolReadWriter* skt, iovec* iovs, int size, ssize_t* pnwrite)
{
    srs_error_t err = srs_success;

    // the limits of writev iovs.
    // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32
    // for linux, generally it's 1024.
    static int limits = (int)sysconf(_SC_IOV_MAX);
#else
    static int limits = 1024;
#endif

    // send in a time.
    if (size < limits) {
        if ((err = skt->writev(iovs, size, pnwrite)) != srs_success) {
            return srs_error_wrap(err, "writev");
        }
        return err;
    }

    // send in multiple times.
    int cur_iov = 0;
    ssize_t nwrite = 0;
    while (cur_iov < size) {
        int cur_count = srs_min(limits, size - cur_iov);
        if ((err = skt->writev(iovs + cur_iov, cur_count, &nwrite)) != srs_success) {
            return srs_error_wrap(err, "writev");
        }
        cur_iov += cur_count;
        if (pnwrite) {
            *pnwrite += nwrite;
        }
    }

    return err;
}

string srs_join_vector_string(vector<string>& vs, string separator)
{
    string str = "";

    for (int i = 0; i < (int)vs.size(); i++) {
        str += vs.at(i);
        if (i != (int)vs.size() - 1) {
            str += separator;
        }
    }

    return str;
}

bool srs_is_ipv4(string domain)
{
    for (int i = 0; i < (int)domain.length(); i++) {
        char ch = domain.at(i);
        if (ch == '.') {
            continue;
        }
        if (ch >= '0' && ch <= '9') {
            continue;
        }

        return false;
    }

    return true;
}

<|endoftext|>


<commit_before>/*
    Copyright (C) 2000 by W.C.A. Wijngaards
    Copyright (C) 2000 by Andrew Zabolotny

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define CS_SYSDEF_PROVIDE_PATH
#define CS_SYSDEF_PROVIDE_GETOPT
#include "cssysdef.h"
#include "cssys/sysdriv.h"
#include "cstool/initapp.h"
#include "ivideo/fontserv.h"
#include "iutil/objreg.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "isys/plugin.h"

CS_IMPLEMENT_APPLICATION

csSystemDriver* System;

char *programversion = "0.0.1";
char *programname;

static struct option long_options[] =
{
  {"first", required_argument, 0, 'f'},
  {"glyphs", required_argument, 0, 'g'},
  {"size", required_argument, 0, 's'},
  {"output", required_argument, 0, 'o'},
  {"text", no_argument, 0, 't'},
  {"display", no_argument, 0, 'd'},
  {"help", no_argument, 0, 'h'},
  {"version", no_argument, 0, 'V'},
  {"verbose", no_argument, 0, 'v'},
  {0, no_argument, 0, 0}
};

static struct
{
  bool verbose;
  bool sourcecode;
  bool display;
  int fontsize;
  int first;
  int glyphs;
  char *output;
} opt =
{
  false,
  false,
  false,
  -1,
  0,
  256,
  NULL
};

static int lastglyph;

static int display_help ()
{
  printf ("Crystal Space font conversion/generation utility v%s\n", programversion);
  printf ("Copyright (C) 2000 by W.C.A. Wijngaards and Andrew Zabolotny\n\n");
  printf ("Usage: %s {option/s} [truetype font file] [...]\n\n", programname);
  printf ("This program allows to convert TTF font files to bitmap format CSF\n");
  printf ("which is faster to load although it is non-scalable. By default the\n");
  printf ("program will convert all the fonts given on command line to CSF.\n\n");
  printf ("  -d   --display     Display font rather than converting it\n");
  printf ("  -f#  --first=#     Start conversion at glyph # (default = 0)\n");
  printf ("  -g#  --glyphs=#    Convert just # (default = 256) glyphs of the font\n");
  printf ("  -s#  --size=#      Set font size # in points\n");
  printf ("  -o#  --output=#    Output CSF font to file #\n");
  printf ("  -t   --text        Generate text output (C++ code) rather than binary\n");
  printf ("  -h   --help        Display this help text\n");
  printf ("  -v   --verbose     Comment on what's happening\n");
  printf ("  -V   --version     Display program version\n");
  return 1;
}

static bool Display (iFontServer *fs, iFont *font)
{
  int c, l, i;
  for (c = opt.first; c < lastglyph; c++)
  {
    int w, h;
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    if (!bitmap || !w || !h)
      continue;

    printf ("---- Character:%d\n", c);
    for (l = 0; l < h; l++)
    {
      uint8 *line = bitmap + l * ((w + 7) / 8);
      for (i = 0; i < w; i++)
        printf ("%s", (line [i / 8] & (0x80 >> (i & 7))) ? "@" : ".");
      printf ("\n");
    }
  }
  font->DecRef ();
  fs->DecRef ();
  return true;
}

static bool Convert (const char *fontfile)
{
  if (opt.verbose)
    printf ("Loading font %s, size = %d\n", fontfile, opt.fontsize);

  iObjectRegistry* object_reg = System->GetObjectRegistry ();
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iFontServer *fs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_FONTSERVER, iFontServer);
  if (!fs)
  {
    printf ("Font server plugin has not been loaded.\n");
    return false;
  }

  iFont *font = fs->LoadFont (fontfile);
  if (font == NULL)
  {
    printf ("Cannot load font file %s\n", fontfile);
    return false;
  }

  if (opt.fontsize > 0)
  {
    font->SetSize (opt.fontsize);
    int oldsize = opt.fontsize;
    opt.fontsize = font->GetSize ();
    if (opt.fontsize != oldsize)
      printf ("Could not set font size %d, using size %d\n",
        oldsize, opt.fontsize);
  }
  else
    opt.fontsize = font->GetSize ();

  // max height of font
  int maxheight, maxwidth;
  font->GetMaxSize (maxwidth, maxheight);

  if (maxwidth > 255)
  {
    fprintf (stderr, "Font too large (%dx%d): CSF format supports only widths < 256\n", maxwidth, maxheight);
    return false;
  }

  if (opt.display)
    return Display (fs, font);

  char fontname [MAXPATHLEN + 1];
  char outfile [MAXPATHLEN + 1];
  csSplitPath (fontfile, NULL, 0, fontname, sizeof (fontname));
  if (fontname [0] == '*')
    strcpy (fontname, fontname + 1);
  char *dot = strchr (fontname, '.');
  if (dot) *dot = 0;
  sprintf (outfile, "%s%d.%s", fontname, opt.fontsize, opt.sourcecode ? "inc" : "csf");

  FILE *out = fopen (outfile, opt.sourcecode ? "w" : "wb");
  if (!out)
  {
    printf ("Could not open output file %s\n", outfile);
    return false;
  }

  int i, c, w, h;

  if (opt.sourcecode)
  {
    /// make a text version
    fprintf (out, "// %s.%d %dx%d font\n", fontname, opt.fontsize, maxwidth, maxheight);
    fprintf (out, "// FontDef: { \"%s%d\", %d, %d, %d, %d, font_%s%d, width_%s%d }\n",
      fontname, opt.fontsize, maxwidth, maxheight, opt.first, opt.glyphs,
      fontname, opt.fontsize, fontname, opt.fontsize);
    fprintf (out, "\n");
  }
  else
    fprintf (out, "CSF [Font=%s.%d Width=%d Height=%d First=%d Glyphs=%d]\n",
      fontname, opt.fontsize, maxwidth, maxheight, opt.first, opt.glyphs);

  int arrsize = 0;
  uint8 width [256];
  for (c = opt.first; c < lastglyph; c++)
  {
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    width [c] = (bitmap && h) ? w : 0;
    arrsize += ((width [c] + 7) / 8) * h;
  }

  // Character widths go first
  if (opt.sourcecode)
  {
    fprintf (out, "unsigned char width_%s%d [%d] =\n{\n  ",
      fontname, opt.fontsize, opt.glyphs);
    for (i = opt.first; i < lastglyph; i++)
    {
      fprintf (out, "%2d%s", width [i], (i < lastglyph - 1) ? "," : "");
      if ((i & 15) == 15)
      {
        fprintf (out, "\t// %02x..%02x\n", i - 15, i);
        if (i < lastglyph - 1)
          fprintf (out, "  ");
      }
    }
    if (opt.glyphs & 15)
      fprintf (out, "\n");
    fprintf (out, "};\n\n");
    fprintf (out, "unsigned char font_%s%d [%d] =\n{\n",
      fontname, opt.fontsize, arrsize);
  }
  else
    fwrite (width + opt.first, opt.glyphs, 1, out);

  // Output every character in turn
  for (c = opt.first; c < lastglyph; c++)
  {
    // get bitmap
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    if (opt.verbose)
    {
      if (!c) printf ("character ");
      printf ("%d%s", c, (c < lastglyph - 1) ? "," : "\n");
    }

    int bpc = ((width [c] + 7) / 8) * h;

    if (bitmap)
      if (opt.sourcecode)
      {
        fprintf (out, "  ");
        for (i = 0; i < bpc; i++)
          fprintf (out, "0x%02x%s", bitmap [i], (i >= bpc - 1) && (c >= lastglyph - 1) ? "" : ",");
        fprintf (out, "\t// %02x\n", c);
      }
      else if (width [c])
        fwrite (bitmap, bpc, 1, out);
  }

  fprintf (out, "};\n\n");
  fclose (out);
  font->DecRef ();
  fs->DecRef ();
  return true;
}

int main (int argc, char* argv[])
{
#if defined (__EMX__)	// Expand wildcards on OS/2+GCC+EMX
  _wildcard (&argc, &argv);
#endif

  // Create our main class.
  System = new csSystemDriver ();

  // Load VFS (for file management) and the TTF font server
  System->RequestPlugin ("crystalspace.kernel.vfs:" CS_FUNCID_VFS);
  System->RequestPlugin ("crystalspace.font.server.freetype:" CS_FUNCID_FONTSERVER);

  if (!System->Initialize (argc, argv, NULL))
  {
    fprintf (stderr, "Initialization error!\n");
    return -1;
  }

  if (!csInitializeApplication (System->GetObjectRegistry ()))
  {
    fprintf (stderr, "couldn't init app! (perhaps some plugins are missing?)");
    return -1;
  }

  programname = argv [0];

  int c;
  while ((c = getopt_long (argc, argv, "f:g:s:o:tdhvV", long_options, NULL)) != EOF)
    switch (c)
    {
      case '?':
        // unknown option
        return -1;
      case 'f':
        opt.first = atoi (optarg);
        if ((opt.first < 0)
         || (opt.first > 255))
        {
          fprintf (stderr, "ERROR: first glyph should be 0..255\n");
          return -2;
        }
        break;
      case 'g':
        opt.glyphs = atoi (optarg);
        if ((opt.glyphs < 1)
         || (opt.glyphs > 256))
        {
          fprintf (stderr, "ERROR: glyph count should be 1..256\n");
          return -2;
        }
        break;
      case 's':
        opt.fontsize = atoi (optarg);
        if ((opt.fontsize < 1)
         || (opt.fontsize > 1000))
        {
          fprintf (stderr, "ERROR: font size should be 1..1000\n");
          return -2;
        }
        break;
      case 'o':
        opt.output = optarg;
        break;
      case 't':
        opt.sourcecode = true;
        break;
      case 'd':
        opt.display = true;
        break;
      case 'h':
        return display_help ();
      case 'v':
        opt.verbose = true;
        break;
      case 'V':
        printf ("%s version %s\n\n", programname, programversion);
        printf ("This program is distributed in the hope that it will be useful,\n");
        printf ("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
        printf ("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
        printf ("GNU Library General Public License for more details.\n");
        return 0;
    } /* endswitch */

  if (optind >= argc)
    return display_help ();

  lastglyph = opt.first + opt.glyphs;
  if (lastglyph > 256)
  {
    fprintf (stderr, "WARNING: Last glyph = %d, limiting to 256\n", lastglyph);
    lastglyph = 256;
    opt.glyphs = 256 - opt.first;
  }

  // Interpret the non-option arguments as file names
  for (; optind < argc; ++optind)
    if (!Convert (argv [optind]))
      return -2;

  delete System;

  return 0;
}
<commit_msg>Removed NULL from Initialize.<commit_after>/*
    Copyright (C) 2000 by W.C.A. Wijngaards
    Copyright (C) 2000 by Andrew Zabolotny

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define CS_SYSDEF_PROVIDE_PATH
#define CS_SYSDEF_PROVIDE_GETOPT
#include "cssysdef.h"
#include "cssys/sysdriv.h"
#include "cstool/initapp.h"
#include "ivideo/fontserv.h"
#include "iutil/objreg.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "isys/plugin.h"

CS_IMPLEMENT_APPLICATION

csSystemDriver* System;

char *programversion = "0.0.1";
char *programname;

static struct option long_options[] =
{
  {"first", required_argument, 0, 'f'},
  {"glyphs", required_argument, 0, 'g'},
  {"size", required_argument, 0, 's'},
  {"output", required_argument, 0, 'o'},
  {"text", no_argument, 0, 't'},
  {"display", no_argument, 0, 'd'},
  {"help", no_argument, 0, 'h'},
  {"version", no_argument, 0, 'V'},
  {"verbose", no_argument, 0, 'v'},
  {0, no_argument, 0, 0}
};

static struct
{
  bool verbose;
  bool sourcecode;
  bool display;
  int fontsize;
  int first;
  int glyphs;
  char *output;
} opt =
{
  false,
  false,
  false,
  -1,
  0,
  256,
  NULL
};

static int lastglyph;

static int display_help ()
{
  printf ("Crystal Space font conversion/generation utility v%s\n", programversion);
  printf ("Copyright (C) 2000 by W.C.A. Wijngaards and Andrew Zabolotny\n\n");
  printf ("Usage: %s {option/s} [truetype font file] [...]\n\n", programname);
  printf ("This program allows to convert TTF font files to bitmap format CSF\n");
  printf ("which is faster to load although it is non-scalable. By default the\n");
  printf ("program will convert all the fonts given on command line to CSF.\n\n");
  printf ("  -d   --display     Display font rather than converting it\n");
  printf ("  -f#  --first=#     Start conversion at glyph # (default = 0)\n");
  printf ("  -g#  --glyphs=#    Convert just # (default = 256) glyphs of the font\n");
  printf ("  -s#  --size=#      Set font size # in points\n");
  printf ("  -o#  --output=#    Output CSF font to file #\n");
  printf ("  -t   --text        Generate text output (C++ code) rather than binary\n");
  printf ("  -h   --help        Display this help text\n");
  printf ("  -v   --verbose     Comment on what's happening\n");
  printf ("  -V   --version     Display program version\n");
  return 1;
}

static bool Display (iFontServer *fs, iFont *font)
{
  int c, l, i;
  for (c = opt.first; c < lastglyph; c++)
  {
    int w, h;
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    if (!bitmap || !w || !h)
      continue;

    printf ("---- Character:%d\n", c);
    for (l = 0; l < h; l++)
    {
      uint8 *line = bitmap + l * ((w + 7) / 8);
      for (i = 0; i < w; i++)
        printf ("%s", (line [i / 8] & (0x80 >> (i & 7))) ? "@" : ".");
      printf ("\n");
    }
  }
  font->DecRef ();
  fs->DecRef ();
  return true;
}

static bool Convert (const char *fontfile)
{
  if (opt.verbose)
    printf ("Loading font %s, size = %d\n", fontfile, opt.fontsize);

  iObjectRegistry* object_reg = System->GetObjectRegistry ();
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iFontServer *fs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_FONTSERVER, iFontServer);
  if (!fs)
  {
    printf ("Font server plugin has not been loaded.\n");
    return false;
  }

  iFont *font = fs->LoadFont (fontfile);
  if (font == NULL)
  {
    printf ("Cannot load font file %s\n", fontfile);
    return false;
  }

  if (opt.fontsize > 0)
  {
    font->SetSize (opt.fontsize);
    int oldsize = opt.fontsize;
    opt.fontsize = font->GetSize ();
    if (opt.fontsize != oldsize)
      printf ("Could not set font size %d, using size %d\n",
        oldsize, opt.fontsize);
  }
  else
    opt.fontsize = font->GetSize ();

  // max height of font
  int maxheight, maxwidth;
  font->GetMaxSize (maxwidth, maxheight);

  if (maxwidth > 255)
  {
    fprintf (stderr, "Font too large (%dx%d): CSF format supports only widths < 256\n", maxwidth, maxheight);
    return false;
  }

  if (opt.display)
    return Display (fs, font);

  char fontname [MAXPATHLEN + 1];
  char outfile [MAXPATHLEN + 1];
  csSplitPath (fontfile, NULL, 0, fontname, sizeof (fontname));
  if (fontname [0] == '*')
    strcpy (fontname, fontname + 1);
  char *dot = strchr (fontname, '.');
  if (dot) *dot = 0;
  sprintf (outfile, "%s%d.%s", fontname, opt.fontsize, opt.sourcecode ? "inc" : "csf");

  FILE *out = fopen (outfile, opt.sourcecode ? "w" : "wb");
  if (!out)
  {
    printf ("Could not open output file %s\n", outfile);
    return false;
  }

  int i, c, w, h;

  if (opt.sourcecode)
  {
    /// make a text version
    fprintf (out, "// %s.%d %dx%d font\n", fontname, opt.fontsize, maxwidth, maxheight);
    fprintf (out, "// FontDef: { \"%s%d\", %d, %d, %d, %d, font_%s%d, width_%s%d }\n",
      fontname, opt.fontsize, maxwidth, maxheight, opt.first, opt.glyphs,
      fontname, opt.fontsize, fontname, opt.fontsize);
    fprintf (out, "\n");
  }
  else
    fprintf (out, "CSF [Font=%s.%d Width=%d Height=%d First=%d Glyphs=%d]\n",
      fontname, opt.fontsize, maxwidth, maxheight, opt.first, opt.glyphs);

  int arrsize = 0;
  uint8 width [256];
  for (c = opt.first; c < lastglyph; c++)
  {
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    width [c] = (bitmap && h) ? w : 0;
    arrsize += ((width [c] + 7) / 8) * h;
  }

  // Character widths go first
  if (opt.sourcecode)
  {
    fprintf (out, "unsigned char width_%s%d [%d] =\n{\n  ",
      fontname, opt.fontsize, opt.glyphs);
    for (i = opt.first; i < lastglyph; i++)
    {
      fprintf (out, "%2d%s", width [i], (i < lastglyph - 1) ? "," : "");
      if ((i & 15) == 15)
      {
        fprintf (out, "\t// %02x..%02x\n", i - 15, i);
        if (i < lastglyph - 1)
          fprintf (out, "  ");
      }
    }
    if (opt.glyphs & 15)
      fprintf (out, "\n");
    fprintf (out, "};\n\n");
    fprintf (out, "unsigned char font_%s%d [%d] =\n{\n",
      fontname, opt.fontsize, arrsize);
  }
  else
    fwrite (width + opt.first, opt.glyphs, 1, out);

  // Output every character in turn
  for (c = opt.first; c < lastglyph; c++)
  {
    // get bitmap
    uint8 *bitmap = font->GetGlyphBitmap (c, w, h);
    if (opt.verbose)
    {
      if (!c) printf ("character ");
      printf ("%d%s", c, (c < lastglyph - 1) ? "," : "\n");
    }

    int bpc = ((width [c] + 7) / 8) * h;

    if (bitmap)
      if (opt.sourcecode)
      {
        fprintf (out, "  ");
        for (i = 0; i < bpc; i++)
          fprintf (out, "0x%02x%s", bitmap [i], (i >= bpc - 1) && (c >= lastglyph - 1) ? "" : ",");
        fprintf (out, "\t// %02x\n", c);
      }
      else if (width [c])
        fwrite (bitmap, bpc, 1, out);
  }

  fprintf (out, "};\n\n");
  fclose (out);
  font->DecRef ();
  fs->DecRef ();
  return true;
}

int main (int argc, char* argv[])
{
#if defined (__EMX__)	// Expand wildcards on OS/2+GCC+EMX
  _wildcard (&argc, &argv);
#endif

  // Create our main class.
  System = new csSystemDriver ();

  // Load VFS (for file management) and the TTF font server
  System->RequestPlugin ("crystalspace.kernel.vfs:" CS_FUNCID_VFS);
  System->RequestPlugin ("crystalspace.font.server.freetype:" CS_FUNCID_FONTSERVER);

  if (!System->Initialize (argc, argv))
  {
    fprintf (stderr, "Initialization error!\n");
    return -1;
  }

  if (!csInitializeApplication (System->GetObjectRegistry ()))
  {
    fprintf (stderr, "couldn't init app! (perhaps some plugins are missing?)");
    return -1;
  }

  programname = argv [0];

  int c;
  while ((c = getopt_long (argc, argv, "f:g:s:o:tdhvV", long_options, NULL)) != EOF)
    switch (c)
    {
      case '?':
        // unknown option
        return -1;
      case 'f':
        opt.first = atoi (optarg);
        if ((opt.first < 0)
         || (opt.first > 255))
        {
          fprintf (stderr, "ERROR: first glyph should be 0..255\n");
          return -2;
        }
        break;
      case 'g':
        opt.glyphs = atoi (optarg);
        if ((opt.glyphs < 1)
         || (opt.glyphs > 256))
        {
          fprintf (stderr, "ERROR: glyph count should be 1..256\n");
          return -2;
        }
        break;
      case 's':
        opt.fontsize = atoi (optarg);
        if ((opt.fontsize < 1)
         || (opt.fontsize > 1000))
        {
          fprintf (stderr, "ERROR: font size should be 1..1000\n");
          return -2;
        }
        break;
      case 'o':
        opt.output = optarg;
        break;
      case 't':
        opt.sourcecode = true;
        break;
      case 'd':
        opt.display = true;
        break;
      case 'h':
        return display_help ();
      case 'v':
        opt.verbose = true;
        break;
      case 'V':
        printf ("%s version %s\n\n", programname, programversion);
        printf ("This program is distributed in the hope that it will be useful,\n");
        printf ("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
        printf ("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
        printf ("GNU Library General Public License for more details.\n");
        return 0;
    } /* endswitch */

  if (optind >= argc)
    return display_help ();

  lastglyph = opt.first + opt.glyphs;
  if (lastglyph > 256)
  {
    fprintf (stderr, "WARNING: Last glyph = %d, limiting to 256\n", lastglyph);
    lastglyph = 256;
    opt.glyphs = 256 - opt.first;
  }

  // Interpret the non-option arguments as file names
  for (; optind < argc; ++optind)
    if (!Convert (argv [optind]))
      return -2;

  delete System;

  return 0;
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2007 by Seth Yastrov

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "csutil/scf.h"

#include "cstool/initapp.h"
#include "csutil/cmdhelp.h"
#include "imap/saverfile.h"
#include "iutil/cmdline.h"
#include "iutil/stringarray.h"
#include "ivaria/collider.h"
#include "ivideo/graph2d.h"

#include <wx/textctrl.h>

#include "ieditor/panelmanager.h"
#include "objectlist.h"
#include "auipanelmanager.h"
#include "interfacewrappermanager.h"
#include "actionmanager.h"
#include "editorobject.h"
#include "mainframe.h"

#include "editor.h"

namespace CS {
namespace EditorApp {

Editor::Editor ()
  : scfImplementationType (this), helper_meshes (0), transstatus (NOTHING)
{
}

Editor::~Editor ()
{
  delete helper_meshes;
  // Remove ourself from object registry
  object_reg->Unregister (this, "iEditor");
}

bool Editor::Initialize (iObjectRegistry* reg)
{
  object_reg = reg;

  // Check for commandline help.
  if (csCommandLineHelper::CheckHelp (object_reg))
  {
    Help ();
    return true;
  }

  selection.AttachNew (new ObjectList ());
  objects.AttachNew (new ObjectList ());

  object_reg->Register (this, "iEditor");

  panelManager.AttachNew (new AUIPanelManager (object_reg));
  interfaceManager.AttachNew (new InterfaceWrapperManager (object_reg));
  actionManager.AttachNew (new ActionManager (object_reg));

  // Create the main frame
  mainFrame = new MainFrame (wxT ("Crystal Space Editor"), wxDefaultPosition, wxSize (800, 600));
  mainFrame->Initialize (object_reg, this);

  mainFrame->Show ();

  // Initialize CS and load plugins
  if (!InitCS ())
    return false;

  mainFrame->SecondInitialize (object_reg);

  return true;
}

void Editor::Help ()
{
  csCommandLineHelper commandLineHelper;

  // Usage examples
  commandLineHelper.AddCommandLineExample ("cseditor data/castel/world");
  commandLineHelper.AddCommandLineExample ("cseditor -R=data/kwartz.zip kwartz.lib");
  commandLineHelper.AddCommandLineExample ("cseditor -R=data/seymour.zip Seymour.dae");

  // Command line options
  commandLineHelper.AddCommandLineOption
    ("R", "Real path to be mounted in VFS", csVariant (""));
  commandLineHelper.AddCommandLineOption
    ("C", "VFS directory where to find the files", csVariant ("/"));
  commandLineHelper.AddCommandLineOption
    ("L", "Load a library file (for textures/materials)", csVariant (""));

  // Printing help
  commandLineHelper.PrintApplicationHelp
    (object_reg, "cseditor", "cseditor <OPTIONS> [filename]",
     "The Crystal Space editor\n\n"
     "If provided, it will load the given file from the specified VFS directory."
     " If no VFS directory is provided then it will assume the one of the file. "
     "An additional real path can be provided to be mounted before loading the file."
     " This is useful for example to mount an archive in VFS before accessing the"
     " files in it.");
}

bool Editor::InitCS ()
{
  // Request every standard plugin except for OpenGL/WXGL canvas
  if (!csInitializer::RequestPlugins (object_reg,
        CS_REQUEST_VFS,
	CS_REQUEST_PLUGIN ("crystalspace.graphics2d.wxgl", iGraphics2D),
        CS_REQUEST_OPENGL3D,
        CS_REQUEST_ENGINE,
        CS_REQUEST_FONTSERVER,
        CS_REQUEST_IMAGELOADER,
        CS_REQUEST_LEVELLOADER,
        CS_REQUEST_REPORTER,
        CS_REQUEST_REPORTERLISTENER,
        CS_REQUEST_PLUGIN ("crystalspace.collisiondetection.opcode",
                           iCollideSystem),
        CS_REQUEST_END))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Can't initialize plugins!");
    return false;
  }

  engine = csQueryRegistry<iEngine> (object_reg);
  if (!engine)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate 3D engine!");
    return false;
  }
  engine->SetSaveableFlag(true);

  if (!csInitializer::RequestPlugins(object_reg,
    CS_REQUEST_LEVELSAVER,
    CS_REQUEST_END))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to initialize iSaver plugin!");
    return false;
  }

  // Load plugins
  LoadPlugins ();

  // Open the main system. This will open all the previously loaded plug-ins.
  if (!csInitializer::OpenApplication (object_reg))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Error opening system!");
    return false;
  }

  vfs = csQueryRegistry<iVFS> (object_reg);
  if (!vfs)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iVFS plugin!");
    return false;
  }

  loader = csQueryRegistry<iThreadedLoader> (object_reg);
  if (!loader)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iThreadedLoader plugin!");
    return false;
  }

  saver = csQueryRegistry<iSaver> (object_reg);
  if (!saver)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iSaver plugin!");
    return false;
  }

  mainCollection = engine->CreateCollection ("Main collection");

  // Analyze the command line arguments
  csRef<iCommandLineParser> cmdline =
    csQueryRegistry<iCommandLineParser> (object_reg);

  const char* libname;
  for (int i = 0; (libname = cmdline->GetOption ("L", i)); i++)
    mainFrame->PushLibraryFile ("", libname);

  const char* realPath = cmdline->GetOption ("R");
  if (realPath)
  {
    vfs->Mount ("/tmp/viewmesh", realPath);
    //vfs->ChDir ("/tmp/viewmesh");
  }

  csString filename = cmdline->GetName (0);
  csString vfsDir = cmdline->GetOption ("C");
  if (vfsDir.IsEmpty ())
    vfsDir = realPath;

  if (vfsDir.IsEmpty () && filename)
  {
    size_t index = filename.FindLast ('/');
    if (index != (size_t) -1)
    {
      vfsDir = filename.Slice (0, index);
      filename = filename.Slice (index + 1);
    }
  }

  if (filename)
    mainFrame->PushMapFile (vfsDir, filename, false);

/*
  if (!vfsDir.IsEmpty())
  {
    if (!vfs->ChDir (vfsDir))
    {
      ReportError("Cannot change to path: %s\n", vfsDir.GetData ());
    }
    else
    {
      // Update StdDlg path.
      CEGUI::WindowManager* winMgr = cegui->GetWindowManagerPtr ();
      CEGUI::Window* window = winMgr->getWindow("StdDlg/Path");
      window->setProperty("Text", vfs->GetCwd());
      StdDlgUpdateLists(vfs->GetCwd());
    }
  }
*/
  return true;
}

void Editor::LoadPlugins ()
{
  // TODO: Add additional plugin directories to scan, through settings system?
  csRef<iStringArray> pluginClasses =
    iSCF::SCF->QueryClassList ("crystalspace.editor.plugin.");
  if (pluginClasses.IsValid())
  {
    csRef<iPluginManager> plugmgr =
      csQueryRegistry<iPluginManager> (object_reg);
    for (size_t i = 0; i < pluginClasses->GetSize (); i++)
    {
      const char* className = pluginClasses->Get (i);
      csRef<iComponent> c (plugmgr->LoadPluginInstance (className,
        iPluginManager::lpiInitialize | iPluginManager::lpiReportErrors
          | iPluginManager::lpiLoadDependencies));
      csRef<iBase> b = scfQueryInterface<iBase> (c);

      csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY,
        "crystalspace.application.editor", "Attempt to load plugin '%s' %s",
        className, b ? "successful" : "failed");
      if (b) b->DecRef ();
    }
  };
}

csPtr<iProgressMeter> Editor::GetProgressMeter ()
{
  return mainFrame->GetProgressMeter ();
}

iThreadReturn* Editor::LoadMapFile (const char* path, const char* filename, bool clearEngine)
{
  printf ("Editor::LoadMapFile %s %s\n", path, filename);
  vfs->ChDir (path);

  csRef<iThreadReturn> loadingResult =
    loader->LoadMapFile (vfs->GetCwd (), filename, clearEngine, mainCollection);
  return loadingResult;
}

iThreadReturn* Editor::LoadLibraryFile (const char* path, const char* filename)
{
  vfs->ChDir (path);

  iCollection* collection = engine->CreateCollection ("loading_collection");

  csRef<iThreadReturn> loadingResult =
    loader->LoadLibraryFile (vfs->GetCwd (), filename, collection);
  return loadingResult;
}

void Editor::SaveMapFile (const char* path, const char* filename)
{
  vfs->ChDir (path);

  saver->SaveCollectionFile (mainCollection, filename, CS_SAVER_FILE_WORLD);
}

void Editor::FireMapLoaded (const char* path, const char* filename)
{
    //engine->Prepare ();
  csRef<iProgressMeter> progressMeter = GetProgressMeter ();
  engine->Prepare (progressMeter);

  // TODO: Remove me. I'm only here to test the relighting progress gauge.
  //engine->SetLightingCacheMode (0);

  // Notify map listeners
  csRefArray<iMapListener>::Iterator it = mapListeners.GetIterator ();
  while (it.HasNext ())
  {
    it.Next ()->OnMapLoaded (path, filename);
  }
}

void Editor::FireLibraryLoaded (const char* path, const char* filename)
{
  // Notify map listeners
  csRefArray<iMapListener>::Iterator it = mapListeners.GetIterator ();
  while (it.HasNext ())
  {
    it.Next ()->OnLibraryLoaded (path, filename, nullptr/*collection*/);
  }
}

void Editor::AddMapListener (iMapListener* listener)
{
  mapListeners.Push (listener);
}

void Editor::RemoveMapListener (iMapListener* listener)
{
  mapListeners.Delete (listener);
}

csPtr<iEditorObject> Editor::CreateEditorObject (iBase* object, wxBitmap* icon)
{
  return csPtr<iEditorObject> (new EditorObject (object_reg, object, icon));
}

iObjectList* Editor::GetSelection ()
{
  return selection;
}

iObjectList* Editor::GetObjects ()
{
  return objects;
}

void Editor::SetHelperMeshes (csArray<csSimpleRenderMesh>* helpers)
{
  delete helper_meshes;
  helper_meshes = helpers;
}
csArray<csSimpleRenderMesh>* Editor::GetHelperMeshes ()
{
  return helper_meshes;
}

void Editor::SetTransformStatus (TransformStatus status)
{
  transstatus = status;
}
Editor::TransformStatus Editor::GetTransformStatus ()
{
  return transstatus;
}

} // namespace EditorApp
} // namespace CS
<commit_msg>Fixed loading of library files & collection management<commit_after>/*
    Copyright (C) 2007 by Seth Yastrov

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "csutil/scf.h"

#include "cstool/initapp.h"
#include "csutil/cmdhelp.h"
#include "imap/saverfile.h"
#include "iutil/cmdline.h"
#include "iutil/stringarray.h"
#include "ivaria/collider.h"
#include "ivideo/graph2d.h"

#include <wx/textctrl.h>

#include "ieditor/panelmanager.h"
#include "objectlist.h"
#include "auipanelmanager.h"
#include "interfacewrappermanager.h"
#include "actionmanager.h"
#include "editorobject.h"
#include "mainframe.h"

#include "editor.h"

namespace CS {
namespace EditorApp {

Editor::Editor ()
  : scfImplementationType (this), helper_meshes (0), transstatus (NOTHING)
{
}

Editor::~Editor ()
{
  delete helper_meshes;
  // Remove ourself from object registry
  object_reg->Unregister (this, "iEditor");
}

bool Editor::Initialize (iObjectRegistry* reg)
{
  object_reg = reg;

  // Check for commandline help.
  if (csCommandLineHelper::CheckHelp (object_reg))
  {
    Help ();
    return true;
  }

  selection.AttachNew (new ObjectList ());
  objects.AttachNew (new ObjectList ());

  object_reg->Register (this, "iEditor");

  panelManager.AttachNew (new AUIPanelManager (object_reg));
  interfaceManager.AttachNew (new InterfaceWrapperManager (object_reg));
  actionManager.AttachNew (new ActionManager (object_reg));

  // Create the main frame
  mainFrame = new MainFrame (wxT ("Crystal Space Editor"), wxDefaultPosition, wxSize (800, 600));
  mainFrame->Initialize (object_reg, this);

  mainFrame->Show ();

  // Initialize CS and load plugins
  if (!InitCS ())
    return false;

  mainFrame->SecondInitialize (object_reg);

  return true;
}

void Editor::Help ()
{
  csCommandLineHelper commandLineHelper;

  // Usage examples
  commandLineHelper.AddCommandLineExample ("cseditor data/castel/world");
  commandLineHelper.AddCommandLineExample ("cseditor -R=data/kwartz.zip kwartz.lib");
  commandLineHelper.AddCommandLineExample ("cseditor -R=data/seymour.zip Seymour.dae");

  // Command line options
  commandLineHelper.AddCommandLineOption
    ("R", "Real path to be mounted in VFS", csVariant (""));
  commandLineHelper.AddCommandLineOption
    ("C", "VFS directory where to find the files", csVariant ("/"));
  commandLineHelper.AddCommandLineOption
    ("L", "Load a library file (for textures/materials)", csVariant (""));

  // Printing help
  commandLineHelper.PrintApplicationHelp
    (object_reg, "cseditor", "cseditor <OPTIONS> [filename]",
     "The Crystal Space editor\n\n"
     "If provided, it will load the given file from the specified VFS directory."
     " If no VFS directory is provided then it will assume the one of the file. "
     "An additional real path can be provided to be mounted before loading the file."
     " This is useful for example to mount an archive in VFS before accessing the"
     " files in it.");
}

bool Editor::InitCS ()
{
  // Request every standard plugin except for OpenGL/WXGL canvas
  if (!csInitializer::RequestPlugins (object_reg,
        CS_REQUEST_VFS,
	CS_REQUEST_PLUGIN ("crystalspace.graphics2d.wxgl", iGraphics2D),
        CS_REQUEST_OPENGL3D,
        CS_REQUEST_ENGINE,
        CS_REQUEST_FONTSERVER,
        CS_REQUEST_IMAGELOADER,
        CS_REQUEST_LEVELLOADER,
        CS_REQUEST_REPORTER,
        CS_REQUEST_REPORTERLISTENER,
        CS_REQUEST_PLUGIN ("crystalspace.collisiondetection.opcode",
                           iCollideSystem),
        CS_REQUEST_END))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Can't initialize plugins!");
    return false;
  }

  engine = csQueryRegistry<iEngine> (object_reg);
  if (!engine)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate 3D engine!");
    return false;
  }
  engine->SetSaveableFlag(true);

  if (!csInitializer::RequestPlugins(object_reg,
    CS_REQUEST_LEVELSAVER,
    CS_REQUEST_END))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to initialize iSaver plugin!");
    return false;
  }

  // Load plugins
  LoadPlugins ();

  // Open the main system. This will open all the previously loaded plug-ins.
  if (!csInitializer::OpenApplication (object_reg))
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Error opening system!");
    return false;
  }

  vfs = csQueryRegistry<iVFS> (object_reg);
  if (!vfs)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iVFS plugin!");
    return false;
  }

  loader = csQueryRegistry<iThreadedLoader> (object_reg);
  if (!loader)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iThreadedLoader plugin!");
    return false;
  }

  saver = csQueryRegistry<iSaver> (object_reg);
  if (!saver)
  {
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR,
              "crystalspace.application.editor",
              "Failed to locate iSaver plugin!");
    return false;
  }

  mainCollection = engine->CreateCollection ("Main collection");

  // Analyze the command line arguments
  csRef<iCommandLineParser> cmdline =
    csQueryRegistry<iCommandLineParser> (object_reg);

  const char* libname;
  for (int i = 0; (libname = cmdline->GetOption ("L", i)); i++)
    mainFrame->PushLibraryFile ("", libname);

  const char* realPath = cmdline->GetOption ("R");
  if (realPath)
  {
    vfs->Mount ("/tmp/viewmesh", realPath);
    //vfs->ChDir ("/tmp/viewmesh");
  }

  csString filename = cmdline->GetName (0);
  csString vfsDir = cmdline->GetOption ("C");
  if (vfsDir.IsEmpty ())
    vfsDir = realPath;

  if (vfsDir.IsEmpty () && filename)
  {
    size_t index = filename.FindLast ('/');
    if (index != (size_t) -1)
    {
      vfsDir = filename.Slice (0, index);
      filename = filename.Slice (index + 1);
    }
  }

  if (filename)
    mainFrame->PushMapFile (vfsDir, filename, false);

  return true;
}

void Editor::LoadPlugins ()
{
  // TODO: Add additional plugin directories to scan, through settings system?
  csRef<iStringArray> pluginClasses =
    iSCF::SCF->QueryClassList ("crystalspace.editor.plugin.");
  if (pluginClasses.IsValid())
  {
    csRef<iPluginManager> plugmgr =
      csQueryRegistry<iPluginManager> (object_reg);
    for (size_t i = 0; i < pluginClasses->GetSize (); i++)
    {
      const char* className = pluginClasses->Get (i);
      csRef<iComponent> c (plugmgr->LoadPluginInstance (className,
        iPluginManager::lpiInitialize | iPluginManager::lpiReportErrors
          | iPluginManager::lpiLoadDependencies));
      csRef<iBase> b = scfQueryInterface<iBase> (c);

      csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY,
        "crystalspace.application.editor", "Attempt to load plugin '%s' %s",
        className, b ? "successful" : "failed");
      if (b) b->DecRef ();
    }
  };
}

csPtr<iProgressMeter> Editor::GetProgressMeter ()
{
  return mainFrame->GetProgressMeter ();
}

iThreadReturn* Editor::LoadMapFile (const char* path, const char* filename, bool clearEngine)
{
  vfs->ChDir (path);

  if (clearEngine)
  {
    engine->RemoveCollection (mainCollection);
    mainCollection = engine->CreateCollection ("Main collection");
  }

  csRef<iThreadReturn> loadingResult =
    loader->LoadMapFile (vfs->GetCwd (), filename, clearEngine, mainCollection);
  return loadingResult;
}

iThreadReturn* Editor::LoadLibraryFile (const char* path, const char* filename)
{
  vfs->ChDir (path);

  csRef<iThreadReturn> loadingResult =
    loader->LoadLibraryFile (vfs->GetCwd (), filename, mainCollection);
  return loadingResult;
}

void Editor::SaveMapFile (const char* path, const char* filename)
{
  vfs->ChDir (path);

  saver->SaveCollectionFile (mainCollection, filename, CS_SAVER_FILE_WORLD);
}

void Editor::FireMapLoaded (const char* path, const char* filename)
{
  csRef<iProgressMeter> progressMeter = GetProgressMeter ();
  engine->Prepare (progressMeter);

  // TODO: Remove me. I'm only here to test the relighting progress gauge.
  //engine->SetLightingCacheMode (0);

  // Notify map listeners
  csRefArray<iMapListener>::Iterator it = mapListeners.GetIterator ();
  while (it.HasNext ())
  {
    it.Next ()->OnMapLoaded (path, filename);
  }
}

void Editor::FireLibraryLoaded (const char* path, const char* filename)
{
  // Notify map listeners
  csRefArray<iMapListener>::Iterator it = mapListeners.GetIterator ();
  while (it.HasNext ())
  {
    it.Next ()->OnLibraryLoaded (path, filename, mainCollection);
  }
}

void Editor::AddMapListener (iMapListener* listener)
{
  mapListeners.Push (listener);
}

void Editor::RemoveMapListener (iMapListener* listener)
{
  mapListeners.Delete (listener);
}

csPtr<iEditorObject> Editor::CreateEditorObject (iBase* object, wxBitmap* icon)
{
  return csPtr<iEditorObject> (new EditorObject (object_reg, object, icon));
}

iObjectList* Editor::GetSelection ()
{
  return selection;
}

iObjectList* Editor::GetObjects ()
{
  return objects;
}

void Editor::SetHelperMeshes (csArray<csSimpleRenderMesh>* helpers)
{
  delete helper_meshes;
  helper_meshes = helpers;
}
csArray<csSimpleRenderMesh>* Editor::GetHelperMeshes ()
{
  return helper_meshes;
}

void Editor::SetTransformStatus (TransformStatus status)
{
  transstatus = status;
}
Editor::TransformStatus Editor::GetTransformStatus ()
{
  return transstatus;
}

} // namespace EditorApp
} // namespace CS
<|endoftext|>


<commit_before>// Copyright (c) 2012, Sergey Zolotarev
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 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 <cstddef>
#include <map>

#include "jit.h"
#include "jump-x86.h"
#include "plugin.h"
#include "pluginversion.h"

using namespace jit;

typedef void (*logprintf_t)(const char *format, ...);

static logprintf_t logprintf;

static std::map<AMX*, JIT*> jits;

// This implementation of amx_GetAddr can accept ANY amx_addr, even out of the data section.
static int AMXAPI amx_GetAddr_JIT(AMX *amx, cell amx_addr, cell **phys_addr) {
	AMX_HEADER *hdr = reinterpret_cast<AMX_HEADER*>(amx->base);
	*phys_addr = reinterpret_cast<cell*>(amx->base + hdr->dat + amx_addr);
	return AMX_ERR_NONE;
}

// amx_Exec_JIT compiles a public function (if needed) and runs the generated JIT code.
static int AMXAPI amx_Exec_JIT(AMX *amx, cell *retval, int index) {
	if (index >= -1) {
		std::map<AMX*, JIT*>::iterator iterator = jits.find(amx);
		if (iterator != jits.end()) {
			return iterator->second->CallPublicFunction(index, retval);
		}
	}
	return AMX_ERR_NONE;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	new JumpX86(((void**)ppData[PLUGIN_DATA_AMX_EXPORTS])[PLUGIN_AMX_EXPORT_Exec], (void*)amx_Exec_JIT);
	new JumpX86(((void**)ppData[PLUGIN_DATA_AMX_EXPORTS])[PLUGIN_AMX_EXPORT_GetAddr], (void*)amx_GetAddr_JIT);

	logprintf("  JIT plugin v%s is OK.", PLUGIN_VERSION_STRING);

	return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload() {
	// nothing
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	JIT *jit = new JIT(amx);
	jits.insert(std::make_pair(amx, jit));
	return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	JIT *jit = jits[amx];
	jits.erase(amx);
	delete jit;
	return AMX_ERR_NONE;
}
<commit_msg>Fix Linux crash because of amx_Exec() being called before AmxLoad<commit_after>// Copyright (c) 2012, Sergey Zolotarev
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 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 <cstddef>
#include <map>

#include "jit.h"
#include "jump-x86.h"
#include "plugin.h"
#include "pluginversion.h"

using namespace jit;

typedef void (*logprintf_t)(const char *format, ...);
static logprintf_t logprintf;

typedef std::map<AMX*, JIT*> JITMap;
static JITMap jit_map;

static JIT *GetJIT(AMX *amx) {
	JITMap::const_iterator it = jit_map.find(amx);
	if (it == jit_map.end()) {
		JIT *jit = new JIT(amx);
		jit_map.insert(std::make_pair(amx, jit));
		return jit;
	} else {
		return it->second;
	}
}

static void DeleteJIT(AMX *amx) {
	JITMap::iterator it = jit_map.find(amx);
	if (it != jit_map.end()) {
		jit_map.erase(it);
		delete it->second;
	}
}

// This implementation of amx_GetAddr can accept ANY amx_addr, even out of the data section.
static int AMXAPI amx_GetAddr_JIT(AMX *amx, cell amx_addr, cell **phys_addr) {
	AMX_HEADER *hdr = reinterpret_cast<AMX_HEADER*>(amx->base);
	*phys_addr = reinterpret_cast<cell*>(amx->base + hdr->dat + amx_addr);
	return AMX_ERR_NONE;
}

// amx_Exec_JIT compiles a public function (if needed) and runs the generated JIT code.
static int AMXAPI amx_Exec_JIT(AMX *amx, cell *retval, int index) {
	if (index >= -1) {
		GetJIT(amx)->CallPublicFunction(index, retval);
	}
	return AMX_ERR_NONE;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	new JumpX86(((void**)ppData[PLUGIN_DATA_AMX_EXPORTS])[PLUGIN_AMX_EXPORT_Exec], (void*)amx_Exec_JIT);
	new JumpX86(((void**)ppData[PLUGIN_DATA_AMX_EXPORTS])[PLUGIN_AMX_EXPORT_GetAddr], (void*)amx_GetAddr_JIT);

	logprintf("  JIT plugin v%s is OK.", PLUGIN_VERSION_STRING);

	return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload() {
	// nothing
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	DeleteJIT(amx);
	return AMX_ERR_NONE;
}
<|endoftext|>