//----------------------------------------------------------------------
//  This software is part of the OpenBeOS distribution and is covered 
//  by the OpenBeOS license.
//---------------------------------------------------------------------
/*!
	\file Resources.cpp
	BResources implementation.

	BResources delegates most of the work to ResourcesContainer and
	ResourceFile. The first one manages a collections of ResourceItem's,
	the actual resources, whereas the latter provides the file I/O
	functionality.
	An InitCheck() method is not needed, since a BResources object will
	never be invalid. It always serves as a resources container, even if
	it is not associated with a file. It is always possible to WriteTo()
	the resources BResources contains to a file (a valid one of course).
*/
#include <Resources.h>

#include <new>
#include <stdlib.h>

#include "ResourceFile.h"
#include "ResourceItem.h"
#include "ResourcesContainer.h"

using namespace BPrivate::Storage;

enum {
	NOT_IMPLEMENTED	= B_ERROR,
};

// constructor
/*!	\brief Creates an unitialized BResources object.
*/
BResources::BResources()
		  : fFile(),
			fContainer(NULL),
			fResourceFile(NULL),
			fReadOnly(false)
{
	fContainer = new(nothrow) ResourcesContainer;
}

// constructor
/*!	\brief Creates a BResources object that represents the resources of the
	supplied file.
	If the \a clobber argument is \c true, the data of the file are erased
	and it is turned into an empty resource file. Otherwise \a file
	must refer either to a resource file or to an executable (ELF or PEF
	binary). If the file has been opened \c B_READ_ONLY, only read access
	to its resources is possible.
	The BResources object makes a copy of \a file, that is the caller remains
	owner of the BFile object.
	\param file the file
	\param clobber if \c true, the \a file is truncated to size 0
*/
BResources::BResources(const BFile *file, bool clobber)
		  : fFile(),
			fContainer(NULL),
			fResourceFile(NULL),
			fReadOnly(false)
{
	fContainer = new(nothrow) ResourcesContainer;
	SetTo(file, clobber);
}

// destructor
/*!	\brief Frees all resources associated with this object
	Calls Sync() before doing so to make sure that the changes are written
	back to the file.
*/
BResources::~BResources()
{
	Unset();
	delete fContainer;
}

// SetTo
/*!	\brief Re-initialized the BResources object to represent the resources of
	the supplied file.
	What happens, if \a clobber is \c true, depends on the type of the file.
	If the file is capable of containing resources, that is, is a resource
	file or an executable (ELF or PEF), its resources are removed. Otherwise
	the file's data are erased and it is turned into an empty resource file.
	If \a clobber is \c false, \a file must refer to a file that is capable
	of containing resources.
	If the file has been opened \c B_READ_ONLY, only read access
	to its resources is possible.
	The BResources object makes a copy of \a file, that is the caller remains
	owner of the BFile object.
	\param file the file
	\param clobber if \c true, the \a file is truncated to size 0
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \c NULL or uninitialized \a file.
	- \c B_ERROR: Failed to initialize the object (for whatever reason).
*/
status_t
BResources::SetTo(const BFile *file, bool clobber)
{
	Unset();
	status_t error = B_OK;
	if (file) {
		error = file->InitCheck();
		if (error == B_OK) {
			fFile = *file;
			error = fFile.InitCheck();
		}
		if (error == B_OK) {
			fReadOnly = !fFile.IsWritable();
			fResourceFile = new(nothrow) ResourceFile;
			if (fResourceFile)
				error = fResourceFile->SetTo(&fFile, clobber);
			else
				error = B_NO_MEMORY;
		}
		if (error == B_OK) {
			if (fContainer)
				error = fResourceFile->InitContainer(*fContainer);
			else
				error = B_NO_MEMORY;
		}
	}
	if (error != B_OK) {
		delete fResourceFile;
		fResourceFile = NULL;
		if (fContainer)
			fContainer->MakeEmpty();
	}
	return error;
}

// Unset
/*!	\brief Returns the BResources object to an uninitialized state.
	If the object represented resources that had been modified, the data are
	written back to the file.
	\note This method extends the BeOS R5 API.
*/
void
BResources::Unset()
{
	if (fContainer && fContainer->IsModified())
		Sync();
	delete fResourceFile;
	fResourceFile = NULL;
	fFile.Unset();
	if (fContainer)
		fContainer->MakeEmpty();
	else
		fContainer = new(nothrow) ResourcesContainer;
	fReadOnly = false;
}

// InitCheck
/*!	Returns the current initialization status.
	Unlike other Storage Kit classes a BResources object is always properly
	initialized, unless it couldn't allocate memory for some important
	internal structures. Thus even after a call to SetTo() that reported an
	error, InitCheck() is likely to return \c B_OK.
	\return
	- \c B_OK, if the objects is properly initialized,
	- \c B_NO_MEMORY otherwise.
	\note This method extends the BeOS R5 API.
*/
status_t
BResources::InitCheck() const
{
	return (fContainer ? B_OK : B_NO_MEMORY);
}

// File
/*!	\brief Returns a reference to the BResources' BFile object.
	\return a reference to the object's BFile.
*/
const BFile &
BResources::File() const
{
	return fFile;
}

// LoadResource
/*!	\brief Loads a resource identified by type and ID into memory.
	A resource is loaded into memory only once. A second call with the same
	parameters will result in the same pointer. The BResources object is the
	owner of the allocated memory and the pointer to it will be valid until
	the object is destroyed or the resource is removed or modified.
	\param type the type of the resource to be loaded
	\param id the ID of the resource to be loaded
	\param outSize a pointer to a variable into which the size of the resource
		   shall be written
	\return A pointer to the resource data, if everything went fine, or
			\c NULL, if the file does not have a resource that matchs the
			parameters or an error occured.
*/
const void *
BResources::LoadResource(type_code type, int32 id, size_t *outSize)
{
	// find the resource
	status_t error = InitCheck();
	ResourceItem *resource = NULL;
	if (error == B_OK) {
		resource = fContainer->ResourceAt(fContainer->IndexOf(type, id));
		if (!resource)
			error = B_ENTRY_NOT_FOUND;
	}
	// load it, if necessary
	if (error == B_OK && !resource->IsLoaded() && fResourceFile)
		error = fResourceFile->ReadResource(*resource);
	// return the result
	const void *result = NULL;
	if (error == B_OK) {
		result = resource->Data();
		if (outSize)
			*outSize = resource->DataSize();
	}
	return result;
}

// LoadResource
/*!	\brief Loads a resource identified by type and name into memory.
	A resource is loaded into memory only once. A second call with the same
	parameters will result in the same pointer. The BResources object is the
	owner of the allocated memory and the pointer to it will be valid until
	the object is destroyed or the resource is removed or modified.
	\param type the type of the resource to be loaded
	\param name the name of the resource to be loaded
	\param outSize a pointer to a variable into which the size of the resource
		   shall be written
	\return A pointer to the resource data, if everything went fine, or
			\c NULL, if the file does not have a resource that matches the
			parameters or an error occured.
	\note Since a type and name pair may not identify a resource uniquely,
		  this method always returns the first resource that matches the
		  parameters, that is the one with the least index.
*/
const void *
BResources::LoadResource(type_code type, const char *name, size_t *outSize)
{
	// find the resource
	status_t error = InitCheck();
	ResourceItem *resource = NULL;
	if (error == B_OK) {
		resource = fContainer->ResourceAt(fContainer->IndexOf(type, name));
		if (!resource)
			error = B_ENTRY_NOT_FOUND;
	}
	// load it, if necessary
	if (error == B_OK && !resource->IsLoaded() && fResourceFile)
		error = fResourceFile->ReadResource(*resource);
	// return the result
	const void *result = NULL;
	if (error == B_OK) {
		result = resource->Data();
		if (outSize)
			*outSize = resource->DataSize();
	}
	return result;
}

// PreloadResourceType
/*!	\brief Loads all resources of a certain type into memory.
	For performance reasons it might be useful to do that. If \a type is
	0, all resources are loaded.
	\param type of the resources to be loaded
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_FILE: The resource map is empty???
	- The negative of the number of errors occured.
*/
status_t
BResources::PreloadResourceType(type_code type)
{
	status_t error = InitCheck();
	if (error == B_OK && fResourceFile) {
		if (type == 0)
			error = fResourceFile->ReadResources(*fContainer);
		else {
			int32 count = fContainer->CountResources();
			int32 errorCount = 0;
			for (int32 i = 0; i < count; i++) {
				ResourceItem *resource = fContainer->ResourceAt(i);
				if (resource->Type() == type) {
					if (fResourceFile->ReadResource(*resource) != B_OK)
						errorCount++;
				}
			}
			error = -errorCount;
		}
	}
	return error;
}

// Sync
/*!	\brief Writes all changes to the resources to the file.
	Since AddResource() and RemoveResource() may change the resources only in
	memory, this method can be used to make sure, that all changes are
	actually written to the file.
	The BResources object's destructor calls Sync() before cleaning up.
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_FILE: The resource map is empty???
	- \c B_NOT_ALLOWED: The file is opened read only.
	- \c B_FILE_ERROR: A file error occured.
	- \c B_IO_ERROR: An error occured while writing the resources.
	\note When a resource is written to the file, its data are converted
		  to the endianess of the file, and when reading a resource, the
		  data are converted to the host's endianess. This does of course
		  only work for known types, i.e. those that swap_data() is able to
		  cope with.
*/
status_t
BResources::Sync()
{
	status_t error = InitCheck();
	if (error == B_OK)
		error = fFile.InitCheck();
	if (error == B_OK) {
		if (fReadOnly)
			error = B_NOT_ALLOWED;
		else if (!fResourceFile)
			error = B_FILE_ERROR;
	}
	if (error == B_OK)
		error = fResourceFile->ReadResources(*fContainer);
	if (error == B_OK)
		error = fResourceFile->WriteResources(*fContainer);
	return error;
}

// MergeFrom
/*!	\brief Adds the resources of the supplied file to this file's resources.
	\param fromFile the file whose resources shall be copied
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \c NULL \a fromFile.
	- \c B_BAD_FILE: The resource map is empty???
	- \c B_FILE_ERROR: A file error occured.
	- \c B_IO_ERROR: An error occured while writing the resources.
*/
status_t
BResources::MergeFrom(BFile *fromFile)
{
	status_t error = (fromFile ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	if (error == B_OK) {
		ResourceFile resourceFile;
		error = resourceFile.SetTo(fromFile);
		ResourcesContainer container;
		if (error == B_OK)
			error = resourceFile.InitContainer(container);
		if (error == B_OK)
			error = resourceFile.ReadResources(container);
		if (error == B_OK)
			fContainer->AssimilateResources(container);
	}
	return error;
}

// WriteTo
/*!	\brief Writes the resources to a new file.
	The resources formerly contained in the target file (if any) are erased.
	When the method returns, the BResources object refers to the new file.
	\param file the file the resources shall be written to.
	\return
	- \c B_OK: Everything went fine.
	- a specific error code.
	\note If the resources have been modified, but not Sync()ed, the old file
		  remains unmodified.
*/
status_t
BResources::WriteTo(BFile *file)
{
	status_t error = (file ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	// make sure, that all resources are loaded
	if (error == B_OK && fResourceFile) {
		error = fResourceFile->ReadResources(*fContainer);
		fResourceFile->Unset();
	}
	// set the new file, but keep the old container
	if (error == B_OK) {
		ResourcesContainer *container = fContainer;
		fContainer = new(nothrow) ResourcesContainer;
		if (fContainer) {
			error = SetTo(file, false);
			delete fContainer;
		} else
			error = B_NO_MEMORY;
		fContainer = container;
	}
	// write the resources
	if (error == B_OK && fResourceFile)
		error = fResourceFile->WriteResources(*fContainer);
	return error;
}

// AddResource
/*!	\brief Adds a new resource to the file.
	If a resource with the same type and ID does already exist, it is
	replaced. The caller keeps the ownership of the supplied chunk of memory
	containing the resource data.
	Supplying an empty name (\c "") is equivalent to supplying a \c NULL name.
	\param type the type of the resource
	\param id the ID of the resource
	\param data the resource data
	\param length the size of the data in bytes
	\param name the name of the resource (may be \c NULL)
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \c NULL \a data
	- \c B_NOT_ALLOWED: The file is opened read only.
	- \c B_FILE_ERROR: A file error occured.
	- \c B_NO_MEMORY: Not enough memory for that operation.
*/
status_t
BResources::AddResource(type_code type, int32 id, const void *data,
						size_t length, const char *name)
{
	status_t error = (data ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	if (error == B_OK)
		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
	if (error == B_OK) {
		ResourceItem *item = new(nothrow) ResourceItem;
		if (!item)
			error = B_NO_MEMORY;
		if (error == B_OK) {
			item->SetIdentity(type, id, name);
			ssize_t written = item->WriteAt(0, data, length);
			if (written < 0)
				error = written;
			else if (written != (ssize_t)length)
				error = B_ERROR;
		}
		if (error == B_OK) {
			if (!fContainer->AddResource(item))
				error = B_NO_MEMORY;
		}
		if (error != B_OK)
			delete item;
	}
	return error;
}

// HasResource
/*!	\brief Returns whether the file contains a resource with a certain
	type and ID.
	\param type the resource type
	\param id the ID of the resource
	\return \c true, if the file contains a matching resource, \false otherwise
*/
bool
BResources::HasResource(type_code type, int32 id)
{
	return (InitCheck() == B_OK && fContainer->IndexOf(type, id) >= 0);
}

// HasResource
/*!	\brief Returns whether the file contains a resource with a certain
	type and name.
	\param type the resource type
	\param name the name of the resource
	\return \c true, if the file contains a matching resource, \false otherwise
*/
bool
BResources::HasResource(type_code type, const char *name)
{
	return (InitCheck() == B_OK && fContainer->IndexOf(type, name) >= 0);
}

// GetResourceInfo
/*!	\brief Returns information about a resource identified by an index.
	\param byIndex the index of the resource in the file
	\param typeFound a pointer to a variable the type of the found resource
		   shall be written into
	\param idFound a pointer to a variable the ID of the found resource
		   shall be written into
	\param nameFound a pointer to a variable the name pointer of the found
		   resource shall be written into
	\param lengthFound a pointer to a variable the data size of the found
		   resource shall be written into
	\return \c true, if a matching resource could be found, false otherwise
*/
bool
BResources::GetResourceInfo(int32 byIndex, type_code *typeFound,
							int32 *idFound, const char **nameFound,
							size_t *lengthFound)
{
	ResourceItem *item = NULL;
	if (InitCheck() == B_OK)
		item = fContainer->ResourceAt(byIndex);
	if (item) {
		if (typeFound)
			*typeFound = item->Type();
		if (idFound)
			*idFound = item->ID();
		if (nameFound)
			*nameFound = item->Name();
		if (lengthFound)
			*lengthFound = item->DataSize();
	}
	return item;
}

// GetResourceInfo
/*!	\brief Returns information about a resource identified by a type and an
	index.
	\param byType the resource type
	\param andIndex the index into a array of resources of type \a byType
	\param idFound a pointer to a variable the ID of the found resource
		   shall be written into
	\param nameFound a pointer to a variable the name pointer of the found
		   resource shall be written into
	\param lengthFound a pointer to a variable the data size of the found
		   resource shall be written into
	\return \c true, if a matching resource could be found, false otherwise
*/
bool
BResources::GetResourceInfo(type_code byType, int32 andIndex, int32 *idFound,
							const char **nameFound, size_t *lengthFound)
{
	ResourceItem *item = NULL;
	if (InitCheck() == B_OK) {
		item = fContainer->ResourceAt(fContainer->IndexOfType(byType,
															  andIndex));
	}
	if (item) {
		if (idFound)
			*idFound = item->ID();
		if (nameFound)
			*nameFound = item->Name();
		if (lengthFound)
			*lengthFound = item->DataSize();
	}
	return item;
}

// GetResourceInfo
/*!	\brief Returns information about a resource identified by a type and an ID.
	\param byType the resource type
	\param andID the resource ID
	\param nameFound a pointer to a variable the name pointer of the found
		   resource shall be written into
	\param lengthFound a pointer to a variable the data size of the found
		   resource shall be written into
	\return \c true, if a matching resource could be found, false otherwise
*/
bool
BResources::GetResourceInfo(type_code byType, int32 andID,
							const char **nameFound, size_t *lengthFound)
{
	ResourceItem *item = NULL;
	if (InitCheck() == B_OK)
		item = fContainer->ResourceAt(fContainer->IndexOf(byType, andID));
	if (item) {
		if (nameFound)
			*nameFound = item->Name();
		if (lengthFound)
			*lengthFound = item->DataSize();
	}
	return item;
}

// GetResourceInfo
/*!	\brief Returns information about a resource identified by a type and a
	name.
	\param byType the resource type
	\param andName the resource name
	\param idFound a pointer to a variable the ID of the found resource
		   shall be written into
	\param lengthFound a pointer to a variable the data size of the found
		   resource shall be written into
	\return \c true, if a matching resource could be found, false otherwise
*/
bool
BResources::GetResourceInfo(type_code byType, const char *andName,
							int32 *idFound, size_t *lengthFound)
{
	ResourceItem *item = NULL;
	if (InitCheck() == B_OK)
		item = fContainer->ResourceAt(fContainer->IndexOf(byType, andName));
	if (item) {
		if (idFound)
			*idFound = item->ID();
		if (lengthFound)
			*lengthFound = item->DataSize();
	}
	return item;
}

// GetResourceInfo
/*!	\brief Returns information about a resource identified by a data pointer.
	\param byPointer the pointer to the resource data (formely returned by
		   LoadResource())
	\param typeFound a pointer to a variable the type of the found resource
		   shall be written into
	\param idFound a pointer to a variable the ID of the found resource
		   shall be written into
	\param lengthFound a pointer to a variable the data size of the found
		   resource shall be written into
	\param nameFound a pointer to a variable the name pointer of the found
		   resource shall be written into
	\return \c true, if a matching resource could be found, false otherwise
*/
bool
BResources::GetResourceInfo(const void *byPointer, type_code *typeFound,
							int32 *idFound, size_t *lengthFound,
							const char **nameFound)
{
	ResourceItem *item = NULL;
	if (InitCheck() == B_OK)
		item = fContainer->ResourceAt(fContainer->IndexOf(byPointer));
	if (item) {
		if (typeFound)
			*typeFound = item->Type();
		if (idFound)
			*idFound = item->ID();
		if (nameFound)
			*nameFound = item->Name();
		if (lengthFound)
			*lengthFound = item->DataSize();
	}
	return item;
}

// RemoveResource
/*!	\brief Removes a resource identified by its data pointer.
	\param resource the pointer to the resource data (formely returned by
		   LoadResource())
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \c NULL or invalid (not pointing to any resource data of
	  this file) \a resource.
	- \c B_NOT_ALLOWED: The file is opened read only.
	- \c B_FILE_ERROR: A file error occured.
	- \c B_ERROR: An error occured while removing the resource.
*/
status_t
BResources::RemoveResource(const void *resource)
{
	status_t error = (resource ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	if (error == B_OK)
		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
	if (error == B_OK) {
		ResourceItem *item
			= fContainer->RemoveResource(fContainer->IndexOf(resource));
		if (item)
			delete item;
		else
			error = B_BAD_VALUE;
	}
	return error;
}

// RemoveResource
/*!	\brief Removes a resource identified by type and ID.
	\param type the type of the resource
	\param id the ID of the resource
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: No such resource.
	- \c B_NOT_ALLOWED: The file is opened read only.
	- \c B_FILE_ERROR: A file error occured.
	- \c B_ERROR: An error occured while removing the resource.
*/
status_t
BResources::RemoveResource(type_code type, int32 id)
{
	status_t error = InitCheck();
	if (error == B_OK)
		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
	if (error == B_OK) {
		ResourceItem *item
			= fContainer->RemoveResource(fContainer->IndexOf(type, id));
		if (item)
			delete item;
		else
			error = B_BAD_VALUE;
	}
	return error;
}


// deprecated

// WriteResource
/*!	\brief Writes data into an existing resource.
	If writing the data would exceed the bounds of the resource, it is
	enlarged respectively. If \a offset is past the end of the resource,
	padding with unspecified data is inserted.
	\param type the type of the resource
	\param id the ID of the resource
	\param data the data to be written
	\param offset the byte offset relative to the beginning of the resource at
		   which the data shall be written
	\param length the size of the data to be written
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \a type and \a id do not identify an existing resource or
	  \c NULL \a data.
	- \c B_NO_MEMORY: Not enough memory for this operation.
	- other error codes.
	\deprecated Always use AddResource().
*/
status_t
BResources::WriteResource(type_code type, int32 id, const void *data,
						  off_t offset, size_t length)
{
	status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	if (error == B_OK)
		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
	ResourceItem *item = NULL;
	if (error == B_OK) {
		item = fContainer->ResourceAt(fContainer->IndexOf(type, id));
		if (!item)
			error = B_BAD_VALUE;
	}
	if (error == B_OK && fResourceFile)
		error = fResourceFile->ReadResource(*item);
	if (error == B_OK) {
		if (item) {
			ssize_t written = item->WriteAt(offset, data, length);
			if (written < 0)
				error = written;
			else if (written != (ssize_t)length)
				error = B_ERROR;
		}
	}
	return error;
}

// ReadResource
/*!	\brief Reads data from an existing resource.
	If more data than existing are requested, this method does not fail. It
	will then read only the existing data. As a consequence an offset past
	the end of the resource will not cause the method to fail, but no data
	will be read at all.
	\param type the type of the resource
	\param id the ID of the resource
	\param data a pointer to a buffer into which the data shall be read
	\param offset the byte offset relative to the beginning of the resource
		   from which the data shall be read
	\param length the size of the data to be read
	\return
	- \c B_OK: Everything went fine.
	- \c B_BAD_VALUE: \a type and \a id do not identify an existing resource or
	  \c NULL \a data.
	- \c B_NO_MEMORY: Not enough memory for this operation.
	- other error codes.
	\deprecated Use LoadResource() only.
*/
status_t
BResources::ReadResource(type_code type, int32 id, void *data, off_t offset,
						 size_t length)
{
	status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE);
	if (error == B_OK)
		error = InitCheck();
	ResourceItem *item = NULL;
	if (error == B_OK) {
		item = fContainer->ResourceAt(fContainer->IndexOf(type, id));
		if (!item)
			error = B_BAD_VALUE;
	}
	if (error == B_OK && fResourceFile)
		error = fResourceFile->ReadResource(*item);
	if (error == B_OK) {
		if (item) {
			ssize_t read = item->ReadAt(offset, data, length);
			if (read < 0)
				error = read;
		} else
			error = B_BAD_VALUE;
	}
	return error;
}

// FindResource
/*!	\brief Finds a resource by type and ID and returns a copy of its data.
	The caller is responsible for free()ing the returned memory.
	\param type the type of the resource
	\param id the ID of the resource
	\param lengthFound a pointer to a variable into which the size of the
		   resource data shall be written
	\return
	- a pointer to the resource data, if everything went fine,
	- \c NULL, if an error occured.
	\deprecated Use LoadResource().
*/
void *
BResources::FindResource(type_code type, int32 id, size_t *lengthFound)
{
	void *result = NULL;
	size_t size = 0;
	if (const void *data = LoadResource(type, id, &size)) {
		if ((result = malloc(size)))
			memcpy(result, data, size);
	}
	if (lengthFound)
		*lengthFound = size;
	return result;
}

// FindResource
/*!	\brief Finds a resource by type and name and returns a copy of its data.
	The caller is responsible for free()ing the returned memory.
	\param type the type of the resource
	\param name the name of the resource
	\param lengthFound a pointer to a variable into which the size of the
		   resource data shall be written
	\return
	- a pointer to the resource data, if everything went fine,
	- \c NULL, if an error occured.
	\deprecated Use LoadResource().
*/
void *
BResources::FindResource(type_code type, const char *name, size_t *lengthFound)
{
	void *result = NULL;
	size_t size = 0;
	if (const void *data = LoadResource(type, name, &size)) {
		if ((result = malloc(size)))
			memcpy(result, data, size);
	}
	if (lengthFound)
		*lengthFound = size;
	return result;
}


// FBC
void BResources::_ReservedResources1() {}
void BResources::_ReservedResources2() {}
void BResources::_ReservedResources3() {}
void BResources::_ReservedResources4() {}
void BResources::_ReservedResources5() {}
void BResources::_ReservedResources6() {}
void BResources::_ReservedResources7() {}
void BResources::_ReservedResources8() {}




