Skip to content

Commit 4748f23

Browse files
committed
Gold.
1 parent f0f9510 commit 4748f23

11 files changed

Lines changed: 67 additions & 63 deletions

MGSHDFix.vcxproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<ClInclude Include="src\fixes\aiming_after_equip.hpp" />
7171
<ClInclude Include="src\fixes\color_filters.hpp" />
7272
<ClInclude Include="src\resources\RegStateHelpers.hpp" />
73+
<ClInclude Include="src\resources\resource.h" />
7374
<ClInclude Include="src\resources\version_checking.hpp" />
7475
<ClInclude Include="src\resources\version.h" />
7576
<ClInclude Include="src\warnings\corrupt_save_message.hpp" />
@@ -100,7 +101,8 @@
100101
<None Include="README.md" />
101102
</ItemGroup>
102103
<ItemGroup>
103-
<Image Include="src\mgshdfix.ico" />
104+
<Image Include="mgshdfix.ico" />
105+
<Image Include="src\resources\mgshdfix.ico" />
104106
</ItemGroup>
105107
<ItemGroup>
106108
<ProjectReference Include="external\zydis\msvc\zydis\Zydis.vcxproj">

MGSHDFix.vcxproj.filters

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,13 @@
196196
<ClInclude Include="src\resources\RegStateHelpers.hpp">
197197
<Filter>Utility</Filter>
198198
</ClInclude>
199+
<ClInclude Include="src\resources\resource.h" />
199200
</ItemGroup>
200201
<ItemGroup>
201-
<Image Include="src\mgshdfix.ico">
202+
<Image Include="src\resources\mgshdfix.ico">
202203
<Filter>Resources</Filter>
203204
</Image>
205+
<Image Include="mgshdfix.ico" />
204206
</ItemGroup>
205207
<ItemGroup>
206208
<None Include="README.md">

src/dllmain.cpp

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -498,12 +498,7 @@ static void Init_CustomResolution()
498498
spdlog::warn("MGS 2 | MGS 3: Custom Resolution: Splashscreens: Incompatible game version. Skipping.");
499499
else
500500
{
501-
uint8_t* MGS2_MGS3_SplashscreenResult = Memory::PatternScanSilent(baseModule, "FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 4C 8D 44 24 ?? 48 8D 54 24 ?? 48 8B 08 48 8B 01 FF 50 ?? 48 8B 58");
502-
if (!MGS2_MGS3_SplashscreenResult)
503-
{
504-
spdlog::error("MGS 2 | MGS 3: Custom Resolution: Splashscreens: Pattern scan failed.");
505-
}
506-
else
501+
if (uint8_t* MGS2_MGS3_SplashscreenResult = Memory::PatternScan(baseModule, "FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 4C 8D 44 24 ?? 48 8D 54 24 ?? 48 8B 08 48 8B 01 FF 50 ?? 48 8B 58", "MGS 2 | MGS 3: Custom Resolution: Splashscreens"))
507502
{
508503
static SafetyHookMid MGS2_MGS3_SplashScreenMidHook{};
509504
MGS2_MGS3_SplashScreenMidHook = safetyhook::create_mid(MGS2_MGS3_SplashscreenResult,
@@ -518,15 +513,10 @@ static void Init_CustomResolution()
518513
ctx.rdx = reinterpret_cast<uintptr_t>(fileName.c_str());
519514
}
520515
});
521-
spdlog::info("MGS 2 | MGS 3: Custom Resolution: Splashscreens patched at {:s}+{:x}", sExeName.c_str(), (uintptr_t)MGS2_MGS3_SplashscreenResult - (uintptr_t)baseModule);
516+
LOG_HOOK(MGS2_MGS3_SplashScreenMidHook, "MGS 2 | MGS 3: Custom Resolution")
522517
}
523518

524-
uint8_t* MGS2_MGS3_LoadingScreenEngScanResult = Memory::PatternScanSilent(baseModule, "48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24"); // /loading.ctxr
525-
if (!MGS2_MGS3_LoadingScreenEngScanResult)
526-
{
527-
spdlog::error("MGS 2 | MGS 3: Custom Resolution: Loading Screen (ENG) {}: Pattern scan failed.");
528-
}
529-
else
519+
if (uint8_t* MGS2_MGS3_LoadingScreenEngScanResult = Memory::PatternScan(baseModule, "48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24", "MGS 2 | MGS 3: Custom Resolution: Loading Screen (ENG)"))
530520
{
531521
static SafetyHookMid MGS2_MGS3_LoadingScreenEngMidHook{};
532522
MGS2_MGS3_LoadingScreenEngMidHook = safetyhook::create_mid(MGS2_MGS3_LoadingScreenEngScanResult,
@@ -536,15 +526,10 @@ static void Init_CustomResolution()
536526
iOutputResY >= 1440 ? reinterpret_cast<uintptr_t>(&"$/misc/loading/****/loading_wqhd.ctxr") :
537527
/*iOutputResY >= 1080*/ reinterpret_cast<uintptr_t>(&"$/misc/loading/****/loading_fhd.ctxr");
538528
});
539-
spdlog::info("MGS 2 | MGS 3: Custom Resolution: Loading Screen (ENG) patched at {:s}+{:x}", sExeName.c_str(), (uintptr_t)MGS2_MGS3_LoadingScreenEngScanResult - (uintptr_t)baseModule);
529+
LOG_HOOK(MGS2_MGS3_LoadingScreenEngMidHook, "MGS 2 | MGS 3: Custom Resolution: Loading Screen (ENG)")
540530
}
541531

542-
uint8_t* MGS2_MGS3_LoadingScreenJPScanResult = Memory::PatternScanSilent(baseModule, "48 8D 4C 24 ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 4C 24"); // /loading_jp.ctxr
543-
if (!MGS2_MGS3_LoadingScreenJPScanResult)
544-
{
545-
spdlog::error("MGS 2 | MGS 3: Custom Resolution: Loading Screen (JPN) {}: Pattern scan failed.");
546-
}
547-
else
532+
if (uint8_t* MGS2_MGS3_LoadingScreenJPScanResult = Memory::PatternScan(baseModule, "48 8D 4C 24 ?? FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 4C 24", "MGS 2 | MGS 3: Custom Resolution: Loading Screen (JP)")) // /loading_jp.ctxr
548533
{
549534
static SafetyHookMid MGS2_MGS3_LoadingScreenJPMidHook{};
550535
MGS2_MGS3_LoadingScreenJPMidHook = safetyhook::create_mid(MGS2_MGS3_LoadingScreenJPScanResult,
@@ -554,7 +539,7 @@ static void Init_CustomResolution()
554539
iOutputResY >= 1440 ? reinterpret_cast<uintptr_t>(&"$/misc/loading/****/loading_jp_wqhd.ctxr") :
555540
/*iOutputResY >= 1080*/ reinterpret_cast<uintptr_t>(&"$/misc/loading/****/loading_jp_fhd.ctxr");
556541
});
557-
spdlog::info("MGS 2 | MGS 3: Custom Resolution: Loading Screen (JP) patched at {:s}+{:x}", sExeName.c_str(), (uintptr_t)MGS2_MGS3_LoadingScreenJPScanResult - (uintptr_t)baseModule);
542+
LOG_HOOK(MGS2_MGS3_LoadingScreenJPMidHook, "MGS 2 | MGS 3: Custom Resolution: Loading Screen (JP)")
558543
}
559544
}
560545

@@ -587,7 +572,7 @@ static void Init_CustomResolution()
587572

588573
// MG 1/2 | MGS 2 | MGS 3: CreateWindowExA
589574
CreateWindowExA_hook = safetyhook::create_inline(CreateWindowExA, reinterpret_cast<void*>(CreateWindowExA_hooked));
590-
spdlog::info("MG/MG2 | MGS 2 | MGS 3: CreateWindowExA: Hooked function.");
575+
LOG_HOOK(CreateWindowExA_hook, "MG/MG2 | MGS 2 | MGS 3: CreateWindowExA")
591576

592577
// MG 1/2 | MGS 2 | MGS 3: SetWindowPos
593578
if (uint8_t* MGS2_MGS3_SetWindowPosScanResult = Memory::PatternScan(baseModule, "33 ?? 48 ?? ?? ?? FF ?? ?? ?? ?? ?? 8B ?? ?? BA 02 00 00 00", "SetWindowPos"))
@@ -703,33 +688,24 @@ static void Init_ScaleEffects()
703688
if ((eGameType & (MGS2|MGS3)) && bOutputResolution)
704689
{
705690
// MGS 2 | MGS 3: Fix scaling for added volume menu in v1.4.0 patch
706-
uint8_t* MGS2_MGS3_VolumeMenuScanResult = Memory::PatternScanSilent(baseModule, "F3 0F ?? ?? 48 ?? ?? ?? 89 ?? ?? ?? 00 00 F3 0F ?? ?? 89 ?? ?? ?? 00 00");
707-
if (MGS2_MGS3_VolumeMenuScanResult)
691+
if (uint8_t* MGS2_MGS3_VolumeMenuScanResult = Memory::PatternScan(baseModule, "F3 0F ?? ?? 48 ?? ?? ?? 89 ?? ?? ?? 00 00 F3 0F ?? ?? 89 ?? ?? ?? 00 00", "MGS 2 | MGS 3: Volume Menu"))
708692
{
709-
spdlog::info("MGS 2 | MGS 3: Volume Menu: Address is {:s}+{:x}", sExeName.c_str(), (uintptr_t)MGS2_MGS3_VolumeMenuScanResult - (uintptr_t)baseModule);
710-
711693
static SafetyHookMid MGS2_MGS3_VolumeMenuMidHook{};
712694
MGS2_MGS3_VolumeMenuMidHook = safetyhook::create_mid(MGS2_MGS3_VolumeMenuScanResult,
713695
[](SafetyHookContext& ctx)
714696
{
715697
ctx.xmm2.f32[0] = (float)1280;
716698
ctx.xmm3.f32[0] = (float)720;
717699
});
718-
}
719-
else if (!MGS2_MGS3_VolumeMenuScanResult)
720-
{
721-
spdlog::error("MGS 2 | MGS 3: Volume Menu: Pattern scan failed.");
700+
LOG_HOOK(MGS2_MGS3_VolumeMenuMidHook, "MGS 2 | MGS 3: Volume Menu")
722701
}
723702
}
724703

725704
if (eGameType & MGS2 && bOutputResolution)
726705
{
727706
// MGS 2: Scale Effects
728-
uint8_t* MGS2_ScaleEffectsScanResult = Memory::PatternScanSilent(baseModule, "48 8B ?? ?? 66 ?? ?? ?? 0F ?? ?? F3 0F ?? ?? F3 0F ?? ?? F3 0F ?? ?? ?? ?? ?? ??");
729-
if (MGS2_ScaleEffectsScanResult)
707+
if (uint8_t* MGS2_ScaleEffectsScanResult = Memory::PatternScan(baseModule, "48 8B ?? ?? 66 ?? ?? ?? 0F ?? ?? F3 0F ?? ?? F3 0F ?? ?? F3 0F ?? ?? ?? ?? ?? ??", "MGS 2: Scale Effects"))
730708
{
731-
spdlog::info("MGS 2: Scale Effects: Address is {:s}+{:x}", sExeName.c_str(), (uintptr_t)MGS2_ScaleEffectsScanResult - (uintptr_t)baseModule);
732-
733709
float fMGS2_DefaultEffectScaleX = *reinterpret_cast<float*>(Memory::GetAbsolute((uintptr_t)MGS2_ScaleEffectsScanResult - 0x4));
734710
float fMGS2_DefaultEffectScaleY = *reinterpret_cast<float*>(Memory::GetAbsolute((uintptr_t)MGS2_ScaleEffectsScanResult + 0x28));
735711
spdlog::info("MGS 2: Scale Effects: Default X is {}, Y is {}", fMGS2_DefaultEffectScaleX, fMGS2_DefaultEffectScaleY);
@@ -773,10 +749,6 @@ static void Init_ScaleEffects()
773749
ctx.xmm1.f32[0] = fMGS2_EffectScaleY;
774750
});
775751
}
776-
else if (!MGS2_ScaleEffectsScanResult)
777-
{
778-
spdlog::error("MGS 2: Scale Effects: Pattern scan failed.");
779-
}
780752
}
781753

782754
}
@@ -1403,10 +1375,10 @@ static void InitializeSubsystems()
14031375
INITIALIZE(g_Logging.LogSysInfo()); //0
14041376
INITIALIZE(ASILoaderCompatibility::Check()); //1
14051377
INITIALIZE(DetectGame()); //2
1406-
INITIALIZE(g_GameVars.Initialize()); //3
1407-
INITIALIZE(g_D3D11Hooks.Initialize()); //4 Caches the D3DDevice, DXGIFactory, and D3DContext from D3DCreateDevice/DXGICreateFactory
1408-
INITIALIZE(g_SteamAPI.Setup()); //5 Hook early so we don't miss any Steam API calls.
1409-
INITIALIZE(Init_ReadConfig()); //6
1378+
INITIALIZE(Init_ReadConfig()); //3
1379+
INITIALIZE(g_GameVars.Initialize()); //4
1380+
INITIALIZE(g_D3D11Hooks.Initialize()); //5 Caches the D3DDevice, DXGIFactory, and D3DContext from D3DCreateDevice/DXGICreateFactory
1381+
INITIALIZE(g_SteamAPI.Setup()); //6 Hook early so we don't miss any Steam API calls.
14101382
INITIALIZE(ReshadeCompatibility::Check()); //7 Dependent on ReadConfig, must also be before LauncherConfigOverride to warn the user before a crash.
14111383
INITIALIZE(Init_CalculateScreenSize()); //8
14121384
INITIALIZE(Init_LauncherConfigOverride()); //9

src/fixes/effect_speeds.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ int64_t __fastcall MGS2_splashPartsSlow(DWORD* a1, __int16* a2, float duration)
8484
#endif
8585
safetyhook::MidHook debrisVelocityHook;
8686

87-
float g_VelocityScale = (1.0f * FRAME_IOP_DIVIDER);
88-
8987
void EffectSpeedFix::Initialize()
9088
{
9189
if (!(eGameType & MGS2))
@@ -195,7 +193,9 @@ void EffectSpeedFix::Initialize()
195193
{
196194
//spdlog::info("flying_smoke_slow before {}", reghelpers::Getr8d(ctx));
197195
reghelpers::set_r8d(ctx, static_cast<unsigned int>((g_GameVars.ActorWaitMultiplier() * (g_GameVars.InCutscene() ? 2.0 : 1.0)) * reghelpers::get_r8d(ctx)));
196+
#ifdef _MGSDEBUGGING
198197
spdlog::info("flying_smoke_slow after {}", reghelpers::get_r8d(ctx));
198+
#endif
199199
});
200200
LOG_HOOK(flyingSmokeSlow_MidHook, "MGS 2: Effect Speed Fix: effect3\\flying_smoke_slow.c")
201201
}
@@ -236,7 +236,7 @@ void EffectSpeedFix::Initialize()
236236
}
237237
else if (strcmp(g_GameVars.GetCurrentStage(), "d012p01") == 0) // P012_01_P01 Fortune encounter 1 polygon demo 1 (BC connecting bridge - Fortune vs Seals encounter)
238238
{
239-
ctx.xmm0.f32[0] /= 4.0f;
239+
ctx.xmm0.f32[0] /= 18.0f;
240240
}
241241
else
242242
{

src/resources/MGSHDFix.rc

136 Bytes
Binary file not shown.

src/resources/d3d11_api.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
#include <d3d11_api.hpp>
1+
#include "common.hpp"
2+
#include "d3d11_api.hpp"
23
#include "logging.hpp"
3-
#include <RegStateHelpers.hpp>
4+
45
#pragma comment(lib,"d3d11.lib")
56
#pragma comment(lib, "dxgi.lib")
67

@@ -81,9 +82,9 @@ HRESULT WINAPI CreateDXGIFactory_hooked(REFIID riid, _COM_Outptr_ void** ppFacto
8182
void D3D11Hooks::Initialize()
8283
{
8384
D3D11CreateDevice_hook = safetyhook::create_inline(D3D11CreateDevice, reinterpret_cast<void*>(D3D11CreateDevice_hooked));
84-
spdlog::info("MG/MG2 | MGS 2 | MGS 3: D3D11CreateDevice: Hooked function.");
85+
LOG_HOOK(D3D11CreateDevice_hook, "MG/MG2 | MGS 2 | MGS 3: D3D11CreateDevice: Hooked function.")
8586
CreateDXGIFactory_hook = safetyhook::create_inline(CreateDXGIFactory, reinterpret_cast<void*>(CreateDXGIFactory_hooked));
86-
spdlog::info("MG/MG2 | MGS 2 | MGS 3: CreateDXGIFactory: Hooked function.");
87+
LOG_HOOK(CreateDXGIFactory_hook, "MG/MG2 | MGS 2 | MGS 3: CreateDXGIFactory: Hooked function.")
8788
}
8889

8990

src/resources/logging.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void Logging::Initialize()
114114
spdlog::info("Module Path: {0:s}", sExePath.string());
115115
spdlog::info("Module Address: 0x{0:x}", (uintptr_t)baseModule);
116116
spdlog::info("Module Version: {}", Memory::GetModuleVersion(baseModule));
117-
if (std::filesystem::exists(sExePath / "steamclient64.dll") || std::filesystem::exists(sExePath / "steamclient.dll") || std::filesystem::exists(sExePath / "GameOverlayRenderer64.dll") || std::filesystem::exists(sExePath / "GameOverlayRenderer.dll") || std::filesystem::exists(sExePath / "Renderer.dll"))
117+
if (std::filesystem::exists(sExePath / "steamclient64.dll") || std::filesystem::exists(sExePath / "steamclient.dll") || std::filesystem::exists(sExePath / "GameOverlayRenderer64.dll") || std::filesystem::exists(sExePath / "GameOverlayRenderer.dll"))
118118
{
119119
g_SteamAPI.bIsLegitCopy = false;
120120
spdlog::warn("Piracy Warning: This has been detected as a pirated copy of the game. Crashing issues are VERY likely to occur due to missing memory patterns.");

src/resources/steamworks_api.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@ void SteamAPI::Setup()
1919
return;
2020
}
2121

22-
if (uint8_t* AfterSteamSetupResult = Memory::PatternScan(baseModule, "48 8B 05 ?? ?? ?? ?? 8B CB", "Steam API Initialization"))
22+
if (!(eGameType & (MG|MGS2|MGS3)))
23+
{
24+
return;
25+
}
26+
27+
if (uint8_t* AfterSteamSetupResult = Memory::PatternScan(baseModule, eGameType & MGS2 ? "48 8B 05 ?? ?? ?? ?? 8B CB" : (eGameType & MGS3 ? "48 8B 05 ?? ?? ?? ?? 8B CF 83 78" : "48 8B 05 ?? ?? ?? ?? 8B CF"), "Steam API Initialization"))
2328
{
2429
static SafetyHookMid SteamMidhook {};
2530
SteamMidhook = safetyhook::create_mid(AfterSteamSetupResult,
2631
[](SafetyHookContext& ctx)
2732
{
2833
g_SteamAPI.FetchAndCacheSteamID();
2934
});
30-
spdlog::info("SteamAPI: Hook placed at Steam initialization.");
35+
LOG_HOOK(SteamMidhook, "SteamAPI Initialization")
3136
}
3237
}
3338

src/resources/version_checking.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <iomanip>
1111

1212
#include "logging.hpp"
13+
#include "version.h"
1314

1415
#pragma comment(lib, "winhttp.lib")
1516

@@ -56,14 +57,18 @@ bool LatestVersionChecker::checkForUpdates()
5657
saveCache(cachedLatest, warnedVersion); // preserve warnedVersion
5758
}
5859
}
60+
else
61+
{
62+
spdlog::info("Version Check: Under 24 hours since last version check. Skipping Github API call.");
63+
}
5964

6065
int cmp = compareSemVer(m_dllVersion, cachedLatest);
6166

6267
if (cmp < 0)
6368
{
6469
// Always log to spdlog when outdated
6570
spdlog::warn("Version Check: A new version of MGSHDFix is available.");
66-
spdlog::warn("Current Version : {}, Latest Release : {}", m_dllVersion, cachedLatest);
71+
spdlog::warn("Vection Check - Current Version : {}, Latest Release : {}", m_dllVersion, cachedLatest);
6772

6873
if (warnedVersion != cachedLatest)
6974
{
@@ -84,7 +89,7 @@ bool LatestVersionChecker::checkForUpdates()
8489
else if (cmp > 0)
8590
{
8691
spdlog::info("Version Check: Welcome back, Commander! You're running a development build of MGSHDFix!");
87-
spdlog::info("Current Version : {}, Latest Release : {}", m_dllVersion, cachedLatest);
92+
spdlog::info("Version Check - Current Version : {}, Latest Release : {}", m_dllVersion, cachedLatest);
8893
return false;
8994
}
9095

@@ -126,15 +131,28 @@ void LatestVersionChecker::saveCache(const std::string& latestVersion, const std
126131
file << warnedVersion << "\n";
127132
}
128133

134+
std::wstring LatestVersionChecker::buildUserAgent() const
135+
{
136+
std::string ua = "MGSHDFix/";
137+
ua += VERSION_STRING;
138+
return std::wstring(ua.begin(), ua.end());
139+
}
140+
129141
bool LatestVersionChecker::queryGitHubLatestVersion(std::string& latestVersion)
130142
{
143+
spdlog::info("Version Check: Contacting GitHub API for latest version...");
131144
HINTERNET hSession = WinHttpOpen(
132-
L"MGSHDFix/1.0",
145+
buildUserAgent().c_str(),
133146
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
134147
nullptr,
135148
nullptr,
136149
0);
137-
if (!hSession) return false;
150+
151+
if (!hSession)
152+
{
153+
spdlog::error("WinHttpOpen failed.");
154+
return false;
155+
}
138156

139157
HINTERNET hConnect = WinHttpConnect(
140158
hSession,
@@ -165,11 +183,12 @@ bool LatestVersionChecker::queryGitHubLatestVersion(std::string& latestVersion)
165183
return false;
166184
}
167185

186+
std::wstring userAgentHeader = L"User-Agent: " + buildUserAgent();
168187
WinHttpAddRequestHeaders(
169188
hRequest,
170-
L"User-Agent: MGSHDFix",
189+
userAgentHeader.c_str(),
171190
-1,
172-
WINHTTP_ADDREQ_FLAG_ADD);
191+
WINHTTP_ADDREQ_FLAG_REPLACE);
173192

174193
std::string response;
175194

0 commit comments

Comments
 (0)