#include <os/os.h>
#include <os/pe.h>
#include <wchar.h>
#include <stdlib.h>
#include <errno.h>

static const IMAGE_RESOURCE_DIRECTORY_ENTRY* resFindItem(dword base, 
	const IMAGE_RESOURCE_DIRECTORY* dir, const word* ids)
{
	const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
	int i;

	entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*) (dir + 1);
	for (i = 0; i < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; i++)
	{
		if (entry[i].u.Id == ids[0] || ids[0] == 0)
		{
			ids++;
			if (entry[i].u2.s.DataIsDirectory)
			{
				//wprintf(L"%x: Directory: offset to directory = %x\n", 
					//entry[i].u.Id, entry[i].u2.s.OffsetToDirectory);
				dir = (const IMAGE_RESOURCE_DIRECTORY*) (base + entry[i].u2.s.OffsetToDirectory);
				return resFindItem(base, dir, ids);
			}
			else
			{
				//wprintf(L"%x: Resource: offset to data = %x\n", 
					//entry[i].u.Id, entry[i].u2.OffsetToData);
				return entry + i;
			}
		}
	}

	//wprintf(L"%x: Not found\n", ids[0]);
	sysSetErrno(ENOTFOUND);
	return NULL;
}
  
//! Finds a resource in the specified module.
/*!
 *	\param	base	The base address of the module where the resource is to be 
 *		found. Use _info.base for the current module, or another base address if 
 *		a different module is to be used.
 *
 *	\param	type	A pre-defined or numeric user-defined resource type.
 *	Pre-defined resource types are:
 *	- RT_CURSOR		A cursor at a specific resolution and colour depth
 *	- RT_BITMAP		A bitmap
 *	- RT_ICON		An icon at a specific resolution and colour depth
 *	- RT_MENU		A menu definition
 *	- RT_DIALOG		A dialog box definition
 *	- RT_STRING		A block of up to 16 strings (use resLoadString() to load a 
 *		specific string)
 *	- RT_FONTDIR	A list of fonts
 *	- RT_FONT		A font with a specific typeface, weight, size and attributes
 *	- RT_ACCEL		A list of keyboard accelerators
 *	- RT_RCDATA		Arbitrary binary data
 *	- RT_MSGTABLE	A table of error messages
 *	- RT_GROUP_CURSOR	A group of cursors which describe the same image but at
 *		different resolutions and colour depths
 *	- RT_GROUP_ICON	A group of icons which describe the same image but at
 *		different resolutions and colour depths
 *	- RT_VERSION	A version information block
 *	\param	id		The numeric identifier of the resource to be loaded.
 *	\param	language	The ID of the specific language of the resource to
 *		be loaded. Passing NULL for this parameter specifies the default 
 *		language for the process.
 *
 *	\note The Möbius only supports numeric resource IDs; string IDs (such
 *		as those used in Microsoft Windows) are not supported.
 *
 *	\return	A pointer to the start of the resource within the specified module,
 *		or NULL if the resource could not be found.
 *		Since the resource is contained within the image of the module in 
 *		memory, it is read-only (or it conforms to the attributes of the 
 *		PE section that contains it). There is also no corresponding function
 *		to free a loaded resource; all resources are freed when the process
 *		terminates.
 */
const void* resFind(dword base, word type, word id, word language)
{
	word ids[4] = { type, id, language, 0 };
	const IMAGE_DOS_HEADER* dos_head;
	const IMAGE_PE_HEADERS* header;
	const IMAGE_RESOURCE_DIRECTORY *dir;
	const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
	const IMAGE_RESOURCE_DATA_ENTRY *data;
	
	if (!base)
		return NULL;

	dos_head = (const IMAGE_DOS_HEADER*) base;
	header = (const IMAGE_PE_HEADERS*) ((char *) dos_head + dos_head->e_lfanew);
	dir = (const IMAGE_RESOURCE_DIRECTORY*) 
		(base + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);

	if ((dword) dir <= base)
	{
		sysSetErrno(ENOTFOUND);
		return NULL;
	}

	entry = resFindItem((dword) dir, dir, ids);
	if (entry)
	{
		//wprintf(L"entry->OffsetToData = %x\n", entry->OffsetToData);
		data = (const IMAGE_RESOURCE_DATA_ENTRY*) ((byte*) dir + entry->u2.OffsetToData);
		//wprintf(L"data->OffsetToData = %x\n", data->OffsetToData);
		return (const void*) (base + data->OffsetToData);
	}
	else
		return NULL;
}

//! Loads a string from the specified module.
/*!
 *	\param	base	The base address of the module where the resource is to be 
 *		found. Use _info.base for the current module, or another base address if 
 *		a different module is to be used.
 *	\param	id		The numeric identifier of the string to be loaded.
 *	\param	str		String buffer to receive the loaded string.
 *	\param	str_max	Size, in characters, of the buffer pointed to by str.
 *
 *	\return	\p true if the string was found and loaded successfully; false 
 *		otherwise.
 *	\note	Due to a limitation of the PE resource file format, it is not 
 *		possible to distinguish between a zero-length string and a not-present
 *		string if there are any strings within the same block of 16.
 */
bool resLoadString(dword base, word id, wchar_t* str, size_t str_max)
{
	const wchar_t* buf;
	word i;

	buf = resFind(base, RT_STRING, (word) ((id >> 4) + 1), 0);
	if (buf)
	{
		id &= 15;

		for (i = 0; i < id; i++)
		{
			//wprintf(L"%x\t%s\n", buf[0], buf + 1);
			buf += buf[0] + 1;
		}

		wcsncpy(str, buf + 1, min((word) buf[0], str_max));
		return true;
	}
	else
		return false;
}

