code:
#include "ScoreSaver.h"
#include "../Constants.h"
#include "../framework.h"
#include "../Utilities/Operations.h"
#include <stdio.h>
#include <windows.h>
#include <detours.h>
#include <algorithm>
#pragma comment(lib, "detours.lib")

namespace TLAC::Components
{
	WCHAR ScoreSaver::configPath[256];
	WCHAR ScoreSaver::rival_configPath[256];
	ScoreSaver::ScoreSaver()
	{
		std::string utf8path = TLAC::framework::GetModuleDirectory() + "/scores.ini";
		MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, configPath, 256);
		utf8path = TLAC::framework::GetModuleDirectory() + "/rivalscores.ini";
		MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, rival_configPath, 256);
	}

	ScoreSaver::~ScoreSaver()
	{
		for (int diff = 0; diff < 4; diff++)
		{
			*(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = 0;
			*(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = 0;
		}
	}

	const char* ScoreSaver::GetDisplayName()
	{
		return "score_saver";
	}

	bool(__stdcall* ScoreSaver::divaInitResults)(void* cls) = (bool(__stdcall*)(void* cls))RESULTS_INIT_ADDRESS;
	void ScoreSaver::Initialize(ComponentsManager*)
	{
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach(&(PVOID&)ScoreSaver::divaInitResults, (PVOID)(ScoreSaver::hookedInitResults));
		DetourTransactionCommit();

		// build the score cache
		UpdateScoreCache();
		UpdateClearCounts();

		// update score begin and end vars from game
		for (int diff = 0; diff < 4; diff++)
		{
			*(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = &ScoreCache[diff][0][0];
			*(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = &ScoreCache[diff][1000][0]; // deliberately use 1000 to get past end of cache
		}
	}

	bool ScoreSaver::checkExistingScoreValid(int pv, int difficulty, int isEx)
	{
		WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
		WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers

		const WCHAR section[] = L"scores";

		if (isEx == 0)
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pv, difficulty);
		else
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pv, difficulty);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
		int score = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"notecounts");
		WCHAR countsBufW[32];
		char countsBufA[32];
		int cntHitTypes[2] = { 0, 0 };
		GetPrivateProfileStringW(section, key, L"", countsBufW, 32, configPath);
		WideCharToMultiByte(CP_UTF8, 0, countsBufW, -1, countsBufA, 32, NULL, NULL);
		std::vector<std::string> countToks = TLAC::Utilities::Split(countsBufA, ",");
		if (countToks.size() >= 2)
		{
			cntHitTypes[0] = atoi(countToks[0].c_str());
			cntHitTypes[1] = atoi(countToks[1].c_str());
		}

		swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
		int percent = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"combo");
		int combo = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
		int clearRank = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
		int allTimeRank = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
		int allTimeModifiers = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
		int allTimePercent = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
		int expectedCheck = GetPrivateProfileIntW(section, key, -1, configPath);

		return expectedCheck == calculateCheck(score, cntHitTypes[0], cntHitTypes[1], percent, combo, clearRank, allTimeRank, allTimeModifiers, allTimePercent);
	}

	int ScoreSaver::calculateCheck(int score, int cntCools, int cntFines, int percent, int combo, int clearRank, int allTimeRank, int allTimeModifiers, int allTimePercent)
	{
		int checkField = ((short)score ^ ((short)cntCools << 16) ^ ((short)cntFines) ^ ((short)percent << 16) ^ ((short)combo)) * clearRank;
		checkField += (allTimeRank * clearRank); // first extension
		checkField ^= (allTimeModifiers * allTimePercent);
		return checkField;
	}

	bool ScoreSaver::hookedInitResults(void* cls)
	{
		bool result = divaInitResults(cls);

		int clearRank = *(int*)(RESULTS_BASE_ADDRESS + 0xe8);
		byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14);

		if (insurance != 0)
			return result;

		// get the base for this specific set of results
		uint64_t resultBase = *(uint64_t*)(RESULTS_BASE_ADDRESS + 0x100);

		int pvNum = *(int*)(resultBase + 0x2c);
		int pvDifficulty = *(int*)(resultBase + 0x34);
		int pvDifficultyIsEx = *(int*)(resultBase + 0x44);
		int modifier = *(int*)(resultBase + 0x70);

		int* cntHitTypes = (int*)(resultBase + 0x158);
		int* pctHitTypes = (int*)(resultBase + 0x16c);
		int combo = *(int*)(resultBase + 0x180);
		int challengeScore = *(int*)(resultBase + 0x184);
		int holdScore = *(int*)(resultBase + 0x188);
		int score = *(int*)(resultBase + 0x18c);
		int percent = *(int*)(resultBase + 0x190);
		int slideScore = *(int*)(resultBase + 0x194);

		WCHAR songName[256];
		//std::string utf8song = *(std::string*)(CURRENT_SONG_NAME_ADDRESS);
		uint64_t songnamelen = *(uint64_t*)(CURRENT_SONG_NAME_ADDRESS + 0x18);
		char* utf8song = songnamelen < 0x10 ? (char*)CURRENT_SONG_NAME_ADDRESS : *(char**)CURRENT_SONG_NAME_ADDRESS;
		MultiByteToWideChar(CP_UTF8, 0, utf8song, -1, songName, 256);

		WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
		WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers
		WCHAR val[32]; // needs to be big enough to store five <=four digit ints (with comma separators)

		const WCHAR section[] = L"scores";

		if (pvDifficultyIsEx == 0)
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, pvDifficulty);
		else
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, pvDifficulty);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
		int oldScore = GetPrivateProfileIntW(section, key, 0, configPath);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
		int oldAlltimeRank = GetPrivateProfileIntW(section, key, -1, configPath);

		if (oldAlltimeRank == -1) // fallback for old scores without alltimerank
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
			oldAlltimeRank = GetPrivateProfileIntW(section, key, 0, configPath);
		}
		int allTimeRank = clearRank > oldAlltimeRank ? clearRank : oldAlltimeRank;

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
		int oldAlltimeModifiers = GetPrivateProfileIntW(section, key, -1, configPath);

		if (oldAlltimeModifiers == -1) // fallback for old scores without alltimemodifiers
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
			oldAlltimeModifiers = GetPrivateProfileIntW(section, key, 0, configPath);
			oldAlltimeModifiers = oldAlltimeModifiers > 0 ? 1 << (oldAlltimeModifiers - 1) : 0;
		}
		int allTimeModifiers = oldAlltimeModifiers | ((clearRank > 1 && modifier > 0) ? (1 << (modifier - 1)) : 0); // use clearRank to not update if didn't clear

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
		int oldAlltimePercent = GetPrivateProfileIntW(section, key, -1, configPath);
		if (oldAlltimePercent == -1) // fallback for old scores without alltimepercent
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
			oldAlltimePercent = GetPrivateProfileIntW(section, key, 0, configPath);
		}
		int allTimePercent = percent > oldAlltimePercent ? percent : oldAlltimePercent;

		bool oldScoreValid = true;
		if (!checkExistingScoreValid(pvNum, pvDifficulty, pvDifficultyIsEx))
		{
			oldScore = 0; // ensure new score is written
			allTimeRank = clearRank; // don't trust old all-time clear rank, use a new one
			allTimeModifiers = ((clearRank > 1 && modifier > 0) ? (1 << (modifier - 1)) : 0); // don't trust old all-time modifiers, use a new one
			allTimePercent = percent; // don't trust old all-time percent, use a new one
			oldScoreValid = false;
		}

		if (clearRank > 1 && score > oldScore)
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
			swprintf(val, 32, L"%d", allTimeModifiers);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
			swprintf(val, 32, L"%d", allTimePercent);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
			swprintf(val, 32, L"%d", allTimeRank);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"challengescore");
			swprintf(val, 32, L"%d", challengeScore);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"combo");
			swprintf(val, 32, L"%d", combo);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"holdscore");
			swprintf(val, 32, L"%d", holdScore);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
			swprintf(val, 32, L"%d", modifier);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"name");
			WritePrivateProfileStringW(section, key, songName, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"notecounts");
			swprintf(val, 32, L"%d,%d,%d,%d,%d", cntHitTypes[0], cntHitTypes[1], cntHitTypes[2], cntHitTypes[3], cntHitTypes[4]);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"notepercents");
			swprintf(val, 32, L"%d,%d,%d,%d,%d", pctHitTypes[0], pctHitTypes[1], pctHitTypes[2], pctHitTypes[3], pctHitTypes[4]);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
			swprintf(val, 32, L"%d", percent);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
			swprintf(val, 32, L"%d", clearRank);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
			swprintf(val, 32, L"%d", score);
			WritePrivateProfileStringW(section, key, val, configPath);

			swprintf(key, 32, L"%ls.%ls", keyBase, L"slidescore");
			swprintf(val, 32, L"%d", slideScore);
			WritePrivateProfileStringW(section, key, val, configPath);

			int checkField = calculateCheck(score, cntHitTypes[0], cntHitTypes[1], percent, combo, clearRank, allTimeRank, allTimeModifiers, allTimePercent);
			swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
			swprintf(val, 32, L"%d", checkField);
			WritePrivateProfileStringW(section, key, val, configPath);
		}
		else // if (oldScoreValid)
		{
			if (allTimeRank != oldAlltimeRank) // first extension
			{
				swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
				swprintf(val, 32, L"%d", allTimeRank);
				WritePrivateProfileStringW(section, key, val, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
				int oldClearRank = GetPrivateProfileIntW(section, key, 0, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
				int oldCheck = GetPrivateProfileIntW(section, key, 0, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
				swprintf(val, 32, L"%d", oldCheck - (oldAlltimeRank * oldClearRank) + (allTimeRank * oldClearRank));
				WritePrivateProfileStringW(section, key, val, configPath);
			}
			if (allTimeModifiers != oldAlltimeModifiers || allTimePercent != oldAlltimePercent) // second extension
			{
				swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
				swprintf(val, 32, L"%d", allTimeModifiers);
				WritePrivateProfileStringW(section, key, val, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
				swprintf(val, 32, L"%d", allTimePercent);
				WritePrivateProfileStringW(section, key, val, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
				int oldCheck = GetPrivateProfileIntW(section, key, 0, configPath);

				swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
				swprintf(val, 32, L"%d", oldCheck ^ (oldAlltimeModifiers * oldAlltimePercent) ^ (allTimeModifiers * allTimePercent));
				WritePrivateProfileStringW(section, key, val, configPath);
			}
		}

		UpdateSingleScoreCacheEntry(pvNum, pvDifficulty, pvDifficultyIsEx);
		UpdateClearCounts();

		return result;
	}

	void ScoreSaver::Update()
	{
		return;
	}

	void ScoreSaver::UpdateInput()
	{
		return;
	}

	void ScoreSaver::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);
	}

	ScoreSaver::DivaScore ScoreSaver::ScoreCache[4][1000][2]; // 4 difficulties * 1000 pvs * extra or not
	void ScoreSaver::UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff)
	{
		if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
			return;

		WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
		WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers

		const WCHAR section[] = L"scores";

		if (exDiff == 0)
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, diff);
		else
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, diff);

		if (checkExistingScoreValid(pvNum, diff, exDiff))
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
			int score = GetPrivateProfileIntW(section, key, 0, configPath);

			if (score > 99999999)
				score = 99999999;

			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
			int percent = GetPrivateProfileIntW(section, key, -1, configPath);

			if (percent == -1) // fallback for old scores without alltimepercent
			{
				swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
				percent = GetPrivateProfileIntW(section, key, 0, configPath);
			}

			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
			int allTimeRank = GetPrivateProfileIntW(section, key, -1, configPath);

			if (allTimeRank == -1) // fallback for old scores without alltimerank
			{
				swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
				allTimeRank = GetPrivateProfileIntW(section, key, -1, configPath);
			}

			swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
			int modifiers = GetPrivateProfileIntW(section, key, -1, configPath);

			if (modifiers == -1) // fallback for old scores without alltimemodifiers
			{
				swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
				modifiers = GetPrivateProfileIntW(section, key, 0, configPath);
				modifiers = modifiers > 0 ? 1 << (modifiers - 1) : 0;
			}

			DivaScore* cachedScore = &ScoreCache[diff][pvNum][exDiff];
			cachedScore->score = score;
			cachedScore->percent = percent;
			cachedScore->clearRank = allTimeRank;
			if (modifiers & 1) cachedScore->optionA = 1;
			if (modifiers & 2) cachedScore->optionB = 1;
			if (modifiers & 4) cachedScore->optionC = 1;
		}
	}

	void ScoreSaver::UpdateSingleScoreCacheRivalEntry(int pvNum, int diff, int exDiff)
	{
		if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
			return;

		WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
		WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers

		const WCHAR section[] = L"scores";

		if (exDiff == 0)
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, diff);
		else
			swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, diff);

		swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
		int score = GetPrivateProfileIntW(section, key, 0, rival_configPath);

		if (score > 99999999)
			score = 99999999;

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
		int percent = GetPrivateProfileIntW(section, key, -1, rival_configPath);

		if (percent == -1) // fallback for old scores without alltimepercent
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
			percent = GetPrivateProfileIntW(section, key, 0, rival_configPath);
		}

		swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
		int allTimeRank = GetPrivateProfileIntW(section, key, -1, rival_configPath);

		if (allTimeRank == -1) // fallback for old scores without alltimerank
		{
			swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
			allTimeRank = GetPrivateProfileIntW(section, key, -1, rival_configPath);
		}

		DivaScore* cachedScore = &ScoreCache[diff][pvNum][exDiff];
		cachedScore->rival_clearRank = allTimeRank;
		cachedScore->rival_score = score;
		cachedScore->rival_percent = percent;
	}

	void ScoreSaver::UpdateScoreCache()
	{
		for (int pvNum = 0; pvNum < 1000; pvNum++)
		{
			for (int diff = 0; diff < 4; diff++)
			{
				for (int exDiff = 0; exDiff < 2; exDiff++)
				{
					ScoreCache[diff][pvNum][exDiff] = DivaScore(pvNum, exDiff);
					UpdateSingleScoreCacheEntry(pvNum, diff, exDiff);
					UpdateSingleScoreCacheRivalEntry(pvNum, diff, exDiff);
				}
			}
		}
	}

	void ScoreSaver::UpdateClearCounts()
	{
		int* counts = (int*)SONG_CLEAR_COUNTS_ADDRESS;
		for (int i = 0; i < 20; i++)
		{
			counts[i] = 0;
		}

		for (int diff = 0; diff < 4; diff++)
		{
			for (int pvNum = 0; pvNum < 1000; pvNum++)
			{
				DivaScore &scoreinfo = ScoreCache[diff][pvNum][0];
				if (scoreinfo.clearRank > 1 && scoreinfo.clearRank <= 5 && scoreinfo.exDifficulty == 0) // at least clear and no greater than perfect and not ex
				{
					counts[diff * 4 + scoreinfo.clearRank - 2] += 1;
				}
			}
		}

		// exex special case
		for (int pvNum = 0; pvNum < 1000; pvNum++)
		{
			DivaScore &scoreinfo = ScoreCache[3][pvNum][1];
			if (scoreinfo.clearRank > 1 && scoreinfo.clearRank <= 5 && scoreinfo.exDifficulty == 1) // at least clear and no greater than perfect and not ex
			{
				counts[4 * 4 + scoreinfo.clearRank - 2] += 1;
			}
		}

		// perfects count as clears for <= excellent, etc
		for (int diff = 0; diff < 5; diff++)
		{
			counts[diff * 4 + 2] += counts[diff * 4 + 3];
			counts[diff * 4 + 1] += counts[diff * 4 + 2];
			counts[diff * 4 + 0] += counts[diff * 4 + 1];
		}
	}
}


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

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

		virtual const char* GetDisplayName() override;

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

		struct DivaScore
		{
			int pvNum;
			int exDifficulty;
			byte padding08[0xa4]; // ??? -- seems to be 6*6 ints (0x90 bytes) for modules + 5 ints (0x14 bytes) for skin and button se
			int clearRank;        // +0xac: clear rank
			int score;            // +0xb0: score
			int percent;          // +0xb4: best percent
			byte paddingB8[0x14]; // ??? -- challenge_kind, chain_challenge_flag, chain_challenge_num, chain_challenge_max, challenge_ng_flag
			int rival_clearRank;  // +0xcc: rival clear rank?
			int rival_score;      // +0xd0: rival score
			int rival_percent;    // +0xd4: rival percent
			byte paddingD8[0x9];  // ??? -- interim_ranking int?
			byte optionA;         // +0xe1: gamemode options
			byte optionB;
			byte optionC;

			inline bool operator==(const DivaScore& cmp)
			{
				return cmp.pvNum == pvNum && cmp.exDifficulty == exDifficulty;
			}

			DivaScore(int pv, int exDiff)
			{
				// this is just copied from 140113510
				// no clue what most of it is
				pvNum = pv;
				exDifficulty = exDiff;
				*(int*)((uint64_t)this + 0x08) = 0;
				*(int*)((uint64_t)this + 0x0c) = 0;
				*(int*)((uint64_t)this + 0x10) = 0;
				*(int*)((uint64_t)this + 0x14) = 0;
				*(int*)((uint64_t)this + 0x18) = 0;
				*(int*)((uint64_t)this + 0x1c) = 0;
				*(long long*)((uint64_t)this + 0x20) = -1;
				*(long long*)((uint64_t)this + 0x28) = -1;
				*(long long*)((uint64_t)this + 0x30) = -1;
				*(long long*)((uint64_t)this + 0x38) = -1;
				*(long long*)((uint64_t)this + 0x40) = -1;
				*(long long*)((uint64_t)this + 0x48) = -1;
				*(long long*)((uint64_t)this + 0x50) = -1;
				*(long long*)((uint64_t)this + 0x58) = -1;
				*(long long*)((uint64_t)this + 0x60) = -1;
				*(long long*)((uint64_t)this + 0x68) = -1;
				*(long long*)((uint64_t)this + 0x70) = -1;
				*(long long*)((uint64_t)this + 0x78) = -1;
				*(int*)((uint64_t)this + 0x80) = 0x01010101;
				*(int*)((uint64_t)this + 0x84) = 0x01010101;
				*(int*)((uint64_t)this + 0x88) = 0x01010101;
				*(int*)((uint64_t)this + 0x8c) = 0x01010101;
				*(int*)((uint64_t)this + 0x90) = 0x01010101;
				*(int*)((uint64_t)this + 0x94) = 0x01010101;
				*(int*)((uint64_t)this + 0x98) = 0;
				*(long long*)((uint64_t)this + 0x9c) = -1;
				*(long long*)((uint64_t)this + 0xa4) = -1;
				*(int*)((uint64_t)this + 0xac) = -1;
				*(long long*)((uint64_t)this + 0xb0) = 0;
				*(int*)((uint64_t)this + 0xb8) = -1;
				*(byte*)((uint64_t)this + 0xbc) = 0;
				*(long long*)((uint64_t)this + 0xc0) = -1;
				*(byte*)((uint64_t)this + 0xc8) = 0;
				*(int*)((uint64_t)this + 0xcc) = -1;
				*(long long*)((uint64_t)this + 0xd0) = 0;
				*(int*)((uint64_t)this + 0xd8) = -1;
				*(byte*)((uint64_t)this + 0xdc) = 0;
				*(byte*)((uint64_t)this + 0xdd) = 0;
				*(byte*)((uint64_t)this + 0xde) = 0;
				*(byte*)((uint64_t)this + 0xdf) = 0;
				*(byte*)((uint64_t)this + 0xe0) = 0;
				*(byte*)((uint64_t)this + 0xe1) = 0;
				*(byte*)((uint64_t)this + 0xe2) = 0;
				*(byte*)((uint64_t)this + 0xe3) = 0;
			}
			DivaScore()
			{
				DivaScore(0, 0);
			}
		};

		static DivaScore ScoreCache[4][1000][2]; // 4 difficulties * 1000 pvs * extra or not

		static void UpdateScoreCache();
		static void UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff);
		static void UpdateSingleScoreCacheRivalEntry(int pvNum, int diff, int exDiff);
		static void UpdateClearCounts();

	private:
		static bool(__stdcall* divaInitResults)(void* cls);
		static bool hookedInitResults(void* cls);
		static void InjectCode(void* address, const std::vector<uint8_t> data);
		static bool checkExistingScoreValid(int pv, int difficulty, int isEx);
		static int calculateCheck(int score, int cntCools, int cntFines, int percent, int combo, int clearRank, int allTimeRank, int allTimeModifiers, int allTimePercent);

		static WCHAR configPath[256];
		static WCHAR rival_configPath[256];
	};
}

repo_name: twistero/PD

license: mit


code:
#include "StageManager.h"
#include "../Constants.h"
#include <windows.h>

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

	StageManager::~StageManager()
	{
	}

	const char* StageManager::GetDisplayName()
	{
		return "stage_manager";
	}

	void StageManager::Initialize(ComponentsManager*)
	{
		// add the offset between moving 2 into ebx and the start of the function
		int32_t* playCount = (int32_t*)(((uint8_t*)PLAYS_PER_SESSION_GETTER_ADDRESS) + 0x7);

		DWORD oldProtect;
		VirtualProtect(playCount, sizeof(int32_t), PAGE_EXECUTE_READWRITE, &oldProtect);

		// set �ʏ탂�[�h per session play count
		*playCount = GetPlayCount();
	}

	void StageManager::Update()
	{
		return;
	}

	int32_t StageManager::GetPlayCount()
	{
		return INT32_MAX;
	}
}


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

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

		virtual const char* GetDisplayName() override;

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

	private:
		int32_t GetPlayCount();
	};
}


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

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

	SysTimer::~SysTimer()
	{
	}

	const char* SysTimer::GetDisplayName()
	{
		return "sys_timer";
	}

	void SysTimer::Initialize(ComponentsManager*)
	{
		selPvTime = GetSysTimePtr((void*)SEL_PV_TIME_ADDRESS);
	}

	void SysTimer::Update()
	{
		// account for the decrement that occures during this frame
		*selPvTime = SEL_PV_FREEZE_TIME * SYS_TIME_FACTOR + 1;
	}

	int* SysTimer::GetSysTimePtr(void *address)
	{
		return (int*)address;
	}
}


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

namespace TLAC::Components
{
	class SysTimer : public EmulatorComponent
	{
		const int SYS_TIME_FACTOR = 60;
		const int SEL_PV_FREEZE_TIME = 39;

	public:
		SysTimer();
		~SysTimer();

		virtual const char* GetDisplayName() override;

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

	private:
		int* selPvTime;
		int* GetSysTimePtr(void *address);
	};
}


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

constexpr uint8_t NOP_OPCODE = 0x90;
constexpr uint8_t RET_OPCODE = 0xC3;
constexpr uint8_t JMP_OPCODE = 0xE9;
constexpr uint8_t JNE_OPCODE = 0x85;

//constexpr uint64_t ENGINE_UPDATE_HOOK_TARGET_ADDRESS	= 0x000000014018CC40;
constexpr uint64_t ENGINE_UPDATE_INPUT_ADDRESS			= 0x000000014018CBB0;

constexpr uint64_t CURRENT_GAME_STATE_ADDRESS			= 0x0000000140EDA810;
constexpr uint64_t CURRENT_GAME_SUB_STATE_ADDRESS		= 0x0000000140EDA82C;
constexpr uint64_t RESOLUTION_WIDTH_ADDRESS				= 0x0000000140EDA8BC;
constexpr uint64_t RESOLUTION_HEIGHT_ADDRESS			= 0x0000000140EDA8C0;

constexpr uint64_t SYSTEM_WARNING_ELAPSED_ADDRESS		= (0x00000001411A1430 + 0x68);
constexpr uint64_t DATA_INIT_STATE_ADDRESS				= 0x0000000140EDA7A8;

constexpr uint64_t AET_FRAME_DURATION_ADDRESS			= 0x00000001409A0A58;
constexpr uint64_t PV_FRAME_RATE_ADDRESS				= 0x0000000140EDA7CC;
constexpr uint64_t FRAME_SPEED_ADDRESS					= 0x0000000140EDA798;
constexpr uint64_t FRAME_RATE_ADDRESS					= 0x0000000140EDA6D0;
constexpr uint64_t USE_AUTO_FRAMESPEED_ADDRESS			= 0x0000000140EDA79C;
constexpr uint64_t AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = 0x0000000140EDA6CC;

constexpr uint64_t DW_GUI_DISPLAY_INSTANCE_PTR_ADDRESS	= 0x0000000141190108;
constexpr uint64_t INPUT_STATE_PTR_ADDRESS				= 0x0000000140EDA330;
constexpr uint64_t SLIDER_CTRL_TASK_ADDRESS				= 0x000000014CC5DE40;
constexpr uint64_t TASK_TOUCH_ADDRESS					= 0x000000014CC9EC30;
constexpr uint64_t SEL_PV_TIME_ADDRESS					= 0x000000014CC12498;
constexpr uint64_t PLAYER_DATA_ADDRESS					= 0x00000001411A8850;
constexpr uint64_t SET_DEFAULT_PLAYER_DATA_ADDRESS		= 0x00000001404A7370;
constexpr uint64_t PLAYS_PER_SESSION_GETTER_ADDRESS		= 0x000000014038AEE0;
constexpr uint64_t PV_SEL_SLOTS_TO_SCROLL				= 0x000000014CC12470;
constexpr uint64_t PV_SEL_SLOTS_CONST					= 0x000000014CC119C8;
constexpr uint64_t MODULE_SEL_SLOTS_TO_SCROLL			= 0x00000001418047EC;
constexpr uint64_t MODULE_IS_RECOMMENDED				= 0x00000001418047E0;

constexpr uint64_t CAMERA_ADDRESS						= 0x0000000140FBC2C0;
constexpr uint64_t CAMERA_POS_SETTER_ADDRESS			= 0x00000001401F9460;
constexpr uint64_t CAMERA_INTR_SETTER_ADDRESS			= 0x00000001401F93F0;
constexpr uint64_t CAMERA_ROT_SETTER_ADDRESS			= 0x00000001401F9480;
constexpr uint64_t CAMERA_PERS_SETTER_ADDRESS			= 0x00000001401F9430;

constexpr uint64_t UPDATE_TASKS_ADDRESS					= 0x000000014019B980;
constexpr uint64_t GLUT_SET_CURSOR_ADDRESS				= 0x00000001408B68E6;
constexpr uint64_t CHANGE_MODE_ADDRESS					= 0x00000001401953D0;
constexpr uint64_t CHANGE_SUB_MODE_ADDRESS				= 0x0000000140195260;

constexpr uint64_t TGT_CLASS_ADDRESS = 0x0000000140D0B660;
constexpr uint64_t TGT_STATES_FIRST_POINTER_ADDRESS = 0x0000000140D0B680;
constexpr uint64_t TGT_STATES_BASE_ADDRESS = 0x0000000140D0B688;
constexpr uint64_t TGT_ON_SCREEN_ADDRESS = 0x0000000140D0B678;

constexpr uint64_t HOLD_STATE_ADDRESS = 0x0000000140D1E20C;
constexpr uint64_t MAX_HOLD_STATE_ADDRESS = 0x0000000140D1E234;

constexpr uint64_t CHECK_SOMETHING_SET_MODULE_ADDRESS = 0x0000000140581C78;
constexpr uint64_t MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS = 0x00000001405869AD;
constexpr uint64_t MODSELECTOR_CLOSE_AFTER_MODULE = 0x0000000140583B45;
constexpr uint64_t MODSELECTOR_CLOSE_AFTER_CUSTOMIZE = 0x0000000140583C8C;
constexpr uint64_t MODULE_TABLE_START = PLAYER_DATA_ADDRESS + 0x140;
constexpr uint64_t MODULE_TABLE_END = MODULE_TABLE_START + 128;
constexpr uint64_t ITEM_TABLE_START = PLAYER_DATA_ADDRESS + 0x2B8;
constexpr uint64_t ITEM_TABLE_END = ITEM_TABLE_START + 128;

constexpr uint64_t FB_WIDTH_ADDRESS = 0x00000001411ABCA8;
constexpr uint64_t FB_HEIGHT_ADDRESS = 0x00000001411ABCAC;
constexpr uint64_t FB1_WIDTH_ADDRESS = 0x00000001411AD5F8;
constexpr uint64_t FB1_HEIGHT_ADDRESS = 0x00000001411AD5FC;
constexpr uint64_t FB2_WIDTH_ADDRESS = 0x0000000140EDA8E4;
constexpr uint64_t FB2_HEIGHT_ADDRESS = 0x0000000140EDA8E8;

constexpr uint64_t FB_RESOLUTION_WIDTH_ADDRESS = 0x00000001411ABB50;
constexpr uint64_t FB_RESOLUTION_HEIGHT_ADDRESS = 0x00000001411ABB54;

constexpr uint64_t UI_WIDTH_ADDRESS = 0x000000014CC621E4;
constexpr uint64_t UI_HEIGHT_ADDRESS = 0x000000014CC621E8;

constexpr uint64_t FB_ASPECT_RATIO = 0x0000000140FBC2E8;
constexpr uint64_t UI_ASPECT_RATIO = 0x000000014CC621D0;

constexpr uint64_t GET_SLIDER_TAPPED_ADDRESS = 0x0000000140618C60;
constexpr uint64_t GET_SLIDER_RELEASED_ADDRESS = 0x0000000140618C40;
constexpr uint64_t GET_SLIDER_DOWN_ADDRESS = 0x0000000140618C20;

constexpr uint64_t SHOULD_EXIT_BOOL_ADDRESS = 0x0000000140EDA6B0;
constexpr uint64_t DOEXIT_ADDRESS = 0x000000014085B574;

constexpr uint64_t RESULTS_INIT_ADDRESS = 0x000000014065BBC0;
constexpr uint64_t RESULTS_BASE_ADDRESS = 0x000000014CC93830;
constexpr uint64_t GAME_INFO_ADDRESS = 0x0000000141197E00;

constexpr uint64_t CURRENT_SONG_NAME_ADDRESS = 0x0000000140D0A578;
constexpr uint64_t CURRENT_SONG_ID_ADDRESS = 0x0000000140CDD8E0;
constexpr uint64_t SELPV_CURRENT_SONG_ADDRESS = 0x000000014CC12438;
constexpr uint64_t SONG_CLEAR_COUNTS_ADDRESS = 0x00000001411A95E8;

#define XINPUT_A 0x00
#define XINPUT_B 0x01
#define XINPUT_X 0x02
#define XINPUT_Y 0x03
#define XINPUT_UP 0x10
#define XINPUT_DOWN 0x11
#define XINPUT_LEFT 0x12
#define XINPUT_RIGHT 0x13
#define XINPUT_LS 0x20
#define XINPUT_RS 0x21
#define XINPUT_LT 0x22
#define XINPUT_RT 0x23
#define XINPUT_LSB 0x24
#define XINPUT_RSB 0x25
#define XINPUT_START 0x30
#define XINPUT_BACK 0x31
#define XINPUT_LUP 0x40
#define XINPUT_LDOWN 0x41
#define XINPUT_LLEFT 0x42
#define XINPUT_LRIGHT 0x43
#define XINPUT_RUP 0x50
#define XINPUT_RDOWN 0x51
#define XINPUT_RLEFT 0x52
#define XINPUT_RRIGHT 0x53


code:
#include <stdio.h>
#include "ConfigFile.h"
#include "../Utilities/Operations.h"

namespace TLAC::FileSystem
{
	ConfigFile::ConfigFile(const std::string &path) : TextFile(path)
	{
		return;
	}

	ConfigFile::ConfigFile(const std::string &directory, const std::string &file) : TextFile(directory, file)
	{
		return;
	}

	bool ConfigFile::TryGetValue(const std::string &key, std::string **value)
	{
		auto pair = ConfigMap.find(key);
		bool found = pair != ConfigMap.end();

		*value = found ? new std::string(pair->second) : nullptr;
		return found;
	}

	int ConfigFile::GetIntegerValue(const std::string& key)
	{
		auto pair = ConfigMap.find(key);
		bool found = pair != ConfigMap.end();

		return found ? atoi(pair->second.c_str()) : 0;
	}

	bool ConfigFile::GetBooleanValue(const std::string& key)
	{
		auto pair = ConfigMap.find(key);
		bool found = pair != ConfigMap.end();

		return found ? pair->second == "true" : false;
	}

	float ConfigFile::GetFloatValue(const std::string & key)
	{
		auto pair = ConfigMap.find(key);
		bool found = pair != ConfigMap.end();

		return found ? (float)atof(pair->second.c_str()) : 0.0f;
	}

	void ConfigFile::Parse(std::ifstream &fileStream)
	{
		std::string line;

		// check for BOM
		std::getline(fileStream, line);
		if (line.size() >= 3 && line.rfind("\xEF\xBB\xBF", 0) == 0)
			fileStream.seekg(3);
		else
			fileStream.seekg(0);

		while (std::getline(fileStream, line))
		{
			if (IsComment(line))
				continue;

			auto splitline = Utilities::Split(line, "=");

			for (auto &line : splitline)
				Utilities::Trim(line);

			ConfigMap.insert(std::make_pair(splitline[0], splitline[1]));
		}
	}

	bool ConfigFile::IsComment(const std::string &line)
	{
		return line.size() <= 0 || line[0] == '#' || line[0] == '[' || (line.size() >= 2 && line.rfind("//", 0) == 0);
	}
}


code:
#pragma once

#include "TextFile.h"
#include <unordered_map>

namespace TLAC::FileSystem
{
	class ConfigFile : public TextFile
	{
	public:
		ConfigFile(const std::string &path);
		ConfigFile(const std::string &directory, const std::string &file);

		std::unordered_map<std::string, std::string> ConfigMap;

		bool TryGetValue(const std::string &key, std::string **value);
		int GetIntegerValue(const std::string& key);
		bool GetBooleanValue(const std::string& key);
		float GetFloatValue(const std::string& key);

	protected:
		virtual void Parse(std::ifstream &fileStream) override;

	private:
		bool IsComment(const std::string &line);
	};
}


code:
#include "TextFile.h"
#include <filesystem>

namespace fs = std::filesystem;

namespace TLAC::FileSystem
{
	TextFile::TextFile(const std::string &path)
	{
		FileName = path;
	}

	TextFile::TextFile(const std::string &directory, const std::string &file)
	{
		auto fullPath = directory + "/" + file;
		FileName = fullPath;
	}

	TextFile::~TextFile()
	{
	}

	bool TextFile::OpenRead()
	{
		fs::path configPath = fs::u8path(FileName);

		if (!fs::exists(configPath))
			return false;

		std::ifstream fileStream(configPath.wstring().c_str());

		if (!fileStream.is_open())
			return false;

		Parse(fileStream);

		fileStream.close();

		return true;
	}
}


code:
#pragma once

#include <string>
#include <fstream>

namespace TLAC::FileSystem
{
	class TextFile
	{
	public:
		std::string FileName;

		TextFile(const std::string &path);
		TextFile(const std::string &directory, const std::string &file);
		~TextFile();

		bool OpenRead();

	protected:
		virtual void Parse(std::ifstream &fileStream) = 0;
	};
}


code:
#include "Binding.h"

namespace TLAC::Input
{
	Binding::Binding()
	{
	}

	Binding::~Binding()
	{
		for (auto& binding : InputBindings)
			delete binding;
	}

	void Binding::AddBinding(IInputBinding* inputBinding)
	{
		InputBindings.push_back(inputBinding);
	}

	bool Binding::AnyDown()
	{
		for (const auto& binding : InputBindings)
		{
			if (binding->IsDown())
				return true;
		}

		return false;
	}

	bool Binding::AnyTapped()
	{
		for (const auto& binding : InputBindings)
		{
			if (binding->IsTapped())
				return true;
		}

		return false;
	}

	bool Binding::AnyReleased()
	{
		for (const auto& binding : InputBindings)
		{
			if (binding->IsReleased())
				return true;
		}

		return false;
	}

	int Binding::GetDownCount()
	{
		int count = 0;

		for (const auto& binding : InputBindings)
		{
			if (binding->IsDown())
				count++;
		}

		return count;
	}

	int Binding::GetTappedCount()
	{
		int count = 0;

		for (const auto& binding : InputBindings)
		{
			if (binding->IsTapped())
				count++;
		}

		return count;
	}

	int Binding::GetReleasedCount()
	{
		int count = 0;

		for (const auto& binding : InputBindings)
		{
			if (binding->IsReleased())
				count++;
		}

		return count;
	}
}


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

namespace TLAC::Input
{
	class Binding
	{
	public:
		std::vector<IInputBinding*> InputBindings;

		Binding();
		~Binding();

		void AddBinding(IInputBinding* inputBinding);

		bool AnyDown();
		bool AnyTapped();
		bool AnyReleased();

		int GetDownCount();
		int GetTappedCount();
		int GetReleasedCount();
	};
}


code:
#include "Ds4Binding.h"

namespace TLAC::Input
{
	#define Ds4InstanceCheckDefault(checkFunc) (DualShock4::InstanceInitialized() ? DualShock4::GetInstance()->checkFunc : false)

	Ds4Binding::Ds4Binding(Ds4Button button) : Button(button)
	{
	}

	Ds4Binding::~Ds4Binding()
	{
	}

	bool Ds4Binding::IsDown()
	{
		return Ds4InstanceCheckDefault(IsDown(Button));
	}

	bool Ds4Binding::IsTapped()
	{
		return Ds4InstanceCheckDefault(IsTapped(Button));
	}

	bool Ds4Binding::IsReleased()
	{
		return Ds4InstanceCheckDefault(IsReleased(Button));
	}
}


code:
#pragma once
#include "IInputBinding.h"
#include "../DirectInput/Ds4/DualShock4.h"

namespace TLAC::Input
{
	class Ds4Binding : public IInputBinding
	{
	public:
		Ds4Button Button;

		Ds4Binding(Ds4Button button);
		~Ds4Binding();

		bool IsDown() override;
		bool IsTapped() override;
		bool IsReleased() override;
	};
}


code:
#pragma once

namespace TLAC::Input
{
	class IInputBinding
	{
	public:
		virtual bool IsDown() = 0;
		virtual bool IsTapped() = 0;
		virtual bool IsReleased() = 0;
	};
}


code:
#include "KeyboardBinding.h"

namespace TLAC::Input
{
	KeyboardBinding::KeyboardBinding(BYTE keycode) : Keycode(keycode)
	{
	}

	KeyboardBinding::~KeyboardBinding()
	{
	}

	bool KeyboardBinding::IsDown()
	{
		return Keyboard::GetInstance()->IsDown(Keycode);
	}

	bool KeyboardBinding::IsTapped()
	{
		return Keyboard::GetInstance()->IsTapped(Keycode);
	}

	bool KeyboardBinding::IsReleased()
	{
		return Keyboard::GetInstance()->IsReleased(Keycode);
	}
}


code:
#pragma once
#include "IInputBinding.h"
#include "../Keyboard/Keyboard.h"

namespace TLAC::Input
{
	class KeyboardBinding : public IInputBinding
	{
	public:
		BYTE Keycode;

		KeyboardBinding(BYTE keycode);
		~KeyboardBinding();

		bool IsDown() override;
		bool IsTapped() override;
		bool IsReleased() override;
	};
}


code:
#include "MouseBinding.h"
#include "../Keyboard/Keyboard.h"

namespace TLAC::Input
{
	MouseBinding::MouseBinding(MouseAction action) : Action(action)
	{
	}

	MouseBinding::~MouseBinding()
	{
	}

	bool MouseBinding::IsDown()
	{
		switch (Action)
		{
		case MouseAction_LeftButton:
			return Keyboard::GetInstance()->IsDown(MK_LBUTTON);
		case MouseAction_RightButton:
			return Keyboard::GetInstance()->IsDown(MK_RBUTTON);
		case MouseAction_MiddleButton:
			return Keyboard::GetInstance()->IsDown(MK_MBUTTON);
		case MouseAction_ScrollUp:
			return Mouse::GetInstance()->GetIsScrolledUp();
		case MouseAction_ScrollDown:
			return Mouse::GetInstance()->GetIsScrolledDown();
		default:
			return false;
		}
	}

	bool MouseBinding::IsTapped()
	{
		switch (Action)
		{
		case MouseAction_LeftButton:
			return Keyboard::GetInstance()->IsTapped(MK_LBUTTON);
		case MouseAction_RightButton:
			return Keyboard::GetInstance()->IsTapped(MK_RBUTTON);
		case MouseAction_MiddleButton:
			return Keyboard::GetInstance()->IsTapped(MK_MBUTTON);
		case MouseAction_ScrollUp:
			return Mouse::GetInstance()->GetIsScrolledUp();
		case MouseAction_ScrollDown:
			return Mouse::GetInstance()->GetIsScrolledDown();
		default:
			return false;
		}
	}

	bool MouseBinding::IsReleased()
	{
		switch (Action)
		{
		case MouseAction_LeftButton:
			return Keyboard::GetInstance()->IsReleased(MK_LBUTTON);
		case MouseAction_RightButton:
			return Keyboard::GetInstance()->IsReleased(MK_RBUTTON);
		case MouseAction_MiddleButton:
			return Keyboard::GetInstance()->IsReleased(MK_MBUTTON);
		case MouseAction_ScrollUp:
			return Mouse::GetInstance()->GetWasScrolledUp();
		case MouseAction_ScrollDown:
			return Mouse::GetInstance()->GetWasScrolledDown();
		default:
			return false;
		}
	}
}


code:
#pragma once
#include "IInputBinding.h"
#include "../Mouse/Mouse.h"

namespace TLAC::Input
{
	enum MouseAction
	{
		MouseAction_LeftButton,
		MouseAction_RightButton,
		MouseAction_MiddleButton,
		MouseAction_ScrollUp,
		MouseAction_ScrollDown,
	};

	class MouseBinding : public IInputBinding
	{
	public:
		MouseAction Action;

		MouseBinding(MouseAction action);
		~MouseBinding();

		bool IsDown() override;
		bool IsTapped() override;
		bool IsReleased() override;
	};
}


code:
#include "XinputBinding.h"
#include "..\Xinput\Xinput.h"

namespace TLAC::Input
{
	XinputBinding::XinputBinding(BYTE keycode) : Keycode(keycode)
	{
	}

	XinputBinding::~XinputBinding()
	{
	}

	bool XinputBinding::IsDown()
	{
		return Xinput::GetInstance()->IsDown(Keycode);
	}

	bool XinputBinding::IsTapped()
	{
		return Xinput::GetInstance()->IsTapped(Keycode);
	}

	bool XinputBinding::IsReleased()
	{
		return Xinput::GetInstance()->IsReleased(Keycode);
	}

	bool XinputBinding::IsDoubleTapped()
	{
		return Xinput::GetInstance()->IsDoubleTapped(Keycode);
	}
}


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

namespace TLAC::Input
{
	class XinputBinding : public IInputBinding
	{
	public:
		BYTE Keycode;

		XinputBinding(BYTE keycode);
		~XinputBinding();

		bool IsDown() override;
		bool IsTapped() override;
		bool IsReleased() override;
		bool IsDoubleTapped();
	};
}


code:
#include "Controller.h"

namespace TLAC::Input
{
	Controller::Controller()
	{
	}

	Controller::~Controller()
	{

	}
}


code:
#pragma once
#include "DirectInputDevice.h"

namespace TLAC::Input
{
	class Controller : public DirectInputDevice
	{
	protected:
		Controller();
		~Controller();
	};
}


code:
#include "DirectInput.h"

namespace TLAC::Input
{
	IDirectInput8 *IDirectInputInstance = nullptr;

	HRESULT InitializeDirectInput(HMODULE module)
	{
		HRESULT result = DirectInput8Create(module, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&IDirectInputInstance, nullptr);
		return result;
	}

	bool DirectInputInitialized()
	{
		return IDirectInputInstance != nullptr;
	}

	void DisposeDirectInput()
	{
		if (IDirectInputInstance == nullptr)
			return;

		IDirectInputInstance->Release();
		IDirectInputInstance = nullptr;
	}
}


code:
#pragma once
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

namespace TLAC::Input
{
	extern IDirectInput8 *IDirectInputInstance;

	HRESULT InitializeDirectInput(HMODULE module);
	bool DirectInputInitialized();
	void DisposeDirectInput();
}


code:
#include "DirectInputDevice.h"

namespace TLAC::Input
{
	HRESULT DirectInputDevice::DI_CreateDevice(const GUID &guid)
	{
		if (!DirectInputInitialized())
			return DIERR_NOTINITIALIZED;

		HRESULT result = IDirectInputInstance->CreateDevice(guid, &directInputdevice, NULL);
		return result;
	}

	HRESULT DirectInputDevice::DI_SetDataFormat(LPCDIDATAFORMAT dataFormat)
	{
		HRESULT result = directInputdevice->SetDataFormat(dataFormat);
		return result;
	}

	HRESULT DirectInputDevice::DI_SetCooperativeLevel(HWND windowHandle, DWORD flags)
	{
		HRESULT result = directInputdevice->SetCooperativeLevel(windowHandle, flags);
		return result;
	}

	HRESULT DirectInputDevice::DI_Acquire()
	{
		HRESULT result = directInputdevice->Acquire();
		return result;
	}

	HRESULT DirectInputDevice::DI_Unacquire()
	{
		HRESULT result = directInputdevice->Unacquire();
		return result;
	}

	HRESULT DirectInputDevice::DI_Release()
	{
		HRESULT result = directInputdevice->Release();
		return result;
	}

	HRESULT DirectInputDevice::DI_Poll()
	{
		HRESULT result = directInputdevice->Poll();
		return result;
	}

	HRESULT DirectInputDevice::DI_GetDeviceState(DWORD size, LPVOID data)
	{
		HRESULT result = directInputdevice->GetDeviceState(size, data);
		return result;
	}

	void DirectInputDevice::DI_Dispose()
	{
		if (directInputdevice == nullptr)
			return;

		HRESULT result = NULL;

		result = DI_Unacquire();
		result = DI_Release();
	}
}


code:
#pragma once
#include "DirectInput.h"

namespace TLAC::Input
{
	class DirectInputDevice
	{
	protected:
		IDirectInputDevice8 *directInputdevice;

		HRESULT DI_CreateDevice(const GUID& guid);
		HRESULT DI_SetDataFormat(LPCDIDATAFORMAT dataFormat);
		HRESULT DI_SetCooperativeLevel(HWND windowHandle, DWORD flags);
		HRESULT DI_Acquire();
		HRESULT DI_Unacquire();
		HRESULT DI_Release();
		HRESULT DI_Poll();
		HRESULT DI_GetDeviceState(DWORD size, LPVOID data);

		void DI_Dispose();
	};
}


code:
#include "DirectInputMouse.h"

namespace TLAC::Input
{
	DirectInputMouse::DirectInputMouse()
	{
		HRESULT result = NULL;

		result = DI_CreateDevice(GUID_SysMouse);

		if (FAILED(result))
			return;

		result = DI_SetDataFormat(&c_dfDIMouse);
		result = DI_Acquire();
	}

	DirectInputMouse::~DirectInputMouse()
	{
		DI_Dispose();
	}

	bool DirectInputMouse::Poll()
	{
		if (!DirectInputInitialized())
			return FALSE;

		HRESULT result = NULL;

		result = DI_Poll();
		result = DI_GetDeviceState(sizeof(mouseState), &mouseState);

		return !FAILED(result);
	}
}


code:
#pragma once
#include "DirectInputDevice.h"

namespace TLAC::Input
{
	class DirectInputMouse : public DirectInputDevice
	{
	public:
		DirectInputMouse();
		~DirectInputMouse();

		bool Poll();

		inline long GetXPosition() { return mouseState.lX; };
		inline long GetYPosition() { return mouseState.lY; };
		inline long GetMouseWheel() { return mouseState.lZ; };

	private:
		DIMOUSESTATE mouseState;
	};
}


code:
#pragma once

namespace TLAC::Input
{
	enum Direction
	{
		DIR_UP,
		DIR_RIGHT,
		DIR_DOWN,
		DIR_LEFT
	};

	enum Ds4Button : int
	{
		DS4_SQUARE = 0,
		DS4_CROSS = 1,
		DS4_CIRCLE = 2,
		DS4_TRIANGLE = 3,

		DS4_L1 = 4,
		DS4_R1 = 5,

		DS4_L_TRIGGER = 6,
		DS4_R_TRIGGER = 7,

		DS4_SHARE = 8,
		DS4_OPTIONS = 9,

		DS4_L3 = 10,
		DS4_R3 = 11,

		DS4_PS = 12,
		DS4_TOUCH = 13,

		DS4_DPAD_UP,
		DS4_DPAD_RIGHT,
		DS4_DPAD_DOWN,
		DS4_DPAD_LEFT,

		DS4_L_STICK_UP,
		DS4_L_STICK_RIGHT,
		DS4_L_STICK_DOWN,
		DS4_L_STICK_LEFT,

		DS4_R_STICK_UP,
		DS4_R_STICK_RIGHT,
		DS4_R_STICK_DOWN,
		DS4_R_STICK_LEFT,

		DS4_BUTTON_MAX,
	};
}


code:
#include "Ds4State.h"

namespace TLAC::Input
{
	Joystick::Joystick() : XAxis(0.0f), YAxis(0.0f)
	{
		return;
	};

	Joystick::Joystick(float xAxis, float yAxis) : XAxis(xAxis), YAxis(yAxis)
	{
		return;
	};
}


code:
#pragma once
#include "../DirectInput.h"
#include "Ds4Button.h"

namespace TLAC::Input
{
	struct Joystick
	{
		FLOAT XAxis, YAxis;

		Joystick();
		Joystick(float xAxis, float yAxis);
	};

	struct Dpad
	{
		BOOL IsDown;
		FLOAT Angle;
		Joystick Stick;
	};

	struct Trigger
	{
		FLOAT Axis;
	};

	struct Ds4State
	{
		DIJOYSTATE2 DI_JoyState;

		BYTE Buttons[DS4_BUTTON_MAX];

		Dpad Dpad;
		Joystick LeftStick;
		Joystick RightStick;
		Trigger LeftTrigger;
		Trigger RightTrigger;
	};
}


code:
#include "DualShock4.h"
#include "../../../Utilities/Math.h"
#include "../../../framework.h"
#include <stdio.h>

namespace TLAC::Input
{
	DualShock4* DualShock4::instance;

	DualShock4::DualShock4()
	{
	}

	DualShock4::~DualShock4()
	{
		DI_Dispose();
	}

	bool DualShock4::TryInitializeInstance()
	{
		if (InstanceInitialized())
			return true;

		if (!DirectInputInitialized())
			return false;

		DualShock4 *dualShock4 = new DualShock4();

		bool success = dualShock4->Initialize();
		instance = success ? dualShock4 : nullptr;

		if (!success)
			delete dualShock4;

		return success;
	}

	bool DualShock4::Initialize()
	{
		HRESULT result = NULL;

		const size_t guidCount = sizeof(GUID_Ds4) / sizeof(GUID);
		for (size_t i = 0; i < guidCount; i++)
		{
			result = DI_CreateDevice(GUID_Ds4[i]);

			if (!FAILED(result))
				break;
			else if (i == guidCount - 1)
				return false;
		}

		if (FAILED(result = DI_SetDataFormat(&c_dfDIJoystick2)))
			return false;

		result = DI_Acquire();

		return true;
	}

	bool DualShock4::PollInput()
	{
		lastState = currentState;

		HRESULT result = NULL;
		result = DI_Poll();
		result = DI_GetDeviceState(sizeof(DIJOYSTATE2), &currentState.DI_JoyState);

		if (result != DI_OK)
			return false;

		UpdateInternalDs4State(currentState);

		for (int button = 0; button < DS4_BUTTON_MAX; button++)
			currentState.Buttons[button] = GetButtonState(currentState, (Ds4Button)button);

		return true;
	}

	void DualShock4::UpdateInternalDs4State(Ds4State &state)
	{
		if (state.Dpad.IsDown = state.DI_JoyState.rgdwPOV[0] != -1)
		{
			state.Dpad.Angle = (state.DI_JoyState.rgdwPOV[0] / 100.0f);

			auto direction = Utilities::GetDirection(state.Dpad.Angle);
			state.Dpad.Stick = { direction.Y, -direction.X };
		}
		else
		{
			state.Dpad.Angle = 0;
			state.Dpad.Stick = Joystick();
		}

		state.LeftStick = NormalizeStick(state.DI_JoyState.lX, state.DI_JoyState.lY);
		state.RightStick = NormalizeStick(state.DI_JoyState.lZ, state.DI_JoyState.lRz);

		state.LeftTrigger = { NormalizeTrigger(state.DI_JoyState.lRx) };
		state.RightTrigger = { NormalizeTrigger(state.DI_JoyState.lRy) };
	}

	bool DualShock4::IsDown(Ds4Button button)
	{
		return currentState.Buttons[button];
	}

	bool DualShock4::IsUp(Ds4Button button)
	{
		return !IsDown(button);
	}

	bool DualShock4::IsTapped(Ds4Button button)
	{
		return IsDown(button) && WasUp(button);
	}

	bool DualShock4::IsReleased(Ds4Button button)
	{
		return IsUp(button) && WasDown(button);
	}

	bool DualShock4::WasDown(Ds4Button button)
	{
		return lastState.Buttons[button];
	}

	bool DualShock4::WasUp(Ds4Button button)
	{
		return !WasDown(button);
	}

	bool DualShock4::MatchesDirection(Joystick joystick, Direction directionEnum, float threshold)
	{
		switch (directionEnum)
		{
		case TLAC::Input::DIR_UP:
			return joystick.YAxis <= -threshold;

		case TLAC::Input::DIR_RIGHT:
			return joystick.XAxis >= +threshold;

		case TLAC::Input::DIR_DOWN:
			return joystick.YAxis >= +threshold;

		case TLAC::Input::DIR_LEFT:
			return joystick.XAxis <= -threshold;

		default:
			return false;
		}
	}

	bool DualShock4::GetButtonState(Ds4State &state, Ds4Button button)
	{
		if (button >= DS4_SQUARE && button <= DS4_TOUCH)
			return state.DI_JoyState.rgbButtons[button];

		if (button >= DS4_DPAD_UP && button <= DS4_DPAD_LEFT)
			return state.Dpad.IsDown ? MatchesDirection(state.Dpad.Stick, (Direction)(button - DS4_DPAD_UP), dpadThreshold) : false;

		if (button >= DS4_L_STICK_UP && button <= DS4_L_STICK_LEFT)
			return MatchesDirection(state.LeftStick, (Direction)(button - DS4_L_STICK_UP), joystickThreshold);

		if (button >= DS4_R_STICK_UP && button <= DS4_R_STICK_LEFT)
			return MatchesDirection(state.RightStick, (Direction)(button - DS4_R_STICK_UP), joystickThreshold);

		return false;
	}
}


code:
#pragma once
#include "../Controller.h"
#include "../../IInputDevice.h"
#include "Ds4State.h"

namespace TLAC::Input
{
	// Controller GUIDs:
	const GUID GUID_Ds4[] =
	{
		// First Generation:  {05C4054C-0000-0000-0000-504944564944}
		{ 0x05C4054C, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
		// Second Generation: {09CC054C-0000-0000-0000-504944564944}
		{ 0x09CC054C, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
		// GAMO2 DIVALLER: {11140E8F-0000-0000-0000-504944564944}
		{ 0x11140E8F, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
		// Hori HATSUNE MIKU X: {00A50F0D-0000-0000-0000-504944564944}
		{ 0x00A50F0D, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
		// Hori F2nd Miniboard (Hatsune Miku Sho): {00490F0D-0000-0000-0000-504944564944}
		{ 0x00490F0D, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }
	};

	class DualShock4 : public Controller, public IInputDevice
	{
	public:
		DualShock4();
		~DualShock4();

		static bool TryInitializeInstance();

		bool Initialize();
		bool PollInput() override;

		bool IsDown(Ds4Button button);
		bool IsUp(Ds4Button button);
		bool IsTapped(Ds4Button button);
		bool IsReleased(Ds4Button button);
		bool WasDown(Ds4Button button);
		bool WasUp(Ds4Button button);

		inline Joystick GetLeftStick() { return currentState.LeftStick; };
		inline Joystick GetRightStick() { return currentState.RightStick; };
		inline Joystick GetDpad() { return currentState.Dpad.Stick; };

		static inline bool InstanceInitialized() { return instance != nullptr; };
		static inline DualShock4* GetInstance() { return instance; };
		static inline void DeleteInstance() { delete instance; instance = nullptr; };

	private:
		static DualShock4* instance;

		Ds4State lastState;
		Ds4State currentState;

		const float triggerThreshold = 0.5f;
		const float joystickThreshold = 0.5f;
		const float dpadThreshold = 0.5f;

		inline float NormalizeTrigger(long value) { return (float)value / USHRT_MAX; };
		inline float NormalizeStick(long value) { return (float)value / USHRT_MAX * 2.0f - 1.0f; };
		inline Joystick NormalizeStick(long x, long y) { return Joystick(NormalizeStick(x), NormalizeStick(y)); };

		void UpdateInternalDs4State(Ds4State &state);

		bool MatchesDirection(Joystick joystick, Direction directionEnum, float threshold);
		bool GetButtonState(Ds4State &state, Ds4Button button);
	};
}


code:
#pragma once

namespace TLAC::Input
{
	class IInputDevice
	{
	public:
		virtual bool PollInput() = 0;
	};
}


code:
#include "Config.h"
#include "windows.h"
#include "../Bindings/KeyboardBinding.h"
#include "../Bindings/Ds4Binding.h"
#include "../../Utilities/Operations.h"
#include "../Bindings/XinputBinding.h"
#include "../../Constants.h"

namespace TLAC::Input::KeyConfig
{
	KeycodeMap Config::Keymap =
	{
		// NumPad Keys
		{ "NumPad0", VK_NUMPAD0 },
		{ "NumPad1", VK_NUMPAD1 },
		{ "NumPad2", VK_NUMPAD2 },
		{ "NumPad3", VK_NUMPAD3 },
		{ "NumPad4", VK_NUMPAD4 },
		{ "NumPad5", VK_NUMPAD5 },
		{ "NumPad6", VK_NUMPAD6 },
		{ "NumPad7", VK_NUMPAD7 },
		{ "NumPad8", VK_NUMPAD8 },
		{ "NumPad9", VK_NUMPAD9 },
		{ "Plus", VK_ADD },
		{ "Minus", VK_SUBTRACT },
		{ "Divide", VK_DIVIDE },
		{ "Multiply", VK_MULTIPLY },
		{ "Comma", VK_OEM_COMMA },
		{ "Period", VK_OEM_PERIOD },
		{ "Slash", VK_OEM_2 },
		// F-Keys
		{ "F1", VK_F1 },
		{ "F2", VK_F2 },
		{ "F3", VK_F3 },
		{ "F4", VK_F4 },
		{ "F5", VK_F5 },
		{ "F6", VK_F6 },
		{ "F7", VK_F7 },
		{ "F8", VK_F8 },
		{ "F9", VK_F9 },
		{ "F10", VK_F10 },
		{ "F11", VK_F11 },
		{ "F12", VK_F12 },
		{ "F13", VK_F13 },
		{ "F14", VK_F14 },
		{ "F15", VK_F15 },
		{ "F16", VK_F16 },
		{ "F17", VK_F17 },
		{ "F18", VK_F18 },
		{ "F19", VK_F19 },
		{ "F20", VK_F20 },
		{ "F21", VK_F21 },
		{ "F22", VK_F22 },
		{ "F23", VK_F23 },
		{ "F24", VK_F24 },
		// Shift Keys
		{ "LeftShift", VK_LSHIFT },
		{ "LShift",    VK_LSHIFT },
		{ "RightShift", VK_RSHIFT },
		{ "RShift",     VK_RSHIFT },
		// Control Keys
		{ "LeftControl",  VK_LCONTROL },
		{ "LControl",     VK_LCONTROL },
		{ "LCtrl",        VK_LCONTROL },
		{ "RightControl", VK_RCONTROL },
		{ "RControl",     VK_RCONTROL },
		{ "RCtrl",        VK_RCONTROL },
		// Arrow Keys
		{ "Up",    VK_UP },
		{ "Down",  VK_DOWN },
		{ "Left",  VK_LEFT },
		{ "Right", VK_RIGHT },
		// Special Keys
		{ "Enter",  VK_RETURN },
		{ "Return", VK_RETURN },
		{ "Tab",	VK_TAB },
		{ "Back",      VK_BACK },
		{ "Backspace", VK_BACK },
		{ "Insert", VK_INSERT },
		{ "Ins",    VK_INSERT },
		{ "Delete", VK_DELETE },
		{ "Del",    VK_DELETE },
		{ "Home", VK_HOME },
		{ "End",  VK_END },
		{ "PageUp",   VK_PRIOR },
		{ "PageDown", VK_NEXT },
		{ "ESC",    VK_ESCAPE },
		{ "Escape", VK_ESCAPE },
		{ "Comma",	VK_OEM_COMMA },
		{ "Period", VK_OEM_PERIOD },
		{ "Slash",	VK_OEM_2 },
		// Mouse buttons
		{ "MouseLeft",	VK_LBUTTON },
		{ "MouseMiddle",	VK_MBUTTON },
		{ "MouseRight",	VK_RBUTTON },
		{ "MouseX1",	VK_XBUTTON1 },
		{ "MouseX2",	VK_XBUTTON2 },
	};

	KeycodeMap Config::XinputMap =
	{
		//XINPUT
		{ "XINPUT_A", XINPUT_A},
		{ "XINPUT_B", XINPUT_B},
		{ "XINPUT_X", XINPUT_X},
		{ "XINPUT_Y", XINPUT_Y},
		{ "XINPUT_UP", XINPUT_UP},
		{ "XINPUT_DOWN", XINPUT_DOWN},
		{ "XINPUT_LEFT", XINPUT_LEFT},
		{ "XINPUT_RIGHT", XINPUT_RIGHT},
		{ "XINPUT_START", XINPUT_START},
		{ "XINPUT_BACK", XINPUT_BACK},
		{ "XINPUT_LS", XINPUT_LS},
		{ "XINPUT_RS", XINPUT_RS},
		{ "XINPUT_LB", XINPUT_LS},
		{ "XINPUT_RB", XINPUT_RS},
		{ "XINPUT_LT", XINPUT_LT},
		{ "XINPUT_RT", XINPUT_RT},
		{ "XINPUT_LSB", XINPUT_LSB},
		{ "XINPUT_RSB", XINPUT_RSB},
		{ "XINPUT_LLEFT", XINPUT_LLEFT},
		{ "XINPUT_LRIGHT", XINPUT_LRIGHT},
		{ "XINPUT_RLEFT", XINPUT_RLEFT},
		{ "XINPUT_RRIGHT", XINPUT_RRIGHT},
	};

	Ds4ButtonMap Config::Ds4Map =
	{
		// Face Buttons
		{ "DS4_SQUARE", DS4_SQUARE },
		{ "Ds4_Square", DS4_SQUARE },

		{ "DS4_CROSS", DS4_CROSS },
		{ "Ds4_Cross", DS4_CROSS },

		{ "DS4_CIRCLE", DS4_CIRCLE },
		{ "Ds4_Circle", DS4_CIRCLE },

		{ "DS4_TRIANGLE", DS4_TRIANGLE },
		{ "Ds4_Triangle", DS4_TRIANGLE },

		// Standard Buttons
		{ "DS4_SHARE", DS4_SHARE },
		{ "Ds4_Share", DS4_SHARE },

		{ "DS4_OPTIONS", DS4_OPTIONS },
		{ "Ds4_Options", DS4_OPTIONS },

		{ "DS4_PS", DS4_PS },
		{ "Ds4_PS", DS4_PS },

		{ "DS4_TOUCH", DS4_TOUCH },
		{ "Ds4_Touch", DS4_TOUCH },

		{ "DS4_L1", DS4_L1 },
		{ "Ds4_L1", DS4_L1 },

		{ "DS4_R1", DS4_R1 },
		{ "Ds4_R1", DS4_R1 },

		// D-Pad Directions
		{ "DS4_DPAD_UP", DS4_DPAD_UP },
		{ "Ds4_DPad_Up", DS4_DPAD_UP },

		{ "DS4_DPAD_RIGHT", DS4_DPAD_RIGHT },
		{ "Ds4_DPad_Right", DS4_DPAD_RIGHT },

		{ "DS4_DPAD_DOWN", DS4_DPAD_DOWN },
		{ "Ds4_DPad_Down", DS4_DPAD_DOWN },

		{ "DS4_DPAD_LEFT", DS4_DPAD_LEFT },
		{ "Ds4_DPad_Left", DS4_DPAD_LEFT },

		// Trigger Buttons
		{ "DS4_L_TRIGGER", DS4_L_TRIGGER },
		{ "Ds4_L_Trigger", DS4_L_TRIGGER },

		{ "DS4_R_TRIGGER", DS4_R_TRIGGER },
		{ "Ds4_R_Trigger", DS4_R_TRIGGER },

		// Joystick Buttons
		{ "DS4_L3", DS4_L3 },
		{ "Ds4_L3", DS4_L3 },

		{ "DS4_R3", DS4_R3 },
		{ "Ds4_R3", DS4_R3 },

		// Left Joystick
		{ "DS4_L_STICK_UP", DS4_L_STICK_UP },
		{ "Ds4_L_Stick_Up", DS4_L_STICK_UP },

		{ "DS4_L_STICK_RIGHT", DS4_L_STICK_RIGHT },
		{ "Ds4_L_Stick_Right", DS4_L_STICK_RIGHT },

		{ "DS4_L_STICK_DOWN", DS4_L_STICK_DOWN },
		{ "Ds4_L_Stick_Down", DS4_L_STICK_DOWN },

		{ "DS4_L_STICK_LEFT", DS4_L_STICK_LEFT },
		{ "Ds4_L_Stick_Left", DS4_L_STICK_LEFT },

		// Right Joystick
		{ "DS4_R_STICK_UP", DS4_R_STICK_UP },
		{ "Ds4_R_Stick_Up", DS4_R_STICK_UP },

		{ "DS4_R_STICK_RIGHT", DS4_R_STICK_RIGHT },
		{ "Ds4_R_Stick_Right", DS4_R_STICK_RIGHT },

		{ "DS4_R_STICK_DOWN", DS4_R_STICK_DOWN },
		{ "Ds4_R_Stick_Down", DS4_R_STICK_DOWN },

		{ "DS4_R_STICK_LEFT", DS4_R_STICK_LEFT },
		{ "Ds4_R_Stick_Left", DS4_R_STICK_LEFT },
	};

	void Config::BindConfigKeys(std::unordered_map<std::string, std::string> &configMap, const char *configKeyName, Binding &bindObj, std::vector<std::string> defaultKeys)
	{
		std::vector<std::string> keys;

		auto configPair = configMap.find(configKeyName);

		// config variable was found in the ini
		if (configPair != configMap.end())
		{
			keys = Utilities::Split(configPair->second, ",");
		}
		else
		{
			keys = defaultKeys;
		}

		for (std::string key : keys)
		{
			Utilities::Trim(key);

			// Applies only for Single-Character keys
			if (key.length() == 1)
			{
				bindObj.AddBinding(new KeyboardBinding(key[0]));
			}
			else // for special key names
			{
				auto keycode = Config::Keymap.find(key.c_str());

				// name is known in the special keys map
				if (keycode != Config::Keymap.end())
				{
					bindObj.AddBinding(new KeyboardBinding(keycode->second));
				}
				else
				{
					auto xinputBtn = Config::XinputMap.find(key.c_str());

					if (xinputBtn != Config::XinputMap.end())
					{
						bindObj.AddBinding(new XinputBinding(xinputBtn->second));
					}
					else
					{
						// just gonna be lazy for now and put this inside an else statement
						auto ds4Button = Config::Ds4Map.find(key.c_str());

						if (ds4Button != Config::Ds4Map.end())
						{
							bindObj.AddBinding(new Ds4Binding(ds4Button->second));
						}
						else
						{
							printf("[TLAC] Config::BindConfigKeys(): Unable to parse key: '%s'\n", key.c_str());
						}
					}
				}
			}
		}
	}
}


code:
#pragma once

#include <unordered_map>
#include "KeyString.h"
#include "KeyStringHash.h"
#include "../Bindings/Binding.h"
#include "../DirectInput/Ds4/Ds4Button.h"

namespace TLAC::Input::KeyConfig
{
	typedef std::unordered_map<KeyString, uint8_t, KeyStringHash> KeycodeMap;
	typedef std::unordered_map<KeyString, Ds4Button, KeyStringHash> Ds4ButtonMap;

	class Config
	{
	public:
		static KeycodeMap Keymap;
		static Ds4ButtonMap Ds4Map;
		static KeycodeMap XinputMap;

		static void BindConfigKeys(std::unordered_map<std::string, std::string> &configMap, const char *configKeyName, Binding &bindObj, std::vector<std::string> defaultKeys);
	};
}


code:
#include "KeyString.h"

namespace TLAC::Input::KeyConfig
{
	KeyString::KeyString(const char* str) : value(str)
	{
	}

	bool KeyString::operator==(const KeyString& rsv) const
	{
		return !_strcmpi(value.c_str(), rsv.value.c_str());
	}
}


code:
#pragma once
#include <string>

namespace TLAC::Input::KeyConfig
{
	struct KeyString
	{
		std::string value;

		KeyString(const char* str);
		bool operator==(const KeyString& rsv) const;
	};
}


code:
#include "KeyStringHash.h"
#include <filesystem>

namespace TLAC::Input::KeyConfig
{
	size_t KeyStringHash::operator()(const KeyString& key) const
	{
		std::string ret = key.value;
		std::transform(ret.begin(), ret.end(), ret.begin(),
			[](unsigned char c) { return std::tolower(c, std::locale()); });
		return std::hash<std::string>()(ret);
	}
}


code:
#pragma once
#include "KeyString.h"

namespace TLAC::Input::KeyConfig
{
	struct KeyStringHash
	{
		size_t operator()(const KeyString& key) const;
	};
}


code:
#include "Keyboard.h"

namespace TLAC::Input
{
	Keyboard* Keyboard::instance;

	Keyboard::Keyboard()
	{
	}

	Keyboard* Keyboard::GetInstance()
	{
		if (instance == nullptr)
			instance = new Keyboard();

		return instance;
	}

	bool Keyboard::PollInput()
	{
		lastState = currentState;

		float elapsed = keyIntervalWatch.Restart();

		for (BYTE i = 0; i < KEYBOARD_KEYS; i++)
		{
			// DOWN
			bool isDown = GetAsyncKeyState(i) < 0;
			currentState.KeyStates[i] = isDown;

			// DOUBLE TAPPED
			bool isTapped = IsTapped(i);
			keyDoubleTapStates[i] = isTapped ? keyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false;

			// INTERVAL TAPPED
			keyIntervalTapStates[i] = isTapped;

			if (isTapped)
			{
				keyIntervalTapTimes[i] = 0;
				keyIntervalInitials[i] = true;
			}
			else if (isDown)
			{
				float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD;

				bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold;
				keyIntervalTapStates[i] = intervalTapped;

				if (intervalTapped)
				{
					keyIntervalTapTimes[i] = 0;
					keyIntervalInitials[i] = false;
				}
			}
		}

		return true;
	}

	bool Keyboard::IsDown(BYTE keycode)
	{
		return currentState.IsDown(keycode);
	}

	bool Keyboard::IsUp(BYTE keycode)
	{
		return !IsDown(keycode);
	}

	bool Keyboard::IsTapped(BYTE keycode)
	{
		return IsDown(keycode) && WasUp(keycode);
	}

	bool Keyboard::IsDoubleTapped(BYTE keycode)
	{
		return keyDoubleTapStates[keycode];
	}

	bool Keyboard::IsReleased(BYTE keycode)
	{
		return IsUp(keycode) && WasDown(keycode);
	}

	inline bool Keyboard::WasDown(BYTE keycode)
	{
		return lastState.IsDown(keycode);
	}

	inline bool Keyboard::WasUp(BYTE keycode)
	{
		return !WasDown(keycode);
	}

	bool Keyboard::IsIntervalTapped(BYTE keycode)
	{
		return keyIntervalTapStates[keycode];
	}
}


code:
#pragma once
#include "../IInputDevice.h"
#include "KeyboardState.h"
#include "../../Utilities/Stopwatch.h"

using Stopwatch = TLAC::Utilities::Stopwatch;

namespace TLAC::Input
{
	constexpr float DOUBLE_TAP_THRESHOLD = 200.0f;
	constexpr float INTERVAL_TAP_DELAY_THRESHOLD = 500.0f;
	constexpr float INTERVAL_TAP_THRESHOLD = 75.0f;

	class Keyboard : public IInputDevice
	{
	public:
		static Keyboard* GetInstance();

		bool PollInput() override;
		bool IsDown(BYTE keycode);
		bool IsUp(BYTE keycode);
		bool IsTapped(BYTE keycode);
		bool IsDoubleTapped(BYTE keycode);
		bool IsReleased(BYTE keycode);
		bool IsIntervalTapped(BYTE keycode);

		bool WasDown(BYTE keycode);
		bool WasUp(BYTE keycode);

	private:
		Keyboard();
		KeyboardState lastState;
		KeyboardState currentState;

		Stopwatch keyIntervalWatch;

		BYTE keyDoubleTapStates[KEYBOARD_KEYS];
		Stopwatch keyDoubleTapWatches[KEYBOARD_KEYS];

		BOOL keyIntervalInitials[KEYBOARD_KEYS];
		BYTE keyIntervalTapStates[KEYBOARD_KEYS];
		FLOAT keyIntervalTapTimes[KEYBOARD_KEYS];

		static Keyboard* instance;
	};
}


code:
#include "KeyboardState.h"

namespace TLAC::Input
{
	bool KeyboardState::IsDown(BYTE keycode)
	{
		return KeyStates[keycode];
	}
}


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

namespace TLAC::Input
{
	const int KEYBOARD_KEYS = 0xFF;

	struct KeyboardState
	{
		BYTE KeyStates[KEYBOARD_KEYS];

		bool IsDown(BYTE keycode);
	};
}


code:
#include "Mouse.h"
#include "../../framework.h"
#include "../../Constants.h"

namespace TLAC::Input
{
	Mouse* Mouse::instance;

	Mouse::Mouse()
	{
		directInputMouse = new DirectInputMouse();
	}

	Mouse::~Mouse()
	{
		if (directInputMouse != nullptr)
			delete directInputMouse;
	}

	Mouse* Mouse::GetInstance()
	{
		if (instance == nullptr)
			instance = new Mouse();

		return instance;
	}

	POINT Mouse::GetPosition()
	{
		return currentState.Position;
	}

	POINT Mouse::GetRelativePosition()
	{
		return currentState.RelativePosition;
	}

	POINT Mouse::GetDeltaPosition()
	{
		return
		{
			currentState.Position.x - lastState.Position.x,
			currentState.Position.y - lastState.Position.y
		};
	}

	long Mouse::GetMouseWheel()
	{
		return currentState.MouseWheel;
	}

	long Mouse::GetDeltaMouseWheel()
	{
		return currentState.MouseWheel - lastState.MouseWheel;
	}

	bool Mouse::HasMoved()
	{
		POINT delta = GetDeltaPosition();
		return delta.x != 0 || delta.y != 0;
	}

	bool Mouse::GetIsScrolledUp()
	{
		return currentState.ScrolledUp;
	}

	bool Mouse::GetIsScrolledDown()
	{
		return currentState.ScrolledDown;
	}

	bool Mouse::GetWasScrolledUp()
	{
		return lastState.ScrolledUp;
	}

	bool Mouse::GetWasScrolledDown()
	{
		return lastState.ScrolledDown;
	}

	void Mouse::SetPosition(int x, int y)
	{
		lastState.Position.x = x;
		lastState.Position.y = y;
		SetCursorPos(x, y);
	}

	bool Mouse::PollInput()
	{
		lastState = currentState;

		GetCursorPos(&currentState.Position);
		currentState.RelativePosition = currentState.Position;

		if (framework::DivaWindowHandle != NULL)
			ScreenToClient(framework::DivaWindowHandle, &currentState.RelativePosition);

			RECT hWindow;
		GetClientRect(TLAC::framework::DivaWindowHandle, &hWindow);

		gameHeight = (int*)RESOLUTION_HEIGHT_ADDRESS;
		gameWidth = (int*)RESOLUTION_WIDTH_ADDRESS;
		fbWidth = (int*)FB_WIDTH_ADDRESS;
		fbHeight = (int*)FB_HEIGHT_ADDRESS;

		if (directInputMouse != nullptr)
		{
			if (directInputMouse->Poll())
				currentState.MouseWheel += directInputMouse->GetMouseWheel();

			currentState.ScrolledUp = (GetDeltaMouseWheel() > 0);
			currentState.ScrolledDown = (GetDeltaMouseWheel() < 0);
		}

		if ((fbWidth != gameWidth) && (fbHeight != gameHeight)) {
			xoffset = ((float)16 / (float)9) * (hWindow.bottom - hWindow.top);
			if (xoffset != (hWindow.right - hWindow.left))
			{
				scale = xoffset / (hWindow.right - hWindow.left);
				xoffset = ((hWindow.right - hWindow.left) / 2) - (xoffset / 2);
			}
			else {
				xoffset = 0;
				scale = 1;
			}

			currentState.RelativePosition.x = ((currentState.RelativePosition.x - round(xoffset)) * *gameWidth / (hWindow.right - hWindow.left)) / scale;
			currentState.RelativePosition.y = currentState.RelativePosition.y * *gameHeight / (hWindow.bottom - hWindow.top);
		}

		return true;
	}
}


code:
#pragma once
#include "../IInputDevice.h"
#include "../DirectInput/DirectInputMouse.h"
#include "MouseState.h"

namespace TLAC::Input
{
	class Mouse : public IInputDevice
	{
	public:
		~Mouse();

		static Mouse* GetInstance();

		bool PollInput() override;

		POINT GetPosition();
		POINT GetRelativePosition();
		POINT GetDeltaPosition();

		long GetMouseWheel();
		long GetDeltaMouseWheel();

		bool HasMoved();
		bool GetIsScrolledUp();
		bool GetIsScrolledDown();
		bool GetWasScrolledUp();
		bool GetWasScrolledDown();

		void SetPosition(int x, int y);

	private:
		Mouse();
		MouseState lastState;
		MouseState currentState;
		DirectInputMouse* directInputMouse = nullptr;

		int* gameWidth;
		int* gameHeight;
		int* fbWidth;
		int* fbHeight;

		float xoffset;
		float scale;

		static Mouse* instance;
	};
}


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

namespace TLAC::Input
{
	struct MouseState
	{
		POINT Position;
		POINT RelativePosition;
		long MouseWheel;
		bool ScrolledUp;
		bool ScrolledDown;
	};
}


code:
#include <windows.h>
#include "Xinput.h"
#include "../../Constants.h"
#include "../../FileSystem/ConfigFile.h"
#include "../../framework.h"
#include "../../Components/GameTargets/TargetInspector.h"
#include "../../Components/Input/TouchSliderEmulator.h"

namespace TLAC::Input
{
	Xinput* Xinput::instance;

	Xinput::Xinput()
	{
		TLAC::FileSystem::ConfigFile config(TLAC::framework::GetModuleDirectory(), "keyconfig.ini");
		config.OpenRead();
		xinput_num = config.GetIntegerValue("xinput_preferred");
		rumble = config.GetBooleanValue("rumble");
	}

	Xinput* Xinput::GetInstance()
	{
		if (instance == nullptr)
		{
			instance = new Xinput();
		}
		return instance;
	}

	void Xinput::SetTapStates(BYTE keycode, float elapsed)
	{
		KeyDoubleTapStates[keycode] = IsTapped(keycode) ? KeyDoubleTapWatches[keycode].Restart() <= DOUBLE_TAP_THRESHOLD : false;

		bool isDown = currentState.KeyStates[keycode];
		bool isTapped = IsTapped(keycode);

		keyIntervalTapStates[keycode] = isTapped;

		if (isTapped)
		{
			keyIntervalTapTimes[keycode] = 0;
			keyIntervalInitials[keycode] = true;
		}
		else if (isDown)
		{
			float threshold = keyIntervalInitials[keycode] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD;

			bool intervalTapped = (keyIntervalTapTimes[keycode] += elapsed) > threshold;
			keyIntervalTapStates[keycode] = intervalTapped;

			if (intervalTapped)
			{
				keyIntervalTapTimes[keycode] = 0;
				keyIntervalInitials[keycode] = false;
			}
		}
	}

	bool Xinput::PollInput()
	{
		lastState = currentState;
		ZeroMemory(&state, sizeof(XINPUT_STATE));
		float elapsed = keyIntervalWatch.Restart();

		int xc_pref = xinput_num;
		if (xc_pref < 0 || xc_pref > 3) xc_pref = 0;
		for (int n = 0; n < 4; n++)
		{
			ZeroMemory(&state, sizeof(XINPUT_STATE));
			if (XInputGetState(xc_pref, &state) == ERROR_SUCCESS)
			{
				BYTE i = XINPUT_A;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_DOWN;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_B;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_RIGHT;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_X;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_LEFT;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_Y;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_UP;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_LS;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_RS;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_LSB;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_RSB;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_START;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_BACK;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_LT;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.bLeftTrigger > 230)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				i = XINPUT_RT;
				{
					currentState.KeyStates[i] = false;
					if (state.Gamepad.bRightTrigger > 230)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				{
					//float normLX = fmaxf(-1, (float)state.Gamepad.sThumbLX / 32767);
					i = XINPUT_LRIGHT;
					currentState.KeyStates[i] = false;
					if (state.Gamepad.sThumbLX > 10000)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);

					i = XINPUT_LLEFT;
					currentState.KeyStates[i] = false;
					if (state.Gamepad.sThumbLX < -10000)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				{
					//float normLX = fmaxf(-1, (float)state.Gamepad.sThumbRX / 32767);
					i = XINPUT_RRIGHT;
					currentState.KeyStates[i] = false;
					if (state.Gamepad.sThumbRX > 10000)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);

					i = XINPUT_RLEFT;
					currentState.KeyStates[i] = false;
					if (state.Gamepad.sThumbRX < -10000)
						currentState.KeyStates[i] = true;
					SetTapStates(i, elapsed);
				}

				if (rumble && TLAC::Components::TouchSliderEmulator::LatestInstance->isSliderTouched() && TLAC::Components::TargetInspector::ShouldVibrate)
				{
					XINPUT_VIBRATION vibration;
					ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
					vibration.wLeftMotorSpeed = 8000; // use any value between 0-65535 here
					vibration.wRightMotorSpeed = 4000; // use any value between 0-65535 here
					XInputSetState(xc_pref, &vibration);
				}
				else
				{
					XINPUT_VIBRATION vibration;
					ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
					vibration.wLeftMotorSpeed = 0; // use any value between 0-65535 here
					vibration.wRightMotorSpeed = 0; // use any value between 0-65535 here
					XInputSetState(xc_pref, &vibration);
					//printf("0");
				}

				break;
			}
			else if (n == 3) {
				ZeroMemory(&state, sizeof(XINPUT_STATE));
				ZeroMemory(&currentState, sizeof(currentState));
			}

			if (xc_pref == 3) xc_pref = 0;
			else xc_pref++;
		}
			return TRUE;
	}

	bool Xinput::IsDown(BYTE keycode)
	{
		return currentState.IsDown(keycode);
	}

	bool Xinput::IsUp(BYTE keycode)
	{
		return !IsDown(keycode);
	}

	bool Xinput::IsTapped(BYTE keycode)
	{
		return IsDown(keycode) && WasUp(keycode);
	}

	bool Xinput::IsDoubleTapped(BYTE keycode)
	{
		return KeyDoubleTapStates[keycode];
	}

	bool Xinput::IsReleased(BYTE keycode)
	{
		return IsUp(keycode) && WasDown(keycode);
	}

	inline bool Xinput::WasDown(BYTE keycode)
	{
		return lastState.IsDown(keycode);
	}

	inline bool Xinput::WasUp(BYTE keycode)
	{
		return !WasDown(keycode);
	}

	bool Xinput::IsIntervalTapped(BYTE keycode)
	{
		return keyIntervalTapStates[keycode];
	}
}


code:
#pragma once
#include "../IInputDevice.h"
#include <xinput.h>
#include "../../Utilities/Stopwatch.h"
#include "XinputState.h"

using Stopwatch = TLAC::Utilities::Stopwatch;

namespace TLAC::Input
{
	class Xinput : public IInputDevice
	{
		const float DOUBLE_TAP_THRESHOLD = 200.0f;
		const float INTERVAL_TAP_DELAY_THRESHOLD = 500.0f;
		const float INTERVAL_TAP_THRESHOLD = 75.0f;

	public:
		static Xinput* GetInstance();

		bool PollInput() override;
		bool IsDown(BYTE keycode);
		bool IsUp(BYTE keycode);
		bool IsTapped(BYTE keycode);
		bool IsReleased(BYTE keycode);
		bool IsDoubleTapped(BYTE keycode);
		bool IsIntervalTapped(BYTE keycode);

		bool WasDown(BYTE keycode);
		bool WasUp(BYTE keycode);

	private:
		Xinput();
		XinputState lastState;
		XinputState currentState;

		XINPUT_STATE state;

		BYTE KeyDoubleTapStates[0xFF];
		Utilities::Stopwatch KeyDoubleTapWatches[0xFF];

		Stopwatch keyIntervalWatch;

		BOOL keyIntervalInitials[0xFF];
		BYTE keyIntervalTapStates[0xFF];
		FLOAT keyIntervalTapTimes[0xFF];

		static Xinput* instance;

		void SetTapStates(BYTE keycode, float elapsed);

		int xinput_num;
		bool rumble;
	};
}


code:
#include "XinputState.h"

namespace TLAC::Input
{
	bool XinputState::IsDown(BYTE keycode)
	{
		return KeyStates[keycode];
	}
}


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

namespace TLAC::Input
{
	struct XinputState
	{
		bool KeyStates[0x8F];

		bool IsDown(BYTE keycode);
	};
}


code:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <ProjectGuid>{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>TLAC</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>$(ProjectName)</TargetName>
    <TargetExt>.dva</TargetExt>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>$(ProjectName)</TargetName>
    <TargetExt>.dva</TargetExt>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>
      </PrecompiledHeaderFile>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalIncludeDirectories>..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalDependencies>XINPUT9_1_0.LIB;dinput8.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
      <IgnoreSpecificDefaultLibraries>Xinput.h</IgnoreSpecificDefaultLibraries>
      <AdditionalLibraryDirectories>..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;_DEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>NotUsing</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>
      </PrecompiledHeaderFile>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalIncludeDirectories>..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalLibraryDirectories>..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>XINPUT9_1_0.LIB;dinput8.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
      <IgnoreSpecificDefaultLibraries>Xinput.h</IgnoreSpecificDefaultLibraries>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="Components\CameraController.h" />
    <ClInclude Include="Components\ComponentsManager.h" />
    <ClInclude Include="Components\CustomPlayerData.h" />
    <ClInclude Include="Components\DebugComponent.h" />
    <ClInclude Include="Components\EmulatorComponent.h" />
    <ClInclude Include="Components\FastLoader.h" />
    <ClInclude Include="Components\FrameRateManager.h" />
    <ClInclude Include="Components\GameState.h" />
    <ClInclude Include="Components\GameTargets\HoldState.h" />
    <ClInclude Include="Components\GameTargets\TargetHitStates.h" />
    <ClInclude Include="Components\GameTargets\TargetInspector.h" />
    <ClInclude Include="Components\GameTargets\TargetState.h" />
    <ClInclude Include="Components\GameTargets\TargetTypes.h" />
    <ClInclude Include="Components\Input\InputBufferType.h" />
    <ClInclude Include="Components\Input\InputEmulator.h" />
    <ClInclude Include="Components\Input\InputState.h" />
    <ClInclude Include="Components\Input\JvsButtons.h" />
    <ClInclude Include="Components\Input\TouchPanelEmulator.h" />
    <ClInclude Include="Components\Input\TouchPanelState.h" />
    <ClInclude Include="Components\Input\TouchSliderEmulator.h" />
    <ClInclude Include="Components\Input\TouchSliderState.h" />
    <ClInclude Include="Components\PlayerData.h" />
    <ClInclude Include="Components\PlayerDataManager.h" />
    <ClInclude Include="Components\ScaleComponent.h" />
    <ClInclude Include="Components\ScoreSaver.h" />
    <ClInclude Include="Components\StageManager.h" />
    <ClInclude Include="Components\SysTimer.h" />
    <ClInclude Include="Constants.h" />
    <ClInclude Include="FileSystem\ConfigFile.h" />
    <ClInclude Include="FileSystem\TextFile.h" />
    <ClInclude Include="framework.h" />
    <ClInclude Include="Input\Bindings\Binding.h" />
    <ClInclude Include="Input\Bindings\Ds4Binding.h" />
    <ClInclude Include="Input\Bindings\IInputBinding.h" />
    <ClInclude Include="Input\Bindings\KeyboardBinding.h" />
    <ClInclude Include="Input\Bindings\MouseBinding.h" />
    <ClInclude Include="Input\Bindings\XinputBinding.h" />
    <ClInclude Include="Input\DirectInput\Controller.h" />
    <ClInclude Include="Input\DirectInput\DirectInput.h" />
    <ClInclude Include="Input\DirectInput\DirectInputDevice.h" />
    <ClInclude Include="Input\DirectInput\DirectInputMouse.h" />
    <ClInclude Include="Input\DirectInput\Ds4\Ds4Button.h" />
    <ClInclude Include="Input\DirectInput\Ds4\Ds4State.h" />
    <ClInclude Include="Input\DirectInput\Ds4\DualShock4.h" />
    <ClInclude Include="Input\IInputDevice.h" />
    <ClInclude Include="Input\Keyboard\Keyboard.h" />
    <ClInclude Include="Input\Keyboard\KeyboardState.h" />
    <ClInclude Include="Input\KeyConfig\Config.h" />
    <ClInclude Include="Input\KeyConfig\KeyString.h" />
    <ClInclude Include="Input\KeyConfig\KeyStringHash.h" />
    <ClInclude Include="Input\Mouse\Mouse.h" />
    <ClInclude Include="Input\Mouse\MouseState.h" />
    <ClInclude Include="Input\Xinput\Xinput.h" />
    <ClInclude Include="Input\Xinput\XinputState.h" />
    <ClInclude Include="Utilities\EnumBitwiseOperations.h" />
    <ClInclude Include="Utilities\Math.h" />
    <ClInclude Include="Utilities\Operations.h" />
    <ClInclude Include="Utilities\Stopwatch.h" />
    <ClInclude Include="Utilities\Vec2.h" />
    <ClInclude Include="Utilities\Vec3.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="Components\CameraController.cpp" />
    <ClCompile Include="Components\ComponentsManager.cpp" />
    <ClCompile Include="Components\DebugComponent.cpp" />
    <ClCompile Include="Components\EmulatorComponent.cpp" />
    <ClCompile Include="Components\FastLoader.cpp" />
    <ClCompile Include="Components\FrameRateManager.cpp" />
    <ClCompile Include="Components\GameTargets\TargetInspector.cpp" />
    <ClCompile Include="Components\Input\InputEmulator.cpp" />
    <ClCompile Include="Components\Input\InputState.cpp" />
    <ClCompile Include="Components\Input\TouchPanelEmulator.cpp" />
    <ClCompile Include="Components\Input\TouchSliderEmulator.cpp" />
    <ClCompile Include="Components\Input\TouchSliderState.cpp" />
    <ClCompile Include="Components\PlayerDataManager.cpp" />
    <ClCompile Include="Components\ScaleComponent.cpp" />
    <ClCompile Include="Components\ScoreSaver.cpp" />
    <ClCompile Include="Components\StageManager.cpp" />
    <ClCompile Include="Components\SysTimer.cpp" />
    <ClCompile Include="dllmain.cpp" />
    <ClCompile Include="FileSystem\ConfigFile.cpp" />
    <ClCompile Include="FileSystem\TextFile.cpp" />
    <ClCompile Include="framework.cpp" />
    <ClCompile Include="Input\Bindings\Binding.cpp" />
    <ClCompile Include="Input\Bindings\Ds4Binding.cpp" />
    <ClCompile Include="Input\Bindings\KeyboardBinding.cpp" />
    <ClCompile Include="Input\Bindings\MouseBinding.cpp" />
    <ClCompile Include="Input\Bindings\XinputBinding.cpp" />
    <ClCompile Include="Input\DirectInput\Controller.cpp" />
    <ClCompile Include="Input\DirectInput\DirectInput.cpp" />
    <ClCompile Include="Input\DirectInput\DirectInputDevice.cpp" />
    <ClCompile Include="Input\DirectInput\DirectInputMouse.cpp" />
    <ClCompile Include="Input\DirectInput\Ds4\Ds4State.cpp" />
    <ClCompile Include="Input\DirectInput\Ds4\DualShock4.cpp" />
    <ClCompile Include="Input\Keyboard\Keyboard.cpp" />
    <ClCompile Include="Input\Keyboard\KeyboardState.cpp" />
    <ClCompile Include="Input\KeyConfig\Config.cpp" />
    <ClCompile Include="Input\KeyConfig\KeyString.cpp" />
    <ClCompile Include="Input\KeyConfig\KeyStringHash.cpp" />
    <ClCompile Include="Input\Mouse\Mouse.cpp" />
    <ClCompile Include="Input\Xinput\Xinput.cpp" />
    <ClCompile Include="Input\Xinput\XinputState.cpp" />
    <ClCompile Include="Utilities\Math.cpp" />
    <ClCompile Include="Utilities\Operations.cpp" />
    <ClCompile Include="Utilities\Stopwatch.cpp" />
    <ClCompile Include="Utilities\Vec2.cpp" />
    <ClCompile Include="Utilities\Vec3.cpp" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>