Skip to content

Commit

Permalink
Fixed letterboxing for all resolutions
Browse files Browse the repository at this point in the history
  • Loading branch information
LennardF1989 committed Nov 17, 2017
1 parent fdf8abb commit bdf143b
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 42 deletions.
283 changes: 243 additions & 40 deletions Src/NiohResolution/Program.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Steamless.API.Model;
using Steamless.API.Services;
using Steamless.Unpacker.Variant31.x64;
Expand All @@ -14,16 +16,41 @@ public class Program
private const string EXE_FILE_BACKUP = "nioh.exe.bak";
private const string EXE_FILE_UNPACKED = "nioh.exe.unpacked.exe";

private static readonly byte[] _exePattern = { 0x80, 0x07, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00 };
private const string PATTERN_ASPECTRATIO1 = "C7 43 50 39 8E E3 3F";
private const int PATTERN_ASPECTRATIO1_OFFSET = 3;

private const string PATTERN_ASPECTRATIO2 = "00 00 87 44 00 00 F0 44";

private const string PATTERN_MAGIC1 = "0F 95 C0 88 46 34";
private const string PATTERN_MAGIC1_PATCH = "32 C0 90 88 46 34";

private const string PATTERN_MAGIC2_A = "45 85 D2 7E 1A";
private const string PATTERN_MAGIC2_A_PATCH = "45 85 D2 EB 1A";

private const string PATTERN_MAGIC2_B = "C3 79 14";
private const string PATTERN_MAGIC2_B_PATCH = "C3 EB 14";

private const string PATTERN_RESOLUTION = "80 07 00 00 38 04 00 00";

public static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

Console.WriteLine("Welcome to the Nioh Resolution patcher!");

Console.WriteLine("\nPlease specify your resolution.");
Console.WriteLine("\nPlease enter your desired resolution.\n");

int width = ReadInt("Width", 1920);
int height = ReadInt("Height", 1080);

int width = ReadInt("Width");
int height = ReadInt("Height");
Console.WriteLine("\nPlease enter a scale for your desired render resolution.\n");
Console.WriteLine("Keep in mind that this scale:");
Console.WriteLine("- Has no effect on the entered resolution.");
Console.WriteLine("- Has no effect on the in-game UI.");
Console.WriteLine("- Can less than 1.0 to increase performance.");
Console.WriteLine();

float scale = ReadFloat("Scale", 1.0f);

if (!File.Exists(EXE_FILE))
{
Expand All @@ -38,40 +65,29 @@ public static void Main(string[] args)

File.Copy(EXE_FILE, EXE_FILE_BACKUP, true);

Console.WriteLine($"Unpacking {EXE_FILE}...");
Console.WriteLine($"\nUnpacking {EXE_FILE}...");

var result = UnpackExe();
if (!result)
{
Exit();

return;
}
Console.WriteLine($"Patching resolution of {width}x{height}...");

Console.WriteLine($"\nApplying resolution of {width}x{height}...");

var buffer = File.ReadAllBytes(EXE_FILE_UNPACKED);
var positions = FindSequence(buffer, _exePattern, 0);
var resolution = ConvertToBytes(width, height);

if (!positions.Any())
result = PatchExe(ref buffer, width, height, scale);
if (!result)
{
Console.WriteLine($"Could not find any offsets in {EXE_FILE_UNPACKED}!");

Exit();

return;
}

foreach (int position in positions)
{
Console.WriteLine($"Patching offset {position}...");

for (int i = 0; i < resolution.Length; i++)
{
buffer[position + i] = resolution[i];
}
}

Console.WriteLine("Cleaning up...");
Console.WriteLine("\nCleaning up...");

File.WriteAllBytes(EXE_FILE, buffer);
File.Delete(EXE_FILE_UNPACKED);
Expand All @@ -96,7 +112,7 @@ private static bool UnpackExe()

if (!result)
{
Console.WriteLine($"Cannot process {EXE_FILE}!");
Console.WriteLine($"-> Cannot process {EXE_FILE}!");

Exit();

Expand All @@ -111,7 +127,7 @@ private static bool UnpackExe()

if (!result)
{
Console.WriteLine($"Could not process {EXE_FILE}!");
Console.WriteLine($"-> Could not process {EXE_FILE}!");

Exit();

Expand All @@ -121,41 +137,199 @@ private static bool UnpackExe()
return true;
}

private static int ReadInt(string name)
private static bool PatchExe(ref byte[] buffer, int width, int height, float scale)
{
return
PatchAspectRatio(ref buffer, width, height) &&
PatchResolution(ref buffer, width, height, scale);
}

//Source: http://www.wsgf.org/forums/viewtopic.php?f=64&t=32376&sid=4b092cca9c19f600524045733f6db9ab&start=110
private static bool PatchAspectRatio(ref byte[] buffer, int width, int height)
{
float ratio = width / (float)height;
float ratioWidth = 1920;
float ratioHeight = 1080;

if (ratio < 1.77777)
{
ratioHeight = ratioWidth / ratio;
}
else
{
ratioWidth = ratioHeight * ratio;
}

//Aspect Ratio Fix #1
var positions = FindSequence(ref buffer, StringToPattern(PATTERN_ASPECTRATIO1), 0);

if (!AssertPatch(nameof(PATTERN_ASPECTRATIO1), 1, positions.Count))
{
return false;
}

var ratio1Patch = ConvertToBytes(ratio);
Patch(ref buffer, positions.First() + PATTERN_ASPECTRATIO1_OFFSET, ratio1Patch);

//Aspect Ratio Fix #2
positions = FindSequence(ref buffer, StringToPattern(PATTERN_ASPECTRATIO2), 0);

if (!AssertPatch(nameof(PATTERN_ASPECTRATIO2), 1, positions.Count))
{
return false;
}

var ratio2Patch = ConvertToBytes(ratioHeight).Concat(ConvertToBytes(ratioWidth)).ToArray();
Patch(ref buffer, positions, ratio2Patch);

//Magic Fix #1
positions = FindSequence(ref buffer, StringToPattern(PATTERN_MAGIC1), 0);

if (!AssertPatch(nameof(PATTERN_MAGIC1), 1, positions.Count))
{
return false;
}

Patch(ref buffer, positions, StringToPattern(PATTERN_MAGIC1_PATCH));

//Magic Fix #2 - A
positions = FindSequence(ref buffer, StringToPattern(PATTERN_MAGIC2_A), 0);

if (!AssertPatch(nameof(PATTERN_MAGIC2_A), 1, positions.Count))
{
return false;
}

Patch(ref buffer, positions, StringToPattern(PATTERN_MAGIC2_A_PATCH));

//Magic Fix #2 - B
positions = FindSequence(ref buffer, StringToPattern(PATTERN_MAGIC2_B), positions.First());

if (!AssertPatch(nameof(PATTERN_MAGIC2_B), 1, positions.Count))
{
return false;
}

Patch(ref buffer, positions.First(), StringToPattern(PATTERN_MAGIC2_B_PATCH));

return true;
}

private static bool PatchResolution(ref byte[] buffer, int width, int height, float scale)
{
var positions = FindSequence(ref buffer, StringToPattern(PATTERN_RESOLUTION), 0);

if (!AssertPatch(nameof(PATTERN_RESOLUTION), 2, positions.Count))
{
return false;
}

//Window resolution
var resolution = ConvertToBytes(width).Concat(ConvertToBytes(height)).ToArray();
Patch(ref buffer, positions[0], resolution);

//Internal resolution
int internalWidth = (int) Math.Round(width * scale);
int internalHeight = (int) Math.Round(height * scale);

resolution = ConvertToBytes(internalWidth).Concat(ConvertToBytes(internalHeight)).ToArray();
Patch(ref buffer, positions[1], resolution);

return true;
}

private static void Exit()
{
Console.ReadKey();
}

private static int ReadInt(string name, int defaultValue)
{
int input;

do
{
Console.Write($"{name}: ");
Console.Write($"-> {name} [default = {defaultValue}]: ");

string inputString = Console.ReadLine();

if (string.IsNullOrWhiteSpace(inputString))
{
return defaultValue;
}


int.TryParse(inputString, out input);

if (input <= 0)
{
Console.WriteLine("--> Invalid value, try again!");
}
} while (input <= 0);

return input;
}

private static float ReadFloat(string name, float defaultValue)
{
float input;

do
{
Console.Write($"-> {name} [default = {defaultValue:F1}]: ");

string inputString = Console.ReadLine();

if (string.IsNullOrWhiteSpace(inputString))
{
return defaultValue;
}

int.TryParse(Console.ReadLine(), out input);
float.TryParse(inputString, out input);

if (input <= 0)
{
Console.WriteLine($"That's not a valid {name.ToLower()}, try again!");
Console.WriteLine("--> Invalid value, try again!");
}
} while (input <= 0);

return input;
}

private static byte[] ConvertToBytes(int width, int height)
private static byte[] ConvertToBytes(int value)
{
byte[] widthBytes = BitConverter.GetBytes(width);
byte[] heightBytes = BitConverter.GetBytes(height);
byte[] bytes = BitConverter.GetBytes(value);

if (!BitConverter.IsLittleEndian)
{
Array.Reverse(widthBytes);
Array.Reverse(heightBytes);
Array.Reverse(bytes);
}

return widthBytes.Concat(heightBytes).ToArray();
return bytes;
}

private static byte[] ConvertToBytes(float value)
{
byte[] bytes = BitConverter.GetBytes(value);

if (!BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}

return bytes;
}

private static byte[] StringToPattern(string pattern)
{
return pattern
.Split(' ')
.Select(x => Convert.ToByte(x, 16))
.ToArray();
}

//Source: https://stackoverflow.com/questions/283456/byte-array-pattern-search
private static List<int> FindSequence(byte[] buffer, byte[] pattern, int startIndex)
private static List<int> FindSequence(ref byte[] buffer, byte[] pattern, int startIndex)
{
List<int> positions = new List<int>();

Expand All @@ -170,17 +344,46 @@ private static List<int> FindSequence(byte[] buffer, byte[] pattern, int startIn
if (segment.SequenceEqual(pattern))
{
positions.Add(i);
}

i = Array.IndexOf(buffer, pattern[0], i + pattern.Length);
i = Array.IndexOf(buffer, pattern[0], i + pattern.Length);
}
else
{
i = Array.IndexOf(buffer, pattern[0], i + 1);
}
}

return positions;
}

private static void Exit()
private static bool AssertPatch(string name, int expected, int value)
{
Console.ReadKey();
if (value != expected)
{
Console.WriteLine($"-> Expected to find {expected} {name} offset(s) in {EXE_FILE_UNPACKED}, but found {value}!");

return false;
}

return true;
}

private static void Patch(ref byte[] buffer, List<int> positions, byte[] patchBytes)
{
foreach (int position in positions)
{
Patch(ref buffer, position, patchBytes);
}
}

private static void Patch(ref byte[] buffer, int position, byte[] patchBytes)
{
Console.WriteLine($"-> Patching offset {position}");

for (int i = 0; i < patchBytes.Length; i++)
{
buffer[position + i] = patchBytes[i];
}
}
}
}
4 changes: 2 additions & 2 deletions Src/NiohResolution/Properties/AssemblyInfo.cs
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]

0 comments on commit bdf143b

Please sign in to comment.