code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Boot/Patches.hpp>

/*
	Please, please, please!
	Keep patches consistent with the used patch style for readability.
	thx: Fire30
*/
void Mira::Boot::Patches::install_prerunPatches_620()
{
#if MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_620
	// NOTE: Only apply patches that the loader requires to run, the rest of them should go into Mira's ELF
	// You must assign the kernel base pointer before anything is done
	if (!gKernelBase)
		return;

	// Use "kmem" for all patches
	uint8_t *kmem;

	// Enable UART
	kmem = (uint8_t *)&gKernelBase[0x01570338];
	kmem[0] = 0x00;

	// Patch sys_dynlib_dlsym: Allow from anywhere
	kmem = (uint8_t *)&gKernelBase[0x0027F67A];
	kmem[0] = 0xE9;
	kmem[1] = 0xC4;
	kmem[2] = 0x01;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	kmem = (uint8_t *)&gKernelBase[0x0001A810];
	kmem[0] = 0x31;
	kmem[1] = 0xC0;
	kmem[2] = 0xC3;

	// Patch sys_mmap: Allow RWX (read-write-execute) mapping
	kmem = (uint8_t *)&gKernelBase[0x0024026D];
	kmem[0] = 0x37;
	kmem[3] = 0x37;

	// Patch setuid: Don't run kernel exploit more than once/privilege escalation
	kmem = (uint8_t *)&gKernelBase[0x000290E2];
	kmem[0] = 0xB8;
	kmem[1] = 0x00;
	kmem[2] = 0x00;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	// Enable RWX (kmem_alloc) mapping
	kmem = (uint8_t *)&gKernelBase[0x002704E8];
	kmem[0] = 0x07;

	kmem = (uint8_t *)&gKernelBase[0x002704F6];
	kmem[0] = 0x07;

	// Patch copyin/copyout: Allow userland + kernel addresses in both params
	// copyin
	kmem = (uint8_t *)&gKernelBase[0x00114947];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x00114953];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// copyout
	kmem = (uint8_t *)&gKernelBase[0x00114852];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x0011485E];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch copyinstr
	kmem = (uint8_t *)&gKernelBase[0x00114DF3];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x00114DFF];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch memcpy stack
	kmem = (uint8_t *)&gKernelBase[0x0011470D];
	kmem[0] = 0xEB;

	// Patch mprotect: Allow RWX (mprotect) mapping
	kmem = (uint8_t *)&gKernelBase[0x00352278];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;
	kmem[3] = 0x90;
	kmem[4] = 0x90;
	kmem[5] = 0x90;
#endif
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Boot/Patches.hpp>

/*
	Please, please, please!
	Keep patches consistent with the used patch style for readability.
	thx: Fire30
*/
void Mira::Boot::Patches::install_prerunPatches_650()
{
#if MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_650
	// NOTE: Only apply patches that the loader requires to run, the rest of them should go into Mira's ELF
	// You must assign the kernel base pointer before anything is done
	if (!gKernelBase)
		return;

	// Enable UART
	kmem = (uint8_t *)&gKernelBase[0x01A6EB18];
	kmem[0] = 0x00;

	// Patch sys_dynlib_dlsym: Allow from anywhere
	kmem = (uint8_t *)&gKernelBase[0x001D85AA];
	kmem[0] = 0xE9;
	kmem[1] = 0xC7;
	kmem[2] = 0x01;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	kmem = (uint8_t *)&gKernelBase[0x00419F20];
	kmem[0] = 0x31;
	kmem[1] = 0xC0;
	kmem[2] = 0xC3;

	// Patch sys_mmap: Allow RWX (read-write-execute) mapping
	kmem = (uint8_t *)&gKernelBase[0x000AB57A];
	kmem[0] = 0x37;
	kmem[3] = 0x37;

	// Patch setuid: Don't run kernel exploit more than once/privilege escalation
	kmem = (uint8_t *)&gKernelBase[0x0010BB20];
	kmem[0] = 0xB8;
	kmem[1] = 0x00;
	kmem[2] = 0x00;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	// Enable RWX (kmem_alloc) mapping
	kmem = (uint8_t *)&gKernelBase[0x00250445];
	kmem[0] = 0x07;

	kmem = (uint8_t *)&gKernelBase[0x00250453];
	kmem[0] = 0x07;

	// Patch copyin/copyout: Allow userland + kernel addresses in both params
	// copyin
	kmem = (uint8_t *)&gKernelBase[0x003C1447];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C1453];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// copyout
	kmem = (uint8_t *)&gKernelBase[0x003C1352];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C135E];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch copyinstr
	kmem = (uint8_t *)&gKernelBase[0x003C18F3];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C18FF];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch memcpy stack
	kmem = (uint8_t *)&gKernelBase[0x003C120D];
	kmem[0] = 0xEB;

	// Patch mprotect: Allow RWX (mprotect) mapping
	kmem = (uint8_t *)&gKernelBase[0x00451A08];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;
	kmem[3] = 0x90;
	kmem[4] = 0x90;
	kmem[5] = 0x90;
#endif
}

repo_name: veiledmerc/mira

license: mit


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Boot/Patches.hpp>

/*
	Please, please, please!
	Keep patches consistent with the used patch style for readability.
*/
void Mira::Boot::Patches::install_prerunPatches_672()
{
#if MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_672
	// NOTE: Only apply patches that the loader requires to run, the rest of them should go into Mira's ELF
	// You must assign the kernel base pointer before anything is done
	if (!gKernelBase)
		return;

	// Use "kmem" for all patches
	uint8_t *kmem;

	// Enable UART
	kmem = (uint8_t *)&gKernelBase[0x01A6EB18];
	kmem[0] = 0x00;

	// Patch sys_dynlib_dlsym: Allow from anywhere
	kmem = (uint8_t *)&gKernelBase[0x001D895A];
	kmem[0] = 0xE9;
	kmem[1] = 0xC7;
	kmem[2] = 0x01;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	kmem = (uint8_t *)&gKernelBase[0x0041A2D0];
	kmem[0] = 0x31;
	kmem[1] = 0xC0;
	kmem[2] = 0xC3;

	// Patch sys_mmap: Allow RWX (read-write-execute) mapping
	kmem = (uint8_t *)&gKernelBase[0x000AB57A];
	kmem[0] = 0x37;
	kmem[3] = 0x37;

	// Patch setuid: Don't run kernel exploit more than once/privilege escalation
	kmem = (uint8_t *)&gKernelBase[0x0010BED0];
	kmem[0] = 0xB8;
	kmem[1] = 0x00;
	kmem[2] = 0x00;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	// Enable RWX (kmem_alloc) mapping
	kmem = (uint8_t *)&gKernelBase[0x002507F5];
	kmem[0] = 0x07;

	kmem = (uint8_t *)&gKernelBase[0x00250803];
	kmem[0] = 0x07;

	// Patch copyin/copyout: Allow userland + kernel addresses in both params
	// copyin
	kmem = (uint8_t *)&gKernelBase[0x003C17F7];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C1803];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// copyout
	kmem = (uint8_t *)&gKernelBase[0x003C1702];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C170E];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch copyinstr
	kmem = (uint8_t *)&gKernelBase[0x003C1CA3];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x003C1CAF];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch memcpy stack
	kmem = (uint8_t *)&gKernelBase[0x003C15BD];
	kmem[0] = 0xEB;

	// Patch mprotect: Allow RWX (mprotect) mapping
	kmem = (uint8_t *)&gKernelBase[0x00451DB8];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;
	kmem[3] = 0x90;
	kmem[4] = 0x90;
	kmem[5] = 0x90;
#endif
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Boot/Patches.hpp>

/*
	Please, please, please!
	Keep patches consistent with the used patch style for readability.
*/
void Mira::Boot::Patches::install_prerunPatches_900()
{
#if MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_900
	// NOTE: Only apply patches that the loader requires to run, the rest of them should go into Mira's ELF
	// You must assign the kernel base pointer before anything is done
	if (!gKernelBase)
		return;

	// Use "kmem" for all patches
	uint8_t *kmem;

	// Enable UART
	kmem = (uint8_t *)&gKernelBase[0x0152BF5D];
	kmem[0] = 0x00;
	kmem[1] = 0x00;
	kmem[2] = 0x00;
	kmem[3] = 0x00;

	// Patch sys_dynlib_dlsym: Allow from anywhere
	kmem = (uint8_t *)&gKernelBase[0x0023B67F];
	kmem[0] = 0xEB;
	kmem[1] = 0x4C;

	kmem = (uint8_t *)&gKernelBase[0x00221B40];
	kmem[0] = 0x31;
	kmem[1] = 0xC0;
	kmem[2] = 0xC3;

	// Patch sys_mmap: Allow RWX (read-write-execute) mapping
	kmem = (uint8_t *)&gKernelBase[0x0016632A];
	kmem[0] = 0x37;
	kmem[3] = 0x37;

	// Patch setuid: Don't run kernel exploit more than once/privilege escalation
	kmem = (uint8_t *)&gKernelBase[0x000019FF];
	kmem[0] = 0xB8;
	kmem[1] = 0x00;
	kmem[2] = 0x00;
	kmem[3] = 0x00;
	kmem[4] = 0x00;

	// Enable RWX (kmem_alloc) mapping
	kmem = (uint8_t *)&gKernelBase[0x0037BF3C];
	kmem[0] = 0x07;

	kmem = (uint8_t *)&gKernelBase[0x0037BF44];
	kmem[0] = 0x07;

	// Patch copyin/copyout: Allow userland + kernel addresses in both params
	// copyin
	kmem = (uint8_t *)&gKernelBase[0x002716F7];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x00271703];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// copyout
	kmem = (uint8_t *)&gKernelBase[0x00271602];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x0027160E];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	// Patch copyinstr
	kmem = (uint8_t *)&gKernelBase[0x00271BA3];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x00271BAF];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;

	kmem = (uint8_t *)&gKernelBase[0x00271BE0];
	kmem[0] = 0x90;
	kmem[1] = 0x90;

	// Patch memcpy stack
	kmem = (uint8_t *)&gKernelBase[0x002714BD];
	kmem[0] = 0xEB;

	// Patch mprotect: Allow RWX (mprotect) mapping
	kmem = (uint8_t *)&gKernelBase[0x00080B8B];
	kmem[0] = 0x90;
	kmem[1] = 0x90;
	kmem[2] = 0x90;
	kmem[3] = 0x90;
	kmem[4] = 0x90;
	kmem[5] = 0x90;
#endif
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

extern "C"
{
	#include <Utils/_Syscall.hpp>
};

#include <Utils/New.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/Kernel.hpp>
#include <Boot/Patches.hpp>
#include <Boot/InitParams.hpp>

#include <Utils/Dynlib.hpp>
#include <Utils/Logger.hpp>

#include <sys/elf64.h>
#include <sys/socket.h>
#include <sys/proc.h>
#include <sys/unistd.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/imgact.h>
#include <sys/filedesc.h>
#include <sys/malloc.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_param.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <netinet/in.h>

#include <Boot/MiraLoader.hpp>

using namespace Mira::Utils;
using namespace MiraLoader;

#define ALLOC_3MB	0x300000
#define ALLOC_5MB	0x500000

uint8_t* gKernelBase = nullptr;

struct kexec_uap
{
	void* func;
	void* arg0;
};

int(*sceNetSocket)(const char *, int, int, int) = nullptr;
int(*sceNetSocketClose)(int) = nullptr;
int(*sceNetBind)(int, struct sockaddr *, int) = nullptr;
int(*sceNetListen)(int, int) = nullptr;
int(*sceNetAccept)(int, struct sockaddr *, unsigned int *) = nullptr;
int(*sceNetRecv)(int, void *, size_t, int) = nullptr;

int(*snprintf)(char *str, size_t size, const char *format, ...) = nullptr;

void miraloader_kernelInitialization(struct thread* td, struct kexec_uap* uap);

void WriteNotificationLog(const char* text)
{
	if (!text)
		return;

	// Load the sysutil module, needs to be rooted for this to work
	int32_t moduleId = -1;
	Dynlib::LoadPrx("/system/common/lib/libSceSysUtil.sprx", &moduleId);

	// Validate that the module loaded properly
	if (moduleId == -1)
		return;

	int(*sceSysUtilSendSystemNotificationWithText)(int messageType, const char* message) = NULL;

	// Resolve the symbol
	Dynlib::Dlsym(moduleId, "sceSysUtilSendSystemNotificationWithText", &sceSysUtilSendSystemNotificationWithText);

	if (sceSysUtilSendSystemNotificationWithText)
		sceSysUtilSendSystemNotificationWithText(222, text);

	Dynlib::UnloadPrx(moduleId);
}

void mira_escape(struct thread* td, void* uap)
{
	gKernelBase = (uint8_t*)kernelRdmsr(0xC0000082) - kdlsym_addr_Xfast_syscall;
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);

	printf("[+] mira_escape\n");

	struct ucred* cred = td->td_proc->p_ucred;
	struct filedesc* fd = td->td_proc->p_fd;

	cred->cr_uid = 0;
	cred->cr_ruid = 0;
	cred->cr_rgid = 0;
	cred->cr_groups[0] = 0;

	cred->cr_prison = *(struct prison**)kdlsym(prison0);
	fd->fd_rdir = fd->fd_jdir = *(struct vnode**)kdlsym(rootvnode);

	// set diag auth ID flags
	td->td_ucred->cr_sceAuthID = SceAuthenticationId_t::Decid;

	// make system credentials
	td->td_ucred->cr_sceCaps[0] = SceCapabilities_t::Max;
	td->td_ucred->cr_sceCaps[1] = SceCapabilities_t::Max;

	// Apply patches
	cpu_disable_wp();

	Mira::Boot::Patches::install_prePatches();

	cpu_enable_wp();

	printf("[-] mira_escape\n");
}

extern "C" void* mira_entry(void* args)
{
	// Escape the jail and sandbox
	syscall2(KEXEC_SYSCALL_NUM, reinterpret_cast<void*>(mira_escape), NULL);

	//int32_t sysUtilModuleId = -1;
	int32_t netModuleId = -1;
	int32_t libcModuleId = -1;
	//int32_t libKernelWebModuleId = -1;

	{
		Dynlib::LoadPrx("libSceLibcInternal.sprx", &libcModuleId);

		Dynlib::Dlsym(libcModuleId, "snprintf", &snprintf);

	}

	// Networking resolving
	{
		Dynlib::LoadPrx("libSceNet.sprx", &netModuleId);

		Dynlib::Dlsym(netModuleId, "sceNetSocket", &sceNetSocket);
		Dynlib::Dlsym(netModuleId, "sceNetSocketClose", &sceNetSocketClose);
		Dynlib::Dlsym(netModuleId, "sceNetBind", &sceNetBind);
		Dynlib::Dlsym(netModuleId, "sceNetListen", &sceNetListen);
		Dynlib::Dlsym(netModuleId, "sceNetAccept", &sceNetAccept);
		Dynlib::Dlsym(netModuleId, "sceNetRecv", &sceNetRecv);
	}

	// Allocate a 3MB buffer
	size_t bufferSize = ALLOC_5MB;
	uint8_t* buffer = (uint8_t*)_mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
	if (!buffer)
	{
		WriteNotificationLog("could not allocate 5MB buffer");
		return nullptr;
	}
	memset(buffer, 0, bufferSize);
	//Loader::Memset(buffer, 0, bufferSize);
	// Hold our server socket address
	struct sockaddr_in serverAddress = { 0 };
	//Loader::Memset(&serverAddress, 0, sizeof(serverAddress));

	// Listen on port 9021
	serverAddress.sin_len = sizeof(serverAddress);
	serverAddress.sin_addr.s_addr = INADDR_ANY;
	serverAddress.sin_port = __bswap16(9021); // port 9021
	serverAddress.sin_family = AF_INET;

	// Create a new socket
	int32_t serverSocket = sceNetSocket("miraldr", AF_INET, SOCK_STREAM, 0);
	if (serverSocket < 0)
	{
		WriteNotificationLog("socket error");
		return nullptr;
	}

	// Bind to localhost
	int32_t result = sceNetBind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
	if (result < 0)
	{
		WriteNotificationLog("bind error");
		return nullptr;
	}

	// Listen
	result = sceNetListen(serverSocket, 10);
	if (result < 0)
	{
		WriteNotificationLog("listen error");
		return nullptr;
	}

	WriteNotificationLog("waiting for clients");

	// Wait for a client to send something
	int32_t clientSocket = sceNetAccept(serverSocket, nullptr, nullptr);
	if (clientSocket < 0)
	{
		WriteNotificationLog("accept errror");
		return nullptr;
	}

	int32_t currentSize = 0;
	int32_t recvSize = 0;

	// Recv one byte at a time until we get our buffer
	while ((recvSize = sceNetRecv(clientSocket, buffer + currentSize, bufferSize - currentSize, 0)) > 0)
		currentSize += recvSize;

	// Close the client and server socket connections
	sceNetSocketClose(clientSocket);
	sceNetSocketClose(serverSocket);

	// Determine if we launch a elf or a payload
	if (buffer[0] == ELFMAG0 &&
		buffer[1] == ELFMAG1 &&
		buffer[2] == ELFMAG2 &&
		buffer[3] == ELFMAG3) // 0x7F 'ELF'
	{

		// TODO: Check for custom Mira elf section to determine launch type
		char buf[64];
		memset(buf, 0, sizeof(buf));

		snprintf(buf, sizeof(buf), "elf: %p elfSize: %llx", buffer, currentSize);
		WriteNotificationLog(buf);

		uint8_t isLaunchingKernel = true;
		if (isLaunchingKernel)
		{
			Mira::Boot::InitParams initParams = { 0 };
			initParams.isElf = true;
			initParams.isRunning = false;
			initParams.payloadBase = (uint64_t)buffer;
			initParams.payloadSize = currentSize;
			initParams.process = nullptr;
			initParams.elfLoader = nullptr;
			initParams.entrypoint = nullptr;

			syscall2(KEXEC_SYSCALL_NUM, reinterpret_cast<void*>(miraloader_kernelInitialization), reinterpret_cast<void*>(&initParams));
		}
		else
		{
			// Launch ELF
			// TODO: Check/Add a flag to the elf that determines if this is a kernel or userland elf
			// Loader(const void* p_Elf, uint32_t p_ElfSize, ElfLoaderType_t p_Type);
			MiraLoader::Loader loader(buffer, currentSize, ElfLoaderType_t::UserProc);

			auto entryPoint = reinterpret_cast<void(*)(void*)>(loader.GetEntrypoint());
			if (!entryPoint)
			{
				WriteNotificationLog("could not find entry point");
				return NULL;
			}

			// Launch userland
			entryPoint(nullptr);
		}
	}
	else
	{
		// Launch Userland Payload
		WriteNotificationLog("launching payload");

		void(*payload_start)() = (void(*)())buffer;
		payload_start();
	}

	return nullptr;
}

void miraloader_kernelInitialization(struct thread* td, struct kexec_uap* uap)
{
	// If we do not have a valid parameter passed, kick back
	if (!uap || !uap->arg0)
		return;

	Mira::Boot::InitParams* userInitParams = reinterpret_cast<Mira::Boot::InitParams*>(uap->arg0);

	// Thread should already be escaped from earlier

	// Fill the kernel base address
	gKernelBase = (uint8_t*)kernelRdmsr(0xC0000082) - kdlsym_addr_Xfast_syscall;
	//void(*critical_enter)(void) = kdlsym(critical_enter);
	//void(*crtical_exit)(void) = kdlsym(critical_exit);
	auto kmem_alloc = (vm_offset_t(*)(vm_map_t map, vm_size_t size))kdlsym(kmem_alloc);
	auto kmem_free = (void(*)(void* map, void* addr, size_t size))kdlsym(kmem_free);
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);
	auto kproc_create = (int(*)(void(*func)(void*), void* arg, struct proc** newpp, int flags, int pages, const char* fmt, ...))kdlsym(kproc_create);
	vm_map_t map = (vm_map_t)(*(uint64_t *)(kdlsym(kernel_map)));
	//auto memset = (void* (*)(void *s, int c, size_t n))kdlsym(memset);
	auto copyin = (int(*)(const void* uaddr, void* kaddr, size_t len))kdlsym(copyin);
	auto kthread_exit = (void(*)(void))kdlsym(kthread_exit);

	// Allocate a new logger for the MiraLoader
	auto s_Logger = Mira::Utils::Logger::GetInstance();
	if (!s_Logger)
	{
		printf("[-] could not allocate logger\n");
		kthread_exit();
		return;
	}
	printf("logger created\n");

	// Create launch parameters, this is floating in "free kernel space" so the other process should
	// be able to grab and use the pointer directly
	Mira::Boot::InitParams* initParams = (Mira::Boot::InitParams*)kmem_alloc(map, sizeof(Mira::Boot::InitParams));
	if (!initParams)
	{
		WriteLog(LL_Error, "could not allocate initialization parameters.\n");
		return;
	}
	memset(initParams, 0, sizeof(*initParams));

	// Copyin our new arguments from userland
	int copyResult = copyin(userInitParams, initParams, sizeof(*initParams));
	if (copyResult != 0)
	{
		kmem_free(map, initParams, sizeof(*initParams));
		WriteLog(LL_Error, "could not copyin initalization parameters (%d)\n", copyResult);
		return;
	}

	// initparams are read from the uap in this syscall func
	uint64_t payloadSize = initParams->payloadSize;
	uint64_t payloadBase = initParams->payloadBase;

	// Allocate some memory
	uint8_t* kernelElf = (uint8_t*)kmem_alloc(map, payloadSize);
	if (!kernelElf)
	{
		// Free the previously allocated initialization parameters
		kmem_free(map, initParams, sizeof(*initParams));
		WriteLog(LL_Error, "could not allocate kernel payload.\n");
		return;
	}
	memset(kernelElf, 0, payloadSize);
	WriteLog(LL_Debug, "payloadBase: %p payloadSize: %llx kernelElf: %p\n", payloadBase, payloadSize, kernelElf);

	// Copy the ELF data from userland
	copyResult = copyin((const void*)payloadBase, kernelElf, payloadSize);
	if (copyResult != 0)
	{
		// Intentionally blow the fuck up
		WriteLog(LL_Error, "fuck, this is bad...\n");
		for (;;)
			__asm__("nop");
	}

	WriteLog(LL_Debug, "finished allocating and copying ELF from userland");

	// Determine if we launch a elf or a payload
	uint32_t magic = *(uint32_t*)kernelElf;
	if (magic != 0x464C457F)
	{
		printf("invalid elf header.\n");
		return;
	}
	WriteLog(LL_Debug, "elf header: %X\n", magic);

	// Launch ELF
	MiraLoader::Loader* loader = new MiraLoader::Loader(kernelElf, payloadSize, ElfLoaderType_t::KernelProc); //malloc(sizeof(ElfLoader_t), M_LINKER, M_WAITOK);
	if (!loader)
	{
		printf("could not allocate loader\n");
		return;
	}

	// Update the loader
	initParams->elfLoader = loader;
	initParams->entrypoint = reinterpret_cast<void(*)(void*)>(loader->GetEntrypoint());
	initParams->allocatedBase = reinterpret_cast<uint64_t>(loader->GetAllocatedMap());
	initParams->payloadBase = reinterpret_cast<uint64_t>(kernelElf);
	initParams->payloadSize = payloadSize;

	// Update the initial running state
	initParams->isRunning = false;

	auto s_EntryPoint = initParams->entrypoint;
	if (s_EntryPoint != nullptr)
	{
		printf("[+]entrypoint: %p", s_EntryPoint);
		(void)kproc_create(s_EntryPoint, initParams, &initParams->process, 0, 200, "miraldr2"); // 8MiB stack
	}
	else
	{
		printf("[-]could not get entry point.\n");
	}
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include "MiraLoader.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#else
#include <Boot/MiraLoader.hpp>
//#include <Utils/New.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/_Syscall.hpp>
#include <Utils/Kernel.hpp>

#include <sys/mman.h>
using namespace MiraLoader;

extern void WriteNotificationLog(const char* text);
#endif

/* values for type */
#define ELF_RELOC_REL	1
#define ELF_RELOC_RELA	2

// https://github.com/lattera/freebsd/blob/master/sys/kern/kern_linker.c
// https://github.com/lattera/freebsd/blob/401a161083850a9a4ce916f37520c084cff1543b/sys/amd64/amd64/elf_machdep.c
// https://github.com/lattera/freebsd/blob/master/sys/kern/link_elf.c
// https://github.com/lattera/freebsd/tree/master/include
// https://github.com/OpenOrbis/mira-project/blob/master/Firmware/MiraFW/src/mira/plugins/debugger/debugger_plugin.c

Loader::Loader(const void* p_Elf, uint32_t p_ElfSize, ElfLoaderType_t p_Type) :
	m_SourceElf(p_Elf),
	m_SourceElfSize(p_ElfSize),
	m_LoaderType(p_Type),
	m_SourceHeader(nullptr),
	m_SourceProgramHeadersStart(nullptr),
	m_LoadableSegments{ 0 },
	m_LoadableSegmentsCount(0),
	m_SourcePhphdr(nullptr),
	m_SourceDynamicProgramHeader(nullptr),
	m_SourceInterpreterProgramHeader(nullptr),
	m_AllocatedMap(nullptr),
	m_AllocatedMapSize(0),
	m_Dynamic(nullptr),
	m_BucketsCount(0),
	m_ChainsCount(0),
	m_Buckets(nullptr),
	m_Chains(nullptr),
	m_StringTable(nullptr),
	m_StringTableSize(0),
	m_SymbolTable(nullptr),
	m_GlobalOffsetTable(nullptr),
	m_Rel(nullptr),
	m_RelSize(0),
	m_PltRel(nullptr),
	m_PltRelSize(0),
	m_PltRela(nullptr),
	m_PltRelaSize(0),
	m_Rela(nullptr),
	m_RelaSize(0),
	m_Debug(0),
	m_DdbSymbolTable(nullptr),
	m_DdbSymbolCount(0),
	m_DdbStringTable(nullptr),
	m_DdbStringCount(0),
	m_PcpuStart(0),
	m_PcpuStop(0),
	m_PcpuBase(0),
	m_SymbolTableBase(0),
	m_SymbolTableSize(0),
	m_StringTableBase(0),
	m_StringTableBaseSize(0),
	m_Entrypoint(nullptr)
{
	if (!Load())
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not load properly\n");
		}
		else
			WriteNotificationLog("err: could not load properly\n");
	}

}

Loader::~Loader()
{
}

#ifdef _WIN32
void Loader::DumpLoadedElf(const char * p_Path)
{
	FILE* s_Handle = nullptr;
	auto s_Ret = fopen_s(&s_Handle, p_Path, "wb");
	if (s_Ret != ERROR_SUCCESS)
	{
		printf("err: could not write output file (%s) (0x%x)\n", p_Path, s_Ret);
		return;
	}

	if (fwrite(m_AllocatedMap, sizeof(uint8_t), m_AllocatedMapSize, s_Handle) < 0)
	{
		fclose(s_Handle);
		s_Handle = nullptr;

		printf("err: could not write output file\n");
		return;
	}

	fclose(s_Handle);
	s_Handle = nullptr;

	printf("info: elf dumped successfully\n");
}
#endif

bool Loader::ElfReloc(Elf64_Addr p_RelocationBase, const void * p_Data, int32_t p_RelocationType)
{
	return ElfRelocInternal(p_RelocationBase, p_Data, p_RelocationType, false);
}

bool Loader::ElfRelocLocal(Elf64_Addr p_RelocationBase, const void * p_Data, int32_t p_RelocationType)
{
	return ElfRelocInternal(p_RelocationBase, p_Data, p_RelocationType, true);
}

bool Loader::ElfRelocInternal(Elf64_Addr p_RelocationBase, const void * p_Data, int32_t p_RelocationType, bool p_Local)
{
	Elf64_Addr* s_Where, s_Val;
	uint32_t* s_Where32, s_Val32;
	Elf64_Addr s_Addr;
	Elf64_Addr s_Addend;
	Elf64_Size s_Type, s_SymbolIndex;

	const Elf64_Rel* s_Rel;
	const Elf64_Rela* s_Rela;

	switch (p_RelocationType)
	{
	case ELF_RELOC_REL:
	{
		s_Rel = static_cast<const Elf64_Rel*>(p_Data);
		s_Where = reinterpret_cast<Elf64_Addr*>(p_RelocationBase + s_Rel->r_offset);

		s_Type = ELF64_R_TYPE(s_Rel->r_info);
		s_SymbolIndex = ELF64_R_SYM(s_Rel->r_info);

		switch (s_Type)
		{
			/* Addend is 32 bit on 32 bit relocs */
		case R_X86_64_PC32:
		case R_X86_64_32S:
			s_Addend = *(Elf64_Addr*)s_Where;
			break;
		default:
			s_Addend = *s_Where;
			break;
		}
		break;
	}
	case ELF_RELOC_RELA:
	{
		s_Rela = static_cast<const Elf64_Rela*>(p_Data);
		s_Where = (Elf64_Addr*)(p_RelocationBase + s_Rela->r_offset);
		s_Addend = s_Rela->r_addend;
		s_Type = ELF64_R_TYPE(s_Rela->r_info);
		s_SymbolIndex = ELF64_R_SYM(s_Rela->r_info);
		break;
	}
	default:
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: unknown reloc type %d\n", p_RelocationType);
		}
		else
			WriteNotificationLog("err: unknown reloc type\n");

		return false;
	}

	switch (s_Type)
	{
	case R_X86_64_NONE:	/* none */
		break;

	case R_X86_64_64:		/* S + A */
		s_Addr = Lookup(s_SymbolIndex, true);
		s_Val = s_Addr + s_Addend;
		if (s_Addr == 0)
			return false;
		if (*s_Where != s_Val)
			*s_Where = s_Val;
		break;

	case R_X86_64_PC32:	/* S + A - P */
		s_Addr = Lookup(s_SymbolIndex, true);
		s_Where32 = (uint32_t *)s_Where;
		s_Val32 = (uint32_t)(s_Addr + s_Addend - (Elf64_Addr)s_Where);
		if (s_Addr == 0)
			return false;
		if (*s_Where32 != s_Val32)
			*s_Where32 = s_Val32;
		break;

	case R_X86_64_32S:	/* S + A sign extend */
		s_Addr = Lookup(s_SymbolIndex, true);
		s_Val32 = (uint32_t)(s_Addr + s_Addend);
		s_Where32 = (uint32_t *)s_Where;
		if (s_Addr == 0)
			return false;
		if (*s_Where32 != s_Val32)
			*s_Where32 = s_Val32;
		break;

	case R_X86_64_COPY:	/* none */
		/*
		 * There shouldn't be copy relocations in kernel
		 * objects.
		 */

		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("kldload: unexpected R_COPY relocation\n");
		}
		else
			WriteNotificationLog("kldload: unexpected R_COPY relocation\n");

		return false;

	case R_X86_64_GLOB_DAT:	/* S */
	case R_X86_64_JMP_SLOT:	/* XXX need addend + offset */
		s_Addr = Lookup(s_SymbolIndex, true);
		if (s_Addr == 0)
			return false;
		if (*s_Where != s_Addr)
			*s_Where = s_Addr;
		break;

	case R_X86_64_RELATIVE:	/* B + A */
		s_Addr = p_RelocationBase + s_Addend;
		s_Val = s_Addr;
		if (*s_Where != s_Val)
			*s_Where = s_Val;
		break;

	default:
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("kldload: unexpected relocation type %lld\n",
			s_Type);
		}
		else
			WriteNotificationLog("kldload: unexpected relocation type\n");

		return false;
	}

	return true;
}

Elf64_Addr Loader::Lookup(Elf64_Size p_SymbolIndex, bool p_CheckDependencies)
{
	if (p_SymbolIndex >= m_ChainsCount)
		return 0;

	const Elf64_Sym* s_Symbol = m_SymbolTable + p_SymbolIndex;
	const char* s_SymbolName = "";

	// Skip doing a full lookup when the symbol is local, it may even fail because it may not be found through hash tables
	if (ELF64_ST_BIND(s_Symbol->st_info) == STB_LOCAL)
	{
		// FreeBSD dev's are smoking crack wtf
		if (s_Symbol->st_shndx == SHN_UNDEF || s_Symbol->st_value == 0)
			return 0;
		return reinterpret_cast<Elf64_Addr>(m_AllocatedMap) + s_Symbol->st_value;
	}

	s_SymbolName = m_StringTable + s_Symbol->st_name;
	if (*s_SymbolName == 0)
		return 0;

	// TODO: Implement
	//const auto s_Symbol2 = LinkerFileLookupSymbol(s_SymbolName, p_CheckDependencies);

	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("err: Lookup not implemented\n");
	}
	else
		WriteNotificationLog("err: Lookup not implemented\n");
	return 0;
}

Elf64_Addr Loader::LinkerFileLookupSymbol(const char * p_Name, bool p_CheckDependencies)
{
	// TODO: Implement
	//Elf64_Sym* s_Symbol = LinkerFileLookupSymbolInternal(p_Name, true);

	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("err: LinkerFileLookupSymbol not implemented\n");
	}
	else
		WriteNotificationLog("err: LinkerFileLookupSymbol not implemented\n");
	return 0;
}

Elf64_Sym* Loader::LinkerFileLookupSymbolInternal(const char * p_Name, bool p_CheckDependencies)
{
	if (m_Buckets == nullptr || m_BucketsCount == 0)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: LinkerFileLookupSymbolInternal: missing symbol hash table\n");
		}
		else
			WriteNotificationLog("err: LinkerFileLookupSymbolInternal: missing symbol hash table\n");

		return nullptr;
	}

	const Elf64_Sym* s_Symp = nullptr;
	const char* s_Strp = "";
	auto s_Hash = Hash(p_Name);
	auto s_SymbolNum = m_Buckets[s_Hash % m_BucketsCount];

	while (s_SymbolNum != STN_UNDEF)
	{
		if (s_SymbolNum >= m_ChainsCount)
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: corrupt symbol table\n");
			}
			else
				WriteNotificationLog("err: corrupt symbol table\n");
			return nullptr;
		}

		s_Symp = m_SymbolTable + s_SymbolNum;
		if (s_Symp->st_name == 0)
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: corrupt symbol table\n");
			}
			else
				WriteNotificationLog("err: corrupt symbol table\n");
			return nullptr;
		}

		s_Strp = m_StringTable + s_Symp->st_name;
		if (Strcmp(p_Name, s_Strp) == 0)
		{
			if (s_Symp->st_shndx != SHN_UNDEF ||
				(s_Symp->st_value != 0 && ELF64_ST_TYPE(s_Symp->st_info) == STT_FUNC))
			{
				return const_cast<Elf64_Sym*>(s_Symp);
			}

			return nullptr;
		}

		s_SymbolNum = m_Chains[s_SymbolNum];
	}

	// If we have not found it, look at the full table (if loaded)
	if (m_SymbolTable == m_DdbSymbolTable)
		return nullptr;

	// Exhaustive search

	for (auto i = 0; i < m_DdbSymbolCount; i++)
	{
		s_Symp = m_DdbSymbolTable + i;
		s_Strp = m_DdbStringTable + s_Symp->st_name;
		if (Strcmp(p_Name, s_Strp) == 0)
		{
			if (s_Symp->st_shndx != SHN_UNDEF ||
				(s_Symp->st_value != 0 && ELF64_ST_TYPE(s_Symp->st_info) == STT_FUNC))
			{
				return const_cast<Elf64_Sym*>(s_Symp);
			}
			return nullptr;
		}
	}

	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("err: symbol not found\n");
	}
	else
		WriteNotificationLog("err: symbol not found\n");
	return nullptr;
}

bool Loader::RelocateFile()
{
	const Elf64_Rel* s_RelLimit = nullptr;
	const Elf64_Rel* s_Rel = nullptr;
	const Elf64_Rela* s_RelaLimit = nullptr;
	const Elf64_Rela* s_Rela = nullptr;

	// Perform relocations without addend if there are any
	if ((s_Rel = m_Rel) != nullptr)
	{
		s_RelLimit = reinterpret_cast<const Elf64_Rel*>(reinterpret_cast<const uint8_t*>(m_Rel) + m_RelSize);
		while (s_Rel < s_RelLimit)
		{
			if (!ElfReloc(reinterpret_cast<Elf64_Addr>(m_AllocatedMap), s_Rel, ELF_RELOC_REL))
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("warn: could not elf reloc local for rel's\n");
				}
				else
					WriteNotificationLog("warn: could not elf reloc local for rel's\n");
			}

			s_Rel++;
		}
	}

	// Perform relocations with addend if there are any
	if ((s_Rela = m_Rela) != nullptr)
	{
		s_RelaLimit = reinterpret_cast<const Elf64_Rela*>(reinterpret_cast<const uint8_t*>(m_Rela) + m_RelaSize);
		while (s_Rela < s_RelaLimit)
		{
			if (!ElfReloc(reinterpret_cast<Elf64_Addr>(m_AllocatedMap), s_Rela, ELF_RELOC_RELA))
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("warn: could not elf reloc local for rela's\n");
				}
				else
					WriteNotificationLog("warn: could not elf reloc local for rela's\n");
			}

			s_Rela++;
		}
	}

	return true;
}

bool Loader::SetProtection(void * p_Address, Elf64_Xword p_Size, Elf64_Word p_Protection)
{
	if (p_Address == nullptr)
		return false;

#ifdef _WIN32
	DWORD s_Error = ERROR_SUCCESS;
	DWORD s_OldProtection = 0;
	if (!VirtualProtect(p_Address, p_Size, p_Protection, &s_OldProtection))
	{
		s_Error = GetLastError();
		printf("err: VirtualProtect failed (%d)\n", s_Error);
		return false;
	}
#else
	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("pmap_protect: %p %llx %x\n", p_Address, p_Size, p_Protection);

		/*auto pmap_protect = (void(*)(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t))kdlsym(pmap_protect);

		// TODO: pmap_protect
		uint64_t s_StartAddress = ((uint64_t)p_Address) & ~(uint64_t)(PAGE_SIZE - 1);
		uint64_t s_EndAddress = ((uint64_t)p_Address + p_Size + PAGE_SIZE - 1) & ~(uint64_t)(PAGE_SIZE - 1);

		WriteLog(LL_Debug, "pmap_protect: %p %llx %x", p_Address, p_Size, p_Protection);*/
	}
	else if (m_LoaderType == ElfLoaderType_t::UserProc ||
		m_LoaderType == ElfLoaderType_t::UserTrainer)
	{
		if ((int64_t)syscall3(SYS_MPROTECT, p_Address, (void*)p_Size, (void*)(int64_t)p_Protection) < 0)
		{
			WriteNotificationLog("mprotect error\n");
			return false;
		}
	}
#endif

	return true;
}

bool Loader::ParseDynamic()
{
	if (m_Dynamic == nullptr)
		return false;

	auto s_PltType = DT_REL;

	// TODO: More bounds checking
	for (Elf64_Dyn* l_Dynamic = m_Dynamic; l_Dynamic->d_tag != DT_NULL; l_Dynamic++)
	{
		switch (l_Dynamic->d_tag)
		{
		case DT_HASH:
		{
			// Stole from: src/libexec/rtld-elf/rtld.c
			const Elf64_Hashelt* l_HashTable = reinterpret_cast<const Elf64_Hashelt*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);

			m_BucketsCount = l_HashTable[0];
			m_ChainsCount = l_HashTable[1];
			m_Buckets = l_HashTable + 2;
			m_Chains = m_Buckets + m_BucketsCount;
			break;
		}
		case DT_STRTAB:
			m_StringTable = static_cast<char*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr;
			break;
		case DT_STRSZ:
			m_StringTableSize = l_Dynamic->d_un.d_val;
			break;
		case DT_SYMTAB:
			m_SymbolTable = reinterpret_cast<const Elf64_Sym*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);
			break;
		case DT_SYMENT:
			if (l_Dynamic->d_un.d_val != sizeof(Elf64_Sym))
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("err: elf64_sym size isn't correct in this elf wtf\n");
				}
				else
					WriteNotificationLog("err: elf64_sym size isn't correct in this elf wtf\n");

				return false;
			}
			break;
		case DT_PLTGOT:
			m_GlobalOffsetTable = reinterpret_cast<const Elf64_Addr*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);
			break;
		case DT_REL:
			m_Rel = reinterpret_cast<const Elf64_Rel*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);
			break;
		case DT_RELSZ:
			m_RelSize = l_Dynamic->d_un.d_val;
			break;
		case DT_RELENT:
			if (l_Dynamic->d_un.d_val != sizeof(Elf64_Rel))
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("err: elf64_rel size isn't the correct size in this elf\n");
				}
				else
					WriteNotificationLog("err: elf64_rel size isn't the correct size in this elf\n");
				return false;
			}
			break;
		case DT_JMPREL:
			m_PltRel = reinterpret_cast<const Elf64_Rel*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);
			break;
		case DT_PLTRELSZ:
			m_PltRelSize = l_Dynamic->d_un.d_val;
			break;
		case DT_RELA:
			m_Rela = reinterpret_cast<const Elf64_Rela*>(static_cast<uint8_t*>(m_AllocatedMap) + l_Dynamic->d_un.d_ptr);
			break;
		case DT_RELASZ:
			m_RelaSize = l_Dynamic->d_un.d_val;
			break;
		case DT_RELAENT:
			if (l_Dynamic->d_un.d_val != sizeof(Elf64_Rela))
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("err: elf64_rela size isn't the correct size in this elf\n");
				}
				else
					WriteNotificationLog("err: elf64_rela size isn't the correct size in this elf\n");
				return false;
			}
			break;
		case DT_PLTREL:
			s_PltType = static_cast<int>(l_Dynamic->d_un.d_val);
			if (s_PltType != DT_REL ||
				s_PltType != DT_RELA)
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("err: pltrel is not DT_REL or DT_RELA\n");
				}
				else
					WriteNotificationLog("err: pltrel is not DT_REL or DT_RELA\n");
				return false;
			}
			break;
		case DT_DEBUG:
			// TODO: Implement if needed
			break;
		}
	}

	// Switch over if we are a rela type
	if (s_PltType == DT_RELA)
	{
		m_PltRela = reinterpret_cast<const Elf64_Rela*>(m_PltRel);
		m_PltRel = nullptr;
		m_PltRelaSize = m_PltRelSize;
		m_PltRelSize = 0;
	}

	m_DdbSymbolTable = m_SymbolTable;
	m_DdbSymbolCount = m_ChainsCount;
	m_DdbStringTable = m_StringTable;
	m_DdbStringCount = m_StringTableSize;

	return true;
}

bool Loader::ParseDpcpu()
{
	m_Debug = 0;
	m_PcpuBase = 0;
	m_PcpuStart = 0;
	m_PcpuStop = 0;

	// TODO: Implement
	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("warn: ParseDpcpu not implemented.\n");
	}
	else
		WriteNotificationLog("warn: ParseDpcpu not implemented.\n");
	return true;
}

const Elf64_Phdr * Loader::GetProgramHeaderByIndex(uint32_t p_Index)
{
	if (m_SourceHeader == nullptr)
		return nullptr;

	if (p_Index >= m_SourceHeader->e_phnum)
		return nullptr;

	return m_SourceProgramHeadersStart + p_Index;
}

void* k_malloc(size_t size)
{
	if (!size)
		size = sizeof(uint64_t);

	auto kmem_alloc = (vm_offset_t(*)(vm_map_t map, vm_size_t size))kdlsym(kmem_alloc);
	vm_map_t map = (vm_map_t)(*(uint64_t *)(kdlsym(kernel_map)));

	uint8_t* data = (uint8_t*)kmem_alloc(map, size);
	if (!data)
		return NULL;

	// Set our pointer header
	(*(uint64_t*)data) = size;

	// Return the start of the requested data
	return data + sizeof(uint64_t);
}

void k_free(void* address)
{
	if (!address)
		return;

	vm_map_t map = (vm_map_t)(*(uint64_t *)(kdlsym(kernel_map)));
	auto kmem_free = (void(*)(void* map, void* addr, size_t size))kdlsym(kmem_free);

	uint8_t* data = ((uint8_t*)address) - sizeof(uint64_t);

	uint64_t size = *(uint64_t*)data;

	kmem_free(map, data, size);
}

void * Loader::Allocate(Elf64_Xword p_Size)
{
	// Validate that loader type is valid
	if (m_LoaderType <= ElfLoaderType_t::Invalid ||
		m_LoaderType >= ElfLoaderType_t::MAX)
		return nullptr;

#ifdef _WIN32
	auto s_Allocation = malloc(p_Size);
	if (s_Allocation == nullptr)
		return nullptr;

	// Zero allocation
	memset(s_Allocation, 0, p_Size);
	return s_Allocation;
#else
	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		//auto kmem_alloc = (vm_offset_t(*)(vm_map_t map, vm_size_t size))kdlsym(kmem_alloc);
		//vm_map_t map = (vm_map_t)(*(uint64_t *)(kdlsym(kernel_map)));

		void* s_Allocation = k_malloc(p_Size); //(void*)kmem_alloc(map, p_Size);

		//auto s_Allocation = new uint8_t[p_Size];

		// Zero allocation
		memset(s_Allocation, 0, p_Size);

		// return happy
		return s_Allocation;
	}
	else if (m_LoaderType == ElfLoaderType_t::UserProc ||
		m_LoaderType == ElfLoaderType_t::UserTrainer)
	{
		// Get allocation
		auto s_AllocationData = _mmap(NULL, p_Size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
		if (s_AllocationData == nullptr)
			return nullptr;

		memset(s_AllocationData, 0, p_Size);

		return s_AllocationData;
	}
#endif

	return nullptr;
}

void Loader::Free(void * p_Data)
{
	// Validate that loader type is valid
	if (m_LoaderType <= ElfLoaderType_t::Invalid ||
		m_LoaderType >= ElfLoaderType_t::MAX)
		return;

#ifdef _WIN32
	free(p_Data);
#else
	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		k_free(p_Data);
		//delete [] static_cast<uint8_t*>(p_Data);
	}
	else if (m_LoaderType == ElfLoaderType_t::UserProc ||
		m_LoaderType == ElfLoaderType_t::UserTrainer)
	{
		// TODO: unmap
		WriteNotificationLog("err: Free for userland not implemented\n");
		return;
	}
#endif
}

uint32_t Loader::Hash(const char * p_Name)
{
	const unsigned char *p = (const unsigned char *)p_Name;
	unsigned long h = 0;
	unsigned long g;

	while (*p != '\0') {
		h = (h << 4) + *p++;
		if ((g = h & 0xf0000000) != 0)
			h ^= g >> 24;
		h &= ~g;
	}
	return (h);
}

bool Loader::Load()
{
	// Validate that our source header information is correct
	if (m_SourceElf == nullptr || m_SourceElfSize <= sizeof(Elf64_Ehdr))
		return false;

	// Save out our source information header
	m_SourceHeader = static_cast<const Elf64_Ehdr*>(m_SourceElf);

	// Validate the elf magic
	if (!IS_ELF(*m_SourceHeader))
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: file is not elf\n");
		}
		else
			WriteNotificationLog("err: file is not elf\n");
		return false;
	}

	// Check that we are x64-little endian
	if (m_SourceHeader->e_ident[EI_CLASS] != ELFCLASS64 ||
		m_SourceHeader->e_ident[EI_DATA] != ELFDATA2LSB)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: unsupported file layout\n");
		}
		else
			WriteNotificationLog("err: unsupported file layout\n");
		return false;
	}

	// Validate elf version
	if (m_SourceHeader->e_ident[EI_VERSION] != EV_CURRENT)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: unsupported file version\n");
		}
		else
			WriteNotificationLog("err: unsupported file version\n");
		return false;
	}

	// We only support executables and shared libraries
	if (m_SourceHeader->e_type != ET_EXEC && m_SourceHeader->e_type != ET_DYN && m_SourceHeader->e_type != ET_REL)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: non-executable elf format\n");
		}
		else
			WriteNotificationLog("err: non-executable elf format\n");
		return false;
	}

	// Validate correct machine arch
	if (m_SourceHeader->e_machine != EM_X86_64)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: unsupported arch type\n");
		}
		else
			WriteNotificationLog("err: unsupported arch type\n");
		return false;
	}

	// Validate that everthing is the correct sizes
	if (m_SourceHeader->e_phentsize != sizeof(Elf64_Phdr))
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: invalid program header entry size\n");
		}
		else
			WriteNotificationLog("err: invalid program header entry size\n");
		return false;
	}

	// Validate that all program headers are within bounds
	const auto s_TotalProgramHeaderSize = m_SourceHeader->e_phnum * m_SourceHeader->e_phentsize;
	if (m_SourceHeader->e_phoff >= m_SourceElfSize || m_SourceHeader->e_phoff + s_TotalProgramHeaderSize > m_SourceElfSize)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: corrupt elf program headers\n");
		}
		else
			WriteNotificationLog("err: corrupt elf program headers\n");
		return false;
	}

	// Update our loaders program header location
	m_SourceProgramHeadersStart = reinterpret_cast<const Elf64_Phdr*>(static_cast<const uint8_t*>(m_SourceElf) + m_SourceHeader->e_phoff);

	// Get all of the program headers
	for (auto l_ProgramHeaderIndex = 0; l_ProgramHeaderIndex < m_SourceHeader->e_phnum; ++l_ProgramHeaderIndex)
	{
		auto l_ProgramHeader = GetProgramHeaderByIndex(l_ProgramHeaderIndex);
		if (l_ProgramHeader == nullptr)
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: could not get program header (idx: %d).\n", l_ProgramHeaderIndex);
			}
			else
				WriteNotificationLog("err: could not get program header\n");
			continue;
		}

		switch (l_ProgramHeader->p_type)
		{
		case PT_LOAD:
			if (m_LoadableSegmentsCount == MaxSegments)
			{
				if (m_LoaderType == ElfLoaderType_t::KernelProc)
				{
					auto printf = (void(*)(const char *format, ...))kdlsym(printf);
					printf("err: too many loadable segments\n");
				}
				else
					WriteNotificationLog("err: too many loadable segments\n");
				return false;
			}

			// FreeBSD trusts that they are in correct order, so fuck it.
			m_LoadableSegments[m_LoadableSegmentsCount] = l_ProgramHeader;
			++m_LoadableSegmentsCount;
			break;
		case PT_PHDR:
			m_SourcePhphdr = l_ProgramHeader;
			break;
		case PT_DYNAMIC:
			m_SourceDynamicProgramHeader = l_ProgramHeader;
			break;
		case PT_INTERP:
			m_SourceInterpreterProgramHeader = l_ProgramHeader;
			break;
		}
	}

	// Validate that we actually got a dynamic program header
	if (m_SourceDynamicProgramHeader == nullptr)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not find dynamic program header\n");
		}
		else
			WriteNotificationLog("err: could not find dynamic program header\n");
		return false;
	}

	// Validate that we have any loadable segments
	if (m_LoadableSegmentsCount == 0 || m_LoadableSegmentsCount > MaxSegments)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: no loadable segments found\n");
		}
		else
			WriteNotificationLog("err: no loadable segments found\n");
		return false;
	}

	// We want to allocate the *entire* address space, so we have to calculate the min/max range
	Elf64_Addr s_VirtualAddressMinimum = __UINT64_MAX__;
	Elf64_Addr s_VirtualAddressMaximum = 0;

	for (Elf64_Xword l_LoadableIndex = 0; l_LoadableIndex < m_LoadableSegmentsCount; ++l_LoadableIndex)
	{
		auto l_ProgramHeader = m_LoadableSegments[l_LoadableIndex];
		if (l_ProgramHeader == nullptr)
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: loadable program header (%lld) is invalid\n", l_LoadableIndex);
			}
			else
				WriteNotificationLog("err: loadable program header\n");
			continue;
		}

		// Skip any loadable segments without any file data
		if (l_ProgramHeader->p_filesz == 0)
			continue;

		// Check to see if this VA is before our min VA, update it if so
		if (l_ProgramHeader->p_vaddr < s_VirtualAddressMinimum)
			s_VirtualAddressMinimum = l_ProgramHeader->p_vaddr;

		if (l_ProgramHeader->p_vaddr + l_ProgramHeader->p_memsz > s_VirtualAddressMaximum)
			s_VirtualAddressMaximum = l_ProgramHeader->p_vaddr + l_ProgramHeader->p_memsz;
	}

	// Check to see if we got any loadable segments
	if (s_VirtualAddressMinimum == __UINT64_MAX__ || s_VirtualAddressMaximum == 0)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not calculate loadable range\n");
		}
		else
			WriteNotificationLog("err: could not calculate loadable range\n");
		return false;
	}

	// Get the total VA range size
	Elf64_Xword s_MapSize = s_VirtualAddressMaximum - s_VirtualAddressMinimum;

	// Allocate pre-zeroed map
	auto s_AllocatedMap = Allocate(s_MapSize);
	if (s_AllocatedMap == nullptr)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not allocate map\n");
		}
		else
			WriteNotificationLog("err: could not allocate map\n");
		return false;
	}

	// Update our loaders maps
	m_AllocatedMap = s_AllocatedMap;
	m_AllocatedMapSize = s_MapSize;

	// Read the text, data, sections and zero bss
	for (Elf64_Xword l_LoadableSegmentIndex = 0; l_LoadableSegmentIndex < m_LoadableSegmentsCount; ++l_LoadableSegmentIndex)
	{
		auto l_LoadableSegment = m_LoadableSegments[l_LoadableSegmentIndex];
		if (l_LoadableSegment == nullptr)
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: could not load loadable segment (%lld).\n", l_LoadableSegmentIndex);
			}
			else
				WriteNotificationLog("err: could not load loadable segment\n");
			return false;
		}

		// Calculate where this loadable segment base is
		auto l_SegmentBase = static_cast<uint8_t*>(m_AllocatedMap) + (l_LoadableSegment->p_vaddr - s_VirtualAddressMinimum);
		// TODO: DO we need to check segment base?

		// Zero the entire segment base
		memset(l_SegmentBase, 0, l_LoadableSegment->p_memsz);

		// Copy over just the file size from source
		memcpy(l_SegmentBase, static_cast<const uint8_t*>(m_SourceElf) + l_LoadableSegment->p_offset, l_LoadableSegment->p_filesz);

		// Update permissions on this loadable segment

		// Calculate data protection starting from ---
		Elf64_Word l_DataProtection = 0;

#ifdef _WIN32
		/*if (l_LoadableSegment->p_flags & PF_R)
			l_DataProtection = PAGE_READONLY;
		if (l_LoadableSegment->p_flags & PF_W)
			l_DataProtection = PAGE_READWRITE;
		if (l_LoadableSegment->p_flags & PF_X)
			l_DataProtection = PAGE_EXECUTE_READ;*/
		l_DataProtection = PAGE_EXECUTE_READWRITE;
#else
		if (l_LoadableSegment->p_flags & PF_R)
			l_DataProtection |= PF_R;
		if (l_LoadableSegment->p_flags & PF_W)
			l_DataProtection |= PF_W;
		if (l_LoadableSegment->p_flags & PF_X)
			l_DataProtection |= PF_X;
#endif

		// Update the protection on this crap
		if (!SetProtection(reinterpret_cast<void*>(l_SegmentBase), l_LoadableSegment->p_memsz, l_DataProtection))
		{
			if (m_LoaderType == ElfLoaderType_t::KernelProc)
			{
				auto printf = (void(*)(const char *format, ...))kdlsym(printf);
				printf("err: could update memory protections\n");
			}
			else
				WriteNotificationLog("err: could update memory protections\n");
			return false;
		}
	}

	// Save the dynamic location
	m_Dynamic = reinterpret_cast<Elf64_Dyn*>(static_cast<uint8_t*>(m_AllocatedMap) + (m_SourceDynamicProgramHeader->p_vaddr - s_VirtualAddressMinimum));

	// Parse the dynamic information
	if (!ParseDynamic())
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not parse dynamics\n");
		}
		else
			WriteNotificationLog("err: could not parse dynamics\n");
		return false;
	}

	// TODO: Parse dpcpu
	if (!ParseDpcpu())
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not parse pcpu info\n");
		}
		else
			WriteNotificationLog("err: could not parse pcpu info\n");
		return false;
	}

	// TODO: Load dependencies

	if (!RelocateFile())
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not elf reloc local\n");
		}
		else
			WriteNotificationLog("err: could not elf reloc local\n");
		return false;
	}

	// Update the entrypoint
	m_Entrypoint = static_cast<uint8_t*>(m_AllocatedMap) + (m_SourceHeader->e_entry - s_VirtualAddressMinimum);
	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("info: map: %p src: %p ep: %p\n", GetAllocatedMap(), m_SourceElf, m_Entrypoint);
	}

	// TODO: Attempt to load symbol table (if it fucking exists, fuck you)
	auto s_SectionHeaderSize = m_SourceHeader->e_shnum * m_SourceHeader->e_shentsize;
	if (s_SectionHeaderSize == 0 || m_SourceHeader->e_shoff == 0 || m_SourceHeader->e_shoff + s_SectionHeaderSize > m_SourceElfSize)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("warn: no symbol information exists\n");
		}
		else
			WriteNotificationLog("warn: no symbol information exists\n");
		return true; // Ensure we return true here
	}

	auto s_SectionHeaderData = static_cast<Elf64_Shdr*>(Allocate(s_SectionHeaderSize));
	if (s_SectionHeaderData == nullptr)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not allocate section header data\n");
		}
		else
			WriteNotificationLog("err: could not allocate section header data\n");
		return true; // Elf is still ready to go
	}

	// Copy the table information
	memcpy(s_SectionHeaderData, static_cast<const uint8_t*>(m_SourceElf) + m_SourceHeader->e_shoff, s_SectionHeaderSize);

	// Find the symbol table index, and the string table index
	auto s_SymTabIndex = -1;
	auto s_SymStrIndex = -1;

	// Yerrrrrrr
	for (auto i = 0; i < m_SourceHeader->e_shnum; ++i)
	{
		if (s_SectionHeaderData[i].sh_type == SHT_SYMTAB)
		{
			s_SymTabIndex = i;
			s_SymStrIndex = s_SectionHeaderData[i].sh_link;
		}
	}

	if (s_SymTabIndex < 0 || s_SymStrIndex < 0)
	{
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("warn: could not find symbol table index (%d) or symbol string table index (%d)\n", s_SymTabIndex, s_SymStrIndex);
		}
		else
			WriteNotificationLog("warn: could not find symbol table or symbol string table\n");

		// Don't forget to free memory
		Free(s_SectionHeaderData);
		s_SectionHeaderData = nullptr;

		return true;
	}

	// Allocate our symbol table
	m_SymbolTableSize = s_SectionHeaderData[s_SymTabIndex].sh_size;
	m_SymbolTableBase = reinterpret_cast<Elf64_Addr>(Allocate(m_SymbolTableSize));
	if (m_SymbolTableBase == reinterpret_cast<Elf64_Addr>(nullptr))
	{
		m_SymbolTableSize = 0;
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not allocate symbol table (sz: 0x%llx)\n", m_SymbolTableSize);
		}
		else
			WriteNotificationLog("err: could not allocate symbol table\n");

		Free(s_SectionHeaderData);
		s_SectionHeaderData = nullptr;

		return true;
	}

	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("info: copying data from (%p 0x%llx) -> (%p 0x%llx)\n", static_cast<const uint8_t*>(m_SourceElf) + s_SectionHeaderData[s_SymTabIndex].sh_offset, m_SymbolTableSize, reinterpret_cast<void*>(m_SymbolTableBase), m_SymbolTableSize);
	}
	else
		WriteNotificationLog("info: copying data\n");

	memcpy(reinterpret_cast<void*>(m_SymbolTableBase), static_cast<const uint8_t*>(m_SourceElf) + s_SectionHeaderData[s_SymTabIndex].sh_offset, m_SymbolTableSize);

	m_StringTableBaseSize = s_SectionHeaderData[s_SymStrIndex].sh_size;
	m_StringTableBase = reinterpret_cast<Elf64_Addr>(Allocate(m_StringTableBaseSize));
	if (m_StringTableBase == reinterpret_cast<Elf64_Addr>(nullptr))
	{
		Free(reinterpret_cast<void*>(m_SymbolTableBase));
		m_SymbolTableBase = 0;

		m_SymbolTableSize = 0;
		if (m_LoaderType == ElfLoaderType_t::KernelProc)
		{
			auto printf = (void(*)(const char *format, ...))kdlsym(printf);
			printf("err: could not allocate string table (sz: 0x%llx)\n", m_StringTableBaseSize);
		}
		else
			WriteNotificationLog("err: could not allocate string table\n");
		m_StringTableBaseSize = 0;

		Free(s_SectionHeaderData);
		s_SectionHeaderData = nullptr;

		return true;
	}

	if (m_LoaderType == ElfLoaderType_t::KernelProc)
	{
		auto printf = (void(*)(const char *format, ...))kdlsym(printf);
		printf("info: 2 copying data from (%p 0x%llx) -> (%p 0x%llx)\n", static_cast<const uint8_t*>(m_SourceElf) + s_SectionHeaderData[s_SymStrIndex].sh_offset, m_StringTableBaseSize, reinterpret_cast<void*>(m_StringTableBase), m_StringTableBaseSize);
	}
	else
		WriteNotificationLog("info: 2 copying data\n");

	memcpy(reinterpret_cast<void*>(m_StringTableBase), static_cast<const uint8_t*>(m_SourceElf) + s_SectionHeaderData[s_SymStrIndex].sh_offset, m_StringTableBaseSize);

	m_DdbSymbolCount = m_SymbolTableSize / sizeof(Elf64_Sym);
	m_DdbSymbolTable = (const Elf64_Sym*)m_SymbolTableBase;
	m_DdbStringCount = m_StringTableBaseSize;
	m_DdbStringTable = reinterpret_cast<const char*>(m_StringTableBase);

	if (s_SectionHeaderData != nullptr)
	{
		Free(s_SectionHeaderData);
		s_SectionHeaderData = nullptr;
		//s_SectionHeaderSize = 0;
	}

	return true;
}

int Loader::Strcmp(const char* s1, const char* s2)
{
	while (*s1 == *s2++)
		if (*s1++ == '\0')
			return (0);
	return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
}


code:
.intel_syntax noprefix
.text

.global _start

.org 0
_start:
	jmp		mira_entry


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Utils/_Syscall.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/Dynlib.hpp>

using namespace Mira::Utils;

int64_t Dynlib::LoadPrx(const char* p_PrxPath, int* p_OutModuleId)
{
	return (int64_t)syscall4(594, reinterpret_cast<void*>(const_cast<char*>(p_PrxPath)), 0, p_OutModuleId, 0);
}

int64_t Dynlib::UnloadPrx(int64_t p_PrxId)
{
	return (int64_t)syscall1(595, (void*)p_PrxId);
}

int64_t Dynlib::Dlsym(int64_t p_PrxId, const char* p_FunctionName, void* p_DestinationFunctionOffset)
{
	return (int64_t)syscall3(591, (void*)p_PrxId, (void*)p_FunctionName, p_DestinationFunctionOffset);
}


code:
.intel_syntax noprefix
.text

.global kernelRdmsr
.global cpu_enable_wp
.global cpu_disable_wp

# This is needed for freestanding modules
.global memcpy
.global memmove
.global memset
.global memcmp
.global strlen
.global strcmp

kernelRdmsr:
	mov ecx, edi
	rdmsr
	shl rdx, 32
	or rax, rdx
	ret

cpu_enable_wp:
	mov rax, cr0
	or rax, 0x10000
	mov cr0, rax
	ret

cpu_disable_wp:
	mov rax, cr0
	and rax, ~0x10000
	mov cr0, rax
	ret

memcpy:
	# Preserve destination address because we have to return it.
	mov rax, rdi

	# Do a byte-by-byte move. On modern x86 chips, there is not a significant
	# performance difference between byte-wise and word-wise moves. In fact,
	# sometimes movsb is faster. See instlatx64.atw.hu for benchmarks.
	mov rcx, rdx
	rep movsb

	ret

memmove:
	# Preserve destination address because we have to return it.
	mov rax, rdi

	# If dest < src, we can always do a fast pointer-incrementing move.
	# If dest == src, do nothing.
	cmp rdi, rsi
	je .done
	jb .fast

	# If dest > src and there are no overlapping regions (dest >= src+num), we
	# can still do a fast pointer-incrementing move.
	mov rcx, rsi
	add rcx, rdx
	cmp rdi, rcx
	jae .fast

	# If dest > src and dest < src+num, we have to do a right-to-left move to
	# preserve overlapping data.
	.slow:

		# Set the direction flag so copying is right-to-left.
		std

		# Set the move count register.
		mov rcx, rdx

		# Update pointers to the right-hand side (minus one).
		dec rdx
		add rsi, rdx
		add rdi, rdx

		# Do a byte-by-byte move.
		rep movsb

		# Reset the direction flag.
		cld

		ret

	.fast:

		# Do a byte-by-byte move.
		mov rcx, rdx
		rep movsb

	.done:

		ret

memset:
	# Preserve the original destination address.
	mov r8, rdi

	# The value to store is the second parameter (rsi).
	mov rax, rsi

	# Do a byte-by-byte store.
	mov rcx, rdx
	rep stosb

	# Return the original destination address.
	mov rax, r8
	ret

memcmp:																	# @memcmp
		test rdx, rdx
		je .LBB0_5
		xor ecx, ecx
.LBB0_2:																# =>This Inner Loop Header: Depth=1
		movzx eax, byte ptr [rdi + rcx]
		movzx r8d, byte ptr [rsi + rcx]
		cmp al, r8b
		jne .LBB0_3
		add rcx, 1
		cmp rdx, rcx
		jne .LBB0_2
.LBB0_5:
		xor eax, eax
		ret
.LBB0_3:
		sub eax, r8d
		ret

strlen:
	# save rdi
	push rdi

	# *al = '\0'
	xor al, al
	# clear rcx
	xor rcx, rcx
	# *rcx = ~*rcx
	not rcx
	# while (*al /* '\0' */ != *rdi) ++rdi, --*rcx#
	cld
	repne scasb
	# *rcx = ~*rcx
	not rcx
	dec rcx

	# set return value
	mov rax, rcx
	# restore rdi
	pop rdi

	ret

strcmp:
myasm_strcmp:
	mov r8, 0 #i = 0
	mov r10, 0
myasm_loop:
	mov r9b, [rdi + r8]
	mov r10b, [rsi + r8]

	cmp r9b, r10b
	jne myasm_end

	cmp r9b, 0
	je myasm_end

	inc r8 #i++
	jmp myasm_loop

myasm_end:
	mov rax, 0
	mov al, r9b
	sub rax, r10
	ret


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Utils/Logger.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/SysWrappers.hpp>
#include <Mira.hpp>

extern "C"
{
	#include <sys/fcntl.h>
};

using namespace Mira::Utils;

Logger* Logger::m_Instance = nullptr;
Logger* Logger::GetInstance()
{
	if (m_Instance == nullptr)
		m_Instance = new Logger();

	return m_Instance;
}

Logger::Logger() :
	m_LogLevel(LL_None),
	m_Buffer{0},
	m_FinalBuffer{0},
	m_Handle(-1)
{
#ifdef _DEBUG
	m_LogLevel = LL_Debug;
#else
	m_LogLevel = LL_Error;
#endif
	m_Handle = -1;

	memset(m_Buffer, 0, sizeof(m_Buffer));
	memset(m_FinalBuffer, 0, sizeof(m_FinalBuffer));

	// Initialize a mutex to prevent overlapping spam
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);
	auto sx_init_flags = (void(*)(struct sx* sx, const char* description, int opts))kdlsym(_sx_init_flags);
	sx_init_flags(&m_Mutex, "logsx", 0);

	printf("post sx init flags\n");
}

Logger::~Logger()
{
	// auto mtx_destroy = (void(*)(struct mtx* mutex))kdlsym(mtx_destroy);
	// mtx_destroy(&m_Mutex);
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include <Utils/New.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/Kernel.hpp>
#include <Utils/Logger.hpp>

#include <vm/vm.h>
#include <sys/malloc.h>

#include <vm/uma.h>

void * operator new(unsigned long int p_Size)
{
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);
	auto malloc = (void*(*)(unsigned long size, struct malloc_type* type, int flags))kdlsym(malloc);
	auto M_TEMP = (struct malloc_type*)kdlsym(M_TEMP);

	printf("[+] %llx\n", p_Size);

	auto s_Allocation = malloc(p_Size, M_TEMP, M_ZERO | M_WAITOK);
	printf("[+] %p\n", s_Allocation);

	return s_Allocation;
}

// placement new
void * operator new(unsigned long int p_Size, void * p_Pointer)
{
	return p_Pointer;
}

// operator new[]
void * operator new[] (unsigned long int p_Size)
{
	return ::operator new(p_Size);
}

// Delete
void operator delete(void* p_Pointer) noexcept
{
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);
	auto free = (void(*)(void* addr, struct malloc_type* type))kdlsym(free);
	auto M_TEMP = (struct malloc_type*)kdlsym(M_TEMP);

	printf("[-] %p\n", p_Pointer);

	free(p_Pointer, M_TEMP);
}

void operator delete[](void* p_Pointer) noexcept
{
	::operator delete(p_Pointer);
}


code:
.intel_syntax noprefix
.text

.global syscall1, syscall2, syscall3, syscall4, syscall5, syscall6, _mmap

syscall:
	mov rax,rdi
	syscall
	ret

syscall1:
	mov rax,rdi
	mov rdi,rsi
	syscall
	ret

syscall2:
	mov rax,rdi
	mov rdi,rsi
	mov rsi,rdx
	syscall
	ret

syscall3:
	mov rax,rdi
	mov rdi,rsi
	mov rsi,rdx
	mov rdx,rcx
	syscall
	ret

syscall4:
	mov rax,rdi
	mov rdi,rsi
	mov rsi,rdx
	mov rdx,rcx
	mov r10,r8
	syscall
	ret

syscall5:
	mov rax,rdi
	mov rdi,rsi
	mov rsi,rdx
	mov rdx,rcx
	mov r10,r8
	mov r8,r9
	syscall
	ret

syscall6:
	mov rax,rdi
	mov rdi,rsi
	mov rsi,rdx
	mov rdx,rcx
	mov r10,r8
	mov r8,r9
	mov r9, [rsp]
	syscall
	ret

_mmap:
	mov rax, 477
	mov r10, rcx
	syscall
	ret


code:
OUTPUT_FORMAT("elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)

ENTRY(_start)

PHDRS
{
	/*
	 * PF_X = 0x1
	 * PF_W = 0x2
	 * PF_R = 0x4
	 */

	ph_text PT_LOAD FLAGS (0x1 | 0x4);
	ph_data PT_LOAD FLAGS (0x2 | 0x4);
}

SECTIONS
{
	__payload_base = .;

	.text :
	{
		KEEP (*(.init))
		KEEP (*(.fini))

		*(.text .text.*)

		. = ALIGN(4);
	} : ph_text = 0x90909090

	. = ALIGN(0x4000);

	.rodata :
	{
		*(.rodata .rodata.*)

		. = ALIGN(4);
	}

	. = ALIGN(0x4000);

	.data :
	{
		*(.data .data.*)

		. = ALIGN(0x10);

		__imports_start = .;
		KEEP(*(.imports .imports.*))
		__imports_end = .;

		__patches_start = .;
		KEEP(*(.patches .patches.*))
		QUAD(0); BYTE(0); BYTE(0);
		__patches_end = .;

		__bss_start = .;
		*(.bss .bss.*) *(COMMON)
		__bss_end = .;

		. = . + 4;
		. = ALIGN(4);
	} : ph_data
}


code:
In order to build the protobuf files you will need to do a few things.

1. Install protobuf-c & protoc-c compilers from source elsewhere (https://github.com/protobuf-c/protobuf-c)
2. Generate all of the c-bindings (protoc-c --c_out=. rpc.proto filemanager.proto debugger.proto) this will create the Mira-.h/.c files
3. Generate all of the C# bindings (protoc --csharp_out=. rpc.proto filemanager.proto debugger.proto) this will create the needed .cs files for use with MiraUtils
4. Fix the include paths in the .h/.c files (usually point to local file path, change to use proper `#include <Messages/Rpc/rpc-pb.blah>`)
5. The csharp generator is broken out of box (for some odd reason), you will need to find all references to

```
    default:
    if (!pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input)) { ... }
    ...
```

and change them to

```default:
    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
    break;
```

Then everything should build cleanly/work properly providing you did everything correctly


code:
#!/usr/bin/env python3
import fileinput
import argparse
import os

MAGIC_STRING_CS = "if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {"
FIXED_STRING_CS = "_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);"

MAGIC_STRING_PBC = '#include "external/'
FIXED_STRING_PBC = '#include "'

def fixFile(fileName):
    data = [ ]

    with open(fileName, "r") as file:
        data = file.readlines()

    for i in range(0, len(data), 1):
        if MAGIC_STRING_CS in data[i]:
            data[i] = data[i].replace(MAGIC_STRING_CS, FIXED_STRING_CS)

            if "return;" not in data[i+1]:
                print("error error error, return statement not on line " + (i+1))

            data[i+1] = ""

            if "}" not in data[i+2]:
                print("error error error closing bracket not found")

            data[i+2] = ""

        elif MAGIC_STRING_PBC in data[i]:
            data[i] = data[i].replace(MAGIC_STRING_PBC, FIXED_STRING_PBC)

    with open(fileName, "w") as file:
        file.writelines(data)

if __name__== "__main__":
    inputDirectory = "."
    miraDirectory = "."
    noMvPbcFiles = False

    parser = argparse.ArgumentParser(prog="build_proto.py")
    parser.add_argument("--inputDir", help="input directory (default: .)")
    parser.add_argument("--outputDir", help="output directory (default is input directory)")
    parser.add_argument("--miraDir", help="mira directory (default: .)")
    parser.add_argument("--noPbcMv", help="option to skip the move of the protobuf files (default: False)")

    args = parser.parse_args()

    # if the user provided a input directory then use that instead of .
    if args.inputDir:
        inputDirectory = args.inputDir

    outputDirectory = inputDirectory

    if args.outputDir:
        outputDirectory = args.outputDir

    if args.miraDir:
        miraDirectory = args.miraDir

    pbFileList = [ ]
    pbFileListString = ""

    # Find all of the proto files in the current running directory
    for file in os.listdir(inputDirectory):
        if file.endswith(".proto"):
            fullFilePath = os.path.join(inputDirectory, file)
            pbFileList.append(fullFilePath)
            pbFileListString += fullFilePath + " "

    os.system("protoc-c --c_out=. " + pbFileListString)
    os.system("protoc --csharp_out=" + outputDirectory + " " + pbFileListString)

    csharpFileList = [ ]
    pbcFileList = [ ]
    pbcPathList = [ ]

    for file in os.listdir(outputDirectory):
        if file.endswith(".cs"):
            fullFilePath = os.path.join(outputDirectory, file)
            csharpFileList.append(fullFilePath)

    for file in os.listdir(inputDirectory):
        if "pb-c" in file:
            pbcFileList.append(file)
            fullFilePath = os.path.join(inputDirectory, file)
            pbcPathList.append(fullFilePath)

    for file in (csharpFileList + pbcPathList):
        fixFile(file)

    if noMvPbcFiles is False:
        # Move fixed protobuf files
        for file in pbcFileList:
            sourcePath = os.path.join(inputDirectory, file)
            destinationPath = ""

            if "debugger" in file:
                destinationPath = os.path.join(miraDirectory, ("src/Plugins/Debugger/" + file))
            elif "filemanager" in file:
                destinationPath = os.path.join(miraDirectory, ("src/Plugins/FileManager/" + file))
            elif "rpc" in file:
                destinationPath = os.path.join(miraDirectory, ("src/Messaging/Rpc/" + file))

            os.replace(sourcePath, destinationPath)

    print("completed")


code:
*.bat	text eol=crlf
*.tw	text eol=lf
*.sh	text eol=lf
*.py	text eol=lf
*.txt	text eol=lf
compile	text eol=lf
compile-git	text eol=lf
sanityCheck	text eol=lf

repo_name: AnonDev/fc

path: .gitattributes


code:
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.swp
*.swo

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# Start.tw
src/config/start.tw

# eslint
.eslintrc.js
node_modules
package-lock.json
package.json

# misc
fc-pregmod
*.outline
*.todo
TODO.txt

path: .gitignore


code:
Common problems:

How do I start the game?
-Run the compile file, go to folder "bin", click the "FC_Pregmod" and play. (Recommendation: Drag it into incognito mode)

I get an error on gamestart reading "Apologies! A fatal error has occurred. Aborting. Error: Unexpected token @ in JSON at position 0. Stack Trace: SyntaxError: Unexpected token @ in JSON at position 0 at JSON.parse (<anonymous>) at JSON.value" or some variant
-clear cookies

Everything is broken!
-Do not copy over your existing download as it may leave old files behind, replace it entirely

I can't save more than once or twice.
-Known issue caused by sugarcube level changes. Save to file doesn't have this problem and will likely avoid the first problem as well.
-It is possible to increase the memory utilized by your browser to delay this

I wish to report a sanityCheck issue.
-Great, however a large majority of the results are false positives coming from those specific sections being split over several lines in the name of readability and git grep's intentionally (http://git.661346.n2.nabble.com/bug-git-grep-P-and-multiline-mode-td7613900.html ) lacking support for multiline. An Attempt to add -Pzl (https://gitgud.io/pregmodfan/fc-pregmod/merge_requests/2108 ) created a sub condition black hole. What follows are examples of common false positives that can safely be ignored;

	[MissingClosingAngleBracket]src/art/vector/Generate_Stylesheet.tw:11:<<print "<style>."+_art_display_class+" {
		<<print "<style>."+_art_display_class+" {
	position: absolute;
	height: 100%;
	margin-left: auto;
	margin-right: auto;
	left: 0;
	right: 0;
	}

How to mod (basic doc):

1. All sources now in the src subdir, in separate files. 1 passage = 1 file.

2. Special files and dir's:
	- src/config 		- configuration of the story is here.
	- src/config/start.tw 	- contains list of .tw passage files, regenerated automatic, by building scripts. Do not change by hands. (original passage Start from pregmod renamed and moved to src/events/intro/introSummary.tw)
	- src/js/storyJS.tw		- special passage with [script] tag - contain all native JavaScript from pregmod.
	- devTools/tweeGo/targets/sugarcube-2/userlib.js - on original FC JS moved here (I deleted it after moving JS to storyJS.tw). Compare to storyJS.tw but do not copy file here. May conflict with src/js/storyJS.tw if copied.
	- src/pregmod 		- I put all pregmod-only passages here.
	- .gitignore 		- special file for git - to ignore some files. For example - compilation results.

3. Compilation:

	Windows:
	Run compile.bat - result will be file bin/FC_pregmod.html
	Second run of compile.but will overwrite bin/FC_pregmod.html without prompt.

	Linux:
	Ensure executable permission on file "devTools/tweeGo/tweego" (not tweego.exe!)
	Ensure executable permission on file "compile"
	In the root dir of sources (where you see src, devTools, bin...) run command "./compile" from console
	compile-git will produce the same result file but with current commit hash in filename.

	Mac:
	Not supported directly (I don't have access to Mac for testing).
	But you can use linux compilation script if you download tweego for mac from here: https://bitbucket.org/tmedwards/tweego/downloads/ and replace linux executable with mac executable in ./devTools/tweeGo/ folder. This is not tested though, so be warned.

4. Simple comparing and merging with original FC:

	Use meld tool. Place folder FreeCities (original FC sources tree) near FreeCitiesPregmod (this sources tree) and use command:
	meld FreeCities FreeCitiesPregmod
	or just select these folders in meld's GUI.

5. All modders will be very grateful if anyone who makes some changes to game with .html file also post his/her resulting src folder tree.

6. For contributors to pregmod: if you don't use git, then you need to post your version of src folder tree, not just produced FC_pregmod.html file!!! This html file can't be reverted to proper sources, and useless as contribution!

7. Git workflow:
	- Master branch is pregmod-master. Only Pregmodder can add something to it directly. Always contain his last public changes.
	- pregmod-dev - branch with experimental code mainly by pregmodfan.
	- Any contributions will be placed in separate branches like pregmod-mod-<something> (if it's ready to merge with master complete feature/mod) or pregmod-contrib-<something> if it's partial work until contributions is reviewed.

	Typical cycle with git:
	1. Make account on gitgud if you don't have usable one.
	2. Fork main repository through gitgud interface. (Or pull changes from main repo if you already have fork.)
	3. Clone your fork to local machine with git client (Or pull changes if already cloned.)
	4. Make you changes as you like, commit, and push result into your forked repository (with git client).
	5. Make merge request through gitgud interface.


code:
further development:
-specialized slave schools
-fortifications
-more levels for militia edict (further militarize society)
-conquering other arcologies?

Events:
-famous criminal escapes to the arcology, followed by another arcology police force

Bugs:
-sometimes troop counts breaks
-sometimes rebel numbers have fractionary parts

Rules Assistant:
- find a way for the new intense drugs to fit in
- everything mentioned in https://gitgud.io/pregmodfan/fc-pregmod/issues/81

main.tw porting:
- slaveart
- createsimpletabs
- displaybuilding
- optionssortasappearsonmain
- resetassignmentfilter
- mainlinks
- arcology description
- office description
- slave summary
- use guard
- toychest
- walk past


code:
# How to vector art

Please read the whole document before starting to work.
Some aspects are not obvious right away.

Note: This does not actually describe how to be an artist.

## TL;DR

    killall inkscape
    artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
    compile
    python3 artTools/normalize_svg.py artTools/vector_source.svg
    git commit -a

## 1. Be an artist

Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.

## 2. Respect the structure

While editing, keep the Layers in mind.

* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-definded IDs.
* All Layers should have an ID that is globally unique
  (not just within their subtree).

* Please use anonymous groups only for the ease of editing. Remove all "helper" groups before finally saving the file.
* Anonymous groups can be used for continous scaling (of e.g. boobs).

* Every asset that should go into a separate file needs to be in a labelled group
  (even if that is a group with only one shape).
* There are some globally available styles defined as CSS classes (e.g. skin, hair).
  Use them if your asset should be changed upon display.
  Do not set the style directly in the object.

## 3. Normalise the document (before committing)

**THIS IS IMPORTANT**

The various editors out there (Inkscape, Illustrator) may use different variants of formatting the SVG XML.
Use

    python3 normalize_svg.py vector_source.svg

before committing to normalise the format so git will not freak out about the changed indentation.

If you use Inkscape, please use in Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
In case your Editor uses another path data style which cannot be changed, please contact the other artists and developers via the issue tracker to find a new common ground.

What it does:
* Formats the SVG XML according to Pythons lxml module, regardless of editor.
* Adobe Illustrator uses group IDs as layer labels.
  Inkscape however uses layer labels and a separate, auto-generated group ID.
  normalize_svg.py overwrites the group ID with the Inkscape layer label
  so they are synchronised with Inkscape layer labels.
* Inkscape copies the global style definition into the object *every time*
  the object is edited. If an object references a CSS class AND at the same time
  has a local style, the local style is removed
  so global dynamic styling is possible later on.

Note: Behaviour of Adobe Illustrator is untested.

## 4. Split the layers

For NoX original, deepurk extensions, execute

    python3 vector_layer_split.py vector_deepmurk_primary.svg tw ../src/art/vector/layers/

For faraen revamped art (based on NoX original)

	python3 vector_revamp_layer_split.py vector_revamp_source.svg tw ../src/art/vector_revamp/layers/

. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
The group ID sets the file name. Therefore, the group ID **must not** contain spaces or any other weird characters.

Also consider:
* A group with ID ending in _ is ignored. Use Layers ending in _ to keep overview.
* A group with ID starting with "g" (Inkscape) or "XMLID" (Illustrator) is also ignored.
* Existing files are overwritten **without enquiry**.
* The target directory is not emptied. If a file is no longer needed, you should remove it manually.
* This procedure removes global definitions. This means, SVG filters are currently not supported.

Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
`tw` embeds the SVG data into Twine files, but removes the global style definitions so they can be set during display.

## 5. Edit the code

`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
The code also generates the previously removed global style definitions on the fly and per display.

## 6. Compile the story

Use the provided compile script (Windows batch or shell) for compiling.

## 7. Advanced usage

You can define multiple CSS classes to one element, e.g. "skin torso". When procedurally generating CSS, you can then set a global "skin" style, and a different one for "torso".

You can put variable text into the image using single quotes, e.g. "'+_tattooText+'". The single quotes *break* the quoting in Twine so the content of the Twine variable `_tattooText` is shown upon display. You can even align text on paths.

An anonymous group can be used for dynamic transformation. However, finding the correct parameters for the affine transformation matrix can be cumbersome. Use any method you see fit. See `src/art/vector/Boob.tw` for one example of the result.


code:
Apron
AttractiveLingerie
AttractiveLingerieForAPregnantWoman
BallGown
Battledress
Blouse
BodyOil
Bunny
Chains
ChattelHabit
Cheerleader
ClubslutNetting
ComfortableBodysuit
Conservative
Cutoffs
Cybersuit
FallenNunsHabit
FuckdollSuit
HalterTopDress
HaremGauze
Hijab
Huipil
Kimono
LatexCatsuit
Leotard
LongQipao
MaternityDress
MilitaryUniform
MiniDress
Monokini
NiceBusinessAttire
NiceMaid
NiceNurse
No
PenitentNunsHabit
RedArmyUniform
RestrictiveLatex
ScalemailBikini
SchoolgirlUniform
SchutzstaffelUniform
ShibariRopes
SlaveGown
Slutty
SluttyBusinessAttire
SluttyJewelry
SluttyMaid
SluttyNurse
SluttyQipao
SluttySchutzstaffelUniform
Spats
StretchPants
StringBikini
Succubus
Toga
UncomfortableStraps
Western


code:
#!/bin/bash

# This script reads all possible values of $slave.clothes as mentioned by the documentation.
# This script uses the actual implementation of the JS clothing2artSuffix function as defined in src/art/vector/Helper_Functions.tw
# This script outputs suffixes to be used in the SVG naming scheme
# This script is meant to be executed at the project root directory.
# This script depends on bash, grep, sed, paste and nodejs (so best executed on Linux, I guess)

# grep -Porh '(?<=\.clothes = )"[^"]+"' src/ | sort | uniq # searches sources for all clothes strings actually used in the game, even undocumented ones

(
  echo 'var window = {};'
  grep -v '^:' src/art/vector/Helper_Functions.tw
  echo -n 'Array('
  sed '/^clothes:/,/:/!d' "slave variables documentation - Pregmod.txt" | grep '"' | paste -sd,
  echo ').forEach(v => {console.log(window.clothing2artSuffix(v));});'
) | nodejs | sort


code:
#!/usr/bin/env python3

'''
Application for "normalizing" SVGs

These problems are addressed:

* Inkscape notoriously copies class styles into the object definitions.
https://bugs.launchpad.net/inkscape/+bug/167937

* Inkscape uses labels on layers. Layers are basically named groups.
  Inkscape does not sync the group id with the layer label.

Usage Example:
python3 inkscape_svg_fixup.py vector_source.svg
'''

import lxml.etree as etree
import sys

def fix(tree):
  # know namespaces
  ns = {
    'svg' : 'http://www.w3.org/2000/svg',
    'inkscape' : 'http://www.inkscape.org/namespaces/inkscape'
  }

  # find document global style definition
  # mangle it and interpret as python dictionary
  style_element = tree.find('./svg:style',namespaces=ns)
  style_definitions = style_element.text
  pythonic_style_definitions = '{'+style_definitions.\
  replace('\t.','"').replace('{','":"').replace('}','",').\
  replace('/*','#')+'}'
  styles = eval(pythonic_style_definitions)

  # go through all SVG elements
  for elem in tree.iter():
    if (elem.tag == etree.QName(ns['svg'], 'g')):
      # compare inkscape label with group element ID
      l = elem.get(etree.QName(ns['inkscape'], 'label'))
      if l:
        i = elem.get('id')
        if (i != l):
          print("Overwriting ID %s with Label %s..."%(i, l))
          elem.set('id', l)

    # clean styles (for easier manual merging)
    style_string = elem.get('style')
    if style_string:
      split_styles = style_string.strip('; ').split(';')
      styles_pairs = [s.strip('; ').split(':') for s in split_styles]
      filtered_pairs = [ (k,v) for k,v in styles_pairs if not (
        k.startswith('font-') or
        k.startswith('text-') or
        k.endswith('-spacing') or
        k in ["line-height", " direction", " writing", " baseline-shift", " white-space", " writing-mode"]
      )]
      split_styles = [':'.join(p) for p in filtered_pairs]
      style_string = ';'.join(sorted(split_styles))
      elem.attrib["style"] = style_string

    # remove all style attributes offending class styles
    s = elem.get('style')
    c = elem.get('class')
    if (c and s):
      s = s.lower()
      c = c.split(' ')[0] # regard main style only
      cs = ''
      if c in styles:
        cs = styles[c].strip('; ').lower()
      if (c not in styles):
        print("Object id %s references unknown style class %s."%(i,c))
      else:
        if (cs != s.strip('; ')):
          print("Style %s removed from object id %s differed from class %s style %s."%(s,i,c,cs))
        del elem.attrib["style"]

if __name__ == "__main__":
  input_file = sys.argv[1]
  tree = etree.parse(input_file)
  fix(tree)
  # store SVG into file (input file is overwritten)
  svg = etree.tostring(tree, pretty_print=True)
  with open(input_file, 'wb') as f:
    f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
    f.write(svg)


code:
#!/usr/bin/env python3

'''
Application for procedural content adaption
*THIS IS VERY EXPERIMENTAL*
Contains a very poor man's implementation of spline mesh warping.

This application parses SVG path data of an outfit sample aligned to a body part.
The outfit is then replicated for other shapes of the same body part.

Example data is geared towards generating a strap outfit for boobs and torso
for all sizes of boobs and all shapes of torsos based on a single outfit for
boobs of size 2 and a hourglass torso respectively.

Limitations:
* handles paths only
* only svg.path Line and CubicBezier are tested
* only the aforementioned examples are tested

Usage:
python3 vector_clothing_replicator.py infile clothing bodypart destinationfile

Usage Example:
python3 vector_clothing_replicator.py vector_source.svg Straps Boob vector_destination.svg
python3 vector_clothing_replicator.py vector_source.svg Straps Torso vector_destination.svg
'''

from svg.path import parse_path
import copy
import lxml.etree as etree
import sys

REFERENCE_PATH_SAMPLES = 200
EMBED_REPLICATIONS = True # wether to embed all replications into the input file or output separate files

input_file = sys.argv[1]
clothing = sys.argv[2]
bodypart = sys.argv[3]
output_file_embed = sys.argv[4]

# TODO: make these configurable
output_file_pattern = '%s_%s_%s.svg' #bodypart, target_id, clothing

if ('Torso' == bodypart):
  xpath_shape = './svg:g[@id="Torso_"]/svg:g[@id="Torso_%s"]/svg:path[@class="skin torso"]/@d' # TODO: formulate more general, independent of style
  xpath_outfit_container = '//svg:g[@id="Torso_Outfit_%s_"]'%(clothing)
  xpath_outfit = '//svg:g[@id="Torso_Outfit_%s_%s"]'%(clothing,'%s')
  target_ids = "Unnatural,Hourglass,Normal".split(",")
  reference_id = "Hourglass"
else:
  raise RuntimeError("Please specify a bodypart for clothing to replicate.")

tree = etree.parse(input_file)
ns = {'svg' : 'http://www.w3.org/2000/svg'}

canvas = copy.deepcopy(tree)
for e in canvas.xpath('./svg:g',namespaces=ns)+canvas.xpath('./svg:path',namespaces=ns):
  # TODO: this should be "remove all objects, preserve document properties"
  e.getparent().remove(e)

def get_points(xpath_shape):
  '''
  This funciton extracts reference paths by the given xpath selector.
  Each path is used to sample a fixed number of points.
  '''
  paths_data = tree.xpath(xpath_shape,namespaces=ns)
  points = []
  path_length = None
  for path_data in paths_data:
    p = parse_path(path_data)
    points += [
      p.point(1.0/float(REFERENCE_PATH_SAMPLES)*i)
      for i in range(REFERENCE_PATH_SAMPLES)
    ]
  if (not points):
    raise RuntimeError(
      'No paths for reference points found by selector "%s".'%(xpath_shape)
    )
  return points

def point_movement(point, reference_points, target_points):
  '''
  For a given point, finds the nearest point in the reference path.
  Gives distance vector from the nearest reference point to the
  respective target reference point.
  '''
  distances = [abs(point-reference_point) for reference_point in reference_points]
  min_ref_dist_idx = min(enumerate(distances), key=lambda x:x[1])[0]
  movement = target_points[min_ref_dist_idx] - reference_points[min_ref_dist_idx]
  return movement

reference_points = get_points(xpath_shape%(reference_id))
container = tree.xpath(xpath_outfit_container,namespaces=ns)
if (len(container) != 1):
  raise RuntimeError('Outfit container selector "%s" does not yield exactly one layer.'%(xpath_outfit_container))
container = container[0]
outfit_source = container.xpath(xpath_outfit%(reference_id),namespaces=ns)
if (len(outfit_source) != 1):
  raise RuntimeError('Outfit source selector "%s" does not yield exactly one outfit layer in container selected by "%s".'%(xpath_outfit%(reference_id), xpath_outfit_container))
outfit_source = outfit_source[0]

for target_id in target_ids:
  print(
    'Generating variant "%s" of clothing "%s" for bodypart "%s"...'%
    (target_id, clothing, bodypart)
  )
  outfit = copy.deepcopy(outfit_source)
  paths = outfit.xpath('./svg:path',namespaces=ns)
  if target_id == reference_id:
    print("This is the source variant. Skipping...")
  else:
    layerid = outfit.get('id').replace('_%s'%(reference_id),'_%s'%(target_id))
    outfit.set('id', layerid)
    outfit.set(etree.QName('http://www.inkscape.org/namespaces/inkscape', 'label'), layerid) # for the Inkscape-users
    target_points = get_points(xpath_shape%(target_id))
    if (len(reference_points) != len(target_points)):
      raise RuntimeError(
        ('Different amounts of sampled points in reference "%s" and target "%s" paths. '+
        'Selector "%s" probably matches different number of paths in the two layers.')%
        (reference_id, target_id, xpath_shape)
      )
    for path in paths:
      path_data = path.get("d")
      p = parse_path(path_data)
      for segment in p:
        original_distance = abs(segment.end-segment.start)
        start_movement = point_movement(segment.start, reference_points, target_points)
        segment.start += start_movement
        end_movement = point_movement(segment.end, reference_points, target_points)
        segment.end += end_movement
        distance = abs(segment.end-segment.start)
        try:
          # enhance position of CubicBezier control points
          # amplification is relative to the distance gained by movement
          segment.control1 += start_movement
          segment.control1 += (segment.control1-segment.start)*(distance/original_distance-1.0)
          segment.control2 += end_movement
          segment.control2 += (segment.control2-segment.end)*(distance/original_distance-1.0)
        except AttributeError as ae:
          # segment is not a CubicBezier
          pass
      path.set("d", p.d())
    if EMBED_REPLICATIONS:
      container.append(outfit)
  if not EMBED_REPLICATIONS:
    container = copy.deepcopy(canvas).xpath('.',namespaces=ns)[0]
    container.append(outfit)

  if not EMBED_REPLICATIONS:
    svg = etree.tostring(container, pretty_print=True)
    with open((output_file_pattern%(bodypart, target_id, clothing)).lower(), 'wb') as f:
      f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
      f.write(svg)

if EMBED_REPLICATIONS:
  svg = etree.tostring(tree, pretty_print=True)
  with open(output_file_embed, 'wb') as f:
    f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
    f.write(svg)


code:
#!/usr/bin/env python3

'''
Application for splitting groups from one SVG file into separate files

Usage:
python3 vector_layer_split.py infile format outdir

Usage Example:
python3 vector_layer_split.py vector_source.svg tw ../src/art/vector/layers/
'''

import lxml.etree as etree
import sys
import os
import copy
import re
import normalize_svg

input_file = sys.argv[1]
output_format = sys.argv[2]
output_directory = sys.argv[3]
if not os.path.exists(output_directory):
    os.makedirs(output_directory)

ns = {
  'svg' : 'http://www.w3.org/2000/svg',
  'inkscape' : 'http://www.inkscape.org/namespaces/inkscape',
  'sodipodi':"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
}

tree = etree.parse(input_file)
normalize_svg.fix(tree)

# prepare output template
template = copy.deepcopy(tree)
root = template.getroot()

# remove all svg root attributes except document size
for a in root.attrib:
  if (a != "viewBox"):
    del root.attrib[a]

# add placeholder for CSS class (needed for workaround for non HTML 5.1 compliant browser)
if output_format == 'tw':
  root.attrib["class"] = "'+_art_display_class+'"

# remove all content, including metadata
# for twine output, style definitions are removed, too
defs = None
for e in root:
  if (e.tag == etree.QName(ns['svg'], 'defs')):
    defs = e
  if (e.tag == etree.QName(ns['svg'], 'g') or
      e.tag == etree.QName(ns['svg'], 'metadata') or
      e.tag == etree.QName(ns['svg'], 'defs') or
      e.tag == etree.QName(ns['sodipodi'], 'namedview') or
      (output_format == 'tw' and e.tag == etree.QName(ns['svg'], 'style'))
      ):
    root.remove(e)
# template preparation finished

# prepare regex for later use
regex_xmlns = re.compile(' xmlns[^ ]+',)
regex_space = re.compile('[>][ ]+[<]',)

# find all groups
layers = tree.xpath('//svg:g',namespaces=ns)
for layer in layers:
  i = layer.get('id')
  if ( # disregard non-content groups
    i.endswith("_") or # manually suppressed with underscore
    i.startswith("XMLID") or # Illustrator generated group
    i.startswith("g") # Inkscape generated group
  ):
    continue
  # create new canvas
  output = copy.deepcopy(template)
  # copy all shapes into template
  canvas = output.getroot()
  for e in layer:
    canvas.append(e)
  # represent template as SVG (binary string)
  svg = etree.tostring(output, pretty_print=False)
  # poor man's conditional defs insertion
  # TODO: extract only referenced defs (filters, gradients, ...)
  # TODO: detect necessity by traversing the elements. do not stupidly search in the string representation
  if ("filter:" in svg.decode('utf-8')):
    # it seems there is a filter referenced in the generated SVG, re-insert defs from main document
    canvas.insert(0,defs)
    # re-generate output
    svg = etree.tostring(output, pretty_print=False)

  if (output_format == 'tw'):
    # remove unnecessary attributes
    # TODO: never generate unnecessary attributes in the first place
    svg = svg.decode('utf-8')
    svg = regex_xmlns.sub('',svg)
    svg = svg.replace(' inkscape:connector-curvature="0"','') # this just saves space
    svg = svg.replace('\n','').replace('\r','') # print cannot be multi-line
    svg = regex_space.sub('><',svg) # remove indentaion
    svg = svg.replace('svg:','') # svg namespace was removed
    if ("Boob" in i): # internal groups are used for scaling
      svg = svg.replace('<g ','<g transform="\'+_artTransformBoob+\'"') # boob art uses the boob scaling
    elif ("Belly" in i):
      svg = svg.replace('<g ','<g transform="\'+_artTransformBelly+\'"') # belly art uses the belly scaling
    else:
      svg = svg.replace('<g ','<g transform="\'+_art_transform+\'"') # otherwise use default scaling
    svg = svg.encode('utf-8')

  # save SVG string to file
  i = layer.get('id')
  output_path = os.path.join(output_directory,i+'.'+output_format)
  with open(output_path, 'wb') as f:
    if (output_format == 'svg'):
      # Header for normal SVG (XML)
      f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
      f.write(svg)
    elif (output_format == 'tw'):
      # Header for SVG in Twine file (SugarCube print statement)
      f.write((':: Art_Vector_%s [nobr]\n\n'%(i)).encode("utf-8"))
      f.write("<<print '<html>".encode("utf-8"))
      f.write(svg)
      f.write("</html>' >>".encode("utf-8"))