<commit_before>//===-- SelectionDAGPrinter.cpp - Implement SelectionDAG::viewGraph() -----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements the SelectionDAG::viewGraph method.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/Function.h"
#include "llvm/Assembly/Writer.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/ScheduleDAG.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/Target/MRegisterInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h"
#include <fstream>
#include <sstream>
using namespace llvm;

namespace llvm {
  template<>
  struct DOTGraphTraits<SelectionDAG*> : public DefaultDOTGraphTraits {
    static std::string getGraphName(const SelectionDAG *G) {
      return G->getMachineFunction().getFunction()->getName();
    }

    static bool renderGraphFromBottomUp() {
      return true;
    }

    static bool hasNodeAddressLabel(const SDNode *Node,
                                    const SelectionDAG *Graph) {
      return true;
    }

    /// If you want to override the dot attributes printed for a particular
    /// edge, override this method.
    template<typename EdgeIter>
    static std::string getEdgeAttributes(const void *Node, EdgeIter EI) {
      SDOperand Op = EI.getNode()->getOperand(EI.getOperand());
      MVT::ValueType VT = Op.getValueType();
      if (VT == MVT::Flag)
        return "color=red,style=bold";
      else if (VT == MVT::Other)
        return "color=blue,style=dashed";
      return "";
    }

    static std::string getNodeLabel(const SDNode *Node,
                                    const SelectionDAG *Graph);
    static std::string getNodeAttributes(const SDNode *N,
                                         const SelectionDAG *Graph) {
#ifndef NDEBUG
      const std::string &Attrs = Graph->getGraphAttrs(N);
      if (!Attrs.empty()) {
        if (Attrs.find("shape=") == std::string::npos)
          return std::string("shape=Mrecord,") + Attrs;
        else
          return Attrs;
      }
#endif
      return "shape=Mrecord";
    }

    static void addCustomGraphFeatures(SelectionDAG *G,
                                       GraphWriter<SelectionDAG*> &GW) {
      GW.emitSimpleNode(0, "plaintext=circle", "GraphRoot");
      if (G->getRoot().Val)
        GW.emitEdge(0, -1, G->getRoot().Val, -1, "");
    }
  };
}

std::string DOTGraphTraits<SelectionDAG*>::getNodeLabel(const SDNode *Node,
                                                        const SelectionDAG *G) {
  std::string Op = Node->getOperationName(G);

  for (unsigned i = 0, e = Node->getNumValues(); i != e; ++i)
    if (Node->getValueType(i) == MVT::Other)
      Op += ":ch";
    else
      Op = Op + ":" + MVT::getValueTypeString(Node->getValueType(i));

  if (const ConstantSDNode *CSDN = dyn_cast<ConstantSDNode>(Node)) {
    Op += ": " + utostr(CSDN->getValue());
  } else if (const ConstantFPSDNode *CSDN = dyn_cast<ConstantFPSDNode>(Node)) {
    Op += ": " + ftostr(CSDN->getValueAPF());
  } else if (const GlobalAddressSDNode *GADN =
             dyn_cast<GlobalAddressSDNode>(Node)) {
    int offset = GADN->getOffset();
    Op += ": " + GADN->getGlobal()->getName();
    if (offset > 0)
      Op += "+" + itostr(offset);
    else
      Op += itostr(offset);
  } else if (const FrameIndexSDNode *FIDN = dyn_cast<FrameIndexSDNode>(Node)) {
    Op += " " + itostr(FIDN->getIndex());
  } else if (const JumpTableSDNode *JTDN = dyn_cast<JumpTableSDNode>(Node)) {
    Op += " " + itostr(JTDN->getIndex());
  } else if (const ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(Node)){
    if (CP->isMachineConstantPoolEntry()) {
      std::ostringstream SS;
      CP->getMachineCPVal()->print(SS);
      Op += "<" + SS.str() + ">";
    } else {
      if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal()))
        Op += "<" + ftostr(CFP->getValueAPF()) + ">";
      else if (ConstantInt *CI = dyn_cast<ConstantInt>(CP->getConstVal()))
        Op += "<" + utostr(CI->getZExtValue()) + ">";
      else {
        std::ostringstream SS;
        WriteAsOperand(SS, CP->getConstVal(), false);
        Op += "<" + SS.str() + ">";
      }
    }
  } else if (const BasicBlockSDNode *BBDN = dyn_cast<BasicBlockSDNode>(Node)) {
    Op = "BB: ";
    const Value *LBB = (const Value*)BBDN->getBasicBlock()->getBasicBlock();
    if (LBB)
      Op += LBB->getName();
    //Op += " " + (const void*)BBDN->getBasicBlock();
  } else if (const RegisterSDNode *R = dyn_cast<RegisterSDNode>(Node)) {
    if (G && R->getReg() != 0 &&
        MRegisterInfo::isPhysicalRegister(R->getReg())) {
      Op = Op + " " + G->getTarget().getRegisterInfo()->getName(R->getReg());
    } else {
      Op += " #" + utostr(R->getReg());
    }
  } else if (const ExternalSymbolSDNode *ES =
             dyn_cast<ExternalSymbolSDNode>(Node)) {
    Op += "'" + std::string(ES->getSymbol()) + "'";
  } else if (const SrcValueSDNode *M = dyn_cast<SrcValueSDNode>(Node)) {
    if (M->getValue())
      Op += "<" + M->getValue()->getName() + ":" + itostr(M->getOffset()) + ">";
    else
      Op += "<null:" + itostr(M->getOffset()) + ">";
  } else if (const VTSDNode *N = dyn_cast<VTSDNode>(Node)) {
    Op = Op + " VT=" + MVT::getValueTypeString(N->getVT());
  } else if (const StringSDNode *N = dyn_cast<StringSDNode>(Node)) {
    Op = Op + "\"" + N->getValue() + "\"";
  } else if (const LoadSDNode *LD = dyn_cast<LoadSDNode>(Node)) {
    bool doExt = true;
    switch (LD->getExtensionType()) {
    default: doExt = false; break;
    case ISD::EXTLOAD:
      Op = Op + "<anyext ";
      break;
    case ISD::SEXTLOAD:
      Op = Op + " <sext ";
      break;
    case ISD::ZEXTLOAD:
      Op = Op + " <zext ";
      break;
    }
    if (doExt)
      Op = Op + MVT::getValueTypeString(LD->getLoadedVT()) + ">";

    Op += LD->getIndexedModeName(LD->getAddressingMode());
  } else if (const StoreSDNode *ST = dyn_cast<StoreSDNode>(Node)) {
    if (ST->isTruncatingStore())
      Op = Op + "<trunc " + MVT::getValueTypeString(ST->getStoredVT()) + ">";
    Op += ST->getIndexedModeName(ST->getAddressingMode());
  }

#if 0
  Op += " Id=" + itostr(Node->getNodeId());
#endif

  return Op;
}

/// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
/// rendered using 'dot'.
///
void SelectionDAG::viewGraph() {
// This code is only for debugging!
#ifndef NDEBUG
  ViewGraph(this, "dag." + getMachineFunction().getFunction()->getName());
#else
  cerr << "SelectionDAG::viewGraph is only available in debug builds on "
       << "systems with Graphviz or gv!\n";
#endif  // NDEBUG
}

/// clearGraphAttrs - Clear all previously defined node graph attributes.
/// Intended to be used from a debugging tool (eg. gdb).
void SelectionDAG::clearGraphAttrs() {
#ifndef NDEBUG
  NodeGraphAttrs.clear();
#else
  cerr << "SelectionDAG::clearGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

/// setGraphAttrs - Set graph attributes for a node. (eg. "color=red".)
///
void SelectionDAG::setGraphAttrs(const SDNode *N, const char *Attrs) {
#ifndef NDEBUG
  NodeGraphAttrs[N] = Attrs;
#else
  cerr << "SelectionDAG::setGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

/// getGraphAttrs - Get graph attributes for a node. (eg. "color=red".)
/// Used from getNodeAttributes.
const std::string SelectionDAG::getGraphAttrs(const SDNode *N) const {
#ifndef NDEBUG
  std::map<const SDNode *, std::string>::const_iterator I =
    NodeGraphAttrs.find(N);

  if (I != NodeGraphAttrs.end())
    return I->second;
  else
    return "";
#else
  cerr << "SelectionDAG::getGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
  return std::string("");
#endif
}

/// setGraphColor - Convenience for setting node color attribute.
///
void SelectionDAG::setGraphColor(const SDNode *N, const char *Color) {
#ifndef NDEBUG
  NodeGraphAttrs[N] = std::string("color=") + Color;
#else
  cerr << "SelectionDAG::setGraphColor is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

namespace llvm {
  template<>
  struct DOTGraphTraits<ScheduleDAG*> : public DefaultDOTGraphTraits {
    static std::string getGraphName(const ScheduleDAG *G) {
      return DOTGraphTraits<SelectionDAG*>::getGraphName(&G->DAG);
    }

    static bool renderGraphFromBottomUp() {
      return true;
    }

    static bool hasNodeAddressLabel(const SUnit *Node,
                                    const ScheduleDAG *Graph) {
      return true;
    }

    /// If you want to override the dot attributes printed for a particular
    /// edge, override this method.
    template<typename EdgeIter>
    static std::string getEdgeAttributes(const void *Node, EdgeIter EI) {
      if (EI.isCtrlDep())
        return "color=blue,style=dashed";
      return "";
    }

    static std::string getNodeLabel(const SUnit *Node,
                                    const ScheduleDAG *Graph);
    static std::string getNodeAttributes(const SUnit *N,
                                         const ScheduleDAG *Graph) {
      return "shape=Mrecord";
    }

    static void addCustomGraphFeatures(ScheduleDAG *G,
                                       GraphWriter<ScheduleDAG*> &GW) {
      GW.emitSimpleNode(0, "plaintext=circle", "GraphRoot");
      if (G->DAG.getRoot().Val)
        GW.emitEdge(0, -1, G->SUnitMap[G->DAG.getRoot().Val].front(), -1, "");
    }
  };
}

std::string DOTGraphTraits<ScheduleDAG*>::getNodeLabel(const SUnit *SU,
                                                       const ScheduleDAG *G) {
  std::string Op;

  for (unsigned i = 0; i < SU->FlaggedNodes.size(); ++i) {
    Op += DOTGraphTraits<SelectionDAG*>::getNodeLabel(SU->FlaggedNodes[i],
                                                      &G->DAG) + "\n";
  }

  Op += DOTGraphTraits<SelectionDAG*>::getNodeLabel(SU->Node, &G->DAG);

  return Op;
}

/// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
/// rendered using 'dot'.
///
void ScheduleDAG::viewGraph() {
// This code is only for debugging!
#ifndef NDEBUG
  ViewGraph(this, "dag." + DAG.getMachineFunction().getFunction()->getName());
#else
  cerr << "ScheduleDAG::viewGraph is only available in debug builds on "
       << "systems with Graphviz or gv!\n";
#endif  // NDEBUG
}
<commit_msg>include alignment and volatility information in -view-*-dags output<commit_after>//===-- SelectionDAGPrinter.cpp - Implement SelectionDAG::viewGraph() -----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements the SelectionDAG::viewGraph method.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/Function.h"
#include "llvm/Assembly/Writer.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/ScheduleDAG.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/Target/MRegisterInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h"
#include <fstream>
#include <sstream>
using namespace llvm;

namespace llvm {
  template<>
  struct DOTGraphTraits<SelectionDAG*> : public DefaultDOTGraphTraits {
    static std::string getGraphName(const SelectionDAG *G) {
      return G->getMachineFunction().getFunction()->getName();
    }

    static bool renderGraphFromBottomUp() {
      return true;
    }

    static bool hasNodeAddressLabel(const SDNode *Node,
                                    const SelectionDAG *Graph) {
      return true;
    }

    /// If you want to override the dot attributes printed for a particular
    /// edge, override this method.
    template<typename EdgeIter>
    static std::string getEdgeAttributes(const void *Node, EdgeIter EI) {
      SDOperand Op = EI.getNode()->getOperand(EI.getOperand());
      MVT::ValueType VT = Op.getValueType();
      if (VT == MVT::Flag)
        return "color=red,style=bold";
      else if (VT == MVT::Other)
        return "color=blue,style=dashed";
      return "";
    }

    static std::string getNodeLabel(const SDNode *Node,
                                    const SelectionDAG *Graph);
    static std::string getNodeAttributes(const SDNode *N,
                                         const SelectionDAG *Graph) {
#ifndef NDEBUG
      const std::string &Attrs = Graph->getGraphAttrs(N);
      if (!Attrs.empty()) {
        if (Attrs.find("shape=") == std::string::npos)
          return std::string("shape=Mrecord,") + Attrs;
        else
          return Attrs;
      }
#endif
      return "shape=Mrecord";
    }

    static void addCustomGraphFeatures(SelectionDAG *G,
                                       GraphWriter<SelectionDAG*> &GW) {
      GW.emitSimpleNode(0, "plaintext=circle", "GraphRoot");
      if (G->getRoot().Val)
        GW.emitEdge(0, -1, G->getRoot().Val, -1, "");
    }
  };
}

std::string DOTGraphTraits<SelectionDAG*>::getNodeLabel(const SDNode *Node,
                                                        const SelectionDAG *G) {
  std::string Op = Node->getOperationName(G);

  for (unsigned i = 0, e = Node->getNumValues(); i != e; ++i)
    if (Node->getValueType(i) == MVT::Other)
      Op += ":ch";
    else
      Op = Op + ":" + MVT::getValueTypeString(Node->getValueType(i));

  if (const ConstantSDNode *CSDN = dyn_cast<ConstantSDNode>(Node)) {
    Op += ": " + utostr(CSDN->getValue());
  } else if (const ConstantFPSDNode *CSDN = dyn_cast<ConstantFPSDNode>(Node)) {
    Op += ": " + ftostr(CSDN->getValueAPF());
  } else if (const GlobalAddressSDNode *GADN =
             dyn_cast<GlobalAddressSDNode>(Node)) {
    int offset = GADN->getOffset();
    Op += ": " + GADN->getGlobal()->getName();
    if (offset > 0)
      Op += "+" + itostr(offset);
    else
      Op += itostr(offset);
  } else if (const FrameIndexSDNode *FIDN = dyn_cast<FrameIndexSDNode>(Node)) {
    Op += " " + itostr(FIDN->getIndex());
  } else if (const JumpTableSDNode *JTDN = dyn_cast<JumpTableSDNode>(Node)) {
    Op += " " + itostr(JTDN->getIndex());
  } else if (const ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(Node)){
    if (CP->isMachineConstantPoolEntry()) {
      std::ostringstream SS;
      CP->getMachineCPVal()->print(SS);
      Op += "<" + SS.str() + ">";
    } else {
      if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal()))
        Op += "<" + ftostr(CFP->getValueAPF()) + ">";
      else if (ConstantInt *CI = dyn_cast<ConstantInt>(CP->getConstVal()))
        Op += "<" + utostr(CI->getZExtValue()) + ">";
      else {
        std::ostringstream SS;
        WriteAsOperand(SS, CP->getConstVal(), false);
        Op += "<" + SS.str() + ">";
      }
    }
  } else if (const BasicBlockSDNode *BBDN = dyn_cast<BasicBlockSDNode>(Node)) {
    Op = "BB: ";
    const Value *LBB = (const Value*)BBDN->getBasicBlock()->getBasicBlock();
    if (LBB)
      Op += LBB->getName();
    //Op += " " + (const void*)BBDN->getBasicBlock();
  } else if (const RegisterSDNode *R = dyn_cast<RegisterSDNode>(Node)) {
    if (G && R->getReg() != 0 &&
        MRegisterInfo::isPhysicalRegister(R->getReg())) {
      Op = Op + " " + G->getTarget().getRegisterInfo()->getName(R->getReg());
    } else {
      Op += " #" + utostr(R->getReg());
    }
  } else if (const ExternalSymbolSDNode *ES =
             dyn_cast<ExternalSymbolSDNode>(Node)) {
    Op += "'" + std::string(ES->getSymbol()) + "'";
  } else if (const SrcValueSDNode *M = dyn_cast<SrcValueSDNode>(Node)) {
    if (M->getValue())
      Op += "<" + M->getValue()->getName() + ":" + itostr(M->getOffset()) + ">";
    else
      Op += "<null:" + itostr(M->getOffset()) + ">";
  } else if (const VTSDNode *N = dyn_cast<VTSDNode>(Node)) {
    Op = Op + " VT=" + MVT::getValueTypeString(N->getVT());
  } else if (const StringSDNode *N = dyn_cast<StringSDNode>(Node)) {
    Op = Op + "\"" + N->getValue() + "\"";
  } else if (const LoadSDNode *LD = dyn_cast<LoadSDNode>(Node)) {
    bool doExt = true;
    switch (LD->getExtensionType()) {
    default: doExt = false; break;
    case ISD::EXTLOAD:
      Op = Op + "<anyext ";
      break;
    case ISD::SEXTLOAD:
      Op = Op + " <sext ";
      break;
    case ISD::ZEXTLOAD:
      Op = Op + " <zext ";
      break;
    }
    if (doExt)
      Op += MVT::getValueTypeString(LD->getLoadedVT()) + ">";
    if (LD->isVolatile())
      Op += "<V>";
    Op += LD->getIndexedModeName(LD->getAddressingMode());
    if (LD->getAlignment() > 1)
      Op += " A=" + utostr(LD->getAlignment());
  } else if (const StoreSDNode *ST = dyn_cast<StoreSDNode>(Node)) {
    if (ST->isTruncatingStore())
      Op += "<trunc " + MVT::getValueTypeString(ST->getStoredVT()) + ">";
    if (ST->isVolatile())
      Op += "<V>";
    Op += ST->getIndexedModeName(ST->getAddressingMode());
    if (ST->getAlignment() > 1)
      Op += " A=" + utostr(ST->getAlignment());
  }

#if 0
  Op += " Id=" + itostr(Node->getNodeId());
#endif

  return Op;
}

/// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
/// rendered using 'dot'.
///
void SelectionDAG::viewGraph() {
// This code is only for debugging!
#ifndef NDEBUG
  ViewGraph(this, "dag." + getMachineFunction().getFunction()->getName());
#else
  cerr << "SelectionDAG::viewGraph is only available in debug builds on "
       << "systems with Graphviz or gv!\n";
#endif  // NDEBUG
}

/// clearGraphAttrs - Clear all previously defined node graph attributes.
/// Intended to be used from a debugging tool (eg. gdb).
void SelectionDAG::clearGraphAttrs() {
#ifndef NDEBUG
  NodeGraphAttrs.clear();
#else
  cerr << "SelectionDAG::clearGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

/// setGraphAttrs - Set graph attributes for a node. (eg. "color=red".)
///
void SelectionDAG::setGraphAttrs(const SDNode *N, const char *Attrs) {
#ifndef NDEBUG
  NodeGraphAttrs[N] = Attrs;
#else
  cerr << "SelectionDAG::setGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

/// getGraphAttrs - Get graph attributes for a node. (eg. "color=red".)
/// Used from getNodeAttributes.
const std::string SelectionDAG::getGraphAttrs(const SDNode *N) const {
#ifndef NDEBUG
  std::map<const SDNode *, std::string>::const_iterator I =
    NodeGraphAttrs.find(N);

  if (I != NodeGraphAttrs.end())
    return I->second;
  else
    return "";
#else
  cerr << "SelectionDAG::getGraphAttrs is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
  return std::string("");
#endif
}

/// setGraphColor - Convenience for setting node color attribute.
///
void SelectionDAG::setGraphColor(const SDNode *N, const char *Color) {
#ifndef NDEBUG
  NodeGraphAttrs[N] = std::string("color=") + Color;
#else
  cerr << "SelectionDAG::setGraphColor is only available in debug builds"
       << " on systems with Graphviz or gv!\n";
#endif
}

namespace llvm {
  template<>
  struct DOTGraphTraits<ScheduleDAG*> : public DefaultDOTGraphTraits {
    static std::string getGraphName(const ScheduleDAG *G) {
      return DOTGraphTraits<SelectionDAG*>::getGraphName(&G->DAG);
    }

    static bool renderGraphFromBottomUp() {
      return true;
    }

    static bool hasNodeAddressLabel(const SUnit *Node,
                                    const ScheduleDAG *Graph) {
      return true;
    }

    /// If you want to override the dot attributes printed for a particular
    /// edge, override this method.
    template<typename EdgeIter>
    static std::string getEdgeAttributes(const void *Node, EdgeIter EI) {
      if (EI.isCtrlDep())
        return "color=blue,style=dashed";
      return "";
    }

    static std::string getNodeLabel(const SUnit *Node,
                                    const ScheduleDAG *Graph);
    static std::string getNodeAttributes(const SUnit *N,
                                         const ScheduleDAG *Graph) {
      return "shape=Mrecord";
    }

    static void addCustomGraphFeatures(ScheduleDAG *G,
                                       GraphWriter<ScheduleDAG*> &GW) {
      GW.emitSimpleNode(0, "plaintext=circle", "GraphRoot");
      if (G->DAG.getRoot().Val)
        GW.emitEdge(0, -1, G->SUnitMap[G->DAG.getRoot().Val].front(), -1, "");
    }
  };
}

std::string DOTGraphTraits<ScheduleDAG*>::getNodeLabel(const SUnit *SU,
                                                       const ScheduleDAG *G) {
  std::string Op;

  for (unsigned i = 0; i < SU->FlaggedNodes.size(); ++i) {
    Op += DOTGraphTraits<SelectionDAG*>::getNodeLabel(SU->FlaggedNodes[i],
                                                      &G->DAG) + "\n";
  }

  Op += DOTGraphTraits<SelectionDAG*>::getNodeLabel(SU->Node, &G->DAG);

  return Op;
}

/// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
/// rendered using 'dot'.
///
void ScheduleDAG::viewGraph() {
// This code is only for debugging!
#ifndef NDEBUG
  ViewGraph(this, "dag." + DAG.getMachineFunction().getFunction()->getName());
#else
  cerr << "ScheduleDAG::viewGraph is only available in debug builds on "
       << "systems with Graphviz or gv!\n";
#endif  // NDEBUG
}
<|endoftext|>


<commit_before>//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp -------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Atoms.h"
#include "EdataPass.h"
#include "GroupedSectionsPass.h"
#include "IdataPass.h"
#include "LinkerGeneratedSymbolFile.h"
#include "SetSubsystemPass.h"

#include "lld/Core/PassManager.h"
#include "lld/Passes/LayoutPass.h"
#include "lld/Passes/RoundTripNativePass.h"
#include "lld/Passes/RoundTripYAMLPass.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Simple.h"
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Path.h"

#include <bitset>
#include <climits>
#include <set>

namespace lld {

static void assignOrdinals(PECOFFLinkingContext &ctx) {
  int maxOrdinal = -1;
  for (const PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports())
    maxOrdinal = std::max(maxOrdinal, desc.ordinal);
  int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1);
  for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports())
    if (desc.ordinal == -1)
      desc.ordinal = nextOrdinal++;
}

bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) {
  if (_stackReserve < _stackCommit) {
    diagnostics << "Invalid stack size: reserve size must be equal to or "
                << "greater than commit size, but got " << _stackCommit
                << " and " << _stackReserve << ".\n";
    return false;
  }

  if (_heapReserve < _heapCommit) {
    diagnostics << "Invalid heap size: reserve size must be equal to or "
                << "greater than commit size, but got " << _heapCommit
                << " and " << _heapReserve << ".\n";
    return false;
  }

  // It's an error if the base address is not multiple of 64K.
  if (_baseAddress & 0xffff) {
    diagnostics << "Base address have to be multiple of 64K, but got "
                << _baseAddress << "\n";
    return false;
  }

  // Check for duplicate export ordinals.
  std::set<int> exports;
  for (const PECOFFLinkingContext::ExportDesc &desc : getDllExports()) {
    if (desc.ordinal == -1)
      continue;
    if (exports.count(desc.ordinal) == 1) {
      diagnostics << "Duplicate export ordinals: " << desc.ordinal << "\n";
      return false;
    }
    exports.insert(desc.ordinal);
  }

  std::bitset<64> alignment(_sectionDefaultAlignment);
  if (alignment.count() != 1) {
    diagnostics << "Section alignment must be a power of 2, but got "
                << _sectionDefaultAlignment << "\n";
    return false;
  }

  // Architectures other than i386 is not supported yet.
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) {
    diagnostics << "Machine type other than x86 is not supported.\n";
    return false;
  }

  // Assign default ordinals to export symbols.
  assignOrdinals(*this);

  _writer = createWriterPECOFF(*this);
  return true;
}

std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const {
  if (entrySymbolName().empty())
    return nullptr;
  std::unique_ptr<SimpleFile> entryFile(
      new SimpleFile("command line option /entry"));
  entryFile->addAtom(
      *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
  return std::move(entryFile);
}

std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const {
  if (_initialUndefinedSymbols.empty())
    return nullptr;
  std::unique_ptr<SimpleFile> undefinedSymFile(
      new SimpleFile("command line option /c (or) /include"));
  for (auto undefSymStr : _initialUndefinedSymbols)
    undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
                                   *undefinedSymFile, undefSymStr)));
  return std::move(undefinedSymFile);
}

bool PECOFFLinkingContext::createImplicitFiles(
    std::vector<std::unique_ptr<File> > &) const {
  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Implicit Files"));
  std::unique_ptr<File> linkerGeneratedSymFile(
      new pecoff::LinkerGeneratedSymbolFile(*this));
  fileNode->appendInputFile(std::move(linkerGeneratedSymFile));
  inputGraph().insertOneElementAt(std::move(fileNode),
                                  InputGraph::Position::END);
  return true;
}

/// Returns the section name in the resulting executable.
///
/// Sections in object files are usually output to the executable with the same
/// name, but you can rename by command line option. /merge:from=to makes the
/// linker to combine "from" section contents to "to" section in the
/// executable. We have a mapping for the renaming. This method looks up the
/// table and returns a new section name if renamed.
StringRef
PECOFFLinkingContext::getOutputSectionName(StringRef sectionName) const {
  auto it = _renamedSections.find(sectionName);
  if (it == _renamedSections.end())
    return sectionName;
  return getOutputSectionName(it->second);
}

/// Adds a mapping to the section renaming table. This method will be used for
/// /merge command line option.
bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics,
                                              StringRef from, StringRef to) {
  auto it = _renamedSections.find(from);
  if (it != _renamedSections.end()) {
    if (it->second == to)
      // There's already the same mapping.
      return true;
    diagnostics << "Section \"" << from << "\" is already mapped to \""
                << it->second << ", so it cannot be mapped to \"" << to << "\".";
    return true;
  }

  // Add a mapping, and check if there's no cycle in the renaming mapping. The
  // cycle detection algorithm we use here is naive, but that's OK because the
  // number of mapping is usually less than 10.
  _renamedSections[from] = to;
  for (auto elem : _renamedSections) {
    StringRef sectionName = elem.first;
    std::set<StringRef> visited;
    visited.insert(sectionName);
    for (;;) {
      auto pos = _renamedSections.find(sectionName);
      if (pos == _renamedSections.end())
        break;
      if (visited.count(pos->second)) {
        diagnostics << "/merge:" << from << "=" << to << " makes a cycle";
        return false;
      }
      sectionName = pos->second;
      visited.insert(sectionName);
    }
  }
  return true;
}

StringRef PECOFFLinkingContext::getAlternateName(StringRef def) const {
  auto it = _alternateNames.find(def);
  if (it == _alternateNames.end())
    return "";
  return it->second;
}

void PECOFFLinkingContext::setAlternateName(StringRef weak, StringRef def) {
  _alternateNames[def] = weak;
}

/// Try to find the input library file from the search paths and append it to
/// the input file list. Returns true if the library file is found.
StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const {
  // Current directory always takes precedence over the search paths.
  if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename))
    return filename;
  // Iterate over the search paths.
  for (StringRef dir : _inputSearchPaths) {
    SmallString<128> path = dir;
    llvm::sys::path::append(path, filename);
    if (llvm::sys::fs::exists(path.str()))
      return allocate(path.str());
  }
  return filename;
}

/// Returns the decorated name of the given symbol name. On 32-bit x86, it
/// adds "_" at the beginning of the string. On other architectures, the
/// return value is the same as the argument.
StringRef PECOFFLinkingContext::decorateSymbol(StringRef name) const {
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386)
    return name;
  std::string str = "_";
  str.append(name);
  return allocate(str);
}

StringRef PECOFFLinkingContext::undecorateSymbol(StringRef name) const {
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386)
    return name;
  assert(name.startswith("_"));
  return name.substr(1);
}

Writer &PECOFFLinkingContext::writer() const { return *_writer; }

void PECOFFLinkingContext::setSectionSetMask(StringRef sectionName,
                                             uint32_t newFlags) {
  _sectionSetMask[sectionName] |= newFlags;
  _sectionClearMask[sectionName] &= ~newFlags;
  const uint32_t rwx = (llvm::COFF::IMAGE_SCN_MEM_READ |
                        llvm::COFF::IMAGE_SCN_MEM_WRITE |
                        llvm::COFF::IMAGE_SCN_MEM_EXECUTE);
  if (newFlags & rwx)
    _sectionClearMask[sectionName] |= ~_sectionSetMask[sectionName] & rwx;
  assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}

void PECOFFLinkingContext::setSectionClearMask(StringRef sectionName,
                                               uint32_t newFlags) {
  _sectionClearMask[sectionName] |= newFlags;
  _sectionSetMask[sectionName] &= ~newFlags;
  assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}

uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName,
                                                    uint32_t flags) const {
  auto si = _sectionSetMask.find(sectionName);
  uint32_t setMask = (si == _sectionSetMask.end()) ? 0 : si->second;
  auto ci = _sectionClearMask.find(sectionName);
  uint32_t clearMask = (ci == _sectionClearMask.end()) ? 0 : ci->second;
  return (flags | setMask) & ~clearMask;
}

void PECOFFLinkingContext::addDllExport(ExportDesc &desc) {
  if (_dllExportSet.count(desc.name)) {
    llvm::errs() << "Export symbol '" << desc.name
                 << "' specified more than once.";
    return;
  }
  _dllExports.push_back(desc);
  _dllExportSet.insert(_dllExports.back().name);
}

void PECOFFLinkingContext::addPasses(PassManager &pm) {
  pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this)));
  pm.add(std::unique_ptr<Pass>(new pecoff::EdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new LayoutPass()));
  pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));
}

} // end namespace lld
<commit_msg>Add a newline to an error message.<commit_after>//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp -------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Atoms.h"
#include "EdataPass.h"
#include "GroupedSectionsPass.h"
#include "IdataPass.h"
#include "LinkerGeneratedSymbolFile.h"
#include "SetSubsystemPass.h"

#include "lld/Core/PassManager.h"
#include "lld/Passes/LayoutPass.h"
#include "lld/Passes/RoundTripNativePass.h"
#include "lld/Passes/RoundTripYAMLPass.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Simple.h"
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Path.h"

#include <bitset>
#include <climits>
#include <set>

namespace lld {

static void assignOrdinals(PECOFFLinkingContext &ctx) {
  int maxOrdinal = -1;
  for (const PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports())
    maxOrdinal = std::max(maxOrdinal, desc.ordinal);
  int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1);
  for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports())
    if (desc.ordinal == -1)
      desc.ordinal = nextOrdinal++;
}

bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) {
  if (_stackReserve < _stackCommit) {
    diagnostics << "Invalid stack size: reserve size must be equal to or "
                << "greater than commit size, but got " << _stackCommit
                << " and " << _stackReserve << ".\n";
    return false;
  }

  if (_heapReserve < _heapCommit) {
    diagnostics << "Invalid heap size: reserve size must be equal to or "
                << "greater than commit size, but got " << _heapCommit
                << " and " << _heapReserve << ".\n";
    return false;
  }

  // It's an error if the base address is not multiple of 64K.
  if (_baseAddress & 0xffff) {
    diagnostics << "Base address have to be multiple of 64K, but got "
                << _baseAddress << "\n";
    return false;
  }

  // Check for duplicate export ordinals.
  std::set<int> exports;
  for (const PECOFFLinkingContext::ExportDesc &desc : getDllExports()) {
    if (desc.ordinal == -1)
      continue;
    if (exports.count(desc.ordinal) == 1) {
      diagnostics << "Duplicate export ordinals: " << desc.ordinal << "\n";
      return false;
    }
    exports.insert(desc.ordinal);
  }

  std::bitset<64> alignment(_sectionDefaultAlignment);
  if (alignment.count() != 1) {
    diagnostics << "Section alignment must be a power of 2, but got "
                << _sectionDefaultAlignment << "\n";
    return false;
  }

  // Architectures other than i386 is not supported yet.
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) {
    diagnostics << "Machine type other than x86 is not supported.\n";
    return false;
  }

  // Assign default ordinals to export symbols.
  assignOrdinals(*this);

  _writer = createWriterPECOFF(*this);
  return true;
}

std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const {
  if (entrySymbolName().empty())
    return nullptr;
  std::unique_ptr<SimpleFile> entryFile(
      new SimpleFile("command line option /entry"));
  entryFile->addAtom(
      *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
  return std::move(entryFile);
}

std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const {
  if (_initialUndefinedSymbols.empty())
    return nullptr;
  std::unique_ptr<SimpleFile> undefinedSymFile(
      new SimpleFile("command line option /c (or) /include"));
  for (auto undefSymStr : _initialUndefinedSymbols)
    undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
                                   *undefinedSymFile, undefSymStr)));
  return std::move(undefinedSymFile);
}

bool PECOFFLinkingContext::createImplicitFiles(
    std::vector<std::unique_ptr<File> > &) const {
  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Implicit Files"));
  std::unique_ptr<File> linkerGeneratedSymFile(
      new pecoff::LinkerGeneratedSymbolFile(*this));
  fileNode->appendInputFile(std::move(linkerGeneratedSymFile));
  inputGraph().insertOneElementAt(std::move(fileNode),
                                  InputGraph::Position::END);
  return true;
}

/// Returns the section name in the resulting executable.
///
/// Sections in object files are usually output to the executable with the same
/// name, but you can rename by command line option. /merge:from=to makes the
/// linker to combine "from" section contents to "to" section in the
/// executable. We have a mapping for the renaming. This method looks up the
/// table and returns a new section name if renamed.
StringRef
PECOFFLinkingContext::getOutputSectionName(StringRef sectionName) const {
  auto it = _renamedSections.find(sectionName);
  if (it == _renamedSections.end())
    return sectionName;
  return getOutputSectionName(it->second);
}

/// Adds a mapping to the section renaming table. This method will be used for
/// /merge command line option.
bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics,
                                              StringRef from, StringRef to) {
  auto it = _renamedSections.find(from);
  if (it != _renamedSections.end()) {
    if (it->second == to)
      // There's already the same mapping.
      return true;
    diagnostics << "Section \"" << from << "\" is already mapped to \""
                << it->second << ", so it cannot be mapped to \"" << to << "\".";
    return true;
  }

  // Add a mapping, and check if there's no cycle in the renaming mapping. The
  // cycle detection algorithm we use here is naive, but that's OK because the
  // number of mapping is usually less than 10.
  _renamedSections[from] = to;
  for (auto elem : _renamedSections) {
    StringRef sectionName = elem.first;
    std::set<StringRef> visited;
    visited.insert(sectionName);
    for (;;) {
      auto pos = _renamedSections.find(sectionName);
      if (pos == _renamedSections.end())
        break;
      if (visited.count(pos->second)) {
        diagnostics << "/merge:" << from << "=" << to << " makes a cycle";
        return false;
      }
      sectionName = pos->second;
      visited.insert(sectionName);
    }
  }
  return true;
}

StringRef PECOFFLinkingContext::getAlternateName(StringRef def) const {
  auto it = _alternateNames.find(def);
  if (it == _alternateNames.end())
    return "";
  return it->second;
}

void PECOFFLinkingContext::setAlternateName(StringRef weak, StringRef def) {
  _alternateNames[def] = weak;
}

/// Try to find the input library file from the search paths and append it to
/// the input file list. Returns true if the library file is found.
StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const {
  // Current directory always takes precedence over the search paths.
  if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename))
    return filename;
  // Iterate over the search paths.
  for (StringRef dir : _inputSearchPaths) {
    SmallString<128> path = dir;
    llvm::sys::path::append(path, filename);
    if (llvm::sys::fs::exists(path.str()))
      return allocate(path.str());
  }
  return filename;
}

/// Returns the decorated name of the given symbol name. On 32-bit x86, it
/// adds "_" at the beginning of the string. On other architectures, the
/// return value is the same as the argument.
StringRef PECOFFLinkingContext::decorateSymbol(StringRef name) const {
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386)
    return name;
  std::string str = "_";
  str.append(name);
  return allocate(str);
}

StringRef PECOFFLinkingContext::undecorateSymbol(StringRef name) const {
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386)
    return name;
  assert(name.startswith("_"));
  return name.substr(1);
}

Writer &PECOFFLinkingContext::writer() const { return *_writer; }

void PECOFFLinkingContext::setSectionSetMask(StringRef sectionName,
                                             uint32_t newFlags) {
  _sectionSetMask[sectionName] |= newFlags;
  _sectionClearMask[sectionName] &= ~newFlags;
  const uint32_t rwx = (llvm::COFF::IMAGE_SCN_MEM_READ |
                        llvm::COFF::IMAGE_SCN_MEM_WRITE |
                        llvm::COFF::IMAGE_SCN_MEM_EXECUTE);
  if (newFlags & rwx)
    _sectionClearMask[sectionName] |= ~_sectionSetMask[sectionName] & rwx;
  assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}

void PECOFFLinkingContext::setSectionClearMask(StringRef sectionName,
                                               uint32_t newFlags) {
  _sectionClearMask[sectionName] |= newFlags;
  _sectionSetMask[sectionName] &= ~newFlags;
  assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}

uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName,
                                                    uint32_t flags) const {
  auto si = _sectionSetMask.find(sectionName);
  uint32_t setMask = (si == _sectionSetMask.end()) ? 0 : si->second;
  auto ci = _sectionClearMask.find(sectionName);
  uint32_t clearMask = (ci == _sectionClearMask.end()) ? 0 : ci->second;
  return (flags | setMask) & ~clearMask;
}

void PECOFFLinkingContext::addDllExport(ExportDesc &desc) {
  if (_dllExportSet.count(desc.name)) {
    llvm::errs() << "Export symbol '" << desc.name
                 << "' specified more than once.\n";
    return;
  }
  _dllExports.push_back(desc);
  _dllExportSet.insert(_dllExports.back().name);
}

void PECOFFLinkingContext::addPasses(PassManager &pm) {
  pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this)));
  pm.add(std::unique_ptr<Pass>(new pecoff::EdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new LayoutPass()));
  pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));
}

} // end namespace lld
<|endoftext|>


<commit_before>#include <imgui/imgui.h>

#include "game_view.h"
#include "editor/asset_browser.h"
#include "editor/asset_compiler.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "editor/world_editor.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/geometry.h"
#include "engine/input_system.h"
#include "engine/lua_wrapper.h"
#include "engine/plugin_manager.h"
#include "engine/profiler.h"
#include "engine/resource_manager.h"
#include "engine/universe/universe.h"
#include "gui/gui_system.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"
#include "renderer/texture.h"

namespace Lumix
{

struct GUIInterface : GUISystem::Interface
{
	explicit GUIInterface(GameView& game_view)
		: m_game_view(game_view)
	{
	}

	Pipeline* getPipeline() override { return m_game_view.m_pipeline; }
	Vec2 getPos() const override { return m_game_view.m_pos; }
	Vec2 getSize() const override { return m_game_view.m_size; }

	void enableCursor(bool enable) override
	{
		m_game_view.enableIngameCursor(enable);
	}

	GameView& m_game_view;
};

GameView::GameView(StudioApp& app)
	: m_studio_app(app)
	, m_is_open(false)
	, m_is_fullscreen(false)
	, m_pipeline(nullptr)
	, m_is_mouse_captured(false)
	, m_is_ingame_cursor(false)
	, m_time_multiplier(1.0f)
	, m_paused(false)
	, m_show_stats(false)
	, m_gui_interface(nullptr)
	, m_editor(app.getWorldEditor())
{
	Engine& engine = app.getWorldEditor().getEngine();
	auto f = &LuaWrapper::wrapMethodClosure<&GameView::forceViewport>;
	LuaWrapper::createSystemClosure(engine.getState(), "GameView", this, "forceViewport", f);

	WorldEditor& editor = app.getWorldEditor();
	Action* action = LUMIX_NEW(editor.getAllocator(), Action)("Game View", "Toggle game view", "game_view");
	action->func.bind<&GameView::onAction>(this);
	action->is_selected.bind<&GameView::isOpen>(this);
	app.addWindowAction(action);

	Action* fullscreen_action = LUMIX_NEW(editor.getAllocator(), Action)("Game View fullscreen", "Game View fullscreen", "game_view_fullscreen");
	fullscreen_action->func.bind<&GameView::toggleFullscreen>(this);
	app.addAction(fullscreen_action);

	auto* renderer = (Renderer*)engine.getPluginManager().getPlugin("renderer");
	PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
	m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW", engine.getAllocator());

	editor.universeCreated().bind<&GameView::onUniverseCreated>(this);
	editor.universeDestroyed().bind<&GameView::onUniverseDestroyed>(this);
	if (editor.getUniverse()) onUniverseCreated();

	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui)
	{
		m_gui_interface = LUMIX_NEW(engine.getAllocator(), GUIInterface)(*this);
		gui->setInterface(m_gui_interface);
	}
}

GameView::~GameView()
{
	m_editor.universeCreated().unbind<&GameView::onUniverseCreated>(this);
	m_editor.universeDestroyed().unbind<&GameView::onUniverseDestroyed>(this);
	auto* gui = static_cast<GUISystem*>(m_editor.getEngine().getPluginManager().getPlugin("gui"));
	if (gui)
	{
		gui->setInterface(nullptr);
		LUMIX_DELETE(m_editor.getEngine().getAllocator(), m_gui_interface);
	}
	Pipeline::destroy(m_pipeline);
	m_pipeline = nullptr;

}

void GameView::enableIngameCursor(bool enable)
{
	m_is_ingame_cursor = enable;
	if (!m_is_mouse_captured) return;

	OS::showCursor(m_is_ingame_cursor);
}

void GameView::onUniverseCreated()
{
	auto* scene = m_editor.getUniverse()->getScene(crc32("renderer"));
	m_pipeline->setScene(static_cast<RenderScene*>(scene));
}

void GameView::onUniverseDestroyed()
{
	m_pipeline->setScene(nullptr);
}

void GameView::setScene(RenderScene* scene)
{
	m_pipeline->setScene(scene);
}

void GameView::captureMouse(bool capture)
{
	if (m_is_mouse_captured == capture) return;

	m_is_mouse_captured = capture;
	m_editor.getEngine().getInputSystem().enable(m_is_mouse_captured);
	OS::showCursor(!capture || m_is_ingame_cursor);

	if (capture) {
		const OS::Point cp = OS::getMouseScreenPos();
		m_captured_mouse_x = cp.x;
		m_captured_mouse_y = cp.y;
	}
	else {
		OS::unclipCursor();
		OS::setMouseScreenPos(m_captured_mouse_x, m_captured_mouse_y);
	}
}

void GameView::onFullscreenGUI()
{
	processInputEvents();

	ImGuiIO& io = ImGui::GetIO();
	bool open = true;
	ImVec2 size = io.DisplaySize;
	ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
	ImGui::SetNextWindowSize(size);
	if (!ImGui::Begin("game view fullscreen",
		&open,
		ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
		ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
	{
		ImGui::End();
		return;
	}

	EntityPtr camera = m_pipeline->getScene()->getActiveCamera();
	if (camera.isValid()) {
		Viewport vp = m_pipeline->getScene()->getCameraViewport((EntityRef)camera);
		vp.w = (int)size.x;
		vp.h = (int)size.y;
		m_pipeline->getScene()->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
		m_pipeline->setViewport(vp);
		m_pipeline->render(false);
		const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
		if (gpu::isOriginBottomLeft())
		{
			ImGui::Image((void*)(uintptr_t)texture_handle.value, size, ImVec2(0, 1), ImVec2(1, 0));
		}
		else
		{
			ImGui::Image((void*)(uintptr_t)texture_handle.value, size);
		}
	}
	else {
		ImGui::Rect(size.x, size.y, 0xff0000FF);
	}
	m_pos = ImGui::GetItemRectMin();
	m_size = ImGui::GetItemRectSize();

	ImGui::End();

	if (m_is_fullscreen && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode()))
	{
		setFullscreen(false);
	}
}

void GameView::toggleFullscreen()
{
	if (!m_editor.isGameMode()) return;
	setFullscreen(!m_is_fullscreen);
}

void GameView::setFullscreen(bool fullscreen)
{
	captureMouse(fullscreen);
	m_studio_app.setFullscreen(fullscreen);
	m_is_fullscreen = fullscreen;
}

void GameView::onStatsGUI(const ImVec2& view_pos)
{
	if (!m_show_stats || !m_is_open) return;

	float toolbar_height = 24 + ImGui::GetStyle().FramePadding.y * 2;
	ImVec2 v = view_pos;
	v.x += ImGui::GetStyle().FramePadding.x;
	v.y += ImGui::GetStyle().FramePadding.y + toolbar_height;
	ImGui::SetNextWindowPos(v);
	auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
	col.w = 0.3f;
	ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
	ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
	if (ImGui::Begin("###stats_overlay", nullptr, flags)) {
		const auto& stats = m_pipeline->getStats();
		ImGui::LabelText("Draw calls", "%d", stats.draw_call_count);
		ImGui::LabelText("Instances", "%d", stats.instance_count);
		char buf[30];
		toCStringPretty(stats.triangle_count, Span(buf));
		ImGui::LabelText("Triangles", "%s", buf);
		ImGui::LabelText("Resolution", "%dx%d", (int)m_size.x, (int)m_size.y);
	}
	ImGui::End();
	ImGui::PopStyleColor();
}

void GameView::forceViewport(bool enable, int w, int h)
{
	m_forced_viewport.enabled = enable;
	m_forced_viewport.width = w;
	m_forced_viewport.height = h;
}

void GameView::processInputEvents()
{
	if (!m_is_mouse_captured) return;

	InputSystem& input = m_editor.getEngine().getInputSystem();
	const OS::Event* events = m_studio_app.getEvents();
	for (int i = 0, c = m_studio_app.getEventsCount(); i < c; ++i) {
		input.injectEvent(events[i]);
	}
}

void GameView::controlsGUI() {
	if (ImGui::Checkbox("Pause", &m_paused)) m_editor.getEngine().pause(m_paused);
	if (m_paused) {
		ImGui::SameLine();
		if (ImGui::Button("Next frame")) m_editor.getEngine().nextFrame();
	}
	ImGui::SameLine();
	ImGui::PushItemWidth(50);
	if (ImGui::DragFloat("Time multiplier", &m_time_multiplier, 0.01f, 0.01f, 30.0f)) {
		m_editor.getEngine().setTimeMultiplier(m_time_multiplier);
	}
	ImGui::PopItemWidth();
	if(m_editor.isGameMode()) {
		ImGui::SameLine();
		if (ImGui::Button("Fullscreen")) setFullscreen(true);
	}
	ImGui::SameLine();
	ImGui::Checkbox("Stats", &m_show_stats);
	ImGui::SameLine();
	m_pipeline->callLuaFunction("onGUI");
}

void GameView::onWindowGUI()
{
	PROFILE_FUNCTION();
	if (!m_pipeline->isReady()) {
		captureMouse(false);
		return;
	}

	ImGuiIO& io = ImGui::GetIO();
	if (m_is_mouse_captured && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode())) {
		captureMouse(false);
	}

	const char* window_name = "Game View###game_view";
	if (m_is_mouse_captured) window_name = "Game View (mouse captured)###game_view";

	if (m_is_fullscreen) {
		onFullscreenGUI();
		return;
	}

	if (!m_is_open) {
		captureMouse(false);
		return;
	}

	ImVec2 view_pos;
	bool is_game_view_visible = false;
	if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
		is_game_view_visible = true;
		view_pos = ImGui::GetCursorScreenPos();

		const ImVec2 content_min = ImGui::GetCursorScreenPos();
		ImVec2 size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing();
		ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
		if (m_forced_viewport.enabled) size = { (float)m_forced_viewport.width, (float)m_forced_viewport.height };
		if (size.x > 0 && size.y > 0) {
			RenderScene* scene = m_pipeline->getScene();
			const EntityPtr camera = scene->getActiveCamera();
			Viewport vp;
			vp.w = (int)size.x;
			vp.h = (int)size.y;
			if (camera.isValid()) {
				vp = scene->getCameraViewport((EntityRef)camera);
				scene->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
			}
			else {
				vp.fov = degreesToRadians(90.f);
				vp.is_ortho = false;
				vp.far = 10'000.f;
				vp.near = 1.f;
				vp.pos = DVec3(0);
				vp.rot = Quat(0, 0, 0, 1);
			}
			m_pipeline->setViewport(vp);
			m_pipeline->render(false);
			const gpu::TextureHandle texture_handle = m_pipeline->getOutput();

			if (texture_handle.isValid()) {
				if (gpu::isOriginBottomLeft()) {
					ImGui::Image((void*)(uintptr_t)texture_handle.value, size, ImVec2(0, 1), ImVec2(1, 0));
				}
				else {
					ImGui::Image((void*)(uintptr_t)texture_handle.value, size);
				}
			}
			else {
				ImGui::Rect(size.x, size.y, 0xffFF00FF);
			}
			const bool is_hovered = ImGui::IsItemHovered();
			if (is_hovered && ImGui::IsMouseClicked(0) && m_editor.isGameMode()) captureMouse(true);
			m_pos = ImGui::GetItemRectMin();
			m_size = ImGui::GetItemRectSize();

			if (m_is_mouse_captured && m_is_ingame_cursor) {
				OS::clipCursor(ImGui::GetWindowViewport()->PlatformHandle, (int)m_pos.x, (int)m_pos.y, (int)m_size.x, (int)m_size.y);
			}

			processInputEvents();
			controlsGUI();
		}

	}
	if (m_is_mouse_captured && OS::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) captureMouse(false);
	ImGui::End();
	if (is_game_view_visible) onStatsGUI(view_pos);
}

} // namespace Lumix
<commit_msg>fixed game view resolution<commit_after>#include <imgui/imgui.h>

#include "game_view.h"
#include "editor/asset_browser.h"
#include "editor/asset_compiler.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "editor/world_editor.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/geometry.h"
#include "engine/input_system.h"
#include "engine/lua_wrapper.h"
#include "engine/plugin_manager.h"
#include "engine/profiler.h"
#include "engine/resource_manager.h"
#include "engine/universe/universe.h"
#include "gui/gui_system.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"
#include "renderer/texture.h"

namespace Lumix
{

struct GUIInterface : GUISystem::Interface
{
	explicit GUIInterface(GameView& game_view)
		: m_game_view(game_view)
	{
	}

	Pipeline* getPipeline() override { return m_game_view.m_pipeline; }
	Vec2 getPos() const override { return m_game_view.m_pos; }
	Vec2 getSize() const override { return m_game_view.m_size; }

	void enableCursor(bool enable) override
	{
		m_game_view.enableIngameCursor(enable);
	}

	GameView& m_game_view;
};

GameView::GameView(StudioApp& app)
	: m_studio_app(app)
	, m_is_open(false)
	, m_is_fullscreen(false)
	, m_pipeline(nullptr)
	, m_is_mouse_captured(false)
	, m_is_ingame_cursor(false)
	, m_time_multiplier(1.0f)
	, m_paused(false)
	, m_show_stats(false)
	, m_gui_interface(nullptr)
	, m_editor(app.getWorldEditor())
{
	Engine& engine = app.getWorldEditor().getEngine();
	auto f = &LuaWrapper::wrapMethodClosure<&GameView::forceViewport>;
	LuaWrapper::createSystemClosure(engine.getState(), "GameView", this, "forceViewport", f);

	WorldEditor& editor = app.getWorldEditor();
	Action* action = LUMIX_NEW(editor.getAllocator(), Action)("Game View", "Toggle game view", "game_view");
	action->func.bind<&GameView::onAction>(this);
	action->is_selected.bind<&GameView::isOpen>(this);
	app.addWindowAction(action);

	Action* fullscreen_action = LUMIX_NEW(editor.getAllocator(), Action)("Game View fullscreen", "Game View fullscreen", "game_view_fullscreen");
	fullscreen_action->func.bind<&GameView::toggleFullscreen>(this);
	app.addAction(fullscreen_action);

	auto* renderer = (Renderer*)engine.getPluginManager().getPlugin("renderer");
	PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
	m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW", engine.getAllocator());

	editor.universeCreated().bind<&GameView::onUniverseCreated>(this);
	editor.universeDestroyed().bind<&GameView::onUniverseDestroyed>(this);
	if (editor.getUniverse()) onUniverseCreated();

	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui)
	{
		m_gui_interface = LUMIX_NEW(engine.getAllocator(), GUIInterface)(*this);
		gui->setInterface(m_gui_interface);
	}
}

GameView::~GameView()
{
	m_editor.universeCreated().unbind<&GameView::onUniverseCreated>(this);
	m_editor.universeDestroyed().unbind<&GameView::onUniverseDestroyed>(this);
	auto* gui = static_cast<GUISystem*>(m_editor.getEngine().getPluginManager().getPlugin("gui"));
	if (gui)
	{
		gui->setInterface(nullptr);
		LUMIX_DELETE(m_editor.getEngine().getAllocator(), m_gui_interface);
	}
	Pipeline::destroy(m_pipeline);
	m_pipeline = nullptr;

}

void GameView::enableIngameCursor(bool enable)
{
	m_is_ingame_cursor = enable;
	if (!m_is_mouse_captured) return;

	OS::showCursor(m_is_ingame_cursor);
}

void GameView::onUniverseCreated()
{
	auto* scene = m_editor.getUniverse()->getScene(crc32("renderer"));
	m_pipeline->setScene(static_cast<RenderScene*>(scene));
}

void GameView::onUniverseDestroyed()
{
	m_pipeline->setScene(nullptr);
}

void GameView::setScene(RenderScene* scene)
{
	m_pipeline->setScene(scene);
}

void GameView::captureMouse(bool capture)
{
	if (m_is_mouse_captured == capture) return;

	m_is_mouse_captured = capture;
	m_editor.getEngine().getInputSystem().enable(m_is_mouse_captured);
	OS::showCursor(!capture || m_is_ingame_cursor);

	if (capture) {
		const OS::Point cp = OS::getMouseScreenPos();
		m_captured_mouse_x = cp.x;
		m_captured_mouse_y = cp.y;
	}
	else {
		OS::unclipCursor();
		OS::setMouseScreenPos(m_captured_mouse_x, m_captured_mouse_y);
	}
}

void GameView::onFullscreenGUI()
{
	processInputEvents();

	ImGuiIO& io = ImGui::GetIO();
	bool open = true;
	ImVec2 size = io.DisplaySize;
	ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
	ImGui::SetNextWindowSize(size);
	if (!ImGui::Begin("game view fullscreen",
		&open,
		ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
		ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
	{
		ImGui::End();
		return;
	}

	EntityPtr camera = m_pipeline->getScene()->getActiveCamera();
	if (camera.isValid()) {
		Viewport vp = m_pipeline->getScene()->getCameraViewport((EntityRef)camera);
		vp.w = (int)size.x;
		vp.h = (int)size.y;
		m_pipeline->getScene()->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
		m_pipeline->setViewport(vp);
		m_pipeline->render(false);
		const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
		if (gpu::isOriginBottomLeft())
		{
			ImGui::Image((void*)(uintptr_t)texture_handle.value, size, ImVec2(0, 1), ImVec2(1, 0));
		}
		else
		{
			ImGui::Image((void*)(uintptr_t)texture_handle.value, size);
		}
	}
	else {
		ImGui::Rect(size.x, size.y, 0xff0000FF);
	}
	m_pos = ImGui::GetItemRectMin();
	m_size = ImGui::GetItemRectSize();

	ImGui::End();

	if (m_is_fullscreen && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode()))
	{
		setFullscreen(false);
	}
}

void GameView::toggleFullscreen()
{
	if (!m_editor.isGameMode()) return;
	setFullscreen(!m_is_fullscreen);
}

void GameView::setFullscreen(bool fullscreen)
{
	captureMouse(fullscreen);
	m_studio_app.setFullscreen(fullscreen);
	m_is_fullscreen = fullscreen;
}

void GameView::onStatsGUI(const ImVec2& view_pos)
{
	if (!m_show_stats || !m_is_open) return;

	float toolbar_height = 24 + ImGui::GetStyle().FramePadding.y * 2;
	ImVec2 v = view_pos;
	v.x += ImGui::GetStyle().FramePadding.x;
	v.y += ImGui::GetStyle().FramePadding.y + toolbar_height;
	ImGui::SetNextWindowPos(v);
	auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
	col.w = 0.3f;
	ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
	ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
	if (ImGui::Begin("###stats_overlay", nullptr, flags)) {
		const auto& stats = m_pipeline->getStats();
		ImGui::LabelText("Draw calls", "%d", stats.draw_call_count);
		ImGui::LabelText("Instances", "%d", stats.instance_count);
		char buf[30];
		toCStringPretty(stats.triangle_count, Span(buf));
		ImGui::LabelText("Triangles", "%s", buf);
		ImGui::LabelText("Resolution", "%dx%d", (int)m_size.x, (int)m_size.y);
	}
	ImGui::End();
	ImGui::PopStyleColor();
}

void GameView::forceViewport(bool enable, int w, int h)
{
	m_forced_viewport.enabled = enable;
	m_forced_viewport.width = w;
	m_forced_viewport.height = h;
}

void GameView::processInputEvents()
{
	if (!m_is_mouse_captured) return;

	InputSystem& input = m_editor.getEngine().getInputSystem();
	const OS::Event* events = m_studio_app.getEvents();
	for (int i = 0, c = m_studio_app.getEventsCount(); i < c; ++i) {
		input.injectEvent(events[i]);
	}
}

void GameView::controlsGUI() {
	if (ImGui::Checkbox("Pause", &m_paused)) m_editor.getEngine().pause(m_paused);
	if (m_paused) {
		ImGui::SameLine();
		if (ImGui::Button("Next frame")) m_editor.getEngine().nextFrame();
	}
	ImGui::SameLine();
	ImGui::PushItemWidth(50);
	if (ImGui::DragFloat("Time multiplier", &m_time_multiplier, 0.01f, 0.01f, 30.0f)) {
		m_editor.getEngine().setTimeMultiplier(m_time_multiplier);
	}
	ImGui::PopItemWidth();
	if(m_editor.isGameMode()) {
		ImGui::SameLine();
		if (ImGui::Button("Fullscreen")) setFullscreen(true);
	}
	ImGui::SameLine();
	ImGui::Checkbox("Stats", &m_show_stats);
	ImGui::SameLine();
	m_pipeline->callLuaFunction("onGUI");
}

void GameView::onWindowGUI()
{
	PROFILE_FUNCTION();
	if (!m_pipeline->isReady()) {
		captureMouse(false);
		return;
	}

	ImGuiIO& io = ImGui::GetIO();
	if (m_is_mouse_captured && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode())) {
		captureMouse(false);
	}

	const char* window_name = "Game View###game_view";
	if (m_is_mouse_captured) window_name = "Game View (mouse captured)###game_view";

	if (m_is_fullscreen) {
		onFullscreenGUI();
		return;
	}

	if (!m_is_open) {
		captureMouse(false);
		return;
	}

	ImVec2 view_pos;
	bool is_game_view_visible = false;
	if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
		is_game_view_visible = true;
		view_pos = ImGui::GetCursorScreenPos();

		const ImVec2 content_min = ImGui::GetCursorScreenPos();
		ImVec2 size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing();
		ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
		if (m_forced_viewport.enabled) size = { (float)m_forced_viewport.width, (float)m_forced_viewport.height };
		if (size.x > 0 && size.y > 0) {
			RenderScene* scene = m_pipeline->getScene();
			const EntityPtr camera = scene->getActiveCamera();
			Viewport vp;
			if (camera.isValid()) {
				vp = scene->getCameraViewport((EntityRef)camera);
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				scene->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
			}
			else {
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				vp.fov = degreesToRadians(90.f);
				vp.is_ortho = false;
				vp.far = 10'000.f;
				vp.near = 1.f;
				vp.pos = DVec3(0);
				vp.rot = Quat(0, 0, 0, 1);
			}
			m_pipeline->setViewport(vp);
			m_pipeline->render(false);
			const gpu::TextureHandle texture_handle = m_pipeline->getOutput();

			if (texture_handle.isValid()) {
				if (gpu::isOriginBottomLeft()) {
					ImGui::Image((void*)(uintptr_t)texture_handle.value, size, ImVec2(0, 1), ImVec2(1, 0));
				}
				else {
					ImGui::Image((void*)(uintptr_t)texture_handle.value, size);
				}
			}
			else {
				ImGui::Rect(size.x, size.y, 0xffFF00FF);
			}
			const bool is_hovered = ImGui::IsItemHovered();
			if (is_hovered && ImGui::IsMouseClicked(0) && m_editor.isGameMode()) captureMouse(true);
			m_pos = ImGui::GetItemRectMin();
			m_size = ImGui::GetItemRectSize();

			if (m_is_mouse_captured && m_is_ingame_cursor) {
				OS::clipCursor(ImGui::GetWindowViewport()->PlatformHandle, (int)m_pos.x, (int)m_pos.y, (int)m_size.x, (int)m_size.y);
			}

			processInputEvents();
			controlsGUI();
		}

	}
	if (m_is_mouse_captured && OS::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) captureMouse(false);
	ImGui::End();
	if (is_game_view_visible) onStatsGUI(view_pos);
}

} // namespace Lumix
<|endoftext|>


<commit_before>#ifndef RENHOOK_HOOKS_INLINE_HOOK_H
#define RENHOOK_HOOKS_INLINE_HOOK_H

#include <string>
#include <Windows.h>

#include <renhook/exception.hpp>
#include <renhook/executable.hpp>
#include <renhook/hook_writer.hpp>
#include <renhook/pattern.hpp>
#include <renhook/suspend_threads.hpp>
#include <renhook/zydis.hpp>

#include <renhook/memory/memory_allocator.hpp>
#include <renhook/memory/virtual_protect.hpp>

namespace renhook
{
    /**
     * @brief An inline hook.
     *
     * @tparam T The hooked function type.
     */
    template<typename T>
    class inline_hook
    {
    public:

        /**
         * @brief Construct an empty hook.
         */
        inline_hook()
            : m_target_address(0)
            , m_detour_address(0)
            , m_wildcard(0)
            , m_offset(0)
            , m_attached(false)
            , m_decoded_length(0)
            , m_block(nullptr)
        {
        }

        /**
         * @brief Construct a new hook.
         *
         * @param target_address[in] The address of the function that will be hooked.
         * @param detour_address[in] The address of the callback.
         */
        inline_hook(uintptr_t target_address, uintptr_t detour_address)
            : inline_hook()
        {
            m_target_address = target_address;
            m_detour_address = detour_address;
        }

        /**
         * @brief Construct a new hook.
         *
         * @param target_address[in] The address of the function that will be hooked.
         * @param detour_address[in] The address of the callback.
         */
        inline_hook(uintptr_t target_address, T detour_address)
            : inline_hook(target_address, reinterpret_cast<uintptr_t>(detour_address))
        {
        }

        /**
         * @brief Construct a new hook using a pattern.
         *
         * @param pattern[in]           The pattern of the function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         * @param wildcard[in]          The wildcard for #pattern.
         * @param offset[in]            The offset of the #pattern.
         */
        inline_hook(pattern pattern, uintptr_t detour_address, uint8_t wildcard = 0xCC, size_t offset = 0)
            : inline_hook()
        {
            m_detour_address = detour_address;
            m_pattern = std::move(pattern);
            m_wildcard = wildcard;
            m_offset = offset;
        }

        /**
         * @brief Construct a new hook using a pattern.
         *
         * @param pattern[in]           The pattern of the function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         * @param wildcard[in]          The wildcard for #pattern.
         * @param offset[in]            The offset of the #pattern.
         */
        inline_hook(pattern pattern, T detour_address, uint8_t wildcard = 0xCC, size_t offset = 0)
            : inline_hook(pattern, reinterpret_cast<uintptr_t>(detour_address), wildcard, offset)
        {
        }

        /**
         * @brief Construct a new hook.
         *
         * @param module[in]            The module which contain the #function.
         * @param function[in]          The function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         *
         * @note If the module is not loaded, the library will load it when the hook is attached.
         */
        inline_hook(const std::string& module, const std::string& function, uintptr_t detour_address)
            : inline_hook()
        {
            m_module = module;
            m_function = function;
        }

        /**
         * @brief Construct a new hook.
         *
         * @param module[in]            The module which contain the #function.
         * @param function[in]          The function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         *
         * @note If the module is not loaded, the library will load it when the hook is attached.
         */
        inline_hook(const std::string& module, const std::string& function, T detour_address)
            : inline_hook(module, function, reinterpret_cast<uintptr_t>(detour_address))
        {
        }

        inline_hook(inline_hook&& rhs) noexcept
            : m_target_address(rhs.m_target_address)
            , m_detour_address(rhs.m_detour_address)
            , m_pattern(std::move(rhs.m_pattern))
            , m_wildcard(rhs.m_wildcard)
            , m_offset(rhs.m_offset)
            , m_attached(rhs.m_attached)
            , m_decoded_length(rhs.m_decoded_length)
            , m_block(rhs.m_block)
        {
            rhs.m_attached = false;
            rhs.m_block = nullptr;
        }

        ~inline_hook()
        {
            detach();
        }

        inline_hook& operator=(inline_hook&& rhs) noexcept
        {
            m_target_address = rhs.m_target_address;
            m_detour_address = rhs.m_detour_address;
            m_pattern = std::move(rhs.m_pattern);
            m_wildcard = rhs.m_wildcard;
            m_offset = rhs.m_offset;
            m_attached = rhs.m_attached;
            m_decoded_length = rhs.m_decoded_length;
            m_block = rhs.m_block;

            rhs.m_attached = false;
            rhs.m_block = nullptr;

            return *this;
        }

        inline_hook(inline_hook&) = delete;
        inline_hook& operator=(const inline_hook&) = delete;

        /**
         * @brief Call the original function.
         *
         * @return The value returned by the original function.
         */
        operator T() const
        {
            return reinterpret_cast<T>(m_block);
        }

        /**
         * @brief Enable the hook.
         */
        void attach()
        {
            using namespace renhook::memory;

            if (m_attached)
            {
                return;
            }

            if (!m_module.empty())
            {
                auto module_handle = GetModuleHandleA(m_module.c_str());
                if (!module_handle)
                {
                    LoadLibraryA(m_module.c_str());

                    module_handle = GetModuleHandleA(m_module.c_str());
                    if (!module_handle)
                    {
                        throw renhook::exception("module not found");
                    }
                }

                m_target_address = reinterpret_cast<uintptr_t>(GetProcAddress(module_handle, m_function.c_str()));
                if (m_target_address == 0)
                {
                    throw renhook::exception("cannot find function in module");
                }
            }

            if (m_target_address == 0)
            {
                if (m_pattern.empty())
                {
                    throw renhook::exception("cannot attach an empty hook");
                }

                auto addresses = m_pattern.find(m_wildcard);
                m_target_address = addresses.at(m_offset);
            }

            m_target_address = skip_jumps(m_target_address);
            m_detour_address = skip_jumps(m_detour_address);

            renhook::zydis zydis;
            auto instructions = zydis.decode(m_target_address, executable::get_code_size(), hook_size, m_decoded_length);

            suspend_threads threads(m_target_address, m_decoded_length);

            extern memory_allocator global_allocator;
            m_block = static_cast<uint8_t*>(global_allocator.alloc());

            // Write the bytes to our memory.
            virtual_protect block_protection(m_block, memory_allocator::block_size, protection::write);
            hook_writer block_writer(m_block);

            block_writer.copy_from(m_target_address, m_decoded_length);
            block_writer.write_jump(m_target_address + m_decoded_length);

            relocate_instructions(instructions, &block_writer);

            // Write the jump in the original function.
            virtual_protect func_protection(m_target_address, m_decoded_length, protection::write);
            hook_writer func_writer(m_target_address);

            func_writer.write_jump(m_detour_address);
            func_writer.write_nops(m_decoded_length - hook_size);

            flush_cache();
            m_attached = true;
        }

        /**
         * @brief Disable the hook.
         */
        void detach()
        {
            using namespace renhook::memory;

            if (!m_attached)
            {
                return;
            }

            suspend_threads threads(m_target_address, m_decoded_length);
            virtual_protect func_protection(m_target_address, m_decoded_length, protection::write);

            hook_writer func_writer(m_target_address);
            func_writer.copy_from(m_block, m_decoded_length);

            renhook::zydis zydis;
            auto instructions = zydis.decode(reinterpret_cast<uintptr_t>(m_block), executable::get_code_size(), m_decoded_length, m_decoded_length);

            relocate_instructions(instructions, nullptr);

            extern memory_allocator global_allocator;
            global_allocator.free(m_block);

            m_block = nullptr;

            flush_cache();
            m_attached = false;
        }

    protected:

        /**
         * @brief Get the block address.
         *
         * @return The block address.
         *
         * @note This is only used in tests.
         */
        const uint8_t* get_block_address() const
        {
            return m_block;
        }

    private:

        /**
         * @brief The size of the hook.
         */
#ifdef _WIN64
        static constexpr size_t hook_size = 14;
#else
        static constexpr size_t hook_size = 5;
#endif

        /**
         * @brief Check if the first instruction is a jump, if it is follow it until the real address of the function is found.
         *
         * @param address[in] The address to check.
         *
         * @return The real function's address.
         */
        uintptr_t skip_jumps(uintptr_t address) const
        {
            if (address == 0)
            {
                return 0;
            }

            auto memory = reinterpret_cast<uint8_t*>(address);
            if (memory[0] == 0xEB)
            {
                // We have a 8-bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 2 + *reinterpret_cast<int8_t*>(&memory[1]));
                return skip_jumps(address);
            }
            else if (memory[0] == 0xE9)
            {
                // We have a 32-bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 5 + *reinterpret_cast<int32_t*>(&memory[1]));
                return skip_jumps(address);
            }
            else if (memory[0] == 0xFF && memory[1] == 0x25)
            {
#ifdef _WIN64
                // We have a 32bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 6 + *reinterpret_cast<int32_t*>(&memory[2]));
#else
                // We have an absolute pointer.
                address = *reinterpret_cast<uintptr_t*>(&memory[2]);
#endif

                return skip_jumps(address);
            }
#ifdef _WIN64
            else if (memory[0] == 0x48 && memory[1] == 0xFF && memory[2] == 0x25)
            {
                // We have a 32bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 7 + *reinterpret_cast<int32_t*>(&memory[3]));
                return skip_jumps(address);
            }
#endif

            return address;
        }

        /**
         * @brief Relocate all EIP / RIP instructions.
         *
         * @param instructions[in] An array of decoded instructions.
         * @param block_writer[in] The writer of the block (only necessary if the hook is attached).
         */
        void relocate_instructions(std::vector<zydis::instruction>& instructions, hook_writer* block_writer)
        {
            auto instr_address = m_attached ? m_target_address : reinterpret_cast<uintptr_t>(m_block);
            size_t index = 0;

            for (auto& instr : instructions)
            {
                // Check if it is a conditional jump.
                if (instr.is_relative &&
                    ((instr.decoded.opcode & 0xF0) == 0x70 ||
                     (instr.decoded.opcode_map == ZYDIS_OPCODE_MAP_0F && (instr.decoded.opcode & 0xF0) == 0x80) ||
                      instr.decoded.mnemonic == ZYDIS_MNEMONIC_CALL))
                {
                    // Calculate where the displacement is in instruction.
                    auto disp_address = instr_address + instr.disp.offset;

#ifdef _WIN64
                    constexpr size_t jmp_size = 14;
                    constexpr size_t jmp_instr_size = 6;
#else
                    constexpr size_t jmp_size = 5;
                    constexpr size_t jmp_instr_size = 1;
#endif

                    auto table_address = m_block + m_decoded_length + hook_size;

                    // The address of the jump instruction in jump table for the current instruction.
                    auto jmp_instr_address = table_address + (jmp_size * index);

                    // Create a jump table if it is not attached, else get the real address from jump table.
                    if (!m_attached)
                    {
                        block_writer->write_jump(instr.disp.absolute_address);
                        instr.disp.absolute_address = reinterpret_cast<uintptr_t>(jmp_instr_address);
                    }
                    else
                    {
                        instr.disp.absolute_address = *reinterpret_cast<uintptr_t*>(jmp_instr_address + jmp_instr_size);

#ifndef _WIN64
                        // On x86 we have a displacement instead of absolute address.
                        instr.disp.absolute_address += reinterpret_cast<uintptr_t>(jmp_instr_address) + jmp_size;
#endif
                    }

                    switch (instr.disp.size)
                    {
                        case 8:
                        {
                            *reinterpret_cast<int8_t*>(disp_address) = static_cast<int8_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                        case 16:
                        {
                            *reinterpret_cast<int16_t*>(disp_address) = static_cast<int16_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                        case 32:
                        {
                            *reinterpret_cast<int32_t*>(disp_address) = static_cast<int32_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                    }

                    index++;
                }

                instr_address += instr.decoded.length;
            }
        }

        /**
         * @brief Flush instruction cache for the original function and for the codecave.
         */
        void flush_cache() const
        {
            auto current_process = GetCurrentProcess();

            FlushInstructionCache(current_process, m_block, memory::memory_allocator::block_size);
            FlushInstructionCache(current_process, reinterpret_cast<void*>(m_target_address), m_decoded_length);
        }

        uintptr_t m_target_address;
        uintptr_t m_detour_address;

        pattern m_pattern;
        uint8_t m_wildcard;
        size_t m_offset;

        std::string m_module;
        std::string m_function;

        bool m_attached;

        size_t m_decoded_length;
        uint8_t* m_block;
    };
}
#endif
<commit_msg>Change function and block protection to RWX when writing the hook<commit_after>#ifndef RENHOOK_HOOKS_INLINE_HOOK_H
#define RENHOOK_HOOKS_INLINE_HOOK_H

#include <string>
#include <Windows.h>

#include <renhook/exception.hpp>
#include <renhook/executable.hpp>
#include <renhook/hook_writer.hpp>
#include <renhook/pattern.hpp>
#include <renhook/suspend_threads.hpp>
#include <renhook/zydis.hpp>

#include <renhook/memory/memory_allocator.hpp>
#include <renhook/memory/virtual_protect.hpp>

namespace renhook
{
    /**
     * @brief An inline hook.
     *
     * @tparam T The hooked function type.
     */
    template<typename T>
    class inline_hook
    {
    public:

        /**
         * @brief Construct an empty hook.
         */
        inline_hook()
            : m_target_address(0)
            , m_detour_address(0)
            , m_wildcard(0)
            , m_offset(0)
            , m_attached(false)
            , m_decoded_length(0)
            , m_block(nullptr)
        {
        }

        /**
         * @brief Construct a new hook.
         *
         * @param target_address[in] The address of the function that will be hooked.
         * @param detour_address[in] The address of the callback.
         */
        inline_hook(uintptr_t target_address, uintptr_t detour_address)
            : inline_hook()
        {
            m_target_address = target_address;
            m_detour_address = detour_address;
        }

        /**
         * @brief Construct a new hook.
         *
         * @param target_address[in] The address of the function that will be hooked.
         * @param detour_address[in] The address of the callback.
         */
        inline_hook(uintptr_t target_address, T detour_address)
            : inline_hook(target_address, reinterpret_cast<uintptr_t>(detour_address))
        {
        }

        /**
         * @brief Construct a new hook using a pattern.
         *
         * @param pattern[in]           The pattern of the function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         * @param wildcard[in]          The wildcard for #pattern.
         * @param offset[in]            The offset of the #pattern.
         */
        inline_hook(pattern pattern, uintptr_t detour_address, uint8_t wildcard = 0xCC, size_t offset = 0)
            : inline_hook()
        {
            m_detour_address = detour_address;
            m_pattern = std::move(pattern);
            m_wildcard = wildcard;
            m_offset = offset;
        }

        /**
         * @brief Construct a new hook using a pattern.
         *
         * @param pattern[in]           The pattern of the function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         * @param wildcard[in]          The wildcard for #pattern.
         * @param offset[in]            The offset of the #pattern.
         */
        inline_hook(pattern pattern, T detour_address, uint8_t wildcard = 0xCC, size_t offset = 0)
            : inline_hook(pattern, reinterpret_cast<uintptr_t>(detour_address), wildcard, offset)
        {
        }

        /**
         * @brief Construct a new hook.
         *
         * @param module[in]            The module which contain the #function.
         * @param function[in]          The function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         *
         * @note If the module is not loaded, the library will load it when the hook is attached.
         */
        inline_hook(const std::string& module, const std::string& function, uintptr_t detour_address)
            : inline_hook()
        {
            m_module = module;
            m_function = function;
        }

        /**
         * @brief Construct a new hook.
         *
         * @param module[in]            The module which contain the #function.
         * @param function[in]          The function that will be hooked.
         * @param detour_address[in]    The address of the callback.
         *
         * @note If the module is not loaded, the library will load it when the hook is attached.
         */
        inline_hook(const std::string& module, const std::string& function, T detour_address)
            : inline_hook(module, function, reinterpret_cast<uintptr_t>(detour_address))
        {
        }

        inline_hook(inline_hook&& rhs) noexcept
            : m_target_address(rhs.m_target_address)
            , m_detour_address(rhs.m_detour_address)
            , m_pattern(std::move(rhs.m_pattern))
            , m_wildcard(rhs.m_wildcard)
            , m_offset(rhs.m_offset)
            , m_attached(rhs.m_attached)
            , m_decoded_length(rhs.m_decoded_length)
            , m_block(rhs.m_block)
        {
            rhs.m_attached = false;
            rhs.m_block = nullptr;
        }

        ~inline_hook()
        {
            detach();
        }

        inline_hook& operator=(inline_hook&& rhs) noexcept
        {
            m_target_address = rhs.m_target_address;
            m_detour_address = rhs.m_detour_address;
            m_pattern = std::move(rhs.m_pattern);
            m_wildcard = rhs.m_wildcard;
            m_offset = rhs.m_offset;
            m_attached = rhs.m_attached;
            m_decoded_length = rhs.m_decoded_length;
            m_block = rhs.m_block;

            rhs.m_attached = false;
            rhs.m_block = nullptr;

            return *this;
        }

        inline_hook(inline_hook&) = delete;
        inline_hook& operator=(const inline_hook&) = delete;

        /**
         * @brief Call the original function.
         *
         * @return The value returned by the original function.
         */
        operator T() const
        {
            return reinterpret_cast<T>(m_block);
        }

        /**
         * @brief Enable the hook.
         */
        void attach()
        {
            using namespace renhook::memory;

            if (m_attached)
            {
                return;
            }

            if (!m_module.empty())
            {
                auto module_handle = GetModuleHandleA(m_module.c_str());
                if (!module_handle)
                {
                    LoadLibraryA(m_module.c_str());

                    module_handle = GetModuleHandleA(m_module.c_str());
                    if (!module_handle)
                    {
                        throw renhook::exception("module not found");
                    }
                }

                m_target_address = reinterpret_cast<uintptr_t>(GetProcAddress(module_handle, m_function.c_str()));
                if (m_target_address == 0)
                {
                    throw renhook::exception("cannot find function in module");
                }
            }

            if (m_target_address == 0)
            {
                if (m_pattern.empty())
                {
                    throw renhook::exception("cannot attach an empty hook");
                }

                auto addresses = m_pattern.find(m_wildcard);
                m_target_address = addresses.at(m_offset);
            }

            m_target_address = skip_jumps(m_target_address);
            m_detour_address = skip_jumps(m_detour_address);

            renhook::zydis zydis;
            auto instructions = zydis.decode(m_target_address, executable::get_code_size(), hook_size, m_decoded_length);

            suspend_threads threads(m_target_address, m_decoded_length);

            extern memory_allocator global_allocator;
            m_block = static_cast<uint8_t*>(global_allocator.alloc());

            // Write the bytes to our memory.
            virtual_protect block_protection(m_block, memory_allocator::block_size, protection::read | protection::write | protection::execute);
            hook_writer block_writer(m_block);

            block_writer.copy_from(m_target_address, m_decoded_length);
            block_writer.write_jump(m_target_address + m_decoded_length);

            relocate_instructions(instructions, &block_writer);

            // Write the jump in the original function.
            virtual_protect func_protection(m_target_address, m_decoded_length, protection::read | protection::write | protection::execute);
            hook_writer func_writer(m_target_address);

            func_writer.write_jump(m_detour_address);
            func_writer.write_nops(m_decoded_length - hook_size);

            flush_cache();
            m_attached = true;
        }

        /**
         * @brief Disable the hook.
         */
        void detach()
        {
            using namespace renhook::memory;

            if (!m_attached)
            {
                return;
            }

            suspend_threads threads(m_target_address, m_decoded_length);
            virtual_protect func_protection(m_target_address, m_decoded_length, protection::read | protection::write | protection::execute);

            hook_writer func_writer(m_target_address);
            func_writer.copy_from(m_block, m_decoded_length);

            renhook::zydis zydis;
            auto instructions = zydis.decode(reinterpret_cast<uintptr_t>(m_block), executable::get_code_size(), m_decoded_length, m_decoded_length);

            relocate_instructions(instructions, nullptr);

            extern memory_allocator global_allocator;
            global_allocator.free(m_block);

            m_block = nullptr;

            flush_cache();
            m_attached = false;
        }

    protected:

        /**
         * @brief Get the block address.
         *
         * @return The block address.
         *
         * @note This is only used in tests.
         */
        const uint8_t* get_block_address() const
        {
            return m_block;
        }

    private:

        /**
         * @brief The size of the hook.
         */
#ifdef _WIN64
        static constexpr size_t hook_size = 14;
#else
        static constexpr size_t hook_size = 5;
#endif

        /**
         * @brief Check if the first instruction is a jump, if it is follow it until the real address of the function is found.
         *
         * @param address[in] The address to check.
         *
         * @return The real function's address.
         */
        uintptr_t skip_jumps(uintptr_t address) const
        {
            if (address == 0)
            {
                return 0;
            }

            auto memory = reinterpret_cast<uint8_t*>(address);
            if (memory[0] == 0xEB)
            {
                // We have a 8-bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 2 + *reinterpret_cast<int8_t*>(&memory[1]));
                return skip_jumps(address);
            }
            else if (memory[0] == 0xE9)
            {
                // We have a 32-bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 5 + *reinterpret_cast<int32_t*>(&memory[1]));
                return skip_jumps(address);
            }
            else if (memory[0] == 0xFF && memory[1] == 0x25)
            {
#ifdef _WIN64
                // We have a 32bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 6 + *reinterpret_cast<int32_t*>(&memory[2]));
#else
                // We have an absolute pointer.
                address = *reinterpret_cast<uintptr_t*>(&memory[2]);
#endif

                return skip_jumps(address);
            }
#ifdef _WIN64
            else if (memory[0] == 0x48 && memory[1] == 0xFF && memory[2] == 0x25)
            {
                // We have a 32bit offset to the target.
                address = reinterpret_cast<uintptr_t>(memory + 7 + *reinterpret_cast<int32_t*>(&memory[3]));
                return skip_jumps(address);
            }
#endif

            return address;
        }

        /**
         * @brief Relocate all EIP / RIP instructions.
         *
         * @param instructions[in] An array of decoded instructions.
         * @param block_writer[in] The writer of the block (only necessary if the hook is attached).
         */
        void relocate_instructions(std::vector<zydis::instruction>& instructions, hook_writer* block_writer)
        {
            auto instr_address = m_attached ? m_target_address : reinterpret_cast<uintptr_t>(m_block);
            size_t index = 0;

            for (auto& instr : instructions)
            {
                // Check if it is a conditional jump.
                if (instr.is_relative &&
                    ((instr.decoded.opcode & 0xF0) == 0x70 ||
                     (instr.decoded.opcode_map == ZYDIS_OPCODE_MAP_0F && (instr.decoded.opcode & 0xF0) == 0x80) ||
                      instr.decoded.mnemonic == ZYDIS_MNEMONIC_CALL))
                {
                    // Calculate where the displacement is in instruction.
                    auto disp_address = instr_address + instr.disp.offset;

#ifdef _WIN64
                    constexpr size_t jmp_size = 14;
                    constexpr size_t jmp_instr_size = 6;
#else
                    constexpr size_t jmp_size = 5;
                    constexpr size_t jmp_instr_size = 1;
#endif

                    auto table_address = m_block + m_decoded_length + hook_size;

                    // The address of the jump instruction in jump table for the current instruction.
                    auto jmp_instr_address = table_address + (jmp_size * index);

                    // Create a jump table if it is not attached, else get the real address from jump table.
                    if (!m_attached)
                    {
                        block_writer->write_jump(instr.disp.absolute_address);
                        instr.disp.absolute_address = reinterpret_cast<uintptr_t>(jmp_instr_address);
                    }
                    else
                    {
                        instr.disp.absolute_address = *reinterpret_cast<uintptr_t*>(jmp_instr_address + jmp_instr_size);

#ifndef _WIN64
                        // On x86 we have a displacement instead of absolute address.
                        instr.disp.absolute_address += reinterpret_cast<uintptr_t>(jmp_instr_address) + jmp_size;
#endif
                    }

                    switch (instr.disp.size)
                    {
                        case 8:
                        {
                            *reinterpret_cast<int8_t*>(disp_address) = static_cast<int8_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                        case 16:
                        {
                            *reinterpret_cast<int16_t*>(disp_address) = static_cast<int16_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                        case 32:
                        {
                            *reinterpret_cast<int32_t*>(disp_address) = static_cast<int32_t>(utils::calculate_displacement(instr_address, instr.disp.absolute_address, instr.decoded.length));
                            break;
                        }
                    }

                    index++;
                }

                instr_address += instr.decoded.length;
            }
        }

        /**
         * @brief Flush instruction cache for the original function and for the codecave.
         */
        void flush_cache() const
        {
            auto current_process = GetCurrentProcess();

            FlushInstructionCache(current_process, m_block, memory::memory_allocator::block_size);
            FlushInstructionCache(current_process, reinterpret_cast<void*>(m_target_address), m_decoded_length);
        }

        uintptr_t m_target_address;
        uintptr_t m_detour_address;

        pattern m_pattern;
        uint8_t m_wildcard;
        size_t m_offset;

        std::string m_module;
        std::string m_function;

        bool m_attached;

        size_t m_decoded_length;
        uint8_t* m_block;
    };
}
#endif
<|endoftext|>