Initial commit: Complete open-source Decal rebuild

All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erik 2026-02-08 18:27:56 +01:00
commit d1442e3747
1382 changed files with 170725 additions and 0 deletions

View file

@ -0,0 +1,527 @@
#include "stdafx.h"
#include <string>
#include "DenAgent.h"
#include "DownloaderDlg.h"
#include "AutoUpdate.h"
static LPCSTR updateList = "updatelist.xml" ;
// DownloadAgent
/* static */ bool DownloadAgent::downloadFile(std::string remoteFile, std::string localFile) {
// Perform a direct, one time download of a file
cDownloaderDlg dlg;
dlg.addFile(remoteFile, localFile);
dlg.DoModal();
return (dlg.GetDownloadStatus() == cDownloaderDlg::DownloadStatus::DOWNLOAD_SUCCEEDED);
}
DownloadAgent::DownloadAgent() {
m_ScheduledDownloads.clear();
}
void DownloadAgent::scheduleDownload(std::string remoteFile, std::string localFile) {
// Encapsulate this download in a download slot, and add it to the schedule list
DownloadSlot slot;
slot.remoteFile = remoteFile;
slot.localFile = localFile;
m_ScheduledDownloads.push_back(slot);
}
bool DownloadAgent::runDownloads() {
if (!m_ScheduledDownloads.size())
return false;
cDownloaderDlg dlg;
// Add each schduled download to our downloader
std::list<DownloadSlot>::iterator it;
for (it = m_ScheduledDownloads.begin(); it != m_ScheduledDownloads.end(); ++it) {
DownloadSlot slot = *it;
dlg.addFile(slot.remoteFile, slot.localFile);
}
// And run them
dlg.DoModal();
return (dlg.GetDownloadStatus() == cDownloaderDlg::DownloadStatus::DOWNLOAD_SUCCEEDED);
}
// AutoUpdateSource
AutoUpdateSource::AutoUpdateSource(std::string decalDir, AutoUpdateType type, std::string localTarget, std::string remoteSource, std::string requiredVersion)
: m_DecalDir(decalDir)
, m_LocalTarget(localTarget)
, m_RequiredVersion(requiredVersion)
, m_RemoteSource(remoteSource)
, m_Type(type)
{
}
bool AutoUpdateSource::needsUpdating() {
USES_CONVERSION;
// Get the version of the local file
// Make sure the file exists
OFSTRUCT targetFile;
std::string localTarget = m_DecalDir + "/" + m_LocalTarget;
if (OpenFile(localTarget.c_str(), &targetFile, OF_EXIST) == HFILE_ERROR) {
// It didn't exist, we must update
return true;
}
if (m_RequiredVersion.length() == 0) {
// If we don't have a remote version, assume we want to update
return true;
}
int reqMajor(0), reqMinor(0), reqPatch(0), reqBuild(0) ;
sscanf(m_RequiredVersion.c_str(), "%i.%i.%i.%i", &reqMajor, &reqMinor, &reqPatch, &reqBuild);
if (m_Type == AU_TYPE_XML) {
// Extract the version node from an XML document
MSXML::IXMLDOMDocumentPtr pDoc;
try {
pDoc.CreateInstance (__uuidof(MSXML::DOMDocument), NULL, CLSCTX_INPROC_SERVER);
pDoc->async = false;
// Construct a list of update targets based on the XML contents
if (pDoc->load(static_cast<LPCTSTR>(localTarget.c_str()))) {
// Read the autoupdate version string
// MSXML::IXMLDOMNodePtr pVersionNode = pDoc->selectSingleNode ( _T( "/*/@version" ) );
MSXML::IXMLDOMElementPtr pVersionNode = pDoc->selectSingleNode ("//revision");
if (pVersionNode != NULL) {
// _variant_t vXMLVer = pVersionNode->text;
_variant_t vXMLVer = pVersionNode->getAttribute("version") ;
if (vXMLVer.vt == VT_BSTR) {
int locMajor(0), locMinor(0), locPatch(0), locBuild(0) ;
sscanf(OLE2T(vXMLVer.bstrVal), "%i.%i.%i.%i", &locMajor, &locMinor, &locPatch, &locBuild);
if (locMajor==reqMajor) {
if (locMinor==reqMinor) {
if (locPatch==reqPatch) {
return locBuild<reqBuild ;
} else {
return locPatch<reqPatch ;
}
} else {
return locMinor<reqMinor ;
}
} else {
return locMajor<reqMajor ;
}
}
}
}
} catch (...) {
return true ; // if error reading xml, assume needs updating
}
} else if (m_Type == AU_TYPE_DLL || m_Type == AU_TYPE_BETADLL) {
// Check to see if the file itself needs updating
if (isOutDatedVersion(localTarget, reqMajor, reqMinor, reqPatch, reqBuild))
{
// File is outdated, check requirements
std::vector<UpdateRequirement>::iterator it;
for (it = m_Requirements.begin(); it != m_Requirements.end(); ++it)
{
UpdateRequirement ur = *it;
localTarget = m_DecalDir + "/" + ur.sFile;
sscanf(ur.sVers.c_str(), "%i.%i.%i.%i", &reqMajor, &reqMinor, &reqPatch, &reqBuild);
if (isOutDatedVersion(localTarget, reqMajor, reqMinor, reqPatch, reqBuild))
{
std::string sMsg = m_LocalTarget + " cannot be updated because one or more dependencies is outdated";
::MessageBox(0, _T(sMsg.c_str()), NULL, S_OK);
return false;
}
}
return true;
}
else // File is newer, ignore requirements and exit
{
return false;
}
}
// if anything went wrong or we were unable to tell whether it should be updated
// or not...assume it needs updating.
return true ;
}
std::string AutoUpdateSource::getSource() {
return m_RemoteSource;
}
std::string AutoUpdateSource::getDestination() {
return m_LocalTarget;
}
AutoUpdateType AutoUpdateSource::getType() {
return m_Type;
}
bool AutoUpdateSource::isOutDatedVersion(std::string sFile, int nMajor, int nMinor, int nPatch, int nBuild)
{
// Extract the DLL version
DWORD dwDummy, dwVerSize;
dwVerSize = ::GetFileVersionInfoSize(const_cast<LPTSTR>(sFile.c_str()), &dwDummy);
if( dwVerSize == 0 )
return true; // if file vsn not available, assume needs updating
else
{
BYTE *pbVersionInfo = reinterpret_cast< BYTE * >(::_alloca(dwVerSize));
::GetFileVersionInfo(const_cast<LPTSTR>(sFile.c_str()), 0, dwVerSize, pbVersionInfo);
VS_FIXEDFILEINFO *vffi;
UINT nLength = sizeof(VS_FIXEDFILEINFO);
if( ::VerQueryValue( pbVersionInfo, _T("\\"), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) )
{
// Got it, so format it
int locMajor(0), locMinor(0), locPatch(0), locBuild(0);
locMajor = static_cast< int >( HIWORD( vffi->dwFileVersionMS ) );
locMinor = static_cast< int >( LOWORD( vffi->dwFileVersionMS ) );
locPatch = static_cast< int >( HIWORD( vffi->dwFileVersionLS ) );
locBuild = static_cast< int >( LOWORD( vffi->dwFileVersionLS ) );
if( locMajor == nMajor )
{
if( locMinor == nMinor )
{
if( locPatch == nPatch )
return locBuild < nBuild;
else
return locPatch < nPatch;
}
else
return locMinor < nMinor ;
}
else
return locMajor < nMajor ;
}
// problem with VerQueryValue... maybe updating will fix it.
else
return true;
}
}
void AutoUpdateSource::AddRequirement(UpdateRequirement reqUpdate)
{
m_Requirements.push_back(reqUpdate);
}
// AutoUpdate
AutoUpdate::AutoUpdate(std::string decalDir) {
m_DecalDir = decalDir;
m_LocalXML = "";
m_RemoteSources.clear();
}
bool AutoUpdate::initialiseFromXML(std::string remoteXML) {
USES_CONVERSION;
m_RemoteXML = remoteXML + "/" + updateList ;
// Download the remote XML document to a local file
if (!DownloadAgent::downloadFile(m_RemoteXML, m_DecalDir + "/" + updateList))
{
// We couldn't download the update list. We must bail here, or suffer!
::MessageBox(NULL, "Could not obtain updatelist.xml. Cannot update - Hosting server down?", NULL, MB_OK);
return false;
}
// Parse it as an XML document
MSXML::IXMLDOMDocumentPtr pDoc;
// try {
pDoc.CreateInstance (__uuidof(MSXML::DOMDocument), NULL, CLSCTX_INPROC_SERVER);
pDoc->async = false;
// Construct a list of update targets based on the XML contents
std::string localXML = m_DecalDir + "/" + updateList ;
if (!pDoc->load( static_cast<LPCTSTR>(localXML.c_str())))
return false;
// Read the autoupdate version string
MSXML::IXMLDOMNodePtr pVersionNode = pDoc->selectSingleNode ( _T( "/*/@version" ) );
if (pVersionNode == NULL) {
::MessageBox(NULL,"No version information in updatelist.xml. Cannot update",NULL,MB_OK) ;
return false;
}
// Check to make sure we understand this version
char *version = OLE2T(pVersionNode->text);
if (strcmp(version,"1.0.0.0")) {
::MessageBox(NULL,"Unknown version type in updatelist.xml. Cannot update",NULL,MB_OK) ;
return false ;
}
// Now iterate over the Files entries, creating an AutoUpdateSource for each
MSXML::IXMLDOMNodeListPtr xmlFiles = pDoc->selectNodes(_T("/updatelist/file"));
if (xmlFiles.GetInterfacePtr() == NULL)
return false;
for (MSXML::IXMLDOMElementPtr xmlFile = xmlFiles->nextNode(); xmlFile.GetInterfacePtr() != NULL; xmlFile = xmlFiles->nextNode()) {
std::string localFile, remoteFile, version;
AutoUpdateType type;
BSTR bResult;
_variant_t vResult;
// Read the type
vResult = xmlFile->getAttribute("type");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
char *cType = OLE2T(bResult);
if (strcmp(cType, "xml") == 0) {
type = AU_TYPE_XML;
} else if (strcmp(cType, "dll") == 0) {
type = AU_TYPE_DLL;
} else if (strcmp(cType, "beta") == 0) {
type = AU_TYPE_BETADLL ;
} else {
continue;
}
// Local name
vResult = xmlFile->getAttribute("localname");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
localFile = OLE2T(bResult);
// Remote name
vResult = xmlFile->getAttribute("remotename");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
remoteFile = OLE2T(bResult);
// And version
vResult = xmlFile->getAttribute("version");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
version = OLE2T(bResult);
AutoUpdateSource aUSource(m_DecalDir, type, localFile, remoteFile, version);
//if the source is dll || beta check for requirements
if (type == AU_TYPE_DLL || type == AU_TYPE_BETADLL)
{
MSXML::IXMLDOMNodeListPtr xmlReqs = NULL;//xmlFile->selectNodes(_T("requirement")); //pDoc->selectNodes(_T("/updatelist/file"));
xmlReqs = xmlFile->getElementsByTagName(_T("requirement"));
if (xmlReqs.GetInterfacePtr() == NULL)
continue;
for (MSXML::IXMLDOMElementPtr xmlNode = xmlReqs->nextNode(); xmlNode.GetInterfacePtr() != NULL; xmlNode = xmlReqs->nextNode())
{
std::string sLocalFile, sVersion;
// Read the file name
vResult = xmlNode->getAttribute("file");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
sLocalFile = OLE2T(bResult);
// Read the version
vResult = xmlNode->getAttribute("version");
if (vResult.vt != VT_BSTR)
continue;
bResult = vResult.bstrVal;
sVersion = OLE2T(bResult);
UpdateRequirement uReq(sLocalFile, sVersion);
aUSource.AddRequirement(uReq);
}
}
// Add the source to the cache
m_RemoteSources.push_back(aUSource);
}
// } catch (...) {
// return false;
// }
return true;
}
bool AutoUpdate::needsUpdate() {
// Ask all the dependancies if they want to update themselves
// Check the versions of all our local XML documents to see if we need an update
std::list<AutoUpdateSource>::iterator it;
for (it = m_RemoteSources.begin(); it != m_RemoteSources.end(); ++it) {
AutoUpdateSource aUSource = *it;
if (aUSource.needsUpdating())
return true;
}
// Nothing needed updating
return false;
}
bool AutoUpdate::performUpdate() {
// Construct a list of all the remote components that need updating
std::list<AutoUpdateSource> updateList;
DownloadAgent dlAgent;
bool DllUpdate = false ;
bool BetaUpdate = false ;
std::list<AutoUpdateSource>::iterator itRemote = m_RemoteSources.begin();;
while (itRemote != m_RemoteSources.end()) {
AutoUpdateSource aUSource = *itRemote++;
if (aUSource.needsUpdating()) {
switch(aUSource.getType()) {
case AU_TYPE_DLL: DllUpdate = true ; break ;
case AU_TYPE_BETADLL: BetaUpdate = true ; break ;
}
if (aUSource.getType()==AU_TYPE_BETADLL) {
BetaUpdate = true ;
}
updateList.push_back(aUSource);
}
}
if (DllUpdate) {
if ( MessageBox(NULL,
"Updates to the Decal program are available.\n\n"
"These are supported updates to the current release\n"
"version of Decal.\n"
"Installation of these updates is recommended.\n\n"
"Do you wish to install these updates now?",
"Decal Program Updates Available",
MB_YESNO) == IDNO
){
DllUpdate = false ;
}
}
if (BetaUpdate) {
if (MessageBox(NULL,
"Decal Beta updates are available.\n\n"
"NOTE: Beta versions are not supported and have NOT\n"
"been tested extensively.\n"
" They can crash your system, and require a full\n"
"re-install of the original Decal program.\n\n"
"Do you wish to install these unsupported Beta updates?",
"Beta program updates available",
MB_YESNO|MB_DEFBUTTON2) == IDNO
) {
BetaUpdate = false ;
}
}
// Schedule downloads and processing for all selected types
std::list<AutoUpdateSource>::iterator itUpdates = updateList.begin();;
while (itUpdates != updateList.end()) {
AutoUpdateSource source = *itUpdates++ ;
if ( (source.getType() == AU_TYPE_DLL && !DllUpdate)
|| (source.getType() == AU_TYPE_BETADLL && !BetaUpdate)
){
continue ;
}
dlAgent.scheduleDownload(source.getSource(), m_DecalDir + "\\" + source.getDestination() + ".tmp");
}
// Download them to the local cache
if (!dlAgent.runDownloads()) {
// Something went horribly wrong
return false;
}
// Delete the destinations and move the cached objects to their new homes
itUpdates = updateList.begin() ;
while (itUpdates != updateList.end()) {
AutoUpdateSource aUSource = *itUpdates++;
if ( (aUSource.getType() == AU_TYPE_DLL && !DllUpdate)
|| (aUSource.getType() == AU_TYPE_BETADLL && !BetaUpdate)
){
continue ;
}
std::string newFile = (m_DecalDir + "\\" + aUSource.getDestination());
std::string tmpFile = (m_DecalDir + "\\" + aUSource.getDestination() + ".tmp");
BOOL bResult = ::DeleteFile (newFile.c_str ());
DWORD dwLastError = 0;
if (!bResult)
{
dwLastError = ::GetLastError ();
}
if (bResult || dwLastError == ERROR_FILE_NOT_FOUND)
{
MoveFile(tmpFile.c_str(), newFile.c_str());
// We may have to register DLLs
if (aUSource.getType() == AU_TYPE_DLL || aUSource.getType() == AU_TYPE_BETADLL )
{
HINSTANCE hDLL = LoadLibrary(newFile.c_str());
if (hDLL) {
typedef CRuntimeClass * (*DLLREG)();
DLLREG DllReg = (DLLREG)GetProcAddress(hDLL, "DllRegisterServer");
if (DllReg != NULL)
DllReg();
FreeLibrary(hDLL);
}
}
}
// Delete failed - file is in use
else
{
MessageBox(NULL, "A file being updated is in use - close programs and try again.", "Decal", MB_OK);
}
}
if (updateList.size() > 0) {
MessageBox(NULL, "Your Decal components have been updated", "Decal", MB_OK);
} else {
MessageBox(NULL, "Your Decal components are already up to date", "Decal", MB_OK);
}
return true;
}