Skip to content

Commit

Permalink
Add auto skin injection functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Lpsd committed Jan 15, 2025
1 parent 67541e7 commit 880ca95
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 226 deletions.
1 change: 1 addition & 0 deletions Client/core/CCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ void CCore::DeinitGUI()
void CCore::InitGUI(IDirect3DDevice9* pDevice)
{
m_pGUI = InitModule<CGUI>(m_GUIModule, "GUI", "InitGUIInterface", pDevice);
m_pGUI->SetXMLParser(m_pXML);
}

void CCore::CreateGUI()
Expand Down
208 changes: 194 additions & 14 deletions Client/gui/CGUI_Impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,28 @@

#include "StdInc.h"
#include "CEGUIExceptions.h"
#include <xml/CXMLFile.h>
#include <xml/CXMLNode.h>
#include <core/CCoreInterface.h>
#include <fstream>

using std::list;

#define CGUI_MTA_DEFAULT_FONT "tahoma.ttf" // %WINDIR%/font/<...>
#define CGUI_MTA_DEFAULT_FONT_BOLD "tahomabd.ttf" // %WINDIR%/font/<...>
#define CGUI_MTA_CLEAR_FONT "verdana.ttf" // %WINDIR%/font/<...>
#define CGUI_MTA_DEFAULT_FONT "tahoma.ttf" // %WINDIR%/font/<...>
#define CGUI_MTA_DEFAULT_FONT_BOLD "tahomabd.ttf" // %WINDIR%/font/<...>
#define CGUI_MTA_CLEAR_FONT "verdana.ttf" // %WINDIR%/font/<...>

#define CGUI_MTA_DEFAULT_REG "Tahoma (TrueType)"
#define CGUI_MTA_DEFAULT_REG_BOLD "Tahoma Bold (TrueType)"
#define CGUI_MTA_CLEAR_REG "Verdana (TrueType)"
#define CGUI_MTA_DEFAULT_REG "Tahoma (TrueType)"
#define CGUI_MTA_DEFAULT_REG_BOLD "Tahoma Bold (TrueType)"
#define CGUI_MTA_CLEAR_REG "Verdana (TrueType)"

#define CGUI_MTA_SUBSTITUTE_FONT "cgui/unifont.ttf" // GTA/MTA/<...>
#define CGUI_MTA_SANS_FONT "cgui/sans.ttf" // GTA/MTA/<...>
#define CGUI_SA_HEADER_FONT "cgui/saheader.ttf" // GTA/MTA/<...>
#define CGUI_SA_GOTHIC_FONT "cgui/sagothic.ttf" // GTA/MTA/<...>
#define CGUI_SA_HEADER_SIZE 26
#define CGUI_SA_GOTHIC_SIZE 47
#define CGUI_MTA_SANS_FONT_SIZE 9
#define CGUI_MTA_SUBSTITUTE_FONT "cgui/unifont.ttf" // GTA/MTA/<...>
#define CGUI_MTA_SANS_FONT "cgui/sans.ttf" // GTA/MTA/<...>
#define CGUI_SA_HEADER_FONT "cgui/saheader.ttf" // GTA/MTA/<...>
#define CGUI_SA_GOTHIC_FONT "cgui/sagothic.ttf" // GTA/MTA/<...>
#define CGUI_SA_HEADER_SIZE 26
#define CGUI_SA_GOTHIC_SIZE 47
#define CGUI_MTA_SANS_FONT_SIZE 9

CGUI_Impl::CGUI_Impl(IDirect3DDevice9* pDevice) : m_HasSchemeLoaded(false), m_fCurrentServerCursorAlpha(1.0f)
{
Expand Down Expand Up @@ -115,7 +119,15 @@ void CGUI_Impl::SetSkin(const char* szName)
CEGUI::SchemeManager::getSingleton().unloadScheme(m_CurrentSchemeName);
}

PushGuiWorkingDirectory(CalcMTASAPath(PathJoin("skins", szName)));
bool bConvertSkin = ConvertToModernSkin(szName);

if (!bConvertSkin)
{
BrowseToSolution("gui-skin", EXIT_GAME_FIRST, SString("Error converting skin '%s' to modern", szName));
return;
}

PushGuiWorkingDirectory(CalcMTASAPath(PathJoin("MTA", "cgui", "modern", "autogenerated")));

CEGUI::Scheme* scheme = CEGUI::SchemeManager::getSingleton().loadScheme("CGUI.xml");
m_CurrentSchemeName = scheme->getName().c_str();
Expand Down Expand Up @@ -150,6 +162,174 @@ void CGUI_Impl::SetSkin(const char* szName)
m_eInputMode = INPUTMODE_NO_BINDS_ON_EDIT;
}

bool CGUI_Impl::ConvertToModernSkin(const char* szSkin)
{
if (!m_pXML)
return false;

// Duplicate all the CGUI files in the skin folder to a temporary folder
SString strSkinPath = CalcMTASAPath(PathJoin("skins", szSkin));

if (!DirectoryExists(strSkinPath))
return false;

SString tempPath = CalcMTASAPath(PathJoin("MTA", "cgui", "modern", "autogenerated"));

if (!DirectoryExists(tempPath))
{
CreateDirectory(tempPath, 0);
}
else
{
// Verify if the modern skin version has changed since the skin was last converted
SString strModernVersionPath = CalcMTASAPath(PathJoin("MTA", "cgui", "modern", ".version"));
std::ifstream pModernVersionFile(strModernVersionPath);
std::string modernVersion;

if (pModernVersionFile.is_open())
{
std::getline(pModernVersionFile, modernVersion);
pModernVersionFile.close();
}

SString strSkinVersionPath = PathJoin(tempPath, ".version");
std::ifstream pSkinVersionFile(strSkinVersionPath);
std::string skinVersion;

if (pSkinVersionFile.is_open())
{
std::getline(pSkinVersionFile, skinVersion);
pSkinVersionFile.close();
}

if (!modernVersion.empty() && !skinVersion.empty() && modernVersion == skinVersion)
{
// Verify if the skin has changed, or if files already up to date
SString strHashesPath = PathJoin(tempPath, ".integrity");

if (FileExists(strHashesPath))
{
std::ifstream pHashesFile(strHashesPath);

if (pHashesFile.good())
{
std::string skinName;
std::getline(pHashesFile, skinName);

if (skinName == szSkin)
{
SString lookNFeelXml = PathJoin(strSkinPath, "CGUI.lnf.xml");
SString imagesetXml = PathJoin(strSkinPath, "CGUI.is.xml");
SString imagesetPng = PathJoin(strSkinPath, "CGUI.png");
SString schemeXml = PathJoin(strSkinPath, "CGUI.xml");

SString lookNFeelXmlHash = CMD5Hasher::CalculateHexString(lookNFeelXml);
SString imagesetXmlHash = CMD5Hasher::CalculateHexString(imagesetXml);
SString imagesetPngHash = CMD5Hasher::CalculateHexString(imagesetPng);
SString schemeXmlHash = CMD5Hasher::CalculateHexString(schemeXml);

std::string hashes;
std::getline(pHashesFile, hashes);

if (hashes == lookNFeelXmlHash + imagesetXmlHash + imagesetPngHash + schemeXmlHash)
{
pHashesFile.close();
return true;
}
}
}
}
}
}

// Copy all files
SString lookNFeelXml = PathJoin(strSkinPath, "CGUI.lnf.xml");
SString imagesetXml = PathJoin(strSkinPath, "CGUI.is.xml");
SString imagesetPng = PathJoin(strSkinPath, "CGUI.png");
SString schemeXml = PathJoin(strSkinPath, "CGUI.xml");

if (!FileExists(lookNFeelXml) || !FileExists(imagesetXml) || !FileExists(imagesetPng) || !FileExists(schemeXml))
return false;

CopyFile(lookNFeelXml, PathJoin(tempPath, "CGUI.lnf.xml"), 0);
CopyFile(imagesetXml, PathJoin(tempPath, "CGUI.is.xml"), 0);
CopyFile(imagesetPng, PathJoin(tempPath, "CGUI.png"), 0);
CopyFile(schemeXml, PathJoin(tempPath, "CGUI.xml"), 0);

// Store the hashes of each file under .skinname
SString strHashesPath = PathJoin(tempPath, ".integrity");
std::ofstream pHashesFile(strHashesPath, std::ofstream::out | std::ofstream::trunc);

if (!pHashesFile.is_open())
return false;

SString lookNFeelXmlHash = CMD5Hasher::CalculateHexString(lookNFeelXml);
SString imagesetXmlHash = CMD5Hasher::CalculateHexString(imagesetXml);
SString imagesetPngHash = CMD5Hasher::CalculateHexString(imagesetPng);
SString schemeXmlHash = CMD5Hasher::CalculateHexString(schemeXml);

pHashesFile << szSkin << std::endl;
pHashesFile << lookNFeelXmlHash << imagesetXmlHash << imagesetPngHash << schemeXmlHash;
pHashesFile.close();

// Open the modern XML templates (using TinyXML)
SString strModernTemplatesPath = CalcMTASAPath(PathJoin("MTA", "cgui", "modern", "templates"));
SString strModernLookNFeel = PathJoin(strModernTemplatesPath, "looknfeel.xml");
SString strModernScheme = PathJoin(strModernTemplatesPath, "scheme.xml");

if (!FileExists(strModernLookNFeel) || !FileExists(strModernScheme))
return false;

// Load the modern LookNFeel, read-only
CXMLFile* pModernLookNFeel = m_pXML->CreateXML(strModernLookNFeel, false, true);

if (!pModernLookNFeel || !pModernLookNFeel->Parse())
return false;

// Load the requested skin's LookNFeel, read-write
CXMLFile* pSkinLookNFeel = m_pXML->CreateXML(PathJoin(tempPath, "CGUI.lnf.xml"));

if (!pSkinLookNFeel || !pSkinLookNFeel->Parse())
return false;

CXMLNode* modernRootNode = pModernLookNFeel->GetRootNode();
CXMLNode* skinRootNode = pSkinLookNFeel->GetRootNode();
modernRootNode->CopyChildrenInto(skinRootNode, true, false);

if (!pSkinLookNFeel->Write())
return false;

delete pModernLookNFeel;
delete pSkinLookNFeel;

// Load the modern scheme, read-only
CXMLFile* pModernScheme = m_pXML->CreateXML(strModernScheme, false, true);

if (!pModernScheme || !pModernScheme->Parse())
return false;

// Load the requested skin's scheme, read-write
CXMLFile* pSkinScheme = m_pXML->CreateXML(PathJoin(tempPath, "CGUI.xml"));

if (!pSkinScheme || !pSkinScheme->Parse())
return false;

modernRootNode = pModernScheme->GetRootNode();
skinRootNode = pSkinScheme->GetRootNode();
modernRootNode->CopyChildrenInto(skinRootNode, true, false);

if (!pSkinScheme->Write())
return false;

// Copy the modern version file
CopyFile(CalcMTASAPath(PathJoin("MTA", "cgui", "modern", ".version")), PathJoin(tempPath, ".version"), 0);

delete pModernScheme;
delete pSkinScheme;

return true;
}

void CGUI_Impl::SetBidiEnabled(bool bEnabled)
{
m_pSystem->SetBidiEnabled(bEnabled);
Expand Down
6 changes: 6 additions & 0 deletions Client/gui/CGUI_Impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class CGUI_Impl;
#include <gui/CGUI.h>
#include <list>
#include <windows.h>
#include <xml/CXML.h>

#define CGUI_CHAR_SIZE 6

Expand Down Expand Up @@ -282,6 +283,8 @@ class CGUI_Impl : public CGUI, public CGUITabList
void SetModernSkinEnabled(bool bEnabled) { m_bUseModernSkin = bEnabled; }
const char* ResolveSkin(const char* szSkin);

void SetXMLParser(CXML* pXML) { m_pXML = pXML; }

private:
CGUIButton* _CreateButton(CGUIElement_Impl* pParent = NULL, const char* szCaption = "");
CGUICheckBox* _CreateCheckBox(CGUIElement_Impl* pParent = NULL, const char* szCaption = "", bool bChecked = false);
Expand All @@ -303,6 +306,8 @@ class CGUI_Impl : public CGUI, public CGUITabList
bool bAutoScale = false);
void ApplyGuiWorkingDirectory();

bool ConvertToModernSkin(const char* szSkin);

IDirect3DDevice9* m_pDevice;

CEGUI::Renderer* m_pRenderer;
Expand Down Expand Up @@ -355,4 +360,5 @@ class CGUI_Impl : public CGUI, public CGUITabList
CElapsedTime m_RenderOkTimer;

bool m_bUseModernSkin = false;
CXML* m_pXML;
};
2 changes: 2 additions & 0 deletions Client/sdk/gui/CGUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CGUI;
#include "CGUITabPanel.h"
#include "CGUIComboBox.h"
#include "CGUITypes.h"
#include <xml/CXML.h>

// Path defines for CGUI
#define CGUI_ICON_MESSAGEBOX_INFO "cgui\\images\\info.png"
Expand Down Expand Up @@ -171,4 +172,5 @@ class CGUI
virtual bool LoadImageset(const SString& strFilename) = 0;

virtual void SetModernSkinEnabled(bool bEnabled) = 0;
virtual void SetXMLParser(CXML* pXML) = 0;
};
17 changes: 12 additions & 5 deletions Shared/XML/CXMLNodeImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,13 @@ CXMLNode* CXMLNodeImpl::CopyNode(CXMLNode* pParent)
return dynamic_cast<CXMLNode*>(pNew);
}

bool CXMLNodeImpl::CopyChildrenInto(CXMLNode* pDestination, bool bRecursive)
bool CXMLNodeImpl::CopyChildrenInto(CXMLNode* pDestination, bool bRecursive, bool bDelete)
{
// Delete all the children of the target
pDestination->DeleteAllSubNodes();
if (bDelete)
{
// Delete all the children of the target
pDestination->DeleteAllSubNodes();
}

// Iterate through our children if we have. Otherwize just copy the content
if (m_Children.size() > 0)
Expand Down Expand Up @@ -405,14 +408,18 @@ bool CXMLNodeImpl::CopyChildrenInto(CXMLNode* pDestination, bool bRecursive)
{
if (!pMyChildNode->CopyChildrenInto(pNewChildNode, true))
{
pDestination->DeleteAllSubNodes();
if (bDelete)
pDestination->DeleteAllSubNodes();

return false;
}
}
}
else
{
pDestination->DeleteAllSubNodes();
if (bDelete)
pDestination->DeleteAllSubNodes();

return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion Shared/XML/CXMLNodeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class CXMLNodeImpl : public CXMLNode
bool IsUsingIDs() { return m_bUsingIDs; };

CXMLNode* CopyNode(CXMLNode* pParent = NULL);
bool CopyChildrenInto(CXMLNode* pDestination, bool bRecursive);
bool CopyChildrenInto(CXMLNode* pDestination, bool bRecursive, bool bDelete = true);

TiXmlElement* GetNode();
void DeleteWrapper();
Expand Down
1 change: 1 addition & 0 deletions Shared/data/MTA San Andreas/MTA/cgui/modern/.version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.1
Loading

0 comments on commit 880ca95

Please sign in to comment.