/*---------------------------------------------------------------------------*
  Project:  NitroSDK - FS - libraries
  File:     fs_archive.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: fs_archive.c,v $
  Revision 1.25  2004/11/12 13:36:42  yosizaki
  fix around LoadTable (ignore if empty archive)

  Revision 1.24  2004/11/02 10:06:15  yosizaki
  fix typo.

  Revision 1.23  2004/09/02 11:03:00  yosizaki
  fix FS include directives.

  Revision 1.22  2004/07/23 08:29:52  yosizaki
  add cast to OS_LockCard.

  Revision 1.21  2004/07/19 13:17:36  yosizaki
  add all commands.

  Revision 1.20  2004/07/13 08:00:14  yosizaki
  fix FS_ResumeArchive

  Revision 1.19  2004/07/13 02:54:05  yosizaki
  add argument of FS_OpenFileDirect.

  Revision 1.18  2004/07/12 13:04:45  yosizaki
  change FS_COMMAND_READFILE & FS_COMMAND_WRITEFILE (add argument)

  Revision 1.17  2004/07/09 00:56:32  yosizaki
  fix argument of FS_COMMAND_IDLE procedure-message.

  Revision 1.16  2004/07/08 13:42:00  yosizaki
  change archive commands.

  Revision 1.15  2004/06/30 04:32:30  yosizaki
  implement user-procedure system.

  Revision 1.14  2004/06/22 01:48:48  yosizaki
  fix archive command operation.

  Revision 1.13  2004/06/07 10:26:33  yosizaki
  fix around archive asynchronous operation.

  Revision 1.12  2004/06/03 13:33:07  yosizaki
  fix around archive asynchronous operation.

  Revision 1.11  2004/06/01 10:17:56  yosizaki
  change around assertion code.

  Revision 1.10  2004/05/31 02:56:13  yosizaki
  change FSi_ASSERT_ARG() to FS_ASSERT_ARG().

  Revision 1.9  2004/05/21 10:55:27  yosizaki
  change OS_IsThreadAvailable() to SDK_NO_THREAD.

  Revision 1.8  2004/05/20 09:59:22  yosizaki
  fix asynchronous operation.

  Revision 1.7  2004/05/20 06:31:16  yosizaki
  add FS_ConvertPathToFileID.

  Revision 1.6  2004/05/19 02:30:14  yosizaki
  add FS_GetPathName().

  Revision 1.5  2004/05/17 08:32:26  yosizaki
  add comment.
  add assertion of each interface function.

  Revision 1.4  2004/05/12 05:22:55  yosizaki
  prepare a option "SDK_ARM7FS".

  Revision 1.3  2004/05/11 09:36:36  yosizaki
  fix FS_SeekFile().

  Revision 1.2  2004/05/11 04:30:57  yosizaki
  change archive system.

  Revision 1.1  2004/05/10 06:27:56  yosizaki
  (none)

  $NoKeywords: $
 *---------------------------------------------------------------------------*/


#include <nitro/mi.h>
#include <nitro/os.h>

#include <nitro/fs.h>

#include "../include/util.h"
#include "../include/command.h"

// NOTE:
// Need to clearly note that "next request comes before return from NotifyAsyncEnd()"	
//  in archive implementation cautionary notes.


/* Archive is not usually included in ARM7 */
#if	defined(FS_IMPLEMENT)


/********************************************************************/
/* Variables */

// Archive list.
static FSArchive	*arc_list = NULL;

// Current directory position.
FSDirPos	current_dir_pos;


/********************************************************************/
/* Functions */

/** Internal ************************************************************/

// Internal function. Set specified number of chars to lowercase and pack into u32.
// If length is greater than FS_ARCHIVE_NAME_LEN_MAX, return 0.
static u32	FSi_GetPackedName(const char *name, int name_len)
{
	u32	ret = 0;
	if(name_len <= FS_ARCHIVE_NAME_LEN_MAX) {
		int	i = 0;
		for( ; i < name_len ; ++i ) {
			u32	c = (u32)MI_ReadByte(name + i);
			if(!c)	break;
			c = (u32)(c - 'A');
			if(c <= (u32)('Z' - 'A'))
				c = (u32)(c + 'a');
			else
				c = (u32)(c + 'A');
			ret |= (u32)(c << (i * 8));
		}
	}
	return	ret;
}


// Internal function. Default memory access callback.
static FSResult	FSi_ReadMemCallback(FSArchive *p_arc, void *dst, u32 pos, u32 size)
{
	MI_CpuCopy8((const void*)FS_GetArchiveOffset(p_arc, pos), dst, size);
	return	FS_RESULT_SUCCESS;
}
static FSResult	FSi_WriteMemCallback(FSArchive *p_arc, const void *src, u32 pos, u32 size)
{
	MI_CpuCopy8(src, (void*)FS_GetArchiveOffset(p_arc, pos), size);
	return	FS_RESULT_SUCCESS;
}

// Internal function. Read from preload table.
static FSResult	FSi_ReadMemoryCore(FSArchive *p_arc, void *dst, u32 pos, u32 size)
{
	(void)p_arc;
	MI_CpuCopy8((const void*)pos, dst, size);
	return	FS_RESULT_SUCCESS;
}

// Search archive names. If there is no matching name, return NULL. 
FSArchive*	FS_FindArchive(const char *name, int name_len)
{
	u32	pack = FSi_GetPackedName(name, name_len);
	OSIntrMode    bak_psr = OS_DisableInterrupts();
	FSArchive	*p_arc = arc_list;
	while(p_arc && (p_arc->name.pack != pack))
		p_arc = p_arc->next;
	(void)OS_RestoreInterrupts(bak_psr);

	return	p_arc;
}


/* Archive access ***********************************************/

/*
 * Select the next command to process. 
 * If async command is selected, return the pointer. 
 * If non-NULL is returned, it must be processed there. 
 */
FSFile*	FSi_NextCommand(FSArchive *p_arc)
{
	/* Disable interrupts for selection of cancel, command */
	OSIntrMode	bak_psr = OS_DisableInterrupts();

	/* First, check all cancellation requests */
	if(FSi_IsArchiveCanceling(p_arc)) {
		FSFile	*p, *q;
		p_arc->flag &= ~FS_ARCHIVE_FLAG_CANCELING;
		for( p = p_arc->list.next ; p ; p = q ) {
			 q = p->link.next;
			 if(FS_IsCanceling(p)) {
				if(p_arc->list.next == p)
					p_arc->list.next = q;
				FSi_ReleaseCommand(p, FS_RESULT_CANCELED);
				if(!q)
					q = p_arc->list.next;
			 }
		}
	}

	/* If not suspended, select next command  */
	if(!FS_IsArchiveSuspended(p_arc) && p_arc->list.next) {
		FSFile	*p_file = p_arc->list.next;
		const BOOL	is_start = !FSi_IsArchiveRunning(p_arc);
		if(is_start)
			p_arc->flag |= FS_ARCHIVE_FLAG_RUNNING;
		(void)OS_RestoreInterrupts(bak_psr);
		if(is_start) {
			/*
			 * Activate message notification.
			 * (return value has not meaning)
			 */
			if((p_arc->proc_flag & FS_ARCHIVE_PROC_ACTIVATE) != 0)
				(void)(*p_arc->proc)(p_file, FS_COMMAND_ACTIVATE);
		}
		/* If sync command, shift to idle thread */
		if(FS_IsFileSyncMode(p_file)) {
			OS_WakeupThread(p_file->queue);
			return	NULL;
		}
		/* If async command, process at source of call */
		else {
			return	p_file;
		}
	}

	/* If no command is executed, change to idle state  */
	if(FSi_IsArchiveRunning(p_arc)) {
		p_arc->flag &= ~FS_ARCHIVE_FLAG_RUNNING;
		if((p_arc->proc_flag & FS_ARCHIVE_PROC_IDLE) != 0) {
			FSFile	tmp;
			FS_InitFile(&tmp);
			tmp.arc = p_arc;
			/*
			 * IDLE message notification.
			 * (return value has no meaning)
			 */
			(void)(*p_arc->proc)(&tmp, FS_COMMAND_IDLE);
		}
	}

	/* Wake the executing thread if in idle state as a result of suspension */
	if(FSi_IsArchiveSuspending(p_arc)) {
		p_arc->flag &= ~FS_ARCHIVE_FLAG_SUSPENDING;
		p_arc->flag |= FS_ARCHIVE_FLAG_SUSPEND;
		OS_WakeupThread(&p_arc->stat_q);
	}
	(void)OS_RestoreInterrupts(bak_psr);

	return	NULL;
}

/*
 * Execute the async command.
 *
 * Called initially from the user thread with interrupts enabled.  
 * As long as the archive is performing sync. operations, command processing will repeat here. 
 * If a single async process occurs, processing continues with NotifyAsyncEnd().
 *
 * When switching between sync/async archive processing, 
 * be aware of the NotifyAsyncEnd() calling environment. 
 */
void	FSi_ExecuteAsyncCommand(FSFile *p_file)
{
	FSArchive	* const p_arc = p_file->arc;
	while(p_file) {
		OSIntrMode	bak_psr = OS_DisableInterrupts();
		/* If source of call is waiting for sync, wake the thread and pass processing  */
		if(FS_IsFileSyncMode(p_file)) {
			OS_WakeupThread(p_file->queue);
			break;
		}
		/* If not, set async mode here */
		else {
			p_file->stat |= FS_FILE_STATUS_ASYNC;
		}
		(void)OS_RestoreInterrupts(bak_psr);
		/* If processing is async, stop temporarily */
		if(FSi_TranslateCommand(p_file, p_file->command) == FS_RESULT_PROC_ASYNC)
			break;
		/* If result is sync complete, select continuation here  */
		p_file = FSi_NextCommand(p_arc);
	}
}

/* Execute command in sync mode */
BOOL	FSi_ExecuteSyncCommand(FSFile *p_file)
{
	FSFile	*p_target;
	FSResult	ret = FSi_TranslateCommand(p_file, p_file->command);
	FSi_ReleaseCommand(p_file, ret);
	p_target = FSi_NextCommand(p_file->arc);
	if(p_target)
		FSi_ExecuteAsyncCommand(p_target);
	return	FS_IsSucceeded(p_file);
}

/*
 * Add command to archive. 
 * Adjust start timing and block here if sync command.
 * If successful, TRUE.
 */
BOOL	FSi_SendCommand(FSFile *p_file, FSCommandType command)
{
	FSArchive	* const p_arc = p_file->arc;
	const int	bit = (1 << command);

	/*
	 * SDK cannot guarantee thread safety of file itself. 
	 * Also, status has already been determined by interface of call source.  
	 */
	p_file->command = command;
	p_file->error = FS_RESULT_BUSY;
	p_file->stat |= FS_FILE_STATUS_BUSY;

	{
		/* Disable interrupts for list addition and start determination */
		OSIntrMode	bak_psr = OS_DisableInterrupts();

		/* If archive is unloading, cancel  */
		if(p_arc->flag & FS_ARCHIVE_FLAG_UNLOADING) {
			FSi_ReleaseCommand(p_file, FS_RESULT_CANCELED);
			(void)OS_RestoreInterrupts(bak_psr);
			return	FALSE;
		}

		/* Add to list  */
		if((bit & FS_ARCHIVE_PROC_SYNC) != 0)
			p_file->stat |= FS_FILE_STATUS_SYNC;
		FS_AppendToList(p_file, (FSFile*)&p_arc->list);

		/* If idling, start the archive */
		if(!FSi_IsArchiveRunning(p_arc)) {
			p_arc->flag |= FS_ARCHIVE_FLAG_RUNNING;
			(void)OS_RestoreInterrupts(bak_psr);
			/*
			 * Activate message notification.
			 * (return value has no meaning)
			 */
			if((p_arc->proc_flag & FS_ARCHIVE_PROC_ACTIVATE) != 0)
				(void)(*p_arc->proc)(p_file, FS_COMMAND_ACTIVATE);
			/* If async mode, execute the command and return immediately */
			if(!FS_IsFileSyncMode(p_file)) {
				FSi_ExecuteAsyncCommand(p_file);
				return	TRUE;
			}
		}

		/* If started and async mode, follow the flow */
		else if(!FS_IsFileSyncMode(p_file)) {
			(void)OS_RestoreInterrupts(bak_psr);
			return	TRUE;
		}

		/* If started and sync mode, wait for turn here */
		else {
			do	OS_SleepThread(p_file->queue);
			while(p_file != p_arc->list.next);
			(void)OS_RestoreInterrupts(bak_psr);
		}

	}

	/*
	 * All sync-mode commands come here and complete. 
	 * Including ReadFileAsync + WaitAsync.
	 */
	return	FSi_ExecuteSyncCommand(p_file);
}

/*
 * Acknowledge completion of resource's async operation
 *  This is called from the archive implementation side that completed async processing.
 */
void	FS_NotifyArchiveAsyncEnd(FSArchive *p_arc, FSResult ret)
{
	/* If during async command, then operation is completed */
	if(FSi_IsArchiveAsync(p_arc)) {
		FSFile	*p_file = p_arc->list.next;
		p_arc->flag &= ~FS_ARCHIVE_FLAG_IS_ASYNC;
		FSi_ReleaseCommand(p_file, ret);
		/* If async command is next, then execute here */
		p_file = FSi_NextCommand(p_arc);
		if(p_file)
			FSi_ExecuteAsyncCommand(p_file);
	}
	/* If sync command, restore the blocking thread */
	else {
		FSFile	*p_file = p_arc->list.next;
		OSIntrMode    bak_psr = OS_DisableInterrupts();
		p_file->error = ret;
		p_arc->flag &= ~FS_ARCHIVE_FLAG_IS_SYNC;
#if	!defined(SDK_NO_THREAD)
		OS_WakeupThread(&p_arc->sync_q);
#endif
		(void)OS_RestoreInterrupts(bak_psr);
	}
}


/* Interface ***************************************************/

// Initialize the archive structure.
void	FS_InitArchive(FSArchive *p_arc)
{
	FS_ASSERT_ARG(p_arc, FALSE)
	MI_CpuClear8(p_arc, sizeof(FSArchive));
#if	!defined(SDK_NO_THREAD)
	OS_InitThreadQueue(&p_arc->sync_q);
	OS_InitThreadQueue(&p_arc->stat_q);
#endif
}

// Save the archive name. (The archive name "rom" is reserved.)
BOOL	FS_RegisterArchiveName(FSArchive *p_arc,
	const char *name, u32 name_len)
{
	FS_ASSERT_INIT(FALSE)
	FS_ASSERT_ARG(p_arc && name, FALSE)
	FS_ASSERT_ARC_FREE(p_arc, FALSE)

	{
		BOOL	ret = FALSE;
		OSIntrMode    bak_intr = OS_DisableInterrupts();
		if(!FS_FindArchive(name, name_len)) {
			FSArchive	*p_tail = arc_list;
			if(!p_tail) {
				arc_list = p_arc;
				current_dir_pos.arc = p_arc;
				current_dir_pos.pos = 0;
				current_dir_pos.index = 0;
				current_dir_pos.own_id = 0;
			} else {
				while(p_tail->next)
					p_tail = p_tail->next;
				p_tail->next = p_arc;
				p_arc->prev = p_tail;
			}
			p_arc->name.pack = FSi_GetPackedName(name, name_len);
			ret = TRUE;
		}
		(void)OS_RestoreInterrupts(bak_intr);
		return	ret;
	}
}

// Release saved archive names. 
//	Must be unloaded from the file system. 
void	FS_ReleaseArchiveName(FSArchive *p_arc)
{
	FS_ASSERT_INIT(void)
	FS_ASSERT_ARG(p_arc, void)
	FS_ASSERT_ARC_UNLOADED(p_arc, void)
	FS_ASSERT_ARC_NOT_ROM(p_arc, void)

	if(p_arc->name.pack) {
		OSIntrMode    bak_psr = OS_DisableInterrupts();
		// Cut from list. 
		if(p_arc->next)	p_arc->next->prev = p_arc->prev;
		if(p_arc->prev)	p_arc->prev->next = p_arc->next;
		p_arc->name.pack = 0;
		p_arc->next = p_arc->prev = NULL;
		p_arc->flag &= ~FS_ARCHIVE_FLAG_REGISTER;
		// If freed archive name is in the current directory, 
		//	shift automatically to "rom" root. 
		if(current_dir_pos.arc == p_arc) {
			current_dir_pos.arc = arc_list;
			current_dir_pos.pos = 0;
			current_dir_pos.index = 0;
			current_dir_pos.own_id = 0;
		}
		(void)OS_RestoreInterrupts(bak_psr);
	}
}

// Load the archive into the file system. 
// It must already be saved on the archive list. 
BOOL	FS_LoadArchive(FSArchive *p_arc, u32 base,
	u32 fat, u32 fat_size,
	u32 fnt, u32 fnt_size,
	FS_ARCHIVE_READ_FUNC read_func,
	FS_ARCHIVE_WRITE_FUNC write_func)
{
	FS_ASSERT_INIT(FALSE)
	FS_ASSERT_ARG(p_arc, FALSE)
	FS_ASSERT_ARC_UNLOADED(p_arc, FALSE)

	// Set the base offset. 
	p_arc->base = base;
	p_arc->fat_size = fat_size;
	p_arc->fat = p_arc->fat_bak = fat;
	p_arc->fnt_size = fnt_size;
	p_arc->fnt = p_arc->fnt_bak = fnt;
	// Access callback. (If NULL, use memory)
	p_arc->read_func = read_func ? read_func : FSi_ReadMemCallback;
	p_arc->write_func = write_func ? write_func : FSi_WriteMemCallback;
	// Initially, preload disabled 
	p_arc->table_func = p_arc->read_func;
	p_arc->load_mem = NULL;
	// 
	p_arc->flag |= FS_ARCHIVE_FLAG_LOADED;
	return	TRUE;
}

// Unload the archive from the file system. 
//	Block until all currently running tasks are completed. 
BOOL	FS_UnloadArchive(FSArchive *p_arc)
{
	FS_ASSERT_INIT(FALSE)
	FS_ASSERT_ARG(p_arc, FALSE)

	{
		OSIntrMode	bak_psr = OS_DisableInterrupts();
		/* If not loaded, ignore */
		if(FS_IsArchiveLoaded(p_arc)) {
			/*
			 * If table is still not unloaded,
			 * warn that memory leak is possible.
			 */
			if(FS_IsArchiveTableLoaded(p_arc)) {
				OS_Warning("memory may leak. preloaded-table of archive \"%s\" (0x%08X)",
					p_arc->name.ptr, p_arc->load_mem);
			}
			/* First, suspend. */
			(void)FS_SuspendArchive(p_arc);
			/* Enable the unload flag and cancel all commands. */
			{
				FSFile	*p, *q;
				p_arc->flag |= FS_ARCHIVE_FLAG_UNLOADING;
				for( p = p_arc->list.next ; p ; p = q ) {
					 q = p->link.next;
					FSi_ReleaseCommand(p, FS_RESULT_CANCELED);
				}
				p_arc->list.next = NULL;
			}
			/* Actual unload processing */
			p_arc->base = 0;
			p_arc->fat = 0;
			p_arc->fat_size = 0;
			p_arc->fnt = 0;
			p_arc->fnt_size = 0;
			p_arc->fat_bak = p_arc->fnt_bak = 0;
			p_arc->flag &= ~(FS_ARCHIVE_FLAG_CANCELING |
				FS_ARCHIVE_FLAG_LOADED | FS_ARCHIVE_FLAG_UNLOADING);
		}
		(void)OS_RestoreInterrupts(bak_psr);
	}
	return	TRUE;
}

// Preload archive FAT + FNT in memory.
//	Execute the load and return the required size, only if within specified size.
u32	FS_LoadArchiveTables(FSArchive *p_arc, void *p_mem, u32 max_size)
{
	FS_ASSERT_INIT(0)
	FS_ASSERT_ARG(p_arc, 0)

	{
		// Preload size is 32-byte aligned.
		u32	total_size = ALIGN_BYTE(p_arc->fat_size + p_arc->fnt_size + 32, 32);
		if(total_size <= max_size) {
			// If size is sufficient, load into memory.
			u8	*p_cache = (u8*)ALIGN_BYTE((u32)p_mem, 32);
			FSFile	tmp;
			FS_InitFile(&tmp);
			/*
			 * Sometimes the table does not read.
			 * In that case, the table could not be accessed from the beginning so nothing happens.
			 */
			// Load FAT.
			if(FS_OpenFileDirect(&tmp,
				p_arc, p_arc->fat, p_arc->fat + p_arc->fat_size, (u32)~0)) {
				if(FS_ReadFile(&tmp, p_cache, p_arc->fat_size) < 0) {
					MI_CpuFill8(p_cache, 0x00, p_arc->fat_size);
				}
				(void)FS_CloseFile(&tmp);
			}
			p_arc->fat = (u32)p_cache;
			p_cache += p_arc->fat_size;
			// Load FNT.
			if(FS_OpenFileDirect(&tmp,
				p_arc, p_arc->fnt, p_arc->fnt + p_arc->fnt_size, (u32)~0)) {
				if(FS_ReadFile(&tmp, p_cache, p_arc->fnt_size) < 0) {
					MI_CpuFill8(p_cache, 0x00, p_arc->fnt_size);
				}
				(void)FS_CloseFile(&tmp);
			}
			p_arc->fnt = (u32)p_cache;
			// Save the allocated memory. 
			p_arc->load_mem = p_mem;
			// Thereafter, table read-type preload memory will work. 
			p_arc->table_func = FSi_ReadMemoryCore;
			p_arc->flag |= FS_ARCHIVE_FLAG_TABLE_LOAD;
		}
		return	total_size;
	}
}

// Free the preload memory for FNT and FAT.
void*	FS_UnloadArchiveTables(FSArchive *p_arc)
{
	FS_ASSERT_INIT(0)
	FS_ASSERT_ARG(p_arc, 0)

	{
		void	*ret = NULL;
		if(FS_IsArchiveLoaded(p_arc)) {
			BOOL	bak_stat = FS_SuspendArchive(p_arc);
			if(FS_IsArchiveTableLoaded(p_arc)) {
				p_arc->flag &= ~FS_ARCHIVE_FLAG_TABLE_LOAD;
				ret = p_arc->load_mem;
				p_arc->load_mem = NULL;
				p_arc->fat = p_arc->fat_bak;
				p_arc->fnt = p_arc->fnt_bak;
				p_arc->table_func = p_arc->read_func;
			}
			if(bak_stat)
				(void)FS_ResumeArchive(p_arc);
		}
		return	ret;
	}
}


//  Suspend the archive processing mechanism itself. 
//	If process is running, enter true suspension after it completes. 
BOOL	FS_SuspendArchive(FSArchive *p_arc)
{
	FS_ASSERT_INIT(0)
	FS_ASSERT_ARG(p_arc, 0)

	{
		/* If already suspended, ignore. */
		OSIntrMode	bak_psr = OS_DisableInterrupts();
		const BOOL	bak_stat = FS_IsArchiveSuspended(p_arc) ? FALSE : TRUE;
		if(bak_stat) {
			/* If running, enable the flag and wait for stop */
			if(FSi_IsArchiveRunning(p_arc)) {
				p_arc->flag |= FS_ARCHIVE_FLAG_SUSPENDING;
				do	OS_SleepThread(&p_arc->stat_q);
				while(FSi_IsArchiveSuspending(p_arc));
			}
			/* If idle, stop directly */
			else {
				p_arc->flag |= FS_ARCHIVE_FLAG_SUSPEND;
			}
		}
		(void)OS_RestoreInterrupts(bak_psr);
		return	bak_stat;
	}
}

// Restart the suspended archive processing. 
BOOL	FS_ResumeArchive(FSArchive *p_arc)
{
	FS_ASSERT_INIT(0)
	FS_ASSERT_ARG(p_arc, 0)

	{
		FSFile	*p_target = NULL;
		/* Disable the suspend flag  */
		OSIntrMode	bak_irq = OS_DisableInterrupts();
		const BOOL	bak_stat = FS_IsArchiveSuspended(p_arc) ? FALSE : TRUE;
		if(!bak_stat) {
			p_arc->flag &= ~FS_ARCHIVE_FLAG_SUSPEND;
			/* If start time, start the command.  */
			p_target = FSi_NextCommand(p_arc);
		}
		(void)OS_RestoreInterrupts(bak_irq);
		if(p_target)
			FSi_ExecuteAsyncCommand(p_target);
		return	bak_stat;
	}
}

// Set the archive's user procedure. 
//	If proc == NULL or flags = 0, simply invalidate the procedure. 
void	FS_SetArchiveProc(struct FSArchive *p_arc,
	FS_ARCHIVE_PROC_FUNC proc, u32 flags)
{
	if(!flags)	proc = NULL;
	else if(!proc)	flags = 0;
	p_arc->proc = proc;
	p_arc->proc_flag = flags;
}

#endif	/* FS_IMPLEMENT */
