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

#include "emulatorhandler.h"

#include "../global.h"
#include "../common.h"

#include <QFile>
#include <QMessageBox>
#include <QProcess>
#include <QCryptographicHash>

#include <quazip5/quazip.h>
#include <quazip5/quazipfile.h>

EmulatorHandler::EmulatorHandler(QWidget *parent) : QObject(parent)
{
    this->parent = parent;

    lastOutput = "";
}

void EmulatorHandler::checkStatus(int status)
{
    if (status > 0) {
        QMessageBox exitDialog(parent);
        exitDialog.setWindowTitle(tr("Warning"));
        exitDialog.setText(tr("<ParentName> quit unexpectedly. Check the log for more information.")
                           .replace("<ParentName>",ParentName));
        exitDialog.setIcon(QMessageBox::Warning);
        exitDialog.addButton(QMessageBox::Ok);
        exitDialog.addButton(tr("View Log..."), QMessageBox::HelpRole);

        int ret = exitDialog.exec();
        if (ret == 0) emit showLog();
    }

    updateStatus(tr("Emulation stopped"), 3000);
}

void EmulatorHandler::cleanTemp()
{
    QFile::remove(QDir::tempPath() + "/" + AppNameLower + "/" + qgetenv("USER") + "/temp.bin");
    QFile::remove(QDir::tempPath() + "/" + AppNameLower + "/" + qgetenv("USER") + "/64dd-temp.bin");
}

void EmulatorHandler::emitFinished()
{
    emit finished();
}

QStringList EmulatorHandler::parseArgString(QString argString)
{
    QStringList result;
    QString arg;
    bool inQuote = false;
    bool inApos = false;

    for (int i = 0; i < argString.size(); i++)
    {
        // Check if inside of a quote
        if (argString.at(i) == QLatin1Char('"')) {
            inQuote = !inQuote;

            // Only continue if outside of both quotes and apostrophes
            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
        }

        // Same check for apostrophes
        if (argString.at(i) == QLatin1Char('\'')) {
            inApos = !inApos;
            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
        }

        if (!inQuote && !inApos && argString.at(i).isSpace()) {
            if (!arg.isEmpty()) {
                result += arg;
                arg.clear();
            }
        } else
            arg += argString.at(i);
    }

    if (!arg.isEmpty())
        result += arg;

    return result;
}

void EmulatorHandler::readOutput()
{
    QString output = emulatorProc->readAllStandardOutput();
    QStringList outputList = output.split("\n");

    int lastIndex = outputList.lastIndexOf(QRegExp("^.*VI/s.*MHz$"));

    if (lastIndex >= 0)
        updateStatus(outputList[lastIndex]);

    lastOutput.append(output);
}

void EmulatorHandler::startEmulator(QDir romDir, QString romFileName, QString zipFileName,
                                    QDir ddDir, QString ddFileName, QString ddZipName)
{
    QString completeRomPath = "", complete64DDPath = "";
    bool zip = false, ddZip = false;

    //If zipped file, extract and write to temp location for loading
    QStringList zippedFiles;
    zippedFiles << zipFileName << ddZipName;
    bool ddZipCheck = false;

    foreach (QString zippedFile, zippedFiles)
    {
        if (zippedFile != "") {
            QString fileInZip, tempName, romPath, zipFile;

            if (!ddZipCheck) {
                zip = true;
                tempName = "/temp.bin";
                fileInZip = romFileName;
                zipFile = romDir.absoluteFilePath(zippedFile);
            } else {
                ddZip = true;
                tempName = "/64dd-temp.bin";
                fileInZip = ddFileName;
                zipFile = ddDir.absoluteFilePath(zippedFile);
            }

            QByteArray *romData = getZippedRom(fileInZip, zipFile);

            QString tempDir = QDir::tempPath() + "/" + AppNameLower + "/" + qgetenv("USER");
            QDir().mkpath(tempDir);

            romPath = tempDir + tempName;
            QFile tempRom(romPath);

            tempRom.open(QIODevice::WriteOnly);
            tempRom.write(*romData);
            tempRom.close();

            delete romData;

            if (!ddZipCheck)
                completeRomPath = romPath;
            else
                complete64DDPath = romPath;
        }

        ddZipCheck = true;
    }

    if (zipFileName == "" && romFileName != "")
        completeRomPath = romDir.absoluteFilePath(romFileName);
    if (ddZipName == "" && ddFileName != "")
        complete64DDPath = ddDir.absoluteFilePath(ddFileName);

    QString emulatorPath = SETTINGS.value("Paths/cen64", "").toString();
    QString pifPath = SETTINGS.value("Paths/pifrom", "").toString();
    QString ddIPLPath = SETTINGS.value("Paths/ddiplrom", "").toString();

    bool ddMode = false;
    if (SETTINGS.value("Emulation/64dd", "").toString() == "true")
        ddMode = true;

    QFile emulatorFile(emulatorPath);
    QFile pifFile(pifPath);
    QFile ddIPL(ddIPLPath);
    QFile romFile(completeRomPath);
    QFile ddFile(complete64DDPath);

    //Sanity checks
    if (!emulatorFile.exists() || QFileInfo(emulatorFile).isDir() || !QFileInfo(emulatorFile).isExecutable()) {
        QMessageBox::warning(parent, tr("Warning"),
                             tr("<ParentName> executable not found.").replace("<ParentName>",ParentName));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (!pifFile.exists() || QFileInfo(pifFile).isDir()) {
        QMessageBox::warning(parent, tr("Warning"), tr("PIF IPL file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (ddIPLPath != "" && (!ddIPL.exists() || QFileInfo(ddIPL).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("64DD IPL file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath != "" && (!romFile.exists() || QFileInfo(romFile).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("ROM file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath == "" && complete64DDPath != ""
            && (!ddFile.exists() || QFileInfo(ddFile).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("64DD ROM file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath == "" && complete64DDPath == "") {
        QMessageBox::warning(parent, tr("Warning"), tr("No ROM selected."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath != "") {
        romFile.open(QIODevice::ReadOnly);
        QByteArray romCheck = romFile.read(4);
        romFile.close();

        if (romCheck.toHex() != "80371240") {
            if (romCheck.toHex() == "e848d316") { // 64DD file instead
                if (complete64DDPath == "" && ddMode) {
                    complete64DDPath = completeRomPath;
                    ddFile.setFileName(complete64DDPath);
                    completeRomPath = "";
                } else {
                    QMessageBox::warning(parent, tr("Warning"), tr("64DD not enabled."));
                    if (zip || ddZip) cleanTemp();
                    return;
                }
            } else {
                QMessageBox::warning(parent, tr("Warning"), tr("Not a valid Z64 File."));
                if (zip || ddZip) cleanTemp();
                return;
            }
        }
    }

    if (complete64DDPath != "" && ddMode) {
        ddFile.open(QIODevice::ReadOnly);
        QByteArray romCheck = ddFile.read(4);
        ddFile.close();

        if (romCheck.toHex() != "e848d316") {
            QMessageBox::warning(parent, tr("Warning"), tr("Not a valid 64DD File."));
            if (zip || ddZip) cleanTemp();
            return;
        }
    }

    QStringList args;

    if (SETTINGS.value("Saves/individualsave", "").toString() == "true") {
        QString eeprom4kPath = SETTINGS.value("Saves/eeprom4k", "").toString();
        QString eeprom16kPath = SETTINGS.value("Saves/eeprom16k", "").toString();
        QString sramPath = SETTINGS.value("Saves/sram", "").toString();
        QString flashPath = SETTINGS.value("Saves/flash", "").toString();

        if (eeprom4kPath != "")
            args << "-eep4k" << eeprom4kPath;
        if (eeprom16kPath != "")
            args << "-eep16k" << eeprom16kPath;
        if (sramPath != "")
            args << "-sram" << sramPath;
        if (flashPath != "")
            args << "-flash" << flashPath;
    } else {
        QString savesPath = SETTINGS.value("Saves/directory", "").toString();
        if (savesPath != "") {
            QDir savesDir(savesPath);

            if (savesDir.exists()) {
                romFile.open(QIODevice::ReadOnly);
                QByteArray *romData = new QByteArray(romFile.readAll());
                romFile.close();

                QString romMD5 = QString(QCryptographicHash::hash(*romData,
                                                                  QCryptographicHash::Md5).toHex());

                QString romBaseName = QFileInfo(romFile).completeBaseName();
                QString eeprom4kFileName = romBaseName + "." + romMD5 + ".eep4k";
                QString eeprom16kFileName = romBaseName + "." + romMD5 + ".eep16k";
                QString sramFileName = romBaseName + "." + romMD5 + ".sram";
                QString flashFileName = romBaseName + "." + romMD5 + ".flash";
                QString eeprom4kPath = savesDir.absoluteFilePath(eeprom4kFileName);
                QString eeprom16kPath = savesDir.absoluteFilePath(eeprom16kFileName);
                QString sramPath = savesDir.absoluteFilePath(sramFileName);
                QString flashPath = savesDir.absoluteFilePath(flashFileName);

                // Check ROM catalog to determine save type
                QString catalogFile = SETTINGS.value("Paths/catalog", "").toString();
                if (QFileInfo(catalogFile).exists()) {
                    QSettings romCatalog(catalogFile, QSettings::IniFormat, parent);
                    QString saveType = romCatalog.value(romMD5.toUpper()+"/SaveType","").toString();

                    if (saveType == "Eeprom 4KB")
                        args << "-eep4k"  << eeprom4kPath;
                    else if (saveType == "Eeprom 16KB")
                        args << "-eep16k"  << eeprom16kPath;
                    else if (saveType == "SRAM")
                        args << "-sram"  << sramPath;
                    else if (saveType == "Flash RAM")
                        args << "-flash"  << flashPath;
                    else if (saveType == "Controller Pack");
                    else
                        args << "-eep4k"  << eeprom4kPath
                             << "-eep16k" << eeprom16kPath
                             << "-sram"   << sramPath
                             << "-flash"  << flashPath;
                } else {
                    args << "-eep4k"  << eeprom4kPath
                         << "-eep16k" << eeprom16kPath
                         << "-sram"   << sramPath
                         << "-flash"  << flashPath;
                }

                delete romData;
            }
        }
    }

    for (int i = 1; i <= 4; i++)
    {
        QString ctrl = "Controller"+QString::number(i);

        if (SETTINGS.value(ctrl+"/enabled", "").toString() == "true") {
            args << "-controller";
            QString options = "num="+QString::number(i);

            int accessory = SETTINGS.value(ctrl+"/accessory", 0).toInt();
            QString memPak = SETTINGS.value(ctrl+"/mempak", "").toString();
            QString tPakROM = SETTINGS.value(ctrl+"/tpakrom", "").toString();
            QString tPakSave = SETTINGS.value(ctrl+"/tpaksave", "").toString();

            if (accessory == 1) //Rumble Pak
                options += ",pak=rumble";
            else if (accessory == 2 && memPak != "") //Controller Pak
                options += ",mempak="+memPak;
            else if (accessory == 3 && tPakROM != "" && tPakSave != "") //Transfer Pak
                options += ",tpak_rom="+tPakROM+",tpak_save="+tPakSave;

            args << options;
        }
    }

    if (SETTINGS.value("Emulation/multithread", "").toString() == "true")
        args << "-multithread";
    if (SETTINGS.value("Emulation/noaudio", "").toString() == "true")
        args << "-noaudio";
    if (SETTINGS.value("Emulation/novideo", "").toString() == "true")
        args << "-novideo";

    if (ddIPLPath != "" && complete64DDPath != "" && ddMode)
        args << "-ddipl" << ddIPLPath << "-ddrom" << complete64DDPath;
    else if (completeRomPath == "") {
        QMessageBox::warning(parent, tr("Warning"), tr("No ROM selected or 64DD not enabled."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    QString otherParameters = SETTINGS.value("Other/parameters", "").toString();
    if (otherParameters != "")
        args.append(parseArgString(otherParameters));

    args << pifPath;

    if (completeRomPath != "")
        args << completeRomPath;

    emulatorProc = new QProcess(this);
    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(emitFinished()));
    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(checkStatus(int)));

    if (zip || ddZip)
        connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(cleanTemp()));

    if (SETTINGS.value("Other/consoleoutput", "").toString() == "true")
        emulatorProc->setProcessChannelMode(QProcess::ForwardedChannels);
    else {
        connect(emulatorProc, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
        emulatorProc->setProcessChannelMode(QProcess::MergedChannels);
    }

    //clear log
    lastOutput = "";

    emulatorProc->start(emulatorPath, args);

    //Add command to log
    QString executable = emulatorPath;
    if (executable.contains(" "))
        executable = '"' + executable + '"';

    QString argString;

    foreach(QString arg, args)
    {
        if (arg.contains(" "))
            argString += " \"" + arg + "\"";
        else
            argString += " " + arg;
    }

    lastOutput.append(executable + argString + "\n\n");

    updateStatus(tr("Emulation started"), 3000);
    emit started();
}

void EmulatorHandler::stopEmulator()
{
    emulatorProc->terminate();
}

void EmulatorHandler::updateStatus(QString message, int timeout)
{
    emit statusUpdate(message, timeout);
}
<commit_msg>Fix temp directory path for multi user setup (dh4/mupen64plus-qt#50)<commit_after>/***
 * Copyright (c) 2013, Dan Hasting
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the organization nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ***/

#include "emulatorhandler.h"

#include "../global.h"
#include "../common.h"

#include <QFile>
#include <QMessageBox>
#include <QProcess>
#include <QCryptographicHash>

#include <quazip5/quazip.h>
#include <quazip5/quazipfile.h>

EmulatorHandler::EmulatorHandler(QWidget *parent) : QObject(parent)
{
    this->parent = parent;

    lastOutput = "";
}

void EmulatorHandler::checkStatus(int status)
{
    if (status > 0) {
        QMessageBox exitDialog(parent);
        exitDialog.setWindowTitle(tr("Warning"));
        exitDialog.setText(tr("<ParentName> quit unexpectedly. Check the log for more information.")
                           .replace("<ParentName>",ParentName));
        exitDialog.setIcon(QMessageBox::Warning);
        exitDialog.addButton(QMessageBox::Ok);
        exitDialog.addButton(tr("View Log..."), QMessageBox::HelpRole);

        int ret = exitDialog.exec();
        if (ret == 0) emit showLog();
    }

    updateStatus(tr("Emulation stopped"), 3000);
}

void EmulatorHandler::cleanTemp()
{
    QFile::remove(QDir::tempPath() + "/" + AppNameLower + "-" + qgetenv("USER") + "/temp.bin");
    QFile::remove(QDir::tempPath() + "/" + AppNameLower + "-" + qgetenv("USER") + "/64dd-temp.bin");
}

void EmulatorHandler::emitFinished()
{
    emit finished();
}

QStringList EmulatorHandler::parseArgString(QString argString)
{
    QStringList result;
    QString arg;
    bool inQuote = false;
    bool inApos = false;

    for (int i = 0; i < argString.size(); i++)
    {
        // Check if inside of a quote
        if (argString.at(i) == QLatin1Char('"')) {
            inQuote = !inQuote;

            // Only continue if outside of both quotes and apostrophes
            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
        }

        // Same check for apostrophes
        if (argString.at(i) == QLatin1Char('\'')) {
            inApos = !inApos;
            if (arg.isEmpty() || (!inQuote && !inApos)) continue;
        }

        if (!inQuote && !inApos && argString.at(i).isSpace()) {
            if (!arg.isEmpty()) {
                result += arg;
                arg.clear();
            }
        } else
            arg += argString.at(i);
    }

    if (!arg.isEmpty())
        result += arg;

    return result;
}

void EmulatorHandler::readOutput()
{
    QString output = emulatorProc->readAllStandardOutput();
    QStringList outputList = output.split("\n");

    int lastIndex = outputList.lastIndexOf(QRegExp("^.*VI/s.*MHz$"));

    if (lastIndex >= 0)
        updateStatus(outputList[lastIndex]);

    lastOutput.append(output);
}

void EmulatorHandler::startEmulator(QDir romDir, QString romFileName, QString zipFileName,
                                    QDir ddDir, QString ddFileName, QString ddZipName)
{
    QString completeRomPath = "", complete64DDPath = "";
    bool zip = false, ddZip = false;

    //If zipped file, extract and write to temp location for loading
    QStringList zippedFiles;
    zippedFiles << zipFileName << ddZipName;
    bool ddZipCheck = false;

    foreach (QString zippedFile, zippedFiles)
    {
        if (zippedFile != "") {
            QString fileInZip, tempName, romPath, zipFile;

            if (!ddZipCheck) {
                zip = true;
                tempName = "/temp.bin";
                fileInZip = romFileName;
                zipFile = romDir.absoluteFilePath(zippedFile);
            } else {
                ddZip = true;
                tempName = "/64dd-temp.bin";
                fileInZip = ddFileName;
                zipFile = ddDir.absoluteFilePath(zippedFile);
            }

            QByteArray *romData = getZippedRom(fileInZip, zipFile);

            QString tempDir = QDir::tempPath() + "/" + AppNameLower + "-" + qgetenv("USER");
            QDir().mkpath(tempDir);

            romPath = tempDir + tempName;
            QFile tempRom(romPath);

            tempRom.open(QIODevice::WriteOnly);
            tempRom.write(*romData);
            tempRom.close();

            delete romData;

            if (!ddZipCheck)
                completeRomPath = romPath;
            else
                complete64DDPath = romPath;
        }

        ddZipCheck = true;
    }

    if (zipFileName == "" && romFileName != "")
        completeRomPath = romDir.absoluteFilePath(romFileName);
    if (ddZipName == "" && ddFileName != "")
        complete64DDPath = ddDir.absoluteFilePath(ddFileName);

    QString emulatorPath = SETTINGS.value("Paths/cen64", "").toString();
    QString pifPath = SETTINGS.value("Paths/pifrom", "").toString();
    QString ddIPLPath = SETTINGS.value("Paths/ddiplrom", "").toString();

    bool ddMode = false;
    if (SETTINGS.value("Emulation/64dd", "").toString() == "true")
        ddMode = true;

    QFile emulatorFile(emulatorPath);
    QFile pifFile(pifPath);
    QFile ddIPL(ddIPLPath);
    QFile romFile(completeRomPath);
    QFile ddFile(complete64DDPath);

    //Sanity checks
    if (!emulatorFile.exists() || QFileInfo(emulatorFile).isDir() || !QFileInfo(emulatorFile).isExecutable()) {
        QMessageBox::warning(parent, tr("Warning"),
                             tr("<ParentName> executable not found.").replace("<ParentName>",ParentName));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (!pifFile.exists() || QFileInfo(pifFile).isDir()) {
        QMessageBox::warning(parent, tr("Warning"), tr("PIF IPL file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (ddIPLPath != "" && (!ddIPL.exists() || QFileInfo(ddIPL).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("64DD IPL file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath != "" && (!romFile.exists() || QFileInfo(romFile).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("ROM file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath == "" && complete64DDPath != ""
            && (!ddFile.exists() || QFileInfo(ddFile).isDir())) {
        QMessageBox::warning(parent, tr("Warning"), tr("64DD ROM file not found."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath == "" && complete64DDPath == "") {
        QMessageBox::warning(parent, tr("Warning"), tr("No ROM selected."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    if (completeRomPath != "") {
        romFile.open(QIODevice::ReadOnly);
        QByteArray romCheck = romFile.read(4);
        romFile.close();

        if (romCheck.toHex() != "80371240") {
            if (romCheck.toHex() == "e848d316") { // 64DD file instead
                if (complete64DDPath == "" && ddMode) {
                    complete64DDPath = completeRomPath;
                    ddFile.setFileName(complete64DDPath);
                    completeRomPath = "";
                } else {
                    QMessageBox::warning(parent, tr("Warning"), tr("64DD not enabled."));
                    if (zip || ddZip) cleanTemp();
                    return;
                }
            } else {
                QMessageBox::warning(parent, tr("Warning"), tr("Not a valid Z64 File."));
                if (zip || ddZip) cleanTemp();
                return;
            }
        }
    }

    if (complete64DDPath != "" && ddMode) {
        ddFile.open(QIODevice::ReadOnly);
        QByteArray romCheck = ddFile.read(4);
        ddFile.close();

        if (romCheck.toHex() != "e848d316") {
            QMessageBox::warning(parent, tr("Warning"), tr("Not a valid 64DD File."));
            if (zip || ddZip) cleanTemp();
            return;
        }
    }

    QStringList args;

    if (SETTINGS.value("Saves/individualsave", "").toString() == "true") {
        QString eeprom4kPath = SETTINGS.value("Saves/eeprom4k", "").toString();
        QString eeprom16kPath = SETTINGS.value("Saves/eeprom16k", "").toString();
        QString sramPath = SETTINGS.value("Saves/sram", "").toString();
        QString flashPath = SETTINGS.value("Saves/flash", "").toString();

        if (eeprom4kPath != "")
            args << "-eep4k" << eeprom4kPath;
        if (eeprom16kPath != "")
            args << "-eep16k" << eeprom16kPath;
        if (sramPath != "")
            args << "-sram" << sramPath;
        if (flashPath != "")
            args << "-flash" << flashPath;
    } else {
        QString savesPath = SETTINGS.value("Saves/directory", "").toString();
        if (savesPath != "") {
            QDir savesDir(savesPath);

            if (savesDir.exists()) {
                romFile.open(QIODevice::ReadOnly);
                QByteArray *romData = new QByteArray(romFile.readAll());
                romFile.close();

                QString romMD5 = QString(QCryptographicHash::hash(*romData,
                                                                  QCryptographicHash::Md5).toHex());

                QString romBaseName = QFileInfo(romFile).completeBaseName();
                QString eeprom4kFileName = romBaseName + "." + romMD5 + ".eep4k";
                QString eeprom16kFileName = romBaseName + "." + romMD5 + ".eep16k";
                QString sramFileName = romBaseName + "." + romMD5 + ".sram";
                QString flashFileName = romBaseName + "." + romMD5 + ".flash";
                QString eeprom4kPath = savesDir.absoluteFilePath(eeprom4kFileName);
                QString eeprom16kPath = savesDir.absoluteFilePath(eeprom16kFileName);
                QString sramPath = savesDir.absoluteFilePath(sramFileName);
                QString flashPath = savesDir.absoluteFilePath(flashFileName);

                // Check ROM catalog to determine save type
                QString catalogFile = SETTINGS.value("Paths/catalog", "").toString();
                if (QFileInfo(catalogFile).exists()) {
                    QSettings romCatalog(catalogFile, QSettings::IniFormat, parent);
                    QString saveType = romCatalog.value(romMD5.toUpper()+"/SaveType","").toString();

                    if (saveType == "Eeprom 4KB")
                        args << "-eep4k"  << eeprom4kPath;
                    else if (saveType == "Eeprom 16KB")
                        args << "-eep16k"  << eeprom16kPath;
                    else if (saveType == "SRAM")
                        args << "-sram"  << sramPath;
                    else if (saveType == "Flash RAM")
                        args << "-flash"  << flashPath;
                    else if (saveType == "Controller Pack");
                    else
                        args << "-eep4k"  << eeprom4kPath
                             << "-eep16k" << eeprom16kPath
                             << "-sram"   << sramPath
                             << "-flash"  << flashPath;
                } else {
                    args << "-eep4k"  << eeprom4kPath
                         << "-eep16k" << eeprom16kPath
                         << "-sram"   << sramPath
                         << "-flash"  << flashPath;
                }

                delete romData;
            }
        }
    }

    for (int i = 1; i <= 4; i++)
    {
        QString ctrl = "Controller"+QString::number(i);

        if (SETTINGS.value(ctrl+"/enabled", "").toString() == "true") {
            args << "-controller";
            QString options = "num="+QString::number(i);

            int accessory = SETTINGS.value(ctrl+"/accessory", 0).toInt();
            QString memPak = SETTINGS.value(ctrl+"/mempak", "").toString();
            QString tPakROM = SETTINGS.value(ctrl+"/tpakrom", "").toString();
            QString tPakSave = SETTINGS.value(ctrl+"/tpaksave", "").toString();

            if (accessory == 1) //Rumble Pak
                options += ",pak=rumble";
            else if (accessory == 2 && memPak != "") //Controller Pak
                options += ",mempak="+memPak;
            else if (accessory == 3 && tPakROM != "" && tPakSave != "") //Transfer Pak
                options += ",tpak_rom="+tPakROM+",tpak_save="+tPakSave;

            args << options;
        }
    }

    if (SETTINGS.value("Emulation/multithread", "").toString() == "true")
        args << "-multithread";
    if (SETTINGS.value("Emulation/noaudio", "").toString() == "true")
        args << "-noaudio";
    if (SETTINGS.value("Emulation/novideo", "").toString() == "true")
        args << "-novideo";

    if (ddIPLPath != "" && complete64DDPath != "" && ddMode)
        args << "-ddipl" << ddIPLPath << "-ddrom" << complete64DDPath;
    else if (completeRomPath == "") {
        QMessageBox::warning(parent, tr("Warning"), tr("No ROM selected or 64DD not enabled."));
        if (zip || ddZip) cleanTemp();
        return;
    }

    QString otherParameters = SETTINGS.value("Other/parameters", "").toString();
    if (otherParameters != "")
        args.append(parseArgString(otherParameters));

    args << pifPath;

    if (completeRomPath != "")
        args << completeRomPath;

    emulatorProc = new QProcess(this);
    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(emitFinished()));
    connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(checkStatus(int)));

    if (zip || ddZip)
        connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(cleanTemp()));

    if (SETTINGS.value("Other/consoleoutput", "").toString() == "true")
        emulatorProc->setProcessChannelMode(QProcess::ForwardedChannels);
    else {
        connect(emulatorProc, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
        emulatorProc->setProcessChannelMode(QProcess::MergedChannels);
    }

    //clear log
    lastOutput = "";

    emulatorProc->start(emulatorPath, args);

    //Add command to log
    QString executable = emulatorPath;
    if (executable.contains(" "))
        executable = '"' + executable + '"';

    QString argString;

    foreach(QString arg, args)
    {
        if (arg.contains(" "))
            argString += " \"" + arg + "\"";
        else
            argString += " " + arg;
    }

    lastOutput.append(executable + argString + "\n\n");

    updateStatus(tr("Emulation started"), 3000);
    emit started();
}

void EmulatorHandler::stopEmulator()
{
    emulatorProc->terminate();
}

void EmulatorHandler::updateStatus(QString message, int timeout)
{
    emit statusUpdate(message, timeout);
}
<|endoftext|>


<commit_before>#include "hierarchy.h"
#include "engine/blob.h"
#include "engine/engine.h"
#include "engine/hash_map.h"
#include "engine/json_serializer.h"
#include "engine/property_register.h"
#include "universe.h"

namespace Lumix
{

static const ComponentType HIERARCHY_TYPE_HANDLE = PropertyRegister::getComponentType("hierarchy");

class HierarchyImpl LUMIX_FINAL : public Hierarchy
{
private:
	typedef HashMap<Entity, Entity> Parents;

public:
	HierarchyImpl(IPlugin& system, Universe& universe, IAllocator& allocator)
		: m_universe(universe)
		, m_parents(allocator)
		, m_children(allocator)
		, m_allocator(allocator)
		, m_system(system)
	{
		universe.registerComponentTypeScene(HIERARCHY_TYPE_HANDLE, this);
		m_is_processing = false;
		universe.entityDestroyed().bind<HierarchyImpl, &HierarchyImpl::onEntityDestroyed>(this);
		universe.entityTransformed().bind<HierarchyImpl, &HierarchyImpl::onEntityMoved>(this);
	}

	void clear() override
	{
		auto iter = m_children.begin(), end = m_children.end();
		while (iter != end)
		{
			LUMIX_DELETE(m_allocator, iter.value());
			++iter;
		}
		m_children.clear();
		m_parents.clear();
	}

	ComponentHandle createComponent(ComponentType type, Entity entity) override
	{
		if (HIERARCHY_TYPE_HANDLE == type)
		{
			m_parents.insert(entity, INVALID_ENTITY);
			m_universe.addComponent(entity, type, this, {entity.index});
			return {entity.index};
		}
		return INVALID_COMPONENT;
	}

	void destroyComponent(ComponentHandle component, ComponentType type) override
	{
		if (HIERARCHY_TYPE_HANDLE == type)
		{
			Entity entity = {component.index};
			auto parent_iter = m_parents.find(entity);

			if (parent_iter.isValid())
			{
				auto iter = m_children.find(parent_iter.value());
				if (iter != m_children.end())
				{
					auto& children = *iter.value();
					if (children.size() == 1)
					{
						LUMIX_DELETE(m_allocator, iter.value());
						m_children.erase(iter);
					}
					else
					{
						for (int i = 0; i < children.size(); ++i)
						{
							if (children[i].m_entity == entity)
							{
								children.eraseFast(i);
								break;
							}
						}
					}
				}
				m_parents.erase(parent_iter);
			}

			m_universe.destroyComponent(entity, type, this, component);
		}
	}

	IPlugin& getPlugin() const override { return m_system; }
	void update(float time_delta, bool paused) override {}
	Universe& getUniverse() override { return m_universe; }
	IAllocator& getAllocator() { return m_allocator; }

	ComponentHandle getComponent(Entity entity, ComponentType type) override
	{
		ComponentHandle cmp = {entity.index};
		return m_parents.find(entity) != m_parents.end() ? cmp : INVALID_COMPONENT;
	}

	void onEntityDestroyed(Entity entity)
	{
		auto iter = m_children.find(entity);
		if (iter != m_children.end())
		{
			for (auto& x : *iter.value())
			{
				m_parents.erase(x.m_entity);
			}
			LUMIX_DELETE(m_allocator, iter.value());
			m_children.erase(iter);
		}
	}

	void onEntityMoved(Entity entity)
	{
		bool was_processing = m_is_processing;
		m_is_processing = true;
		Children::iterator iter = m_children.find(entity);
		if (iter.isValid())
		{
			Transform parent_transform = m_universe.getTransform(entity);
			Array<Child>& children = *iter.value();
			for (int i = 0, c = children.size(); i < c; ++i)
			{
				m_universe.setTransform(children[i].m_entity, parent_transform * children[i].m_local_transform);
			}
		}
		m_is_processing = was_processing;

		if (m_is_processing) return;

		Parents::iterator parent_iter = m_parents.find(entity);
		if (parent_iter.isValid())
		{
			Entity parent(parent_iter.value());
			Children::iterator child_iter = m_children.find(parent);
			if (child_iter.isValid())
			{
				Array<Child>& children = *child_iter.value();
				for (int i = 0, c = children.size(); i < c; ++i)
				{
					if (children[i].m_entity == entity)
					{
						Transform parent_transform = m_universe.getTransform(parent);
						children[i].m_local_transform = parent_transform.inverted() * m_universe.getTransform(entity);
						break;
					}
				}
			}
		}
	}

	void setLocalPosition(ComponentHandle cmp, const Vec3& position) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && Lumix::isValid(parent_iter.value()))
		{
			Quat parent_rot = m_universe.getRotation(parent_iter.value());
			Vec3 parent_pos = m_universe.getPosition(parent_iter.value());
			m_universe.setPosition(entity, parent_pos + parent_rot.rotate(position));
			return;
		}

		m_universe.setPosition(entity, position);
	}

	Vec3 getLocalPosition(ComponentHandle cmp) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			auto child_iter = m_children.find(parent_iter.value());
			ASSERT(child_iter.isValid());

			for (auto& child : *child_iter.value())
			{
				if (child.m_entity == entity)
				{
					return child.m_local_transform.pos;
				}
			}
		}

		return m_universe.getPosition(entity);
	}

	void setLocalRotationEuler(ComponentHandle cmp, const Vec3& rotation) override
	{
		Quat q;
		q.fromEuler(rotation);
		setLocalRotation(cmp, q);
	}

	Vec3 getLocalRotationEuler(ComponentHandle cmp) override
	{
		return getLocalRotation(cmp).toEuler();
	}

	void setLocalRotation(ComponentHandle cmp, const Quat& rotation) override
	{
		Entity entity = { cmp.index };
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid())
		{
			Quat parent_rot = m_universe.getRotation(parent_iter.value());
			m_universe.setRotation(entity, parent_rot * rotation);
			return;
		}

		m_universe.setRotation(entity, rotation);
	}

	Quat getLocalRotation(ComponentHandle cmp) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			auto child_iter = m_children.find(parent_iter.value());
			ASSERT(child_iter.isValid());

			for (auto& child : *child_iter.value())
			{
				if (child.m_entity == entity)
				{
					Quat rot = child.m_local_transform.rot;
					return rot;
				}
			}
		}

		return m_universe.getRotation(entity);
	}

	const Children& getAllChildren() const override { return m_children; }

	void setParent(ComponentHandle child, Entity parent) override
	{
		Entity child_entity = {child.index};
		Parents::iterator old_parent_iter = m_parents.find(child_entity);
		if (old_parent_iter.isValid())
		{
			if (isValid(old_parent_iter.value()))
			{
				Children::iterator child_iter = m_children.find(old_parent_iter.value());
				ASSERT(child_iter.isValid());
				Array<Child>& children = *child_iter.value();
				for (int i = 0; i < children.size(); ++i)
				{
					if (children[i].m_entity == child_entity)
					{
						children.erase(i);
						break;
					}
				}
			}
			m_parents.erase(old_parent_iter);
		}

		if (isValid(parent))
		{
			m_parents.insert(child_entity, parent);

			Children::iterator child_iter = m_children.find(parent);
			if (!child_iter.isValid())
			{
				m_children.insert(parent, LUMIX_NEW(m_allocator, Array<Child>)(m_allocator));
				child_iter = m_children.find(parent);
			}
			Child& c = child_iter.value()->emplace();
			c.m_entity = child_entity;
			Transform parent_transform = m_universe.getTransform(parent);
			c.m_local_transform = parent_transform.inverted() * m_universe.getTransform(child_entity);
		}
	}

	Entity getParent(ComponentHandle child) override
	{
		Entity child_entity = {child.index};
		Parents::iterator parent_iter = m_parents.find(child_entity);
		if (parent_iter.isValid())
		{
			return Entity(parent_iter.value());
		}
		return INVALID_ENTITY;
	}

	void serialize(OutputBlob& serializer) override
	{
		int size = m_parents.size();
		serializer.write((int32)size);
		Parents::iterator iter = m_parents.begin(), end = m_parents.end();
		while (iter != end)
		{
			serializer.write(iter.key());
			serializer.write(iter.value());
			++iter;
		}
	}

	void deserialize(InputBlob& serializer, int /*version*/) override
	{
		int32 size;
		serializer.read(size);
		for (int i = 0; i < size; ++i)
		{
			Entity child, parent;
			serializer.read(child);
			serializer.read(parent);
			ComponentHandle cmp = {child.index};
			setParent(cmp, parent);
			m_universe.addComponent(child, HIERARCHY_TYPE_HANDLE, this, cmp);
		}
	}

	Array<Child>* getChildren(Entity parent) override
	{
		Children::iterator iter = m_children.find(parent);
		if (iter.isValid())
		{
			return iter.value();
		}
		return nullptr;
	}

private:
	IAllocator& m_allocator;
	Universe& m_universe;
	Parents m_parents;
	Children m_children;
	IPlugin& m_system;
	bool m_is_processing;
};

IScene* HierarchyPlugin::createScene(Universe& ctx)
{
	return Hierarchy::create(*this, ctx, m_allocator);
}

void HierarchyPlugin::destroyScene(IScene* scene)
{
	Hierarchy::destroy(static_cast<Hierarchy*>(scene));
}

Hierarchy* Hierarchy::create(IPlugin& system, Universe& universe, IAllocator& allocator)
{
	return LUMIX_NEW(allocator, HierarchyImpl)(system, universe, allocator);
}

void Hierarchy::destroy(Hierarchy* hierarchy)
{
	LUMIX_DELETE(static_cast<HierarchyImpl*>(hierarchy)->getAllocator(), hierarchy);
}

} // namespace Lumix
<commit_msg>fixed hierarchy crash when parent entity is destroyed and then child is destroyed - closes #1026<commit_after>#include "hierarchy.h"
#include "engine/blob.h"
#include "engine/engine.h"
#include "engine/hash_map.h"
#include "engine/json_serializer.h"
#include "engine/property_register.h"
#include "universe.h"

namespace Lumix
{

static const ComponentType HIERARCHY_TYPE_HANDLE = PropertyRegister::getComponentType("hierarchy");

class HierarchyImpl LUMIX_FINAL : public Hierarchy
{
private:
	typedef HashMap<Entity, Entity> Parents;

public:
	HierarchyImpl(IPlugin& system, Universe& universe, IAllocator& allocator)
		: m_universe(universe)
		, m_parents(allocator)
		, m_children(allocator)
		, m_allocator(allocator)
		, m_system(system)
	{
		universe.registerComponentTypeScene(HIERARCHY_TYPE_HANDLE, this);
		m_is_processing = false;
		universe.entityDestroyed().bind<HierarchyImpl, &HierarchyImpl::onEntityDestroyed>(this);
		universe.entityTransformed().bind<HierarchyImpl, &HierarchyImpl::onEntityMoved>(this);
	}

	void clear() override
	{
		auto iter = m_children.begin(), end = m_children.end();
		while (iter != end)
		{
			LUMIX_DELETE(m_allocator, iter.value());
			++iter;
		}
		m_children.clear();
		m_parents.clear();
	}

	ComponentHandle createComponent(ComponentType type, Entity entity) override
	{
		if (HIERARCHY_TYPE_HANDLE == type)
		{
			m_parents.insert(entity, INVALID_ENTITY);
			m_universe.addComponent(entity, type, this, {entity.index});
			return {entity.index};
		}
		return INVALID_COMPONENT;
	}

	void destroyComponent(ComponentHandle component, ComponentType type) override
	{
		if (HIERARCHY_TYPE_HANDLE == type)
		{
			Entity entity = {component.index};
			auto parent_iter = m_parents.find(entity);

			ASSERT(parent_iter.isValid());
			if (isValid(parent_iter.value()))
			{
				auto iter = m_children.find(parent_iter.value());
				if (iter != m_children.end())
				{
					auto& children = *iter.value();
					if (children.size() == 1)
					{
						LUMIX_DELETE(m_allocator, iter.value());
						m_children.erase(iter);
					}
					else
					{
						for (int i = 0; i < children.size(); ++i)
						{
							if (children[i].m_entity == entity)
							{
								children.eraseFast(i);
								break;
							}
						}
					}
				}
			}
			m_parents.erase(parent_iter);
			m_universe.destroyComponent(entity, type, this, component);
		}
	}

	IPlugin& getPlugin() const override { return m_system; }
	void update(float time_delta, bool paused) override {}
	Universe& getUniverse() override { return m_universe; }
	IAllocator& getAllocator() { return m_allocator; }

	ComponentHandle getComponent(Entity entity, ComponentType type) override
	{
		ComponentHandle cmp = {entity.index};
		return m_parents.find(entity) != m_parents.end() ? cmp : INVALID_COMPONENT;
	}

	void onEntityDestroyed(Entity entity)
	{
		auto iter = m_children.find(entity);
		if (iter != m_children.end())
		{
			for (auto& x : *iter.value())
			{
				m_parents[x.m_entity] = INVALID_ENTITY;
			}
			LUMIX_DELETE(m_allocator, iter.value());
			m_children.erase(iter);
		}
	}

	void onEntityMoved(Entity entity)
	{
		bool was_processing = m_is_processing;
		m_is_processing = true;
		Children::iterator iter = m_children.find(entity);
		if (iter.isValid())
		{
			Transform parent_transform = m_universe.getTransform(entity);
			Array<Child>& children = *iter.value();
			for (int i = 0, c = children.size(); i < c; ++i)
			{
				m_universe.setTransform(children[i].m_entity, parent_transform * children[i].m_local_transform);
			}
		}
		m_is_processing = was_processing;

		if (m_is_processing) return;

		Parents::iterator parent_iter = m_parents.find(entity);
		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			Entity parent(parent_iter.value());
			Children::iterator child_iter = m_children.find(parent);
			if (child_iter.isValid())
			{
				Array<Child>& children = *child_iter.value();
				for (int i = 0, c = children.size(); i < c; ++i)
				{
					if (children[i].m_entity == entity)
					{
						Transform parent_transform = m_universe.getTransform(parent);
						children[i].m_local_transform = parent_transform.inverted() * m_universe.getTransform(entity);
						break;
					}
				}
			}
		}
	}

	void setLocalPosition(ComponentHandle cmp, const Vec3& position) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			Quat parent_rot = m_universe.getRotation(parent_iter.value());
			Vec3 parent_pos = m_universe.getPosition(parent_iter.value());
			m_universe.setPosition(entity, parent_pos + parent_rot.rotate(position));
			return;
		}

		m_universe.setPosition(entity, position);
	}

	Vec3 getLocalPosition(ComponentHandle cmp) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			auto child_iter = m_children.find(parent_iter.value());
			ASSERT(child_iter.isValid());

			for (auto& child : *child_iter.value())
			{
				if (child.m_entity == entity)
				{
					return child.m_local_transform.pos;
				}
			}
		}

		return m_universe.getPosition(entity);
	}

	void setLocalRotationEuler(ComponentHandle cmp, const Vec3& rotation) override
	{
		Quat q;
		q.fromEuler(rotation);
		setLocalRotation(cmp, q);
	}

	Vec3 getLocalRotationEuler(ComponentHandle cmp) override
	{
		return getLocalRotation(cmp).toEuler();
	}

	void setLocalRotation(ComponentHandle cmp, const Quat& rotation) override
	{
		Entity entity = { cmp.index };
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			Quat parent_rot = m_universe.getRotation(parent_iter.value());
			m_universe.setRotation(entity, parent_rot * rotation);
			return;
		}

		m_universe.setRotation(entity, rotation);
	}

	Quat getLocalRotation(ComponentHandle cmp) override
	{
		Entity entity = {cmp.index};
		Parents::iterator parent_iter = m_parents.find(entity);

		if (parent_iter.isValid() && isValid(parent_iter.value()))
		{
			auto child_iter = m_children.find(parent_iter.value());
			ASSERT(child_iter.isValid());

			for (auto& child : *child_iter.value())
			{
				if (child.m_entity == entity)
				{
					Quat rot = child.m_local_transform.rot;
					return rot;
				}
			}
		}

		return m_universe.getRotation(entity);
	}

	const Children& getAllChildren() const override { return m_children; }

	void setParent(ComponentHandle child, Entity parent) override
	{
		Entity child_entity = {child.index};
		Parents::iterator old_parent_iter = m_parents.find(child_entity);
		if (old_parent_iter.isValid())
		{
			if (isValid(old_parent_iter.value()))
			{
				Children::iterator child_iter = m_children.find(old_parent_iter.value());
				ASSERT(child_iter.isValid());
				Array<Child>& children = *child_iter.value();
				for (int i = 0; i < children.size(); ++i)
				{
					if (children[i].m_entity == child_entity)
					{
						children.erase(i);
						break;
					}
				}
			}
			m_parents.erase(old_parent_iter);
		}

		m_parents.insert(child_entity, parent);
		if (isValid(parent))
		{
			Children::iterator child_iter = m_children.find(parent);
			if (!child_iter.isValid())
			{
				m_children.insert(parent, LUMIX_NEW(m_allocator, Array<Child>)(m_allocator));
				child_iter = m_children.find(parent);
			}
			Child& c = child_iter.value()->emplace();
			c.m_entity = child_entity;
			Transform parent_transform = m_universe.getTransform(parent);
			c.m_local_transform = parent_transform.inverted() * m_universe.getTransform(child_entity);
		}
	}

	Entity getParent(ComponentHandle child) override
	{
		Entity child_entity = {child.index};
		Parents::iterator parent_iter = m_parents.find(child_entity);
		if (parent_iter.isValid())
		{
			return parent_iter.value();
		}
		return INVALID_ENTITY;
	}

	void serialize(OutputBlob& serializer) override
	{
		int size = m_parents.size();
		serializer.write((int32)size);
		Parents::iterator iter = m_parents.begin(), end = m_parents.end();
		while (iter != end)
		{
			serializer.write(iter.key());
			serializer.write(iter.value());
			++iter;
		}
	}

	void deserialize(InputBlob& serializer, int /*version*/) override
	{
		int32 size;
		serializer.read(size);
		for (int i = 0; i < size; ++i)
		{
			Entity child, parent;
			serializer.read(child);
			serializer.read(parent);
			ComponentHandle cmp = {child.index};
			setParent(cmp, parent);
			m_universe.addComponent(child, HIERARCHY_TYPE_HANDLE, this, cmp);
		}
	}

	Array<Child>* getChildren(Entity parent) override
	{
		Children::iterator iter = m_children.find(parent);
		if (iter.isValid())
		{
			return iter.value();
		}
		return nullptr;
	}

private:
	IAllocator& m_allocator;
	Universe& m_universe;
	Parents m_parents;
	Children m_children;
	IPlugin& m_system;
	bool m_is_processing;
};

IScene* HierarchyPlugin::createScene(Universe& ctx)
{
	return Hierarchy::create(*this, ctx, m_allocator);
}

void HierarchyPlugin::destroyScene(IScene* scene)
{
	Hierarchy::destroy(static_cast<Hierarchy*>(scene));
}

Hierarchy* Hierarchy::create(IPlugin& system, Universe& universe, IAllocator& allocator)
{
	return LUMIX_NEW(allocator, HierarchyImpl)(system, universe, allocator);
}

void Hierarchy::destroy(Hierarchy* hierarchy)
{
	LUMIX_DELETE(static_cast<HierarchyImpl*>(hierarchy)->getAllocator(), hierarchy);
}

} // namespace Lumix
<|endoftext|>


<commit_before>
namespace cusim
{
	namespace detail
	{
		template< typename tCount > __device__ inline
		tCount warp_prefix( unsigned aWTID, tCount aValue )
		{
			/* The PTX version results in much tighter code than the C version.
			 * The C version produces four instructions per step (a ISETP,
			 * IADD, SHFL and SEL); the inline PTX version gets it down to two
			 * (SHFL + predicated IADD).
			 *
			 * Incidentally, the PTX code is shown as an example for the shfl
			 * instruction in the PTX ISA document. See
			 * http://docs.nvidia.com/cuda/parallel-thread-execution/index.html
			 */
#			if 0
			auto x0 = __shfl_up( aValue, 1 );
			aValue += (aWTID >= 1) ? x0 : 0;

			auto x1 = __shfl_up( aValue, 2 );
			aValue += (aWTID >= 2) ? x1 : 0;

			auto x2 = __shfl_up( aValue, 4 );
			aValue += (aWTID >= 4) ? x2 : 0;

			auto x3 = __shfl_up( aValue, 8 );
			aValue += (aWTID >= 8) ? x3 : 0;

			auto x4 = __shfl_up( aValue, 16 );
			aValue += (aWTID >= 16) ? x4 : 0;
#			else
			__asm__ volatile( "{\n\t"
				".reg .u32 t0;\n\t"
				".reg .pred valid;\n\t"

				"shfl.up.b32 t0|valid, %0, 1, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 2, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 4, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 8, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 16, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"}\n\t"
				: "+r"(aValue)
			);
#			endif

			return aValue;
		}

		template< typename tReal > __device__ inline
		tReal reduce0( tReal aValue )
		{
			aValue += __shfl_down_sync( ~0u, aValue, 16 );
			aValue += __shfl_down_sync( ~0u, aValue, 8 );
			aValue += __shfl_down_sync( ~0u, aValue, 4 );
			aValue += __shfl_down_sync( ~0u, aValue, 2 );
			aValue += __shfl_down_sync( ~0u, aValue, 1 );
			return aValue;
		}
	}

	template< typename tReal, typename tCount > __global__
	void /*__launch_bounds__(1024,1)*/ K_distance( tReal* aDistance, unsigned aN, unsigned aM, tCount* aCur, tCount const* aRef )
	{
		__shared__ tReal totals[32]; //XXX-FIXME: number of warps in block.

		auto const warp = threadIdx.y;
		auto const wtid = threadIdx.x;

		auto const n32 = (aN+32-1)/32*32;
		auto const m32 = (aM+32-1)/32*32;

		// init
		if( 0 == warp )
		{
			totals[wtid] = tReal(0);
		}

		__syncthreads();

		// column-wise prefix sums
		for( auto row = warp; row < aN; row += blockDim.y )
		{
			tCount base = 0;
			for( auto col = wtid; col < m32; col += 32 )
			{
				tCount const val = col < aM
					? aCur[row*aM+col]
					: 0
				;

				tCount const sum = base + detail::warp_prefix( wtid, val );

				if( col < aM )
					aCur[row*aM+col] = sum;

				base = __shfl_sync( ~0u, sum, 31 );
			}
		}

		__syncthreads();

		// row-wise prefix sums, and accumulate the squared difference to the reference
		tReal acc = tReal(0);
		for( auto col = warp; col < aM; col += blockDim.y )
		{
			tCount base = 0;
			tReal a2 = tReal(0);
			for( auto row = wtid; row < n32; row += 32 )
			{
				tCount const val = row < aN
					? aCur[row*aM+col]
					: 0
				;

				tCount const sum = base + detail::warp_prefix( wtid, val );
				base = __shfl_sync( ~0u, sum, 31 );

				if( row < aN )
				{
					tCount const ref = aRef[row*aM+col];
					tReal const dd = tReal(sum) - ref;
					a2 += dd*dd;
				}
			}

			acc += a2;
		}

		// reduce the per-thread sums in each warp
		tReal const wsum = detail::reduce0( acc );
		if( 0 == wtid )
			totals[warp] = wsum;

		__syncthreads();

		// have one warp reduce the per-warp sums to the final sum
		if( 0 == warp )
		{
			tReal const tsum = wtid < 32 //XXX
				? totals[wtid]
				: 0
			;

			tReal const fin = detail::reduce0( tsum );

			if( 0 == wtid )
				*aDistance = fin;
		}

		// zero out the histogram for the next invocation
		for( auto row = warp; row < aN; row += blockDim.y )
		{
			for( auto col = wtid; col < aM; col += 32 )
			{
				aCur[row*aM+col] = 0;
			}
		}
	}
}
<commit_msg>Compat: use non-sync __shfl() intrinsics before CUDA9<commit_after>namespace cusim
{
	/* In CUDA v9.x, __shfl*_sync() are introduced, and the old __shfl*() are
	 * immediately deprecated. Unfortunately, the __shfl*_sync() are not
	 * available before CUDA v9.
	 */
#	if __CUDACC_VER_MAJOR__ < 9
#		define __shfl_sync( mask, ... ) __shfl( __VA_ARGS__ )
#		define __shfl_down_sync( mask, ... ) __shfl_down( __VA_ARGS__ )
#	endif // ~ CUDA before version 9

	namespace detail
	{
		template< typename tCount > __device__ inline
		tCount warp_prefix( unsigned aWTID, tCount aValue )
		{
			/* The PTX version results in much tighter code than the C version.
			 * The C version produces four instructions per step (a ISETP,
			 * IADD, SHFL and SEL); the inline PTX version gets it down to two
			 * (SHFL + predicated IADD).
			 *
			 * Incidentally, the PTX code is shown as an example for the shfl
			 * instruction in the PTX ISA document. See
			 * http://docs.nvidia.com/cuda/parallel-thread-execution/index.html
			 */
#			if 0
			auto x0 = __shfl_up( aValue, 1 );
			aValue += (aWTID >= 1) ? x0 : 0;

			auto x1 = __shfl_up( aValue, 2 );
			aValue += (aWTID >= 2) ? x1 : 0;

			auto x2 = __shfl_up( aValue, 4 );
			aValue += (aWTID >= 4) ? x2 : 0;

			auto x3 = __shfl_up( aValue, 8 );
			aValue += (aWTID >= 8) ? x3 : 0;

			auto x4 = __shfl_up( aValue, 16 );
			aValue += (aWTID >= 16) ? x4 : 0;
#			else
			__asm__ volatile( "{\n\t"
				".reg .u32 t0;\n\t"
				".reg .pred valid;\n\t"

				"shfl.up.b32 t0|valid, %0, 1, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 2, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 4, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 8, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"shfl.up.b32 t0|valid, %0, 16, 0;\n\t"
				"@valid add.s32 %0, t0, %0;\n\t"

				"}\n\t"
				: "+r"(aValue)
			);
#			endif

			return aValue;
		}

		template< typename tReal > __device__ inline
		tReal reduce0( tReal aValue )
		{
			aValue += __shfl_down_sync( ~0u, aValue, 16 );
			aValue += __shfl_down_sync( ~0u, aValue, 8 );
			aValue += __shfl_down_sync( ~0u, aValue, 4 );
			aValue += __shfl_down_sync( ~0u, aValue, 2 );
			aValue += __shfl_down_sync( ~0u, aValue, 1 );
			return aValue;
		}
	}

	template< typename tReal, typename tCount > __global__
	void /*__launch_bounds__(1024,1)*/ K_distance( tReal* aDistance, unsigned aN, unsigned aM, tCount* aCur, tCount const* aRef )
	{
		__shared__ tReal totals[32]; //XXX-FIXME: number of warps in block.

		auto const warp = threadIdx.y;
		auto const wtid = threadIdx.x;

		auto const n32 = (aN+32-1)/32*32;
		auto const m32 = (aM+32-1)/32*32;

		// init
		if( 0 == warp )
		{
			totals[wtid] = tReal(0);
		}

		__syncthreads();

		// column-wise prefix sums
		for( auto row = warp; row < aN; row += blockDim.y )
		{
			tCount base = 0;
			for( auto col = wtid; col < m32; col += 32 )
			{
				tCount const val = col < aM
					? aCur[row*aM+col]
					: 0
				;

				tCount const sum = base + detail::warp_prefix( wtid, val );

				if( col < aM )
					aCur[row*aM+col] = sum;

				base = __shfl_sync( ~0u, sum, 31 );
			}
		}

		__syncthreads();

		// row-wise prefix sums, and accumulate the squared difference to the reference
		tReal acc = tReal(0);
		for( auto col = warp; col < aM; col += blockDim.y )
		{
			tCount base = 0;
			tReal a2 = tReal(0);
			for( auto row = wtid; row < n32; row += 32 )
			{
				tCount const val = row < aN
					? aCur[row*aM+col]
					: 0
				;

				tCount const sum = base + detail::warp_prefix( wtid, val );
				base = __shfl_sync( ~0u, sum, 31 );

				if( row < aN )
				{
					tCount const ref = aRef[row*aM+col];
					tReal const dd = tReal(sum) - ref;
					a2 += dd*dd;
				}
			}

			acc += a2;
		}

		// reduce the per-thread sums in each warp
		tReal const wsum = detail::reduce0( acc );
		if( 0 == wtid )
			totals[warp] = wsum;

		__syncthreads();

		// have one warp reduce the per-warp sums to the final sum
		if( 0 == warp )
		{
			tReal const tsum = wtid < 32 //XXX
				? totals[wtid]
				: 0
			;

			tReal const fin = detail::reduce0( tsum );

			if( 0 == wtid )
				*aDistance = fin;
		}

		// zero out the histogram for the next invocation
		for( auto row = warp; row < aN; row += blockDim.y )
		{
			for( auto col = wtid; col < aM; col += 32 )
			{
				aCur[row*aM+col] = 0;
			}
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Complete the solution to the second part of question 3.7<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS.  All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.

#ifndef WEBMIDS_HPP
#define WEBMIDS_HPP

namespace mkvmuxer {

enum MkvId {
  kMkvEBML                    = 0x1A45DFA3,
  kMkvEBMLVersion             = 0x4286,
  kMkvEBMLReadVersion         = 0x42F7,
  kMkvEBMLMaxIDLength         = 0x42F2,
  kMkvEBMLMaxSizeLength       = 0x42F3,
  kMkvDocType                 = 0x4282,
  kMkvDocTypeVersion          = 0x4287,
  kMkvDocTypeReadVersion      = 0x4285,
  kMkvVoid                    = 0xEC,
  kMkvSignatureSlot           = 0x1B538667,
  kMkvSignatureAlgo           = 0x7E8A,
  kMkvSignatureHash           = 0x7E9A,
  kMkvSignaturePublicKey      = 0x7EA5,
  kMkvSignature               = 0x7EB5,
  kMkvSignatureElements       = 0x7E5B,
  kMkvSignatureElementList    = 0x7E7B,
  kMkvSignedElement           = 0x6532,
  //segment
  kMkvSegment                 = 0x18538067,
  //Meta Seek Information
  kMkvSeekHead                = 0x114D9B74,
  kMkvSeek                    = 0x4DBB,
  kMkvSeekID                  = 0x53AB,
  kMkvSeekPosition            = 0x53AC,
  //Segment Information
  kMkvInfo                    = 0x1549A966,
  kMkvTimecodeScale           = 0x2AD7B1,
  kMkvDuration                = 0x4489,
  kMkvDateUTC                 = 0x4461,
  kMkvMuxingApp               = 0x4D80,
  kMkvWritingApp              = 0x5741,
  //Cluster
  kMkvCluster                 = 0x1F43B675,
  kMkvTimecode                = 0xE7,
  kMkvPrevSize                = 0xAB,
  kMkvBlockGroup              = 0xA0,
  kMkvBlock                   = 0xA1,
  kMkvBlockDuration           = 0x9B,
  kMkvReferenceBlock          = 0xFB,
  kMkvLaceNumber              = 0xCC,
  kMkvSimpleBlock             = 0xA3,
  kMkvBlockAdditions          = 0x75A1,
  kMkvBlockMore               = 0xA6,
  kMkvBlockAddID              = 0xEE,
  kMkvBlockAdditional         = 0xA5,
  //Track
  kMkvTracks                  = 0x1654AE6B,
  kMkvTrackEntry              = 0xAE,
  kMkvTrackNumber             = 0xD7,
  kMkvTrackUID                = 0x73C5,
  kMkvTrackType               = 0x83,
  kMkvFlagEnabled             = 0xB9,
  kMkvFlagDefault             = 0x88,
  kMkvFlagForced              = 0x55AA,
  kMkvFlagLacing              = 0x9C,
  kMkvDefaultDuration         = 0x23E383,
  kMkvName                    = 0x536E,
  kMkvLanguage                = 0x22B59C,
  kMkvCodecID                 = 0x86,
  kMkvCodecPrivate            = 0x63A2,
  kMkvCodecName               = 0x258688,
  kMkvMaxBlockAdditionID      = 0x55EE,
  kMkvCodecDelay              = 0x56AA,
  kMkvSeekPreRoll             = 0x56BB,
  //video
  kMkvVideo                   = 0xE0,
  kMkvFlagInterlaced          = 0x9A,
  kMkvStereoMode              = 0x53B8,
  kMkvAlphaMode               = 0x53C0,
  kMkvPixelWidth              = 0xB0,
  kMkvPixelHeight             = 0xBA,
  kMkvPixelCropBottom         = 0x54AA,
  kMkvPixelCropTop            = 0x54BB,
  kMkvPixelCropLeft           = 0x54CC,
  kMkvPixelCropRight          = 0x54DD,
  kMkvDisplayWidth            = 0x54B0,
  kMkvDisplayHeight           = 0x54BA,
  kMkvDisplayUnit             = 0x54B2,
  kMkvAspectRatioType         = 0x54B3,
  kMkvFrameRate               = 0x2383E3,
  //end video
  //audio
  kMkvAudio                   = 0xE1,
  kMkvSamplingFrequency       = 0xB5,
  kMkvOutputSamplingFrequency = 0x78B5,
  kMkvChannels = 0x9F,
  kMkvBitDepth                = 0x6264,
  //end audio
  //ContentEncodings
  kMkvContentEncodings        = 0x6D80,
  kMkvContentEncoding         = 0x6240,
  kMkvContentEncodingOrder    = 0x5031,
  kMkvContentEncodingScope    = 0x5032,
  kMkvContentEncodingType     = 0x5033,
  kMkvContentEncryption       = 0x5035,
  kMkvContentEncAlgo          = 0x47E1,
  kMkvContentEncKeyID         = 0x47E2,
  kMkvContentEncAESSettings   = 0x47E7,
  kMkvAESSettingsCipherMode   = 0x47E8,
  kMkvAESSettingsCipherInitData = 0x47E9,
  //end ContentEncodings
  //Cueing Data
  kMkvCues                    = 0x1C53BB6B,
  kMkvCuePoint                = 0xBB,
  kMkvCueTime                 = 0xB3,
  kMkvCueTrackPositions       = 0xB7,
  kMkvCueTrack                = 0xF7,
  kMkvCueClusterPosition      = 0xF1,
  kMkvCueBlockNumber          = 0x5378,
  //Chapters
  kMkvChapters                = 0x1043A770,
  kMkvEditionEntry            = 0x45B9,
  kMkvChapterAtom             = 0xB6,
  kMkvChapterUID              = 0x73C4,
  kMkvChapterStringUID        = 0x5654,
  kMkvChapterTimeStart        = 0x91,
  kMkvChapterTimeEnd          = 0x92,
  kMkvChapterDisplay          = 0x80,
  kMkvChapString              = 0x85,
  kMkvChapLanguage            = 0x437C,
  kMkvChapCountry             = 0x437E
};

}  // end namespace mkvmuxer

#endif // WEBMIDS_HPP
<commit_msg>mkvmuxer: CRLF->LF webmids.h.<commit_after>// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS.  All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.

#ifndef WEBMIDS_HPP
#define WEBMIDS_HPP

namespace mkvmuxer {

enum MkvId {
  kMkvEBML                    = 0x1A45DFA3,
  kMkvEBMLVersion             = 0x4286,
  kMkvEBMLReadVersion         = 0x42F7,
  kMkvEBMLMaxIDLength         = 0x42F2,
  kMkvEBMLMaxSizeLength       = 0x42F3,
  kMkvDocType                 = 0x4282,
  kMkvDocTypeVersion          = 0x4287,
  kMkvDocTypeReadVersion      = 0x4285,
  kMkvVoid                    = 0xEC,
  kMkvSignatureSlot           = 0x1B538667,
  kMkvSignatureAlgo           = 0x7E8A,
  kMkvSignatureHash           = 0x7E9A,
  kMkvSignaturePublicKey      = 0x7EA5,
  kMkvSignature               = 0x7EB5,
  kMkvSignatureElements       = 0x7E5B,
  kMkvSignatureElementList    = 0x7E7B,
  kMkvSignedElement           = 0x6532,
  //segment
  kMkvSegment                 = 0x18538067,
  //Meta Seek Information
  kMkvSeekHead                = 0x114D9B74,
  kMkvSeek                    = 0x4DBB,
  kMkvSeekID                  = 0x53AB,
  kMkvSeekPosition            = 0x53AC,
  //Segment Information
  kMkvInfo                    = 0x1549A966,
  kMkvTimecodeScale           = 0x2AD7B1,
  kMkvDuration                = 0x4489,
  kMkvDateUTC                 = 0x4461,
  kMkvMuxingApp               = 0x4D80,
  kMkvWritingApp              = 0x5741,
  //Cluster
  kMkvCluster                 = 0x1F43B675,
  kMkvTimecode                = 0xE7,
  kMkvPrevSize                = 0xAB,
  kMkvBlockGroup              = 0xA0,
  kMkvBlock                   = 0xA1,
  kMkvBlockDuration           = 0x9B,
  kMkvReferenceBlock          = 0xFB,
  kMkvLaceNumber              = 0xCC,
  kMkvSimpleBlock             = 0xA3,
  kMkvBlockAdditions          = 0x75A1,
  kMkvBlockMore               = 0xA6,
  kMkvBlockAddID              = 0xEE,
  kMkvBlockAdditional         = 0xA5,
  //Track
  kMkvTracks                  = 0x1654AE6B,
  kMkvTrackEntry              = 0xAE,
  kMkvTrackNumber             = 0xD7,
  kMkvTrackUID                = 0x73C5,
  kMkvTrackType               = 0x83,
  kMkvFlagEnabled             = 0xB9,
  kMkvFlagDefault             = 0x88,
  kMkvFlagForced              = 0x55AA,
  kMkvFlagLacing              = 0x9C,
  kMkvDefaultDuration         = 0x23E383,
  kMkvMaxBlockAdditionID      = 0x55EE,
  kMkvName                    = 0x536E,
  kMkvLanguage                = 0x22B59C,
  kMkvCodecID                 = 0x86,
  kMkvCodecPrivate            = 0x63A2,
  kMkvCodecName               = 0x258688,
  kMkvCodecDelay              = 0x56AA,
  kMkvSeekPreRoll             = 0x56BB,
  //video
  kMkvVideo                   = 0xE0,
  kMkvFlagInterlaced          = 0x9A,
  kMkvStereoMode              = 0x53B8,
  kMkvAlphaMode               = 0x53C0,
  kMkvPixelWidth              = 0xB0,
  kMkvPixelHeight             = 0xBA,
  kMkvPixelCropBottom         = 0x54AA,
  kMkvPixelCropTop            = 0x54BB,
  kMkvPixelCropLeft           = 0x54CC,
  kMkvPixelCropRight          = 0x54DD,
  kMkvDisplayWidth            = 0x54B0,
  kMkvDisplayHeight           = 0x54BA,
  kMkvDisplayUnit             = 0x54B2,
  kMkvAspectRatioType         = 0x54B3,
  kMkvFrameRate               = 0x2383E3,
  //end video
  //audio
  kMkvAudio                   = 0xE1,
  kMkvSamplingFrequency       = 0xB5,
  kMkvOutputSamplingFrequency = 0x78B5,
  kMkvChannels                = 0x9F,
  kMkvBitDepth                = 0x6264,
  //end audio
  //ContentEncodings
  kMkvContentEncodings        = 0x6D80,
  kMkvContentEncoding         = 0x6240,
  kMkvContentEncodingOrder    = 0x5031,
  kMkvContentEncodingScope    = 0x5032,
  kMkvContentEncodingType     = 0x5033,
  kMkvContentEncryption       = 0x5035,
  kMkvContentEncAlgo          = 0x47E1,
  kMkvContentEncKeyID         = 0x47E2,
  kMkvContentEncAESSettings   = 0x47E7,
  kMkvAESSettingsCipherMode   = 0x47E8,
  kMkvAESSettingsCipherInitData = 0x47E9,
  //end ContentEncodings
  //Cueing Data
  kMkvCues                    = 0x1C53BB6B,
  kMkvCuePoint                = 0xBB,
  kMkvCueTime                 = 0xB3,
  kMkvCueTrackPositions       = 0xB7,
  kMkvCueTrack                = 0xF7,
  kMkvCueClusterPosition      = 0xF1,
  kMkvCueBlockNumber          = 0x5378,
  //Chapters
  kMkvChapters                = 0x1043A770,
  kMkvEditionEntry            = 0x45B9,
  kMkvChapterAtom             = 0xB6,
  kMkvChapterUID              = 0x73C4,
  kMkvChapterStringUID        = 0x5654,
  kMkvChapterTimeStart        = 0x91,
  kMkvChapterTimeEnd          = 0x92,
  kMkvChapterDisplay          = 0x80,
  kMkvChapString              = 0x85,
  kMkvChapLanguage            = 0x437C,
  kMkvChapCountry             = 0x437E
};

}  // end namespace mkvmuxer

#endif // WEBMIDS_HPP
<|endoftext|>


<commit_before>#include <lug/Graphic/Vulkan/Loader.hpp>

namespace lug {
namespace Graphic {
namespace Vulkan {

// TODO: Handle errors
Loader::Loader() {
    #if defined(LUG_SYSTEM_WINDOWS)
    _handle = System::Library::open("vulkan-1.dll");
#else
    _handle = System::Library::open("libvulkan.so.1");
#endif

    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = System::Library::sym<PFN_##name>(_handle, #name);
    LUG_EXPORTED_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS

    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = reinterpret_cast<PFN_##name>(vkGetInstanceProcAddr(nullptr, #name));
    LUG_CORE_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS
}

Loader::~Loader() {
    #define LUG_UNLOAD_VULKAN_FUNCTIONS(name) name = nullptr;
    LUG_EXPORTED_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_CORE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_INSTANCE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_DEVICE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    #undef LUG_UNLOAD_VULKAN_FUNCTIONS

    System::Library::close(_handle);
}

// TODO: Handle errors
void Loader::loadInstanceFunctions(VkInstance instance) {
    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = reinterpret_cast<PFN_##name>(vkGetInstanceProcAddr(instance, #name));
    LUG_INSTANCE_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS
}

} // Vulkan
} // Graphic
} // lug
<commit_msg>Fix android vulkan loader name<commit_after>#include <lug/Graphic/Vulkan/Loader.hpp>

namespace lug {
namespace Graphic {
namespace Vulkan {

// TODO: Handle errors
Loader::Loader() {
#if defined(LUG_SYSTEM_WINDOWS)
    _handle = System::Library::open("vulkan-1.dll");
#elsif defined(LUG_SYSTEM_ANDROID)
    _handle = System::Library::open("libvulkan.so");
#else
    _handle = System::Library::open("libvulkan.so.1");
#endif

    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = System::Library::sym<PFN_##name>(_handle, #name);
    LUG_EXPORTED_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS

    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = reinterpret_cast<PFN_##name>(vkGetInstanceProcAddr(nullptr, #name));
    LUG_CORE_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS
}

Loader::~Loader() {
    #define LUG_UNLOAD_VULKAN_FUNCTIONS(name) name = nullptr;
    LUG_EXPORTED_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_CORE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_INSTANCE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    LUG_DEVICE_VULKAN_FUNCTIONS(LUG_UNLOAD_VULKAN_FUNCTIONS);
    #undef LUG_UNLOAD_VULKAN_FUNCTIONS

    System::Library::close(_handle);
}

// TODO: Handle errors
void Loader::loadInstanceFunctions(VkInstance instance) {
    #define LUG_LOAD_VULKAN_FUNCTIONS(name) name = reinterpret_cast<PFN_##name>(vkGetInstanceProcAddr(instance, #name));
    LUG_INSTANCE_VULKAN_FUNCTIONS(LUG_LOAD_VULKAN_FUNCTIONS);
    #undef LUG_LOAD_VULKAN_FUNCTIONS
}

} // Vulkan
} // Graphic
} // lug
<|endoftext|>


<commit_before>#include "ws2812b.h"

int WS2812B_init(WS2812B *self, int pin, Colour *pixels, int flags, unsigned int columns, unsigned int rows)
{
  if (digitalPinToPort(pin) == NOT_A_PIN) return 1;

  self->port = portOutputRegister(digitalPinToPort(pin));
  self->bit = digitalPinToBitMask(pin);
  self->pin = pin;
  self->count = columns*rows;
  self->pixels = pixels;
  self->flags = flags;
  self->columns = columns;
  self->rows = rows;

  return 0;
}

void WS2812B_sync(WS2812B *self)
{
  // https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
  volatile uint16_t count = self->count*3;
  uint8_t *ptr = (uint8_t*)self->pixels; // Next byte ptr (initialized to first here)
  uint8_t byte = *ptr++; // Current byte

  uint8_t hi = *self->port |  self->bit; // Cached high state
  uint8_t lo = *self->port & ~self->bit; // Cached low state

  volatile uint8_t next = lo;
  volatile uint8_t bit = 8;

  if (!count) return;

  cli(); // Disable interrupts

  // @TODO: Go through instruction set to see if we can relax the timing.
  // Note that the C code on the side is pseudo-pseudocode.
  // Some of the C code on the side would get optimsed, while some of it
  // can't be optimsed as effectively, which would significantly skew
  // the signal timing a WS281X chip expects. The timing here is on a very
  // tight schedule anyway, so we need to be careful.
  //
  //
  // See footnotes for extended descriptions.
  asm volatile (
    "__colour_bit:          \n\t"   // __colour_bit:
      "st %a[port], %[hi]   \n\t"   //   *port = hi;
      "sbrc %[byte], 7      \n\t\t" //   if (byte & 0x80)
        "mov %[next], %[hi] \n\t"   //     next = hi;
      "dec %[bit]           \n\t"   //   --bit;
      "st %a[port], %[next] \n\t"   //   *port = next;
      "mov %[next], %[lo]   \n\t"   //   next = lo;
      "breq __colour_byte   \n\t"   //   if (bit == 0) goto __colour_byte; // See [0]
      "lsl %[byte]          \n\t"   //   byte = (byte << 1);
      "nop; nop; nop        \n\t"   //   sleep_cycles(3);
      "st %a[port], %[lo]   \n\t"   //   *port = lo;
      "nop; nop; nop        \n\t"   //   sleep_cycles(3);
      "rjmp __colour_bit    \n"     //   goto __colour_bit;
                                    //
    "__colour_byte:         \n\t"   // __colour_byte:
      "ldi %[bit], 8        \n\t"   //   bit = 8;
      "ld %[byte], %a[ptr]+ \n\t"   //   byte = *ptr++;
      "st %a[port], %[lo]   \n\t"   //   *port = lo;
      "nop                  \n\t"   //   sleep_cycles(1);
      "sbiw %[count], 1     \n\t"   //   --count;
      "brne __colour_bit    \n"     //   if (bit != 0) goto __colour_bit; // See [1]

      // The benefit of extended ASM is that we can define registers and
      // operands elegantly:
    : [port]  "+e" (self->port),
      [count] "+w" (count),
      [bit]   "+r" (bit),
      [byte]  "+r" (byte),
      [next]  "+r" (next)
    : [ptr]    "e" (ptr),
      [hi]     "r" (hi),
      [lo]     "r" (lo)
  );
  // Enable interrupts
  sei();

  // Wait for *at least* 50µs; this causes the chip to latch the voltage onto
  // the individual LEDs contained within the RGB component.
  _delay_us(51);

  /**
   * # 1. `breq __colour_byte`
   * What this effectively does is test whether the Z flag in SREG register is
   * set to `1`, and if so, jumps to the defined symbol (`__colour_byte`).
   *
   * The Z flag (Zero flag) gets set when the previous arithmetic expression
   * evaluated to `0`. In our case, the last arithmetic expression was the
   * subtraction `dec %[bit]` (`--bit`).
   *
   * # 2. `brne __colour_bit`
   * This is similar to `breq`, but tests whether the Z flag in SREG is *not*
   * `1` before jumping. The flag gets cleared after the prevous arithmetic
   * expression evaluates to anything but `0`.
   *
   * ---
   * PS: "expression" is used as a synonym for "instruction"
   *
   * More detailed documentation can be found [here][doclink].
   *
   * [doclink]: http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html
   */
}

int WS2812B_set_pixel(WS2812B *self, unsigned int x, unsigned int y, Colour c)
{
  return _WS2812B_spf(self, x, y, _WS2812B_cc(c, self->flags & WS2812B_SCHEMES));
}

int WS2812B_set_pixel_at_offset(WS2812B *self, uint16_t offset, Colour c)
{
  if (offset >= self->count) return 1;
  self->pixels[offset] = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);
  return 0;
}

int WS2812B_set_all(WS2812B *self, Colour c)
{
  Colour _c = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);
  uint16_t count = self->count;

  while (--count) self->pixels[count] = _c;

  return 0;
}

Colour WS2812B_get_pixel(WS2812B *self, unsigned int x, unsigned int y)
{
  uint16_t offset = 0;
  if (x >= self->columns || y >= self->rows) return {0,0,0};

  if (self->flags & WS2812B_ZIGZAG_ODD && y % 2 != 0)
    x = self->columns - x - 1;

  if (self->flags & WS2812B_ZIGZAG_EVEN && y % 2 == 0)
    x = self->columns - x - 1;

  if(_WS2812B_addr(x, y, self->columns, &offset)) return {0,0,0};
  return self->pixels[offset];
}

int WS2812B_set_row(WS2812B *self, int row, Colour c)
{
  if (row >= self->rows || row < 0) return 1;
  Colour _c = c;

  for (unsigned int i = 0; i < self->columns; i++)
    _WS2812B_spf(self, i, row, _c);

  return 0;
}

int WS2812B_set_column(WS2812B *self, int col, Colour c)
{
  if (col >= self->columns || col < 0) return 1;
  Colour _c = c;

  for (unsigned int i = 0; i < self->rows; i++)
    _WS2812B_spf(self, col, i, _c);

  return 0;
}

/* Helper functions */
int _WS2812B_addr(unsigned int x, unsigned int y, unsigned int c, unsigned int *addr)
{
  if (x >= c) return 1;
  *addr = c * y + x;

  return 0;
}

int _WS2812B_spf(WS2812B *self, unsigned int x, unsigned int y, Colour c)
{
  uint16_t offset = 0;
  if (x >= self->columns || y >= self->rows) return 1;

  if (self->flags & WS2812B_ZIGZAG_ODD && y % 2 != 0)
    x = self->columns - x - 1;

  if (self->flags & WS2812B_ZIGZAG_EVEN && y % 2 == 0)
    x = self->columns - x - 1;

  if(_WS2812B_addr(x, y, self->columns, &offset)) return 1;

  self->pixels[offset] = c;
  return 0;
}

unsigned int _WS2812B_x(unsigned int addr, unsigned int c) {
  return addr % c;
}

unsigned int _WS2812B_y(unsigned int addr, int c) {
  return addr / c;
}

Colour _WS2812B_cc(Colour c, int order)
{
  switch (order) {
    case WS2812B_RBG:  return {c.red, c.blue, c.green};
    case WS2812B_GRB:  return {c.green, c.red, c.blue};
    case WS2812B_GBR:  return {c.green, c.blue, c.red};
    case WS2812B_BRG:  return {c.blue, c.red, c.green};
    case WS2812B_BGR:  return {c.blue, c.green, c.red};
    default /* RGB */: return c;
  }
}
<commit_msg>fix bug in column and row setting<commit_after>#include "ws2812b.h"

int WS2812B_init(WS2812B *self, int pin, Colour *pixels, int flags, unsigned int columns, unsigned int rows)
{
  if (digitalPinToPort(pin) == NOT_A_PIN) return 1;

  self->port = portOutputRegister(digitalPinToPort(pin));
  self->bit = digitalPinToBitMask(pin);
  self->pin = pin;
  self->count = columns*rows;
  self->pixels = pixels;
  self->flags = flags;
  self->columns = columns;
  self->rows = rows;

  return 0;
}

void WS2812B_sync(WS2812B *self)
{
  // https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
  volatile uint16_t count = self->count*3;
  uint8_t *ptr = (uint8_t*)self->pixels; // Next byte ptr (initialized to first here)
  uint8_t byte = *ptr++; // Current byte

  uint8_t hi = *self->port |  self->bit; // Cached high state
  uint8_t lo = *self->port & ~self->bit; // Cached low state

  volatile uint8_t next = lo;
  volatile uint8_t bit = 8;

  if (!count) return;

  cli(); // Disable interrupts

  // @TODO: Go through instruction set to see if we can relax the timing.
  // Note that the C code on the side is pseudo-pseudocode.
  // Some of the C code on the side would get optimsed, while some of it
  // can't be optimsed as effectively, which would significantly skew
  // the signal timing a WS281X chip expects. The timing here is on a very
  // tight schedule anyway, so we need to be careful.
  //
  //
  // See footnotes for extended descriptions.
  asm volatile (
    "__colour_bit:          \n\t"   // __colour_bit:
      "st %a[port], %[hi]   \n\t"   //   *port = hi;
      "sbrc %[byte], 7      \n\t\t" //   if (byte & 0x80)
        "mov %[next], %[hi] \n\t"   //     next = hi;
      "dec %[bit]           \n\t"   //   --bit;
      "st %a[port], %[next] \n\t"   //   *port = next;
      "mov %[next], %[lo]   \n\t"   //   next = lo;
      "breq __colour_byte   \n\t"   //   if (bit == 0) goto __colour_byte; // See [0]
      "lsl %[byte]          \n\t"   //   byte = (byte << 1);
      "nop; nop; nop        \n\t"   //   sleep_cycles(3);
      "st %a[port], %[lo]   \n\t"   //   *port = lo;
      "nop; nop; nop        \n\t"   //   sleep_cycles(3);
      "rjmp __colour_bit    \n"     //   goto __colour_bit;
                                    //
    "__colour_byte:         \n\t"   // __colour_byte:
      "ldi %[bit], 8        \n\t"   //   bit = 8;
      "ld %[byte], %a[ptr]+ \n\t"   //   byte = *ptr++;
      "st %a[port], %[lo]   \n\t"   //   *port = lo;
      "nop                  \n\t"   //   sleep_cycles(1);
      "sbiw %[count], 1     \n\t"   //   --count;
      "brne __colour_bit    \n"     //   if (bit != 0) goto __colour_bit; // See [1]

      // The benefit of extended ASM is that we can define registers and
      // operands elegantly:
    : [port]  "+e" (self->port),
      [count] "+w" (count),
      [bit]   "+r" (bit),
      [byte]  "+r" (byte),
      [next]  "+r" (next)
    : [ptr]    "e" (ptr),
      [hi]     "r" (hi),
      [lo]     "r" (lo)
  );
  // Enable interrupts
  sei();

  // Wait for *at least* 50µs; this causes the chip to latch the voltage onto
  // the individual LEDs contained within the RGB component.
  _delay_us(51);

  /**
   * # 1. `breq __colour_byte`
   * What this effectively does is test whether the Z flag in SREG register is
   * set to `1`, and if so, jumps to the defined symbol (`__colour_byte`).
   *
   * The Z flag (Zero flag) gets set when the previous arithmetic expression
   * evaluated to `0`. In our case, the last arithmetic expression was the
   * subtraction `dec %[bit]` (`--bit`).
   *
   * # 2. `brne __colour_bit`
   * This is similar to `breq`, but tests whether the Z flag in SREG is *not*
   * `1` before jumping. The flag gets cleared after the prevous arithmetic
   * expression evaluates to anything but `0`.
   *
   * ---
   * PS: "expression" is used as a synonym for "instruction"
   *
   * More detailed documentation can be found [here][doclink].
   *
   * [doclink]: http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html
   */
}

int WS2812B_set_pixel(WS2812B *self, unsigned int x, unsigned int y, Colour c)
{
  return _WS2812B_spf(self, x, y, _WS2812B_cc(c, self->flags & WS2812B_SCHEMES));
}

int WS2812B_set_pixel_at_offset(WS2812B *self, uint16_t offset, Colour c)
{
  if (offset >= self->count) return 1;
  self->pixels[offset] = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);
  return 0;
}

int WS2812B_set_all(WS2812B *self, Colour c)
{
  Colour _c = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);
  uint16_t count = self->count;

  while (--count) self->pixels[count] = _c;

  return 0;
}

Colour WS2812B_get_pixel(WS2812B *self, unsigned int x, unsigned int y)
{
  uint16_t offset = 0;
  if (x >= self->columns || y >= self->rows) return {0,0,0};

  if (self->flags & WS2812B_ZIGZAG_ODD && y % 2 != 0)
    x = self->columns - x - 1;

  if (self->flags & WS2812B_ZIGZAG_EVEN && y % 2 == 0)
    x = self->columns - x - 1;

  if(_WS2812B_addr(x, y, self->columns, &offset)) return {0,0,0};
  return self->pixels[offset];
}

int WS2812B_set_row(WS2812B *self, int row, Colour c)
{
  if (row >= self->rows || row < 0) return 1;
  Colour _c = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);

  for (unsigned int i = 0; i < self->columns; i++)
    _WS2812B_spf(self, i, row, _c);

  return 0;
}

int WS2812B_set_column(WS2812B *self, int col, Colour c)
{
  if (col >= self->columns || col < 0) return 1;
  Colour _c = _WS2812B_cc(c, self->flags & WS2812B_SCHEMES);

  for (unsigned int i = 0; i < self->rows; i++)
    _WS2812B_spf(self, col, i, _c);

  return 0;
}

/* Helper functions */
int _WS2812B_addr(unsigned int x, unsigned int y, unsigned int c, unsigned int *addr)
{
  if (x >= c) return 1;
  *addr = c * y + x;

  return 0;
}

int _WS2812B_spf(WS2812B *self, unsigned int x, unsigned int y, Colour c)
{
  uint16_t offset = 0;
  if (x >= self->columns || y >= self->rows) return 1;

  if (self->flags & WS2812B_ZIGZAG_ODD && y % 2 != 0)
    x = self->columns - x - 1;

  if (self->flags & WS2812B_ZIGZAG_EVEN && y % 2 == 0)
    x = self->columns - x - 1;

  if(_WS2812B_addr(x, y, self->columns, &offset)) return 1;

  self->pixels[offset] = c;
  return 0;
}

unsigned int _WS2812B_x(unsigned int addr, unsigned int c) {
  return addr % c;
}

unsigned int _WS2812B_y(unsigned int addr, int c) {
  return addr / c;
}

Colour _WS2812B_cc(Colour c, int order)
{
  switch (order) {
    case WS2812B_RBG:  return {c.red, c.blue, c.green};
    case WS2812B_GRB:  return {c.green, c.red, c.blue};
    case WS2812B_GBR:  return {c.green, c.blue, c.red};
    case WS2812B_BRG:  return {c.blue, c.red, c.green};
    case WS2812B_BGR:  return {c.blue, c.green, c.red};
    default /* RGB */: return c;
  }
}
<|endoftext|>