Skip to content

Commit 810c89a

Browse files
Merge branch 'auto-updater'
2 parents d116df6 + 3be8f21 commit 810c89a

9 files changed

+343
-2
lines changed

Hayzen.vcxproj

+5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<ProgramDatabaseFile>$(IntDir)$(ProjectName)\$(ProjectName).pdb</ProgramDatabaseFile>
6262
<SetChecksum>true</SetChecksum>
6363
<AdditionalOptions>/DLL /ENTRY:"_DllMainCRTStartup" /ALIGN:128,4096 %(AdditionalOptions)</AdditionalOptions>
64+
<AdditionalDependencies>xnet.lib</AdditionalDependencies>
6465
</Link>
6566
<ImageXex>
6667
<ConfigurationFile>config.xml</ConfigurationFile>
@@ -85,6 +86,8 @@
8586
<ClInclude Include="src\Games\AlphaMW2\AlphaMW2Title.h" />
8687
<ClInclude Include="src\Games\AlphaMW2\Structs.h" />
8788
<ClInclude Include="src\Games\Common\CommonFunctions.h" />
89+
<ClInclude Include="src\Games\Dashboard\AutoUpdater.h" />
90+
<ClInclude Include="src\Games\Dashboard\Dashboard.h" />
8891
<ClInclude Include="src\Games\MW2\GameFunctions.h" />
8992
<ClInclude Include="src\Games\MW2\MenuFunctions.h" />
9093
<ClInclude Include="src\Games\MW2\MW2Title.h" />
@@ -118,6 +121,8 @@
118121
<ClCompile Include="src\Games\AlphaMW2\GameFunctions.cpp" />
119122
<ClCompile Include="src\Games\AlphaMW2\MenuFunctions.cpp" />
120123
<ClCompile Include="src\Games\AlphaMW2\AlphaMW2Title.cpp" />
124+
<ClCompile Include="src\Games\Dashboard\AutoUpdater.cpp" />
125+
<ClCompile Include="src\Games\Dashboard\Dashboard.cpp" />
121126
<ClCompile Include="src\Games\MW2\GameFunctions.cpp" />
122127
<ClCompile Include="src\Games\MW2\MenuFunctions.cpp" />
123128
<ClCompile Include="src\Games\MW2\MW2Title.cpp" />

Hayzen.vcxproj.filters

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
<ClCompile Include="src\Games\SpecOps\AlphaMW2\GameFunctions.cpp" />
2929
<ClCompile Include="src\Games\SpecOps\AlphaMW2\MenuFunctions.cpp" />
3030
<ClCompile Include="src\Games\SpecOps\AlphaMW2\SpecOpsAlphaMW2Title.cpp" />
31+
<ClCompile Include="src\Games\Dashboard\Dashboard.cpp" />
32+
<ClCompile Include="src\Games\Dashboard\AutoUpdater.cpp" />
3133
</ItemGroup>
3234
<ItemGroup>
3335
<ClInclude Include="src\pch.h" />
@@ -66,6 +68,8 @@
6668
<ClInclude Include="src\Games\Common\MultiplayerFunctions.h" />
6769
<ClInclude Include="src\Games\Common\SpecOpsFunctions.h" />
6870
<ClInclude Include="src\Core\Bits.h" />
71+
<ClInclude Include="src\Games\Dashboard\Dashboard.h" />
72+
<ClInclude Include="src\Games\Dashboard\AutoUpdater.h" />
6973
</ItemGroup>
7074
<ItemGroup>
7175
<None Include=".editorconfig" />

src/Core/Plugin.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "pch.h"
22
#include "Core\Plugin.h"
33

4+
#include "Games\Dashboard\Dashboard.h"
45
#include "Games\MW2\MW2Title.h"
56
#include "Games\SpecOps\MW2\SpecOpsMW2Title.h"
67
#include "Games\AlphaMW2\AlphaMW2Title.h"
@@ -67,7 +68,7 @@ void Plugin::InitNewTitle(uint32_t newTitleId)
6768
switch (newTitleId)
6869
{
6970
case TITLE_DASHBOARD:
70-
Xam::XNotify("Hayzen - Dashboard Detected");
71+
Dashboard::Init();
7172
break;
7273
case TITLE_MW2:
7374
if (!strcmp(reinterpret_cast<char *>(0x82001270), "multiplayer"))

src/Games/Dashboard/AutoUpdater.cpp

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#include "pch.h"
2+
#include "Games\Dashboard\AutoUpdater.h"
3+
4+
#define HAYZEN_VERSION "6.0.3"
5+
6+
Socket AutoUpdater::s_Socket;
7+
std::string AutoUpdater::s_PluginPath;
8+
9+
HRESULT AutoUpdater::Init()
10+
{
11+
HRESULT hr = S_OK;
12+
13+
// Initialize the DashLaunch functions
14+
hr = DashLaunch::Init();
15+
if (FAILED(hr))
16+
return E_FAIL;
17+
18+
// Get the full path to the Hayzen plugin
19+
s_PluginPath = GetPluginPath();
20+
if (s_PluginPath == "")
21+
return E_FAIL;
22+
23+
// Create the socket that will be used to communicate with the server
24+
s_Socket = Socket("3.219.96.23", 80);
25+
26+
return hr;
27+
}
28+
29+
HRESULT AutoUpdater::CheckForNewVersion(bool &newVersionAvailable)
30+
{
31+
// Connect to the server
32+
HRESULT hr = s_Socket.Connect();
33+
if (FAILED(hr))
34+
return E_FAIL;
35+
36+
std::string request = "HEAD /" HAYZEN_VERSION " HTTP/1.1\r\nHost: hayzen-updater.herokuapp.com\r\n\r\n";
37+
38+
// Send the request to the server
39+
int bytesSent = s_Socket.Send(request.c_str(), request.size());
40+
if (bytesSent != static_cast<int>(request.size()))
41+
{
42+
s_Socket.Disconnect();
43+
return E_FAIL;
44+
}
45+
46+
// Wait a little bit for the server to process the request and send the response
47+
Sleep(700);
48+
49+
// Get the response from the server
50+
char buffer[4096] = { 0 };
51+
std::string response = "";
52+
int bytesReceived = 0;
53+
while ((bytesReceived = s_Socket.Receive(buffer, sizeof(buffer))) > 0)
54+
{
55+
response.append(buffer, bytesReceived);
56+
57+
Sleep(100);
58+
}
59+
60+
// Disconnect from the server
61+
s_Socket.Disconnect();
62+
63+
// Get the status code
64+
size_t pos = response.find("\r\n");
65+
std::string firstResponseLine = response.substr(0, pos);
66+
67+
int statusCode = 0;
68+
sscanf_s(firstResponseLine.c_str(), "HTTP/1.1 %d", &statusCode);
69+
70+
// 304 Not Modified means the version in the HAYZEN_VERSION macro is the latest one
71+
if (statusCode == 304)
72+
{
73+
newVersionAvailable = false;
74+
return S_OK;
75+
}
76+
77+
// 200 OK means the version in the HAYZEN_VERSION macro is not the latest one
78+
if (statusCode == 200)
79+
{
80+
newVersionAvailable = true;
81+
return S_OK;
82+
}
83+
84+
// Something went wrong if the status code is not 200 nor 304
85+
return E_FAIL;
86+
}
87+
88+
HRESULT AutoUpdater::DownloadLatestVersion()
89+
{
90+
// Connect to the server
91+
HRESULT hr = s_Socket.Connect();
92+
if (FAILED(hr))
93+
return E_FAIL;
94+
95+
std::string request = "GET /latest HTTP/1.1\r\nHost: hayzen-updater.herokuapp.com\r\n\r\n";
96+
97+
// Send the request to the server
98+
int bytesSent = s_Socket.Send(request.c_str(), request.size());
99+
if (bytesSent != static_cast<int>(request.size()))
100+
{
101+
s_Socket.Disconnect();
102+
return E_FAIL;
103+
}
104+
105+
// Wait a little bit for the server to process the request and send the response
106+
Sleep(700);
107+
108+
// Get the response from the server
109+
char buffer[4096] = { 0 };
110+
std::string response = "";
111+
int bytesReceived = 0;
112+
while ((bytesReceived = s_Socket.Receive(buffer, sizeof(buffer))) > 0)
113+
{
114+
response.append(buffer, bytesReceived);
115+
116+
Sleep(100);
117+
}
118+
119+
// Disconnect from the server
120+
s_Socket.Disconnect();
121+
122+
// Make sure we received something
123+
if (response.size() == 0)
124+
return E_FAIL;
125+
126+
// Isolate the Content-Length header
127+
size_t startOfContentLengthLine = response.find("Content-Length");
128+
if (startOfContentLengthLine == std::string::npos)
129+
return E_FAIL;
130+
131+
size_t endOfContentLengthLine = response.find("\r\n", startOfContentLengthLine);
132+
std::string contentLengthLine = response.substr(startOfContentLengthLine, endOfContentLengthLine - startOfContentLengthLine);
133+
134+
// Get the content length
135+
int contentLength = 0;
136+
sscanf_s(contentLengthLine.c_str(), "Content-Length: %d", &contentLength);
137+
138+
// Get the content
139+
std::string delimiter = "\r\n\r\n";
140+
size_t startOfContent = response.find(delimiter) + delimiter.size();
141+
std::string content = response.substr(startOfContent, contentLength);
142+
143+
// Open the file
144+
std::ofstream file;
145+
file.open(s_PluginPath, std::ios::out | std::ios::binary);
146+
if (!file.is_open())
147+
return E_FAIL;
148+
149+
// Write the content to the file
150+
file << content;
151+
file.close();
152+
153+
return hr;
154+
}
155+
156+
std::string AutoUpdater::GetPluginPath()
157+
{
158+
// Dashlaunch allows a maximum of 5 plugins
159+
const size_t maxNumberOfPlugins = 5;
160+
161+
for (size_t i = 0; i <= maxNumberOfPlugins; i++)
162+
{
163+
char *pluginFullPath = nullptr;
164+
char pluginKey[8] = { 0 };
165+
166+
// Create the plugin key, plugin1, plugin2 and so on
167+
_snprintf_s(pluginKey, _TRUNCATE, "plugin%d", i);
168+
169+
// Get the path set for the current plugin key
170+
BOOL res = DashLaunch::GetOptionValueByName(pluginKey, reinterpret_cast<uint32_t *>(&pluginFullPath));
171+
172+
// Some plugin keys might be missing in launch.ini so just continue to the next plugin if the key is not found
173+
if (res == FALSE)
174+
continue;
175+
176+
// Extract the file name from the full plugin path
177+
char pluginFileName[MAX_PATH] = { 0 };
178+
_splitpath_s(
179+
pluginFullPath,
180+
nullptr, 0,
181+
nullptr, 0,
182+
pluginFileName, sizeof(pluginFileName),
183+
nullptr, 0
184+
);
185+
186+
// Check if the current plugin file name is Hayzen, return the current plugin full path
187+
if (!strcmp(pluginFileName, "Hayzen"))
188+
{
189+
// The full path returned from DashLaunch does not contain the drive name so we need to add it
190+
std::string result = "hdd:";
191+
result += pluginFullPath;
192+
193+
return result;
194+
}
195+
}
196+
197+
// If nothing is found, just return an empty string
198+
return std::string("");
199+
}

src/Games/Dashboard/AutoUpdater.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
// Automatic updating system.
4+
class AutoUpdater
5+
{
6+
public:
7+
// Get the full path to the Hayzen plugin and create the socket.
8+
static HRESULT Init();
9+
10+
// Send a request to the server to check if a new version is available.
11+
static HRESULT CheckForNewVersion(bool &newVersionAvailable);
12+
13+
// Send a request to the server to download the latest version and overwrite the existing file on the hard drive.
14+
static HRESULT DownloadLatestVersion();
15+
16+
private:
17+
static Socket s_Socket;
18+
static std::string s_PluginPath;
19+
20+
// Get the full path of the Hayzen plugin from the DashLaunch config.
21+
static std::string GetPluginPath();
22+
};

src/Games/Dashboard/Dashboard.cpp

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include "pch.h"
2+
#include "Games\Dashboard\Dashboard.h"
3+
4+
#include "Games\Dashboard\AutoUpdater.h"
5+
6+
void Dashboard::Init()
7+
{
8+
Xam::XNotify("Hayzen - Dashboard Detected");
9+
10+
Sleep(5000);
11+
12+
LookForUpdates();
13+
}
14+
15+
HRESULT Dashboard::LookForUpdates()
16+
{
17+
HRESULT hr = S_OK;
18+
19+
// Initiliaze the auto updater
20+
hr = AutoUpdater::Init();
21+
if (FAILED(hr))
22+
return E_FAIL;
23+
24+
// Check if there is a new update available
25+
bool newVersionAvailable = false;
26+
hr = AutoUpdater::CheckForNewVersion(newVersionAvailable);
27+
if (FAILED(hr))
28+
return E_FAIL;
29+
30+
// If we already have the latest version, just stop here
31+
if (!newVersionAvailable)
32+
return S_OK;
33+
34+
// Display a message box asking the user if they want to download the new version
35+
const wchar_t *buttonLabels[] = { L"Yes", L"No" };
36+
uint32_t buttonPressedIndex = 0;
37+
38+
uint32_t result = Xam::ShowMessageBox(
39+
L"New version available",
40+
L"A new version of Hayzen is available, would you like to download it?",
41+
buttonLabels,
42+
ARRAYSIZE(buttonLabels),
43+
&buttonPressedIndex,
44+
XMB_ALERTICON
45+
);
46+
47+
// If the user cancelled the message box, return early
48+
if (result != ERROR_SUCCESS)
49+
return S_OK;
50+
51+
// If the user pressed anything else than "Yes", return early
52+
if (buttonPressedIndex != 0)
53+
return S_OK;
54+
55+
// Download the latest version and write it to disk
56+
hr = AutoUpdater::DownloadLatestVersion();
57+
if (FAILED(hr))
58+
{
59+
const wchar_t *okButton[] = { L"OK" };
60+
61+
Xam::ShowMessageBox(
62+
L"Download error",
63+
L"An error occured while downloading the latest version of Hayzen.",
64+
okButton,
65+
ARRAYSIZE(okButton),
66+
nullptr,
67+
XMB_ERRORICON
68+
);
69+
70+
return E_FAIL;
71+
}
72+
73+
// Display a success message box to the user and ask them if they want to reboot to load the latest version
74+
result = Xam::ShowMessageBox(
75+
L"Download complete",
76+
L"The latest version of Hayzen was successfully downloaded.\nThe changes won't be applied until the next reboot, would you like to reboot now?",
77+
buttonLabels,
78+
ARRAYSIZE(buttonLabels),
79+
&buttonPressedIndex,
80+
XMB_ALERTICON,
81+
1
82+
);
83+
84+
// If the user cancelled the message box, return early
85+
if (result != ERROR_SUCCESS)
86+
return S_OK;
87+
88+
// If the user pressed anything else than "Yes", return early
89+
if (buttonPressedIndex != 0)
90+
return S_OK;
91+
92+
// Reboot
93+
Xam::Reboot();
94+
95+
return hr;
96+
}

src/Games/Dashboard/Dashboard.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
// Class to run things when getting on the Xbox 360 dashboard
4+
class Dashboard
5+
{
6+
public:
7+
// Print a notification and look for updates.
8+
static void Init();
9+
10+
private:
11+
// Check if a new version is available, if so, download it if the user wants to.
12+
static HRESULT LookForUpdates();
13+
};

0 commit comments

Comments
 (0)