Changed player variable loading to require manual updating instead of relying on the annoying veh hooking, which was inconsistent and requires updating every game update anyway for backup player vars, which has been also deleted now as it is unnecessary

Added Tools directory for various tools that helps in updating the mod (such as the player variables, to use it you simply decompile the function that loads player variables in IDA, copy the decompiled function code, paste it into the tool and it will automatically extract the names and the type of variable like float or boolean, the functions that load float or bool should be named "LoadPlayerFloatVariable" or "LoadPlayerBoolVariable" respectively, otherwise the tool won't work)
This commit is contained in:
EricPlayZ
2024-01-06 04:59:09 +02:00
parent 0d0859395f
commit 3b146e57ac
17 changed files with 18105 additions and 3159 deletions

5
.gitignore vendored
View File

@ -6,3 +6,8 @@
/x64
/DL2GameOverhaulScript/x64
/DL2GameOverhaulScript/DL2GameOverhaulScript.vcxproj.user
/Tools/ExtractPlayerVarsFromDecompiledFunc/bin
/Tools/ExtractPlayerVarsFromDecompiledFunc/.vs
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/bin
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/obj
/Tools/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc/ExtractPlayerVarsFromDecompiledFunc.csproj.user

View File

@ -157,9 +157,8 @@
<AdditionalLibraryDirectories>source\MinHook\lib;</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)Extra Files\Default Config\EGameTools.ini" "$(SolutionDir)$(PlatformShortName)\$(Configuration)\EGameTools.ini"
copy /Y "$(SolutionDir)x64\Debug\EGameTools.asi" "D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64\EGameTools.asi"</Command>
<Message>Copying the default config file to the debug build directory and the compiled ASI into the game folder</Message>
<Command>copy /Y "$(SolutionDir)Extra Files\Default Config\EGameTools.ini" "$(SolutionDir)$(PlatformShortName)\$(Configuration)\EGameTools.ini"</Command>
<Message>Copying the default config file to the debug build directory</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

File diff suppressed because it is too large Load Diff

View File

@ -73,29 +73,6 @@ namespace Core {
}
}
static std::unique_ptr<Hook::BreakpointHook> ldrpRoutineBpHook = nullptr;
static void HookLdrpInitRoutine() {
const WindowsVersion winVer = Utils::GetWindowsVersion();
PDWORD64 pLdrpRoutineFunc = nullptr;
if (winVer != WindowsVersion::Windows7)
pLdrpRoutineFunc = Offsets::Get_LdrpCallInitRoutineOffset();
else
pLdrpRoutineFunc = Offsets::Get_LdrpRunInitializeRoutinesOffset();
if (!pLdrpRoutineFunc)
return;
ldrpRoutineBpHook = std::make_unique<Hook::BreakpointHook>(pLdrpRoutineFunc, [&](PEXCEPTION_POINTERS info) -> void {
DWORD64 entryPoint = info->ContextRecord->R12;
if (Memory::IsValidPtrMod(entryPoint, "gamedll_ph_x64_rwdi.dll", false)) {
ldrpRoutineBpHook->Disable();
GamePH::PlayerVariables::RunHooks();
}
});
}
#pragma region GetRendererAPI
static bool(*pReadVideoSettings)(LPVOID instance, LPVOID file, bool flag1) = nullptr;
static bool(*oReadVideoSettings)(LPVOID instance, LPVOID file, bool flag1) = nullptr;
@ -132,22 +109,11 @@ namespace Core {
createdConfigThread = true;
}
if (Menu::Player::useBACKUPPlayerVarsEnabled && GamePH::PlayerVariables::playerVars.empty())
GamePH::PlayerVariables::RunHooksBACKUP();
if (!GamePH::PlayerVariables::gotPlayerVars)
GamePH::PlayerVariables::GetPlayerVars();
Menu::Player::Update();
Menu::Camera::Update();
//if (GamePH::GameDI_PH::Get()) {
// INT64 gameVer = GamePH::GameDI_PH::Get()->GetCurrentGameVersion();
// std::cout << "Game Version:" << gameVer << std::endl;
//}
//if (GamePH::LevelDI::Get()) {
// float timePlayed = GamePH::LevelDI::Get()->GetTimePlayed();
// std::cout << "Time Played: " << timePlayed << std::endl;
//}
}
DWORD64 WINAPI MainThread(HMODULE hModule) {
@ -155,8 +121,8 @@ namespace Core {
Config::InitConfig();
if (!Menu::Player::useBACKUPPlayerVarsEnabled)
HookLdrpInitRoutine();
if (GamePH::PlayerVariables::playerVars.empty())
GamePH::PlayerVariables::SortPlayerVars();
MH_Initialize();
LoopHookReadVideoSettings();

View File

@ -10,10 +10,6 @@
#include "memory.h"
#include "print.h"
#include "utils.h"
//#define DEBUGPVARS
#ifdef DEBUGPVARS
#include <fstream>
#endif
namespace Core {
extern void OnPostUpdate();
@ -148,51 +144,14 @@ namespace GamePH {
#pragma endregion
#pragma region PlayerVariables
PDWORD64 PlayerVariables::FloatPlayerVariableVT;
PDWORD64 PlayerVariables::BoolPlayerVariableVT;
std::vector <std::pair<std::string, std::pair<LPVOID, std::string>>> PlayerVariables::playerVars;
std::vector <std::pair<std::string, std::pair<std::any, std::string>>> PlayerVariables::playerVarsDefault;
std::vector <std::pair<std::string, std::pair<std::any, std::string>>> PlayerVariables::playerCustomVarsDefault;
bool PlayerVariables::gotPlayerVars = false;
std::unique_ptr<Hook::BreakpointHook> PlayerVariables::loadPlayerFloatVarBpHook = nullptr;
std::unique_ptr<Hook::BreakpointHook> PlayerVariables::loadPlayerBoolVarBpHook = nullptr;
bool PlayerVariables::hooked = false;
bool PlayerVariables::hookedBACKUP = false;
void PlayerVariables::RunHooks() {
if (hooked)
return;
if (!Offsets::Get_LoadPlayerFloatVariableOffset())
return;
loadPlayerFloatVarBpHook = std::make_unique<Hook::BreakpointHook>(Offsets::Get_LoadPlayerFloatVariableOffset(), [&](PEXCEPTION_POINTERS info) -> void {
const char* tempName = reinterpret_cast<const char*>(info->ContextRecord->R8);
const std::string name = tempName;
PlayerVariables::playerVars.emplace_back(name, std::make_pair(nullptr, "float"));
PlayerVariables::playerVarsDefault.emplace_back(name, std::make_pair(0.0f, "float"));
PlayerVariables::playerCustomVarsDefault.emplace_back(name, std::make_pair(0.0f, "float"));
});
loadPlayerBoolVarBpHook = std::make_unique<Hook::BreakpointHook>(Offsets::Get_LoadPlayerFloatVariableOffset() - Offsets::Get_LoadPlayerVariableFuncSize(), [&](PEXCEPTION_POINTERS info) -> void {
const char* tempName = reinterpret_cast<const char*>(info->ContextRecord->R8);
const std::string name = tempName;
PlayerVariables::playerVars.emplace_back(name, std::make_pair(nullptr, "bool"));
PlayerVariables::playerVarsDefault.emplace_back(name, std::make_pair(false, "bool"));
PlayerVariables::playerCustomVarsDefault.emplace_back(name, std::make_pair(false, "bool"));
});
hooked = true;
}
void PlayerVariables::RunHooksBACKUP() {
if (hookedBACKUP)
return;
void PlayerVariables::SortPlayerVars() {
if (!playerVars.empty())
return;
if (!Offsets::Get_LoadPlayerFloatVariableOffset())
return;
std::stringstream ss(Config::playerVars);
@ -219,31 +178,25 @@ namespace GamePH {
PlayerVariables::playerVarsDefault.emplace_back(varName, std::make_pair(varType == "float" ? 0.0f : false, varType));
PlayerVariables::playerCustomVarsDefault.emplace_back(varName, std::make_pair(varType == "float" ? 0.0f : false, varType));
}
hookedBACKUP = true;
}
PDWORD64 PlayerVariables::GetFloatPlayerVariableVT() {
if (FloatPlayerVariableVT)
return FloatPlayerVariableVT;
if (!Offsets::Get_InitializePlayerVariablesOffset())
return nullptr;
const DWORD64 offsetToInstr = Offsets::Get_InitializePlayerVariablesOffset() + Offsets::Get_initPlayerFloatVarsInstrOffset() + 0x3; // 0x3 is instruction size
const DWORD floatPlayerVariableVTOffset = *reinterpret_cast<DWORD*>(offsetToInstr);
return FloatPlayerVariableVT = reinterpret_cast<PDWORD64>(offsetToInstr + sizeof(DWORD) + floatPlayerVariableVTOffset);
return reinterpret_cast<PDWORD64>(offsetToInstr + sizeof(DWORD) + floatPlayerVariableVTOffset);
}
PDWORD64 PlayerVariables::GetBoolPlayerVariableVT() {
if (BoolPlayerVariableVT)
return BoolPlayerVariableVT;
if (!Offsets::Get_InitializePlayerVariablesOffset())
return nullptr;
const DWORD64 offsetToInstr = Offsets::Get_InitializePlayerVariablesOffset() + Offsets::Get_initPlayerBoolVarsInstrOffset() + 0x3; // 0x3 is instruction size
const DWORD boolPlayerVariableVTOffset = *reinterpret_cast<DWORD*>(offsetToInstr);
return BoolPlayerVariableVT = reinterpret_cast<PDWORD64>(offsetToInstr + sizeof(DWORD) + boolPlayerVariableVTOffset);
return reinterpret_cast<PDWORD64>(offsetToInstr + sizeof(DWORD) + boolPlayerVariableVTOffset);
}
void PlayerVariables::GetPlayerVars() {
if (gotPlayerVars)
@ -257,21 +210,6 @@ namespace GamePH {
if (!GetBoolPlayerVariableVT())
return;
#ifdef DEBUGPVARS
std::string pVars{};
for (auto const& [key, val] : GamePH::PlayerVariables::playerVars) {
pVars.append(key);
pVars.append(":");
pVars.append(val.second);
pVars.append(",");
}
pVars.pop_back();
std::ofstream out("pvars.txt");
out << pVars;
out.close();
#endif
PDWORD64* playerVarsMem = reinterpret_cast<PDWORD64*>(Get());
bool isFloatPlayerVar = false;
bool isBoolPlayerVar = false;
@ -313,10 +251,6 @@ namespace GamePH {
}
gotPlayerVars = true;
if (!Menu::Player::useBACKUPPlayerVarsEnabled) {
loadPlayerFloatVarBpHook->Disable();
loadPlayerBoolVarBpHook->Disable();
}
}
PlayerVariables* PlayerVariables::Get() {

View File

@ -54,22 +54,13 @@ namespace GamePH {
extern void LoopHookTogglePhotoMode();
class PlayerVariables {
private:
static PDWORD64 FloatPlayerVariableVT;
static PDWORD64 BoolPlayerVariableVT;
public:
static std::vector<std::pair<std::string, std::pair<LPVOID, std::string>>> playerVars;
static std::vector<std::pair<std::string, std::pair<std::any, std::string>>> playerVarsDefault;
static std::vector<std::pair<std::string, std::pair<std::any, std::string>>> playerCustomVarsDefault;
static bool gotPlayerVars;
static std::unique_ptr<Hook::BreakpointHook> loadPlayerFloatVarBpHook;
static std::unique_ptr<Hook::BreakpointHook> loadPlayerBoolVarBpHook;
static bool hooked;
static bool hookedBACKUP;
static void RunHooks();
static void RunHooksBACKUP();
static void SortPlayerVars();
static PDWORD64 GetFloatPlayerVariableVT();
static PDWORD64 GetBoolPlayerVariableVT();

View File

@ -5690,8 +5690,6 @@ namespace Menu {
SMART_BOOL godModeEnabled{};
SMART_BOOL freezePlayerEnabled{};
bool useBACKUPPlayerVarsEnabled = false;
std::string saveSCRPath{};
std::string loadSCRFilePath{};
@ -5977,8 +5975,6 @@ namespace Menu {
ImGui::EndDisabled();
}
ImGui::Checkbox("Use BACKUP Player Vars (USE ONLY IF PLAYER VARIABLES IS DISABLED!)", &useBACKUPPlayerVarsEnabled);
ImGui::BeginDisabled(!GamePH::PlayerVariables::gotPlayerVars); {
if (ImGui::CollapsingHeader("Player Variables", ImGuiTreeNodeFlags_None)) {
ImGui::Indent();

View File

@ -7,8 +7,6 @@ namespace Menu {
extern SMART_BOOL godModeEnabled;
extern SMART_BOOL freezePlayerEnabled;
extern bool useBACKUPPlayerVarsEnabled;
extern std::string saveSCRPath;
extern std::string loadSCRFilePath;

View File

@ -15,7 +15,6 @@ static DWORD64 Get_## name () {\
return name=static_cast<DWORD64>(off);\
}
// NEEDS REPLACING SIGNATURES FOR MOST!!!
struct Offsets {
// ntdll.dll
AddOffset(LdrpCallInitRoutineOffset, "ntdll.dll", "[48 89 5C 24 08 44 89 44 24 18 48", PatternType::Address, PDWORD64)
@ -44,8 +43,6 @@ struct Offsets {
AddOffset(ReadVideoSettingsOffset, "engine_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8D 4C 24 ?? FF 15 ?? ?? ?? ?? 48 C7 C2 ?? ?? ?? ??", PatternType::RelativePointer, LPVOID)
AddOffset(CalculateFreeCamCollisionOffset, "gamedll_ph_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8B 06 4C 8D 4C 24 ??", PatternType::RelativePointer, LPVOID)
AddOffset(GetViewCameraOffset, "engine_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 85 C0 74 28 48 8B C8", PatternType::RelativePointer, LPVOID)
// NOT USING FOR NOW
//AddOffset(MoveCharacterOffset, "engine_x64_rwdi.dll", "E8 [?? ?? ?? ?? 80 BB ?? ?? ?? ?? ?? 74 4E", PatternType::RelativePointer, LPVOID)
AddOffset(CreatePlayerHealthModuleOffset, "gamedll_ph_x64_rwdi.dll", "[48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 4C 8B F1 E8 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??", PatternType::Address, LPVOID)
AddOffset(LifeSetHealth, "gamedll_ph_x64_rwdi.dll", "E8 [?? ?? ?? ?? 49 8D 4C 24 ?? 49 03 CE", PatternType::RelativePointer, LPVOID)
AddOffset(TogglePhotoMode, "gamedll_ph_x64_rwdi.dll", "48 83 EC 48 38 91 ?? ?? ?? ??", PatternType::Address, LPVOID)

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtractPlayerVarsFromDecompiledFunc", "ExtractPlayerVarsFromDecompiledFunc\ExtractPlayerVarsFromDecompiledFunc.csproj", "{0456BD1A-2E86-4940-8973-C46211117749}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0456BD1A-2E86-4940-8973-C46211117749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0456BD1A-2E86-4940-8973-C46211117749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0456BD1A-2E86-4940-8973-C46211117749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0456BD1A-2E86-4940-8973-C46211117749}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F7BAB3D3-BFA0-4C7F-A6D5-9E7FFCE4D00B}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,105 @@
namespace ExtractPlayerVarsFromDecompiledFunc
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
inputText = new RichTextBox();
inputTextLabel = new Label();
outputText = new RichTextBox();
extractBtn = new Button();
SuspendLayout();
//
// inputText
//
inputText.AcceptsTab = true;
inputText.DetectUrls = false;
inputText.EnableAutoDragDrop = true;
inputText.Location = new Point(12, 35);
inputText.Name = "inputText";
inputText.ScrollBars = RichTextBoxScrollBars.ForcedVertical;
inputText.Size = new Size(716, 226);
inputText.TabIndex = 0;
inputText.Text = "";
//
// inputTextLabel
//
inputTextLabel.AutoSize = true;
inputTextLabel.Location = new Point(12, 9);
inputTextLabel.Name = "inputTextLabel";
inputTextLabel.Size = new Size(121, 15);
inputTextLabel.TabIndex = 1;
inputTextLabel.Text = "Decompiled Function";
//
// outputText
//
outputText.AcceptsTab = true;
outputText.DetectUrls = false;
outputText.EnableAutoDragDrop = true;
outputText.Location = new Point(12, 301);
outputText.Name = "outputText";
outputText.ReadOnly = true;
outputText.ScrollBars = RichTextBoxScrollBars.ForcedVertical;
outputText.Size = new Size(716, 226);
outputText.TabIndex = 1;
outputText.Text = "";
//
// extractBtn
//
extractBtn.Location = new Point(299, 267);
extractBtn.Name = "extractBtn";
extractBtn.Size = new Size(125, 28);
extractBtn.TabIndex = 2;
extractBtn.Text = "Extract Player Vars";
extractBtn.UseVisualStyleBackColor = true;
extractBtn.Click += extractBtn_Click;
//
// Form1
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(740, 539);
Controls.Add(extractBtn);
Controls.Add(outputText);
Controls.Add(inputTextLabel);
Controls.Add(inputText);
MaximizeBox = false;
Name = "Form1";
ShowIcon = false;
Text = "Extract player variables from decompiled function";
ResumeLayout(false);
PerformLayout();
}
#endregion
private RichTextBox inputText;
private Label inputTextLabel;
private RichTextBox outputText;
private Button extractBtn;
}
}

View File

@ -0,0 +1,51 @@
using System.Text.RegularExpressions;
namespace ExtractPlayerVarsFromDecompiledFunc
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void extractBtn_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(inputText.Text))
return;
string decompiledFunc = inputText.Text;
StringReader reader = new(decompiledFunc);
if (reader == null)
return;
string? line;
string finalOutput = "";
while (true)
{
line = reader.ReadLine();
if (line == null)
{
finalOutput = finalOutput.Remove(finalOutput.Length - 1, 1);
break;
}
if (line.Contains("PlayerFloat", StringComparison.CurrentCultureIgnoreCase))
{
foreach (Match match in Regex.Matches(line, "\"([^\"]*)\""))
finalOutput += match.ToString().Trim('"') + ":float,";
}
else if (line.Contains("PlayerBool", StringComparison.CurrentCultureIgnoreCase))
{
foreach (Match match in Regex.Matches(line, "\"([^\"]*)\""))
finalOutput += match.ToString().Trim('"') + ":bool,";
}
}
outputText.Text = finalOutput;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,17 @@
namespace ExtractPlayerVarsFromDecompiledFunc
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
}