<commit_before>/**
 * @file		Encode.cpp
 * @author  	Sebastian Ritterbusch <ospac@ritterbusch.de>
 * @version 	1.0
 * @date		29.3.2016
 * @copyright	MIT License (see LICENSE file)
 * @brief 		Encoding to various formats using external tools
 */

#include <unistd.h>
#include <stdlib.h>
#include <sstream>

#include "Encode.h"
#include "Wave.h"
#include "Log.h"

int Encode::lame(Channels &c,
					std::string filename,
					QualitySetting quality,
					std::string title,
					std::string artist,
					std::string album,
					std::string comment,
					std::string image,
					std::string category,
					std::string episode,
					std::string year)
{

	if(c.size()<1)
		return 0;

	std::string tempfile=filename+".wav";

	Wave::save(tempfile,c);

	unsigned rate=c[0].samplerate();
	if(rate>44100)
		rate=44100;

	unsigned halfrate=rate/2;

	std::string Vrate="9";
	switch(quality)
	{
	case LOW:
		Vrate="9";
		break;
	case STANDARD:
		Vrate="8.5";
		break;
	case HIGH:
		Vrate="5";
		break;
	case INSANE:
		Vrate="1";
		break;
	}

	std::stringstream stream;

	stream << "lame -V" << Vrate
		   << " -q0"
		   << " --lowpass "<< halfrate
		   << " --resample "<< rate;

	if(title!="")
		stream << " --tt \""<<title<<"\"";
	if(artist!="")
		stream << " --ta \""<<artist<<"\"";
	if(comment!="")
		stream << " --tc \""<<comment<<"\"";
	if(year!="")
		stream << " --ty \""<<year<<"\"";
	if(album!="")
		stream << " --tl \""<<album<<"\"";
	if(category!="")
		stream << " --tg \""<<category<<"\"";
	if(image!="")
		stream << " --ti \""<<image<<"\"";
	if(episode!="")
		stream << " --tn \""<<episode<<"\"";

	stream << " \""<<tempfile<<"\" \""<<filename<<"\"";

	LOG(logINFO) << stream.str() << std::endl;

	unsigned result=system(stream.str().c_str());

	unlink(tempfile.c_str());

	return result;
}

int Encode::oggenc(Channels &c,
					std::string filename,
					QualitySetting quality,
					std::string title,
					std::string artist,
					std::string album,
					std::string comment,
					std::string category,
					std::string episode)
{

	if(c.size()<1)
		return 0;

	std::string tempfile=filename+".wav";

	Wave::save(tempfile,c);

	unsigned rate=c[0].samplerate();
	if(rate>44100)
		rate=44100;

	unsigned halfrate=rate/2;

	std::string Vrate="2";
	switch(quality)
	{
	case LOW:
		Vrate="1";
		break;
	case STANDARD:
		Vrate="2";
		break;
	case HIGH:
		Vrate="4";
		break;
	case INSANE:
		Vrate="6";
		break;
	}

	std::stringstream stream;

	stream << "oggenc -q" << Vrate
		   << " --advanced-encode-option lowpass_frequency="<< halfrate;

	if(title!="")
		stream << " -t \""<<title<<"\"";
	if(artist!="")
		stream << " -a \""<<artist<<"\"";
	if(comment!="")
		stream << " -d \""<<comment<<"\"";
	if(album!="")
		stream << " -l \""<<album<<"\"";
	if(category!="")
		stream << " -G \""<<category<<"\"";
	if(episode!="")
		stream << " -N \""<<episode<<"\"";

	stream << " \""<<tempfile<<"\" -o \""<<filename<<"\"";

	LOG(logINFO) << stream.str() << std::endl;

	unsigned result=system(stream.str().c_str());

	unlink(tempfile.c_str());

	return result;
}
<commit_msg>Adapted standard mp3 encoding quality.<commit_after>/**
 * @file		Encode.cpp
 * @author  	Sebastian Ritterbusch <ospac@ritterbusch.de>
 * @version 	1.0
 * @date		29.3.2016
 * @copyright	MIT License (see LICENSE file)
 * @brief 		Encoding to various formats using external tools
 */

#include <unistd.h>
#include <stdlib.h>
#include <sstream>

#include "Encode.h"
#include "Wave.h"
#include "Log.h"

int Encode::lame(Channels &c,
					std::string filename,
					QualitySetting quality,
					std::string title,
					std::string artist,
					std::string album,
					std::string comment,
					std::string image,
					std::string category,
					std::string episode,
					std::string year)
{

	if(c.size()<1)
		return 0;

	std::string tempfile=filename+".wav";

	Wave::save(tempfile,c);

	unsigned rate=c[0].samplerate();
	if(rate>44100)
		rate=44100;

	unsigned halfrate=rate/2;

	std::string Vrate="9";
	switch(quality)
	{
	case LOW:
		Vrate="9";
		break;
	case STANDARD:
		Vrate="8.4";
		break;
	case HIGH:
		Vrate="5";
		break;
	case INSANE:
		Vrate="1";
		break;
	}

	std::stringstream stream;

	stream << "lame -V" << Vrate
		   << " -q0"
		   << " --lowpass "<< halfrate
		   << " --resample "<< rate;

	if(title!="")
		stream << " --tt \""<<title<<"\"";
	if(artist!="")
		stream << " --ta \""<<artist<<"\"";
	if(comment!="")
		stream << " --tc \""<<comment<<"\"";
	if(year!="")
		stream << " --ty \""<<year<<"\"";
	if(album!="")
		stream << " --tl \""<<album<<"\"";
	if(category!="")
		stream << " --tg \""<<category<<"\"";
	if(image!="")
		stream << " --ti \""<<image<<"\"";
	if(episode!="")
		stream << " --tn \""<<episode<<"\"";

	stream << " \""<<tempfile<<"\" \""<<filename<<"\"";

	LOG(logINFO) << stream.str() << std::endl;

	unsigned result=system(stream.str().c_str());

	unlink(tempfile.c_str());

	return result;
}

int Encode::oggenc(Channels &c,
					std::string filename,
					QualitySetting quality,
					std::string title,
					std::string artist,
					std::string album,
					std::string comment,
					std::string category,
					std::string episode)
{

	if(c.size()<1)
		return 0;

	std::string tempfile=filename+".wav";

	Wave::save(tempfile,c);

	unsigned rate=c[0].samplerate();
	if(rate>44100)
		rate=44100;

	unsigned halfrate=rate/2;

	std::string Vrate="2";
	switch(quality)
	{
	case LOW:
		Vrate="1";
		break;
	case STANDARD:
		Vrate="2";
		break;
	case HIGH:
		Vrate="4";
		break;
	case INSANE:
		Vrate="6";
		break;
	}

	std::stringstream stream;

	stream << "oggenc -q" << Vrate
		   << " --advanced-encode-option lowpass_frequency="<< halfrate;

	if(title!="")
		stream << " -t \""<<title<<"\"";
	if(artist!="")
		stream << " -a \""<<artist<<"\"";
	if(comment!="")
		stream << " -d \""<<comment<<"\"";
	if(album!="")
		stream << " -l \""<<album<<"\"";
	if(category!="")
		stream << " -G \""<<category<<"\"";
	if(episode!="")
		stream << " -N \""<<episode<<"\"";

	stream << " \""<<tempfile<<"\" -o \""<<filename<<"\"";

	LOG(logINFO) << stream.str() << std::endl;

	unsigned result=system(stream.str().c_str());

	unlink(tempfile.c_str());

	return result;
}
<|endoftext|>


<commit_before>#include <Engine.hpp>

Engine::Engine(const int _argc, const char** _argv) :
    argc(_argc), argv(_argv)
{
    // check if a game directory is specified on the command line
    if (argc > 1 and argv[1] != nullptr) {
        game = std::string(argv[1]);
    } else {
        game = "game";
    }

    LOGOG_INITIALIZE();

    // create custom format
    class FormatterCustom : public logog::FormatterGCC {
        virtual TOPIC_FLAGS GetTopicFlags(const logog::Topic& topic) {
            return  (logog::FormatterGCC::GetTopicFlags(topic) &
                         ~(TOPIC_FILE_NAME_FLAG | TOPIC_LINE_NUMBER_FLAG));
        }
    };

    const std::string logFilename = data_dir + '/' + game + ".log";

    // remove existing log file
    remove(logFilename.c_str());

    logFile = new logog::LogFile(logFilename.c_str());
    logConsole = new logog::Cout;
    formatter = new FormatterCustom;
    // use custom format
    formatter->SetShowTimeOfDay(true);
    logFile->SetFormatter(*formatter);
    logConsole->SetFormatter(*formatter);

    // try to make sure we don't get extra crap in our console log
    std::cout.flush();

    INFO("Logging system initialized.");

    dumpSystemInfo();

    INFO("Initializing Engine with game data in '%s'", game.c_str());

    data_dir = game;

    // TODO: verify data_dir is accesible, abort if not
    // TODO: _chdir into data_dir for easier relative paths everywhere else

    readConfig();

    INFO("Creating %dx%d render target", renderWidth, renderHeight);
    screen.create(renderWidth, renderHeight);

    createWindow(config.fullscreen);

    std::unique_lock<std::mutex> spritesLock(spritesMutex);
    INFO("Creating entities");
    player = std::make_shared<Sprite>(data_dir + "/player.yaml");
    player->setPosition(renderWidth * 1 / 2, renderHeight * 3 / 4);
    sprites.push_back(player);
    INFO("Created player");
    enemy = std::make_shared<Sprite>(data_dir + "/enemy.yaml");
    enemy->setPosition(renderWidth * 1 / 2, renderHeight * 1 / 4);
    sprites.push_back(enemy);
    INFO("Created enemy");
    spritesLock.unlock();

    isReady = true;
    INFO("Initialization Complete");
}

Engine::~Engine() {
    INFO("Logging system shutting down.");

    delete(logFile);
    delete(logConsole);
    delete(formatter);

    LOGOG_SHUTDOWN();
}

bool Engine::ready() {
    if (isReady) {
        INFO("Game of '%s' is ready", config.name.c_str());
    } else {

    }
    return isReady;
}

bool Engine::run() {
    INFO("Starting a game of '%s'", config.name.c_str());

    INFO("Initializing event loop");
    sf::Int32 updateHz = 60;
    sf::Int32 updateWaitTime = 1'000'000 / updateHz;
    sf::Clock gameClock;
    sf::Time elapsedTime;
    sf::Time lastUpdateTime;
    sf::Int64 totalUpdateTime = 0;
    sf::Int64 averageUpdateTime;
    sf::Int32 updateCount = 0;

    INFO("Creating Render thread");
    running = true;
    renderThread = std::make_unique<std::thread>(&Engine::renderLoop, this);

    INFO("Starting event loop");
    while (running) {
        elapsedTime = gameClock.restart();
        processEvents();
        update(elapsedTime);
        //compute
        lastUpdateTime = gameClock.getElapsedTime();
        totalUpdateTime += lastUpdateTime.asMilliseconds();
        averageUpdateTime = totalUpdateTime / ++updateCount;
        // log the average time per update every 1 seconds
        if (updateCount % (60 * 1) == 0) {
            DBUG("Average update time: %d ms", averageUpdateTime);
        }
        // update at approximately 60 Hz (16666us minus time taken by last update
        sf::sleep(sf::microseconds(updateWaitTime - lastUpdateTime.asMicroseconds()));
    }
    INFO("Stopped event loop");
    renderThread->join();
    INFO("Closing window");
    window.close();
    INFO("Game of '%s' ended successfully", config.name.c_str());
    return true;
}

void Engine::processEvents() {
    static sf::Event event;

    while (window.pollEvent(event)) {
        switch (event.type) {
            case sf::Event::Closed:
                INFO("Window closed");
                running = false;
                break;
            case sf::Event::Resized:
                handleResize(event.size);
                break;
            case sf::Event::KeyPressed:
                handleKeyPress(event);
                break;
            case sf::Event::KeyReleased:
                handleKeyRelease(event);
                break;
            default:
                break;
        }
    }
}

void Engine::update(const sf::Time& elapsed) {
    float x, y = 0;

    // get current state of controls
    float joy0_X = sf::Joystick::getAxisPosition(0, sf::Joystick::X);
    float joy0_y = sf::Joystick::getAxisPosition(0, sf::Joystick::Y);
    x = std::fabs(joy0_X) < config.deadZone ? 0 : joy0_X;
    y = std::fabs(joy0_y) < config.deadZone ? 0 : joy0_y;

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
        y += -config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
        x += config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
        y += config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
        x += -config.keySpeed;
    }

    std::unique_lock<std::mutex> spritesLock(spritesMutex);

    player->moveBy(x, y);

    const int millis = elapsed.asMilliseconds();
    for (auto sprite : sprites) {
        sprite->update(millis);
    }

    spritesLock.unlock();
}

void Engine::renderLoop() {
    INFO("Initializing render loop");
    sf::Clock frameClock;
    sf::Int32 lastFrameTime;
    sf::Int32 averageFrameTime;
    sf::Int32 totalFrameTime = 0;
    sf::Int32 frameCount = 0;
    INFO("Starting render loop");
    while (running) {
        frameClock.restart();
        // blank the render target to black
        screen.clear(sf::Color::Black);
        // render all the normal sprites
        std::unique_lock<std::mutex> spritesLock(spritesMutex);
        for (const auto sprite : sprites) {
            screen.draw(*sprite);
        }
        spritesLock.unlock();
        // update the target
        screen.display();
        // lock and activate the window
        std::unique_lock<std::mutex> windowLock(windowMutex);
        if (window.setActive(true)) {
            // blank the window to gray
            window.clear(sf::Color(128, 128, 128));
            // copy render target to window
            window.draw(sf::Sprite(screen.getTexture()));
            // update the window
            window.display();
        } else {
            ("Failed to get window context for rendering.");
        }
        // release the lock
        windowLock.unlock();
        lastFrameTime = frameClock.getElapsedTime().asMilliseconds();
        totalFrameTime += lastFrameTime;
        averageFrameTime = totalFrameTime / ++frameCount;
        // log the time per frame every 60 frames (every second if at 60 Hz)
        if (frameCount % (60 * 1) == 0) {
            DBUG("Average frame time: %d ms", averageFrameTime);
        }
    }
    INFO("Stopped render loop");
}

void Engine::handleResize(const sf::Event::SizeEvent& newSize) {
    // save the new window size since this came from a resize event
    // not from a window creation event (initialization or fullscreen toggle)
    config.width = newSize.width;
    config.height = newSize.height;
    INFO("Window resized to: %dx%d", config.width, config.height);
    // do the calculation
    adjustAspect(sf::Vector2u(newSize.width, newSize.height));
}

void Engine::handleKeyPress(const sf::Event& event) {
    switch (event.key.code) {
        case sf::Keyboard::Escape:
            INFO("Key: Escape: exiting");
            running = false;
            break;
        case sf::Keyboard::Return:
            if (event.key.alt) {
                createWindow(!config.fullscreen);
            }
            break;
        default:
            break;
    }
}

void Engine::handleKeyRelease(const sf::Event& event) {
    switch (event.key.code) {
        default:
            break;
    }
}

void Engine::dumpSystemInfo() {
#ifndef NDEBUG
    INFO(argv[0]);
#endif
    // dump our own version and build info
    INFO("JAGE %d.%d.%d", JAGE_VERSION_MAJOR, JAGE_VERSION_MINOR, JAGE_VERSION_REVISION);
    INFO("Built %s %s", __DATE__, __TIME__);

    // and SFML's info
    INFO("SFML %d.%d.%d", SFML_VERSION_MAJOR, SFML_VERSION_MINOR, SFML_VERSION_PATCH);

// what compiler did we use?
#ifdef __MINGW32__
#ifdef __MINGW64__
    INFO("MinGW-w64 %s", __MINGW64_VERSION_STR);
#else
    INFO("MinGW %s", __MINGW64_VERSION_STR);
#endif
#endif

#ifdef __clang__
    INFO("CLang %s", __clang_version__);
#endif

#ifdef __GNUG__
    INFO("GCC %s", __VERSION__);
#endif

#ifdef MSC_VER
    INFO("Visual C++ %s", _MCS_VER);
#endif
}

void Engine::readConfig() {
    std::string configFilename = data_dir + "/config.yaml";
    INFO("Reading config from '%s'", configFilename.c_str());
    try {
        YAML::Node yamlConfig = YAML::LoadFile(configFilename);
        config.name = yamlConfig["name"].as<std::string>(game);
        config.width = yamlConfig["width"].as<uint32_t>(config.width);
        config.height = yamlConfig["height"].as<uint32_t>(config.height);
        config.fullscreen = yamlConfig["fullscreen"].as<bool>(config.fullscreen);
        config.useDesktopSize = yamlConfig["useDesktopSize"].as<bool>(config.useDesktopSize);
        config.vsync = yamlConfig["vsync"].as<bool>(config.vsync);
        config.deadZone = yamlConfig["deadzone"].as<float>(config.deadZone);
        config.keySpeed = yamlConfig["keySpeed"].as<float>(config.keySpeed);
    } catch (YAML::Exception e) {
        ERR("YAML Exception: %s", e.msg.c_str());
        ERR("Can't load '%s', using sane defaults", configFilename.c_str());
    }
    INFO("Current settings:");
    INFO("\tname = %s", config.name.c_str());
    INFO("\twidth = %d", config.width);
    INFO("\theight = %d", config.height);
    INFO("\tfullscreen = %s", (config.fullscreen ? "true" : "false"));
    INFO("\tuseDesktopSize = %s", (config.useDesktopSize ? "true" : "false"));
    INFO("\tvsync = %s", (config.vsync ? "true" : "false"));
    INFO("\tdeadZone = %f", config.deadZone);
    INFO("\tkeySpeed = %f", config.keySpeed);
}

void Engine::createWindow(const bool shouldFullscreen) {
    unsigned int flags = 0;

    std::unique_lock<std::mutex> windowLock(windowMutex);

    INFO("Reading config");
    sf::VideoMode mode;
    config.fullscreen = shouldFullscreen;
    if (config.fullscreen) {
        INFO("Going fullscreen");
        window.setMouseCursorVisible(false);
        if (config.useDesktopSize) {
            INFO("Setting fullscreen mode (using desktop size): %dx%d",
                 sf::VideoMode::getDesktopMode().width,
                 sf::VideoMode::getDesktopMode().height);
            mode = sf::VideoMode::getDesktopMode();
        } else {
            INFO("Setting fullscreen mode: %dx%d", config.width, config.height);
            mode = sf::VideoMode(config.width, config.height);
        }
        flags = sf::Style::Fullscreen;
    } else {
        INFO("Setting windowed mode: %dx%d", config.width, config.height);
        window.setMouseCursorVisible(true);
        mode = sf::VideoMode(config.width, config.height);
        flags = sf::Style::Titlebar | sf::Style::Close | sf::Style::Resize;
    }
    INFO("Creating the main window");
    window.create(mode, game, flags);
    if (!window.isOpen()) {
        ERR("Could not create main window");
        exit(EXIT_FAILURE);
    }
    window.setActive(true);
    sf::ContextSettings settings = window.getSettings();
    INFO("Using OpenGL %d.%d", settings.majorVersion, settings.minorVersion);

    // initialize the view
    INFO("Setting window view");
    view = window.getDefaultView();
    view.setSize(renderWidth, renderHeight);
    view.setCenter(renderWidth / 2, renderHeight / 2);
    window.setView(view);
    if (config.vsync) {
        INFO("Enabling V-sync");
        window.setVerticalSyncEnabled(true);
    }

    window.setActive(false);
    windowLock.unlock();

    // scale the viewport to maintain good aspect
    adjustAspect(window.getSize());
}

void Engine::adjustAspect(const sf::Vector2u& newSize) {
    INFO("Adjusting aspect for window size %dx%d", newSize.x, newSize.y);
    // compute the current aspect
    float currentRatio = (float) newSize.x / (float) newSize.y;
    // used to offset and scale the viewport to maintain 16:9 aspect
    float widthScale = 1.0f;
    float widthOffset = 0.0f;
    float heightScale = 1.0f;
    float heightOffset = 0.0f;
    // used to compare and compute aspect ratios
    // for logging
    std::string isSixteenNine = "16:9";
    if (currentRatio > 16.0f / 9.0f) {
        // we are wider
        isSixteenNine = "wide";
        widthScale = newSize.y * (16.0f / 9.0f) / newSize.x;
        widthOffset = (1.0f - widthScale) / 2.0f;
    } else if (currentRatio < 16.0f / 9.0f) {
        // we are narrower
        isSixteenNine = "narrow";
        heightScale = newSize.x * (9.0f / 16.0f) / newSize.y;
        heightOffset = (1.0f - heightScale) / 2.0f;
    }
    std::unique_lock<std::mutex> windowLock(windowMutex);
    INFO("Setting %s viewport (wo:%f, ho:%f; ws:%f, hs: %f", isSixteenNine.c_str(),
         widthOffset, heightOffset, widthScale, heightScale);
    view.setViewport(sf::FloatRect(widthOffset, heightOffset, widthScale, heightScale));
    window.setView(view);
    windowLock.unlock();
}

<commit_msg>Make sure vsync is really off when requested.<commit_after>#include <Engine.hpp>

Engine::Engine(const int _argc, const char** _argv) :
    argc(_argc), argv(_argv)
{
    // check if a game directory is specified on the command line
    if (argc > 1 and argv[1] != nullptr) {
        game = std::string(argv[1]);
    } else {
        game = "game";
    }

    LOGOG_INITIALIZE();

    // create custom format
    class FormatterCustom : public logog::FormatterGCC {
        virtual TOPIC_FLAGS GetTopicFlags(const logog::Topic& topic) {
            return  (logog::FormatterGCC::GetTopicFlags(topic) &
                         ~(TOPIC_FILE_NAME_FLAG | TOPIC_LINE_NUMBER_FLAG));
        }
    };

    const std::string logFilename = data_dir + '/' + game + ".log";

    // remove existing log file
    remove(logFilename.c_str());

    logFile = new logog::LogFile(logFilename.c_str());
    logConsole = new logog::Cout;
    formatter = new FormatterCustom;
    // use custom format
    formatter->SetShowTimeOfDay(true);
    logFile->SetFormatter(*formatter);
    logConsole->SetFormatter(*formatter);

    // try to make sure we don't get extra crap in our console log
    std::cout.flush();

    INFO("Logging system initialized.");

    dumpSystemInfo();

    INFO("Initializing Engine with game data in '%s'", game.c_str());

    data_dir = game;

    // TODO: verify data_dir is accesible, abort if not
    // TODO: _chdir into data_dir for easier relative paths everywhere else

    readConfig();

    INFO("Creating %dx%d render target", renderWidth, renderHeight);
    screen.create(renderWidth, renderHeight);

    createWindow(config.fullscreen);

    std::unique_lock<std::mutex> spritesLock(spritesMutex);
    INFO("Creating entities");
    player = std::make_shared<Sprite>(data_dir + "/player.yaml");
    player->setPosition(renderWidth * 1 / 2, renderHeight * 3 / 4);
    sprites.push_back(player);
    INFO("Created player");
    enemy = std::make_shared<Sprite>(data_dir + "/enemy.yaml");
    enemy->setPosition(renderWidth * 1 / 2, renderHeight * 1 / 4);
    sprites.push_back(enemy);
    INFO("Created enemy");
    spritesLock.unlock();

    isReady = true;
    INFO("Initialization Complete");
}

Engine::~Engine() {
    INFO("Logging system shutting down.");

    delete(logFile);
    delete(logConsole);
    delete(formatter);

    LOGOG_SHUTDOWN();
}

bool Engine::ready() {
    if (isReady) {
        INFO("Game of '%s' is ready", config.name.c_str());
    } else {

    }
    return isReady;
}

bool Engine::run() {
    INFO("Starting a game of '%s'", config.name.c_str());

    INFO("Initializing event loop");
    sf::Int32 updateHz = 60;
    sf::Int32 updateWaitTime = 1'000'000 / updateHz;
    sf::Clock gameClock;
    sf::Time elapsedTime;
    sf::Time lastUpdateTime;
    sf::Int64 totalUpdateTime = 0;
    sf::Int64 averageUpdateTime;
    sf::Int32 updateCount = 0;

    INFO("Creating Render thread");
    running = true;
    renderThread = std::make_unique<std::thread>(&Engine::renderLoop, this);

    INFO("Starting event loop");
    while (running) {
        elapsedTime = gameClock.restart();
        processEvents();
        update(elapsedTime);
        //compute
        lastUpdateTime = gameClock.getElapsedTime();
        totalUpdateTime += lastUpdateTime.asMilliseconds();
        averageUpdateTime = totalUpdateTime / ++updateCount;
        // log the average time per update every 1 seconds
        if (updateCount % (60 * 1) == 0) {
            DBUG("Average update time: %d ms", averageUpdateTime);
        }
        // update at approximately 60 Hz (16666us minus time taken by last update
        sf::sleep(sf::microseconds(updateWaitTime - lastUpdateTime.asMicroseconds()));
    }
    INFO("Stopped event loop");
    renderThread->join();
    INFO("Closing window");
    window.close();
    INFO("Game of '%s' ended successfully", config.name.c_str());
    return true;
}

void Engine::processEvents() {
    static sf::Event event;

    while (window.pollEvent(event)) {
        switch (event.type) {
            case sf::Event::Closed:
                INFO("Window closed");
                running = false;
                break;
            case sf::Event::Resized:
                handleResize(event.size);
                break;
            case sf::Event::KeyPressed:
                handleKeyPress(event);
                break;
            case sf::Event::KeyReleased:
                handleKeyRelease(event);
                break;
            default:
                break;
        }
    }
}

void Engine::update(const sf::Time& elapsed) {
    float x, y = 0;

    // get current state of controls
    float joy0_X = sf::Joystick::getAxisPosition(0, sf::Joystick::X);
    float joy0_y = sf::Joystick::getAxisPosition(0, sf::Joystick::Y);
    x = std::fabs(joy0_X) < config.deadZone ? 0 : joy0_X;
    y = std::fabs(joy0_y) < config.deadZone ? 0 : joy0_y;

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
        y += -config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
        x += config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
        y += config.keySpeed;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
        x += -config.keySpeed;
    }

    std::unique_lock<std::mutex> spritesLock(spritesMutex);

    player->moveBy(x, y);

    const int millis = elapsed.asMilliseconds();
    for (auto sprite : sprites) {
        sprite->update(millis);
    }

    spritesLock.unlock();
}

void Engine::renderLoop() {
    INFO("Initializing render loop");
    sf::Clock frameClock;
    sf::Int32 lastFrameTime;
    sf::Int32 averageFrameTime;
    sf::Int32 totalFrameTime = 0;
    sf::Int32 frameCount = 0;
    INFO("Starting render loop");
    while (running) {
        frameClock.restart();
        // blank the render target to black
        screen.clear(sf::Color::Black);
        // render all the normal sprites
        std::unique_lock<std::mutex> spritesLock(spritesMutex);
        for (const auto sprite : sprites) {
            screen.draw(*sprite);
        }
        spritesLock.unlock();
        // update the target
        screen.display();
        // lock and activate the window
        std::unique_lock<std::mutex> windowLock(windowMutex);
        if (window.setActive(true)) {
            // blank the window to gray
            window.clear(sf::Color(128, 128, 128));
            // copy render target to window
            window.draw(sf::Sprite(screen.getTexture()));
            // update the window
            window.display();
        } else {
            ("Failed to get window context for rendering.");
        }
        // release the lock
        windowLock.unlock();
        lastFrameTime = frameClock.getElapsedTime().asMilliseconds();
        totalFrameTime += lastFrameTime;
        averageFrameTime = totalFrameTime / ++frameCount;
        // log the time per frame every 60 frames (every second if at 60 Hz)
        if (frameCount % (60 * 1) == 0) {
            DBUG("Average frame time: %d ms", averageFrameTime);
        }
    }
    INFO("Stopped render loop");
}

void Engine::handleResize(const sf::Event::SizeEvent& newSize) {
    // save the new window size since this came from a resize event
    // not from a window creation event (initialization or fullscreen toggle)
    config.width = newSize.width;
    config.height = newSize.height;
    INFO("Window resized to: %dx%d", config.width, config.height);
    // do the calculation
    adjustAspect(sf::Vector2u(newSize.width, newSize.height));
}

void Engine::handleKeyPress(const sf::Event& event) {
    switch (event.key.code) {
        case sf::Keyboard::Escape:
            INFO("Key: Escape: exiting");
            running = false;
            break;
        case sf::Keyboard::Return:
            if (event.key.alt) {
                createWindow(!config.fullscreen);
            }
            break;
        default:
            break;
    }
}

void Engine::handleKeyRelease(const sf::Event& event) {
    switch (event.key.code) {
        default:
            break;
    }
}

void Engine::dumpSystemInfo() {
#ifndef NDEBUG
    INFO(argv[0]);
#endif
    // dump our own version and build info
    INFO("JAGE %d.%d.%d", JAGE_VERSION_MAJOR, JAGE_VERSION_MINOR, JAGE_VERSION_REVISION);
    INFO("Built %s %s", __DATE__, __TIME__);

    // and SFML's info
    INFO("SFML %d.%d.%d", SFML_VERSION_MAJOR, SFML_VERSION_MINOR, SFML_VERSION_PATCH);

// what compiler did we use?
#ifdef __MINGW32__
#ifdef __MINGW64__
    INFO("MinGW-w64 %s", __MINGW64_VERSION_STR);
#else
    INFO("MinGW %s", __MINGW64_VERSION_STR);
#endif
#endif

#ifdef __clang__
    INFO("CLang %s", __clang_version__);
#endif

#ifdef __GNUG__
    INFO("GCC %s", __VERSION__);
#endif

#ifdef MSC_VER
    INFO("Visual C++ %s", _MCS_VER);
#endif
}

void Engine::readConfig() {
    std::string configFilename = data_dir + "/config.yaml";
    INFO("Reading config from '%s'", configFilename.c_str());
    try {
        YAML::Node yamlConfig = YAML::LoadFile(configFilename);
        config.name = yamlConfig["name"].as<std::string>(game);
        config.width = yamlConfig["width"].as<uint32_t>(config.width);
        config.height = yamlConfig["height"].as<uint32_t>(config.height);
        config.fullscreen = yamlConfig["fullscreen"].as<bool>(config.fullscreen);
        config.useDesktopSize = yamlConfig["useDesktopSize"].as<bool>(config.useDesktopSize);
        config.vsync = yamlConfig["vsync"].as<bool>(config.vsync);
        config.deadZone = yamlConfig["deadzone"].as<float>(config.deadZone);
        config.keySpeed = yamlConfig["keySpeed"].as<float>(config.keySpeed);
    } catch (YAML::Exception e) {
        ERR("YAML Exception: %s", e.msg.c_str());
        ERR("Can't load '%s', using sane defaults", configFilename.c_str());
    }
    INFO("Current settings:");
    INFO("\tname = %s", config.name.c_str());
    INFO("\twidth = %d", config.width);
    INFO("\theight = %d", config.height);
    INFO("\tfullscreen = %s", (config.fullscreen ? "true" : "false"));
    INFO("\tuseDesktopSize = %s", (config.useDesktopSize ? "true" : "false"));
    INFO("\tvsync = %s", (config.vsync ? "true" : "false"));
    INFO("\tdeadZone = %f", config.deadZone);
    INFO("\tkeySpeed = %f", config.keySpeed);
}

void Engine::createWindow(const bool shouldFullscreen) {
    unsigned int flags = 0;

    std::unique_lock<std::mutex> windowLock(windowMutex);

    INFO("Reading config");
    sf::VideoMode mode;
    config.fullscreen = shouldFullscreen;
    if (config.fullscreen) {
        INFO("Going fullscreen");
        window.setMouseCursorVisible(false);
        if (config.useDesktopSize) {
            INFO("Setting fullscreen mode (using desktop size): %dx%d",
                 sf::VideoMode::getDesktopMode().width,
                 sf::VideoMode::getDesktopMode().height);
            mode = sf::VideoMode::getDesktopMode();
        } else {
            INFO("Setting fullscreen mode: %dx%d", config.width, config.height);
            mode = sf::VideoMode(config.width, config.height);
        }
        flags = sf::Style::Fullscreen;
    } else {
        INFO("Setting windowed mode: %dx%d", config.width, config.height);
        window.setMouseCursorVisible(true);
        mode = sf::VideoMode(config.width, config.height);
        flags = sf::Style::Titlebar | sf::Style::Close | sf::Style::Resize;
    }
    INFO("Creating the main window");
    window.create(mode, game, flags);
    if (!window.isOpen()) {
        ERR("Could not create main window");
        exit(EXIT_FAILURE);
    }
    window.setActive(true);
    sf::ContextSettings settings = window.getSettings();
    INFO("Using OpenGL %d.%d", settings.majorVersion, settings.minorVersion);

    // initialize the view
    INFO("Setting window view");
    view = window.getDefaultView();
    view.setSize(renderWidth, renderHeight);
    view.setCenter(renderWidth / 2, renderHeight / 2);
    window.setView(view);
    if (config.vsync) {
        INFO("Enabling V-sync");
        window.setVerticalSyncEnabled(true);
    } else {
        window.setVerticalSyncEnabled(false);
    }

    window.setActive(false);
    windowLock.unlock();

    // scale the viewport to maintain good aspect
    adjustAspect(window.getSize());
}

void Engine::adjustAspect(const sf::Vector2u& newSize) {
    INFO("Adjusting aspect for window size %dx%d", newSize.x, newSize.y);
    // compute the current aspect
    float currentRatio = (float) newSize.x / (float) newSize.y;
    // used to offset and scale the viewport to maintain 16:9 aspect
    float widthScale = 1.0f;
    float widthOffset = 0.0f;
    float heightScale = 1.0f;
    float heightOffset = 0.0f;
    // used to compare and compute aspect ratios
    // for logging
    std::string isSixteenNine = "16:9";
    if (currentRatio > 16.0f / 9.0f) {
        // we are wider
        isSixteenNine = "wide";
        widthScale = newSize.y * (16.0f / 9.0f) / newSize.x;
        widthOffset = (1.0f - widthScale) / 2.0f;
    } else if (currentRatio < 16.0f / 9.0f) {
        // we are narrower
        isSixteenNine = "narrow";
        heightScale = newSize.x * (9.0f / 16.0f) / newSize.y;
        heightOffset = (1.0f - heightScale) / 2.0f;
    }
    std::unique_lock<std::mutex> windowLock(windowMutex);
    INFO("Setting %s viewport (wo:%f, ho:%f; ws:%f, hs: %f", isSixteenNine.c_str(),
         widthOffset, heightOffset, widthScale, heightScale);
    view.setViewport(sf::FloatRect(widthOffset, heightOffset, widthScale, heightScale));
    window.setView(view);
    windowLock.unlock();
}

<|endoftext|>


<commit_before>//
// Copyright (C) 2007 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// Copyright (C) 2007 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// $$
///////////////////////////////////////////////////////////////////////////////

// Author: Keith Kyzivat <kkyzivat AT SIPez DOT com>

// SYSTEM INCLUDES
#include <Windows.h>
#include <os/OsSysLog.h>

// APPLICATION INCLUDES
#include "mp/MpidWinMM.h"
#include "mp/MpInputDeviceManager.h"

// EXTERNAL FUNCTIONS
extern void showWaveError(char *syscall, int e, int N, int line) ;  // dmaTaskWnt.cpp

// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS
// DEFINES
#if defined(_MSC_VER) && (_MSC_VER < 1300) // if < msvc7 (2003)
#  define CBTYPE DWORD
#else
#  define CBTYPE DWORD_PTR
#endif

/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */
// Default constructor
MpidWinMM::MpidWinMM(const UtlString& name,
                     MpInputDeviceManager& deviceManager,
                     unsigned nInputBuffers)
: MpInputDeviceDriver(name, deviceManager)
, mWinMMDeviceId(-1)
, mDevHandle(NULL)
, mNumInBuffers(nInputBuffers)
, mWaveBufSize(0)  // Unknown until enableDevice()
, mIsOpen(FALSE)
, mnAddBufferFailures(0)
{
    WAVEINCAPS devCaps;
    // Grab the number of input devices that are available.
    UINT nInputDevs = waveInGetNumDevs();

    // Search through the input devices looking for the input device specified.
    MMRESULT wavResult = MMSYSERR_NOERROR;
    unsigned i;
    for (i = 0; i < nInputDevs; i++)
    {
        MMRESULT res = waveInGetDevCaps(i, &devCaps, sizeof(devCaps));
        if (res != MMSYSERR_NOERROR)
        {
            wavResult = res;
        }
        else if (strncmp(name, devCaps.szPname, MAXPNAMELEN) == 0)
        {
            mWinMMDeviceId = i;
            break;
        }
    }

    // Allocate the wave headers and buffer pointers for use with
    // windows audio routines.
    //(This does *not* include allocation of the buffers themselves -
    // that is handled in enableDevice, as we don't know the
    // buffer size (#samplesPerFrame) until then.
    mpWaveHeaders = new WAVEHDR[mNumInBuffers];
    mpWaveBuffers = new LPSTR[mNumInBuffers];
    for (i = 0; i < mNumInBuffers; i++)
    {
        mpWaveBuffers[i] = NULL;
    }
}

// Destructor
MpidWinMM::~MpidWinMM()
{
    // If we happen to still be enabled at this point, disable the device.
    assert(!isEnabled());
    if (isEnabled())
    {
        disableDevice();
    }

    // Delete the sample headers and sample buffer pointers..
    unsigned i;
    for (i = 0; i < mNumInBuffers; i++)
    {
        assert(mpWaveBuffers[i] == NULL);
        if (mpWaveBuffers[i] != NULL)
        {
            delete[] mpWaveBuffers[i];
            mpWaveBuffers[i] = NULL;
        }
    }
    delete[] mpWaveBuffers;
    delete[] mpWaveHeaders;
}

/* ============================ MANIPULATORS ============================== */

OsStatus MpidWinMM::enableDevice(unsigned samplesPerFrame,
                                 unsigned samplesPerSec,
                                 MpFrameTime currentFrameTime)
{
    OsStatus status = OS_SUCCESS;

    // reset the number of addBuffer failures, as we're starting fresh now.
    mnAddBufferFailures = 0;

    // If the device is not valid, let the user know it's bad.
    if (!isDeviceValid())
    {
        return OS_INVALID_STATE;  // perhaps new OsState of OS_RESOURCE_INVALID?
    }

    if (isEnabled())
    {
        return OS_FAILED;
    }

    // Set some wave header stat information.
    mSamplesPerFrame = samplesPerFrame;
    mSamplesPerSec = samplesPerSec;
    mCurrentFrameTime = currentFrameTime;

    // Do stuff to enable device.
    int nChannels = 1;
    WAVEFORMATEX wavFormat;
    wavFormat.wFormatTag = WAVE_FORMAT_PCM;
    wavFormat.nChannels = nChannels;
    wavFormat.nSamplesPerSec = mSamplesPerSec;
    wavFormat.nAvgBytesPerSec =
        nChannels * mSamplesPerSec * sizeof(MpAudioSample);
    wavFormat.nBlockAlign = nChannels * sizeof(MpAudioSample);
    wavFormat.wBitsPerSample = sizeof(MpAudioSample) * 8;
    wavFormat.cbSize = 0;

    // Tell windows to open the input audio device.  This doesn't
    // tell it to send the data to our callback yet, just to get it ready
    // to do so..
    MMRESULT res = waveInOpen(&mDevHandle, mWinMMDeviceId,
                              &wavFormat,
                              (CBTYPE)waveInCallbackStatic,
                              (CBTYPE)this,
                              CALLBACK_FUNCTION);
    if (res != MMSYSERR_NOERROR)
    {
        // If waveInOpen failed, print out the error info,
        // invalidate the handle, and the device driver itself,
        status = OS_FAILED;
        showWaveError("MpidWinMM::enableDevice", res, -1, __LINE__);
        waveInClose(mDevHandle);
        mDevHandle = NULL; // Open didn't work, reset device handle to NULL
        mWinMMDeviceId = -1; // Make device invalid.

        // and return OS_FAILED.
        return status;
    }

    // Allocate the buffers we are going to use to receive audio data from
    // the windows audio input callback.
    // Calculate the buffer length we're going to use.
    // number of samples per frame * sample size in bytes
    mWaveBufSize = mSamplesPerFrame * sizeof(MpAudioSample);
    unsigned i;
    for (i = 0; i < mNumInBuffers; i++)
    {
        mpWaveBuffers[i] = new char[mWaveBufSize];
    }

    // Setup the buffers so windows can stuff them full of audio
    // when it becomes available from this audio input device.
    WAVEHDR* pWaveHdr = NULL;
    for (i=0; i < mNumInBuffers; i++)
    {
        pWaveHdr = initWaveHeader(i);

        res = waveInPrepareHeader(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInPrepareHeader", res, i, __LINE__);
            waveInClose(mDevHandle);
            mDevHandle = NULL;
            mWinMMDeviceId = -1;

            // and return OS_FAILED.
            return status;
        }
        res = waveInAddBuffer(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInAddBuffer", res, i, __LINE__);
            waveInClose(mDevHandle);
            mDevHandle = NULL;
            mWinMMDeviceId = -1;

            // and return OS_FAILED.
            return status;
        }
    }

    // Tell windows to start sending audio data to the callback.
    res = waveInStart(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        // If waveInStart failed, print out the error info,
        // invalidate the handle and the device driver itself,
        status = OS_FAILED;
        showWaveError("waveInStart", res, -1, __LINE__);
        waveInClose(mDevHandle);
        mDevHandle = NULL;
        mWinMMDeviceId = -1;

        // and return OS_FAILED.
        return status;
    }

    // If enableDevice failed, return indicating failure.
    if (status == OS_SUCCESS)
    {
        mIsEnabled = TRUE;
    }

    return status;
}

OsStatus MpidWinMM::disableDevice()
{
    OsStatus status = OS_SUCCESS;
    MMRESULT   res;

    if (!isDeviceValid() || !isEnabled())
    {
        return OS_FAILED;
    }

    // Indicate we are no longer enabled -- Do this first,
    // since we'll be partially disabled from here on out.
    // It is very important that this happen *before* waveInReset,
    // as the callback will continue to add and process buffers
    // while waveInReset is called causing a deadlock.
    mIsEnabled = FALSE;

    // Cleanup
    if (mDevHandle == NULL)
    {
        return OS_INVALID_STATE;
    }

    // Reset performs a stop, resets the buffers, and marks them
    // for being sent to the callback.
    // The remaining data in the windows buffers *IS* sent to the callback,
    // So be sure to watch for it and drop it on the floor.
    res = waveInReset(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        showWaveError("waveInReset", res, -1, __LINE__);
    }

    // Must unprepare the headers after a reset, but before the device is closed
    // (if this is done after waveInClose, mDevHandle will be invalid and
    // MMSYSERR_INVALHANDLE will be returned.
    unsigned i;
    for (i=0; i < mNumInBuffers; i++)
    {
        res = waveInUnprepareHeader(mDevHandle, &mpWaveHeaders[i], sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInUnprepareHeader", res, i, __LINE__);
        }
    }

    res = waveInClose(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        showWaveError("waveInClose", res, -1, __LINE__);
    }

    // Delete the buffers that were allocated in enableDevice()
    for (i = 0; i < mNumInBuffers; i++)
    {
        delete[] mpWaveBuffers[i];
        mpWaveBuffers[i] = NULL;
    }

    // set the device handle to NULL, since it no longer is valid.
    mDevHandle = NULL;

    // Clear out all the wave header information.
    mSamplesPerFrame = 0;
    mSamplesPerSec = 0;
    mCurrentFrameTime = 0;

    return status;
}

/* ============================ ACCESSORS ================================= */

/* ////////////////////////// PUBLIC STATIC ///////////////////////////////// */
UtlString MpidWinMM::getDefaultDeviceName()
{
   UtlString devName = "";

   // Get windows default input device name
   unsigned nDevs = waveInGetNumDevs();
   if (nDevs == 0)
   {
      OsSysLog::add(FAC_AUDIO, PRI_ERR,
                    "MpidWinMM::getDefaultDeviceName: "
                    "No input audio devices present!");
   }
   assert(nDevs != 0);

   MMRESULT wavResult = MMSYSERR_NOERROR;
   WAVEINCAPS devCaps;
   int defaultWinDeviceId = 0;
   wavResult =
      waveInGetDevCaps(defaultWinDeviceId, &devCaps, sizeof(devCaps));
   if (wavResult != MMSYSERR_NOERROR)
   {
      OsSysLog::add(FAC_AUDIO, PRI_ERR,
                    "MpodWinMM::getDefaultDeviceName: "
                    "Couldn't get default input device capabilities!");
      showWaveError("WINDOWS_DEFAULT_DEVICE_HACK",
                    wavResult, -1, __LINE__);
   }
   else
   {
      devName = devCaps.szPname;
   }
   assert(wavResult == MMSYSERR_NOERROR);
   return devName;
}

/* ============================ INQUIRY =================================== */
/* //////////////////////////// PROTECTED ///////////////////////////////// */

WAVEHDR* MpidWinMM::initWaveHeader(int n)
{
    assert((n >= 0) && (n < (int)mNumInBuffers));
    assert(mpWaveHeaders != NULL);
    assert((mpWaveBuffers != NULL) && (mpWaveBuffers[n] != NULL));
    WAVEHDR* pWave_hdr = &(mpWaveHeaders[n]);
    LPSTR    wave_data(mpWaveBuffers[n]);

    // zero out the wave buffer.
    memset(wave_data, 0, mWaveBufSize);

    // Set wave header data to initial values.
    pWave_hdr->lpData = wave_data;
    pWave_hdr->dwBufferLength = mWaveBufSize;
    pWave_hdr->dwBytesRecorded = 0;  // Filled in by wave functions
    pWave_hdr->dwUser = n;
    pWave_hdr->dwFlags = 0;
    pWave_hdr->dwLoops = 0;
    pWave_hdr->lpNext = NULL;
    pWave_hdr->reserved = 0;

    return pWave_hdr;
}
void MpidWinMM::processAudioInput(HWAVEIN hwi,
                                  UINT uMsg,
                                  void* dwParam1)
{
    if (!mIsOpen)
    {
        assert(uMsg == WIM_OPEN);
        if (uMsg == WIM_OPEN)
        {
            printf("received WIM_OPEN\n");
            mIsOpen = TRUE;
        }
    }
    if (uMsg == WIM_DATA)
    {
        //printf("received WIM_DATA\n");
        assert(mIsOpen);
        WAVEHDR* pWaveHdr = (WAVEHDR*)dwParam1;
        assert(pWaveHdr->dwBufferLength
               == (mSamplesPerFrame*sizeof(MpAudioSample)));
        assert(pWaveHdr->lpData != NULL);

        // Only process if we're enabled..
        if(mIsEnabled)
        {
           mpInputDeviceManager->pushFrame(mDeviceId,
                                           mSamplesPerFrame,
                                           (MpAudioSample*)pWaveHdr->lpData,
                                           mCurrentFrameTime);
           // Ok, we have received and pushed a frame to the manager,
           // Now we advance the frame time.
           mCurrentFrameTime += (mSamplesPerFrame*1000)/mSamplesPerSec;

           // Put the wave header back in the pool..
           MMRESULT res = MMSYSERR_NOERROR;

           res = waveInAddBuffer(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
           if (res != MMSYSERR_NOERROR)
           {
              showWaveError("waveInAddBuffer", res, -1, __LINE__);
              mnAddBufferFailures++;
              if(mnAddBufferFailures >= mNumInBuffers)
              {
                 waveInClose(mDevHandle);
                 mDevHandle = NULL;
                 mWinMMDeviceId = -1;
              }
           }
        }
    }
    else if (uMsg == WIM_CLOSE)
    {
        printf("received WIM_CLOSE\n");
        mIsOpen = FALSE;
    }
}

void CALLBACK
MpidWinMM::waveInCallbackStatic(HWAVEIN hwi,
                                UINT uMsg,
                                void* dwInstance,
                                void* dwParam1,
                                void* dwParam2)
{
    assert(dwInstance != NULL);
    MpidWinMM* iddWntPtr = (MpidWinMM*)dwInstance;
    assert((uMsg == WIM_OPEN) || (hwi == iddWntPtr->mDevHandle));
    iddWntPtr->processAudioInput(hwi, uMsg, dwParam1);
}

/* //////////////////////////// PRIVATE /////////////////////////////////// */
<commit_msg>-removed console debug output<commit_after>//
// Copyright (C) 2007 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// Copyright (C) 2007 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// $$
///////////////////////////////////////////////////////////////////////////////

// Author: Keith Kyzivat <kkyzivat AT SIPez DOT com>

// SYSTEM INCLUDES
#include <Windows.h>
#include <os/OsSysLog.h>

// APPLICATION INCLUDES
#include "mp/MpidWinMM.h"
#include "mp/MpInputDeviceManager.h"

// EXTERNAL FUNCTIONS
extern void showWaveError(char *syscall, int e, int N, int line) ;  // dmaTaskWnt.cpp

// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS
// DEFINES
#if defined(_MSC_VER) && (_MSC_VER < 1300) // if < msvc7 (2003)
#  define CBTYPE DWORD
#else
#  define CBTYPE DWORD_PTR
#endif

/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */
// Default constructor
MpidWinMM::MpidWinMM(const UtlString& name,
                     MpInputDeviceManager& deviceManager,
                     unsigned nInputBuffers)
: MpInputDeviceDriver(name, deviceManager)
, mWinMMDeviceId(-1)
, mDevHandle(NULL)
, mNumInBuffers(nInputBuffers)
, mWaveBufSize(0)  // Unknown until enableDevice()
, mIsOpen(FALSE)
, mnAddBufferFailures(0)
{
    WAVEINCAPS devCaps;
    // Grab the number of input devices that are available.
    UINT nInputDevs = waveInGetNumDevs();

    // Search through the input devices looking for the input device specified.
    MMRESULT wavResult = MMSYSERR_NOERROR;
    unsigned i;
    for (i = 0; i < nInputDevs; i++)
    {
        MMRESULT res = waveInGetDevCaps(i, &devCaps, sizeof(devCaps));
        if (res != MMSYSERR_NOERROR)
        {
            wavResult = res;
        }
        else if (strncmp(name, devCaps.szPname, MAXPNAMELEN) == 0)
        {
            mWinMMDeviceId = i;
            break;
        }
    }

    // Allocate the wave headers and buffer pointers for use with
    // windows audio routines.
    //(This does *not* include allocation of the buffers themselves -
    // that is handled in enableDevice, as we don't know the
    // buffer size (#samplesPerFrame) until then.
    mpWaveHeaders = new WAVEHDR[mNumInBuffers];
    mpWaveBuffers = new LPSTR[mNumInBuffers];
    for (i = 0; i < mNumInBuffers; i++)
    {
        mpWaveBuffers[i] = NULL;
    }
}

// Destructor
MpidWinMM::~MpidWinMM()
{
    // If we happen to still be enabled at this point, disable the device.
    assert(!isEnabled());
    if (isEnabled())
    {
        disableDevice();
    }

    // Delete the sample headers and sample buffer pointers..
    unsigned i;
    for (i = 0; i < mNumInBuffers; i++)
    {
        assert(mpWaveBuffers[i] == NULL);
        if (mpWaveBuffers[i] != NULL)
        {
            delete[] mpWaveBuffers[i];
            mpWaveBuffers[i] = NULL;
        }
    }
    delete[] mpWaveBuffers;
    delete[] mpWaveHeaders;
}

/* ============================ MANIPULATORS ============================== */

OsStatus MpidWinMM::enableDevice(unsigned samplesPerFrame,
                                 unsigned samplesPerSec,
                                 MpFrameTime currentFrameTime)
{
    OsStatus status = OS_SUCCESS;

    // reset the number of addBuffer failures, as we're starting fresh now.
    mnAddBufferFailures = 0;

    // If the device is not valid, let the user know it's bad.
    if (!isDeviceValid())
    {
        return OS_INVALID_STATE;  // perhaps new OsState of OS_RESOURCE_INVALID?
    }

    if (isEnabled())
    {
        return OS_FAILED;
    }

    // Set some wave header stat information.
    mSamplesPerFrame = samplesPerFrame;
    mSamplesPerSec = samplesPerSec;
    mCurrentFrameTime = currentFrameTime;

    // Do stuff to enable device.
    int nChannels = 1;
    WAVEFORMATEX wavFormat;
    wavFormat.wFormatTag = WAVE_FORMAT_PCM;
    wavFormat.nChannels = nChannels;
    wavFormat.nSamplesPerSec = mSamplesPerSec;
    wavFormat.nAvgBytesPerSec =
        nChannels * mSamplesPerSec * sizeof(MpAudioSample);
    wavFormat.nBlockAlign = nChannels * sizeof(MpAudioSample);
    wavFormat.wBitsPerSample = sizeof(MpAudioSample) * 8;
    wavFormat.cbSize = 0;

    // Tell windows to open the input audio device.  This doesn't
    // tell it to send the data to our callback yet, just to get it ready
    // to do so..
    MMRESULT res = waveInOpen(&mDevHandle, mWinMMDeviceId,
                              &wavFormat,
                              (CBTYPE)waveInCallbackStatic,
                              (CBTYPE)this,
                              CALLBACK_FUNCTION);
    if (res != MMSYSERR_NOERROR)
    {
        // If waveInOpen failed, print out the error info,
        // invalidate the handle, and the device driver itself,
        status = OS_FAILED;
        showWaveError("MpidWinMM::enableDevice", res, -1, __LINE__);
        waveInClose(mDevHandle);
        mDevHandle = NULL; // Open didn't work, reset device handle to NULL
        mWinMMDeviceId = -1; // Make device invalid.

        // and return OS_FAILED.
        return status;
    }

    // Allocate the buffers we are going to use to receive audio data from
    // the windows audio input callback.
    // Calculate the buffer length we're going to use.
    // number of samples per frame * sample size in bytes
    mWaveBufSize = mSamplesPerFrame * sizeof(MpAudioSample);
    unsigned i;
    for (i = 0; i < mNumInBuffers; i++)
    {
        mpWaveBuffers[i] = new char[mWaveBufSize];
    }

    // Setup the buffers so windows can stuff them full of audio
    // when it becomes available from this audio input device.
    WAVEHDR* pWaveHdr = NULL;
    for (i=0; i < mNumInBuffers; i++)
    {
        pWaveHdr = initWaveHeader(i);

        res = waveInPrepareHeader(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInPrepareHeader", res, i, __LINE__);
            waveInClose(mDevHandle);
            mDevHandle = NULL;
            mWinMMDeviceId = -1;

            // and return OS_FAILED.
            return status;
        }
        res = waveInAddBuffer(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInAddBuffer", res, i, __LINE__);
            waveInClose(mDevHandle);
            mDevHandle = NULL;
            mWinMMDeviceId = -1;

            // and return OS_FAILED.
            return status;
        }
    }

    // Tell windows to start sending audio data to the callback.
    res = waveInStart(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        // If waveInStart failed, print out the error info,
        // invalidate the handle and the device driver itself,
        status = OS_FAILED;
        showWaveError("waveInStart", res, -1, __LINE__);
        waveInClose(mDevHandle);
        mDevHandle = NULL;
        mWinMMDeviceId = -1;

        // and return OS_FAILED.
        return status;
    }

    // If enableDevice failed, return indicating failure.
    if (status == OS_SUCCESS)
    {
        mIsEnabled = TRUE;
    }

    return status;
}

OsStatus MpidWinMM::disableDevice()
{
    OsStatus status = OS_SUCCESS;
    MMRESULT   res;

    if (!isDeviceValid() || !isEnabled())
    {
        return OS_FAILED;
    }

    // Indicate we are no longer enabled -- Do this first,
    // since we'll be partially disabled from here on out.
    // It is very important that this happen *before* waveInReset,
    // as the callback will continue to add and process buffers
    // while waveInReset is called causing a deadlock.
    mIsEnabled = FALSE;

    // Cleanup
    if (mDevHandle == NULL)
    {
        return OS_INVALID_STATE;
    }

    // Reset performs a stop, resets the buffers, and marks them
    // for being sent to the callback.
    // The remaining data in the windows buffers *IS* sent to the callback,
    // So be sure to watch for it and drop it on the floor.
    res = waveInReset(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        showWaveError("waveInReset", res, -1, __LINE__);
    }

    // Must unprepare the headers after a reset, but before the device is closed
    // (if this is done after waveInClose, mDevHandle will be invalid and
    // MMSYSERR_INVALHANDLE will be returned.
    unsigned i;
    for (i=0; i < mNumInBuffers; i++)
    {
        res = waveInUnprepareHeader(mDevHandle, &mpWaveHeaders[i], sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
        {
            showWaveError("waveInUnprepareHeader", res, i, __LINE__);
        }
    }

    res = waveInClose(mDevHandle);
    if (res != MMSYSERR_NOERROR)
    {
        showWaveError("waveInClose", res, -1, __LINE__);
    }

    // Delete the buffers that were allocated in enableDevice()
    for (i = 0; i < mNumInBuffers; i++)
    {
        delete[] mpWaveBuffers[i];
        mpWaveBuffers[i] = NULL;
    }

    // set the device handle to NULL, since it no longer is valid.
    mDevHandle = NULL;

    // Clear out all the wave header information.
    mSamplesPerFrame = 0;
    mSamplesPerSec = 0;
    mCurrentFrameTime = 0;

    return status;
}

/* ============================ ACCESSORS ================================= */

/* ////////////////////////// PUBLIC STATIC ///////////////////////////////// */
UtlString MpidWinMM::getDefaultDeviceName()
{
   UtlString devName = "";

   // Get windows default input device name
   unsigned nDevs = waveInGetNumDevs();
   if (nDevs == 0)
   {
      OsSysLog::add(FAC_AUDIO, PRI_ERR,
                    "MpidWinMM::getDefaultDeviceName: "
                    "No input audio devices present!");
   }
   assert(nDevs != 0);

   MMRESULT wavResult = MMSYSERR_NOERROR;
   WAVEINCAPS devCaps;
   int defaultWinDeviceId = 0;
   wavResult =
      waveInGetDevCaps(defaultWinDeviceId, &devCaps, sizeof(devCaps));
   if (wavResult != MMSYSERR_NOERROR)
   {
      OsSysLog::add(FAC_AUDIO, PRI_ERR,
                    "MpodWinMM::getDefaultDeviceName: "
                    "Couldn't get default input device capabilities!");
      showWaveError("WINDOWS_DEFAULT_DEVICE_HACK",
                    wavResult, -1, __LINE__);
   }
   else
   {
      devName = devCaps.szPname;
   }
   assert(wavResult == MMSYSERR_NOERROR);
   return devName;
}

/* ============================ INQUIRY =================================== */
/* //////////////////////////// PROTECTED ///////////////////////////////// */

WAVEHDR* MpidWinMM::initWaveHeader(int n)
{
    assert((n >= 0) && (n < (int)mNumInBuffers));
    assert(mpWaveHeaders != NULL);
    assert((mpWaveBuffers != NULL) && (mpWaveBuffers[n] != NULL));
    WAVEHDR* pWave_hdr = &(mpWaveHeaders[n]);
    LPSTR    wave_data(mpWaveBuffers[n]);

    // zero out the wave buffer.
    memset(wave_data, 0, mWaveBufSize);

    // Set wave header data to initial values.
    pWave_hdr->lpData = wave_data;
    pWave_hdr->dwBufferLength = mWaveBufSize;
    pWave_hdr->dwBytesRecorded = 0;  // Filled in by wave functions
    pWave_hdr->dwUser = n;
    pWave_hdr->dwFlags = 0;
    pWave_hdr->dwLoops = 0;
    pWave_hdr->lpNext = NULL;
    pWave_hdr->reserved = 0;

    return pWave_hdr;
}
void MpidWinMM::processAudioInput(HWAVEIN hwi,
                                  UINT uMsg,
                                  void* dwParam1)
{
    if (!mIsOpen)
    {
        assert(uMsg == WIM_OPEN);
        if (uMsg == WIM_OPEN)
        {
            //printf("received WIM_OPEN\n");
            mIsOpen = TRUE;
        }
    }
    if (uMsg == WIM_DATA)
    {
        //printf("received WIM_DATA\n");
        assert(mIsOpen);
        WAVEHDR* pWaveHdr = (WAVEHDR*)dwParam1;
        assert(pWaveHdr->dwBufferLength
               == (mSamplesPerFrame*sizeof(MpAudioSample)));
        assert(pWaveHdr->lpData != NULL);

        // Only process if we're enabled..
        if(mIsEnabled)
        {
           mpInputDeviceManager->pushFrame(mDeviceId,
                                           mSamplesPerFrame,
                                           (MpAudioSample*)pWaveHdr->lpData,
                                           mCurrentFrameTime);
           // Ok, we have received and pushed a frame to the manager,
           // Now we advance the frame time.
           mCurrentFrameTime += (mSamplesPerFrame*1000)/mSamplesPerSec;

           // Put the wave header back in the pool..
           MMRESULT res = MMSYSERR_NOERROR;

           res = waveInAddBuffer(mDevHandle, pWaveHdr, sizeof(WAVEHDR));
           if (res != MMSYSERR_NOERROR)
           {
              showWaveError("waveInAddBuffer", res, -1, __LINE__);
              mnAddBufferFailures++;
              if(mnAddBufferFailures >= mNumInBuffers)
              {
                 waveInClose(mDevHandle);
                 mDevHandle = NULL;
                 mWinMMDeviceId = -1;
              }
           }
        }
    }
    else if (uMsg == WIM_CLOSE)
    {
        printf("received WIM_CLOSE\n");
        mIsOpen = FALSE;
    }
}

void CALLBACK
MpidWinMM::waveInCallbackStatic(HWAVEIN hwi,
                                UINT uMsg,
                                void* dwInstance,
                                void* dwParam1,
                                void* dwParam2)
{
    assert(dwInstance != NULL);
    MpidWinMM* iddWntPtr = (MpidWinMM*)dwInstance;
    assert((uMsg == WIM_OPEN) || (hwi == iddWntPtr->mDevHandle));
    iddWntPtr->processAudioInput(hwi, uMsg, dwParam1);
}

/* //////////////////////////// PRIVATE /////////////////////////////////// */
<|endoftext|>


<commit_before>#pragma once

namespace principia {
namespace geometry {

// A simple abstraction for something that can take the values -1 and 1.  Useful
// for instance to represent the determinant of an orthogonal map.
class Sign {
 public:
  template<typename Scalar> explicit Sign(Scalar const& s);
  ~Sign() = default;

  inline bool Negative() const;
  inline bool Positive() const;

 private:
  bool const negative_;
  friend Sign operator*(Sign const& left, Sign const& right);
};

inline Sign operator*(Sign const& left, Sign const& right);

}  // namespace geometry
}  // namespace principia

#include "Geometry/Sign-body.hpp"
<commit_msg>Remove 'inline' from the header as it's redundant with the contents of the -body.hpp.<commit_after>#pragma once

namespace principia {
namespace geometry {

// A simple abstraction for something that can take the values -1 and 1.  Useful
// for instance to represent the determinant of an orthogonal map.
class Sign {
 public:
  template<typename Scalar> explicit Sign(Scalar const& s);
  ~Sign() = default;

  bool Negative() const;
  bool Positive() const;

 private:
  bool const negative_;
  friend Sign operator*(Sign const& left, Sign const& right);
};

Sign operator*(Sign const& left, Sign const& right);

}  // namespace geometry
}  // namespace principia

#include "Geometry/Sign-body.hpp"
<|endoftext|>


<commit_before>/* Kernel.cpp - stalkware(1) kernel implementation
 * stalkware - blah bleh - (C) 2013, Timo Buhrmester
 * See README for contact-, COPYING for license information.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#include <err.h>

#include "Kernel.h"

Kernel::Kernel()
: facmap_(facarr, facarr + (sizeof facarr / sizeof *facarr)),
  modmap_(),
  cfgmap_(),
  stmap_(),
  kerncfg_(),
  buddies_(),
  spc_(0)
{
}

Kernel::~Kernel()
{
}

void
Kernel::dump()
{
	/*for(map<Module*, map<string, cfgent>*>::const_iterator
			it = cfgmap_.begin(); it != cfgmap_.end(); it++) {
	}*/

	for(map<Module*, vector<vector<string> > >::const_iterator
			it = stmap_.begin(); it != stmap_.end(); it++) {
		fprintf(stderr, "stmap[%s] dump:\n",
				it->first->name().c_str());

		const vector<vector<string> > &vec = it->second;

		for(vector<vector<string> >::const_iterator
				st = vec.begin(); st != vec.end(); st++) {
			fprintf(stderr, "\t{\n");

			const vector<string> &nvec = *st;

			for(vector<string>::const_iterator
					nt = nvec.begin();
					nt != nvec.end(); nt++) {
				fprintf(stderr, "\t\t'%s'\n", nt->c_str());
			}

			fprintf(stderr, "\t}\n");

		}

	}

}

bool
Kernel::run()
{
	char *spc = NULL;

	for(map<string, Module*>::const_iterator it = modmap_.begin();
			it != modmap_.end(); it++) {
		Module *mod = it->second;
		map<string, cfgent> *cfg = cfgmap_[mod];
		mod->init(it->first, *cfg);
	}

	for(;;) {
		time_t tnext = time(NULL) + kerncfg_["pollint"].val.lng_;
		for(map<string, Module*>::const_iterator
				it = modmap_.begin(); it != modmap_.end();
				it++) {
			Module *mod = it->second;
			vector<pair<string, string> > res;
			try {
				it->second->check(stmap_[mod], res);
				for(vector<pair<string, string> >::
						const_iterator itt =
						res.begin(); itt !=
						res.end(); itt++) {
					buddies_[itt->first].tlast = time(NULL);
					buddies_[itt->first].plast = mod->pname();
					buddies_[itt->first].mlast = mod->name();
					buddies_[itt->first].ilast = itt->second;
				}
			} catch(std::exception e) {
				warnx("caught exception");
			}
		}

		if (spc_) {
			if (!spc) {
				spc = new char[spc_];
				memset(spc, '\n', spc_-1);
				spc[spc_-1] = '\0';
			} else //dont do it the first time
				puts(spc);
		}

		time_t now = time(NULL);

		for(map<string, buddy>::const_iterator it = buddies_.begin();
				it != buddies_.end(); it++) {
			buddy const& b = it->second;
			printf("%s (%ds ago as '%s' on '%s' using '%s')\n",
					it->first.c_str(), (int)(now-b.tlast),
					b.ilast.c_str(), b.plast.c_str(),
					b.mlast.c_str());
		}

		if (now < tnext)
			sleep(tnext - now);
	}

	return true;
}

void
Kernel::init(string const& stalkrc, int spacing)
{
	spc_ = spacing;
	process_stalkrc(stalkrc);
}

void
Kernel::process_stalkrc(string const& path)
{
	char *line = NULL;
	size_t len = 0;
	ssize_t read;

	errno = 0;
	FILE *fp = fopen(path.c_str(), "r");
	if (!fp) {
		warn("couldn't fopen() '%s'", path.c_str());
		return;
	}

	while ((read = getline(&line, &len, fp)) != -1) {
		char *dup = strdup(line);
		char *ptr = strchr(dup, '#');
		if (ptr)
			*ptr = 0;

		ptr = dup + strlen(dup) - 1;
		while(ptr >= dup && isspace(*ptr))
			*ptr-- = '\0';

		ptr = dup;
		while(isspace(*ptr))
			ptr++;

		if (strlen(ptr) == 0)
			continue;

		process_cfgline(ptr);

		free(dup);
	}

	fclose(fp);
	free(line);
}

void
Kernel::process_cfgline(const char *line)
{
	char *ctx, *ctx2; //context pointers for strtok_r
	char *dup = strdup(line);

	char *tok = strtok_r(dup, "\t ", &ctx);
	if (strcmp(tok, "load") == 0) {
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modclass = strdup(tok);
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modname = strdup(tok);
		if (strtok_r(NULL, "\t ", &ctx))
			warnx("too many tokens (some ignored): '%s'", line);
		load_mod(modname, modclass);

		free(modclass);
		free(modname);

	} else if (strcmp(tok, "stalk") == 0) {
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modname = strdup(tok);
		tok = strtok_r(NULL, "\0", &ctx);
		char *remainder = strdup(tok);
		vector<char*> namevec;

		char *start = remainder;
		char *tokstart, *tokend;
		while(nextqtok(start, &tokstart, &tokend)) {
			namevec.push_back(strndup(tokstart, (size_t)(tokend-tokstart)));

			start = tokend;
			if (*tokend)
				start++;
		}

		add_stalkee(modname, namevec);

		for(vector<char*>::const_iterator it = namevec.begin();
				it != namevec.end(); it++)
			free(*it);

		free(modname);
		free(remainder);

	} else {
		char *cmddup = strdup(tok);
		char *mod = NULL, *itok;
		if (strchr(cmddup, '_')) {
			itok = strtok_r(cmddup, "_", &ctx2);
			mod = strdup(itok);
			itok = strtok_r(NULL, "\t ", &ctx2);
		} else {
			itok = strtok_r(cmddup, "\t ", &ctx2);
		}

		char *sett = strdup(itok);
		itok = strtok_r(NULL, "\0", &ctx);
		while(isspace(*itok))
			itok++;

		char *val = strdup(itok);

		add_cfg(mod, sett, val);

		free(val);
		free(sett);
		free(mod);
		free(cmddup);
	}

	free(dup);
}

bool
Kernel::nextqtok(char *data, char **tokstart, char **tokend)
{
	char *start = data;

	while(isspace(*start))
		start++;

	if(!*start)
		return false;

	bool inq = false;
	bool quoted = false;
	char *end = start;
	while(*end) {
		if (*end == '"') {
			if (!(inq = !inq)) //leaving quotes
				break;
			else
				quoted = true;
		} else if (isspace(*end) && !inq)
			break;

		end++;
	}

	if (quoted)
		start++;

	*tokstart = start;
	*tokend = end;

	return true;
}

void
Kernel::add_stalkee(const char *mname, vector<char*> const& user)
{
	if (!modmap_.count(std::string(mname))) {
		warnx("unknown module: '%s'", mname);
		return;
	}

	Module *mod = modmap_[mname];
	vector<vector<string> > &stvec = stmap_[mod];

	stvec.push_back(vector<string>());

	for(vector<char*>::const_iterator it = user.begin();
			it != user.end(); it++) {
		stvec.back().push_back(string(*it));
	}

	string key(user[0]);
	buddies_[key].tlast = 0;
	buddies_[key].plast = "N/A";
	buddies_[key].mlast = "N/A";
	buddies_[key].ilast = "N/A";
}

void
Kernel::add_cfg(const char *mname, const char *sett, const char *val)
{
	map<string, cfgent> *cfg;
	if (mname) {
		if (!modmap_.count(std::string(mname))) {
			warnx("unknown module: '%s'", mname);
			return;
		}

		Module *mod = modmap_[mname];
		cfg = cfgmap_[mod];
	} else
		cfg = &kerncfg_;

	if ((*val >= '0' && *val <= '9') || *val == '-' || *val == '.') {
		if (strchr(val, '.')||strchr(val, 'e')||strchr(val, 'E')) {
			//treat as double
			(*cfg)[sett].typ = cfgent::DBL;
			(*cfg)[sett].val.dbl_ = strtod(val, NULL);
		} else {
			//treat as long
			(*cfg)[sett].typ = cfgent::LNG;
			(*cfg)[sett].val.lng_ = strtol(val, NULL, 10);
		}
	} else {
		//treat as string
		(*cfg)[sett].typ = cfgent::STR;
		if (*val == '"') {
			bool endq = *(val+strlen(val)-1) == '"';
			(*cfg)[sett].val.str_ = strndup(val + 1,
					strlen(val) - endq?2:1);
		} else
			(*cfg)[sett].val.str_ = strdup(val);
	}
}

void
Kernel::load_mod(const char *mname, const char *mclass)
{
	if (!facmap_.count(std::string(mclass))) {
		warnx("no such class: '%s' (not registered in Kernel.h?)",
				mclass);
		return;
	}

	if (modmap_.count(std::string(mname))) {
		warnx("name already in use: '%s'", mname);
		return;
	}

	Module *mod = facmap_[mclass]();
	if (!mod) {
		warnx("failed to instantiate '%s'", mclass);
		return;
	}

	modmap_[mname] = mod;
	cfgmap_[mod] = new map<string, cfgent>;
	stmap_[mod] = vector<vector<string> >();
}

<commit_msg>sort output list by last-seen<commit_after>/* Kernel.cpp - stalkware(1) kernel implementation
 * stalkware - blah bleh - (C) 2013, Timo Buhrmester
 * See README for contact-, COPYING for license information.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <deque>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#include <err.h>

#include "Kernel.h"

using std::deque;

Kernel::Kernel()
: facmap_(facarr, facarr + (sizeof facarr / sizeof *facarr)),
  modmap_(),
  cfgmap_(),
  stmap_(),
  kerncfg_(),
  buddies_(),
  spc_(0)
{
}

Kernel::~Kernel()
{
}

void
Kernel::dump()
{
	/*for(map<Module*, map<string, cfgent>*>::const_iterator
			it = cfgmap_.begin(); it != cfgmap_.end(); it++) {
	}*/

	for(map<Module*, vector<vector<string> > >::const_iterator
			it = stmap_.begin(); it != stmap_.end(); it++) {
		fprintf(stderr, "stmap[%s] dump:\n",
				it->first->name().c_str());

		const vector<vector<string> > &vec = it->second;

		for(vector<vector<string> >::const_iterator
				st = vec.begin(); st != vec.end(); st++) {
			fprintf(stderr, "\t{\n");

			const vector<string> &nvec = *st;

			for(vector<string>::const_iterator
					nt = nvec.begin();
					nt != nvec.end(); nt++) {
				fprintf(stderr, "\t\t'%s'\n", nt->c_str());
			}

			fprintf(stderr, "\t}\n");

		}

	}

}

bool
Kernel::run()
{
	char *spc = NULL;

	for(map<string, Module*>::const_iterator it = modmap_.begin();
			it != modmap_.end(); it++) {
		Module *mod = it->second;
		map<string, cfgent> *cfg = cfgmap_[mod];
		mod->init(it->first, *cfg);
	}

	for(;;) {
		time_t tnext = time(NULL) + kerncfg_["pollint"].val.lng_;
		for(map<string, Module*>::const_iterator
				it = modmap_.begin(); it != modmap_.end();
				it++) {
			Module *mod = it->second;
			vector<pair<string, string> > res;
			try {
				it->second->check(stmap_[mod], res);
				for(vector<pair<string, string> >::
						const_iterator itt =
						res.begin(); itt !=
						res.end(); itt++) {
					buddies_[itt->first].tlast = time(NULL);
					buddies_[itt->first].plast = mod->pname();
					buddies_[itt->first].mlast = mod->name();
					buddies_[itt->first].ilast = itt->second;
				}
			} catch(std::exception e) {
				warnx("caught exception");
			}
		}

		if (spc_) {
			if (!spc) {
				spc = new char[spc_];
				memset(spc, '\n', spc_-1);
				spc[spc_-1] = '\0';
			} else //dont do it the first time
				puts(spc);
		}

		time_t now = time(NULL);

		deque<string> unordered;
		deque<string> ordered;

		for(map<string, buddy>::const_iterator it = buddies_.begin();
				it != buddies_.end(); it++)
			unordered.push_back(it->first);

		while(unordered.size() > 0) {
			time_t tmax = 0;
			string maxkey = "";
			int maxpos = 0;
			int c = 0;
			for(deque<string>::const_iterator
					it = unordered.begin();
					it != unordered.end(); it++) {
				if (maxkey=="" || buddies_[*it].tlast > tmax) {
					tmax = buddies_[*it].tlast;
					maxkey = *it;
					maxpos = c;
				}
				c++;
			}

			ordered.push_back(maxkey);
			unordered.erase(unordered.begin() + maxpos);
		}
		for(deque<string>::const_iterator it = ordered.begin();
				it != ordered.end(); it++) {
			buddy const& b = buddies_[*it];
			printf("%s (%ds ago as '%s' on '%s' using '%s')\n",
					it->c_str(), (int)(now-b.tlast),
					b.ilast.c_str(), b.plast.c_str(),
					b.mlast.c_str());
		}

		if (now < tnext)
			sleep(tnext - now);
	}

	return true;
}

void
Kernel::init(string const& stalkrc, int spacing)
{
	spc_ = spacing;
	process_stalkrc(stalkrc);
}

void
Kernel::process_stalkrc(string const& path)
{
	char *line = NULL;
	size_t len = 0;
	ssize_t read;

	errno = 0;
	FILE *fp = fopen(path.c_str(), "r");
	if (!fp) {
		warn("couldn't fopen() '%s'", path.c_str());
		return;
	}

	while ((read = getline(&line, &len, fp)) != -1) {
		char *dup = strdup(line);
		char *ptr = strchr(dup, '#');
		if (ptr)
			*ptr = 0;

		ptr = dup + strlen(dup) - 1;
		while(ptr >= dup && isspace(*ptr))
			*ptr-- = '\0';

		ptr = dup;
		while(isspace(*ptr))
			ptr++;

		if (strlen(ptr) == 0)
			continue;

		process_cfgline(ptr);

		free(dup);
	}

	fclose(fp);
	free(line);
}

void
Kernel::process_cfgline(const char *line)
{
	char *ctx, *ctx2; //context pointers for strtok_r
	char *dup = strdup(line);

	char *tok = strtok_r(dup, "\t ", &ctx);
	if (strcmp(tok, "load") == 0) {
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modclass = strdup(tok);
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modname = strdup(tok);
		if (strtok_r(NULL, "\t ", &ctx))
			warnx("too many tokens (some ignored): '%s'", line);
		load_mod(modname, modclass);

		free(modclass);
		free(modname);

	} else if (strcmp(tok, "stalk") == 0) {
		tok = strtok_r(NULL, "\t ", &ctx);
		char *modname = strdup(tok);
		tok = strtok_r(NULL, "\0", &ctx);
		char *remainder = strdup(tok);
		vector<char*> namevec;

		char *start = remainder;
		char *tokstart, *tokend;
		while(nextqtok(start, &tokstart, &tokend)) {
			namevec.push_back(strndup(tokstart, (size_t)(tokend-tokstart)));

			start = tokend;
			if (*tokend)
				start++;
		}

		add_stalkee(modname, namevec);

		for(vector<char*>::const_iterator it = namevec.begin();
				it != namevec.end(); it++)
			free(*it);

		free(modname);
		free(remainder);

	} else {
		char *cmddup = strdup(tok);
		char *mod = NULL, *itok;
		if (strchr(cmddup, '_')) {
			itok = strtok_r(cmddup, "_", &ctx2);
			mod = strdup(itok);
			itok = strtok_r(NULL, "\t ", &ctx2);
		} else {
			itok = strtok_r(cmddup, "\t ", &ctx2);
		}

		char *sett = strdup(itok);
		itok = strtok_r(NULL, "\0", &ctx);
		while(isspace(*itok))
			itok++;

		char *val = strdup(itok);

		add_cfg(mod, sett, val);

		free(val);
		free(sett);
		free(mod);
		free(cmddup);
	}

	free(dup);
}

bool
Kernel::nextqtok(char *data, char **tokstart, char **tokend)
{
	char *start = data;

	while(isspace(*start))
		start++;

	if(!*start)
		return false;

	bool inq = false;
	bool quoted = false;
	char *end = start;
	while(*end) {
		if (*end == '"') {
			if (!(inq = !inq)) //leaving quotes
				break;
			else
				quoted = true;
		} else if (isspace(*end) && !inq)
			break;

		end++;
	}

	if (quoted)
		start++;

	*tokstart = start;
	*tokend = end;

	return true;
}

void
Kernel::add_stalkee(const char *mname, vector<char*> const& user)
{
	if (!modmap_.count(std::string(mname))) {
		warnx("unknown module: '%s'", mname);
		return;
	}

	Module *mod = modmap_[mname];
	vector<vector<string> > &stvec = stmap_[mod];

	stvec.push_back(vector<string>());

	for(vector<char*>::const_iterator it = user.begin();
			it != user.end(); it++) {
		stvec.back().push_back(string(*it));
	}

	string key(user[0]);
	buddies_[key].tlast = 0;
	buddies_[key].plast = "N/A";
	buddies_[key].mlast = "N/A";
	buddies_[key].ilast = "N/A";
}

void
Kernel::add_cfg(const char *mname, const char *sett, const char *val)
{
	map<string, cfgent> *cfg;
	if (mname) {
		if (!modmap_.count(std::string(mname))) {
			warnx("unknown module: '%s'", mname);
			return;
		}

		Module *mod = modmap_[mname];
		cfg = cfgmap_[mod];
	} else
		cfg = &kerncfg_;

	if ((*val >= '0' && *val <= '9') || *val == '-' || *val == '.') {
		if (strchr(val, '.')||strchr(val, 'e')||strchr(val, 'E')) {
			//treat as double
			(*cfg)[sett].typ = cfgent::DBL;
			(*cfg)[sett].val.dbl_ = strtod(val, NULL);
		} else {
			//treat as long
			(*cfg)[sett].typ = cfgent::LNG;
			(*cfg)[sett].val.lng_ = strtol(val, NULL, 10);
		}
	} else {
		//treat as string
		(*cfg)[sett].typ = cfgent::STR;
		if (*val == '"') {
			bool endq = *(val+strlen(val)-1) == '"';
			(*cfg)[sett].val.str_ = strndup(val + 1,
					strlen(val) - endq?2:1);
		} else
			(*cfg)[sett].val.str_ = strdup(val);
	}
}

void
Kernel::load_mod(const char *mname, const char *mclass)
{
	if (!facmap_.count(std::string(mclass))) {
		warnx("no such class: '%s' (not registered in Kernel.h?)",
				mclass);
		return;
	}

	if (modmap_.count(std::string(mname))) {
		warnx("name already in use: '%s'", mname);
		return;
	}

	Module *mod = facmap_[mclass]();
	if (!mod) {
		warnx("failed to instantiate '%s'", mclass);
		return;
	}

	modmap_[mname] = mod;
	cfgmap_[mod] = new map<string, cfgent>;
	stmap_[mod] = vector<vector<string> >();
}

<|endoftext|>


<commit_before>/*
Dining1.cpp

First, coin flip to select hallway.
Next, coin flip to select section.
Finally, similar random pattern to select order of section.

Author:   J. Lerma
Date:     July 12, 2012
/*
// INCLUDES
#include <iostream>
#include <cstring>
#include <ctime>
#include <cstdlib>

// NAMESPACE
using namespace std;

// DEFINES
char last = 'D';
char dining[12];
char AH[4] = {'A','B','C','E'};
char BH[6] = {'F','G','H','J','K','L'};
bool flip;

// DECLARATIONS
int HeadsorTails();
int OneThruThree();
int TwoThruFour();
void AHfirst();
void BHfirst();
void ABfirst();
void CElast();
void FGHfirst();
void JKLfirst();

// MAIN
int main ()
{

srand ( (unsigned)time(NULL) ); //Uses time as a seed for rand

flip = HeadsorTails();

if (flip)
{
AHfirst();
}
else
{
BHfirst();
}

dining[10] = last;

cout << "The order of Dining Hall is: " << dining << endl;

return(0);
}

// FUNCTIONS
int HeadsorTails()
{
int i = (rand()%2)) //btwn 0-1

return(i);
}

int OneThruThree()
{
int i = (rand()%3)+1) //btwn 1-3

return (i);
}

void AHfirst()
{
flip = HeadsorTails();

if (flip)
{
ABfirst();
CElast();
}
else
{
CEfirst();
ABlast();
}
}

void ABfirst()
{
flip = HeadsorTails();
if (flip)
{
dining[0] = AH[0]; // 'A'
dining[1] = AH[1]; // 'B'
}
else
{
dining[0] = AH[1]; // 'B'
dining[1] = AH[0]; // 'A'
}
}

void ABlast()
{
flip = HeadsorTails();
if (flip)
{
dining[2] = AH[0]; // 'A'
dining[3] = AH[1]; // 'B'
}
else
{
dining[2] = AH[1]; // 'B'
dining[3] = AH[0]; // 'A'
}
}

void CEfirst()
{
flip = HeadsorTails();

if (flip)
{
dining[0] = AH[2]; // 'C'
dining[1] = AH[3]; // 'E'
}
else
{
dining[0] = AH[3]; // 'E'
dining[1] = AH[2]; // 'C'
}
}

void CElast()
{
flip = HeadsorTails();

if (flip)
{
dining[2] = AH[2]; // 'C'
dining[3] = AH[3]; // 'E'
}
else
{
dining[2] = AH[3]; // 'E'
dining[3] = AH[2]; // 'C'
}
}

void BHfirst()
{
flip = HeadsorTails();

if (flip)
{
FGHfirst();
JKLlast();
}
else
{
JKLfirst();
FGHlast();
}
}

void FGHfirst()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[4] = BH[0]; // 'F'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[1]; // 'G'
dining[6] = BH[2]; // 'H'
}
else
{
dining[5] = BH[2]; // 'H'
dining[6] = BH[1]; // 'G'
}
}
else if ((temp = 2))
{
dining[4] = BH[1]; // 'G'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[0]; // 'F'
dining[6] = BH[2]; // 'H'
}
else
{
dining[5] = BH[2]; // 'H'
dining[6] = BH[0]; // 'F'
}
}
else
{
dining[4] = BH[2]; // 'H'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[0]; // 'F'
dining[6] = BH[1]; // 'G'
}
else
{
dining[5] = BH[1]; // 'G'
dining[6] = BH[0]; // 'F'
}
}
}

void FGHlast()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[7] = BH[0]; // 'F'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[1]; // 'G'
dining[9] = BH[2]; // 'H'
}
else
{
dining[8] = BH[2]; // 'H'
dining[9] = BH[1]; // 'G'
}
}
else if ((temp = 2))
{
dining[7] = BH[1]; // 'G'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[0]; // 'F'
dining[9] = BH[2]; // 'H'
}
else
{
dining[8] = BH[2]; // 'H'
dining[9] = BH[0]; // 'F'
}
}
else
{
dining[7] = BH[2]; // 'H'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[0]; // 'F'
dining[9] = BH[1]; // 'G'
}
else
{
dining[8] = BH[1]; // 'G'
dining[9] = BH[0]; // 'F'
}
}
}

void JKLfirst()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[4] = BH[3]; // 'J'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[4]; // 'K'
dining[6] = BH[5]; // 'L'
}
else
{
dining[5] = BH[5]; // 'L'
dining[6] = BH[4]; // 'K'
}
}
else if ((temp = 2))
{
dining[4] = BH[4]; // 'K'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[3]; // 'J'
dining[6] = BH[5]; // 'L'
}
else
{
dining[5] = BH[5]; // 'L'
dining[6] = BH[3]; // 'J'
}
}
else
{
dining[4] = BH[5]; // 'L'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[3]; // 'J'
dining[6] = BH[4]; // 'K'
}
else
{
dining[5] = BH[4]; // 'K'
dining[6] = BH[3]; // 'J'
}
}
}

void JKLlast()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[7] = BH[3]; // 'J'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[4]; // 'K'
dining[9] = BH[5]; // 'L'
}
else
{
dining[8] = BH[5]; // 'L'
dining[9] = BH[4]; // 'K'
}
}
else if ((temp = 2))
{
dining[7] = BH[4]; // 'K'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[3]; // 'J'
dining[9] = BH[5]; // 'L'
}
else
{
dining[8] = BH[5]; // 'L'
dining[9] = BH[3]; // 'J'
}
}
else
{
dining[7] = BH[5]; // 'L'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[3]; // 'J'
dining[9] = BH[4]; // 'K'
}
else
{
dining[8] = BH[4]; // 'K'
dining[9] = BH[3]; // 'J'
}
}
}
<commit_msg>Fixed comment<commit_after>/*
Dining1.cpp

First, coin flip to select hallway.
Next, coin flip to select section.
Finally, similar random pattern to select order of section.

Author:   J. Lerma
Date:     July 12, 2012
*/
// INCLUDES
#include <iostream>
#include <cstring>
#include <ctime>
#include <cstdlib>

// NAMESPACE
using namespace std;

// DEFINES
char last = 'D';
char dining[12];
char AH[4] = {'A','B','C','E'};
char BH[6] = {'F','G','H','J','K','L'};
bool flip;

// DECLARATIONS
int HeadsorTails();
int OneThruThree();
int TwoThruFour();
void AHfirst();
void BHfirst();
void ABfirst();
void CElast();
void FGHfirst();
void JKLfirst();

// MAIN
int main ()
{

srand ( (unsigned)time(NULL) ); //Uses time as a seed for rand

flip = HeadsorTails();

if (flip)
{
AHfirst();
}
else
{
BHfirst();
}

dining[10] = last;

cout << "The order of Dining Hall is: " << dining << endl;

return(0);
}

// FUNCTIONS
int HeadsorTails()
{
int i = (rand()%2)) //btwn 0-1

return(i);
}

int OneThruThree()
{
int i = (rand()%3)+1) //btwn 1-3

return (i);
}

void AHfirst()
{
flip = HeadsorTails();

if (flip)
{
ABfirst();
CElast();
}
else
{
CEfirst();
ABlast();
}
}

void ABfirst()
{
flip = HeadsorTails();
if (flip)
{
dining[0] = AH[0]; // 'A'
dining[1] = AH[1]; // 'B'
}
else
{
dining[0] = AH[1]; // 'B'
dining[1] = AH[0]; // 'A'
}
}

void ABlast()
{
flip = HeadsorTails();
if (flip)
{
dining[2] = AH[0]; // 'A'
dining[3] = AH[1]; // 'B'
}
else
{
dining[2] = AH[1]; // 'B'
dining[3] = AH[0]; // 'A'
}
}

void CEfirst()
{
flip = HeadsorTails();

if (flip)
{
dining[0] = AH[2]; // 'C'
dining[1] = AH[3]; // 'E'
}
else
{
dining[0] = AH[3]; // 'E'
dining[1] = AH[2]; // 'C'
}
}

void CElast()
{
flip = HeadsorTails();

if (flip)
{
dining[2] = AH[2]; // 'C'
dining[3] = AH[3]; // 'E'
}
else
{
dining[2] = AH[3]; // 'E'
dining[3] = AH[2]; // 'C'
}
}

void BHfirst()
{
flip = HeadsorTails();

if (flip)
{
FGHfirst();
JKLlast();
}
else
{
JKLfirst();
FGHlast();
}
}

void FGHfirst()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[4] = BH[0]; // 'F'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[1]; // 'G'
dining[6] = BH[2]; // 'H'
}
else
{
dining[5] = BH[2]; // 'H'
dining[6] = BH[1]; // 'G'
}
}
else if ((temp = 2))
{
dining[4] = BH[1]; // 'G'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[0]; // 'F'
dining[6] = BH[2]; // 'H'
}
else
{
dining[5] = BH[2]; // 'H'
dining[6] = BH[0]; // 'F'
}
}
else
{
dining[4] = BH[2]; // 'H'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[0]; // 'F'
dining[6] = BH[1]; // 'G'
}
else
{
dining[5] = BH[1]; // 'G'
dining[6] = BH[0]; // 'F'
}
}
}

void FGHlast()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[7] = BH[0]; // 'F'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[1]; // 'G'
dining[9] = BH[2]; // 'H'
}
else
{
dining[8] = BH[2]; // 'H'
dining[9] = BH[1]; // 'G'
}
}
else if ((temp = 2))
{
dining[7] = BH[1]; // 'G'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[0]; // 'F'
dining[9] = BH[2]; // 'H'
}
else
{
dining[8] = BH[2]; // 'H'
dining[9] = BH[0]; // 'F'
}
}
else
{
dining[7] = BH[2]; // 'H'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[0]; // 'F'
dining[9] = BH[1]; // 'G'
}
else
{
dining[8] = BH[1]; // 'G'
dining[9] = BH[0]; // 'F'
}
}
}

void JKLfirst()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[4] = BH[3]; // 'J'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[4]; // 'K'
dining[6] = BH[5]; // 'L'
}
else
{
dining[5] = BH[5]; // 'L'
dining[6] = BH[4]; // 'K'
}
}
else if ((temp = 2))
{
dining[4] = BH[4]; // 'K'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[3]; // 'J'
dining[6] = BH[5]; // 'L'
}
else
{
dining[5] = BH[5]; // 'L'
dining[6] = BH[3]; // 'J'
}
}
else
{
dining[4] = BH[5]; // 'L'
temp = HeadsorTails();
if ((temp = 1))
{
dining[5] = BH[3]; // 'J'
dining[6] = BH[4]; // 'K'
}
else
{
dining[5] = BH[4]; // 'K'
dining[6] = BH[3]; // 'J'
}
}
}

void JKLlast()
{
int temp = OneThruThree();

if ((temp = 1))
{
dining[7] = BH[3]; // 'J'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[4]; // 'K'
dining[9] = BH[5]; // 'L'
}
else
{
dining[8] = BH[5]; // 'L'
dining[9] = BH[4]; // 'K'
}
}
else if ((temp = 2))
{
dining[7] = BH[4]; // 'K'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[3]; // 'J'
dining[9] = BH[5]; // 'L'
}
else
{
dining[8] = BH[5]; // 'L'
dining[9] = BH[3]; // 'J'
}
}
else
{
dining[7] = BH[5]; // 'L'
temp = HeadsorTails();
if ((temp = 1))
{
dining[8] = BH[3]; // 'J'
dining[9] = BH[4]; // 'K'
}
else
{
dining[8] = BH[4]; // 'K'
dining[9] = BH[3]; // 'J'
}
}
}
<|endoftext|>