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:
commit
d1442e3747
1382 changed files with 170725 additions and 0 deletions
527
Native/DenAgent/AutoUpdate.cpp
Normal file
527
Native/DenAgent/AutoUpdate.cpp
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue