text: /*
  * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  * its licensors.
  *
  * For complete copyright and license terms please see the LICENSE at the root of this
  * distribution (the "License"). All use of this software is governed by the License,
  * or, if provided, by the license below or the license accompanying this file. Do not
  * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *
  */
  // Original file Copyright Crytek GMBH or its affiliates, used under license.
  
  // Description : XML parsing to load a font.
  
  
  #include "StdAfx.h"
  
  #if !defined(USE_NULLFONT_ALWAYS)
  
  #include "FFont.h"
  #include "FontTexture.h"
  #include <Cry_Math.h>
  
  #if defined(WIN32) || defined(WIN64)
  #   include <shlobj.h>
  #   include <StringUtils.h>
  #endif
  
  //////////////////////////////////////////////////////////////////////////
  // Xml parser implementation
  
  enum
  {
      ELEMENT_UNKNOWN              = 0,
      ELEMENT_FONT                     = 1,
      ELEMENT_EFFECT               = 2,
      ELEMENT_PASS                     = 4,
      ELEMENT_PASS_COLOR       = 5,
      ELEMENT_PASS_POSOFFSET = 12,
      ELEMENT_PASS_BLEND       = 14
  };
  
  static inline int GetBlendModeFromString(const string& str, bool dst)
  {
      int blend = GS_BLSRC_ONE;
  
      if (str == "zero")
      {
          blend = dst ? GS_BLDST_ZERO : GS_BLSRC_ZERO;
      }
      else if (str == "one")
      {
          blend = dst ? GS_BLDST_ONE : GS_BLSRC_ONE;
      }
      else if (str == "srcalpha" ||
               str == "src_alpha")
      {
          blend = dst ? GS_BLDST_SRCALPHA : GS_BLSRC_SRCALPHA;
      }
      else if (str == "invsrcalpha" ||
               str == "inv_src_alpha")
      {
          blend = dst ? GS_BLDST_ONEMINUSSRCALPHA : GS_BLSRC_ONEMINUSSRCALPHA;
      }
      else if (str == "dstalpha" ||
               str == "dst_alpha")
      {
          blend = dst ? GS_BLDST_DSTALPHA : GS_BLSRC_DSTALPHA;
      }
      else if (str == "invdstalpha" ||
               str == "inv_dst_alpha")
      {
          blend = dst ? GS_BLDST_ONEMINUSDSTALPHA : GS_BLSRC_ONEMINUSDSTALPHA;
      }
      else if (str == "dstcolor" ||
               str == "dst_color")
      {
          blend = GS_BLSRC_DSTCOL;
      }
      else if (str == "srccolor" ||
               str == "src_color")
      {
          blend = GS_BLDST_SRCCOL;
      }
      else if (str == "invdstcolor" ||
               str == "inv_dst_color")
      {
          blend = GS_BLSRC_ONEMINUSDSTCOL;
      }
      else if (str == "invsrccolor" ||
               str == "inv_src_color")
      {
          blend = GS_BLDST_ONEMINUSSRCCOL;
      }
  
      return blend;
  }
  
  class CXmlFontShader
  {
  public:
      CXmlFontShader(CFFont* pFont)
      {
          m_pFont = pFont;
          m_nElement = ELEMENT_UNKNOWN;
          m_pEffect = NULL;
          m_pPass = NULL;
          m_FontTexSize.set(0, 0);
  
          static const int defaultSlotWidthSize = 16;
          static const int defaultSlotHeightSize = 8;
          m_SlotSizes.set(defaultSlotWidthSize, defaultSlotHeightSize);
  
          m_bNoRescale = false;
          m_FontSmoothAmount = 0;
          m_FontSmoothMethod = FONT_SMOOTH_NONE;
      }
  
      ~CXmlFontShader()
      {
      }
  
      void ScanXmlNodesRecursively(XmlNodeRef node)
      {
          if (!node)
          {
              return;
          }
  
          FoundElement(node->getTag());
  
          for (int i = 0, count = node->getNumAttributes(); i < count; ++i)
          {
              const char* key = "";
              const char* value = "";
              if (node->getAttributeByIndex(i, &key, &value))
              {
                  FoundAttribute(key, value);
              }
          }
  
          for (int i = 0, count = node->getChildCount(); i < count; ++i)
          {
              XmlNodeRef child = node->getChild(i);
              ScanXmlNodesRecursively(child);
          }
      }
  
  private:
      // notify methods
      void FoundElement(const string& name)
      {
          //MessageBox(NULL, string("[" + name + "]").c_str(), "FoundElement", MB_OK);
          // process the previous element
          switch (m_nElement)
          {
          case ELEMENT_FONT:
          {
              if (!m_FontTexSize.x || !m_FontTexSize.y)
              {
                  m_FontTexSize.set(512, 512);
              }
  
              bool fontLoaded = m_pFont->Load(m_strFontPath.c_str(), m_FontTexSize.x, m_FontTexSize.y, m_SlotSizes.x, m_SlotSizes.y, TTFFLAG_CREATE(m_FontSmoothMethod, m_FontSmoothAmount));
  #if defined(WIN32) || defined(WIN64)
              if (!fontLoaded)
              {
                  TCHAR sysFontPath[MAX_PATH];
                  if (SUCCEEDED(SHGetFolderPath(0, CSIDL_FONTS, 0, SHGFP_TYPE_DEFAULT, sysFontPath)))
                  {
                      const char* pFontPath = m_strFontPath.c_str();
                      const char* pFontName = CryStringUtils::FindFileNameInPath(pFontPath);
  
                      string newFontPath(sysFontPath);
                      newFontPath += "/";
                      newFontPath += pFontName;
                      m_pFont->Load(newFontPath, m_FontTexSize.x, m_FontTexSize.y, m_SlotSizes.x, m_SlotSizes.y, TTFFLAG_CREATE(m_FontSmoothMethod, m_FontSmoothAmount));
                  }
              }
  #endif
          }
          break;
  
          default:
              break;
          }
  
          // Translate the m_nElement name to a define
          if (name == "font")
          {
              m_nElement = ELEMENT_FONT;
          }
          else if (name == "effect")
          {
              m_nElement = ELEMENT_EFFECT;
          }
          else if (name == "pass")
          {
              m_pPass = NULL;
              m_nElement = ELEMENT_PASS;
              if (m_pEffect)
              {
                  m_pPass = m_pEffect->AddPass();
              }
          }
          else if (name == "color")
          {
              m_nElement = ELEMENT_PASS_COLOR;
          }
          else if (name == "pos" ||
                   name == "offset")
          {
              m_nElement = ELEMENT_PASS_POSOFFSET;
          }
          else if (name == "blend" ||
                   name == "blending")
          {
              m_nElement = ELEMENT_PASS_BLEND;
          }
          else
          {
              m_nElement = ELEMENT_UNKNOWN;
          }
      }
  
      void FoundAttribute(const string& name, const string& value)
      {
          //MessageBox(NULL, string(name + "\n" + value).c_str(), "FoundAttribute", MB_OK);
          switch (m_nElement)
          {
          case ELEMENT_FONT:
              if (name == "path")
              {
                  m_strFontPath = value;
              }
              else if (name == "w")
              {
                  m_FontTexSize.x = (long)atof(value.c_str());
              }
              else if (name == "h")
              {
                  m_FontTexSize.y = (long)atof(value.c_str());
              }
              else if (name == "widthslots")
              {
                  m_SlotSizes.x = (int)atoi(value.c_str());
              }
              else if (name == "heightslots")
              {
                  m_SlotSizes.y = (int)atoi(value.c_str());
              }
              else if (name == "norescale")
              {
                  if (value.empty() || (atoi(value.c_str()) != 0))
                  {
                      m_bNoRescale = true;
                  }
              }
              else if (name == "smooth")
              {
                  if (value == "blur")
                  {
                      m_FontSmoothMethod = FONT_SMOOTH_BLUR;
                  }
                  else if (value == "supersample")
                  {
                      m_FontSmoothMethod = FONT_SMOOTH_SUPERSAMPLE;
                  }
                  else if (value == "none")
                  {
                      m_FontSmoothMethod = FONT_SMOOTH_NONE;
                  }
              }
              else if (name == "smooth_amount")
              {
                  m_FontSmoothAmount = (long)atof(value.c_str());
              }
              break;
  
          case ELEMENT_EFFECT:
              if (name == "name")
              {
                  if (value == "default")
                  {
                      m_pEffect = m_pFont->GetDefaultEffect();
                      m_pEffect->ClearPasses();
                  }
                  else
                  {
                      m_pEffect = m_pFont->AddEffect(value.c_str());
                  }
              }
              break;
  
          case ELEMENT_PASS_COLOR:
              if (!m_pPass)
              {
                  break;
              }
              if (name == "r")
              {
                  m_pPass->m_color.r = (uint8)((float)atof(value.c_str()) * 255.0f);
              }
              else if (name == "g")
              {
                  m_pPass->m_color.g = (uint8)((float)atof(value.c_str()) * 255.0f);
              }
              else if (name == "b")
              {
                  m_pPass->m_color.b = (uint8)((float)atof(value.c_str()) * 255.0f);
              }
              else if (name == "a")
              {
                  m_pPass->m_color.a = (uint8)((float)atof(value.c_str()) * 255.0f);
              }
              break;
  
          case ELEMENT_PASS_POSOFFSET:
              if (!m_pPass)
              {
                  break;
              }
              if (name == "x")
              {
                  m_pPass->m_posOffset.x = (float)atoi(value.c_str());
              }
              else if (name == "y")
              {
                  m_pPass->m_posOffset.y = (float)atoi(value.c_str());
              }
              break;
  
          case ELEMENT_PASS_BLEND:
              if (!m_pPass)
              {
                  break;
              }
              if (name == "src")
              {
                  m_pPass->m_blendSrc = GetBlendModeFromString(value, false);
              }
              else if (name == "dst")
              {
                  m_pPass->m_blendDest = GetBlendModeFromString(value, true);
              }
              else if (name == "type")
              {
                  if (value == "modulate")
                  {
                      m_pPass->m_blendSrc = GS_BLSRC_SRCALPHA;
                      m_pPass->m_blendDest = GS_BLDST_ONEMINUSSRCALPHA;
                  }
                  else if (value == "additive")
                  {
                      m_pPass->m_blendSrc = GS_BLSRC_SRCALPHA;
                      m_pPass->m_blendDest = GS_BLDST_ONE;
                  }
              }
              break;
  
          default:
          case ELEMENT_UNKNOWN:
              break;
          }
      }
  
  public:
      CFFont* m_pFont;
  
      unsigned long m_nElement;
  
      CFFont::SEffect*        m_pEffect;
      CFFont::SRenderingPass* m_pPass;
  
      string    m_strFontPath;
      vector2l  m_FontTexSize;
      Vec2i     m_SlotSizes;
      bool      m_bNoRescale;
  
      int       m_FontSmoothMethod;
      int       m_FontSmoothAmount;
  };
  
  //////////////////////////////////////////////////////////////////////////
  // Main loading function
  bool CFFont::Load(const char* pXMLFile)
  {
      MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_Other, 0, "Font %s", pXMLFile);
  
      m_curPath = "";
      if (pXMLFile)
      {
          m_curPath = PathUtil::GetPath(pXMLFile);
      }
  
      XmlNodeRef root = GetISystem()->LoadXmlFromFile(pXMLFile);
      if (!root)
      {
          return false;
      }
  
      CXmlFontShader xmlfs(this);
      xmlfs.ScanXmlNodesRecursively(root);
  
      // if this was not a valid font XML file then return false
      if (!m_pFontTexture || !m_pFontBuffer)
      {
          return false;
      }
  
      return true;
  }
  
  #endif
  
  