#pragma once
#include <windows.h>
#include <tlhelp32.h>
#include <cstdint>

#define rw_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2ec33, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define base_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2ec36, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)  
#define auth_key 0x94c9e4bc3

struct _rw {
	uint64_t key;
	DWORD process_id;
	void* addr;
	void* buffer;
	uint64_t size;
	uint64_t return_size;
	bool write;
};

struct _base {
	uint64_t key;
	DWORD process_id;
	uint64_t* base;
};

namespace driver {
	inline HANDLE driver_handle;
	inline DWORD roblox_process_id;
	inline uint64_t roblox_base;

	inline bool connect() {
		driver_handle = CreateFile("\\\\.\\RickOwens00", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
		return !(!driver_handle || (driver_handle == INVALID_HANDLE_VALUE));
	}

	inline bool read(PVOID addr, PVOID buffer, DWORD size) {
		_rw args = { auth_key, roblox_process_id, (void*)addr, (void*)buffer, size, size, false };
		return DeviceIoControl(driver_handle, rw_code, &args, sizeof(args), nullptr, NULL, NULL, NULL);
	}

	inline bool write(PVOID addr, PVOID buffer, DWORD size) {
		_rw args = { auth_key, roblox_process_id, (void*)addr, (void*)buffer, size, size, true };
		return DeviceIoControl(driver_handle, rw_code, &args, sizeof(args), nullptr, NULL, NULL, NULL);
	}

	inline DWORD get_process_id(std::string process_name) {
		auto snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		PROCESSENTRY32 pe;
		pe.dwSize = sizeof(PROCESSENTRY32);
		if (Process32First(snap, &pe)) {
			do {
				if (!_strcmpi(pe.szExeFile, process_name.c_str())) {
					CloseHandle(snap);
					roblox_process_id = pe.th32ProcessID;
					return pe.th32ProcessID;
				}
			} while (Process32Next(snap, &pe));
		}
		CloseHandle(snap);
		return 0;
	}

	inline uint64_t get_process_base() {
		uint64_t base = 0;
		_base args = { auth_key, roblox_process_id, &base };
		DeviceIoControl(driver_handle, base_code, &args, sizeof(args), nullptr, NULL, NULL, NULL);
		roblox_base = base;
		return base;
	}
}

template <typename T>
inline T read(uint64_t addr) {
	T buffer{};
	driver::read((PVOID)addr, &buffer, sizeof(T));
	return buffer;
}

template <typename T>
inline bool write(uint64_t addr, T val) {
	auto res = driver::write((PVOID)addr, &val, sizeof(T));
	return res;
}

inline std::string read_string(uint64_t addr) {
	auto len = read<int>(addr + 0x10);
	if (len >= 16)
		addr = read<uint64_t>(addr);
	std::string str;
	for (auto i = 0; auto ch = read<char>(addr + i); i += sizeof(char)) {
		if (ch == '\0') break;
		str.push_back(ch);
	}
	return str;
}