diff options
Diffstat (limited to 'Research')
| -rwxr-xr-x | Research/parse_lmt.py | 709 | ||||
| -rw-r--r-- | Research/pl_face.edit.xz | bin | 0 -> 3756184 bytes | |||
| -rw-r--r-- | Research/research.txt | 51 |
3 files changed, 760 insertions, 0 deletions
diff --git a/Research/parse_lmt.py b/Research/parse_lmt.py new file mode 100755 index 0000000..ff977a6 --- /dev/null +++ b/Research/parse_lmt.py @@ -0,0 +1,709 @@ +#! /usr/bin/env python3 + +import sys +import os +import io +import math +import struct +import ctypes +from ctypes import c_char, c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64, c_float +from typing import Optional, Self + +# This is entirely based on MHW-Free-HyperKinetics (https://github.com/AsteriskAmpersand/MHW-Free-HyperKinetics). + +class Vector(): + x: float + y: float + z: float + def __str__(self) -> str: + return f'({self.x:8.5f}, {self.y:8.5f}, {self.z:8.5f})' + +class Quaternion(): + x: float + y: float + z: float + w: float + def __str__(self) -> str: + return f'({self.x:12.9f}, {self.y:12.9f}, {self.z:12.9f}, {self.w:12.9f})' + +class Tuple(): + v: float + w: float + def __str__(self) -> str: + return f'({self.x:8.5f}, {self.w:8.5f})' + +class V_Type(): + X = 1 + Y = 2 + Z = 3 + +class C_Struct(ctypes.LittleEndianStructure): + @classmethod + def size(cls) -> int: + return ctypes.sizeof(cls) + + # Default unpack()/pack() relies on ctypes behavior. + @classmethod + def unpack(cls, data) -> Self: + return cls.from_buffer_copy(data) + + def pack(self) -> bytes: + return bytes(self) + +class C_Packed_Value(C_Struct): + struct_format: str + + @classmethod + def size(cls) -> int: + return struct.calcsize(cls.struct_format) + + def normalize(self) -> None: + pass + + def denormalize(self) -> None: + pass + + def __str__(self): + s = '(' + for field in self._fields_ : + s += f'{getattr(self, field[0])}, ' + s = s[0:-2] + ')' + return s + +class C_Packed_Value_Int(C_Packed_Value): + struct_format: str + bits: int + offset: int = 8 + mulmin: int = 7 + is_quaternion: bool = False + is_tuple: bool = False + v_is: Optional[V_Type] = None + raw_value: bool = False + _norm_factor: Optional[int] = None + + def __init__(self): + self.n = self.n_type() + + @classmethod + def norm_factor(cls) -> int: + if cls._norm_factor is None: + cls._norm_factor = (2**cls.bits) - 1 - cls.mulmin - cls.offset + return cls._norm_factor + + @classmethod + def normalize_int_value(cls, v: int) -> float: + return (v - cls.offset) / cls.norm_factor() + + @classmethod + def denormalize_int_value(cls, v: float) -> int: + return round((v * cls.norm_factor()) + cls.offset) + + def normalize(self) -> None: + if self.is_tuple: + self.n.v = self.normalize_int_value(self.v) + else: + self.n.x = self.normalize_int_value(self.x) + self.n.y = self.normalize_int_value(self.y) + self.n.z = self.normalize_int_value(self.z) + if self.is_quaternion: + self.n.w = self.normalize_int_value(self.w) + + def denormalize(self) -> None: + if self.is_tuple: + self.v = self.denormalize_int_value(self.n.v) + else: + self.x = self.denormalize_int_value(self.n.x) + self.y = self.denormalize_int_value(self.n.y) + self.z = self.denormalize_int_value(self.n.z) + if self.is_quaternion: + self.w = self.denormalize_int_value(self.n.w) + +class Vector_Float_Base(C_Packed_Value): + struct_format: str = '<fff' + raw_value: bool = True + _pack_ = 1 + _fields_ = [ + ("x", c_float), + ("y", c_float), + ("z", c_float) + ] + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = data[0] + r.y = data[1] + r.z = data[2] + return r + + def pack(self) -> None: + return struct.pack(self.struct_format, self.x, self.y, self.z) + +class Vector_Float(C_Packed_Value): + struct_format: str = '<fffI' + raw_value: bool = True + _pack_ = 1 + _fields_ = [ + ("x", c_float), + ("y", c_float), + ("z", c_float), + ("frame", c_uint32) + ] + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = data[0] + r.y = data[1] + r.z = data[2] + r.frame = data[3] + return r + + def pack(self) -> None: + return struct.pack(self.struct_format, self.x, self.y, self.z, self.frame) + +class Vector_Short(C_Packed_Value_Int): + struct_format: str = '<HHHH' + bits: int = 16 + n_type: type = Vector + _pack_ = 1 + _fields_ = [ + ("x", c_uint16), + ("y", c_uint16), + ("z", c_uint16), + ("frame", c_uint16) + ] + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = data[0] + r.y = data[1] + r.z = data[2] + r.frame = data[3] + return r + + def pack(self) -> None: + return struct.pack(self.struct_format, self.x, self.y, self.z, self.frame) + +class Vector_Byte(C_Packed_Value_Int): + struct_format: str = '<BBBB' + bits: int = 8 + n_type: type = Vector + _pack_ = 1 + _fields_ = [ + ("x", c_uint8), + ("y", c_uint8), + ("z", c_uint8), + ("frame", c_uint8) + ] + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = data[0] + r.y = data[1] + r.z = data[2] + r.frame = data[3] + return r + + def pack(self) -> None: + return struct.pack(self.struct_format, self.x, self.y, self.z, self.frame) + +class Quaternion_7Bit(C_Packed_Value_Int): + struct_format: str = '<I' + bits: int = 7 + is_quaternion: bool = True + n_type: type = Quaternion + _pack_ = 1 + _fields_ = [ + ("w", c_uint32, 7), + ("z", c_uint32, 7), + ("y", c_uint32, 7), + ("x", c_uint32, 7), + ("frame", c_uint32, 4) + ] + + # ffffxxxxxxxyyyyyyyzzzzzzzwwwwwww + def pack(self) -> None: + return struct.pack(self.struct_format, + (self.frame << 28 | self.x << 21 | self.y << 14 | self.z << 7 | self.w) & 0xFFFFFFFF) + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data)[0] + r.w = data & 0x7F + r.z = (data >> 7) & 0x7F + r.y = (data >> 14) & 0x7F + r.x = (data >> 21) & 0x7F + r.frame = (data >> 28) & 0xF + return r + +class Quaternion_9Bit(C_Packed_Value_Int): + struct_format: str = '<BBBBB' + bits: int = 9 + is_quaternion: bool = True + n_type: type = Quaternion + _pack_ = 1 + _fields_ = [ + ("x", c_uint64, 9), + ("y", c_uint64, 9), + ("z", c_uint64, 9), + ("w", c_uint64, 9), + ("frame", c_uint64, 4) + ] + + # xxxxxxxx|yyyyyyyx|zzzzzzyy|wwwwwzzz|ffffwwww + def pack(self) -> None: + return struct.pack(self.struct_format, + (self.x >> 1) & 0xFF, + ((self.y >> 2) << 1 | self.x & 0x1) & 0xFF, + ((self.z >> 3) << 2 | self.y & 0x3) & 0xFF, + ((self.w >> 4) << 3 | self.z & 0x7) & 0xFF, + (self.frame << 4 | self.w & 0xF) & 0xFF) + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = (data[0] << 1 | data[1] & 0x1) & 0x1FF + r.y = ((data[1] >> 1) << 2 | data[2] & 0x3) & 0x1FF + r.z = ((data[2] >> 2) << 3 | data[3] & 0x7) & 0x1FF + r.w = ((data[3] >> 3) << 4 | data[4] & 0xF) & 0x1FF + r.frame = (data[4] >> 4) & 0xF + return r + +class Quaternion_11Bit(C_Packed_Value_Int): + struct_format: str = '<HHH' + bits: int = 11 + is_quaternion: bool = True + n_type: type = Quaternion + _pack_ = 1 + _fields_ = [ + ("x", c_uint64, 11), + ("y", c_uint64, 11), + ("z", c_uint64, 11), + ("w", c_uint64, 11), + ("frame", c_uint64, 4) + ] + + # yyyyyxxxxxxxxxxx|zzzzzzzzzzyyyyyy|ffffwwwwwwwwwwwz + def pack(self) -> None: + return struct.pack(self.struct_format, + ((self.y >> 6) << 11 | self.x) & 0xFFFF, + ((self.z >> 1) << 6 | self.y & 0x3f) & 0xFFFF, + (self.frame << 12 | self.w << 1 | self.z & 0x1) & 0xFFFF) + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data) + r.x = data[0] & 0x7FF + r.y = ((data[0] >> 11) << 6 | (data[1] & 0x3F)) & 0x7FF + r.z = ((data[1] >> 6) << 1 | (data[2] & 0x1)) & 0x7FF + r.w = (data[2] >> 1) & 0x7FF + r.frame = (data[2] >> 12) & 0xF + return r + +class Quaternion_14Bit(C_Packed_Value_Int): + struct_format: str = '<Q' + bits: int = 14 + is_quaternion: bool = True + raw_value: bool = True + n_type: type = Quaternion + _pack_ = 1 + _fields_ = [ + ("w", c_uint64, 14), + ("z", c_uint64, 14), + ("y", c_uint64, 14), + ("x", c_uint64, 14), + ("frame", c_uint64, 8) + ] + + @classmethod + def normalize_int_value(cls, v: int) -> float: + if v & (2**(cls.bits-1)): # Is the high bit set. + mask = (2**(cls.bits)-1) + absval = ((v & mask) ^ mask) + 1 + return (absval/(2**(cls.bits-1))) * -2.0 + else: + return (v / (2**(cls.bits-1) - 1)) * 2.0 + + @classmethod + def denormalize_int_value(cls, v: float) -> int: + if v < 0: + absval = math.floor((abs(v/2) * (2**(cls.bits-1)-1))) + absval ^= (2**(cls.bits)-1) + return absval + else: + return math.floor(((v/2) * (2**(cls.bits-1)-1))) + + # ffffffffxxxxxxxxxxxxxxyyyyyyyyyyyyyyzzzzzzzzzzzzzzwwwwwwwwwwwwww + def pack(self) -> None: + return struct.pack(self.struct_format, + (self.frame << 56 | self.x << 42 | self.y << 28 | self.z << 14 | self.w) & 0xFFFFFFFFFFFFFFFF) + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data)[0] + r.w = data & 0x3FFF + r.z = (data >> 14) & 0x3FFF + r.y = (data >> 28) & 0x3FFF + r.x = (data >> 42) & 0x3FFF + r.frame = (data >> 56) & 0xFF + return r + +class Tuple_14Bit(C_Packed_Value_Int): + struct_format: str = '<I' + bits: int = 14 + is_quaternion: bool = True + is_tuple: bool = True + n_type: type = Tuple + _pack_ = 1 + _fields_ = [ + ("v", c_uint32, 14), + ("w", c_uint32, 14), + ("frame", c_uint32, 4) + ] + + # ffffwwwwwwwwwwwwwwvvvvvvvvvvvvvv + def pack(self) -> None: + return struct.pack(self.struct_format, (self.frame << 28 | self.w << 14 | self.v) & 0xFFFFFFFF) + + @classmethod + def unpack(cls, data) -> Self: + r = cls() + data = struct.unpack(cls.struct_format, data)[0] + r.v = data & 0x3FFF + r.w = (data >> 14) & 0x3FFF + r.frame = (data >> 28) & 0xF + return r + +class Tuple_XW_14Bit(Tuple_14Bit): + v_is: V_Type = V_Type.X + +class Tuple_YW_14Bit(Tuple_14Bit): + v_is: V_Type = V_Type.Y + +class Tuple_ZW_14Bit(Tuple_14Bit): + v_is: V_Type = V_Type.Z + +class LMT_Header(C_Struct): + _pack_ = 1 + _fields_ = [ + ('signature', c_char * 4), + ('version', c_int16), + ('entry_count', c_int16), + ('unknown', c_char * 8) + ] + +class LMT_Basis(C_Struct): + _pack_ = 1 + _fields_ = [ + ('mul', c_float * 4), + ('add', c_float * 4) + ] + +class LMT_Bone_Data(C_Struct): + values: list[C_Packed_Value] + lerp: Optional[LMT_Basis] = None + _pack_ = 1 + _fields_ = [ + ('buffer_type', c_uint8), + ('raw_usage', c_uint8), + ('joint_type', c_uint8), + ('two_zero_five', c_uint8), + ('bone_id', c_int32), + ('weight', c_float), + ('buffer_size', c_int32), + ('buffer_offset', c_int64), + ('basis', c_float * 4), + ('lerp_offset', c_int64) + ] + + @property + def usage(self): + return self.raw_usage + +class LMT_Action(C_Struct): + action_id: int + offset: int + bone_headers: list[LMT_Bone_Data] + bones: dict[int, dict[int, list[C_Packed_Value]]] + free_lerp_offsets: list[int] + _pack_ = 1 + _fields_ = [ + ('fcurve_offset', c_uint64), + ('fcurve_count', c_uint32), + ('frame_count', c_uint32), + ('loop_count', c_int32), + ('null0', c_int32 * 3), + ('vec0', c_float * 4), + ('vec2', c_float * 4), + ('flags', c_uint8), + ('null2', c_char * 2), + ('flags2', c_uint8), + ('null3', c_int32 * 5), + ('timl_offset', c_uint64) + ] + + def get_bone_header_by_id_and_usage(self, bone_id: int, usage: int) -> Optional[LMT_Bone_Data]: + for data in self.bone_headers: + if data.bone_id == bone_id and data.usage == usage: + return data + return None + + def normalize_keyframes(self) -> None: + for data in self.bone_headers: + for value in data.values: + value.normalize() + + def denormalize_keyframes(self) -> None: + for data in self.bone_headers: + for value in data.values: + value.denormalize() + + def write(self) -> None: + seek(self.offset) + write(self.pack()) + seek(self.fcurve_offset) + for data in self.bone_headers: + write(data.pack()) + for data in self.bone_headers: + if data.buffer_offset != 0: + seek(data.buffer_offset) + for value in data.values: + write(value.pack()) + if data.lerp_offset != 0: + seek(data.lerp_offset) + write(data.lerp.pack()) + +if len(sys.argv) < 3: + print('Usage: ./parse_lmt.py <input.lmt> <output.lmt>') + sys.exit(1) + +LMT_File: io.BytesIO = io.BytesIO(open(sys.argv[1], 'rb').read()) +def read(n: int) -> bytes: + global LMT_File + return LMT_File.read(n) +def write(value: bytes): + global LMT_File + return LMT_File.write(value) +def seek(offset: int) -> None: + global LMT_File + LMT_File.seek(offset) + +header = LMT_Header.unpack(read(16)) +offsets = struct.unpack(f'<{header.entry_count}Q', read(8 * header.entry_count)) +padding = read(16) + +action_headers = [] +for i, offset in enumerate(offsets): + if offset != 0: + seek(offset) + action_header = LMT_Action.unpack(read(96)) + action_header.action_id = i + action_header.offset = offset + action_header.bones = {} + action_header.free_lerp_offsets = [] + action_headers.append(action_header) + +buffer_types = { + 1: Vector_Float_Base, + 2: Vector_Float_Base, + 3: Vector_Float, + 4: Vector_Short, + 5: Vector_Byte, + 6: Quaternion_14Bit, + 7: Quaternion_7Bit, + 11: Tuple_XW_14Bit, + 12: Tuple_YW_14Bit, + 13: Tuple_ZW_14Bit, + 14: Quaternion_11Bit, + 15: Quaternion_9Bit +} + +for action in action_headers: + seek(action.fcurve_offset) + action.bone_headers = [] + for _ in range(action.fcurve_count): + data = LMT_Bone_Data.unpack(read(48)) + data.values = [] + if data.bone_id not in action.bones: + action.bones[data.bone_id] = {} + if data.usage not in action.bones[data.bone_id]: + action.bones[data.bone_id][data.usage] = [] + action.bone_headers.append(data) + for data in action.bone_headers: + if data.buffer_offset != 0: + seek(data.buffer_offset) + packed_type = buffer_types[data.buffer_type] + for _ in range(data.buffer_size // packed_type.size()): + value = packed_type.unpack(read(packed_type.size())) + data.values.append(value) + action.bones[data.bone_id][data.usage].append(data.values[-1]) + if data.lerp_offset != 0: + seek(data.lerp_offset) + data.lerp = LMT_Basis.unpack(read(32)) + +def get_action_by_id(action_headers: list[LMT_Action], action_id: int) -> Optional[LMT_Action]: + for action in action_headers: + if action.action_id == action_id: + return action + return None + +def print_basis_report(action_headers: list[LMT_Action]) -> None: + bone_to_basis_values = {} + for action in action_headers: + for data in action.bone_headers: + if data.usage != 1: + continue + if data.bone_id not in bone_to_basis_values: + bone_to_basis_values[data.bone_id] = {} + if action.action_id not in bone_to_basis_values[data.bone_id]: + bone_to_basis_values[data.bone_id][action.action_id] = [] + bone_to_basis_values[data.bone_id][action.action_id].append( + [data.basis, data.lerp.mul if data.lerp else [0, 0, 0, 0], data.lerp.add if data.lerp else [0, 0, 0, 0]] + ) + for bone_id in bone_to_basis_values.keys(): + print(f'Bone#{bone_id}') + for action_id in sorted(bone_to_basis_values[bone_id].keys()): + for values in bone_to_basis_values[bone_id][action_id]: + basis = values[0] + mul = values[1] + add = values[2] + print(f' [{action_id}] ({basis[0]:6.3f}, {basis[1]:6.3f}, {basis[2]:6.3f}, {basis[3]:6.3f}) ({mul[0]:6.3f}, {mul[1]:6.3f}, {mul[2]:6.3f}, {mul[3]:6.3f}) ({add[0]:6.3f}, {add[1]:6.3f}, {add[2]:6.3f}, {add[3]:6.3f})') + +def print_keyframes(action: LMT_Action, bone_id: int) -> None: + for data in action.bone_headers: + if data.bone_id == -1: + print(*action.vec2, *action.vec0) + if data.bone_id != bone_id: + continue + print('Usage: ', data.usage) + print('Basis: ', *data.basis) + if data.lerp: + print('Add: ', *data.lerp.add) + print('Mul: ', *data.lerp.mul) + for value in data.values: + if not value.raw_value: + if value.is_tuple: + value.n.v = value.n.v * data.lerp.mul[0] + data.lerp.add[0] + else: + value.n.x = value.n.x * data.lerp.mul[0] + data.lerp.add[0] + value.n.y = value.n.y * data.lerp.mul[1] + data.lerp.add[1] + value.n.z = value.n.z * data.lerp.mul[2] + data.lerp.add[2] + if value.is_quaternion: + value.n.w = value.n.w * data.lerp.mul[3] + data.lerp.add[3] + else: + if value.is_tuple: + value.n.v = value.n.v + else: + value.n.x = value.n.x + value.n.y = value.n.y + value.n.z = value.n.z + if value.is_quaternion: + value.n.w = value.n.w + print(' ' + str(value.n) + ' ' + str(value) + f' [{value.__class__.__name__}]') + print('-----') + +def retarget_action(target, reference): + index = 0 + #while index < len(target.bone_headers): + # data = target.bone_headers[index] + # ref = reference.get_bone_header_by_id_and_usage(data.bone_id, data.usage) + # if not ref or (data.lerp and not ref.lerp) or (not data.lerp and ref.lerp): + # print(f'Bone#{data.bone_id} has no match {len(data.values)}') + # target.free_lerp_offsets.append(data.lerp_offset) + # del target.bone_headers[index] + # target.fcurve_count -= 1 + # continue + # index += 1 + for data in target.bone_headers: + ref = reference.get_bone_header_by_id_and_usage(data.bone_id, data.usage) + if len(data.values) == 0: + data.basis[0] = ref.basis[0] + data.basis[1] = ref.basis[1] + data.basis[2] = ref.basis[2] + data.basis[3] = ref.basis[3] + else: + if ref.lerp and data.lerp: + data.lerp.mul[0] = ref.lerp.mul[0] + data.lerp.mul[1] = ref.lerp.mul[1] + data.lerp.mul[2] = ref.lerp.mul[2] + data.lerp.mul[3] = ref.lerp.mul[3] + data.lerp.add[0] = ref.lerp.add[0] + data.lerp.add[1] = ref.lerp.add[1] + data.lerp.add[2] = ref.lerp.add[2] + data.lerp.add[3] = ref.lerp.add[3] + frame_one = data.values[0] + if not frame_one.raw_value: + mul = data.lerp.mul + add = data.lerp.add + if frame_one.is_tuple: + data.basis[0] = (frame_one.n.v * mul[0] + add[0]) + else: + data.basis[0] = (frame_one.n.x * mul[0] + add[0]) + data.basis[1] = (frame_one.n.y * mul[1] + add[1]) + data.basis[2] = (frame_one.n.z * mul[2] + add[2]) + if frame_one.is_quaternion: + data.basis[3] = (frame_one.n.w * mul[3] + add[3]) + else: + if frame_one.is_tuple: + data.basis[0] = frame_one.n.v + else: + data.basis[0] = frame_one.n.x + data.basis[1] = frame_one.n.y + data.basis[2] = frame_one.n.z + if frame_one.is_quaternion: + data.basis[3] = frame_one.n.w + target.write() + +#print_basis_report(action_headers) + +# Test reproducing original file. +#for action in action_headers: +# action.normalize_keyframes() +# action.denormalize_keyframes() +# action.write() + +#target1 = get_action_by_id(action_headers, 112) +#target1.normalize_keyframes() +# +#target1.denormalize_keyframes() +#target1.write() + +#print_keyframes(target1, 328) + +target1 = get_action_by_id(action_headers, 112) +#reference1 = get_action_by_id(action_headers, 199) +reference1 = get_action_by_id(action_headers, 108) + +target2 = get_action_by_id(action_headers, 104) +#reference2 = get_action_by_id(action_headers, 110) +reference2 = get_action_by_id(action_headers, 106) + +target3 = get_action_by_id(action_headers, 105) + +target1.normalize_keyframes() +target2.normalize_keyframes() +target3.normalize_keyframes() +reference1.normalize_keyframes() +reference2.normalize_keyframes() + +print('Rewriting Target #1') +retarget_action(target1, reference1) +print('Rewriting Target #2') +retarget_action(target2, reference2) +print('Rewriting Target #3') +retarget_action(target3, reference2) + +with open(sys.argv[2], 'wb+') as out: + seek(0) + out.write(LMT_File.getbuffer()) diff --git a/Research/pl_face.edit.xz b/Research/pl_face.edit.xz Binary files differnew file mode 100644 index 0000000..36f7b6a --- /dev/null +++ b/Research/pl_face.edit.xz diff --git a/Research/research.txt b/Research/research.txt new file mode 100644 index 0000000..dfb977e --- /dev/null +++ b/Research/research.txt @@ -0,0 +1,51 @@ +## Disable UI
+MonsterHunterWorld.exe+235307B - 0F84 81000000 - je MonsterHunterWorld.exe+2353102
+MonsterHunterWorld.exe+234DE5C - F6 C2 01 - test dl,01
+MonsterHunterWorld.exe+235318C - FF 90 30010000 - call qword ptr [rax+00000130]
+
+## Toggle Minimap Camera
+MonsterHunterWorld.exe+1E55DA0 - 0F84 6D020000 - je MonsterHunterWorld.exe+1E56013
+
+## Higher LODs (Credit: Otis_Inf)
+3F -> 42
+5CF8FC24 - 5CF425b0
+5E59FC24 = 0.0
+5E59FC28 = 0.0
+5E59FC34 = 64.0
+5E59FC4C = 64.0
+
+## Set Time (Credit: Otis_Inf)
+MonsterHunterWorld.exe+1B83487 - F3 0F11 41 38 - movss [rcx+38],xmm0
+
+## Game Speed (Credit: Otis_Inf)
+0AB6C094(speed) - 0AB6C084
+MonsterHunterWorld.exe+225FE48 - F3 0F11 8B 94000000 - movss [rbx+00000094],xmm1
+MonsterHunterWorld.exe+226000C - F3 0F11 B3 94000000 - movss [rbx+00000094],xmm6
+
+## Shadow Map
+LOW = 512x512, 1024x512
+MEDIUM = 640x640, 1280x640
+HIGH = 1024x1024, 2048x1024
+1408 -> 1792 -> 2816
+1024 -> 1536 -> 2048
+MonsterHunterWorld.exe+2317800 - 8B 81 38010000 - mov eax,[rcx+00000138]
+MonsterHunterWorld.exe+2287AD0 - 89 91 30550000 - mov [rcx+00005530],edx
+
+**
+MonsterHunterWorld.exe+2288A70 - F3 0F59 0D 1821D000 - mulss xmm1,[MonsterHunterWorld.exe+2F8AB90](8.0) -> 14028389C(16.0)
+ or
+MonsterHunterWorld.exe+43FF84 - F3 0F10 0D CCFEA002 - movss xmm1,[MonsterHunterWorld.exe+2E4FE58](1.0) -> 1400001B0(2.0)
+MonsterHunterWorld.exe+43F2D8 - F3 0F10 35 7800AD02 - movss xmm6,[MonsterHunterWorld.exe+2F0F358]
+
+
+MonsterHunterWorld.exe+2288B5E - 74 20 - je MonsterHunterWorld.exe+2288B80 -> nop
+ or
+## Check address of left side for shadow map values.
+## Doubling values here allows double res without nop patch.
+MonsterHunterWorld.exe+2288B00 - 89 91 00550000 - mov [rcx+00005500],edx
+## Needs replace or figure out something.
+MonsterHunterWorld.exe+2287B1A - C7 81 20550000 00100000 - mov [rcx+00005520],00000800
+**2
+
+## Disable Dithered Fade Close to Camera. (Credit: Otis_Inf)
+000000005D2ACD10+71
|