<commit_before>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/predecoder.hh"
#include "base/misc.hh"
#include "base/trace.hh"
#include "sim/host.hh"

namespace X86ISA
{
    void Predecoder::reset()
    {
        origPC = basePC + offset;
        DPRINTF(Predecoder, "Setting origPC to %#x\n", origPC);
        emi.opcode.num = 0;

        immediateCollected = 0;
        emi.immediate = 0;
        displacementCollected = 0;
        emi.displacement = 0;

        emi.modRM = 0;
        emi.sib = 0;
    }

    void Predecoder::process()
    {
        //This function drives the predecoder state machine.

        //Some sanity checks. You shouldn't try to process more bytes if
        //there aren't any, and you shouldn't overwrite an already
        //predecoder ExtMachInst.
        assert(!outOfBytes);
        assert(!emiIsReady);

        //While there's still something to do...
        while(!emiIsReady && !outOfBytes)
        {
            uint8_t nextByte = getNextByte();
            switch(state)
            {
              case ResetState:
                reset();
                state = PrefixState;
              case PrefixState:
                state = doPrefixState(nextByte);
                break;
              case OpcodeState:
                state = doOpcodeState(nextByte);
                break;
              case ModRMState:
                state = doModRMState(nextByte);
                break;
              case SIBState:
                state = doSIBState(nextByte);
                break;
              case DisplacementState:
                state = doDisplacementState();
                break;
              case ImmediateState:
                state = doImmediateState();
                break;
              case ErrorState:
                panic("Went to the error state in the predecoder.\n");
              default:
                panic("Unrecognized state! %d\n", state);
            }
        }
    }

    //Either get a prefix and record it in the ExtMachInst, or send the
    //state machine on to get the opcode(s).
    Predecoder::State Predecoder::doPrefixState(uint8_t nextByte)
    {
        uint8_t prefix = Prefixes[nextByte];
        State nextState = PrefixState;
        if(prefix)
            consumeByte();
        switch(prefix)
        {
            //Operand size override prefixes
          case OperandSizeOverride:
            DPRINTF(Predecoder, "Found operand size override prefix.\n");
            emi.legacy.op = true;
            break;
          case AddressSizeOverride:
            DPRINTF(Predecoder, "Found address size override prefix.\n");
            emi.legacy.addr = true;
            break;
            //Segment override prefixes
          case CSOverride:
          case DSOverride:
          case ESOverride:
          case FSOverride:
          case GSOverride:
          case SSOverride:
            DPRINTF(Predecoder, "Found segment override.\n");
            emi.legacy.seg = prefix;
            break;
          case Lock:
            DPRINTF(Predecoder, "Found lock prefix.\n");
            emi.legacy.lock = true;
            break;
          case Rep:
            DPRINTF(Predecoder, "Found rep prefix.\n");
            emi.legacy.rep = true;
            break;
          case Repne:
            DPRINTF(Predecoder, "Found repne prefix.\n");
            emi.legacy.repne = true;
            break;
          case RexPrefix:
            DPRINTF(Predecoder, "Found Rex prefix %#x.\n", nextByte);
            emi.rex = nextByte;
            break;
          case 0:
            nextState = OpcodeState;
            break;
          default:
            panic("Unrecognized prefix %#x\n", nextByte);
        }
        return nextState;
    }

    //Load all the opcodes (currently up to 2) and then figure out
    //what immediate and/or ModRM is needed.
    Predecoder::State Predecoder::doOpcodeState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.opcode.num++;
        //We can't handle 3+ byte opcodes right now
        assert(emi.opcode.num < 3);
        consumeByte();
        if(emi.opcode.num == 1 && nextByte == 0x0f)
        {
            nextState = OpcodeState;
            DPRINTF(Predecoder, "Found two byte opcode.\n");
            emi.opcode.prefixA = nextByte;
        }
        else if(emi.opcode.num == 2 &&
                (nextByte == 0x0f ||
                 (nextByte & 0xf8) == 0x38))
        {
            panic("Three byte opcodes aren't yet supported!\n");
            nextState = OpcodeState;
            DPRINTF(Predecoder, "Found three byte opcode.\n");
            emi.opcode.prefixB = nextByte;
        }
        else
        {
            DPRINTF(Predecoder, "Found opcode %#x.\n", nextByte);
            emi.opcode.op = nextByte;

            //Figure out the effective operand size. This can be overriden to
            //a fixed value at the decoder level.
            if(/*FIXME long mode*/1)
            {
                if(emi.rex && emi.rex.w)
                    emi.opSize = 3; // 64 bit operand size
                else if(emi.legacy.op)
                    emi.opSize = 1; // 16 bit operand size
                else
                    emi.opSize = 2; // 32 bit operand size
            }
            else if(/*FIXME default 32*/1)
            {
                if(emi.legacy.op)
                    emi.opSize = 1; // 16 bit operand size
                else
                    emi.opSize = 2; // 32 bit operand size
            }
            else // 16 bit default operand size
            {
                if(emi.legacy.op)
                    emi.opSize = 2; // 32 bit operand size
                else
                    emi.opSize = 1; // 16 bit operand size
            }

            //Figure out how big of an immediate we'll retreive based
            //on the opcode.
            int immType = ImmediateType[emi.opcode.num - 1][nextByte];
            immediateSize = SizeTypeToSize[emi.opSize - 1][immType];

            //Determine what to expect next
            if (UsesModRM[emi.opcode.num - 1][nextByte]) {
                nextState = ModRMState;
            } else {
                if(immediateSize) {
                    nextState = ImmediateState;
                } else {
                    emiIsReady = true;
                    nextState = ResetState;
                }
            }
        }
        return nextState;
    }

    //Get the ModRM byte and determine what displacement, if any, there is.
    //Also determine whether or not to get the SIB byte, displacement, or
    //immediate next.
    Predecoder::State Predecoder::doModRMState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.modRM = nextByte;
        DPRINTF(Predecoder, "Found modrm byte %#x.\n", nextByte);
        if (0) {//FIXME in 16 bit mode
            //figure out 16 bit displacement size
            if(nextByte & 0xC7 == 0x06 ||
                    nextByte & 0xC0 == 0x80)
                displacementSize = 2;
            else if(nextByte & 0xC0 == 0x40)
                displacementSize = 1;
            else
                displacementSize = 0;
        } else {
            //figure out 32/64 bit displacement size
            if(nextByte & 0xC6 == 0x04 ||
                    nextByte & 0xC0 == 0x80)
                displacementSize = 4;
            else if(nextByte & 0xC0 == 0x40)
                displacementSize = 1;
            else
                displacementSize = 0;
        }
        //If there's an SIB, get that next.
        //There is no SIB in 16 bit mode.
        if(nextByte & 0x7 == 4 &&
                nextByte & 0xC0 != 0xC0) {
                // && in 32/64 bit mode)
            nextState = SIBState;
        } else if(displacementSize) {
            nextState = DisplacementState;
        } else if(immediateSize) {
            nextState = ImmediateState;
        } else {
            emiIsReady = true;
            nextState = ResetState;
        }
        //The ModRM byte is consumed no matter what
        consumeByte();
        return nextState;
    }

    //Get the SIB byte. We don't do anything with it at this point, other
    //than storing it in the ExtMachInst. Determine if we need to get a
    //displacement or immediate next.
    Predecoder::State Predecoder::doSIBState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.sib = nextByte;
        DPRINTF(Predecoder, "Found SIB byte %#x.\n", nextByte);
        consumeByte();
        if(displacementSize) {
            nextState = DisplacementState;
        } else if(immediateSize) {
            nextState = ImmediateState;
        } else {
            emiIsReady = true;
            nextState = ResetState;
        }
        return nextState;
    }

    //Gather up the displacement, or at least as much of it
    //as we can get.
    Predecoder::State Predecoder::doDisplacementState()
    {
        State nextState = ErrorState;

        getImmediate(displacementCollected,
                emi.displacement,
                displacementSize);

        DPRINTF(Predecoder, "Collecting %d byte displacement, got %d bytes.\n",
                displacementSize, displacementCollected);

        if(displacementSize == displacementCollected) {
            //Sign extend the displacement
            switch(displacementSize)
            {
              case 1:
                emi.displacement = sext<8>(emi.displacement);
                break;
              case 2:
                emi.displacement = sext<16>(emi.displacement);
                break;
              case 4:
                emi.displacement = sext<32>(emi.displacement);
                break;
              default:
                panic("Undefined displacement size!\n");
            }
            DPRINTF(Predecoder, "Collected displacement %#x.\n",
                    emi.displacement);
            if(immediateSize) {
                nextState = ImmediateState;
            } else {
                emiIsReady = true;
                nextState = ResetState;
            }
        }
        else
            nextState = DisplacementState;
        return nextState;
    }

    //Gather up the immediate, or at least as much of it
    //as we can get
    Predecoder::State Predecoder::doImmediateState()
    {
        State nextState = ErrorState;

        getImmediate(immediateCollected,
                emi.immediate,
                immediateSize);

        DPRINTF(Predecoder, "Collecting %d byte immediate, got %d bytes.\n",
                immediateSize, immediateCollected);

        if(immediateSize == immediateCollected)
        {
            //XXX Warning! The following is an observed pattern and might
            //not always be true!

            //Instructions which use 64 bit operands but 32 bit immediates
            //need to have the immediate sign extended to 64 bits.
            //Instructions which use true 64 bit immediates won't be
            //affected, and instructions that use true 32 bit immediates
            //won't notice.
            if(immediateSize == 4)
                emi.immediate = sext<32>(emi.immediate);
            DPRINTF(Predecoder, "Collected immediate %#x.\n",
                    emi.immediate);
            emiIsReady = true;
            nextState = ResetState;
        }
        else
            nextState = ImmediateState;
        return nextState;
    }
}
<commit_msg>Reset the rex and legacy prefix components of the ExtMachInst as well.<commit_after>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/predecoder.hh"
#include "base/misc.hh"
#include "base/trace.hh"
#include "sim/host.hh"

namespace X86ISA
{
    void Predecoder::reset()
    {
        origPC = basePC + offset;
        DPRINTF(Predecoder, "Setting origPC to %#x\n", origPC);
        emi.rex = 0;
        emi.legacy = 0;
        emi.opcode.num = 0;

        immediateCollected = 0;
        emi.immediate = 0;
        displacementCollected = 0;
        emi.displacement = 0;

        emi.modRM = 0;
        emi.sib = 0;
    }

    void Predecoder::process()
    {
        //This function drives the predecoder state machine.

        //Some sanity checks. You shouldn't try to process more bytes if
        //there aren't any, and you shouldn't overwrite an already
        //predecoder ExtMachInst.
        assert(!outOfBytes);
        assert(!emiIsReady);

        //While there's still something to do...
        while(!emiIsReady && !outOfBytes)
        {
            uint8_t nextByte = getNextByte();
            switch(state)
            {
              case ResetState:
                reset();
                state = PrefixState;
              case PrefixState:
                state = doPrefixState(nextByte);
                break;
              case OpcodeState:
                state = doOpcodeState(nextByte);
                break;
              case ModRMState:
                state = doModRMState(nextByte);
                break;
              case SIBState:
                state = doSIBState(nextByte);
                break;
              case DisplacementState:
                state = doDisplacementState();
                break;
              case ImmediateState:
                state = doImmediateState();
                break;
              case ErrorState:
                panic("Went to the error state in the predecoder.\n");
              default:
                panic("Unrecognized state! %d\n", state);
            }
        }
    }

    //Either get a prefix and record it in the ExtMachInst, or send the
    //state machine on to get the opcode(s).
    Predecoder::State Predecoder::doPrefixState(uint8_t nextByte)
    {
        uint8_t prefix = Prefixes[nextByte];
        State nextState = PrefixState;
        if(prefix)
            consumeByte();
        switch(prefix)
        {
            //Operand size override prefixes
          case OperandSizeOverride:
            DPRINTF(Predecoder, "Found operand size override prefix.\n");
            emi.legacy.op = true;
            break;
          case AddressSizeOverride:
            DPRINTF(Predecoder, "Found address size override prefix.\n");
            emi.legacy.addr = true;
            break;
            //Segment override prefixes
          case CSOverride:
          case DSOverride:
          case ESOverride:
          case FSOverride:
          case GSOverride:
          case SSOverride:
            DPRINTF(Predecoder, "Found segment override.\n");
            emi.legacy.seg = prefix;
            break;
          case Lock:
            DPRINTF(Predecoder, "Found lock prefix.\n");
            emi.legacy.lock = true;
            break;
          case Rep:
            DPRINTF(Predecoder, "Found rep prefix.\n");
            emi.legacy.rep = true;
            break;
          case Repne:
            DPRINTF(Predecoder, "Found repne prefix.\n");
            emi.legacy.repne = true;
            break;
          case RexPrefix:
            DPRINTF(Predecoder, "Found Rex prefix %#x.\n", nextByte);
            emi.rex = nextByte;
            break;
          case 0:
            nextState = OpcodeState;
            break;
          default:
            panic("Unrecognized prefix %#x\n", nextByte);
        }
        return nextState;
    }

    //Load all the opcodes (currently up to 2) and then figure out
    //what immediate and/or ModRM is needed.
    Predecoder::State Predecoder::doOpcodeState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.opcode.num++;
        //We can't handle 3+ byte opcodes right now
        assert(emi.opcode.num < 3);
        consumeByte();
        if(emi.opcode.num == 1 && nextByte == 0x0f)
        {
            nextState = OpcodeState;
            DPRINTF(Predecoder, "Found two byte opcode.\n");
            emi.opcode.prefixA = nextByte;
        }
        else if(emi.opcode.num == 2 &&
                (nextByte == 0x0f ||
                 (nextByte & 0xf8) == 0x38))
        {
            panic("Three byte opcodes aren't yet supported!\n");
            nextState = OpcodeState;
            DPRINTF(Predecoder, "Found three byte opcode.\n");
            emi.opcode.prefixB = nextByte;
        }
        else
        {
            DPRINTF(Predecoder, "Found opcode %#x.\n", nextByte);
            emi.opcode.op = nextByte;

            //Figure out the effective operand size. This can be overriden to
            //a fixed value at the decoder level.
            if(/*FIXME long mode*/1)
            {
                if(emi.rex && emi.rex.w)
                    emi.opSize = 3; // 64 bit operand size
                else if(emi.legacy.op)
                    emi.opSize = 1; // 16 bit operand size
                else
                    emi.opSize = 2; // 32 bit operand size
            }
            else if(/*FIXME default 32*/1)
            {
                if(emi.legacy.op)
                    emi.opSize = 1; // 16 bit operand size
                else
                    emi.opSize = 2; // 32 bit operand size
            }
            else // 16 bit default operand size
            {
                if(emi.legacy.op)
                    emi.opSize = 2; // 32 bit operand size
                else
                    emi.opSize = 1; // 16 bit operand size
            }

            //Figure out how big of an immediate we'll retreive based
            //on the opcode.
            int immType = ImmediateType[emi.opcode.num - 1][nextByte];
            immediateSize = SizeTypeToSize[emi.opSize - 1][immType];

            //Determine what to expect next
            if (UsesModRM[emi.opcode.num - 1][nextByte]) {
                nextState = ModRMState;
            } else {
                if(immediateSize) {
                    nextState = ImmediateState;
                } else {
                    emiIsReady = true;
                    nextState = ResetState;
                }
            }
        }
        return nextState;
    }

    //Get the ModRM byte and determine what displacement, if any, there is.
    //Also determine whether or not to get the SIB byte, displacement, or
    //immediate next.
    Predecoder::State Predecoder::doModRMState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.modRM = nextByte;
        DPRINTF(Predecoder, "Found modrm byte %#x.\n", nextByte);
        if (0) {//FIXME in 16 bit mode
            //figure out 16 bit displacement size
            if(nextByte & 0xC7 == 0x06 ||
                    nextByte & 0xC0 == 0x80)
                displacementSize = 2;
            else if(nextByte & 0xC0 == 0x40)
                displacementSize = 1;
            else
                displacementSize = 0;
        } else {
            //figure out 32/64 bit displacement size
            if(nextByte & 0xC6 == 0x04 ||
                    nextByte & 0xC0 == 0x80)
                displacementSize = 4;
            else if(nextByte & 0xC0 == 0x40)
                displacementSize = 1;
            else
                displacementSize = 0;
        }
        //If there's an SIB, get that next.
        //There is no SIB in 16 bit mode.
        if(nextByte & 0x7 == 4 &&
                nextByte & 0xC0 != 0xC0) {
                // && in 32/64 bit mode)
            nextState = SIBState;
        } else if(displacementSize) {
            nextState = DisplacementState;
        } else if(immediateSize) {
            nextState = ImmediateState;
        } else {
            emiIsReady = true;
            nextState = ResetState;
        }
        //The ModRM byte is consumed no matter what
        consumeByte();
        return nextState;
    }

    //Get the SIB byte. We don't do anything with it at this point, other
    //than storing it in the ExtMachInst. Determine if we need to get a
    //displacement or immediate next.
    Predecoder::State Predecoder::doSIBState(uint8_t nextByte)
    {
        State nextState = ErrorState;
        emi.sib = nextByte;
        DPRINTF(Predecoder, "Found SIB byte %#x.\n", nextByte);
        consumeByte();
        if(displacementSize) {
            nextState = DisplacementState;
        } else if(immediateSize) {
            nextState = ImmediateState;
        } else {
            emiIsReady = true;
            nextState = ResetState;
        }
        return nextState;
    }

    //Gather up the displacement, or at least as much of it
    //as we can get.
    Predecoder::State Predecoder::doDisplacementState()
    {
        State nextState = ErrorState;

        getImmediate(displacementCollected,
                emi.displacement,
                displacementSize);

        DPRINTF(Predecoder, "Collecting %d byte displacement, got %d bytes.\n",
                displacementSize, displacementCollected);

        if(displacementSize == displacementCollected) {
            //Sign extend the displacement
            switch(displacementSize)
            {
              case 1:
                emi.displacement = sext<8>(emi.displacement);
                break;
              case 2:
                emi.displacement = sext<16>(emi.displacement);
                break;
              case 4:
                emi.displacement = sext<32>(emi.displacement);
                break;
              default:
                panic("Undefined displacement size!\n");
            }
            DPRINTF(Predecoder, "Collected displacement %#x.\n",
                    emi.displacement);
            if(immediateSize) {
                nextState = ImmediateState;
            } else {
                emiIsReady = true;
                nextState = ResetState;
            }
        }
        else
            nextState = DisplacementState;
        return nextState;
    }

    //Gather up the immediate, or at least as much of it
    //as we can get
    Predecoder::State Predecoder::doImmediateState()
    {
        State nextState = ErrorState;

        getImmediate(immediateCollected,
                emi.immediate,
                immediateSize);

        DPRINTF(Predecoder, "Collecting %d byte immediate, got %d bytes.\n",
                immediateSize, immediateCollected);

        if(immediateSize == immediateCollected)
        {
            //XXX Warning! The following is an observed pattern and might
            //not always be true!

            //Instructions which use 64 bit operands but 32 bit immediates
            //need to have the immediate sign extended to 64 bits.
            //Instructions which use true 64 bit immediates won't be
            //affected, and instructions that use true 32 bit immediates
            //won't notice.
            if(immediateSize == 4)
                emi.immediate = sext<32>(emi.immediate);
            DPRINTF(Predecoder, "Collected immediate %#x.\n",
                    emi.immediate);
            emiIsReady = true;
            nextState = ResetState;
        }
        else
            nextState = ImmediateState;
        return nextState;
    }
}
<|endoftext|>


<commit_before>//
// Copyright (c) articy Software GmbH & Co. KG. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include "CodeGenerator.h"
#include "ArticyImportData.h"
#include "DatabaseGenerator.h"
#include "GlobalVarsGenerator.h"
#include "InterfacesGenerator.h"
#include "Developer/HotReload/Public/IHotReload.h"
#include "ArticyRuntime/Public/ArticyGlobalVariables.h"
#include "ObjectDefinitionsGenerator.h"
#include "PackagesGenerator.h"
#include "ArticyDatabase.h"
#include "ExpressoScriptsGenerator.h"
#include "FileHelpers.h"
#include <IProjectManager.h>
#include <GameProjectGenerationModule.h>
#include <UnrealEdMisc.h>
#include <GenericPlatformMisc.h>
#include <Dialogs.h>

//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
#define LOCTEXT_NAMESPACE "CodeGenerator"

FString CodeGenerator::GetSourceFolder()
{
	return FPaths::GameSourceDir() / FApp::GetProjectName() / TEXT("ArticyGenerated");
}

FString CodeGenerator::GetGeneratedInterfacesFilename(const UArticyImportData* Data)
{
	return Data->GetProject().TechnicalName + "Interfaces";
}

FString CodeGenerator::GetGeneratedTypesFilename(const UArticyImportData* Data)
{
	return Data->GetProject().TechnicalName + "ArticyTypes";
}

FString CodeGenerator::GetGlobalVarsClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "GlobalVariables";
}

FString CodeGenerator::GetGVNamespaceClassname(const UArticyImportData* Data, const FString& Namespace)
{
	return "U" + Data->GetProject().TechnicalName + Namespace + "Variables";
}

FString CodeGenerator::GetDatabaseClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "Database";
}

FString CodeGenerator::GetMethodsProviderClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "MethodsProvider";
}

FString CodeGenerator::GetExpressoScriptsClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "ExpressoScripts";
}

FString CodeGenerator::GetFeatureInterfaceClassName(const UArticyImportData* Data, const FArticyTemplateFeatureDef& Feature, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "I") + Data->GetProject().TechnicalName + "ObjectWith" + Feature.GetTechnicalName() + "Feature";
}

bool CodeGenerator::DeleteGeneratedCode(const FString& Filename)
{
	if (Filename.IsEmpty())
		return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectoryRecursively(*GetSourceFolder());

	return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*(GetSourceFolder() / Filename));
}

void CodeGenerator::GenerateCode(UArticyImportData* Data)
{
	if (!Data)
		return;

	//generate GVs and ObjectDefinitions only if needed
	if (Data->GetSettings().DidObjectDefsOrGVsChange())
	{
		//DeleteGeneratedCode();

		GlobalVarsGenerator::GenerateCode(Data);
		DatabaseGenerator::GenerateCode(Data);
		InterfacesGenerator::GenerateCode(Data);
		ObjectDefinitionsGenerator::GenerateCode(Data);

	}

	//gather all the scripts in all packages
	if (Data->GetSettings().set_UseScriptSupport)
		Data->GatherScripts();

	//generate the expresso class, containing all the c++ script fragments
	//the class is also generated if script support is disabled, it's just empty in that case
	ExpressoScriptsGenerator::GenerateCode(Data);

	//trigger compile
	Compile(Data);
}

void CodeGenerator::Recompile(UArticyImportData* Data)
{
	Compile(Data);
}

bool CodeGenerator::DeleteGeneratedAssets()
{
	return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectoryRecursively(*ArticyHelpers::ArticyGeneratedFolder);
}

void CodeGenerator::Compile(UArticyImportData* Data)
{
	bool bWaitingForOtherCompile = false;

	// We can only hot-reload via DoHotReloadFromEditor when we already had code in our project
	IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked<IHotReloadInterface>("HotReload");
	if (HotReloadSupport.IsCurrentlyCompiling())
	{
		bWaitingForOtherCompile = true;
		UE_LOG(LogArticyImporter, Warning, TEXT("Already compiling, waiting until it's done."));
	}

	static FDelegateHandle lambdaHandle;
	if (lambdaHandle.IsValid())
		IHotReloadModule::Get().OnModuleCompilerFinished().Remove(lambdaHandle);

	lambdaHandle = IHotReloadModule::Get().OnModuleCompilerFinished().AddLambda([=](FString OutputLog, ECompilationResult::Type Result, bool bShowLog)
	{
		OnCompiled(Result, Data, bWaitingForOtherCompile);
	});

	if (!bWaitingForOtherCompile)
		HotReloadSupport.DoHotReloadFromEditor(EHotReloadFlags::None /*async*/);
}

void CodeGenerator::OnCompiled(const ECompilationResult::Type Result, UArticyImportData* Data, const bool bWaitingForOtherCompile)
{
	bool succeeded = Result == ECompilationResult::Succeeded || Result == ECompilationResult::UpToDate;
	if (!succeeded)
	{
		//compile failed
		UE_LOG(LogArticyImporter, Error, TEXT("Compile failed, cannot continue importing articy:draft data!"));
		return;
	}

	if (bWaitingForOtherCompile || Data->GetSettings().DidObjectDefsOrGVsChange())
	{
		if (!bWaitingForOtherCompile)
		{
			//the object definitions are up to date now
			Data->GetSettings().SetObjectDefinitionsRebuilt();
		}

		//another compile is needed
		Compile(Data);
		return;
	}

	//compiling is done!
	//check if UArticyBaseGlobalVariables can be found, otherwise something went wrong!
	auto className = GetGlobalVarsClassname(Data, true);
	auto fullClassName = FString::Printf(TEXT("Class'/Script/%s.%s'"), FApp::GetProjectName(), *className);
	if (!ensure(ConstructorHelpersInternal::FindOrLoadClass(fullClassName, UArticyGlobalVariables::StaticClass())))
		UE_LOG(LogArticyImporter, Error, TEXT("Could not find generated global variables class after compile!"));

	ensure(DeleteGeneratedAssets());

	//generate the global variables asset
	GlobalVarsGenerator::GenerateAsset(Data);
	//generate the database asset
	auto db = DatabaseGenerator::GenerateAsset(Data);
	//generate assets for all the imported objects
	PackagesGenerator::GenerateAssets(Data);

	//register the newly imported packages in the database
	if (ensureMsgf(db, TEXT("Could not create ArticyDatabase asset!")))
	{
		db->SetLoadedPackages(Data->GetPackages());
		db->MarkPackageDirty();
	}
	//prompt the user to save newly generated packages
	FEditorFileUtils::SaveDirtyPackages(true, true, /*bSaveContentPackages*/ true, false, false, true);

	FText Message = LOCTEXT("GenerationRestartMessage", "To properly update articy:draft data, the editor needs to be restarted.");
	FText Title = LOCTEXT("GenerationRestartTitle", "Restart editor");
	EAppReturnType::Type returnType = OpenMsgDlgInt(EAppMsgType::Ok, EAppReturnType::Ok, Message, Title);

	if (returnType == EAppReturnType::Ok)
	{
		FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath());

		FText FailMessage;
		IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage);

		const bool bWarn = false;
		FUnrealEdMisc::Get().RestartEditor(bWarn);
	}
}

#undef LOCTEXT_NAMESPACE<commit_msg>Added engine version distinction for dialogue function<commit_after>//
// Copyright (c) articy Software GmbH & Co. KG. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include "CodeGenerator.h"
#include "ArticyImportData.h"
#include "DatabaseGenerator.h"
#include "GlobalVarsGenerator.h"
#include "InterfacesGenerator.h"
#include "Developer/HotReload/Public/IHotReload.h"
#include "ArticyRuntime/Public/ArticyGlobalVariables.h"
#include "ObjectDefinitionsGenerator.h"
#include "PackagesGenerator.h"
#include "ArticyDatabase.h"
#include "ExpressoScriptsGenerator.h"
#include "FileHelpers.h"
#include <IProjectManager.h>
#include <GameProjectGenerationModule.h>
#include <UnrealEdMisc.h>
#include <GenericPlatformMisc.h>
#include <Dialogs.h>
#include "../Launch/Resources/Version.h"

//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
//---------------------------------------------------------------------------//
#define LOCTEXT_NAMESPACE "CodeGenerator"

FString CodeGenerator::GetSourceFolder()
{
	return FPaths::GameSourceDir() / FApp::GetProjectName() / TEXT("ArticyGenerated");
}

FString CodeGenerator::GetGeneratedInterfacesFilename(const UArticyImportData* Data)
{
	return Data->GetProject().TechnicalName + "Interfaces";
}

FString CodeGenerator::GetGeneratedTypesFilename(const UArticyImportData* Data)
{
	return Data->GetProject().TechnicalName + "ArticyTypes";
}

FString CodeGenerator::GetGlobalVarsClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "GlobalVariables";
}

FString CodeGenerator::GetGVNamespaceClassname(const UArticyImportData* Data, const FString& Namespace)
{
	return "U" + Data->GetProject().TechnicalName + Namespace + "Variables";
}

FString CodeGenerator::GetDatabaseClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "Database";
}

FString CodeGenerator::GetMethodsProviderClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "MethodsProvider";
}

FString CodeGenerator::GetExpressoScriptsClassname(const UArticyImportData* Data, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "U") + Data->GetProject().TechnicalName + "ExpressoScripts";
}

FString CodeGenerator::GetFeatureInterfaceClassName(const UArticyImportData* Data, const FArticyTemplateFeatureDef& Feature, const bool bOmittPrefix)
{
	return (bOmittPrefix ? "" : "I") + Data->GetProject().TechnicalName + "ObjectWith" + Feature.GetTechnicalName() + "Feature";
}

bool CodeGenerator::DeleteGeneratedCode(const FString& Filename)
{
	if (Filename.IsEmpty())
		return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectoryRecursively(*GetSourceFolder());

	return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*(GetSourceFolder() / Filename));
}

void CodeGenerator::GenerateCode(UArticyImportData* Data)
{
	if (!Data)
		return;

	//generate GVs and ObjectDefinitions only if needed
	if (Data->GetSettings().DidObjectDefsOrGVsChange())
	{
		//DeleteGeneratedCode();

		GlobalVarsGenerator::GenerateCode(Data);
		DatabaseGenerator::GenerateCode(Data);
		InterfacesGenerator::GenerateCode(Data);
		ObjectDefinitionsGenerator::GenerateCode(Data);

	}

	//gather all the scripts in all packages
	if (Data->GetSettings().set_UseScriptSupport)
		Data->GatherScripts();

	//generate the expresso class, containing all the c++ script fragments
	//the class is also generated if script support is disabled, it's just empty in that case
	ExpressoScriptsGenerator::GenerateCode(Data);

	//trigger compile
	Compile(Data);
}

void CodeGenerator::Recompile(UArticyImportData* Data)
{
	Compile(Data);
}

bool CodeGenerator::DeleteGeneratedAssets()
{
	return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectoryRecursively(*ArticyHelpers::ArticyGeneratedFolder);
}

void CodeGenerator::Compile(UArticyImportData* Data)
{
	bool bWaitingForOtherCompile = false;

	// We can only hot-reload via DoHotReloadFromEditor when we already had code in our project
	IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked<IHotReloadInterface>("HotReload");
	if (HotReloadSupport.IsCurrentlyCompiling())
	{
		bWaitingForOtherCompile = true;
		UE_LOG(LogArticyImporter, Warning, TEXT("Already compiling, waiting until it's done."));
	}

	static FDelegateHandle lambdaHandle;
	if (lambdaHandle.IsValid())
		IHotReloadModule::Get().OnModuleCompilerFinished().Remove(lambdaHandle);

	lambdaHandle = IHotReloadModule::Get().OnModuleCompilerFinished().AddLambda([=](FString OutputLog, ECompilationResult::Type Result, bool bShowLog)
	{
		OnCompiled(Result, Data, bWaitingForOtherCompile);
	});

	if (!bWaitingForOtherCompile)
		HotReloadSupport.DoHotReloadFromEditor(EHotReloadFlags::None /*async*/);
}

void CodeGenerator::OnCompiled(const ECompilationResult::Type Result, UArticyImportData* Data, const bool bWaitingForOtherCompile)
{
	bool succeeded = Result == ECompilationResult::Succeeded || Result == ECompilationResult::UpToDate;
	if (!succeeded)
	{
		//compile failed
		UE_LOG(LogArticyImporter, Error, TEXT("Compile failed, cannot continue importing articy:draft data!"));
		return;
	}

	if (bWaitingForOtherCompile || Data->GetSettings().DidObjectDefsOrGVsChange())
	{
		if (!bWaitingForOtherCompile)
		{
			//the object definitions are up to date now
			Data->GetSettings().SetObjectDefinitionsRebuilt();
		}

		//another compile is needed
		Compile(Data);
		return;
	}

	//compiling is done!
	//check if UArticyBaseGlobalVariables can be found, otherwise something went wrong!
	auto className = GetGlobalVarsClassname(Data, true);
	auto fullClassName = FString::Printf(TEXT("Class'/Script/%s.%s'"), FApp::GetProjectName(), *className);
	if (!ensure(ConstructorHelpersInternal::FindOrLoadClass(fullClassName, UArticyGlobalVariables::StaticClass())))
		UE_LOG(LogArticyImporter, Error, TEXT("Could not find generated global variables class after compile!"));

	ensure(DeleteGeneratedAssets());

	//generate the global variables asset
	GlobalVarsGenerator::GenerateAsset(Data);
	//generate the database asset
	auto db = DatabaseGenerator::GenerateAsset(Data);
	//generate assets for all the imported objects
	PackagesGenerator::GenerateAssets(Data);

	//register the newly imported packages in the database
	if (ensureMsgf(db, TEXT("Could not create ArticyDatabase asset!")))
	{
		db->SetLoadedPackages(Data->GetPackages());
		db->MarkPackageDirty();
	}
	//prompt the user to save newly generated packages
	FEditorFileUtils::SaveDirtyPackages(true, true, /*bSaveContentPackages*/ true, false, false, true);

	FText Message = LOCTEXT("GenerationRestartMessage", "To properly update articy:draft data, the editor needs to be restarted.");
	FText Title = LOCTEXT("GenerationRestartTitle", "Restart editor");
	EAppReturnType::Type returnType;
#if ENGINE_MINOR_VERSION >= 20
	returnType = OpenMsgDlgInt(EAppMsgType::Ok, EAppReturnType::Ok, Message, Title);
#elif ENGINE_MINOR_VERSION == 19
	returnType = OpenMsgDlgInt(EAppMsgType::Ok, Message, Title);
#endif

	if (returnType == EAppReturnType::Ok)
	{
		FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath());

		FText FailMessage;
		IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage);

		const bool bWarn = false;
		FUnrealEdMisc::Get().RestartEditor(bWarn);
	}
}

#undef LOCTEXT_NAMESPACE<|endoftext|>


<commit_before>#include "VirtualBox.h"

#if LAUNCHER_LINUX
const std::string SubutaiLauncher::VirtualBox::BIN = "vboxmanage";
#elif LAUNCHER_MACOS
const std::string SubutaiLauncher::VirtualBox::BIN = "VBoxManage";
#else
const std::string SubutaiLauncher::VirtualBox::BIN = "VBoxManage.exe";
#endif
std::string SubutaiLauncher::VirtualBox::cloneName = "subutai";
std::string SubutaiLauncher::VirtualBox::subutaiBranch = "subutai-dev";

SubutaiLauncher::VirtualBox::VirtualBox()
{
    _logger = &Poco::Logger::get("subutai");
    _logger->trace("Starting VirtualBox instance");
    _version = "";
}

SubutaiLauncher::VirtualBox::~VirtualBox()
{
    _logger->trace("VirtualBox::~VirtualBox");
}

std::vector<SubutaiLauncher::SubutaiVM> SubutaiLauncher::VirtualBox::getPeers()
{
	if (_path == "")
	{
		findInstallation();
	}
	_logger->trace("Retrieving list of peers");
    Poco::Process::Args args;
    args.push_back("list");
    args.push_back("vms");

	_logger->trace("Executing %s", _path);
    Poco::Pipe pOut;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &pOut, 0);
    ph.wait();
    Poco::PipeInputStream istr(pOut);
    std::string buffer;
    Poco::StreamCopier::copyToString(istr, buffer);

    std::vector<SubutaiVM> peers = VirtualBox::parseVms(buffer);
    return peers;
}

bool SubutaiLauncher::VirtualBox::findInstallation()
{
    _logger->trace("Searching for VirtualBox installation in PATH");
    std::string path;
#if LAUNCHER_MACOS
    path.append(Environment::EXTRA_PATH);
#endif
#if LAUNCHER_LINUX || LAUNCHER_MACOS
    path.append(Poco::Environment::get("PATH", ""));
    Poco::StringTokenizer st(path, ":",
            Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
#else
	std::vector<std::string> st;
	st.push_back(Poco::Environment::get("programfiles")+"\\Oracle\\VirtualBox");
	st.push_back(Poco::Environment::get("programfiles(x86)") + "\\Oracle\\VirtualBox");
	st.push_back(Poco::Environment::get("ProgramW6432") + "\\Oracle\\VirtualBox");
#endif
    for (auto it = st.begin(); it != st.end(); it++)
    {
		_logger->trace("Searching for %s in %s", BIN, (*it));
        std::string fp = (*it);
        fp.append("/"+BIN);
        Poco::File f(fp);
        if (f.exists())
		{
            _logger->trace("VirtualBox installation found at %s", fp);
            _installed = true;
            _path = (*it);
            _location = _path;
            _path.append(FileSystem::DELIM);
            _path.append(BIN);
            return true;
        }
    }
    _logger->trace("VirtualBox installation was not found");
    return false;
}

bool SubutaiLauncher::VirtualBox::isInstalled()
{
    return _installed;
}

bool SubutaiLauncher::VirtualBox::isRunning()
{
    return true;
}

bool SubutaiLauncher::VirtualBox::isUpdateRequired()
{
    return false;
}

std::string SubutaiLauncher::VirtualBox::getBinaryLocation()
{
    return _path;
}

std::string SubutaiLauncher::VirtualBox::extractVersion()
{
    if (_version != "")
	{
        return _version;
    }

    Poco::Process::Args args;
    args.push_back("-v");

    Poco::Pipe output;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, 0);
    Poco::PipeInputStream istr(output);

    Poco::StreamCopier::copyToString(istr, _version);
    return _version;
}

void SubutaiLauncher::VirtualBox::getVms()
{
    Poco::Process::Args args;
    args.push_back("list");
    args.push_back("vms");

    Poco::Pipe output;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, 0);
    Poco::PipeInputStream istr(output);

    std::string result;
    Poco::StreamCopier::copyToString(istr, result);
}

std::vector<SubutaiLauncher::SubutaiVM> SubutaiLauncher::VirtualBox::parseVms(const std::string& buffer)
{
    char vmname[150];
    char vmid[150];
    int bsize = 256;
    // Parsing output of vboxmanage:
    // "machine_name" {machine_id}
    std::vector<SubutaiVM> vms;
    SubutaiString buf(buffer);
    std::vector<std::string> lines;
    buf.split('\n', lines);
    for (auto it = lines.begin(); it != lines.end(); it++)
	{
        if ((*it).empty() || (*it).length() < 10) continue;
        const char* line = const_cast<char*>((*it).c_str());
#if LAUNCHER_LINUX || LAUNCHER_MACOS
        sscanf(line, "\"%[^\"]\" {%s}", vmname, vmid);
#elif LAUNCHER_WINDOWS
        //sscanf_s(line, "\"%s\" {%s}", vmname, bsize, vmid, bsize);
		sscanf_s(line, "\"%[^\"]\" {%s}", vmname, bsize, vmid, bsize);
#endif
        SubutaiVM v;
        v.name = std::string(vmname);
        v.id = std::string(vmid);
        int i = v.name.find("subutai",0);
        if (i != std::string::npos)
		{
            vms.push_back(v);
        }
    }
    return vms;
}

std::string SubutaiLauncher::VirtualBox::execute(const std::string& command)
{
    if (_path.empty())
    {
        findInstallation();
    }

    _logger->information("VB: Executing '%s %s'", _path, command);
    Poco::Process::Args args;
    Poco::StringTokenizer st(command, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);

    for (auto it = st.begin(); it != st.end(); it++)
    {

		// Replacing +++ with spaces here to fix path issues
		std::string pCommand = Poco::replace((*it), "+++", " ");
		_logger->trace("Adding arguments %s [%s]", (*it), pCommand);
        args.push_back(pCommand);
    }

    Poco::Pipe output;
    Poco::Pipe error;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, &error);
    int status = ph.wait();
    Poco::PipeInputStream iStdout(output);
    Poco::PipeInputStream iStderr(error);

    std::string pStdout;
    std::string pStderr;
    Poco::StreamCopier::copyToString(iStdout, pStdout);
    Poco::StreamCopier::copyToString(iStderr, pStderr);

    _logger->information("Command executed with status %d", status);

    if (!pStderr.empty())
	{
        _logger->error("Error during execution of a command %s: %s", command, pStderr);
    }
    return pStdout;
}

std::string SubutaiLauncher::VirtualBox::execute(const std::string& command, int &exitStatus)
{
    if (_path.empty())
    {
        findInstallation();
    }
    Poco::Process::Args args;
    Poco::StringTokenizer st(command, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
    for (auto it = st.begin(); it != st.end(); it++)
    {
		// Replacing +++ with spaces here to fix path issues
		std::string pCommand = Poco::replace((*it), "+++", " ");
		_logger->trace("Adding arguments %s [%s]", (*it), pCommand);
		args.push_back(pCommand);
    }

    Poco::Pipe output;
    Poco::Pipe error;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, &error);
    int status = ph.wait();
    Poco::PipeInputStream iStdout(output);
    Poco::PipeInputStream iStderr(error);

    std::string pStdout;
    std::string pStderr;
    Poco::StreamCopier::copyToString(iStdout, pStdout);
    Poco::StreamCopier::copyToString(iStderr, pStderr);

    exitStatus = status;
    _logger->information("Command executed with status %d", status);

    if (!pStderr.empty())
	{
        _logger->error("Error during execution of a command %s: %s", command, pStderr);
    }
    return pStdout;
}

std::string SubutaiLauncher::VirtualBox::getBridgedInterface(const std::string& iface)
{
    //auto out = this->execute("list bridgedifs");
    return iface;
}

std::string SubutaiLauncher::VirtualBox::getMachineInfo(const std::string& name)
{
    Poco::Process::Args args;
    args.push_back("showvminfo");
    args.push_back(name);

    Poco::Pipe pOut;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &pOut, 0);
    ph.wait();
    Poco::PipeInputStream istr(pOut);
    std::string buffer;
    Poco::StreamCopier::copyToString(istr, buffer);
    return buffer;
}

bool SubutaiLauncher::VirtualBox::isMachineExists(const std::string& name)
{
	_logger->trace("Checking %s VM exists", name);
    auto list = getPeers();
    for (auto it = list.begin(); it != list.end(); it++)
	{
        if ((*it).name == name)
		{
			_logger->trace("%s exists", name);
            return true;
        }
    }
	_logger->trace("%s does not exists", name);
    return false;
}

bool SubutaiLauncher::VirtualBox::isMachineRunning(const std::string& name)
{
	_logger->trace("Checking %s VM running", name);
    auto list = getPeers();
    for (auto it = list.begin(); it != list.end(); it++)
	{
		_logger->trace("Checking machine %s", (*it).name);
        if ((*it).name == name)
		{
			_logger->trace("Machine %s has been found", name);
            auto info = getMachineInfo(name);
            Poco::StringTokenizer lines(info, "\n", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
            for (auto line = lines.begin(); line != lines.end(); line++)
			{
                if ((*line).substr(0, 6) == "State:")
				{
                    auto p = (*line).find("running", 0);
                    if (p != std::string::npos)
					{
						_logger->trace("Machine %s is running", name);
                        return true;
                    }
					_logger->trace("Machine %s is stopped", name);
                    return false;
                }
            }
        }
    }
	_logger->error("Machine %s has not been found", name);
    return false;
}

std::string SubutaiLauncher::VirtualBox::getHostOnlyAdapter()
{
	_logger->debug("Looking for Host-Only adapter");
	std::string result = execute("list hostonlyifs");

	Poco::StringTokenizer st(result, "\n", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
	for (auto it = st.begin(); it != st.end(); it++)
	{
		if ((*it).substr(0, 4) == "Name")
		{
			return Poco::trim((*it).substr(5, (*it).length()));
		}
	}

	return "undefined";
}
<commit_msg>VirtualBox fixes<commit_after>#include "VirtualBox.h"

#if LAUNCHER_LINUX
const std::string SubutaiLauncher::VirtualBox::BIN = "vboxmanage";
#elif LAUNCHER_MACOS
const std::string SubutaiLauncher::VirtualBox::BIN = "VBoxManage";
#else
const std::string SubutaiLauncher::VirtualBox::BIN = "VBoxManage.exe";
#endif
std::string SubutaiLauncher::VirtualBox::cloneName = "subutai";
std::string SubutaiLauncher::VirtualBox::subutaiBranch = "subutai-dev";

SubutaiLauncher::VirtualBox::VirtualBox()
{
    _logger = &Poco::Logger::get("subutai");
    _logger->trace("Starting VirtualBox instance");
    _version = "";
}

SubutaiLauncher::VirtualBox::~VirtualBox()
{
    _logger->trace("VirtualBox::~VirtualBox");
}

std::vector<SubutaiLauncher::SubutaiVM> SubutaiLauncher::VirtualBox::getPeers()
{
	if (_path == "")
	{
		findInstallation();
	}
	_logger->trace("Retrieving list of peers");
    Poco::Process::Args args;
    args.push_back("list");
    args.push_back("vms");

	_logger->trace("Executing %s", _path);
    Poco::Pipe pOut;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &pOut, 0);
    ph.wait();
    Poco::PipeInputStream istr(pOut);
    std::string buffer;
    Poco::StreamCopier::copyToString(istr, buffer);

    std::vector<SubutaiVM> peers = VirtualBox::parseVms(buffer);
    return peers;
}

bool SubutaiLauncher::VirtualBox::findInstallation()
{
    _logger->trace("Searching for VirtualBox installation in PATH");
    std::string path;
#if LAUNCHER_MACOS
    path.append(Environment::EXTRA_PATH);
#endif
#if LAUNCHER_LINUX || LAUNCHER_MACOS
    path.append(Poco::Environment::get("PATH", ""));
    Poco::StringTokenizer st(path, ":",
            Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
#else
	std::vector<std::string> st;
	st.push_back(Poco::Environment::get("programfiles")+"\\Oracle\\VirtualBox");
	st.push_back(Poco::Environment::get("programfiles(x86)") + "\\Oracle\\VirtualBox");
	st.push_back(Poco::Environment::get("ProgramW6432") + "\\Oracle\\VirtualBox");
#endif
    for (auto it = st.begin(); it != st.end(); it++)
    {
		_logger->trace("Searching for %s in %s", BIN, (*it));
        std::string fp = (*it);
        fp.append("/"+BIN);
        Poco::File f(fp);
        if (f.exists())
		{
            _logger->trace("VirtualBox installation found at %s", fp);
            _installed = true;
            _path = (*it);
            _location = _path;
            _path.append(FileSystem::DELIM);
            _path.append(BIN);
            return true;
        }
    }
    _logger->trace("VirtualBox installation was not found");
    return false;
}

bool SubutaiLauncher::VirtualBox::isInstalled()
{
    return _installed;
}

bool SubutaiLauncher::VirtualBox::isRunning()
{
    return true;
}

bool SubutaiLauncher::VirtualBox::isUpdateRequired()
{
    return false;
}

std::string SubutaiLauncher::VirtualBox::getBinaryLocation()
{
    return _path;
}

std::string SubutaiLauncher::VirtualBox::extractVersion()
{
    if (_version != "")
	{
        return _version;
    }

    Poco::Process::Args args;
    args.push_back("-v");

    Poco::Pipe output;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, 0);
    Poco::PipeInputStream istr(output);

    Poco::StreamCopier::copyToString(istr, _version);
    return _version;
}

void SubutaiLauncher::VirtualBox::getVms()
{
    Poco::Process::Args args;
    args.push_back("list");
    args.push_back("vms");

    Poco::Pipe output;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, 0);
    Poco::PipeInputStream istr(output);

    std::string result;
    Poco::StreamCopier::copyToString(istr, result);
}

std::vector<SubutaiLauncher::SubutaiVM> SubutaiLauncher::VirtualBox::parseVms(const std::string& buffer)
{
    char vmname[150];
    char vmid[150];
    int bsize = 256;
    // Parsing output of vboxmanage:
    // "machine_name" {machine_id}
    std::vector<SubutaiVM> vms;
    SubutaiString buf(buffer);
    std::vector<std::string> lines;
    buf.split('\n', lines);
    for (auto it = lines.begin(); it != lines.end(); it++)
	{
        if ((*it).empty() || (*it).length() < 10) continue;
        const char* line = const_cast<char*>((*it).c_str());
#if LAUNCHER_LINUX || LAUNCHER_MACOS
        sscanf(line, "\"%[^\"]\" {%s}", vmname, vmid);
#elif LAUNCHER_WINDOWS
		sscanf_s(line, "\"%[^\"]\" {%s}", vmname, bsize, vmid, bsize);
#endif
        SubutaiVM v;
        v.name = std::string(vmname);
        v.id = std::string(vmid);
        int i = v.name.find("subutai",0);
        if (i != std::string::npos)
		{
            vms.push_back(v);
        }
    }
    return vms;
}

std::string SubutaiLauncher::VirtualBox::execute(const std::string& command)
{
    if (_path.empty())
    {
        findInstallation();
    }

    _logger->information("VB: Executing '%s %s'", _path, command);
    Poco::Process::Args args;
    Poco::StringTokenizer st(command, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);

    for (auto it = st.begin(); it != st.end(); it++)
    {

		// Replacing +++ with spaces here to fix path issues
		std::string pCommand = Poco::replace((*it), "+++", " ");
		_logger->trace("Adding arguments %s [%s]", (*it), pCommand);
        args.push_back(pCommand);
    }

    Poco::Pipe output;
    Poco::Pipe error;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, &error);
    int status = ph.wait();
    Poco::PipeInputStream iStdout(output);
    Poco::PipeInputStream iStderr(error);

    std::string pStdout;
    std::string pStderr;
    Poco::StreamCopier::copyToString(iStdout, pStdout);
    Poco::StreamCopier::copyToString(iStderr, pStderr);

    _logger->information("Command executed with status %d", status);

    if (!pStderr.empty())
	{
        _logger->error("Error during execution of a command %s: %s", command, pStderr);
    }
    return pStdout;
}

std::string SubutaiLauncher::VirtualBox::execute(const std::string& command, int &exitStatus)
{
    if (_path.empty())
    {
        findInstallation();
    }
    Poco::Process::Args args;
    Poco::StringTokenizer st(command, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
    for (auto it = st.begin(); it != st.end(); it++)
    {
		// Replacing +++ with spaces here to fix path issues
		std::string pCommand = Poco::replace((*it), "+++", " ");
		_logger->trace("Adding arguments %s [%s]", (*it), pCommand);
		args.push_back(pCommand);
    }

    Poco::Pipe output;
    Poco::Pipe error;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &output, &error);
    int status = ph.wait();
    Poco::PipeInputStream iStdout(output);
    Poco::PipeInputStream iStderr(error);

    std::string pStdout;
    std::string pStderr;
    Poco::StreamCopier::copyToString(iStdout, pStdout);
    Poco::StreamCopier::copyToString(iStderr, pStderr);

    exitStatus = status;
    _logger->information("Command executed with status %d", status);

    if (!pStderr.empty())
	{
        _logger->error("Error during execution of a command %s: %s", command, pStderr);
    }
    return pStdout;
}

std::string SubutaiLauncher::VirtualBox::getBridgedInterface(const std::string& iface)
{
    //auto out = this->execute("list bridgedifs");
    return iface;
}

std::string SubutaiLauncher::VirtualBox::getMachineInfo(const std::string& name)
{
    Poco::Process::Args args;
    args.push_back("showvminfo");
    args.push_back(name);

    Poco::Pipe pOut;
    Poco::ProcessHandle ph = Poco::Process::launch(_path, args, 0, &pOut, 0);
    ph.wait();
    Poco::PipeInputStream istr(pOut);
    std::string buffer;
    Poco::StreamCopier::copyToString(istr, buffer);
    return buffer;
}

bool SubutaiLauncher::VirtualBox::isMachineExists(const std::string& name)
{
	_logger->trace("Checking %s VM exists", name);
    auto list = getPeers();
    for (auto it = list.begin(); it != list.end(); it++)
	{
        if ((*it).name == name)
		{
			_logger->trace("%s exists", name);
            return true;
        }
    }
	_logger->trace("%s does not exists", name);
    return false;
}

bool SubutaiLauncher::VirtualBox::isMachineRunning(const std::string& name)
{
	_logger->trace("Checking %s VM running", name);
    auto list = getPeers();
    for (auto it = list.begin(); it != list.end(); it++)
	{
		_logger->trace("Checking machine %s", (*it).name);
        if ((*it).name == name)
		{
			_logger->trace("Machine %s has been found", name);
            auto info = getMachineInfo(name);
            Poco::StringTokenizer lines(info, "\n", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
            for (auto line = lines.begin(); line != lines.end(); line++)
			{
                if ((*line).substr(0, 6) == "State:")
				{
                    auto p = (*line).find("running", 0);
                    if (p != std::string::npos)
					{
						_logger->trace("Machine %s is running", name);
                        return true;
                    }
					_logger->trace("Machine %s is stopped", name);
                    return false;
                }
            }
        }
    }
	_logger->error("Machine %s has not been found", name);
    return false;
}

std::string SubutaiLauncher::VirtualBox::getHostOnlyAdapter()
{
	_logger->debug("Looking for Host-Only adapter");
	std::string result = execute("list hostonlyifs");

	Poco::StringTokenizer st(result, "\n", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
	for (auto it = st.begin(); it != st.end(); it++)
	{
		if ((*it).substr(0, 4) == "Name")
		{
			return Poco::trim((*it).substr(5, (*it).length()));
		}
	}

	return "undefined";
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <QRegularExpression>
#include <QTextStream>
#include <QDirIterator>
#include "WineBottle.h"

#include "Local.h"  // error reporting

DEFINE_SINGLETON_SCOPE( WineBottle )

namespace {
/*
const int REG_NONE = 0;                 // no type
const int REG_SZ = 1;                 	// string type (ASCII)
const int REG_EXPAND_SZ = 2;	          // string, includes %ENVVAR% (expanded by caller) (ASCII)
const int REG_BINARY = 3;	              // binary format, callerspecific
// YES, REG_DWORD == REG_DWORD_LITTLE_ENDIAN
const int REG_DWORD = 4;	              // DWORD in little endian format
const int REG_DWORD_LITTLE_ENDIAN = 4;	// DWORD in little endian format
const int REG_DWORD_BIG_ENDIAN = 5;	    // DWORD in big endian format
const int REG_LINK = 6;	                // symbolic link (UNICODE)
const int REG_MULTI_SZ = 7;	            // multiple strings, delimited by \0, terminated by \0\0 (ASCII)
const int REG_RESOURCE_LIST = 8;	      // resource list? huh?
const int REG_FULL_RESOURCE_DESCRIPTOR= 9;	// full resource descriptor? huh?
const int REG_RESOURCE_REQUIREMENTS_LIST = 10;
const int REG_QWORD = 11;	              // QWORD in little endian format
const int REG_QWORD_LITTLE_ENDIAN =	11;	// QWORD in little endian format
*/
const int MAX_REG_FILES = 3;
const QString SYSTEM_REG( "system.reg" );
//const int SYSTEM_REG_ID = 0;
const QString USER_REG( "user.reg" );
//const int USER_REG_ID = 1;
const QString USERDEF_REG( "userdef.reg" );
//const int USERDEF_REG_ID = 2;

const QString REG_KEY_LOCAL_APPDATA( "HKEY_USERS/.Default/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/Local AppData" );
const QString REG_KEY_HS_INSTALL_LOCATION( "HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/Uninstall/Hearthstone/InstallLocation" );
const QString REG_KEY_APPDATA( "HKEY_USERS/.Default/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/AppData" );

const QString DOSDEVICES( "dosdevices" );
const QString DRIVE_C( "drive_c" );

}

WineBottle::WineBottle()
  : mRegFiles ( QVector< QPointer< QSettings >>( ( QPointer< QSettings >( nullptr ), 3 ) ) ),
    mRegFormat( QSettings::registerFormat( "reg", ReadWineRegFile, WriteWineRegFile ) ) {
}

WineBottle::~WineBottle() {
  mRegFiles.clear();
}

void WineBottle::SetPath( const QString& path )
{
  mWinePrefix = path;
  //is valid?
  QStringList difference;
  const QStringList defaultContent = { DOSDEVICES, DRIVE_C, SYSTEM_REG, USER_REG, USERDEF_REG };
  QStringList dirContent = QDir( mWinePrefix ).entryList( QDir::NoDotAndDotDot |
                                                          QDir::Files | QDir::Dirs,
                                                          QDir::Name );
  // no need to sort since defaultContent and dirContent are alrady sorted
  std::set_difference( defaultContent.begin(), defaultContent.end(),
                       dirContent.begin(), dirContent.end(),
                       std::inserter( difference, difference.begin() ) );

  if ( ( mIsValid = difference.size() ? false : true ) ) {
    SetupDosDevices();
    SetupRegFiles();
  }
  else {
    ERR( "Directory: %s is not a valid wine prefix!", qt2cstr( path ) );
  }

}

const QString& WineBottle::Path() const {
  return mWinePrefix;
}

QString WineBottle::DosDevicePath( const QChar& drive ) const {
  return mDosDevices[ drive.toLower() ];
}

bool WineBottle::IsValid() const {
  return mIsValid;
}

bool WineBottle::WriteWineRegFile( QIODevice& device, const QSettings::SettingsMap& map )
{
  Q_UNUSED( device );
  Q_UNUSED( map );
  Q_ASSERT_X( true, "WineBottle::WriteWineRegFile",
              "Dummy metod: this method is not implemented!" );
  return false;
}

void WineBottle::SetupRegFiles() {
  const QString reg_files[ MAX_REG_FILES ] = { SYSTEM_REG, USER_REG, USERDEF_REG };
  for( auto id = 0; id < MAX_REG_FILES; ++id ) {
    if ( !mRegFiles.at( id ) ) {
      mRegFiles[ id ] = new QSettings( mWinePrefix + "/" + reg_files[ id ], mRegFormat );
    }
  }
}

QVariant WineBottle::ReadRegistryValue( const QString& key ) const {
  const QString reg_files[ MAX_REG_FILES ] = { SYSTEM_REG, USER_REG, USERDEF_REG };
  for( auto id = 0; id < MAX_REG_FILES; ++id ) {
    if ( mRegFiles[ id ]->value( key ).isValid() ) {
      return mRegFiles[ id ]->value( key );
    }
  }
  return QVariant();
}

QString WineBottle::LocalAppData() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_LOCAL_APPDATA ).toString() );
}

QString WineBottle::HearthstoneInstallLocation() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_HS_INSTALL_LOCATION ).toString() );
}

QString WineBottle::AppData() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_APPDATA ).toString() );
}

QString WineBottle::ToSystemPath( const QString& windowsStylePath ) const {
  QRegularExpression driveReg( "^[a-zA-Z]:");

  if( driveReg.match( windowsStylePath ).hasMatch() ) {
    QString path = windowsStylePath;
    path.replace( "\\\\", QDir::separator() );
    return path.replace( driveReg, DosDevicePath( windowsStylePath.at( 0 ) ) );
  }
  return QString();
}

void WineBottle::SetupDosDevices() {
  QRegularExpression driveReg( "^[a-zA-Z]:$" );
  QDirIterator dirItr( mWinePrefix + "/" +  DOSDEVICES, QDirIterator::FollowSymlinks	);

  while( dirItr.hasNext() ) {
    dirItr.next();
    if ( driveReg.match( dirItr.fileName() ).hasMatch() &&
         dirItr.fileInfo().exists() ) {
      mDosDevices.insert( dirItr.fileName().at( 0 ), dirItr.fileInfo().symLinkTarget() );
    }
  }
}

bool WineBottle::ReadWineRegFile( QIODevice& device, QSettings::SettingsMap& map ) {
  if ( !device.isOpen() )
    return false;

  device.setTextModeEnabled( true );
  QTextStream inputStream( &device );

  // first line should be: WINE REGISTRY Version 2
  if ( inputStream.readLine() != "WINE REGISTRY Version 2" ) {
    return false;
  }
  // determine what prefix needs to be prepend

  QString line = inputStream.readLine();
  QString defaultPrefix, prefix;

  if ( line == ";; All keys relative to \\\\Machine" ) {
    // system.reg
    // ;; All keys relative to \\Machine
    defaultPrefix = "HKEY_LOCAL_MACHINE/";
  }
  else if ( line == ";; All keys relative to \\\\User\\\\.Default") {
    // userdef.reg
    defaultPrefix = "HKEY_USERS/.Default/";
  }
  else if ( line.left( 32 ) == ";; All keys relative to \\\\User\\\\") {
    // has to be user.reg since we cought userdef earlier
    // ;; All keys relative to \\User\\x-x-x-x-x-x-x-xxxx
    // resides in two places:
    // HKEY_CURRENT_USER/ and HKEY_USERS/User/x-x-x-x-x-x-x-xxxx
    // howerever we need only current user one
    defaultPrefix = "HKEY_CURRENT_USER/";
  }
  else {
    ERR( "%s: Couldn't determin prefix ", Q_FUNC_INFO );
    return false;
  }

  // now we fill SettingsMap
  QString key, valueString;
  QStringList explode;
  QVariant value;

  while ( !inputStream.atEnd() ) {
    line = inputStream.readLine();

    if ( line.startsWith( '#' ) ) {} 				/* skip comment */
    /* empty line means that next section is starting so prefix has to be cleared */
    else if ( line.startsWith( '\n' ) ) {
      prefix.clear();
    }
    else if ( line.startsWith( '[' ) ) { 		/* found new section */
      prefix = ( line.mid( 1, line.indexOf( "]" ) - 1 ) ).
               replace( "\\\\", QDir::separator() );
      prefix.prepend( defaultPrefix );
    }
    /* found key */
    //else if ( line.startsWith( '\' ) || line.startsWith( '@' ) )
    else if ( line.startsWith( '"' ) ) {
      explode = line.split( '=' );

      key =  explode[ 0 ].replace( "\"" , "" );
      // cannot replace " cuz we dont know the type
      valueString = explode[ 1 ];

      // REG_SZ
      if ( valueString.startsWith( '"' ) && valueString.endsWith( '"' )) {
        value.setValue( valueString.mid( 1, valueString.length() - 2 ));
      }
      // REG_DWORD
      else if ( valueString.startsWith( "dword:" )) {
        unsigned long dword = valueString.mid( 6 ).toULong();
        value.setValue( dword );
      }
      // REG_BINARY do i even need this?
      // TODO: finish this someday
      //			else if ( valueString.startsWith( "hex:")) {
      //			}
      //			// REG_MULTI_SZ */
      //			else if ( valueString.startsWith( "str(" ) ) {
      //			}
      //
      /* insert key and value to map */
      map [ prefix + "/" + key ] = value;
      key.clear();
      value.clear();
    } else {
      // unhandeled stuff we dont need
    }
  }
  return true;
}
<commit_msg>Fixes #94: Setup devices and regfiles even if prefix isn't valid.<commit_after>#include <algorithm>
#include <QRegularExpression>
#include <QTextStream>
#include <QDirIterator>
#include "WineBottle.h"

#include "Local.h"  // error reporting

DEFINE_SINGLETON_SCOPE( WineBottle )

namespace {
/*
const int REG_NONE = 0;                 // no type
const int REG_SZ = 1;                 	// string type (ASCII)
const int REG_EXPAND_SZ = 2;	          // string, includes %ENVVAR% (expanded by caller) (ASCII)
const int REG_BINARY = 3;	              // binary format, callerspecific
// YES, REG_DWORD == REG_DWORD_LITTLE_ENDIAN
const int REG_DWORD = 4;	              // DWORD in little endian format
const int REG_DWORD_LITTLE_ENDIAN = 4;	// DWORD in little endian format
const int REG_DWORD_BIG_ENDIAN = 5;	    // DWORD in big endian format
const int REG_LINK = 6;	                // symbolic link (UNICODE)
const int REG_MULTI_SZ = 7;	            // multiple strings, delimited by \0, terminated by \0\0 (ASCII)
const int REG_RESOURCE_LIST = 8;	      // resource list? huh?
const int REG_FULL_RESOURCE_DESCRIPTOR= 9;	// full resource descriptor? huh?
const int REG_RESOURCE_REQUIREMENTS_LIST = 10;
const int REG_QWORD = 11;	              // QWORD in little endian format
const int REG_QWORD_LITTLE_ENDIAN =	11;	// QWORD in little endian format
*/
const int MAX_REG_FILES = 3;
const QString SYSTEM_REG( "system.reg" );
//const int SYSTEM_REG_ID = 0;
const QString USER_REG( "user.reg" );
//const int USER_REG_ID = 1;
const QString USERDEF_REG( "userdef.reg" );
//const int USERDEF_REG_ID = 2;

const QString REG_KEY_LOCAL_APPDATA( "HKEY_USERS/.Default/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/Local AppData" );
const QString REG_KEY_HS_INSTALL_LOCATION( "HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/Uninstall/Hearthstone/InstallLocation" );
const QString REG_KEY_APPDATA( "HKEY_USERS/.Default/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/AppData" );

const QString DOSDEVICES( "dosdevices" );
const QString DRIVE_C( "drive_c" );

}

WineBottle::WineBottle()
  : mRegFiles ( QVector< QPointer< QSettings >>( ( QPointer< QSettings >( nullptr ), 3 ) ) ),
    mRegFormat( QSettings::registerFormat( "reg", ReadWineRegFile, WriteWineRegFile ) ) {
}

WineBottle::~WineBottle() {
  mRegFiles.clear();
}

void WineBottle::SetPath( const QString& path )
{
  mWinePrefix = path;
  //is valid?
  QStringList difference;
  const QStringList defaultContent = { DOSDEVICES, DRIVE_C, SYSTEM_REG, USER_REG, USERDEF_REG };
  QStringList dirContent = QDir( mWinePrefix ).entryList( QDir::NoDotAndDotDot |
                                                          QDir::Files | QDir::Dirs,
                                                          QDir::Name );
  // no need to sort since defaultContent and dirContent are alrady sorted
  std::set_difference( defaultContent.begin(), defaultContent.end(),
                       dirContent.begin(), dirContent.end(),
                       std::inserter( difference, difference.begin() ) );

  if ( !( mIsValid = difference.size() ? false : true ) ) {
    ERR( "Directory: %s is not a valid wine prefix!", qt2cstr( path ) );
  }
  SetupDosDevices();
  SetupRegFiles();
}

const QString& WineBottle::Path() const {
  return mWinePrefix;
}

QString WineBottle::DosDevicePath( const QChar& drive ) const {
  return mDosDevices[ drive.toLower() ];
}

bool WineBottle::IsValid() const {
  return mIsValid;
}

bool WineBottle::WriteWineRegFile( QIODevice& device, const QSettings::SettingsMap& map )
{
  Q_UNUSED( device );
  Q_UNUSED( map );
  Q_ASSERT_X( true, "WineBottle::WriteWineRegFile",
              "Dummy metod: this method is not implemented!" );
  return false;
}

void WineBottle::SetupRegFiles() {
  const QString reg_files[ MAX_REG_FILES ] = { SYSTEM_REG, USER_REG, USERDEF_REG };
  for( auto id = 0; id < MAX_REG_FILES; ++id ) {
    if ( !mRegFiles.at( id ) ) {
      mRegFiles[ id ] = new QSettings( mWinePrefix + "/" + reg_files[ id ], mRegFormat );
    }
  }
}

QVariant WineBottle::ReadRegistryValue( const QString& key ) const {
  const QString reg_files[ MAX_REG_FILES ] = { SYSTEM_REG, USER_REG, USERDEF_REG };
  for( auto id = 0; id < MAX_REG_FILES; ++id ) {
    if ( mRegFiles[ id ]->value( key ).isValid() ) {
      return mRegFiles[ id ]->value( key );
    }
  }
  return QVariant();
}

QString WineBottle::LocalAppData() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_LOCAL_APPDATA ).toString() );
}

QString WineBottle::HearthstoneInstallLocation() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_HS_INSTALL_LOCATION ).toString() );
}

QString WineBottle::AppData() const {
  return ToSystemPath( ReadRegistryValue( REG_KEY_APPDATA ).toString() );
}

QString WineBottle::ToSystemPath( const QString& windowsStylePath ) const {
  QRegularExpression driveReg( "^[a-zA-Z]:");

  if( driveReg.match( windowsStylePath ).hasMatch() ) {
    QString path = windowsStylePath;
    path.replace( "\\\\", QDir::separator() );
    return path.replace( driveReg, DosDevicePath( windowsStylePath.at( 0 ) ) );
  }
  return QString();
}

void WineBottle::SetupDosDevices() {
  QRegularExpression driveReg( "^[a-zA-Z]:$" );
  QDirIterator dirItr( mWinePrefix + "/" +  DOSDEVICES, QDirIterator::FollowSymlinks	);

  while( dirItr.hasNext() ) {
    dirItr.next();
    if ( driveReg.match( dirItr.fileName() ).hasMatch() &&
         dirItr.fileInfo().exists() ) {
      mDosDevices.insert( dirItr.fileName().at( 0 ), dirItr.fileInfo().symLinkTarget() );
    }
  }
}

bool WineBottle::ReadWineRegFile( QIODevice& device, QSettings::SettingsMap& map ) {
  if ( !device.isOpen() )
    return false;

  device.setTextModeEnabled( true );
  QTextStream inputStream( &device );

  // first line should be: WINE REGISTRY Version 2
  if ( inputStream.readLine() != "WINE REGISTRY Version 2" ) {
    return false;
  }
  // determine what prefix needs to be prepend

  QString line = inputStream.readLine();
  QString defaultPrefix, prefix;

  if ( line == ";; All keys relative to \\\\Machine" ) {
    // system.reg
    // ;; All keys relative to \\Machine
    defaultPrefix = "HKEY_LOCAL_MACHINE/";
  }
  else if ( line == ";; All keys relative to \\\\User\\\\.Default") {
    // userdef.reg
    defaultPrefix = "HKEY_USERS/.Default/";
  }
  else if ( line.left( 32 ) == ";; All keys relative to \\\\User\\\\") {
    // has to be user.reg since we cought userdef earlier
    // ;; All keys relative to \\User\\x-x-x-x-x-x-x-xxxx
    // resides in two places:
    // HKEY_CURRENT_USER/ and HKEY_USERS/User/x-x-x-x-x-x-x-xxxx
    // howerever we need only current user one
    defaultPrefix = "HKEY_CURRENT_USER/";
  }
  else {
    ERR( "%s: Couldn't determin prefix ", Q_FUNC_INFO );
    return false;
  }

  // now we fill SettingsMap
  QString key, valueString;
  QStringList explode;
  QVariant value;

  while ( !inputStream.atEnd() ) {
    line = inputStream.readLine();

    if ( line.startsWith( '#' ) ) {} 				/* skip comment */
    /* empty line means that next section is starting so prefix has to be cleared */
    else if ( line.startsWith( '\n' ) ) {
      prefix.clear();
    }
    else if ( line.startsWith( '[' ) ) { 		/* found new section */
      prefix = ( line.mid( 1, line.indexOf( "]" ) - 1 ) ).
               replace( "\\\\", QDir::separator() );
      prefix.prepend( defaultPrefix );
    }
    /* found key */
    //else if ( line.startsWith( '\' ) || line.startsWith( '@' ) )
    else if ( line.startsWith( '"' ) ) {
      explode = line.split( '=' );

      key =  explode[ 0 ].replace( "\"" , "" );
      // cannot replace " cuz we dont know the type
      valueString = explode[ 1 ];

      // REG_SZ
      if ( valueString.startsWith( '"' ) && valueString.endsWith( '"' )) {
        value.setValue( valueString.mid( 1, valueString.length() - 2 ));
      }
      // REG_DWORD
      else if ( valueString.startsWith( "dword:" )) {
        unsigned long dword = valueString.mid( 6 ).toULong();
        value.setValue( dword );
      }
      // REG_BINARY do i even need this?
      // TODO: finish this someday
      //			else if ( valueString.startsWith( "hex:")) {
      //			}
      //			// REG_MULTI_SZ */
      //			else if ( valueString.startsWith( "str(" ) ) {
      //			}
      //
      /* insert key and value to map */
      map [ prefix + "/" + key ] = value;
      key.clear();
      value.clear();
    } else {
      // unhandeled stuff we dont need
    }
  }
  return true;
}
<|endoftext|>


<commit_before>#include "SpriteFactory.h"
#include "FileHelper.h"
#include "SymbolFile.h"
#include "ImageSprite.h"
#include "ImageSymbol.h"
#include "FontBlankSprite.h"
#include "FontBlankSymbol.h"
#include "ScriptsSprite.h"
#include "ScriptsSymbol.h"
#include "StringHelper.h"
#include "SymbolSearcher.h"
#include "SymbolMgr.h"
#include "Exception.h"
#include "SymbolType.h"

#include <sprite2/SymType.h>
#include <gum/SymbolFile.h>
#include <gum/FilepathHelper.h>

namespace ee
{

SpriteFactory* SpriteFactory::m_instance = NULL;

SpriteFactory::CallbackMap SpriteFactory::m_creators;

SpriteFactory::SpriteFactory()
	: m_id(0)
{
}

Sprite* SpriteFactory::Create(Symbol* sym)
{
	if (!sym) {
		return NULL;
	}

	Sprite* spr = NULL;
	int type = sym->Type();
	switch (type)
	{
	case s2::SYM_IMAGE:
		spr = new ImageSprite(dynamic_cast<ImageSymbol*>(sym));
		break;
	case SYM_FONTBLANK:
		spr = new FontBlankSprite(dynamic_cast<FontBlankSymbol*>(sym));
		break;
	case SYM_SCRIPTS:
		spr = new ScriptsSprite(dynamic_cast<ScriptsSymbol*>(sym));
		break;
	default:
		{
			CallbackMap::iterator itr = m_creators.find(type);
			if (itr != m_creators.end()) {
				spr = (itr->second)(sym);
			}
		}
	}

	if (spr) {
		Insert(spr);
		spr->SetName(NextName());
	}

	if (!sym->tag.empty()) {
		spr->SetTag("[symbol]");
	}

	return spr;
}

Sprite* SpriteFactory::Create(const Json::Value& val, const std::string& dir)
{
	std::string filepath = SymbolSearcher::GetSymbolPath(dir, val);
	return Create(val, dir, filepath);
}

Sprite* SpriteFactory::Create(const Json::Value& val, const std::string& dir,
							  const std::string& filepath)
{
	if (!gum::FilepathHelper::Exists(filepath)) {
		throw Exception("error filepath: %s", filepath.c_str());
	}

	ee::Symbol* sym = NULL;
	try {
		sym = ee::SymbolMgr::Instance()->FetchSymbol(filepath);
	} catch (ee::Exception& e) {
		if (val.isMember(gum::SymbolFile::Instance()->Tag(s2::SYM_SKELETON))) {
			sym = ee::SymbolMgr::Instance()->FetchSymbol(filepath, s2::SYM_SKELETON);
		} else {
			throw e;
		}
	}

	SymbolSearcher::SetSymbolFilepaths(dir, sym, val);
	Sprite* spr = SpriteFactory::Instance()->Create(sym);
	spr->Load(val, dir);
	spr->SetVisible(true);
	sym->RemoveReference();
	return spr;
}

void SpriteFactory::Insert(Sprite* spr)
{
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(dynamic_cast<const Symbol*>(spr->GetSymbol()));
	if (itr == m_map_symbol2sprites.end())
	{
		SpriteList list;
		list.push_back(spr);
		m_map_symbol2sprites.insert(std::make_pair(dynamic_cast<const Symbol*>(spr->GetSymbol()), list));
	}
	else
	{
		itr->second.push_back(spr);
	}
}

void SpriteFactory::Remove(Sprite* spr)
{
	const Symbol* sym = dynamic_cast<const Symbol*>(spr->GetSymbol());
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(sym);
	if (itr != m_map_symbol2sprites.end())
	{
		SpriteList::iterator itr_sprite = itr->second.begin();
		for ( ; itr_sprite != itr->second.end(); )
		{
			if (*itr_sprite == spr)
				itr_sprite = itr->second.erase(itr_sprite);
			else
				++itr_sprite;
		}
	}
}

std::string SpriteFactory::NextName() const
{
	return "_sprite" + StringHelper::ToString(m_id++);
}

void SpriteFactory::UpdateBoundings(const Symbol& sym)
{
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(&sym);
	if (itr != m_map_symbol2sprites.end())
	{
		for (size_t i = 0, n = itr->second.size(); i < n; ++i)
			itr->second[i]->UpdateBounding();
	}
}

void SpriteFactory::RegisterCreator(int type, CreateCallback cb)
{
	m_creators.insert(std::make_pair(type, cb));
}

void SpriteFactory::UnregisterCreator(int type)
{
	m_creators.erase(type);
}

SpriteFactory* SpriteFactory::Instance()
{
	if (!m_instance)
	{
		m_instance = new SpriteFactory();
	}
	return m_instance;
}
}<commit_msg>Revert "[ADDED] spr factory check filepath exists"<commit_after>#include "SpriteFactory.h"
#include "FileHelper.h"
#include "SymbolFile.h"
#include "ImageSprite.h"
#include "ImageSymbol.h"
#include "FontBlankSprite.h"
#include "FontBlankSymbol.h"
#include "ScriptsSprite.h"
#include "ScriptsSymbol.h"
#include "StringHelper.h"
#include "SymbolSearcher.h"
#include "SymbolMgr.h"
#include "Exception.h"
#include "SymbolType.h"

#include <sprite2/SymType.h>
#include <gum/SymbolFile.h>

namespace ee
{

SpriteFactory* SpriteFactory::m_instance = NULL;

SpriteFactory::CallbackMap SpriteFactory::m_creators;

SpriteFactory::SpriteFactory()
	: m_id(0)
{
}

Sprite* SpriteFactory::Create(Symbol* sym)
{
	if (!sym) {
		return NULL;
	}

	Sprite* spr = NULL;
	int type = sym->Type();
	switch (type)
	{
	case s2::SYM_IMAGE:
		spr = new ImageSprite(dynamic_cast<ImageSymbol*>(sym));
		break;
	case SYM_FONTBLANK:
		spr = new FontBlankSprite(dynamic_cast<FontBlankSymbol*>(sym));
		break;
	case SYM_SCRIPTS:
		spr = new ScriptsSprite(dynamic_cast<ScriptsSymbol*>(sym));
		break;
	default:
		{
			CallbackMap::iterator itr = m_creators.find(type);
			if (itr != m_creators.end()) {
				spr = (itr->second)(sym);
			}
		}
	}

	if (spr) {
		Insert(spr);
		spr->SetName(NextName());
	}

	if (!sym->tag.empty()) {
		spr->SetTag("[symbol]");
	}

	return spr;
}

Sprite* SpriteFactory::Create(const Json::Value& val, const std::string& dir)
{
	std::string filepath = SymbolSearcher::GetSymbolPath(dir, val);
	return Create(val, dir, filepath);
}

Sprite* SpriteFactory::Create(const Json::Value& val, const std::string& dir,
							  const std::string& filepath)
{
	ee::Symbol* sym = NULL;
	try {
		sym = ee::SymbolMgr::Instance()->FetchSymbol(filepath);
	} catch (ee::Exception& e) {
		if (val.isMember(gum::SymbolFile::Instance()->Tag(s2::SYM_SKELETON))) {
			sym = ee::SymbolMgr::Instance()->FetchSymbol(filepath, s2::SYM_SKELETON);
		} else {
			throw e;
		}
	}

	SymbolSearcher::SetSymbolFilepaths(dir, sym, val);
	Sprite* spr = SpriteFactory::Instance()->Create(sym);
	spr->Load(val, dir);
	spr->SetVisible(true);
	sym->RemoveReference();
	return spr;
}

void SpriteFactory::Insert(Sprite* spr)
{
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(dynamic_cast<const Symbol*>(spr->GetSymbol()));
	if (itr == m_map_symbol2sprites.end())
	{
		SpriteList list;
		list.push_back(spr);
		m_map_symbol2sprites.insert(std::make_pair(dynamic_cast<const Symbol*>(spr->GetSymbol()), list));
	}
	else
	{
		itr->second.push_back(spr);
	}
}

void SpriteFactory::Remove(Sprite* spr)
{
	const Symbol* sym = dynamic_cast<const Symbol*>(spr->GetSymbol());
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(sym);
	if (itr != m_map_symbol2sprites.end())
	{
		SpriteList::iterator itr_sprite = itr->second.begin();
		for ( ; itr_sprite != itr->second.end(); )
		{
			if (*itr_sprite == spr)
				itr_sprite = itr->second.erase(itr_sprite);
			else
				++itr_sprite;
		}
	}
}

std::string SpriteFactory::NextName() const
{
	return "_sprite" + StringHelper::ToString(m_id++);
}

void SpriteFactory::UpdateBoundings(const Symbol& sym)
{
	std::map<const Symbol*, SpriteList>::iterator
		itr = m_map_symbol2sprites.find(&sym);
	if (itr != m_map_symbol2sprites.end())
	{
		for (size_t i = 0, n = itr->second.size(); i < n; ++i)
			itr->second[i]->UpdateBounding();
	}
}

void SpriteFactory::RegisterCreator(int type, CreateCallback cb)
{
	m_creators.insert(std::make_pair(type, cb));
}

void SpriteFactory::UnregisterCreator(int type)
{
	m_creators.erase(type);
}

SpriteFactory* SpriteFactory::Instance()
{
	if (!m_instance)
	{
		m_instance = new SpriteFactory();
	}
	return m_instance;
}
}<|endoftext|>