EonaCatMGSTPP-Resolution-Patcher C#
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
10 KiB

using System;
using System.IO;
using System.Windows;
using Microsoft.Win32;
using System.Management;
using System.Runtime.CompilerServices;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using static EonaCatMGSTPP_Resolution_Patcher.ResolutionDetector;
namespace EonaCatMGSTPP_Resolution_Patcher
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private byte[] _replace;
private Dictionary<string, byte[]> _resolutionsDictionary = new Dictionary<string, byte[]>();
public MainWindow()
{
InitializeComponent();
GetAllValidResolutions();
FillInKnownResolutions();
FillInComboBox();
}
private static byte[] GetBytesArrayFromResolution(int resolutionX, int resolutionY)
{
var resultX = resolutionX.ToString("X4");
var resultY = resolutionY.ToString("X4");
var resultXInBytes = StringToByteArray(resultX);
var resultYInBytes = StringToByteArray(resultY);
return resultXInBytes.Concat(resultYInBytes).ToArray();
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private void FillInComboBox()
{
foreach (var resolution in _resolutionsDictionary.Keys)
{
comboBox.Items.Add(resolution);
}
comboBox.SelectedIndex = 0;
}
private void FillInKnownResolutions()
{
_resolutionsDictionary["1024X768 (4:3)"] = GetBytesArrayFromResolution(1024, 768);
_resolutionsDictionary["1280X1024 (5:4)"] = GetBytesArrayFromResolution(1280, 1024);
_resolutionsDictionary["1366X768 (16:9)"] = GetBytesArrayFromResolution(1366, 768);
_resolutionsDictionary["1440X900 (16:10)"] = GetBytesArrayFromResolution(1440, 900);
_resolutionsDictionary["1680X1050 (16:10)"] = GetBytesArrayFromResolution(1680, 1050);
_resolutionsDictionary["1600X1200 (16:10)"] = GetBytesArrayFromResolution(1600, 1200);
_resolutionsDictionary["1920X1080 (16:9)"] = GetBytesArrayFromResolution(1920, 1080);
_resolutionsDictionary["2560X1080 (21:9)"] = GetBytesArrayFromResolution(2560, 1080);
_resolutionsDictionary["3440X1440 (21:9)"] = GetBytesArrayFromResolution(3440, 1440);
_resolutionsDictionary["5040x1050 (48:10)"] = GetBytesArrayFromResolution(5040, 1050);
_resolutionsDictionary["4800X900 (48:9)"] = GetBytesArrayFromResolution(4800, 900);
_resolutionsDictionary["7860X1080 (48:9)"] = GetBytesArrayFromResolution(7860, 1080);
_resolutionsDictionary["7860X2160 (48:9)"] = GetBytesArrayFromResolution(7860, 2160);
}
/* // Is not used anymore
private void OldKnownResolutions()
{
_resolutionsDictionary["1024X768 (4:3)"] = new byte[] { 0xAB, 0xAA, 0xAA, 0x3F };
_resolutionsDictionary["1280X1024 (5:4)"] = new byte[] { 0x00, 0x00, 0x0A, 0x3F };
_resolutionsDictionary["1366X768 (16:9)"] = new byte[] { 0xAB, 0xAA, 0xE3, 0x3F };
_resolutionsDictionary["1440X900 (16:10)"] = new byte[] { 0xCD, 0xCC, 0xCC, 0x3F };
_resolutionsDictionary["1680X1050 (16:10)"] = new byte[] { 0xCD, 0xCC, 0xCC, 0x3F };
_resolutionsDictionary["1920X1080 (16:9)"] = new byte[] { 0x39, 0x8E, 0xE3, 0x3F };
_resolutionsDictionary["2560X1080 (21:9)"] = new byte[] { 0x26, 0xB4, 0x17, 0x40 };
_resolutionsDictionary["3440X1440 (21:9)"] = new byte[] { 0x8E, 0xE3, 0x18, 0x40 };
_resolutionsDictionary["5040x1050 (48:10)"] = new byte[] { 0x9A, 0x99, 0x99, 0x40 };
_resolutionsDictionary["4800X900 (48:9)"] = new byte[] { 0xBA, 0xAA, 0xAA, 0x40 };
_resolutionsDictionary["7860X1080 (48:9)"] = new byte[] { 0xBA, 0xAA, 0xAA, 0x40 };
}
*/
// Doesnt work yet
private void GetAllValidResolutions()
{
var vDevMode = new DEVMODE();
var i = 0;
while (EnumDisplaySettings(null, i, ref vDevMode))
{
Console.WriteLine("Width:{0} Height:{1} Color:{2} Frequency:{3}",
vDevMode.dmPelsWidth,
vDevMode.dmPelsHeight,
1 << vDevMode.dmBitsPerPel, vDevMode.dmDisplayFrequency
);
i++;
}
var scope = new ManagementScope();
var query = new ObjectQuery("SELECT * FROM CIM_VideoControllerResolution");
using (var searcher = new ManagementObjectSearcher(scope, query))
{
var results = searcher.Get();
//previous resolution for checking when new resolution is found
var previousResolution = string.Empty;
//current resolution
var currentResolution = string.Empty;
foreach (var result in results)
{
currentResolution = result["HorizontalResolution"] + "x" + result["VerticalResolution"];
if (currentResolution != previousResolution)
{
comboBox.Items.Add(currentResolution);
previousResolution = currentResolution;
}
}
}
}
private void PatchButton_Click(object sender, RoutedEventArgs e)
{
if (comboBox.Text.Length > 0)
{
PatchFile();
}
else
{
MessageBox.Show("Please select a resolution!");
}
}
private void PatchFile()
{
var startupFolder = @"I:\Steam\SteamApps\common\MGS_TPP";
var dialog = new OpenFileDialog
{
Title = "Select file to patch",
DefaultExt = ".exe",
Filter = "Exe Files (*.exe) | *.exe"
};
// Set to my own resolution
if (Directory.Exists(startupFolder))
{
dialog.InitialDirectory = startupFolder;
_replace = _resolutionsDictionary["2560X1080 (21:9)"];
}
else
{
_replace = _resolutionsDictionary[comboBox.Text];
}
var result = dialog.ShowDialog();
if (result.HasValue && result.Value)
{
var filename = dialog.FileName;
Exception error;
if (PatchTheFile(filename, out error))
{
MessageBox.Show("The file was patched successfully");
}
else
{
if (error != null)
{
MessageBox.Show($"The file failed to patch! => '{error.Message}'");
}
else
{
MessageBox.Show($"The file failed to patch! => 'Could not find original hexValues, are you sure you are trying to patch an UNPATCHED executable?'");
}
}
MessageBox.Show("Patcher created by EonaCat");
}
}
private static void CreateBackup(string filename)
{
var dirPath = Path.GetDirectoryName(filename);
var fileName = Path.GetFileName(filename);
var backupExtension = ".backup";
var files = Directory.GetFiles(dirPath);
var count = files.Count(file => { return file.Contains(fileName + backupExtension); });
var newFileName = (count == 0) ? filename + backupExtension : $"{fileName} ({count + 1}){backupExtension}";
File.Copy(filename, newFileName);
}
private static readonly byte[] PatchFind = { 0x39, 0x8E, 0xE3, 0x3F };
private static readonly byte[] PatchReplace = { 0x26, 0xb4, 0x17, 0x40 };
private static bool DetectPatch(byte[] sequence, int position)
{
if (position + PatchFind.Length > sequence.Length) return false;
for (int p = 0; p < PatchFind.Length; p++)
{
if (PatchFind[p] != sequence[position + p]) return false;
}
return true;
}
private static bool PatchTheFile(string filename, out Exception error)
{
var foundValueForPatching = false;
error = null;
try
{
// Ensure target directory exists.
var targetDirectory = Path.GetDirectoryName(filename);
if (targetDirectory == null) return false;
Directory.CreateDirectory(targetDirectory);
// Read file bytes.
var fileContent = File.ReadAllBytes(filename);
// Detect and patch file.
for (int p = 0; p < fileContent.Length; p++)
{
if (!DetectPatch(fileContent, p)) continue;
foundValueForPatching = true;
for (int w = 0; w < PatchFind.Length; w++)
{
fileContent[p + w] = PatchReplace[w];
}
}
if (foundValueForPatching)
{
CreateBackup(filename);
// Save it to another location.
File.WriteAllBytes(filename, fileContent);
}
return foundValueForPatching;
}
catch (Exception exception)
{
error = exception;
return false;
}
}
}
}