/*
	ragaddress.ini generator; portions based on roextract <http://ro.zhasha.com/>
	Copyright (C) 2009 <renouille@choobs.org>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define ROWNDCLASS "Ragnarok"
#define ROWNDNAME NULL
#define VERSION "ragaddress.ini generator 2009-02-29 <renouille@choobs.org>\n"

#define _CRT_SECURE_NO_WARNINGS
#undef UNICODE
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <tlhelp32.h>

MEMORY_BASIC_INFORMATION mbi;
char *code;

int getaddr(const char *comment, const char *pattern, const char *mask)
{
	DWORD slen = strlen(mask);
	DWORD len = mbi.RegionSize - slen;
	DWORD target = 0;
	for (DWORD i = 0; i <= len; i++)
	{
		for (DWORD j = 0; j < slen; j++)
		{
			if (mask[j] == 'x' && code[i + j] != pattern[j])
			{
				target = 0;
				break;
			}
			if (mask[j] == '!' && !target)
				target = *((DWORD *)&code[i + j]);
		}
		if (target)
		{
			printf("%s=%08lx\n", comment, target);
			return 0;
		}
	}
	fprintf(stderr, "didn't find address! [%s]\n", comment);
	return 1;
}

int main(int argc, char **argv)
{
	HWND hwnd;
	DWORD pid = 0;
	HANDLE ph, fh;

	fprintf(stderr, VERSION);
	fprintf(stderr, "Searching for Ragnarok Window... ");
	hwnd = argv[1] ? FindWindow(NULL, argv[1]) : FindWindow(ROWNDCLASS, ROWNDNAME);
	if (hwnd == NULL)
	{
		fprintf(stderr, "not found\n");
		return 1;
	}
	fprintf(stderr, "found\nOpening process... ");
	GetWindowThreadProcessId(hwnd, &pid);
	ph = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
	if (ph == NULL)
	{
		fprintf(stderr, "failed\n");
		return 1;
	}
	fprintf(stderr, "success (pid=%lu)\n", pid);

	HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
	if (snap == INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "CreateToolhelp32Snapshot failed (%lu)\n", GetLastError());
		CloseHandle(ph);
		return 1;
	}
	MODULEENTRY32 meme;
	meme.dwSize = sizeof(meme);
	if (!Module32First(snap, &meme))
	{
		fprintf(stderr, "Module32First failed (%lu)\n", GetLastError());
		CloseHandle(snap);
		CloseHandle(ph);
		return 1;
	}
	CloseHandle(snap);

	fh = CreateFile(meme.szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (fh == INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "couldn't open `%s` (%lu)\n", meme.szExePath, GetLastError());
		CloseHandle(ph);
		return 1;
	}
	printf("[Ragexe]\nSize=%lu\n\n", GetFileSize(fh, NULL));
	CloseHandle(fh);

	if (!VirtualQueryEx(ph, (LPCVOID)0x401000, &mbi, sizeof(mbi)))
	{
		fprintf(stderr, "VirtualQueryEx failed (%lu)\n", GetLastError());
		CloseHandle(ph);
		return 1;
	}
	code = new char[mbi.RegionSize];
	if (!code)
	{
		fprintf(stderr, "memory allocation failed\n");
		CloseHandle(ph);
		return 1;
	}
	if (!ReadProcessMemory(ph, (LPCVOID)mbi.BaseAddress, code, mbi.RegionSize, NULL))
	{
		fprintf(stderr, "ReadProcessMemory failed (%lu)\n", GetLastError());
		CloseHandle(ph);
		delete code;
		return 1;
	}
	CloseHandle(ph);

	printf("[Address]\n");
	getaddr("Name", "\x56\x57\x8D\xB1\x5C\x07\x00\x00\xB9\x10\x00\x00\x00\xBF\x00\x00\x00\x00\xF3\xA5\x8B\x0D\xB8\xC2\x71\x00\x33\xC0\x81\xE9\x24\xD4\x7A\x00\x8A\x94\x01\x24\xD4\x7A\x00\x8A\x98\x24\xD4\x7A\x00", "xxxx????xxxxxx!!!!xxxx????xxxx????xxx????xx????");
	getaddr("Zeny", "\x6A\x00\x6A\x0E\x6A\x01\x8D\x95\x78\xFF\xFF\xFF\x6A\x00\x52\x6A\x67\x6A\x05\x8B\xCE\xE8\x8D\x11\x07\x00\x8B\x0D\xF0\x03\x7C\x00\x8D\x85\x38\xFF\xFF\xFF\x50\x51\xE8", "xxxxxxxxxxxxxxxxxxxxxx????xx!!!!xxxxxxxxx");
	getaddr("Exp", "\x6A\x4C\x6A\x54\x8B\x11\xFF\x52\x18\x8B\x8B\x90\x00\x00\x00\x68\x54\x03\x7C\x00\x68\x48\x03\x7C\x00\xE8\x0C\xA6\xFD\xFF\x8B\x83\x90\x00\x00\x00\x8B\xCB\x50", "xxxxxxxxxxx????x????x!!!!x????xx????xxx");
	getaddr("MaxExp", "\x6A\x4C\x6A\x54\x8B\x11\xFF\x52\x18\x8B\x8B\x90\x00\x00\x00\x68\x54\x03\x7C\x00\x68\x48\x03\x7C\x00\xE8\x0C\xA6\xFD\xFF\x8B\x83\x90\x00\x00\x00\x8B\xCB\x50", "xxxxxxxxxxx????x!!!!x????x????xx????xxx");
	getaddr("Job", "\x6A\x06\x6A\x66\xC6\x45\xFC\x08\xE8\x39\x0F\x07\x00\x8B\x8B\x94\x00\x00\x00\x6A\x56\x6A\x54\x8B\x11\xFF\x52\x18\x8B\x8B\x94\x00\x00\x00\x68\x00\x04\x7C\x00\x68\x04\x04\x7C\x00\xE8", "xxxxxxxxx????xx????xxxxxxxxxxx????x????x!!!!x");
	getaddr("MaxJob", "\x6A\x06\x6A\x66\xC6\x45\xFC\x08\xE8\x39\x0F\x07\x00\x8B\x8B\x94\x00\x00\x00\x6A\x56\x6A\x54\x8B\x11\xFF\x52\x18\x8B\x8B\x94\x00\x00\x00\x68\x00\x04\x7C\x00\x68\x04\x04\x7C\x00\xE8", "xxxxxxxxx????xx????xxxxxxxxxxx????x!!!!x????x");
	getaddr("Weight", "\xF3\xA4\x8B\x0D\x08\x7A\x72\x00\xE8\xA8\x01\xEA\xFF\x33\xF6\xB9\xA8\xB3\x7A\x00\x89\x35\x00\x00\x00\x00\xE8\xE6\xE5\x0E\x00\xB9\xA8\xB3\x7A\x00\x89\x35\xF4\xC3\x7A\x00\xE8\xD6\xE5\x0E\x00\x56", "xxxx????x????xxx????xx!!!!x????x????xx????x????x");
	getaddr("MaxWeight", "\x33\xF6\xB9\xA8\xB3\x7A\x00\x89\x35\x00\xC4\x7A\x00\xE8\xE6\xE5\x0E\x00\xB9\xA8\xB3\x7A\x00\x89\x35\x00\x00\x00\x00\xE8\xD6\xE5\x0E\x00\x56\xB9\x08\x15\x76\x00\xE8\x6B\x1B\xF7\xFF\xB9\x08\x15\x76\x00", "xxx????xx????x????x????xx!!!!x????xx????x????x????");
	getaddr("BaseLv", "\x83\xC4\x18\x85\xC0\x75\x70\xB9\x28\xED\x7B\x00\xE8\x3E\xE3\x20\x00\x3D\xCE\x0F\x00\x00\x7C\x5F\xB9\x28\xED\x7B\x00\xE8\x2D\xE3\x20\x00\x3D\xD1\x0F\x00\x00\x7F\x4E\xA1\x58\x03\x7C\x00\x8B\x15\x4C\x03\x7C\x00\x8B\x1D", "xxxxxxxx????x????xxxxxxxx????x????xxxxxxxx????xx!!!!xx");
	getaddr("JobLv", "\x83\xC4\x18\x85\xC0\x75\x70\xB9\x28\xED\x7B\x00\xE8\x3E\xE3\x20\x00\x3D\xCE\x0F\x00\x00\x7C\x5F\xB9\x28\xED\x7B\x00\xE8\x2D\xE3\x20\x00\x3D\xD1\x0F\x00\x00\x7F\x4E\xA1\x58\x03\x7C\x00\x8B\x15\x4C\x03\x7C\x00\x8B\x1D", "xxxxxxxx????x????xxxxxxxx????x????xxxxxxxx!!!!xx????xx");

	delete code;
	return 0;
}

