/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   resmgr.c
Author(s)   :   Kenny Ng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   Resource Manager
Revision    :   1.1
Note        :   2/11/98  Add virtual memory support
Res() support bitmap word alignment
3/11/98  ResSkipByte() bug fix      
2/10/99	 System resource support
===========================================================================
*/              


#include "stdafx.h"
#include "datatype.h"
#include "mmu.h"
#include "ui.h"
#include "kernel.h"
#include "resmgr.h"

ResLnk *openres_table;
extern MatInfoList  mat_info[TOTAL_SLOT];
extern UBYTE install_icon_res_data[];


/*	Function	: ResObjLocate
*	Purpose		: Find the physical address of the object
*  Scope		: Internal
*	Input		: res_addr:	start physical address of the resource file
*				  obj_id:	object id
*	Output      : obj_addr:	physical address of the object
*  Return		: TRUE:	 Success
*				  FALSE: Not found
*	Comment		: 
*/
BOOLEAN ResObjLocate(UWORD res_addr, ObjectID obj_id, UWORD *obj_addr)
{
	BYTE *obj_ptr = (BYTE*) res_addr;
	WORD total_len;
	UWORD last_addr;
	ObjectID res_obj_id;
	UWORD field_len;
	
	ResReadByte(res_addr, 4, (BYTE*) &total_len, &last_addr);	
	total_len -=2;
	while(total_len)
	{
		ResReadByte(last_addr, 2, (BYTE*) &res_obj_id, &last_addr);  /* object 0's ID */
		
		if(res_obj_id == obj_id)
		{
			*obj_addr = last_addr;
			return TRUE;
		}
		
		ResReadByte(last_addr, 4, (BYTE*) &field_len, NULL);
		ResSkipByte(last_addr, field_len, &last_addr);
		total_len -= field_len;
	}
	
	return FALSE;
	
}



/*	Function	: ResOpen
*	Purpose		: Open a resource object
*  Scope		: UI
*	Input		: obj_id: object ID
*	Output      : None
*  Return		: TRUE:	Success
*				  ERR_RES_OBJ_MISS:	object not found
*				  ERR_RES_NO_MEM:	not enough memory to open
*								    an entry in the res. table
*	Comment		: A new entry is created in the head of openres_table
*                Call ResClose() to remove the entry  
*/
extern UBYTE sysres_data[];
Err ResOpen(ObjectID obj_id)
{
	ResLnk *res_ptr, *new_ptr;
	AppID app;
	UWORD res_addr, obj_addr;
	USHORT objid_offset = 0;
	
	app = SysGetActiveAppID();
	/* check if object opened */
	res_ptr = openres_table;
	while(res_ptr != NULL)
	{
		if(res_ptr->app == app)
			if(res_ptr->obj_id == obj_id)
				return TRUE;		/* Object already opened */
			res_ptr = res_ptr->next;
	}
	
    /* search for the object in resource file */
	
	if(SysGetAppStatus(app, &res_addr) != TRUE)
		return ERR_RES_OBJ_MISS;
	
	if(obj_id >= SYS_RES_ID)
	{
		if(obj_id < INSTALL_RES_ID)
		{
			objid_offset = SYS_RES_ID;
			res_addr = (UWORD) sysres_data;
		}
		else  // main menu app icon
		{
			objid_offset = INSTALL_RES_ID;
			res_addr = (UWORD) install_icon_res_data;
		}
	}
	
	if(ResObjLocate(res_addr, obj_id - objid_offset, &obj_addr) != TRUE)
		return ERR_RES_OBJ_MISS;
	
	
    if((new_ptr = (ResLnk*) pmalloc(sizeof(ResLnk)))==NULL)
	{
#ifdef DEBUG
		printf("\nResMgr: not enough memory for new object");
#endif
		return ERR_RES_NO_MEM;
	}
	
	/* init. new setting */
	new_ptr->app = app;
	new_ptr->obj_id = obj_id;
	new_ptr->obj_addr = obj_addr;
	new_ptr->field_addr = 0;
	new_ptr->last_field = 0xFFFF;
	
	/* insert at front */
	new_ptr->next = openres_table;
	openres_table = new_ptr;
	
	return TRUE;
}


/*	Function	: ResGetField
*	Purpose		: Read entire field
*  Scope		: UI
*	Input		: obj_id:    object ID
*			      field_num: field to read
*	Output      : buffer
*                byte_read: number of byte read    
*  Return		: TRUE:	Success
*				  ERR_RES_NO_MEM
*	Comment		: This function will not update openres_table
*/
Err ResGetField(ObjectID obj_id, USHORT field_num, BYTE **buffer, UWORD *byte_read)
{
	AppID app;
	UWORD field_addr;
	UWORD field_size;
	
	app = SysGetActiveAppID();
	
	if(ResFieldInfo(app, obj_id, field_num, &field_size, &field_addr)!=TRUE)
		return ERR_RES_NOT_OPEN;
	
	
    if((*buffer = (BYTE*) pmalloc(field_size)) == NULL) /* exclude field header size(4) */
		return ERR_RES_NO_MEM;
	
	ResReadByte(field_addr, field_size, *buffer, &field_addr);
	
	*byte_read = field_size;
	return TRUE;
}


/*	Function	: ResReadField
*	Purpose		: Read some data in the field
*  Scope		: UI
*	Input		: obj_id:         object ID
*			      field_num:      field to read
*                start_position: offset of the field to start read 
*                num_byre:       number of byte to read
*	Output      : buffer:		  field data
*                byte_read:      number of byte read 
*  Return		: TRUE:	Success
*				  ERR_RES_NOT_OPEN
*	Comment		: if num_byte = READ_TO_END wil read to end of the field,
*                record pointer will set to last position
*                must free the buffer by pfree(buffer)
*/
Err ResReadField(ObjectID obj_id, USHORT field_num, UWORD start_pos, UWORD num_byte, BYTE **buffer, UWORD *byte_read)
{
	ResLnk *res_ptr;
	AppID app;
	UWORD field_addr;
	UWORD field_size;
	Err result;
	
	app = SysGetActiveAppID();
	
	if((result = ResObjInfo(app, obj_id, &res_ptr))!=TRUE)
		return result;
	
	if((result=ResFieldInfo(app, obj_id, field_num, &field_size, &field_addr))!=TRUE)
		return result;
	
	ResSkipByte(field_addr, start_pos, &field_addr);
	if(num_byte != READ_TO_END)
	{
		if(num_byte + start_pos > field_size)
			*byte_read = field_size - start_pos;
		else
			*byte_read = num_byte;
	}
	else
		*byte_read = field_size - start_pos;
	
    if((*buffer = (BYTE*) pmalloc(*byte_read))==NULL)
		return ERR_RES_NO_MEM;
	
	ResReadByte(field_addr, *byte_read, *buffer, &field_addr);
	
	return TRUE;
	
}


/*	Function	: ResFieldSize
*	Purpose		: Find a field's size
*  Scope		: UI
*	Input		: obj_id:    object ID
*			      field_num:  field to read
*	Output      : field_size: size of the field
*  Return		: TRUE:	Success
*				  ERR_RES_NOT_OPEN
*	Comment		: This function will not update openres_table
*/
Err ResFieldSize(ObjectID obj_id, USHORT field_num, UWORD *field_size)
{
	AppID app;
	UWORD field_addr;
	
	app = SysGetActiveAppID();
	
	return ResFieldInfo(app, obj_id, field_num, field_size, &field_addr);
}


/*	Function	: ResGetPointer
*	Purpose		: Return a pointer of a field
*  Scope		: UI
*	Input		: obj_id:     object ID
*			      field_num:  field to read
*                offset:     offset from first byte
*	Output      : pointer
*  Return		: TRUE:	Success
*				  ERR_RES_NOT_OPEN
*	Comment		: This function will not update openres_table
*/
Err ResGetPointer(ObjectID obj_id, USHORT field_num, USHORT offset, void **pointer)
{
	AppID app;
	UWORD field_addr;
	UWORD field_size;
	Err result;
	
	app = SysGetActiveAppID();
	
	if((result = ResFieldInfo(app, obj_id, field_num, &field_size, &field_addr))!=TRUE)
		return result;
	
	ResSkipByte(field_addr, offset, &field_addr);  /* skip the offset */
	
	if(field_addr % sizeof(UWORD))  /* need word align */
		field_addr += sizeof(UWORD) - field_addr % sizeof(UWORD);
	
	*pointer = (void*) field_addr;
	
	return TRUE;
	
}

/*	Function	: ResClose
*	Purpose		: Close resource object and release memory
*  Scope		: UI
*	Input		: obj_id:     object ID
*	Output      : None
*  Return		: TRUE:	Success
*				  ERR_RES_NOT_OPEN
*	Comment		: 
*/
Err ResClose(ObjectID obj_id)
{
	ResLnk *ptr, *prev_ptr;
	AppID app;
	
	app = SysGetActiveAppID();
	ptr = openres_table;
	prev_ptr = NULL;
	while(ptr != NULL)
	{
		if(ptr->obj_id == obj_id)
			if(ptr->app == app)
			{
				if(prev_ptr != NULL)
					prev_ptr->next = ptr->next;
				else
					openres_table = ptr->next;
				pfree(ptr);
				return TRUE;
			}
			prev_ptr = ptr;
			ptr = ptr->next;
	}
	return FALSE;
}



/*	Function	: ResInit
*	Purpose		: Init. resource manager
*  Scope		: OS
*	Input		: None
*	Output      : None
*  Return		: None
*	Comment		: Call when system reset
*/
void ResInit()
{
	openres_table = NULL;
}




/*	Function	: ResReadByte
*	Purpose		: Read data from resource file, auto skip MAT block boundary
*  Scope		: Internal
*	Input		: start_addr: physical address of read start
*                num_byte:   number of byte to read
*	Output      : data
*				  last_addr:  next data's physical address 
*  Return		: none
*	Comment		: 
*/
void ResReadByte(UWORD start_addr, UWORD num_byte, BYTE *data, UWORD *last_addr)
{
	BYTE *ptr;
	UWORD last_pos;
	
	ptr = (BYTE*) start_addr;
#ifdef PC_SIMxxxxxxxxx     /* should not execute the following */
	while(1)
	{
		/* chk if need to across block */
		remain = BLOCK_SIZE - (start_addr % BLOCK_SIZE) - 1;
		if(remain > num_byte)	/* no need to across block */
		{
			last_pos = start_addr + num_byte;
			while(num_byte--)
				*data++ = *ptr++;
			if(last_addr)
				*last_addr = last_pos;
			return;
		}
		
		/* need to across block */
		for(i=0;i<remain;i++)
			*data++ = *ptr++;
		
		num_byte -= remain;
		MemoryResolveBlock(start_addr, &block, &mat_num); /* get current block */
		if(MemoryNextBlock(&mat_info[mat_num], block, &block) != TRUE)
			break;
		start_addr = BlockAddr1(mat_info[mat_num], block);  /* update current address */
		if(num_byte)
			num_byte--;
	}  /* while */
#else
	last_pos = start_addr + num_byte;
	while(num_byte--)
		*data++ = *ptr++;
	if(last_addr)
		*last_addr = last_pos;
	return;
	
#endif
	
}

/*	Function	: ResSkipByte
*	Purpose		: Skip bytes across block boundary
*  Scope		: Internal
*	Input		: start_addr: physical address of read start
*                num_byte:   number of byte to skip
*	Output      : last_addr:  next data's physical address 
*  Return		: none
*	Comment		: 
*/
void ResSkipByte(UWORD start_addr, UWORD num_byte, UWORD *last_addr)
{
	BYTE *ptr;
	ptr = (BYTE*) start_addr;
	
#ifdef PC_SIMxxxxxxxxxxxxxxxxx  /* should not execute the following */
	while(1)
	{
		remain = BLOCK_SIZE - (start_addr % BLOCK_SIZE) - 1;
		/* chk if need to across block */
		if(remain > num_byte)	/* no need to across block */
		{
			*last_addr = start_addr + num_byte;
			return;
		}
		
		num_byte -= remain;
		MemoryResolveBlock(start_addr, &block, &mat_num); /* get current block */
		if(MemoryNextBlock(&mat_info[mat_num], block, &block) != TRUE)
			break;
		start_addr = BlockAddr1(mat_info[mat_num], block);  /* update current address */
		if(num_byte)    /* it means the addr is 0x????fff + 1, the result should be 0x???0000 */
			num_byte--;
	}  /* while */
#else
	*last_addr = start_addr + num_byte;
#endif
	return;
}


/*	Function	: ResObjInfo
*	Purpose		: Chk if resource opened and return info
*  Scope		: Internal
*	Input		: app:		Application ID
obj_id:	Object ID
*	Output      : res_ptr:	pointer to the entry of the object in openres_table
*  Return		: TRUE:		Success
FALSE:	Fail 
*	Comment		: 
*/
BOOLEAN ResObjInfo(AppID app, ObjectID obj_id, ResLnk **res_ptr)
{
	ResLnk *ptr;
	ptr = openres_table;
	while(ptr != NULL)
	{
		if(ptr->app == app)
			if(ptr->obj_id == obj_id)
			{
				*res_ptr = ptr;
				return TRUE;
			}
			ptr = ptr->next;
	}
	return FALSE;
}


/*      Function        : ResGetInstallIconData
*      Purpose         : Return pointer of install_icon_res_data
*      Scope           : OS
*      Input           : None    
*      Output          : None
*      Return          : pointer of install_icon_res_data
*	Comment		: 
*/
UBYTE *ResGetInstallIconData()
{
	return install_icon_res_data;
}



/*	Function	: ResFieldInfo
*	Purpose		: Get field size & physical address
*  Scope		: Internal
*	Input		: app:		  Application ID
obj_id:	  Object ID
field_num:  field to check
*	Output      : field_size: size of the field exclude field header(4 byte)
field_addr: physical address of the field(skip field size header)
*  Return		: TRUE:		Success
ERR_RES_NOT_OPEN
*	Comment		: 
*/
Err ResFieldInfo(AppID app, ObjectID obj_id, USHORT field_num, UWORD *field_size, UWORD *field_addr)
{
	ResLnk *res_ptr;
	USHORT cur_field;
	
	if(ResObjInfo(app, obj_id, &res_ptr) == FALSE)
		return ERR_RES_NOT_OPEN;
	
	
	if(res_ptr->last_field == field_num)  /* field address is saved */
		*field_addr = res_ptr->field_addr;
	else		/* need to find the field address */
	{
		if((res_ptr->last_field > field_num) &&	/* can cal. next field from this field's offset */
			(res_ptr->last_field != (USHORT)0xFFFF))
		{
			cur_field = res_ptr->last_field;
			*field_addr = res_ptr->field_addr;
			while(1)
			{
				ResReadByte(*field_addr, 4, (BYTE*)field_size, NULL);
				ResSkipByte(*field_addr, *field_size, field_addr);
				if(++cur_field == field_num)
					break;
			}
		}
		else
		{	/* need to re-cal. the field addr */
			cur_field = 0;
			*field_addr = res_ptr->obj_addr;
			ResSkipByte(*field_addr, 4, field_addr);  /* skip object total len */
			if(field_num != 0)
				while(1)
				{
					ResReadByte(*field_addr, 4, (BYTE*)field_size, NULL); /* read field size */
					ResSkipByte(*field_addr, *field_size, field_addr);
					if(++cur_field == field_num)
						break;
				}
		}
	}
	ResReadByte(*field_addr, 4, (BYTE*)field_size, NULL); /* read field size */
	ResSkipByte(*field_addr, 4, field_addr);  /* skip the header */
	*field_size = *field_size - 4;
	return TRUE;
}
