/*
	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-18 <renouille@choobs.org>\n"

#define _CRT_SECURE_NO_WARNINGS
#undef UNICODE
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <psapi.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;
	for (DWORD i = 0; i <= len; i++)
	{
		target = 0;
		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;
	char ragexe[MAX_PATH];

	fprintf(stderr, VERSION);
	fprintf(stderr, "Searching for Ragnarok Window... ");
	hwnd = 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);

	if (!GetModuleFileNameEx(ph, NULL, ragexe, sizeof(ragexe)))
	{
		fprintf(stderr, "GetModuleFileNameEx failed (%lu)\n", GetLastError());
		CloseHandle(ph);
		return 1;
	}
	fh = CreateFile(ragexe, 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", ragexe, GetLastError());
		CloseHandle(ph);
		return 1;
	}
	printf("[Ragexe]\n");
	printf("Size=%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", "\x89\x04\x8D\x6C\xD0\x7A\x00\xB9\xA8\xB3\x7A\x00\xE8\xAD\x0F\x09\x00\x8B\x86\x38\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\xA3\x00\x00\x00\x00\xE8\xD8\x0F\x09\x00\x8B\x8E\x3C\x0E\x00\x00\x89\x0D\xFC\xC3\x7A\x00\xB9\xA8\xB3\x7A\x00\xE8\xC2\x0F\x09\x00", "xxx????x????x????xxxxxxx????x!!!!x????xxxxxxxx????x????x????");
	getaddr("Exp", "\x8A\x96\x97\x0E\x00\x00\x89\x15\x90\xC3\x7A\x00\xE8\x02\x11\x09\x00\x8B\x86\x34\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\xA3\x00\x00\x00\x00\xE8\xED\x10\x09\x00\x8B\x0D\x00\xD0\x7A\x00\x0F\xBF\x86\x5A\x0E\x00\x00\x8B\x14\x8D\x0C\xD0\x7A\x00\xB9\xA8\xB3\x7A\x00", "xxxxxxxx????x????xxxxxxx????x!!!!x????xx????xxxxxxxxxx????x????");
	getaddr("MaxExp", "\x8B\xE5\x5D\xC2\x04\x00\x8B\x53\x04\xB9\xA8\xB3\x7A\x00\x89\x15\x00\x00\x00\x00\xE8\x7C\x0F\x0C\x00\x5B\x8B\xE5\x5D\xC2\x04\x00", "xxxxxxxxxx????xx!!!!x????xxxxxxx");
	getaddr("Job", "\x8B\x86\x38\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\xA3\xE8\xC3\x7A\x00\xE8\xD8\x0F\x09\x00\x8B\x8E\x3C\x0E\x00\x00\x89\x0D\x00\x00\x00\x00\xB9\xA8\xB3\x7A\x00\xE8\xC2\x0F\x09\x00\x8B\x96\x50\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\x89\x15\x04\xC4\x7A\x00", "xxxxxxx????x????x????xxxxxxxx!!!!x????x????xxxxxxx????xx????");
	getaddr("MaxJob", "\x8B\x53\x04\xB9\xF8\xDC\x79\x00\x89\x15\x9C\xEC\x79\x00\xE8\x6C\x2C\x0C\x00\x5B\x8B\xE5\x5D\xC2\x04\x00\x8B\x43\x04\xB9\xA8\xB3\x7A\x00\xA3\x00\x00\x00\x00\xE8\x63\x0F\x0C\x00\x5B\x8B\xE5\x5D\xC2\x04\x00", "xxxx????xx????x????xxxxxxxxxxx????x!!!!x????xxxxxxx");
	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", "\x0F\xBF\x96\x62\x0E\x00\x00\x89\x15\xEC\xC3\x7A\x00\xE8\xD4\x11\x09\x00\x0F\xBF\x86\x6A\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\xA3\x00\x00\x00\x00\xE8\xBE\x11\x09\x00\x0F\xBF\x8E\x6C\x0E\x00\x00\x89\x0D\x54\xC3\x7A\x00\xB9\xA8\xB3\x7A\x00\xE8\xA7\x11\x09\x00", "xxxxxxxxx????x????xxxxxxxx????x!!!!x????xxxxxxxxx????x????x????");
	getaddr("JobLv", "\x8B\x86\x54\x0E\x00\x00\xB9\xA8\xB3\x7A\x00\xA3\xF0\xC3\x7A\x00\xE8\x97\x0F\x09\x00\x8B\x8E\x40\x0E\x00\x00\x89\x0D\x00\x00\x00\x00\xB9\xA8\xB3\x7A\x00\xE8\x81\x0F\x09\x00\x33\xD2\x8A\x96\x99\x0E\x00\x00\x89\x15\x90\xBC\x7A\x00", "xxxxxxx????x????x????xxxxxxxx!!!!x????x????xxxxxxxxxx????");

	delete code;
	return 0;
}