code:
#pragma once
#include "EmulatorComponent.h"
#include "../Utilities/Stopwatch.h"
#include <vector>
#include <string>

namespace TLAC::Components
{
	const std::string COMPONENTS_CONFIG_FILE_NAME = "components.ini";

	// Incomplete type
	struct DwGuiDisplay
	{
		void* vftable;
		void* active;
		void* cap;
		void* on;
		void* move;
		void* widget;
	};

	class ComponentsManager
	{
	public:
		ComponentsManager();
		~ComponentsManager();
		void Initialize();
		void Update();
		void UpdateInput();
		void OnFocusGain();
		void OnFocusLost();
		void Dispose();

		inline bool GetIsInputEmulatorUsed() { return isInputEmulatorUsed; };
		inline void SetIsInputEmulatorUsed(bool value) { isInputEmulatorUsed = value; };

		inline bool GetUpdateGameInput() { return updateGameInput; };
		inline void SetUpdateGameInput(bool value) { updateGameInput = value; }

		inline bool IsDwGuiActive() { return dwGuiDisplay->active != nullptr; };
		inline bool IsDwGuiHovered() { return dwGuiDisplay->on != nullptr; };

	private:
		DwGuiDisplay* dwGuiDisplay;

		bool isInputEmulatorUsed = false;
		bool updateGameInput = true;

		float elpasedTime;
		Utilities::Stopwatch updateStopwatch;
		std::vector<EmulatorComponent*> components;

		void ParseAddComponents();
	};
}


code:
#pragma once
#include <string>

namespace TLAC::Components
{
	struct CustomPlayerData
	{
		std::string *PlayerName;
		std::string* LevelName;
		int LevelPlateId;
		int LevelPlateEffect;
		int SkinEquip;
		int BtnSeEquip;
		int SlideSeEquip;
		int ChainslideSeEquip;
		int SlidertouchSeEquip;
		bool ShowGreatClearBorder;
		bool ShowExcellentClearBorder;
		bool ShowRivalClearBorder;
		bool UseCard;
		bool GameModifierOptions;
		bool ActionSE;
	};
}

repo_name: twistero/PD

license: mit


code:
#include "DebugComponent.h"
#include "../Constants.h"

namespace TLAC::Components
{
	const char* GameStateNames[] =
	{
		"STARTUP",
		"ADVERTISE",
		"GAME",
		"DATA_TEST",
		"TEST_MODE",
		"APP_ERROR",
		"MAX",
	};

	const char* SubGameStateNames[] =
	{
		"DATA_INITIALIZE",
		"SYSTEM_STARTUP",
		"SYSTEM_STARTUP_ERROR",
		"WARNING",
		"LOGO",
		"RATING",
		"DEMO",
		"TITLE",
		"RANKING",
		"SCORE_RANKING",
		"CM",
		"PHOTO_MODE_DEMO",
		"SELECTOR",
		"GAME_MAIN",
		"GAME_SEL",
		"STAGE_RESULT",
		"SCREEN_SHOT_SEL",
		"SCREEN_SHOT_RESULT",
		"GAME_OVER",
		"DATA_TEST_MAIN",
		"DATA_TEST_MISC",
		"DATA_TEST_OBJ",
		"DATA_TEST_STG",
		"DATA_TEST_MOT",
		"DATA_TEST_COLLISION",
		"DATA_TEST_SPR",
		"DATA_TEST_AET",
		"DATA_TEST_AUTH_3D",
		"DATA_TEST_CHR",
		"DATA_TEST_ITEM",
		"DATA_TEST_PERF",
		"DATA_TEST_PVSCRIPT",
		"DATA_TEST_PRINT",
		"DATA_TEST_CARD",
		"DATA_TEST_OPD",
		"DATA_TEST_SLIDER",
		"DATA_TEST_GLITTER",
		"DATA_TEST_GRAPHICS",
		"DATA_TEST_COLLECTION_CARD",
		"TEST_MODE_MAIN",
		"APP_ERROR",
		"MAX",
	};

	const char* DataTestNames[] =
	{
		"MAIN TEST",
		"MISC TEST",
		"OBJECT TEST",
		"STAGE TEST",
		"MOTION TEST",
		"COLLISION TEST",
		"SPRITE TEST",
		"2DAUTH TEST",
		"3DAUTH TEST",
		"CHARA TEST",
		"ITEM TEST",
		"PERFORMANCE TEST",
		"PVSCRIPT TEST",
		"PRINT TEST",
		"CARD TEST",
		"OPD TEST",
		"SLIDER TEST",
		"GLITTER TEST",
		"GRAPHICS TEST",
		"COLLECTION CARD TEST",
	};

	typedef void ChangeGameState(GameState);
	ChangeGameState* changeGameState = (ChangeGameState*)CHANGE_MODE_ADDRESS;

	typedef void ChangeSubState(GameState, SubGameState);
	ChangeSubState* changeSubState = (ChangeSubState*)CHANGE_SUB_MODE_ADDRESS;

	DebugComponent::DebugComponent()
	{
	}

	DebugComponent::~DebugComponent()
	{
	}

	const char* DebugComponent::GetDisplayName()
	{
		return "debug_component";
	}

	void DebugComponent::Initialize(ComponentsManager*)
	{
		printf("[TLAC] DebugComponent::Initialize(): Initialized\n");

		InjectPatches();

		HWND consoleHandle = GetConsoleWindow();
		ShowWindow(consoleHandle, SW_SHOW);

		// In case the FrameRateManager isn't enabled
		DWORD oldProtect;
		VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect);
	}

	void DebugComponent::Update()
	{
		if (dataTestMain)
		{
			Input::Keyboard::GetInstance()->PollInput();
			UpdateDataTestMain();
		}
	}

	void DebugComponent::UpdateInput()
	{
		auto keyboard = Input::Keyboard::GetInstance();

		// fast forward menus
		if (keyboard->IsDown(VK_SHIFT))
		{
			float* frameDuration = (float*)AET_FRAME_DURATION_ADDRESS;

			if (keyboard->IsDown(VK_TAB))
				*frameDuration = 1.0f / (GetGameFrameRate() / aetSpeedUpFactor);
			else if (keyboard->IsReleased(VK_TAB))
				*frameDuration = 1.0f / 60.0f;
		}

		for (size_t i = 0; i < _countof(gameStateKeyMappings); i++)
		{
			if (keyboard->IsTapped(gameStateKeyMappings[i].KeyCode))
				InternalChangeGameState(gameStateKeyMappings[i].State);
		}
	}

	void DebugComponent::InjectPatches()
	{
		const struct { void* Address; std::initializer_list<uint8_t> Data; } patches[] =
		{
			// Prevent the DATA_TEST game state from exiting on the first frame
			{ (void*)0x0000000140284B01, { 0x00 } },
			// Enable dw_gui sprite draw calls
			{ (void*)0x0000000140192601, { 0x00 } },
			// Update the dw_gui display
			{ (void*)0x0000000140302600, { 0xB0, 0x01 } },
			// Draw the dw_gui display
			{ (void*)0x0000000140302610, { 0xB0, 0x01 } },
			// Enable the dw_gui widgets
			{ (void*)0x0000000140192D00, { 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 } },
		};

		for (size_t i = 0; i < _countof(patches); i++)
			InjectCode(patches[i].Address, patches[i].Data);
	}

	void DebugComponent::SetConsoleForeground()
	{
		HWND consoleHandle = GetConsoleWindow();
		ShowWindow(consoleHandle, SW_SHOW);

		if (consoleHandle == NULL)
			return;

		WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
		GetWindowPlacement(consoleHandle, &place);

		switch (place.showCmd)
		{
		case SW_SHOWMAXIMIZED:
			ShowWindow(consoleHandle, SW_SHOWMAXIMIZED);
			break;
		case SW_SHOWMINIMIZED:
			ShowWindow(consoleHandle, SW_RESTORE);
			break;
		default:
			ShowWindow(consoleHandle, SW_NORMAL);
			break;
		}

		SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
		SetForegroundWindow(consoleHandle);
	}

	void DebugComponent::PrintDataTestMain()
	{
		system("cls");
		printf(" DATA TEST MAIN:\n\n");

		for (int i = SUB_DATA_TEST_MISC; i <= SUB_DATA_TEST_COLLECTION_CARD; i++)
			printf("%s  %s\n", i == selectionIndex ? "->" : "  ", DataTestNames[i - SUB_DATA_TEST_MAIN]);

		printf("\n");
		SetConsoleForeground();
	}

	void DebugComponent::InternalChangeGameState(GameState state)
	{
		changeGameState(state);
		printDataTestMain = dataTestMain = (state == GS_DATA_TEST);
	}

	void DebugComponent::UpdateDataTestMain()
	{
		auto keyboard = Input::Keyboard::GetInstance();

		if (keyboard->IsIntervalTapped(VK_UP))
		{
			selectionIndex--;
			printDataTestMain = true;
		}

		if (keyboard->IsIntervalTapped(VK_DOWN))
		{
			selectionIndex++;
			printDataTestMain = true;
		}

		if (selectionIndex > SUB_DATA_TEST_COLLECTION_CARD)
			selectionIndex = SUB_DATA_TEST_MISC;

		if (selectionIndex < SUB_DATA_TEST_MISC)
			selectionIndex = SUB_DATA_TEST_COLLECTION_CARD;

		if (keyboard->IsTapped(VK_RETURN))
		{
			dataTestMain = false;

			printf("[%s] -> [%s]\n", SubGameStateNames[SUB_DATA_TEST_MAIN], SubGameStateNames[selectionIndex]);
			changeSubState(GS_DATA_TEST, (SubGameState)selectionIndex);
		}

		if (printDataTestMain)
		{
			PrintDataTestMain();
			printDataTestMain = false;
		}
	}

	void DebugComponent::InjectCode(void* address, const std::initializer_list<uint8_t> &data)
	{
		const size_t byteCount = data.size() * sizeof(uint8_t);

		DWORD oldProtect;
		VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);

		memcpy(address, data.begin(), byteCount);
	}
}


code:
#pragma once
#include "EmulatorComponent.h"
#include "GameState.h"
#include "../Input/Keyboard/Keyboard.h"

namespace TLAC::Components
{
	class DebugComponent : public EmulatorComponent
	{
	public:
		DebugComponent();
		~DebugComponent();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

	private:
		const float aetSpeedUpFactor = 4.0f;

		bool dataTestMain = false;
		bool printDataTestMain = false;
		int selectionIndex = SUB_DATA_TEST_MISC;

		const struct { BYTE KeyCode; GameState State; } gameStateKeyMappings[5] =
		{
			{ VK_F4, GS_ADVERTISE },
			{ VK_F5, GS_GAME },
			{ VK_F6, GS_DATA_TEST },
			{ VK_F7, GS_TEST_MODE },
			{ VK_F8, GS_APP_ERROR },
		};

		void InjectPatches();
		void SetConsoleForeground();
		void PrintDataTestMain();
		void InternalChangeGameState(GameState state);
		void UpdateDataTestMain();
		void InjectCode(void* address, const std::initializer_list<uint8_t> &data);
	};
}


code:
#include "EmulatorComponent.h"
#include "../Constants.h"

namespace TLAC::Components
{
	EmulatorComponent::EmulatorComponent()
	{
	}

	EmulatorComponent::~EmulatorComponent()
	{
	}

	void EmulatorComponent::SetElapsedTime(float value)
	{
		elapsedTime = value;
	}

	float EmulatorComponent::GetElapsedTime()
	{
		return elapsedTime == 0.0f ? (1000.0f / 60.0f) : elapsedTime;
	}

	float EmulatorComponent::GetFrameRate()
	{
		return 1000.0f / GetElapsedTime();
	}

	float EmulatorComponent::GetGameFrameRate()
	{
		return *(float*)FRAME_RATE_ADDRESS;
	}
}


code:
#pragma once

namespace TLAC::Components
{
	class ComponentsManager;

	class EmulatorComponent
	{
	public:
		EmulatorComponent();
		~EmulatorComponent();

		virtual const char* GetDisplayName() = 0;

		virtual void Initialize(ComponentsManager*) = 0;
		virtual void Update() = 0;

		virtual void UpdateInput() {};
		virtual void OnFocusGain() {};
		virtual void OnFocusLost() {};

		void SetElapsedTime(float value);
		float GetElapsedTime();
		float GetFrameRate();
		float GetGameFrameRate();

	private:
		float elapsedTime;
	};
}


code:
#include "FastLoader.h"
#include "../Constants.h"
#include <stdio.h>

namespace TLAC::Components
{
	FastLoader::FastLoader()
	{
	}

	FastLoader::~FastLoader()
	{
	}

	const char* FastLoader::GetDisplayName()
	{
		return "fast_loader";
	}

	void FastLoader::Initialize(ComponentsManager*)
	{
	}

	void FastLoader::Update()
	{
		if (dataInitialized)
			return;

		previousGameState = currentGameState;
		currentGameState = *(GameState*)CURRENT_GAME_STATE_ADDRESS;

		if (currentGameState == GS_STARTUP)
		{
			typedef void UpdateTask();
			UpdateTask* updateTask = (UpdateTask*)UPDATE_TASKS_ADDRESS;

			// speed up TaskSystemStartup
			for (int i = 0; i < updatesPerFrame; i++)
				updateTask();

			constexpr int DATA_INITIALIZED = 3;

			// skip TaskDataInit
			*(int*)(DATA_INIT_STATE_ADDRESS) = DATA_INITIALIZED;

			// skip TaskWarning
			*(int*)(SYSTEM_WARNING_ELAPSED_ADDRESS) = 3939;
		}
		else if (previousGameState == GS_STARTUP)
		{
			dataInitialized = true;
			printf("[TLAC] FastLoader::Update(): Data Initialized\n");
		}
	}

	void FastLoader::UpdateInput()
	{
		return;
	}
}


code:
#pragma once
#include "EmulatorComponent.h"
#include "GameState.h"

namespace TLAC::Components
{
	class FastLoader : public EmulatorComponent
	{
	public:
		FastLoader();
		~FastLoader();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

	private:
		const int updatesPerFrame = 39;

		GameState currentGameState;
		GameState previousGameState;
		bool dataInitialized = false;
	};
}


code:
#include "FrameRateManager.h"
#include "../Constants.h"
#include "GameState.h"
#include <stdio.h>
#include <windows.h>
#include "../framework.h"
#include <vector>

namespace TLAC::Components
{
	FrameRateManager::FrameRateManager()
	{
		std::string utf8path = TLAC::framework::GetModuleDirectory() + "/config.ini";
		WCHAR utf16buf[256];
		MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, utf16buf, 256);

		float nMotionRate = GetPrivateProfileIntW(L"graphics", L"frm.motion.rate", 300, utf16buf);
		motionSpeedMultiplier = nMotionRate / 60.0f;
	}

	FrameRateManager::~FrameRateManager()
	{
	}

	const char* FrameRateManager::GetDisplayName()
	{
		return "frame_rate_manager";
	}

	void FrameRateManager::Initialize(ComponentsManager*)
	{
		pvFrameRate = (float*)PV_FRAME_RATE_ADDRESS;
		frameSpeed = (float*)FRAME_SPEED_ADDRESS;
		aetFrameDuration = (float*)AET_FRAME_DURATION_ADDRESS;

		// The default is expected to be 1.0 / 60.0
		defaultAetFrameDuration = *aetFrameDuration;

		// This const variable is stored inside a data segment so we don't want to throw any access violations
		DWORD oldProtect;
		VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect);

		// fix auto speed for high fps
		InjectCode((void*)0x140192d7b, { 0x90, 0x90, 0x90 });

		// fix frame speed slider initial value (should ignore effect of auto speed)
		InjectCode((void*)0x140338f2f, { 0xf3, 0x0f, 0x10, 0x0d, 0x61, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
		InjectCode((void*)0x140338f37, { 0x48, 0x8b, 0x8f, 0x80, 0x01, 0x00, 0x00 });       // MOV RCX, qword ptr [0x180 + RDI]

		InjectCode((void*)0x140338ebe, { 0xf3, 0x0f, 0x10, 0x0d, 0xd2, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
		InjectCode((void*)0x140338ec6, { 0x48, 0x8b, 0x05, 0xfb, 0xb1, 0xe5, 0x00 });       // MOV RAX, qword ptr [0x1411940c8]
		InjectCode((void*)0x140338ecd, { 0x48, 0x8b, 0x88, 0x80, 0x01, 0x00, 0x00 });       // MOV RCX, qword ptr [0x180 + RAX]

		// fix AETs
		InjectCode((void*)0x140170394, { 0xF3, 0x0F, 0x5E, 0x05, 0x34, 0xA3, 0xD6, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)

		// fix edit PV AETs (thanks lyb)
		InjectCode((void*)0x140192d30, { 0xF3, 0x0F, 0x10, 0x05, 0x5C, 0x02, 0x00, 0x00 }); // MOVSS XMM0, dword ptr [0x140192f94]

		// fix ageage hair effect
		InjectCode((void*)0x14054352f, { 0xE8, 0x1C, 0xF8, 0xC4, 0xFF }); // CALL 0x140192d50 (getFrameSpeed)
		InjectCode((void*)0x140543534, { 0xF3, 0x0F, 0x59, 0xD8 });       // MULSS XMM3, XMM0
		InjectCode((void*)0x140543538, { 0xEB, 0xB6 });                   // JMP 0x1405434f0

		// fix wind effect
		InjectCode((void*)0x14053ca71, { 0xEB, 0x3F });                                     // JMP 0x14053cab2
		InjectCode((void*)0x14053cab2, { 0xF3, 0x0F, 0x10, 0x05, 0xFA, 0x53, 0x46, 0x00 }); // MOVSS XMM0, dword ptr [0x1409a1eb4] (60.0f)
		InjectCode((void*)0x14053caba, { 0xE9, 0x42, 0xFE, 0xFF, 0xFF });                   // JMP 0x14053c901
		InjectCode((void*)0x14053c901, { 0xF3, 0x0F, 0x5E, 0x05, 0xC7, 0xDD, 0x99, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)
		InjectCode((void*)0x14053c909, { 0xE9, 0x68, 0x01, 0x00, 0x00 });                   // JMP 0x14053ca76

		// below are patches for the alternate method (breaks physics)

		//// run motions at motionSpeedMultiplier x rate
		//	// skip unncessary code to free code space
		//  InjectCode((void*)0x140194b2b, { 0xEB, 0x24 }); // JMP 0x140194b51

		//  InjectCode((void*)0x140194b2d, { 0xE8, 0x1E, 0xE2, 0xFF, 0xFF });  // CALL 0x140192d50
		//  InjectCode((void*)0x140194b32, {                                   // MOV ECX, &motionSpeedMultiplier
		//  	0x48,
		//  	0xB9,
		//  	(uint8_t)((uint64_t)&motionSpeedMultiplier & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 8) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 16) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 24) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 32) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 40) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 48) & 0xFF),
		//  	(uint8_t)(((uint64_t)&motionSpeedMultiplier >> 56) & 0xFF),
		//  });
		//  InjectCode((void*)0x140194b3c, { 0x66, 0x67, 0x0F, 0x6E, 0x09 }); // MOVD XMM1, dword ptr [ECX]
		//  InjectCode((void*)0x140194b41, { 0xF3, 0x0F, 0x59, 0xC1 });       // MULSS XMM0, XMM1
		//  InjectCode((void*)0x140194b45, { 0xEB, 0xA1 });                   // JMP 0x140194ae8

		//	// jump to new code
		//  InjectCode((void*)0x140194ae3, { 0xEB, 0x48 }); // JMP 0x140194b2d

		////	// trying to fix physics (fail)
		////    InjectCode((void*)0x14011d537, { 0xF3, 0x0F, 0x10, 0x05, 0xB1, 0xFF, 0x9E, 0x00, 0x90 }); // change source to dword ptr [0x140b0d4f0]
	}

	void FrameRateManager::Update()
	{
		// this alterante way makes physics slowmo, but overall may be better if that's fixed

		//// target framerate
		//*(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = 60.0f;

		//// enable dynamic framerate
		//*(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;

		//*pvFrameRate = 60.0f;

		//*aetFrameDuration = 1.0f / GetGameFrameRate();

		//bool inGame = *(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME;
		//if (inGame)
		//{
		//	// This PV struct creates a copy of the PvFrameRate & PvFrameSpeed during the loading screen
		//	// so we'll make sure to keep updating it as well.
		//	// Each new motion also creates its own copy of these values but keeping track of the active motions is annoying
		//	// and they usually change multiple times per PV anyway so this should suffice for now
		//	float* pvStructPvFrameRate = (float*)(0x0000000140CDD978 + 0x2BF98);
		//	float* pvStructPvFrameSpeed = (float*)(0x0000000140CDD978 + 0x2BF9C);

		//	*pvStructPvFrameRate = 60.0; // ?
		//	*pvStructPvFrameSpeed = 1.0 / motionSpeedMultiplier;
		//}

		// this way breaks some anim speeds, but they're fixable
		// *aetFrameDuration = 1.0f / GetGameFrameRate();

		if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME)
		{
			*pvFrameRate = 60.0f * motionSpeedMultiplier;
		}
		else
		{
			*pvFrameRate = 60.0f;
		}

		if (*(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_MAIN || *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_DEMO)
		{
			// enable dynamic framerate
			*(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;

			// target framerate
			*(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = *pvFrameRate;

			// trying to fix meltdown's water
			//if ((uint64_t*)0x1411943f8 != nullptr)
				//*(float*)(*(uint64_t*)0x1411943f8 + 0x1c) = (60.0f / GetGameFrameRate()) / 15.0f;
				//*(float*)(*(uint64_t*)0x1411943f8 + 0x10) = *(float*)(*(uint64_t*)0x1411943f8);
		}
		else
		{
			// enable dynamic framerate
			*(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;

			// target framerate
			*(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = 60.0f;
		}
	}

	void FrameRateManager::InjectCode(void* address, const std::vector<uint8_t> data)
	{
		const size_t byteCount = data.size() * sizeof(uint8_t);

		DWORD oldProtect;
		VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
		memcpy(address, data.data(), byteCount);
		VirtualProtect(address, byteCount, oldProtect, nullptr);
	}
}


code:
#pragma once
#include "EmulatorComponent.h"
#include <vector>

namespace TLAC::Components
{
	class FrameRateManager : public EmulatorComponent
	{
	public:
		FrameRateManager();
		~FrameRateManager();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;

	private:
		float *pvFrameRate;
		float *frameSpeed;
		float *aetFrameDuration;
		float defaultAetFrameDuration;
		float motionSpeedMultiplier = 5.0;
		void InjectCode(void* address, const std::vector<uint8_t> data);
	};
}


code:
#pragma once
#include <stdint.h>

namespace TLAC::Components
{
	enum GameState : uint32_t
	{
		GS_STARTUP,
		GS_ADVERTISE,
		GS_GAME,
		GS_DATA_TEST,
		GS_TEST_MODE,
		GS_APP_ERROR,
		GS_MAX,
	};

	enum SubGameState : uint32_t
	{
		SUB_DATA_INITIALIZE,
		SUB_SYSTEM_STARTUP,
		SUB_SYSTEM_STARTUP_ERROR,
		SUB_WARNING,
		SUB_LOGO,
		SUB_RATING,
		SUB_DEMO,
		SUB_TITLE,
		SUB_RANKING,
		SUB_SCORE_RANKING,
		SUB_CM,
		SUB_PHOTO_MODE_DEMO,
		SUB_SELECTOR,
		SUB_GAME_MAIN,
		SUB_GAME_SEL,
		SUB_STAGE_RESULT,
		SUB_SCREEN_SHOT_SEL,
		SUB_SCREEN_SHOT_RESULT,
		SUB_GAME_OVER,
		SUB_DATA_TEST_MAIN,
		SUB_DATA_TEST_MISC,
		SUB_DATA_TEST_OBJ,
		SUB_DATA_TEST_STG,
		SUB_DATA_TEST_MOT,
		SUB_DATA_TEST_COLLISION,
		SUB_DATA_TEST_SPR,
		SUB_DATA_TEST_AET,
		SUB_DATA_TEST_AUTH_3D,
		SUB_DATA_TEST_CHR,
		SUB_DATA_TEST_ITEM,
		SUB_DATA_TEST_PERF,
		SUB_DATA_TEST_PVSCRIPT,
		SUB_DATA_TEST_PRINT,
		SUB_DATA_TEST_CARD,
		SUB_DATA_TEST_OPD,
		SUB_DATA_TEST_SLIDER,
		SUB_DATA_TEST_GLITTER,
		SUB_DATA_TEST_GRAPHICS,
		SUB_DATA_TEST_COLLECTION_CARD,
		SUB_TEST_MODE_MAIN,
		SUB_APP_ERROR,
		SUB_MAX,
	};
}


code:
#pragma once

namespace TLAC::Components
{
	enum HoldState
	{
		HOLD_NONE,
		HOLD_SANKAKU = 64,
		HOLD_MARU = 128,
		HOLD_BATSU = 256,
		HOLD_SHIKAKU = 512,
	};
}


code:
#pragma once

namespace TLAC::Components
{
	enum TargetHitStates : int32_t
	{
		COOL,
		FINE,
		SAFE,
		SAD,
		COOL_WRONG, // unsure
		FINE_WRONG, // unsure
		SAFE_WRONG, // unsure
		SAD_WRONG, // unsure
		WORST,
		NONE = 21,
	};
}


code:
#include "TargetInspector.h"

namespace TLAC::Components
{
	bool TargetInspector::repressTbl[maxTargetSlots];
	bool TargetInspector::ShouldVibrate;

	TargetInspector::TargetInspector()
	{
	}

	TargetInspector::~TargetInspector()
	{
	}

	void TargetInspector::Initialize(ComponentsManager*)
	{
		tgtStates = (TargetState*)TGT_STATES_BASE_ADDRESS;
	}

	void TargetInspector::Update()
	{
		UpdateRepressTbl();
		UpdateShouldVibrate();
	}

	const char* TargetInspector::GetDisplayName()
	{
		return "target_inspector";
	}

	void TargetInspector::UpdateRepressTbl()
	{
		for (int i = 0; i < maxTargetSlots; ++i)
		{
			repressTbl[i] = IsWithinRange(tgtStates[i].tgtRemainingTime)
				&& HasNotBeenHit(tgtStates[i].tgtHitState)
				&& !IsSlide(tgtStates[i].tgtType);
		}
	}

	void TargetInspector::UpdateShouldVibrate()
	{
		for (int i = 0; i < maxTargetSlots; ++i)
		{
			if (IsWithinRange(tgtStates[i].tgtRemainingTime)
				&& (tgtStates[i].tgtType == SLIDE_LONG_L || tgtStates[i].tgtType == SLIDE_LONG_R)
				&& tgtStates[i].tgtHitState == NONE)
				{
					//printf("%d\n", tgtStates[i].tgtHitState);
					ShouldVibrate = true;
					return;
				}
		}
		ShouldVibrate = false;
	}

	bool TargetInspector::IsWithinRange(float time)
	{
		return time < timingThreshold && time > -timingThreshold && time != 0;
	}

	bool TargetInspector::HasNotBeenHit(int hitState)
	{
		return hitState == NONE;
	}

	bool TargetInspector::IsSlide(int type)
	{
		return (type >= SLIDE_L && type <= SLIDE_LONG_R) || type >= SLIDE_L_CH;
	}

	bool TargetInspector::IsAnyRepress()
	{
		for (int i = 0; i < maxTargetSlots; ++i)
		{
			if (repressTbl[i])
				return true;
		}
		return false;
	}
}


code:
#pragma once
#include "../EmulatorComponent.h"
#include "../Input/InputEmulator.h"
#include "../../Constants.h"
#include "TargetHitStates.h"
#include "TargetState.h"

namespace TLAC::Components
{
	const int maxTargetSlots = 64;

	class TargetInspector : public EmulatorComponent
	{
	public:
		TargetInspector();
		~TargetInspector();

		static bool repressTbl[maxTargetSlots];

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual const char* GetDisplayName() override;

		static bool IsAnyRepress();

		static bool ShouldVibrate;

	private:
		//const uint64_t offset = 0x4A8;
		const float timingThreshold = 0.13f; // PS4 estimate

		TargetState* tgtStates;

		bool IsSlide(int);
		bool IsWithinRange(float);
		bool HasNotBeenHit(int);
		void UpdateRepressTbl();
		void UpdateShouldVibrate();
	};
}


code:
#pragma once

namespace TLAC::Components
{
	struct TargetState
	{
		// seems to be some kind of linked list..
		// not sure if these are for all active targets or just used in some functions
		TargetState* prev;
		TargetState* next;
		byte padding10[0x4];
		TargetTypes tgtType;
		float tgtRemainingTime;
		byte padding1C[0x434];
		byte ToBeRemoved; // FromList
		byte padding451[0xB];
		TargetHitStates tgtHitState;
		byte padding460[0x48];
	};
}


code:
#pragma once

namespace TLAC::Components
{
	enum TargetTypes : int32_t
	{
		SANKAKU,
		MARU,
		BATSU,
		SHIKAKU,
		SANKAKU_H,
		MARU_H,
		BATSU_H,
		SHIKAKU_H,
		SLIDE_L = 12,
		SLIDE_R,
		SLIDE_LONG_L = 15,
		SLIDE_LONG_R,
		SANKAKU_CH = 18,
		MARU_CH,
		BATSU_CH,
		SHIKAKU_CH,
		SLIDE_L_CH = 23,
		SLIDE_R_CH,
	};
}


code:
#pragma once

namespace TLAC::Components
{
	enum InputBufferType
	{
		InputBufferType_Tapped,
		InputBufferType_Released,
		InputBufferType_Down,
		InputBufferType_DoubleTapped,
		InputBufferType_IntervalTapped,
		InputBufferType_Max,
	};
}


code:
#include <iostream>
#include "windows.h"
#include "InputEmulator.h"
#include "../ComponentsManager.h"
#include "../../Constants.h"
#include "../../framework.h"
#include "../../Input/Bindings/KeyboardBinding.h"
#include "../../Input/Bindings/XinputBinding.h"
#include "../../Input/Bindings/MouseBinding.h"
#include "../../Input/Bindings/Ds4Binding.h"
#include "../../Input/KeyConfig/Config.h"
#include "../../Utilities/Operations.h"
#include "../../Utilities/EnumBitwiseOperations.h"
#include "../../FileSystem/ConfigFile.h"

const std::string KEY_CONFIG_FILE_NAME = "keyconfig.ini";

using namespace TLAC::Input;
using namespace TLAC::Input::KeyConfig;
using namespace TLAC::Utilities;

namespace TLAC::Components
{

	InputEmulator::InputEmulator()
	{

	}

	InputEmulator::~InputEmulator()
	{
		delete TestBinding;
		delete ServiceBinding;

		delete StartBinding;
		delete SankakuBinding;
		delete ShikakuBinding;
		delete BatsuBinding;
		delete MaruBinding;

		delete LeftBinding;
		delete RightBinding;
	}

	const char* InputEmulator::GetDisplayName()
	{
		return "input_emulator";
	}

	void InputEmulator::Initialize(ComponentsManager* manager)
	{
		componentsManager = manager;
		componentsManager->SetIsInputEmulatorUsed(true);

		inputState = GetInputStatePtr((void*)INPUT_STATE_PTR_ADDRESS);
		inputState->HideCursor();

		TestBinding = new Binding();
		ServiceBinding = new Binding();
		StartBinding = new Binding();

		SankakuBinding = new Binding();
		ShikakuBinding = new Binding();
		BatsuBinding = new Binding();
		MaruBinding = new Binding();

		LeftBinding = new Binding();
		RightBinding = new Binding();

		FileSystem::ConfigFile configFile(framework::GetModuleDirectory(), KEY_CONFIG_FILE_NAME);
		configFile.OpenRead();

		Config::BindConfigKeys(configFile.ConfigMap, "JVS_TEST", *TestBinding, { "F1" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_SERVICE", *ServiceBinding, { "F2" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_START", *StartBinding, { "Enter" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_TRIANGLE", *SankakuBinding, { "W", "I" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_SQUARE", *ShikakuBinding, { "A", "J" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_CROSS", *BatsuBinding, { "S", "K" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_CIRCLE", *MaruBinding, { "D", "L" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_LEFT", *LeftBinding, { "Q", "U" });
		Config::BindConfigKeys(configFile.ConfigMap, "JVS_RIGHT", *RightBinding, { "E", "O" });

		mouseScrollPvSelection = configFile.GetBooleanValue("mouse_scroll_pv_selection");
	}

	void InputEmulator::Update()
	{
		return;
	}

	void InputEmulator::OnFocusLost()
	{
		// to prevent buttons from being "stuck"
		inputState->ClearState();
		inputState->HideCursor();
	}

	void InputEmulator::UpdateInput()
	{
		if (!componentsManager->GetUpdateGameInput())
			return;

		if (!componentsManager->IsDwGuiActive())
		{
			UpdateJvsInput();

			if (mouseScrollPvSelection && !componentsManager->IsDwGuiHovered())
				UpdateMousePvScroll();
		}

		UpdateDwGuiInput();
	}

	void InputEmulator::UpdateJvsInput()
	{
		auto tappedFunc = [](void* binding) { return ((Binding*)binding)->AnyTapped(); };
		auto releasedFunc = [](void* binding) { return ((Binding*)binding)->AnyReleased(); };
		auto downFunc = [](void* binding) { return ((Binding*)binding)->AnyDown(); };

		lastDownState = inputState->Down.Buttons;

		inputState->Tapped.Buttons = GetJvsButtonsState(tappedFunc);
		inputState->Released.Buttons = GetJvsButtonsState(releasedFunc);
		inputState->Down.Buttons = GetJvsButtonsState(downFunc);
		inputState->DoubleTapped.Buttons = GetJvsButtonsState(tappedFunc);
		inputState->IntervalTapped.Buttons = GetJvsButtonsState(tappedFunc);

		// if not interacting with L and R, use values from the slider instead
		if ( ( (inputState->Tapped.Buttons | inputState->Released.Buttons | inputState->Down.Buttons)
			 & (JVS_L | JVS_R) ) == 0)
		{
			UpdateSliderLR();
		}

		UpdateHoldState();
		heldButtons = GetButtonFromHold();

		if ((lastDownState &= inputState->Tapped.Buttons) != 0)
		{
			inputState->Down.Buttons ^= inputState->Tapped.Buttons;
			if (IsHold() && !TargetInspector::IsAnyRepress())
				inputState->Down.Buttons |= heldButtons;
		}

		// repress held down buttons to not block input
		//inputState->Down.Buttons ^= inputState->Tapped.Buttons;
	}

	HoldState InputEmulator::GetHoldState()
	{
		return (HoldState) * ((int*)HOLD_STATE_ADDRESS);
	}

	int InputEmulator::GetMaxHoldState()
	{
		return *(int*)MAX_HOLD_STATE_ADDRESS;
	}

	bool InputEmulator::IsHold()
	{
		return ((holdState != HOLD_NONE) && (GetMaxHoldState() != 1));
	}

	void InputEmulator::UpdateHoldState()
	{
		holdState = GetHoldState();

		for (int i = 0; i < 4; ++i)
		{
			int holdId = 1 << (i + 6);

			if ((holdId & holdState) != 0)
				holdTbl[i] = 1;
			else
				holdTbl[i] = 0;
		}
	}

	void InputEmulator::UpdateDwGuiInput()
	{
		auto keyboard = Keyboard::GetInstance();
		auto mouse = Mouse::GetInstance();

		auto pos = mouse->GetRelativePosition();
		inputState->MouseX = (int)pos.x;
		inputState->MouseY = (int)pos.y;

		auto deltaPos = mouse->GetDeltaPosition();
		inputState->MouseDeltaX = (int)deltaPos.x;
		inputState->MouseDeltaY = (int)deltaPos.y;

		inputState->Key = GetKeyState();

		for (int i = 0; i < sizeof(keyBits) / sizeof(KeyBit); i++)
			UpdateInputBit(keyBits[i].Bit, keyBits[i].KeyCode);

		for (int i = InputBufferType_Tapped; i < InputBufferType_Max; i++)
		{
			inputState->SetBit(scrollUpBit, mouse->GetIsScrolledUp(), (InputBufferType)i);
			inputState->SetBit(scrollDownBit, mouse->GetIsScrolledDown(), (InputBufferType)i);
		}
	}

	void InputEmulator::UpdateMousePvScroll()
	{
		// I originally wanted to use a MouseBinding set to JVS_LEFT / JVS_RIGHT
		// but that ended up being too slow because a PV slot can only be scrolled to once the scroll animation has finished playing
		int* slotsToScroll = (int*)PV_SEL_SLOTS_TO_SCROLL;
		int* modulesToScroll = (int*)MODULE_SEL_SLOTS_TO_SCROLL;

		auto mouse = Mouse::GetInstance();
		if (mouse->GetIsScrolledUp())
		{
			if (*(int*)PV_SEL_SLOTS_CONST < 26) *slotsToScroll -= 1;
			if (*(int*)MODULE_IS_RECOMMENDED == 0) *modulesToScroll -= 1;
		}
		if (mouse->GetIsScrolledDown())
		{
			if (*(int*)PV_SEL_SLOTS_CONST < 26) *slotsToScroll += 1;
			if (*(int*)MODULE_IS_RECOMMENDED == 0) *modulesToScroll += 1;
		}
	}

	InputState* InputEmulator::GetInputStatePtr(void* address)
	{
		return (InputState*)(*(uint64_t*)address);
	}

	JvsButtons InputEmulator::GetJvsButtonsState(bool(*buttonTestFunc)(void*))
	{
		JvsButtons buttons = JVS_NONE;

		if (buttonTestFunc(TestBinding))
			buttons |= JVS_TEST;
		if (buttonTestFunc(ServiceBinding))
			buttons |= JVS_SERVICE;

		if (buttonTestFunc(StartBinding))
			buttons |= JVS_START;

		if (buttonTestFunc(SankakuBinding))
			buttons |= JVS_TRIANGLE;
		if (buttonTestFunc(ShikakuBinding))
			buttons |= JVS_SQUARE;
		if (buttonTestFunc(BatsuBinding))
			buttons |= JVS_CROSS;
		if (buttonTestFunc(MaruBinding))
			buttons |= JVS_CIRCLE;

		if (buttonTestFunc(LeftBinding))
			buttons |= JVS_L;
		if (buttonTestFunc(RightBinding))
			buttons |= JVS_R;

		return buttons;
	}

	JvsButtons InputEmulator::GetButtonFromHold()
	{
		JvsButtons buttons = JVS_NONE;

		if (holdTbl[0])
			buttons |= JVS_TRIANGLE;
		if (holdTbl[1])
			buttons |= JVS_CIRCLE;
		if (holdTbl[2])
			buttons |= JVS_CROSS;
		if (holdTbl[3])
			buttons |= JVS_SQUARE;

		return buttons;
	}

	char InputEmulator::GetKeyState()
	{
		auto keyboard = Keyboard::GetInstance();

		bool upper = keyboard->IsDown(VK_SHIFT);
		constexpr char caseDifference = 'A' - 'a';

		char inputKey = 0x00;

		for (char key = '0'; key < 'Z'; key++)
		{
			if (keyboard->IsIntervalTapped(key))
				inputKey = (upper || key < 'A') ? key : (key - caseDifference);
		}

		if (keyboard->IsIntervalTapped(VK_BACK))
			inputKey = 0x08;

		if (keyboard->IsIntervalTapped(VK_TAB))
			inputKey = 0x09;

		if (keyboard->IsIntervalTapped(VK_SPACE))
			inputKey = 0x20;

		if (keyboard->IsDoubleTapped(VK_ESCAPE))
			*(bool*)SHOULD_EXIT_BOOL_ADDRESS = true;

		return inputKey;
	}

	void InputEmulator::UpdateInputBit(uint32_t bit, uint8_t keycode)
	{
		auto keyboard = Keyboard::GetInstance();

		inputState->SetBit(bit, keyboard->IsTapped(keycode), InputBufferType_Tapped);
		inputState->SetBit(bit, keyboard->IsReleased(keycode), InputBufferType_Released);
		inputState->SetBit(bit, keyboard->IsDown(keycode), InputBufferType_Down);
		inputState->SetBit(bit, keyboard->IsDoubleTapped(keycode), InputBufferType_DoubleTapped);
		inputState->SetBit(bit, keyboard->IsIntervalTapped(keycode), InputBufferType_IntervalTapped);

	}

	typedef uint8_t getSliderSensorFunc(void*, int);
	void InputEmulator::UpdateSliderLR()
	{
		getSliderSensorFunc* getSliderSensorTapped = (getSliderSensorFunc*)GET_SLIDER_TAPPED_ADDRESS;
		getSliderSensorFunc* getSliderSensorReleased = (getSliderSensorFunc*)GET_SLIDER_RELEASED_ADDRESS;
		getSliderSensorFunc* getSliderSensorDown = (getSliderSensorFunc*)GET_SLIDER_DOWN_ADDRESS;

		if (getSliderSensorTapped((void*)SLIDER_CTRL_TASK_ADDRESS, 36))
			inputState->Tapped.Buttons |= JVS_L;
		else
			inputState->Tapped.Buttons &= ~JVS_L;

		if (getSliderSensorReleased((void*)SLIDER_CTRL_TASK_ADDRESS, 36))
			inputState->Released.Buttons |= JVS_L;
		else
			inputState->Released.Buttons &= ~JVS_L;

		if (getSliderSensorDown((void*)SLIDER_CTRL_TASK_ADDRESS, 36))
			inputState->Down.Buttons |= JVS_L;
		else
			inputState->Down.Buttons &= ~JVS_L;

		if (getSliderSensorTapped((void*)SLIDER_CTRL_TASK_ADDRESS, 37))
			inputState->Tapped.Buttons |= JVS_R;
		else
			inputState->Tapped.Buttons &= ~JVS_R;

		if (getSliderSensorReleased((void*)SLIDER_CTRL_TASK_ADDRESS, 37))
			inputState->Released.Buttons |= JVS_R;
		else
			inputState->Released.Buttons &= ~JVS_R;

		if (getSliderSensorDown((void*)SLIDER_CTRL_TASK_ADDRESS, 37))
			inputState->Down.Buttons |= JVS_R;
		else
			inputState->Down.Buttons &= ~JVS_R;
	}
}


code:
#pragma once
#include <functional>
#include <Windows.h>
#include "InputState.h"
#include "../EmulatorComponent.h"
#include "../../Input/Bindings/Binding.h"
#include "../GameTargets/TargetTypes.h"
#include "../GameTargets/TargetHitStates.h"
#include "../GameTargets/HoldState.h"
#include "../GameTargets/TargetInspector.h"

namespace TLAC::Components
{
	struct KeyBit
	{
		uint32_t Bit;
		uint8_t KeyCode;
	};

	class InputEmulator : public EmulatorComponent
	{
	public:
		Input::Binding* TestBinding;
		Input::Binding* ServiceBinding;

		Input::Binding* StartBinding;
		Input::Binding* SankakuBinding;
		Input::Binding* ShikakuBinding;
		Input::Binding* BatsuBinding;
		Input::Binding* MaruBinding;

		Input::Binding* LeftBinding;
		Input::Binding* RightBinding;

		InputEmulator();
		~InputEmulator();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

		virtual void OnFocusLost() override;

	private:
		ComponentsManager* componentsManager;

		bool mouseScrollPvSelection = false;
		const uint32_t scrollUpBit = 99;
		const uint32_t scrollDownBit = 100;

		KeyBit keyBits[20] =
		{
			{ 5, VK_LEFT },
			{ 6, VK_RIGHT },

			{ 29, VK_SPACE },
			{ 39, 'A' },
			{ 43, 'E' },
			{ 42, 'D' },
			{ 55, 'Q' },
			{ 57, 'S' }, // unsure
			{ 61, 'W' },
			{ 63, 'Y' },
			{ 84, 'L' }, // unsure

			{ 80, VK_RETURN },
			{ 81, VK_SHIFT },
			{ 82, VK_CONTROL },
			{ 83, VK_MENU },

			{ 91, VK_UP },
			{ 93, VK_DOWN },

			{ 96, MK_LBUTTON },
			{ 97, VK_MBUTTON },
			{ 98, MK_RBUTTON },
		};

		InputState* inputState;
		JvsButtons lastDownState;
		JvsButtons heldButtons;

		int holdTbl[4];
		HoldState holdState;

		void UpdateJvsInput();
		void UpdateDwGuiInput();
		void UpdateMousePvScroll();
		void UpdateHoldState();
		InputState* GetInputStatePtr(void* address);
		JvsButtons GetJvsButtonsState(bool(*buttonTestFunc)(void*));
		JvsButtons GetButtonFromHold();
		char GetKeyState();
		HoldState GetHoldState();
		int GetMaxHoldState();
		bool IsHold();

		void UpdateInputBit(uint32_t bit, uint8_t keycode);
		void UpdateSliderLR();
	};
}


code:
#include "InputState.h"
#include <windows.h>
#include <stdio.h>

namespace TLAC::Components
{
	void InputState::ClearState()
	{
		memset(this, 0, sizeof(InputState));
	}

	void InputState::HideCursor()
	{
		MouseX = INT32_MIN;
		MouseY = INT32_MIN;
		MouseDeltaX = 0;
		MouseDeltaY = 0;
	}

	void InputState::SetBit(uint32_t bit, bool value, InputBufferType inputType)
	{
		uint8_t* data = GetInputBuffer(inputType);

		if (data == nullptr || bit < 0 || bit >= MAX_BUTTON_BIT)
			return;

		int byteIndex = (bit / 8);
		int bitIndex = (bit % 8);

		BYTE mask = (1 << bitIndex);

		data[byteIndex] = value ? (data[byteIndex] | mask) : (data[byteIndex] & ~mask);
	}

	uint8_t* InputState::GetInputBuffer(InputBufferType inputType)
	{
		switch (inputType)
		{
		case InputBufferType_Tapped:
			return (uint8_t*)&Tapped;

		case InputBufferType_Released:
			return (uint8_t*)&Released;

		case InputBufferType_Down:
			return (uint8_t*)&Down;

		case InputBufferType_DoubleTapped:
			return (uint8_t*)&DoubleTapped;

		case InputBufferType_IntervalTapped:
			return (uint8_t*)&IntervalTapped;

		default:
			return nullptr;
		}
	}
}


code:
#pragma once
#include "JvsButtons.h"
#include "InputBufferType.h"

namespace TLAC::Components
{
	const int MAX_BUTTON_BIT = 0x6F;

	// The button state is larger than the size of a register
	// but only the first 32 bits are used during normal gameplay
	// so this will provide the convenience of still being able to access them through a bit field
	union ButtonState
	{
		JvsButtons Buttons;
		uint32_t State[4];
	};

	// total sizeof() == 0x20E0
	struct InputState
	{
		ButtonState Tapped;
		ButtonState Released;

		ButtonState Down;
		uint32_t Padding_20[4];

		ButtonState DoubleTapped;
		uint32_t Padding_30[4];

		ButtonState IntervalTapped;
		uint32_t Padding_38[12];

		int32_t MouseX;
		int32_t MouseY;
		int32_t MouseDeltaX;
		int32_t MouseDeltaY;

		uint32_t Padding_AC[8];
		uint8_t Padding_D0[3];
		char Key;

		void ClearState();
		void HideCursor();
		void SetBit(uint32_t bit, bool value, InputBufferType inputType);
		uint8_t* GetInputBuffer(InputBufferType inputType);
	};
}


code:
#include "TouchPanelEmulator.h"
#include <iostream>
#include "../ComponentsManager.h"
#include "../../Constants.h"
#include "../../Input/Mouse/Mouse.h"
#include "../../Input/Keyboard/Keyboard.h"

using namespace TLAC::Input;

namespace TLAC::Components
{
	TouchPanelEmulator::TouchPanelEmulator()
	{
	}

	TouchPanelEmulator::~TouchPanelEmulator()
	{
	}

	const char* TouchPanelEmulator::GetDisplayName()
	{
		return "touch_panel_emulator";
	}

	void TouchPanelEmulator::Initialize(ComponentsManager* manager)
	{
		componentsManager = manager;
		state = GetTouchStatePtr((void*)TASK_TOUCH_ADDRESS);
	}

	void TouchPanelEmulator::Update()
	{
		state->ConnectionState = 1;
	}

	void TouchPanelEmulator::UpdateInput()
	{
		if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive() || componentsManager->IsDwGuiHovered())
			return;

		// TODO: rescale TouchReaction aet position
		auto keyboard = Keyboard::GetInstance();
		auto pos = Mouse::GetInstance()->GetRelativePosition();

		state->XPosition = (float)pos.x;
		state->YPosition = (float)pos.y;

		bool down = keyboard->IsDown(VK_LBUTTON);
		bool released = keyboard->IsReleased(VK_LBUTTON);

		state->ContactType = (down ? 0x2 : released ? 0x1 : 0x0);
		state->Pressure = (float)(state->ContactType != 0);
	}

	TouchPanelState* TouchPanelEmulator::GetTouchStatePtr(void *address)
	{
		return (TouchPanelState*)address;
	}
}


code:
#pragma once
#include "../EmulatorComponent.h"
#include "TouchPanelState.h"

namespace TLAC::Components
{
	class TouchPanelEmulator : public EmulatorComponent
	{
	public:
		TouchPanelEmulator();
		~TouchPanelEmulator();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

	private:
		ComponentsManager* componentsManager;

		TouchPanelState* state;
		TouchPanelState* GetTouchStatePtr(void *address);
	};
}


code:
#pragma once

namespace TLAC::Components
{
	struct TouchPanelState
	{
		int Padding00[0x1E];
		int ConnectionState;
		int Padding01[0x06];
		float XPosition;
		float YPosition;
		float Pressure;
		int ContactType;
	};
}


code:
#include "TouchSliderEmulator.h"
#include "../ComponentsManager.h"
#include "../../Constants.h"
#include "../../framework.h"
#include "../../Input/Mouse/Mouse.h"
#include "../../Input/Keyboard/Keyboard.h"
#include "../../Input/Bindings/KeyboardBinding.h"
#include "../../Input/KeyConfig/Config.h"
#include "../../FileSystem/ConfigFile.h"
#include "../../Utilities/Math.h"
#include <algorithm>
#include <stdio.h>

using namespace TLAC::Input;
using namespace TLAC::Input::KeyConfig;
using namespace TLAC::Utilities;

namespace TLAC::Components
{
	const std::string KEY_CONFIG_FILE_NAME = "keyconfig.ini";

	TouchSliderEmulator* TouchSliderEmulator::LatestInstance;

	TouchSliderEmulator::TouchSliderEmulator()
	{
		LatestInstance = this;
	}

	TouchSliderEmulator::~TouchSliderEmulator()
	{
		delete LeftSideSlideLeft;
		delete LeftSideSlideRight;

		delete RightSideSlideLeft;
		delete RightSideSlideRight;
	}

	const char* TouchSliderEmulator::GetDisplayName()
	{
		return "touch_slider_emulator";
	}

	void TouchSliderEmulator::Initialize(ComponentsManager* manager)
	{
		componentsManager = manager;
		sliderState = (TouchSliderState*)SLIDER_CTRL_TASK_ADDRESS;

		LeftSideSlideLeft = new Binding();
		LeftSideSlideRight = new Binding();

		RightSideSlideLeft = new Binding();
		RightSideSlideRight = new Binding();

		FileSystem::ConfigFile configFile(framework::GetModuleDirectory(), KEY_CONFIG_FILE_NAME);
		configFile.OpenRead();

		Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_LEFT", *LeftSideSlideLeft, { "Q" });
		Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_RIGHT", *LeftSideSlideRight, { "E" });

		Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_LEFT", *RightSideSlideLeft, { "U" });
		Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_RIGHT", *RightSideSlideRight, { "O" });

		float touchSliderEmulationSpeed = configFile.GetFloatValue("touch_slider_emulation_speed");

		if (touchSliderEmulationSpeed != 0.0f)
			sliderSpeed = touchSliderEmulationSpeed;
	}

	void TouchSliderEmulator::Update()
	{
		sliderState->State = SLIDER_OK;
	}

	void TouchSliderEmulator::UpdateInput()
	{
		if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive())
			return;

		sliderIncrement = GetElapsedTime() / sliderSpeed;

		constexpr float sensorStep = (1.0f / SLIDER_SENSORS);

		EmulateSliderInput(LeftSideSlideLeft, LeftSideSlideRight, ContactPoints[0], 0.0f, 0.5f);
		EmulateSliderInput(RightSideSlideLeft, RightSideSlideRight, ContactPoints[1], 0.5f + sensorStep, 1.0f + sensorStep);

		sliderState->ResetSensors();

		for (int i = 0; i < CONTACT_POINTS; i++)
			ApplyContactPoint(ContactPoints[i], i);
	}

	void TouchSliderEmulator::OnFocusLost()
	{
		sliderState->ResetSensors();
	}

	void TouchSliderEmulator::EmulateSliderInput(Binding *leftBinding, Binding *rightBinding, ContactPoint &contactPoint, float start, float end)
	{
		bool leftDown = leftBinding->AnyDown();
		bool rightDown = rightBinding->AnyDown();

		if (leftDown)
			contactPoint.Position -= sliderIncrement;
		else if (rightDown)
			contactPoint.Position += sliderIncrement;

		if (contactPoint.Position < start)
			contactPoint.Position = end;

		if (contactPoint.Position > end)
			contactPoint.Position = start;

		bool leftTapped = leftBinding->AnyTapped();
		bool rightTapped = rightBinding->AnyTapped();

		if (leftTapped || rightTapped)
			contactPoint.Position = (start + end) / 2.0f;

		contactPoint.InContact = leftDown || rightDown;
	}

	void TouchSliderEmulator::ApplyContactPoint(ContactPoint &contactPoint, int section)
	{
		sliderState->SectionTouched[section] = contactPoint.InContact;

		int pressure = contactPoint.InContact ? FULL_PRESSURE : NO_PRESSURE;
		float position = std::clamp(contactPoint.Position, 0.0f, 1.0f);

		if (contactPoint.InContact)
		{
			int sensor = (int)(position * (SLIDER_SENSORS - 1));

			sliderState->SetSensor(sensor, pressure);
		}

		constexpr float startRange = -1.0f;
		constexpr float endRange   = +1.0f;

		sliderState->SectionPositions[section] = contactPoint.InContact ? (ConvertRange(0.0f, 1.0f, startRange, endRange, position)) : 0.0f;
	}
}


code:
#pragma once
#include "../EmulatorComponent.h"
#include "TouchSliderState.h"
#include "../../Input/Bindings/Binding.h"

namespace TLAC::Components
{
	constexpr int SLIDER_INPUTS = 4;
	constexpr int CONTACT_POINTS = 2;

	struct ContactPoint
	{
		float Position;
		bool InContact;
	};

	class TouchSliderEmulator : public EmulatorComponent
	{
	public:
		static TouchSliderEmulator* LatestInstance;

		Input::Binding* LeftSideSlideLeft;
		Input::Binding* LeftSideSlideRight;

		Input::Binding* RightSideSlideLeft;
		Input::Binding* RightSideSlideRight;

		TouchSliderEmulator();
		~TouchSliderEmulator();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

		virtual void OnFocusLost() override;

		bool isSliderTouched()
		{
			for (int i = 0; i < 32; i++)
				if (sliderState->SensorTouched[i].IsTouched) return true;

			return false;
		}

	private:
		ComponentsManager* componentsManager;
		float sliderSpeed = 750.0f;
		float sliderIncrement;

		TouchSliderState *sliderState;
		ContactPoint ContactPoints[CONTACT_POINTS];

		void EmulateSliderInput(Input::Binding *leftBinding, Input::Binding *rightBinding, ContactPoint &contactPoint, float start, float end);
		void ApplyContactPoint(ContactPoint &contactPoint, int section);
	};
}


code:
#include "TouchSliderState.h"

namespace TLAC::Components
{
	void TouchSliderState::SetSensor(int index, int value)
	{
		if (index < 0 || index >= SLIDER_SENSORS)
			return;

		SensorPressureLevels[index] = value;
		SensorTouched[index].IsTouched = value > 0;
	}

	void TouchSliderState::ResetSensors()
	{
		for (int i = 0; i < SLIDER_SENSORS; i++)
			SetSensor(i, NO_PRESSURE);
	}
}


code:
#pragma once
#include <stdint.h>

#define SLIDER_OK 3
#define SLIDER_SECTIONS 4
#define SLIDER_SENSORS 32

#define NO_PRESSURE 0
#define FULL_PRESSURE 180

namespace TLAC::Components
{
	struct TouchSliderState
	{
		uint8_t Padding0000[112];

		int32_t State;

		uint8_t Padding0074[20 + 12];

		int32_t SensorPressureLevels[SLIDER_SENSORS];

		uint8_t Padding0108[52 - 12];

		float SectionPositions[SLIDER_SECTIONS];
		int SectionConnections[SLIDER_SECTIONS];
		uint8_t Padding015C[4];
		bool SectionTouched[SLIDER_SECTIONS];

		uint8_t Padding013C[3128 - 52 - 40];

		struct
		{
			uint8_t Padding00[2];
			bool IsTouched;
			uint8_t Padding[45];
		} SensorTouched[SLIDER_SENSORS];

		void SetSensor(int index, int value);
		void ResetSensors();
	};
}


code:
#pragma once
#include <stdint.h>

struct PlayerData
{
	int8_t use_card;
	int8_t freeplay;
	int8_t field_2;
	int8_t field_3;
	int32_t card_type;
	int32_t field_8;
	int32_t field_C;
	int32_t field_10;
	int32_t field_14;
	int32_t field_18;
	int32_t field_1C;
	int32_t field_20;
	int32_t field_24;
	int32_t field_28;
	int32_t field_2C;
	int32_t field_30;
	int32_t field_34;
	int32_t field_38;
	int32_t field_3C;
	int32_t field_40;
	int32_t field_44;
	int32_t field_48;
	int32_t field_4C;
	int32_t field_50;
	int32_t field_54;
	int32_t field_58;
	int32_t field_5C;
	int32_t field_60;
	int32_t field_64;
	int32_t field_68;
	int32_t field_6C;
	int32_t field_70;
	int32_t field_74;
	int32_t field_78;
	int32_t field_7C;
	int32_t field_80;
	int32_t field_84;
	int32_t field_88;
	int32_t field_8C;
	int32_t field_90;
	int32_t field_94;
	int32_t field_98;
	int32_t field_9C;
	int32_t field_A0;
	int32_t field_A4;
	int32_t field_A8;
	int32_t field_AC;
	int32_t field_B0;
	int32_t field_B4;
	int32_t field_B8;
	int32_t field_BC;
	int32_t field_C0;
	int32_t field_C4;
	int32_t field_C8;
	int32_t field_CC;
	int32_t play_data_id;
	int32_t accept_index;
	int32_t start_index;
	int32_t field_DC;
	char* player_name;
	int32_t field_E8;
	int32_t field_EC;
	int32_t field_F0;
	int32_t field_F4;
	int32_t field_F8;
	int32_t field_FC;
	char* level_name;
	int32_t field_108;
	int32_t field_10C;
	int32_t field_110;
	int32_t field_114;
	int32_t field_118;
	int32_t field_11C;
	int32_t field_120;
	int32_t level_plate_id;
	int32_t level_plate_effect;
	int32_t vocaloid_point;
	int32_t hp_vol;
	int32_t act_toggle;
	int32_t act_vol;
	int32_t act_slide_vol;
	int32_t field_140;
	int32_t field_144;
	int32_t field_148;
	int32_t field_14C;
	int32_t field_150;
	int32_t field_154;
	int32_t field_158;
	int32_t field_15C;
	int32_t field_160;
	int32_t field_164;
	int32_t field_168;
	int32_t field_16C;
	int32_t field_170;
	int32_t field_174;
	int32_t field_178;
	int32_t field_17C;
	int32_t field_180;
	int32_t field_184;
	int32_t field_188;
	int32_t field_18C;
	int32_t field_190;
	int32_t field_194;
	int32_t field_198;
	int32_t field_19C;
	int32_t field_1A0;
	int32_t field_1A4;
	int32_t field_1A8;
	int32_t field_1AC;
	int32_t field_1B0;
	int32_t field_1B4;
	int32_t field_1B8;
	int32_t field_1BC;
	int32_t field_1C0;
	int32_t field_1C4;
	int32_t field_1C8;
	int32_t field_1CC;
	int32_t field_1D0;
	int32_t field_1D4;
	int32_t field_1D8;
	int32_t field_1DC;
	int32_t field_1E0;
	int32_t field_1E4;
	int32_t field_1E8;
	int32_t field_1EC;
	int32_t field_1F0;
	int32_t field_1F4;
	int32_t field_1F8;
	int32_t field_1FC;
	int32_t field_200;
	int32_t field_204;
	int32_t field_208;
	int32_t field_20C;
	int32_t field_210;
	int32_t field_214;
	int32_t field_218;
	int32_t field_21C;
	int32_t field_220;
	int32_t field_224;
	int32_t field_228;
	int32_t field_22C;
	int32_t field_230;
	int32_t field_234;
	int32_t field_238;
	int32_t field_23C;
	int32_t field_240;
	int32_t field_244;
	int32_t field_248;
	int32_t field_24C;
	int32_t field_250;
	int32_t field_254;
	int32_t field_258;
	int32_t field_25C;
	int32_t field_260;
	int32_t field_264;
	int32_t field_268;
	int32_t field_26C;
	int32_t field_270;
	int32_t field_274;
	int32_t field_278;
	int32_t field_27C;
	int32_t field_280;
	int32_t field_284;
	int32_t field_288;
	int32_t field_28C;
	int32_t field_290;
	int32_t field_294;
	int32_t field_298;
	int32_t field_29C;
	int32_t field_2A0;
	int32_t field_2A4;
	int32_t field_2A8;
	int32_t field_2AC;
	int8_t use_pv_module_equip;
	int8_t ch_pv_module_equip;
	int8_t field_2B2;
	int8_t field_2B3;
	int32_t module_filter_kind;
	int32_t field_2B8;
	int32_t field_2BC;
	int32_t field_2C0;
	int32_t field_2C4;
	int32_t field_2C8;
	int32_t field_2CC;
	int32_t field_2D0;
	int32_t field_2D4;
	int32_t field_2D8;
	int32_t field_2DC;
	int32_t field_2E0;
	int32_t field_2E4;
	int32_t field_2E8;
	int32_t field_2EC;
	int32_t field_2F0;
	int32_t field_2F4;
	int32_t field_2F8;
	int32_t field_2FC;
	int32_t field_300;
	int32_t field_304;
	int32_t field_308;
	int32_t field_30C;
	int32_t field_310;
	int32_t field_314;
	int32_t field_318;
	int32_t field_31C;
	int32_t field_320;
	int32_t field_324;
	int32_t field_328;
	int32_t field_32C;
	int32_t field_330;
	int32_t field_334;
	int32_t field_338;
	int32_t field_33C;
	int32_t field_340;
	int32_t field_344;
	int32_t field_348;
	int32_t field_34C;
	int32_t field_350;
	int32_t field_354;
	int32_t field_358;
	int32_t field_35C;
	int32_t field_360;
	int32_t field_364;
	int32_t field_368;
	int32_t field_36C;
	int32_t field_370;
	int32_t field_374;
	int32_t field_378;
	int32_t field_37C;
	int32_t field_380;
	int32_t field_384;
	int32_t field_388;
	int32_t field_38C;
	int32_t field_390;
	int32_t field_394;
	int32_t field_398;
	int32_t field_39C;
	int32_t field_3A0;
	int32_t field_3A4;
	int32_t field_3A8;
	int32_t field_3AC;
	int32_t field_3B0;
	int32_t field_3B4;
	int32_t field_3B8;
	int32_t field_3BC;
	int32_t field_3C0;
	int32_t field_3C4;
	int32_t field_3C8;
	int32_t field_3CC;
	int32_t field_3D0;
	int32_t field_3D4;
	int32_t field_3D8;
	int32_t field_3DC;
	int32_t field_3E0;
	int32_t field_3E4;
	int32_t field_3E8;
	int32_t field_3EC;
	int32_t field_3F0;
	int32_t field_3F4;
	int32_t field_3F8;
	int32_t field_3FC;
	int32_t field_400;
	int32_t field_404;
	int32_t field_408;
	int32_t field_40C;
	int32_t field_410;
	int32_t field_414;
	int32_t field_418;
	int32_t field_41C;
	int32_t field_420;
	int32_t field_424;
	int32_t field_428;
	int32_t field_42C;
	int32_t field_430;
	int32_t field_434;
	int32_t field_438;
	int32_t field_43C;
	int32_t field_440;
	int32_t field_444;
	int32_t field_448;
	int32_t field_44C;
	int32_t field_450;
	int32_t field_454;
	int32_t field_458;
	int32_t field_45C;
	int32_t field_460;
	int32_t field_464;
	int32_t field_468;
	int32_t field_46C;
	int32_t field_470;
	int32_t field_474;
	int32_t field_478;
	int32_t field_47C;
	int32_t field_480;
	int32_t field_484;
	int32_t field_488;
	int32_t field_48C;
	int32_t field_490;
	int32_t field_494;
	int32_t field_498;
	int32_t field_49C;
	int32_t field_4A0;
	int32_t field_4A4;
	int32_t field_4A8;
	int32_t field_4AC;
	int32_t field_4B0;
	int32_t field_4B4;
	int32_t field_4B8;
	int32_t field_4BC;
	int32_t field_4C0;
	int32_t field_4C4;
	int32_t field_4C8;
	int32_t field_4CC;
	int32_t field_4D0;
	int32_t field_4D4;
	int32_t field_4D8;
	int32_t field_4DC;
	int32_t field_4E0;
	int32_t field_4E4;
	int32_t field_4E8;
	int32_t field_4EC;
	int32_t field_4F0;
	int32_t field_4F4;
	int32_t field_4F8;
	int32_t field_4FC;
	int32_t field_500;
	int32_t field_504;
	int32_t field_508;
	int32_t field_50C;
	int32_t field_510;
	int32_t field_514;
	int32_t field_518;
	int32_t field_51C;
	int32_t field_520;
	int32_t field_524;
	int32_t field_528;
	int32_t field_52C;
	int32_t field_530;
	int32_t field_534;
	int32_t field_538;
	int32_t field_53C;
	int32_t field_540;
	int32_t field_544;
	int32_t skin_equip;
	int32_t skin_equip_cmn;
	int32_t use_pv_skin_equip;
	int32_t btn_se_equip;
	int32_t btn_se_equip_cmn;
	int32_t use_pv_btn_se_equip;
	int32_t slide_se_equip;
	int32_t slide_se_equip_cmn;
	int32_t use_pv_slide_se_equip;
	int32_t chainslide_se_equip;
	int32_t chainslide_se_equip_cmn;
	int32_t use_pv_chainslide_se_equip;
	int32_t slidertouch_se_equip;
	int32_t slidertouch_se_equip_cmn;
	int32_t use_pv_slidertouch_se_equip;
	int32_t field_584;
	int32_t field_588;
	int32_t field_58C;
	int32_t field_590;
	int32_t field_594;
	int32_t field_598;
	int32_t field_59C;
	int32_t field_5A0;
	int32_t field_5A4;
	int32_t field_5A8;
	int32_t field_5AC;
	int32_t field_5B0;
	int32_t field_5B4;
	int32_t field_5B8;
	int32_t field_5BC;
	int32_t field_5C0;
	int32_t field_5C4;
	int32_t field_5C8;
	int32_t field_5CC;
	int32_t field_5D0;
	int32_t field_5D4;
	int32_t field_5D8;
	int32_t field_5DC;
	int32_t field_5E0;
	int32_t field_5E4;
	int32_t field_5E8;
	int32_t field_5EC;
	int32_t field_5F0;
	int32_t field_5F4;
	int32_t field_5F8;
	int32_t field_5FC;
	int32_t field_600;
	int32_t field_604;
	int32_t field_608;
	int32_t field_60C;
	int32_t field_610;
	int32_t field_614;
	int32_t field_618;
	int32_t field_61C;
	int32_t field_620;
	int32_t field_624;
	int32_t field_628;
	int32_t field_62C;
	int32_t field_630;
	int32_t field_634;
	int32_t field_638;
	int32_t field_63C;
	int32_t field_640;
	int32_t field_644;
	int32_t field_648;
	int32_t field_64C;
	int32_t field_650;
	int32_t field_654;
	int32_t field_658;
	int32_t field_65C;
	int32_t field_660;
	int32_t field_664;
	int32_t field_668;
	int32_t field_66C;
	int32_t field_670;
	int32_t field_674;
	int32_t field_678;
	int32_t field_67C;
	int32_t field_680;
	int32_t field_684;
	int32_t field_688;
	int32_t field_68C;
	int32_t field_690;
	int32_t field_694;
	int32_t field_698;
	int32_t field_69C;
	int32_t field_6A0;
	int32_t field_6A4;
	int32_t field_6A8;
	int32_t field_6AC;
	int32_t field_6B0;
	int32_t field_6B4;
	int32_t field_6B8;
	int32_t field_6BC;
	int32_t field_6C0;
	int32_t field_6C4;
	int32_t field_6C8;
	int32_t field_6CC;
	int32_t field_6D0;
	int32_t field_6D4;
	int32_t field_6D8;
	int32_t field_6DC;
	int32_t field_6E0;
	int32_t field_6E4;
	int32_t field_6E8;
	int32_t field_6EC;
	int32_t field_6F0;
	int32_t field_6F4;
	int32_t field_6F8;
	int32_t field_6FC;
	int32_t field_700;
	int32_t field_704;
	int32_t field_708;
	int32_t field_70C;
	int32_t field_710;
	int32_t field_714;
	int32_t field_718;
	int32_t field_71C;
	int32_t field_720;
	int32_t field_724;
	int32_t field_728;
	int32_t field_72C;
	int32_t field_730;
	int32_t field_734;
	int32_t field_738;
	int32_t field_73C;
	int32_t field_740;
	int32_t field_744;
	int32_t field_748;
	int32_t field_74C;
	int32_t field_750;
	int32_t field_754;
	int32_t field_758;
	int32_t field_75C;
	int32_t field_760;
	int32_t field_764;
	int32_t field_768;
	int32_t field_76C;
	int32_t field_770;
	int32_t field_774;
	int32_t field_778;
	int32_t field_77C;
	int32_t field_780;
	int32_t field_784;
	int32_t field_788;
	int32_t field_78C;
	int32_t field_790;
	int32_t field_794;
	int32_t field_798;
	int32_t field_79C;
	int32_t field_7A0;
	int32_t field_7A4;
	int32_t field_7A8;
	int32_t field_7AC;
	int32_t field_7B0;
	int32_t field_7B4;
	int32_t field_7B8;
	int32_t field_7BC;
	int32_t field_7C0;
	int32_t field_7C4;
	int32_t field_7C8;
	int32_t field_7CC;
	int32_t field_7D0;
	int32_t field_7D4;
	int32_t field_7D8;
	int32_t field_7DC;
	int32_t field_7E0;
	int32_t field_7E4;
	int32_t field_7E8;
	int32_t field_7EC;
	int32_t field_7F0;
	int32_t field_7F4;
	int32_t field_7F8;
	int32_t field_7FC;
	int32_t field_800;
	int32_t field_804;
	int32_t field_808;
	int32_t field_80C;
	int32_t field_810;
	int32_t field_814;
	int32_t field_818;
	int32_t field_81C;
	int32_t field_820;
	int32_t field_824;
	int32_t field_828;
	int32_t field_82C;
	int32_t field_830;
	int32_t field_834;
	int32_t field_838;
	int32_t field_83C;
	int32_t field_840;
	int32_t field_844;
	int32_t field_848;
	int32_t field_84C;
	int32_t field_850;
	int32_t field_854;
	int32_t field_858;
	int32_t field_85C;
	int32_t field_860;
	int32_t field_864;
	int32_t field_868;
	int32_t field_86C;
	int32_t field_870;
	int32_t field_874;
	int32_t field_878;
	int32_t field_87C;
	int32_t field_880;
	int32_t field_884;
	int32_t field_888;
	int32_t field_88C;
	int32_t field_890;
	int32_t field_894;
	int32_t field_898;
	int32_t field_89C;
	int32_t field_8A0;
	int32_t field_8A4;
	int32_t field_8A8;
	int32_t field_8AC;
	int32_t field_8B0;
	int32_t field_8B4;
	int32_t field_8B8;
	int32_t field_8BC;
	int32_t field_8C0;
	int32_t field_8C4;
	int32_t field_8C8;
	int32_t field_8CC;
	int32_t field_8D0;
	int32_t field_8D4;
	int32_t field_8D8;
	int32_t field_8DC;
	int32_t field_8E0;
	int32_t field_8E4;
	int32_t field_8E8;
	int32_t field_8EC;
	int32_t field_8F0;
	int32_t field_8F4;
	int32_t field_8F8;
	int32_t field_8FC;
	int32_t field_900;
	int32_t field_904;
	int32_t field_908;
	int32_t field_90C;
	int32_t field_910;
	int32_t field_914;
	int32_t field_918;
	int32_t field_91C;
	int32_t field_920;
	int32_t field_924;
	int32_t field_928;
	int32_t field_92C;
	int32_t field_930;
	int32_t field_934;
	int32_t field_938;
	int32_t field_93C;
	int32_t field_940;
	int32_t field_944;
	int32_t field_948;
	int32_t field_94C;
	int32_t field_950;
	int32_t field_954;
	int32_t field_958;
	int32_t field_95C;
	int32_t field_960;
	int32_t field_964;
	int32_t field_968;
	int32_t field_96C;
	int32_t field_970;
	int32_t field_974;
	int32_t field_978;
	int32_t field_97C;
	int32_t field_980;
	int32_t field_984;
	int32_t field_988;
	int32_t field_98C;
	int32_t field_990;
	int32_t field_994;
	int32_t field_998;
	int32_t field_99C;
	int32_t field_9A0;
	int32_t field_9A4;
	int32_t field_9A8;
	int32_t field_9AC;
	int32_t field_9B0;
	int32_t field_9B4;
	int32_t field_9B8;
	int32_t field_9BC;
	int32_t field_9C0;
	int32_t field_9C4;
	int32_t field_9C8;
	int32_t field_9CC;
	int32_t field_9D0;
	int32_t field_9D4;
	int32_t field_9D8;
	int32_t field_9DC;
	int32_t field_9E0;
	int32_t field_9E4;
	int32_t field_9E8;
	int32_t field_9EC;
	int32_t field_9F0;
	int32_t field_9F4;
	int32_t field_9F8;
	int32_t field_9FC;
	int32_t field_A00;
	int32_t field_A04;
	int32_t field_A08;
	int32_t field_A0C;
	int32_t field_A10;
	int32_t field_A14;
	int32_t field_A18;
	int32_t field_A1C;
	int32_t field_A20;
	int32_t field_A24;
	int32_t field_A28;
	int32_t field_A2C;
	int32_t field_A30;
	int32_t field_A34;
	int32_t field_A38;
	int32_t field_A3C;
	int32_t field_A40;
	int32_t field_A44;
	int32_t field_A48;
	int32_t field_A4C;
	int32_t field_A50;
	int32_t field_A54;
	int32_t field_A58;
	int32_t field_A5C;
	int32_t field_A60;
	int32_t field_A64;
	int32_t field_A68;
	int32_t field_A6C;
	int32_t field_A70;
	int32_t field_A74;
	int32_t field_A78;
	int32_t field_A7C;
	int32_t field_A80;
	int32_t field_A84;
	int32_t field_A88;
	int32_t field_A8C;
	int32_t field_A90;
	int32_t field_A94;
	int32_t field_A98;
	int32_t field_A9C;
	int32_t field_AA0;
	int32_t field_AA4;
	int32_t field_AA8;
	int32_t field_AAC;
	int32_t field_AB0;
	int32_t field_AB4;
	int32_t field_AB8;
	int32_t field_ABC;
	int32_t field_AC0;
	int32_t field_AC4;
	int32_t field_AC8;
	int32_t field_ACC;
	int32_t field_AD0;
	int32_t field_AD4;
	int32_t field_AD8;
	int32_t field_ADC;
	int32_t field_AE0;
	int32_t field_AE4;
	int32_t field_AE8;
	int32_t field_AEC;
	int32_t field_AF0;
	int32_t field_AF4;
	int32_t field_AF8;
	int32_t field_AFC;
	int32_t field_B00;
	int32_t field_B04;
	int32_t field_B08;
	int32_t field_B0C;
	int32_t field_B10;
	int32_t field_B14;
	int32_t field_B18;
	int32_t field_B1C;
	int32_t field_B20;
	int32_t field_B24;
	int32_t field_B28;
	int32_t field_B2C;
	int32_t field_B30;
	int32_t field_B34;
	int32_t field_B38;
	int32_t field_B3C;
	int32_t field_B40;
	int32_t field_B44;
	int32_t field_B48;
	int32_t field_B4C;
	int32_t field_B50;
	int32_t field_B54;
	int32_t field_B58;
	int32_t field_B5C;
	int32_t field_B60;
	int32_t field_B64;
	int32_t field_B68;
	int32_t field_B6C;
	int32_t field_B70;
	int32_t field_B74;
	int32_t field_B78;
	int32_t field_B7C;
	int32_t field_B80;
	int32_t field_B84;
	int32_t field_B88;
	int32_t field_B8C;
	int32_t field_B90;
	int32_t field_B94;
	int32_t field_B98;
	int32_t field_B9C;
	int32_t field_BA0;
	int32_t field_BA4;
	int32_t field_BA8;
	int32_t field_BAC;
	int32_t field_BB0;
	int32_t field_BB4;
	int32_t field_BB8;
	int32_t field_BBC;
	int32_t field_BC0;
	int32_t field_BC4;
	int32_t field_BC8;
	int32_t field_BCC;
	int32_t field_BD0;
	int32_t field_BD4;
	int32_t field_BD8;
	int32_t field_BDC;
	int32_t field_BE0;
	int32_t field_BE4;
	int32_t field_BE8;
	int32_t field_BEC;
	int32_t field_BF0;
	int32_t field_BF4;
	int32_t field_BF8;
	int32_t field_BFC;
	int32_t field_C00;
	int32_t field_C04;
	int32_t field_C08;
	int32_t field_C0C;
	int32_t field_C10;
	int32_t field_C14;
	int32_t field_C18;
	int32_t field_C1C;
	int32_t field_C20;
	int32_t field_C24;
	int32_t field_C28;
	int32_t field_C2C;
	int32_t field_C30;
	int32_t field_C34;
	int32_t field_C38;
	int32_t field_C3C;
	int32_t field_C40;
	int32_t field_C44;
	int32_t field_C48;
	int32_t field_C4C;
	int32_t field_C50;
	int32_t field_C54;
	int32_t field_C58;
	int32_t field_C5C;
	int32_t field_C60;
	int32_t field_C64;
	int32_t field_C68;
	int32_t field_C6C;
	int32_t field_C70;
	int32_t field_C74;
	int32_t field_C78;
	int32_t field_C7C;
	int32_t field_C80;
	int32_t field_C84;
	int32_t field_C88;
	int32_t field_C8C;
	int32_t field_C90;
	int32_t field_C94;
	int32_t field_C98;
	int32_t field_C9C;
	int32_t field_CA0;
	int32_t field_CA4;
	int32_t field_CA8;
	int32_t field_CAC;
	int32_t field_CB0;
	int32_t field_CB4;
	int32_t field_CB8;
	int32_t field_CBC;
	int32_t field_CC0;
	int32_t field_CC4;
	int32_t field_CC8;
	int32_t field_CCC;
	int32_t field_CD0;
	int32_t field_CD4;
	int32_t field_CD8;
	int32_t field_CDC;
	int32_t field_CE0;
	int32_t field_CE4;
	int32_t field_CE8;
	int32_t field_CEC;
	int32_t field_CF0;
	int32_t field_CF4;
	int32_t field_CF8;
	int32_t field_CFC;
	int32_t field_D00;
	int32_t field_D04;
	int32_t field_D08;
	int32_t field_D0C;
	int32_t field_D10;
	int32_t field_D14;
	int32_t field_D18;
	int32_t field_D1C;
	int32_t field_D20;
	int32_t field_D24;
	int32_t field_D28;
	int32_t field_D2C;
	int32_t field_D30;
	int32_t field_D34;
	int32_t field_D38;
	int32_t field_D3C;
	int32_t field_D40;
	int32_t field_D44;
	int32_t field_D48;
	int32_t field_D4C;
	int32_t field_D50;
	int32_t field_D54;
	int32_t field_D58;
	int32_t field_D5C;
	int32_t field_D60;
	int32_t field_D64;
	int32_t field_D68;
	int32_t field_D6C;
	int32_t field_D70;
	int32_t field_D74;
	int32_t field_D78;
	int32_t field_D7C;
	int32_t field_D80;
	int32_t field_D84;
	int32_t field_D88;
	int32_t field_D8C;
	int32_t field_D90;
	int32_t field_D94;
	int32_t field_D98;
	int32_t field_D9C;
	int32_t field_DA0;
	int32_t field_DA4;
	int32_t field_DA8;
	int32_t field_DAC;
	int32_t field_DB0;
	int32_t field_DB4;
	int32_t field_DB8;
	int32_t field_DBC;
	int32_t field_DC0;
	int32_t field_DC4;
	int32_t field_DC8;
	int32_t field_DCC;
	int32_t field_DD0;
	int32_t field_DD4;
	int32_t field_DD8;
	int32_t field_DDC;
	int32_t field_DE0;
	int32_t field_DE4;
	int32_t field_DE8;
	int32_t field_DEC;
	int32_t field_DF0;
	int32_t field_DF4;
	int32_t field_DF8;
	int32_t field_DFC;
	int32_t field_E00;
	int32_t field_E04;
	int32_t field_E08;
	int32_t field_E0C;
	int32_t field_E10;
	int32_t field_E14;
	int32_t field_E18;
	int32_t field_E1C;
	int32_t field_E20;
	int32_t field_E24;
	int32_t field_E28;
	int32_t field_E2C;
	int32_t field_E30;
	int8_t field_E34;
	int8_t game_opts;
	int8_t field_E36;
	int8_t field_E37;
	int32_t field_E38;
	int32_t field_E3C;
	int32_t field_E40;
	int32_t field_E44;
	int32_t field_E48;
	int32_t field_E4C;
	int32_t field_E50;
	int32_t field_E54;
	int32_t field_E58;
	int32_t field_E5C;
	int32_t field_E60;
	int32_t field_E64;
	int32_t field_E68;
	int32_t field_E6C;
	int32_t field_E70;
	int32_t field_E74;
	int32_t field_E78;
	int32_t field_E7C;
	int32_t field_E80;
	int32_t field_E84;
	int32_t field_E88;
	int32_t field_E8C;
	int32_t field_E90;
	int32_t field_E94;
	int32_t field_E98;
	int32_t field_E9C;
	int32_t field_EA0;
	int32_t field_EA4;
	int32_t field_EA8;
	int32_t field_EAC;
	int32_t field_EB0;
	int32_t field_EB4;
	int32_t field_EB8;
	int32_t field_EBC;
	int32_t field_EC0;
	int32_t field_EC4;
	int32_t field_EC8;
	int32_t field_ECC;
	int32_t field_ED0;
	int32_t field_ED4;
	int32_t field_ED8;
	int32_t field_EDC;
	int32_t field_EE0;
	int32_t field_EE4;
	int32_t field_EE8;
	int32_t field_EEC;
	int32_t field_EF0;
	int32_t field_EF4;
	int32_t field_EF8;
	int32_t field_EFC;
};


code:
#include "PlayerDataManager.h"
#include <string>
#include "../framework.h"
#include "../Constants.h"
#include "../Input/Keyboard/Keyboard.h"
#include "../FileSystem/ConfigFile.h"
#include "../Constants.h"

const std::string PLAYER_DATA_FILE_NAME = "playerdata.ini";

namespace TLAC::Components
{
	PlayerDataManager::PlayerDataManager()
	{
	}

	PlayerDataManager::~PlayerDataManager()
	{
		if (customPlayerData != nullptr)
			delete customPlayerData;
	}

	const char* PlayerDataManager::GetDisplayName()
	{
		return "player_data_manager";
	}

	void PlayerDataManager::Initialize(ComponentsManager*)
	{
		playerData = (PlayerData*)PLAYER_DATA_ADDRESS;

		ApplyPatch();
		LoadConfig();
		ApplyCustomData();
	}

	void PlayerDataManager::Update()
	{
		ApplyCustomData();

		if (false && Input::Keyboard::GetInstance()->IsTapped(VK_F12))
		{
			printf("[TLAC] PlayerDataManager::Update(): Loading config...\n");
			LoadConfig();
		}
		if (moduleCardWorkaround) {
			int* pvId = (int*)0x00000001418054C4;
			int* modState = (int*)0x00000001411A9790;
			int* loadState = (int*)0x0000000141197E58;

			if (!customPlayerData->UseCard)
			{
				if (*(char*)0x000000014CC5E270 == 32)
				{
					DWORD oldProtect, bck;
					VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405BC8E6 + 0) = 0x00;
					VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);
					pvModuleLoaded = false;
				}
				else {
					if (!pvModuleLoaded)
					{
						pvModuleLoaded = true;
						DWORD oldProtect, bck;
						VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
						*((BYTE*)0x00000001405BC8E6 + 0) = 0x01;
						VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);
					}
				}

				if ((initPvId == false) || ((lastModState == 0) && (*modState == 1)))
				{
					if ((lastModState == 0) && (*modState == 1))
					{
						*(int*)0x00000001411A8A10 = *(int*)0x00000001411A8A28;
						*(int*)(0x00000001411A8A10 + 4) = *(int*)(0x00000001411A8A28 + 4);
						*(int*)(0x00000001411A8A10 + 8) = *(int*)(0x00000001411A8A28 + 8);
						*(int*)(0x00000001411A8A10 + 12) = *(int*)(0x00000001411A8A28 + 12);
						*(int*)(0x00000001411A8A10 + 16) = *(int*)(0x00000001411A8A28 + 16);
						*(int*)(0x00000001411A8A10 + 18) = *(int*)(0x00000001411A8A28 + 18);
					}

					initPvId = true;
					lastModState = *modState;
					DWORD oldProtect, bck;
					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405CBBA3 + 0) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 1) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 2) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 3) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 4) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 5) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 6) = 0x90;
					*((BYTE*)0x00000001405CBBA3 + 7) = 0x90;
					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, oldProtect, &bck);
				}

				if (((*modState == 0) && (*pvId != lastPvId)) || ((lastModState == 1) && (*modState == 0)))
				{
					if ((lastModState == 1) && (*modState == 0))
					{
						*(int*)0x00000001411A8A28 = *(int*)0x00000001411A8A10;
						*(int*)(0x00000001411A8A28 + 4) = *(int*)(0x00000001411A8A10 + 4);
						*(int*)(0x00000001411A8A28 + 8) = *(int*)(0x00000001411A8A10 + 8);
						*(int*)(0x00000001411A8A28 + 12) = *(int*)(0x00000001411A8A10 + 12);
						*(int*)(0x00000001411A8A28 + 16) = *(int*)(0x00000001411A8A10 + 16);
						*(int*)(0x00000001411A8A28 + 18) = *(int*)(0x00000001411A8A10 + 18);
					}

					initPvId = false;
					lastPvId = *pvId;
					lastModState = *modState;
					DWORD oldProtect, bck;
					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405CBBA3 + 0) = 0x42;
					*((BYTE*)0x00000001405CBBA3 + 1) = 0x89;
					*((BYTE*)0x00000001405CBBA3 + 2) = 0x84;
					*((BYTE*)0x00000001405CBBA3 + 3) = 0xb6;
					*((BYTE*)0x00000001405CBBA3 + 4) = 0xc0;
					*((BYTE*)0x00000001405CBBA3 + 5) = 0x01;
					*((BYTE*)0x00000001405CBBA3 + 6) = 0x00;
					*((BYTE*)0x00000001405CBBA3 + 7) = 0x00;
					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, oldProtect, &bck);
				}
			}
			else {
				if (*(char*)0x000000014CC5E270 == 32)
				{
					DWORD oldProtect, bck;
					VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405BC8E6 + 0) = 0x00;
					VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);

					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405CBBA3 + 0) = 0x42;
					*((BYTE*)0x00000001405CBBA3 + 1) = 0x89;
					*((BYTE*)0x00000001405CBBA3 + 2) = 0x84;
					*((BYTE*)0x00000001405CBBA3 + 3) = 0xb6;
					*((BYTE*)0x00000001405CBBA3 + 4) = 0xc0;
					*((BYTE*)0x00000001405CBBA3 + 5) = 0x01;
					*((BYTE*)0x00000001405CBBA3 + 6) = 0x00;
					*((BYTE*)0x00000001405CBBA3 + 7) = 0x00;
					VirtualProtect((BYTE*)0x00000001405CBBA3, 8, oldProtect, &bck);

					VirtualProtect((BYTE*)0x00000001405BCBE3, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
					*((BYTE*)0x00000001405BCBE3 + 0) = 0x00;
					VirtualProtect((BYTE*)0x00000001405BCBE3, 2, oldProtect, &bck);

					pvModuleLoaded = false;
				}
				else {
					if (!pvModuleLoaded)
					{
						pvModuleLoaded = true;
						DWORD oldProtect, bck;
						VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
						*((BYTE*)0x00000001405BC8E6 + 0) = 0x00;
						VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);

						VirtualProtect((BYTE*)0x00000001405CBBA3, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
						*((BYTE*)0x00000001405CBBA3 + 0) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 1) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 2) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 3) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 4) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 5) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 6) = 0x90;
						*((BYTE*)0x00000001405CBBA3 + 7) = 0x90;
						VirtualProtect((BYTE*)0x00000001405CBBA3, 8, oldProtect, &bck);

						VirtualProtect((BYTE*)0x00000001405BCBE3, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
						*((BYTE*)0x00000001405BCBE3 + 0) = 0x01;
						VirtualProtect((BYTE*)0x00000001405BCBE3, 2, oldProtect, &bck);
					}
				}
			}
		}
	}

	void PlayerDataManager::ApplyPatch()
	{
		DWORD oldProtect;
		VirtualProtect((void*)SET_DEFAULT_PLAYER_DATA_ADDRESS, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect);
		{
			// prevent the PlayerData from being reset
			*(uint8_t*)(SET_DEFAULT_PLAYER_DATA_ADDRESS) = RET_OPCODE;
		}
		VirtualProtect((void*)SET_DEFAULT_PLAYER_DATA_ADDRESS, sizeof(uint8_t), oldProtect, &oldProtect);

		// allow player to select the module and extra item
		VirtualProtect((void*)MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS, sizeof(byte) * 2, PAGE_EXECUTE_READWRITE, &oldProtect);
		{
			*(byte*)(MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS) = 0xB0; // xor al,al -> ld al,1
			*(byte*)(MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS + 0x1) = 0x01;
		}
		VirtualProtect((void*)MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS, sizeof(byte) * 2, oldProtect, &oldProtect);

		// fix annoying behavior of closing after changing module or item (don't yet know the reason, maybe NW/Card checks)
		{
			VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_MODULE, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect);
			{
				*(uint8_t*)(MODSELECTOR_CLOSE_AFTER_MODULE) = JNE_OPCODE;
			}
			VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_MODULE, sizeof(uint8_t), oldProtect, &oldProtect);
			VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_CUSTOMIZE, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect);
			{
				*(uint8_t*)(MODSELECTOR_CLOSE_AFTER_CUSTOMIZE) = JNE_OPCODE;
			}
			VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_CUSTOMIZE, sizeof(uint8_t), oldProtect, &oldProtect);
		}
	}

	void PlayerDataManager::LoadConfig()
	{
		if (playerData == nullptr)
			return;

		FileSystem::ConfigFile config(framework::GetModuleDirectory(), PLAYER_DATA_FILE_NAME);

		if (!config.OpenRead())
			return;

		if (customPlayerData != nullptr)
			delete customPlayerData;

		customPlayerData = new CustomPlayerData();
		config.TryGetValue("player_name", &customPlayerData->PlayerName);
		config.TryGetValue("level_name", &customPlayerData->LevelName);

		customPlayerData->LevelPlateId = config.GetIntegerValue("level_plate_id");
		customPlayerData->LevelPlateEffect = config.GetIntegerValue("level_plate_effect");
		customPlayerData->SkinEquip = config.GetIntegerValue("skin_equip");
		customPlayerData->BtnSeEquip = config.GetIntegerValue("btn_se_equip");
		customPlayerData->SlideSeEquip = config.GetIntegerValue("slide_se_equip");
		customPlayerData->ChainslideSeEquip = config.GetIntegerValue("chainslide_se_equip");
		customPlayerData->SlidertouchSeEquip = config.GetIntegerValue("slidertouch_se_equip");
		customPlayerData->ShowExcellentClearBorder = config.GetBooleanValue("border_excellent");
		customPlayerData->ShowGreatClearBorder = config.GetBooleanValue("border_great");
		customPlayerData->ShowRivalClearBorder = config.GetBooleanValue("border_rival");
		customPlayerData->UseCard = config.GetBooleanValue("use_card");
		customPlayerData->GameModifierOptions = config.GetBooleanValue("gamemode_options");
		customPlayerData->ActionSE = config.GetBooleanValue("act_toggle");
		moduleCardWorkaround = config.GetBooleanValue("module_card_workaround");

		if (moduleCardWorkaround) {
			if (!customPlayerData->UseCard)
			{
				DWORD oldProtect, bck;
				VirtualProtect((BYTE*)0x000000014010523F, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x000000014010523F + 0) = 0x30;
				*((BYTE*)0x000000014010523F + 1) = 0xC0;
				*((BYTE*)0x000000014010523F + 2) = 0x90;
				VirtualProtect((BYTE*)0x000000014010523F, 3, oldProtect, &bck);

				VirtualProtect((BYTE*)0x00000001405BCC48, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x00000001405BCC48 + 0) = 0x90;
				*((BYTE*)0x00000001405BCC48 + 1) = 0x90;
				*((BYTE*)0x00000001405BCC48 + 2) = 0x90;
				*((BYTE*)0x00000001405BCC48 + 3) = 0x90;
				*((BYTE*)0x00000001405BCC48 + 4) = 0x90;
				*((BYTE*)0x00000001405BCC48 + 5) = 0x90;
				VirtualProtect((BYTE*)0x00000001405BCC48, 6, oldProtect, &bck);

				VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x00000001405BC8E6 + 0) = 0x01;
				VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);

			}
			else {
				DWORD oldProtect, bck;
				VirtualProtect((BYTE*)0x00000001405BC8E6, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x00000001405BC8E6 + 0) = 0x00;
				VirtualProtect((BYTE*)0x00000001405BC8E6, 3, oldProtect, &bck);

				VirtualProtect((BYTE*)0x00000001405BCC48, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x00000001405BCC48 + 0) = 0xC7;
				*((BYTE*)0x00000001405BCC48 + 1) = 0x03;
				*((BYTE*)0x00000001405BCC48 + 2) = 0x00;
				*((BYTE*)0x00000001405BCC48 + 3) = 0x00;
				*((BYTE*)0x00000001405BCC48 + 4) = 0x00;
				*((BYTE*)0x00000001405BCC48 + 5) = 0x00;
				VirtualProtect((BYTE*)0x00000001405BCC48, 6, oldProtect, &bck);

				VirtualProtect((BYTE*)0x000000014010523F, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x000000014010523F + 0) = 0x0F;
				*((BYTE*)0x000000014010523F + 1) = 0x94;
				*((BYTE*)0x000000014010523F + 2) = 0xC1;
				VirtualProtect((BYTE*)0x000000014010523F, 3, oldProtect, &bck);

				//just incase the player is reloading
				VirtualProtect((BYTE*)0x00000001405CBBA3, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
				*((BYTE*)0x00000001405CBBA3 + 0) = 0x42;
				*((BYTE*)0x00000001405CBBA3 + 1) = 0x89;
				*((BYTE*)0x00000001405CBBA3 + 2) = 0x84;
				*((BYTE*)0x00000001405CBBA3 + 3) = 0xb6;
				*((BYTE*)0x00000001405CBBA3 + 4) = 0xc0;
				*((BYTE*)0x00000001405CBBA3 + 5) = 0x01;
				*((BYTE*)0x00000001405CBBA3 + 6) = 0x00;
				*((BYTE*)0x00000001405CBBA3 + 7) = 0x00;
				VirtualProtect((BYTE*)0x00000001405CBBA3, 8, oldProtect, &bck);
			}
		}
	}

	void PlayerDataManager::ApplyCustomData()
	{
		// don't want to overwrite the default values
		auto setIfNotEqual = [](int *target, int value, int comparison)
		{
			if (value != comparison)
				*target = value;
		};

		setIfNotEqual(&playerData->level_plate_id, customPlayerData->LevelPlateId, 0);
		setIfNotEqual(&playerData->level_plate_effect, customPlayerData->LevelPlateEffect, 0);
		setIfNotEqual(&playerData->skin_equip, customPlayerData->SkinEquip, 0);
		setIfNotEqual(&playerData->btn_se_equip, customPlayerData->BtnSeEquip, -1);
		setIfNotEqual(&playerData->slide_se_equip, customPlayerData->SlideSeEquip, -1);
		setIfNotEqual(&playerData->chainslide_se_equip, customPlayerData->ChainslideSeEquip, -1);
		setIfNotEqual(&playerData->slidertouch_se_equip, customPlayerData->SlidertouchSeEquip, -1);
		setIfNotEqual(&playerData->act_toggle, customPlayerData->ActionSE, 1);

		// Display clear borders on the progress bar
		*(byte*)(PLAYER_DATA_ADDRESS + 0xD94) = (customPlayerData->ShowRivalClearBorder << 2) | (customPlayerData->ShowExcellentClearBorder << 1) | (customPlayerData->ShowGreatClearBorder);

		playerData->use_card = customPlayerData->UseCard; // required to allow for module selection

		playerData->game_opts = customPlayerData->GameModifierOptions; // hi-speed, etc..

		memset((void*)MODULE_TABLE_START, 0xFF, 128);
		memset((void*)ITEM_TABLE_START, 0xFF, 128);

		if (customPlayerData->PlayerName != nullptr)
		{
			playerData->field_DC = 0x10;
			playerData->player_name = (char*)customPlayerData->PlayerName->c_str();
		}

		if (customPlayerData->LevelName != nullptr)
		{
			playerData->level_name = (char*)customPlayerData->LevelName->c_str();
			playerData->field_110 = 0xFF;
			playerData->field_118 = 0x1F;
		}
	}
}


code:
#pragma once
#include "EmulatorComponent.h"
#include "PlayerData.h"
#include "CustomPlayerData.h"
#include <string>

namespace TLAC::Components
{
	class PlayerDataManager : public EmulatorComponent
	{
	public:
		PlayerDataManager();
		~PlayerDataManager();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;

	private:
		PlayerData* playerData;
		CustomPlayerData* customPlayerData;
		int lastPvId = -1;
		bool initPvId = true;
		bool pvModuleLoaded = true;
		bool moduleCardWorkaround = true;
		int lastModState = 0;
		void ApplyPatch();
		void LoadConfig();
		void ApplyCustomData();
	};
}


code:
#include "ScaleComponent.h"
#include <iostream>
#include <Windows.h>
#include "../Constants.h"
#include <stdio.h>
#include "../framework.h"
#include <chrono>
#include <thread>
#include <tchar.h>
#include <GL/freeglut.h>

namespace TLAC::Components
{

	ScaleComponent::ScaleComponent()
	{
	}

	ScaleComponent::~ScaleComponent()
	{
	}

	const char* ScaleComponent::GetDisplayName()
	{
		return "scale_component";
	}

	void ScaleComponent::Initialize(ComponentsManager*)
	{
		{
			DWORD oldProtect, bck;
			VirtualProtect((BYTE*)0x00000001404ACD24, 7, PAGE_EXECUTE_READWRITE, &oldProtect);
			*((byte*)0x00000001404ACD24 + 0) = 0x44;
			*((byte*)0x00000001404ACD24 + 1) = 0x8B;
			*((byte*)0x00000001404ACD24 + 2) = 0x0D;
			*((byte*)0x00000001404ACD24 + 3) = 0xD1;
			*((byte*)0x00000001404ACD24 + 4) = 0x08;
			*((byte*)0x00000001404ACD24 + 5) = 0xD0;
			*((byte*)0x00000001404ACD24 + 6) = 0x00;
			VirtualProtect((BYTE*)0x00000001404ACD24, 7, oldProtect, &bck);
		}
		{
			DWORD oldProtect, bck;
			VirtualProtect((BYTE*)0x00000001404ACD2B, 7, PAGE_EXECUTE_READWRITE, &oldProtect);
			*((byte*)0x00000001404ACD2B + 0) = 0x44;
			*((byte*)0x00000001404ACD2B + 1) = 0x8B;
			*((byte*)0x00000001404ACD2B + 2) = 0x05;
			*((byte*)0x00000001404ACD2B + 3) = 0xC6;
			*((byte*)0x00000001404ACD2B + 4) = 0x08;
			*((byte*)0x00000001404ACD2B + 5) = 0xD0;
			*((byte*)0x00000001404ACD2B + 6) = 0x00;
			VirtualProtect((BYTE*)0x00000001404ACD2B, 7, oldProtect, &bck);
		}
		{
			DWORD oldProtect, bck;
			VirtualProtect((BYTE*)0x00000001405030A0, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
			*((byte*)0x00000001405030A0 + 0) = 0x90;
			*((byte*)0x00000001405030A0 + 1) = 0x90;
			*((byte*)0x00000001405030A0 + 2) = 0x90;
			*((byte*)0x00000001405030A0 + 3) = 0x90;
			*((byte*)0x00000001405030A0 + 4) = 0x90;
			*((byte*)0x00000001405030A0 + 5) = 0x90;
			VirtualProtect((BYTE*)0x00000001404ACD2B, 6, oldProtect, &bck);
		}
	}

	void ScaleComponent::Update()
	{
		uiAspectRatio = (float*)UI_ASPECT_RATIO;
		uiWidth = (float*)UI_WIDTH_ADDRESS;
		uiHeight = (float*)UI_HEIGHT_ADDRESS;
		fb1Height = (int*)FB1_HEIGHT_ADDRESS;
		fb1Width = (int*)FB1_WIDTH_ADDRESS;
		//fb2Height = (int*)FB2_HEIGHT_ADDRESS;
		//fb2Width = (int*)FB2_WIDTH_ADDRESS;
		fbAspectRatio = (double*)FB_ASPECT_RATIO;
		RECT hWindow;
		GetClientRect(TLAC::framework::DivaWindowHandle, &hWindow);
		*uiAspectRatio = (float)(hWindow.right - hWindow.left) / (float)(hWindow.bottom - hWindow.top);
		*fbAspectRatio = (double)(hWindow.right - hWindow.left) / (double)(hWindow.bottom - hWindow.top);
		*uiWidth = hWindow.right - hWindow.left;
		*uiHeight = hWindow.bottom - hWindow.top;
		*fb1Width = hWindow.right - hWindow.left;
		*fb1Height = hWindow.bottom - hWindow.top;
		//*fb2Width = hWindow.right - hWindow.left;
		//*fb2Height = hWindow.bottom - hWindow.top;

		*((int*)0x00000001411AD608) = 0;

		*((int*)0x0000000140EDA8E4) = *(int*)0x0000000140EDA8BC;
		*((int*)0x0000000140EDA8E8) = *(int*)0x0000000140EDA8C0;

		*(float*)0x00000001411A1900 = 0;
		*(float*)0x00000001411A1904 = (float)*(int*)0x0000000140EDA8BC;
		*(float*)0x00000001411A1908 = (float)*(int*)0x0000000140EDA8C0;
		//*((int*)0x00000001411AD5F8) = hWindow.right - hWindow.left;
		//*((int*)0x00000001411AD5FC) = hWindow.bottom - hWindow.top;

		//*((int*)0x00000001411ABB48) = hWindow.right - hWindow.left;
		//*((int*)0x00000001411ABB4C) = hWindow.bottom - hWindow.top;

		//*((int*)0x00000001411ABB5C) = (int)(*((int*)0x00000001411ABB54) * ((float)8 / (float)9)); //wtf??
	}

	void ScaleComponent::UpdateInput()
	{
		return;
	}
}


code:
#pragma once
#include "EmulatorComponent.h"
#include "GameState.h"

namespace TLAC::Components
{
	class ScaleComponent : public EmulatorComponent
	{
	public:
		ScaleComponent();
		~ScaleComponent();

		virtual const char* GetDisplayName() override;

		virtual void Initialize(ComponentsManager*) override;
		virtual void Update() override;
		virtual void UpdateInput() override;

		const int updatesPerFrame = 39;

		float* uiAspectRatio;
		float* uiWidth;
		float* uiHeight;

		int* fb1Width;
		int* fb1Height;
		int* fb2Width;
		int* fb2Height;

		double* fbAspectRatio;

	private:

	};
}