// Stigma DLL.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include <Windows.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include "UsefulFunctions.h"
#include "Stigma DLL.h"
#include "detours.h" // Thanks microsoft :D
#include "SigScan.h" // Thanks for the idea Tarek :D
#include "BrickColors.h" // Unused currently
#include <string>
#include "Form.h" // The GUI shit
#include <vector>
#include <ctime>
#include <chrono>
#include <thread>
#include <filesystem>

using namespace std;
using namespace std::this_thread;     // sleep_for, sleep_until
using namespace std::chrono_literals; // ns, us, ms, s, h, etc.
using std::chrono::system_clock;
namespace fs = std::filesystem;

HWND prnt_hWnd;

vector<int> LuaStatesArray;

bool LInArray(int L) {
	for (vector<int>::iterator it = LuaStatesArray.begin();it != LuaStatesArray.end();++it) {
		if (L == *it)
			return true;
	}
	return false;
}
vector<int> GetLuaStates() {
	return LuaStatesArray;
}

#define lua_pop(lua_State,n) lua_settop(lua_State,-(n)-1)
#define lua_pushcfunction(lua_State,f) lua_pushcclosure(lua_State,f,0)
#define lua_setglobal(lua_State,k) lua_setfield(lua_State,-10002,k)
#define lua_getglobal(lua_State,k) lua_getfield(lua_State,-10002,k)
#define lua_tostring(lua_State,k) lua_tolstring(lua_State,(k), NULL)


typedef int* (__cdecl *printIdentity)(int lua_State);
typedef int* (__cdecl *GetContext)();
typedef int (__cdecl *luaL_NewState)();
typedef void* (__cdecl *lua_GetField)(int lua_State, int idx, const char *k);
typedef int (__cdecl *lua_PCall)(int lua_State, int nargs, int nresults, int errfunc);
typedef void (__cdecl *lua_PushString)(int lua_State, const char *s);
typedef const char *(__cdecl *lua_ToLString)(int lua_State, int idx, size_t *len);
typedef void* (__cdecl *lua_PushValue)(int lua_State,int idx);
typedef void* (__cdecl *lua_LoadFile)(int lua_State, const char *filePath);
typedef const char* (__cdecl *lua_LoadString)(int lua_State, int a2, const char * LuaCode);
typedef int (__cdecl *lua_GetTop)(int lua_State);
typedef void* (__cdecl *lua_SetTop)(int lua_State, int n);
typedef void* (__cdecl *lua_SetField)(int lua_State, int idx, const char *k);
typedef void* (__cdecl *lua_PushCClosure)(int lua_State, int fn(), int n);
typedef void* (__cdecl *lua_Remove)(int lua_State, int idx);
typedef int (__cdecl *Print)();
typedef DWORD (__thiscall *SetLocalPlayerTeam)(int ThisThing,int TeamColor);
typedef int (__cdecl *PropertyArgument)(int a1);
typedef int (__thiscall *TakeDamage)(void* Self, float Dmg);

#define MAX_LENGTH 256


static luaL_NewState luaL_newstate;
static lua_GetField lua_getfield;
static lua_PCall lua_pcall;
static lua_PushString lua_pushstring;
static lua_ToLString lua_tolstring;
static lua_PushValue lua_pushvalue;
static lua_LoadFile lua_loadfile;
static lua_LoadString lua_loadstring;
static lua_GetTop lua_gettop;
static lua_SetTop lua_settop;
static lua_SetField lua_setfield;
static lua_PushCClosure lua_pushcclosure;
static lua_Remove lua_remove;
static Print print;
static GetContext context;
static printIdentity printID;
static SetLocalPlayerTeam SetTeam;
static PropertyArgument propertyArgument;
static TakeDamage takeDamage;

int luaState = 0; // bow down to the mighty LuaState!

int rTHIS = 0; // For setting ROBLOX Properties
int TakeDamageType = 0;
					/*
						0 = Default
						1 = OP
						2 = Nerf
					*/

int FCalled() {
	MessageBoxA(0,"Yay! you called me via lua!",":D",0);
	return 1;
}

HWND ConsoleHandle = nullptr;

const int NUM_SECONDS = 1;

void GetAllWindowsFromProcessID(DWORD dwProcessID)
{
	// find all hWnds (vhWnds) associated with a process id (dwProcessID)
	HWND hCurWnd = nullptr;

	do
	{
		hCurWnd = FindWindowEx(nullptr, hCurWnd, nullptr, nullptr);
		DWORD checkProcessID = 0;
		GetWindowThreadProcessId(hCurWnd, &checkProcessID);
		if (checkProcessID == dwProcessID)
		{
			if (ConsoleHandle != hCurWnd) {
				ShowWindow(hCurWnd, SW_HIDE);
				//wprintf(L"Found hWnd %d\n", hCurWnd);
			}
		}
	} while (hCurWnd != nullptr);
}

auto latest_tm2 = fs::last_write_time(std::filesystem::current_path());
auto latest2 = std::filesystem::current_path();
string lastprint = "";

int update() {

	int count = 1;

	double time_counter = 0;

	clock_t this_time = clock();
	clock_t last_time = this_time;

	while (true)
	{
		this_time = clock();

		time_counter += (double)(this_time - last_time);

		last_time = this_time;

		if (time_counter > (double)(NUM_SECONDS * CLOCKS_PER_SEC))
		{
			try {
					DWORD id = GetCurrentProcessId();
					    GetAllWindowsFromProcessID(id);//prnt_hWnd = FindWindow(NULL, L"ROBLOX");
			}
			catch (...) {

			}


			try {
				auto path = std::filesystem::temp_directory_path().parent_path().parent_path();
				path /= "\Roblox";
				path /= "\logs";
				std::ifstream myfile;
				for (const auto& entry : fs::directory_iterator(path)) {
					auto timestamp = fs::last_write_time(entry.path());
					if (timestamp > latest_tm2) {
						fs::path pathlol{ fs::path(entry.path()).filename() };
						std::string path_string{ pathlol.u8string() };
						std::string b = path_string.substr(0, 3);
						if (b == "rcc") {
							latest2 = entry.path();
							latest_tm2 = timestamp;
						}
					}
				}
				fstream my_file(latest2, std::fstream::in);
				char mychar;
				string s;
				try {
					while (my_file >> std::noskipws >> mychar)
					{
						s.push_back(mychar);
					}
					fs::path pathlol{ fs::path(latest2).filename() };
					std::string path_string{ pathlol.u8string() };
					std::string b = path_string.substr(0, 3);
					if (path_string != lastprint && b=="rcc") {
						cout << s;
						lastprint = path_string;
					}
				}
				catch (...) {

				}
				my_file.close();
			}
			catch (...) {

			}



			time_counter -= (double)(NUM_SECONDS * CLOCKS_PER_SEC);
			count++;
		}
	}

	return 0;

}

void TeleportUpdater() {
	/*
	TODO:
		Allow users to select option BEFORE teleporting
		check placeid until change
		reset luastate
	*/
	luaState = 0;
	//Init(true);
}
int WritePlace() {
	const char * XMLString = lua_tostring(luaState,-1);
	MessageBoxA(0,XMLString,"XML file",0);
	return 1;
}

HANDLE outputHandle;

void console(const char* csttl) {
	SetConsoleTitleA(csttl);
	AllocConsole();
	freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
	outputHandle = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	SetStdHandle(STD_OUTPUT_HANDLE, outputHandle);

	ConsoleHandle = GetConsoleWindow();
	::SetWindowPos(ConsoleHandle, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
	::ShowWindow(ConsoleHandle, SW_NORMAL);
}

void printy() {

	console("RCCService"); // Title
	cout << "RCCService Initializing... \n"; // Start Up Messages
	cout << "Service Starting... \n"; // Start Up Messages
	cout << "Service started on port 64989 \n"; // Start Up Messages
	std::this_thread::sleep_for(10ns);
	std::this_thread::sleep_until(system_clock::now() + 11s);
	try {
		auto path = std::filesystem::temp_directory_path().parent_path().parent_path();
		path /= "\Roblox";
		path /= "\logs";
		std::ifstream myfile;
		for (const auto& entry : fs::directory_iterator(path)) {
			auto timestamp = fs::last_write_time(entry.path());
			if (timestamp > latest_tm2) {
				fs::path pathlol{ fs::path(entry.path()).filename() };
				std::string path_string{ pathlol.u8string() };
				std::string b = path_string.substr(0, 3);
				if (b == "rcc") {
					latest2 = entry.path();
					latest_tm2 = timestamp;
				}
			}
		}
		fstream my_file(latest2, std::fstream::in);
		char mychar;
		string s;
		try {
			while (my_file >> std::noskipws >> mychar)
			{
				s.push_back(mychar);
			}
			fs::path pathlol{ fs::path(latest2).filename() };
			std::string path_string{ pathlol.u8string() };
			std::string b = path_string.substr(0, 3);
			if (path_string != lastprint && b == "rcc") {
				cout << s;
				lastprint = path_string;
			}
		}
		catch (...) {

		}
		my_file.close();
	}
	catch (...) {

	}
	char LSbuf[255] = {0};
	_itoa_s(luaState,LSbuf,16);

	lua_setglobal(luaState,"MB");

	lua_pushcfunction(luaState,WritePlace);
	lua_setglobal(luaState,"PlaceSteal");


	lua_getglobal(luaState,"MB");
	lua_pcall(luaState,0,0,0);

    fstream my_filexd("gameserver.txt", std::fstream::in);
	char mychar;
	string s;
	try {
		while (my_filexd >> std::noskipws >> mychar)
		{
			s.push_back(mychar);
		}
		ExecuteCode(s);
	}
	catch (...) {

	}
	my_filexd.close();
	update();
}

void main() {
	//char LSbuf[255] = {0};
	//_itoa_s(luaState,LSbuf,16);

	//lua_pushcfunction(luaState,FCalled);
	//lua_setglobal(luaState,"MB"); // Usage: MB()

	//lua_pushcfunction(luaState,WritePlace);
	//lua_setglobal(luaState,"PlaceSteal");


	//lua_getglobal(luaState,"MB");
	//lua_pcall(luaState,0,0,0);

	//lua_getglobal(luaState, "print");
	//lua_pushstring(luaState, "Stigma_V2 hooked into memory."); // confirm we are hooked.
	//lua_pcall(luaState, 1, 0, 0);
	printy();
}

void Init(bool Teleported) {
	
	//Disable memory check
	DWORD oldProtection;
	DWORD HC = SigScan("\x74\x1C\x8B\x45\x08\x89\x08\xC6\x40\x04\x00\x8B\x4C\x24\x1C", "xxxxxxxxxxxxxxx");

	if(HC != 0) {
		VirtualProtect((LPVOID)HC, 2048, PAGE_EXECUTE_READWRITE, &oldProtection);
		*(PBYTE)(HC) = 0x75;
		VirtualProtect((LPVOID)HC, 2048, PAGE_EXECUTE_READ, &oldProtection);
		//memory check disabled (:
	}
	
	
	lua_gettop               = (lua_GetTop)SigScan("\x55\x8B\xEC\x8B\x4D\x08\x8B\x41\x08\x2B\x41\x0C\xC1\xF8\x04\x5D\xC3", "xxxxxxxxxxxxxxxxx");
	context                  = (GetContext)SigScan("\x55\x8B\xEC\x51\x56\xE8\x99\x99\x99\x99\x50\x99\x99\x99\x99\x99\x99\x83","xxxxxx????x??????x");
	LoadDetours();

	//luaL_newstate            = (luaL_NewState)SigScan("\x56\x6A\x00\x68\x99\x99\x99\x99\xE8\x99\x99\x99\x99\x8B\xF0\x83\xC4\x08\x85\xF6", "xxxx????x????xxxxxxx");
	lua_getfield             = (lua_GetField)SigScan("\x55\x8B\xEC\x83\xEC\x10\x53\x56\x8B\x75\x08\x57\xFF\x75\x0C\x56\xE8\x99\x99\x99\x99\x8B\x55\x10\x8B\xCA\x83\xC4\x08\x8B\xF8\x8D\x59\x01\x8A\x01\x41\x84\xC0\x75\xF9\x2B\xCB\x51\x52\x56\xE8\x99\x99\x99\x99\xFF\x76","xxxxxxxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxx????xx");
	lua_pcall                = (lua_PCall)SigScan("\x55\x8B\xEC\x8B\x45\x14\x83\xEC\x08\x57\x8B\x7D\x08\x85\xC0\x75\x04\x33\xD2\xEB\x0F","xxxxxxxxxxxxxxxxxxxxx");
	lua_pushstring           = (lua_PushString)SigScan("\x55\x8B\xEC\x8B\x45\x0C\x85\xC0\x75\x13\x8B\x4D\x08\x8B\x41\x08\xC7\x40\x08\x00\x00\x00\x00\x83", "xxxxxxxxxxxxxxxxxxxxxxxx");
	lua_pushvalue            = (lua_PushValue)SigScan("\x55\x8B\xEC\x56\xFF\x75\x0C\x8B\x75\x08\x56\xE8\x99\x99\x99\x99\x8B\x56\x08\x8B\x08\x89\x0A\x8B\x48\x04\x89\x4A\x04\x8B", "xxxxxxxxxxxx????xxxxxxxxxxxxxx");
	lua_tolstring            = (lua_ToLString)SigScan("\x55\x8B\xEC\x56\xFF\x75\x0C\x8B\x75\x08\x56\xE8\x99\x99\x99\x99\x8B\xC8\x83\xC4\x08\x83\x79\x08\x04\x74\x42\x51\x56\xE8", "xxxxxxxxxxxx????xxxxxxxxxxxxxx");
	//lua_loadfile             = (lua_LoadFile)SigScan("\x55\x8B\xEC\x81\xEC\x0C\x02\x00\x00\x53\x56\x57\x8B\x7D\x08\x57\xE8", "xxxxxxxxxxxxxxxxx");
	
	lua_settop               = (lua_SetTop)SigScan("\x55\x8B\xEC\x8B\x55\x0C\x8B\x45\x08\x85\xD2\x78", "xxxxxxxxxxxx");
	lua_pushcclosure         = (lua_PushCClosure)SigScan("\x55\x8B\xEC\x56\x8B\x75\x08\x57\x8B\x4E\x10\x8B\x41\x44\x3B\x41\x40\x72","xxxxxxxxxxxxxxxxxx");
	lua_setfield             = (lua_SetField)SigScan("\x55\x8B\xEC\x83\xEC\x10\x53\x56\x8B\x75\x08\x57\xFF\x75\x0C\x56\xE8\x99\x99\x99\x99\x8B\x55\x10\x8B\xCA\x83\xC4\x08\x8B\xF8\x8D\x59\x01\x8A\x01\x41\x84\xC0\x75\xF9\x2B\xCB\x51\x52\x56\xE8\x99\x99\x99\x99\x89\x45\xF0","xxxxxxxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxx????xxx");
	//lua_remove               = (lua_Remove)SigScan("\x55\x8B\xEC\x56\xFF\x75\x0C\x8B\x75\x08\x56\xE8\x99\x99\x99\x99\x8D\x50\x10", "xxxxxxxxxxxx????xxx");
	
	//SetTeam                  = (SetLocalPlayerTeam)SigScan("\x55\x8B\xEC\x8B\x45\x08\x56\x8B\xF1\x3B\x46\x68\x74\x2C","xxxxxxxxxxxxxx");
	//propertyArgument         = (PropertyArgument)SigScan("\x55\x8B\xEC\x83\xEC\x08\x53\x56\x57\x8B\x7D\x08\x85\xFF\x0F","xxxxxxxxxxxxxxx");
	//takeDamage               = (TakeDamage)SigScan("\x55\x8B\xEC\x51\x56\x6A\x00\x8B\xF1\xE8\x99\x99\x99\x99\x85\xC0\x75\x0D","xxxxxxxxxx????xxxx");
	//LoadDetours();

	do {
		DoEvents();
	} while (luaState == 0);
	if(!Teleported)
	main(); // Calls the main function
}






void ExecuteScriptFromFile(int L,const char *fileName) { // TODO: Finish Lua File Execution.
	char path[MAX_PATH] = {0};
	GetFullPathNameA(fileName, MAX_PATH,path,NULL);
	lua_getglobal(luaState, "loadfile");
	lua_pushstring(luaState, path);
	lua_pcall(L, 1, 1, 0);
	lua_pcall(L, 0, 0, 0); 
}

int luaStateRetriever(int lua_State) {
	if (luaState == 0)
		luaState = lua_State;

	//if(!LInArray(lua_State)) {
	//	LuaStatesArray.insert(LuaStatesArray.end(),lua_State);
	//}
	return lua_gettop(lua_State);
}
int *FakeContextLevel() {
	int *RetVal = context();
	if (*RetVal == 0)
		*RetVal = 7; // Context changer FTW
	return RetVal;
}

int PropertySetterArgument(int a1) {
	int old = propertyArgument(a1);
	rTHIS = old;
	return old;
}

int TakeDamageHook(void* Self, float Dmg) {
	int ret = 75.0f;
	if(TakeDamageType == 1) // OP
	{
		ret = takeDamage(Self,Dmg * (float)100000);
	}
	else if(TakeDamageType == 2) // Nerf
	{
		ret = takeDamage(Self,(float)0);
	}

	takeDamage(Self,Dmg);
	return ret;
}

DWORD SetTeamHook(int ThisThing,int TeamColor) {
	//if(BTC) // if we're blocking team changes
		//return SetTeam(ThisThing,*(DWORD*)(ThisThing + 104));
	//return SetTeam(ThisThing,TeamColor);
	return (DWORD)TeamColor;
}

void LoadDetours(){
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());

	DetourAttach(&(LPVOID&)lua_gettop, (PBYTE)luaStateRetriever); // Steals ROBLOX's luaState

	DetourAttach(&(LPVOID&)context, (PBYTE)FakeContextLevel); // Fake our Identity Level

	//DetourAttach(&(LPVOID&)propertyArgument,(PBYTE)PropertySetterArgument); // Steal "THIS" arg for setting properties

	//DetourAttach(&(LPVOID&)takeDamage,(PBYTE)TakeDamageHook); // Manipulate the TakeDamage function of HUMANOID

	//DetourAttach(&(LPVOID&)SetTeam,(PBYTE)SetTeamHook); // Allow ourselfs to deny/accept team changing

	DetourTransactionCommit();

	//MessageBoxA(0,"Detours loaded","a",0);
}

void ExecuteCode(string Src) {
	if (luaState != 0) {
		lua_getglobal(luaState, "loadstring"); // Push loadstring onto the stack
		lua_pushstring(luaState, Src.c_str()); // Push our code onto the stack
		lua_pcall(luaState, 1, 1, 0); // Call loadstring and push return value onto stack
		lua_pcall(luaState, 0, 0, 0); // Execute loadstring's return value
	}
}