/*---------------------------------------------------------------------------*
  Project:  NitroSDK - FS - libraries
  File:     archive.h

  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: archive.h,v $
  Revision 1.11  2004/11/02 10:07:49  yosizaki
  fix typo.

  Revision 1.10  2004/07/23 08:32:27  yosizaki
  fix command operation.

  Revision 1.9  2004/07/19 13:12:59  yosizaki
  add all commands.

  Revision 1.8  2004/07/08 13:45:54  yosizaki
  change archive commands.

  Revision 1.7  2004/06/30 10:28:08  yosizaki
  add comments.

  Revision 1.6  2004/06/30 04:30:46  yosizaki
  implement user-procedure system.

  Revision 1.5  2004/06/22 01:49:37  yosizaki
  add user-procedure system (reserved).

  Revision 1.4  2004/06/03 13:32:53  yosizaki
  fix around archive asynchronous operation.

  Revision 1.3  2004/05/20 12:37:38  yosizaki
  fix FSFileLink definition.

  Revision 1.2  2004/05/20 06:30:33  yosizaki
  add FS_ConvertPathToFileID.
  add some structure for archive.

  Revision 1.1  2004/05/11 04:32:58  yosizaki
  (none)

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


#if	!defined(NITRO_FS_ARCHIVE_H_)
#define NITRO_FS_ARCHIVE_H_

#include <nitro/misc.h>
#include <nitro/types.h>


#ifdef __cplusplus
extern "C" {
#endif


/********************************************************************
 * Archive structure
 * This is a system that enables the use of all FSFile interfaces  
 * by unifying all the ROM file system formats. 
 *
 *  We have envisioned archives being used for the following purposes: 
 *
 *   Manipulating individual elements contained in a linear data structure  
 *  based on the original ROM file system specifications. 
 *
 *    Using consistent means to transparently control specific data structures 
 *  with access targets that change according to conditions 
 *
 ********************************************************************/


/********************************************************************/
/* Front reference */

struct	FSFile;	/* only pointer */
struct	FSFileLink;
struct	FSArchiveFAT;
struct	FSArchiveFNT;
struct	FSArchive;


enum {
	/* Max. length of archive name */
	FS_ARCHIVE_NAME_LEN_MAX	= 3
} ;

/*
 * Archive internal status flag (in-line function)
 * The user does not directly use these constants. 
 */
#define	FS_ARCHIVE_FLAG_REGISTER	0x00000001
#define	FS_ARCHIVE_FLAG_LOADED		0x00000002
#define	FS_ARCHIVE_FLAG_TABLE_LOAD	0x00000004
#define	FS_ARCHIVE_FLAG_SUSPEND		0x00000008
#define	FS_ARCHIVE_FLAG_RUNNING		0x00000010
#define	FS_ARCHIVE_FLAG_CANCELING	0x00000020
#define	FS_ARCHIVE_FLAG_SUSPENDING	0x00000040
#define	FS_ARCHIVE_FLAG_UNLOADING	0x00000080
#define	FS_ARCHIVE_FLAG_IS_ASYNC	0x00000100
#define	FS_ARCHIVE_FLAG_IS_SYNC		0x00000200

/*
 * FSArchive::flag can use the following areas freely 
 * to implement the archive. 
 */
#define	FS_ARCHIVE_FLAG_USER_RESERVED_BIT	0x00010000
#define	FS_ARCHIVE_FLAG_USER_RESERVED_MASK	0xFFFF0000


/*
 * File command and archive message. 
 * Employs user procedure during archive implementation. 
 */
typedef	enum {
	/* Async  commands. */
	FS_COMMAND_ASYNC_BEGIN = 0,
	FS_COMMAND_READFILE = FS_COMMAND_ASYNC_BEGIN,
	FS_COMMAND_WRITEFILE,
	FS_COMMAND_ASYNC_END,

	/* Sync commands. */
	FS_COMMAND_SYNC_BEGIN = FS_COMMAND_ASYNC_END,
	FS_COMMAND_SEEKDIR = FS_COMMAND_SYNC_BEGIN,
	FS_COMMAND_READDIR,
	FS_COMMAND_FINDPATH,
	FS_COMMAND_GETPATH,
	FS_COMMAND_OPENFILEFAST,
	FS_COMMAND_OPENFILEDIRECT,
	FS_COMMAND_CLOSEFILE,
	FS_COMMAND_SYNC_END,

	/* Archive messages. */
	FS_COMMAND_STATUS_BEGIN = FS_COMMAND_SYNC_END,
	FS_COMMAND_ACTIVATE = FS_COMMAND_STATUS_BEGIN,
	FS_COMMAND_IDLE,
	FS_COMMAND_SUSPEND,
	FS_COMMAND_RESUME,
	FS_COMMAND_STATUS_END,

	FS_COMMAND_INVALID

} FSCommandType;


/*
 * User procedure setting flag.
 * Specify with argument "flags" of FS_SetArchiveProc().  
 *  Enable the bits that require callbacks. 
 */

/* Async commands */
#define	FS_ARCHIVE_PROC_READFILE		(1 << FS_COMMAND_READFILE)
#define	FS_ARCHIVE_PROC_WRITEFILE		(1 << FS_COMMAND_WRITEFILE)
	/* All async commands */
#define	FS_ARCHIVE_PROC_ASYNC	\
	(FS_ARCHIVE_PROC_READFILE | FS_ARCHIVE_PROC_WRITEFILE)

/* Sync commands */
#define	FS_ARCHIVE_PROC_SEEKDIR			(1 << FS_COMMAND_SEEKDIR)
#define	FS_ARCHIVE_PROC_READDIR			(1 << FS_COMMAND_READDIR)
#define	FS_ARCHIVE_PROC_FINDPATH		(1 << FS_COMMAND_FINDPATH)
#define	FS_ARCHIVE_PROC_GETPATH			(1 << FS_COMMAND_GETPATH)
#define	FS_ARCHIVE_PROC_OPENFILEFAST	(1 << FS_COMMAND_OPENFILEFAST)
#define	FS_ARCHIVE_PROC_OPENFILEDIRECT	(1 << FS_COMMAND_OPENFILEDIRECT)
#define	FS_ARCHIVE_PROC_CLOSEFILE		(1 << FS_COMMAND_CLOSEFILE)
	/* All sync commands */
#define	FS_ARCHIVE_PROC_SYNC	\
	(FS_ARCHIVE_PROC_SEEKDIR | FS_ARCHIVE_PROC_READDIR |	\
	 FS_ARCHIVE_PROC_FINDPATH | FS_ARCHIVE_PROC_GETPATH |	\
	FS_ARCHIVE_PROC_OPENFILEFAST | FS_ARCHIVE_PROC_OPENFILEDIRECT | FS_ARCHIVE_PROC_CLOSEFILE)

/* Status change messages */
#define	FS_ARCHIVE_PROC_ACTIVATE		(1 << FS_COMMAND_ACTIVATE)
#define	FS_ARCHIVE_PROC_IDLE			(1 << FS_COMMAND_IDLE)
#define	FS_ARCHIVE_PROC_SUSPENDING		(1 << FS_COMMAND_SUSPEND)
#define	FS_ARCHIVE_PROC_RESUME			(1 << FS_COMMAND_RESUME)
	/* All status change messages  */
#define	FS_ARCHIVE_PROC_STATUS	\
	(FS_ARCHIVE_PROC_ACTIVATE | FS_ARCHIVE_PROC_IDLE |	\
	 FS_ARCHIVE_PROC_SUSPENDING | FS_ARCHIVE_PROC_RESUME)

/* All messages */
#define	FS_ARCHIVE_PROC_ALL	(~0)


typedef	enum
{
	/****************************************************************
	 * User error codes
	 * Get with FS_GetErrorCode().
	 ****************************************************************/

	/*
	 * Indicates a successful result from correct processing.  
	 * For example, even if you attempted to read 100 bytes
	 * and only 50 bytes were read at termination,
	 * the process would be treated as "successful. "
	 */
	FS_RESULT_SUCCESS = 0,

	/*
	 * Indicates a failure from correct processing. 
	 * Refers to a failure that is within the acceptable range of processing, 
	 * such as reading a termination with ReadDir. 
	 */
	FS_RESULT_FAILURE,

	/*
	 * Indicates that a command is being processed. 
	 * Since the command was not completed, this is not a true error. 
	 */
	FS_RESULT_BUSY,

	/*
	 * Indicates that the command was cancelled. 
	 * It could be cancelled explicitly by the user,
	 * or cancelled forcibly during archive unloading. 
	 */
	FS_RESULT_CANCELED,

	/*
	 * Indicates that the archive does not support the specified command.  
	 * This error is issued by an archive when ignoring a command. 
	 * The user should find out the specifications of the target archive. 
	 */
	FS_RESULT_UNSUPPORTED,

	/*
	 * Indicates failure from an error in the file system or archive. 
	 */
	FS_RESULT_ERROR,

	/****************************************************************
	 * Procedure level error codes 
	 * Returned by the user procedure. Not included in FS_GetErrorCode().
	 ****************************************************************/

	/*
	 * Initially returns control because command is processed asynchronously.   
	 * If this value is returned, completion must be acknowledged with FS_NotifyArchiveAsyncEnd(),
	 * just as with a read/write callback. 
	 */
	FS_RESULT_PROC_ASYNC,

	/*
	 * This time only, performs default processing. 
	 * Next time, a user procedure to the command in question will be called. 
	 * Returned each time you want to simply hook a command etc. 
	 */
	FS_RESULT_PROC_DEFAULT,

	/*
	 * Will perform default processing this time and each time hereafter. 
	 * Calls a procedure to the command in question and clears the flag. 
	 * Will not be called next time. 
	 */
	FS_RESULT_PROC_UNKNOWN

} FSResult;


/*
 * User Procedure
 * Set by the user to customize the archive. 
 */
typedef	FSResult	(*FS_ARCHIVE_PROC_FUNC)(struct FSFile*, FSCommandType);


/*
 * Read/Write access callback
 *
 * If access completes during the call, it must return FS_RESULT_SUCCESS. 
 * If async. or other processing continues, FS_RESULT_PROC_ASYNC is returned and
 * FS_NotifyArchiveAsyncEnd() must be called at completion. 
 *
 * All command default processing is realized by using this callback
 * to access FAT/FNT/file images. 
 * Conversely, if all the required commands are properly overwritten with user procedure processing,
 * these callbacks will not be called. 
 */
typedef	FSResult	(*FS_ARCHIVE_READ_FUNC)
	(struct FSArchive *p, void *dst, u32 pos, u32 size);
typedef	FSResult	(*FS_ARCHIVE_WRITE_FUNC)
	(struct FSArchive *p, const void *src, u32 pos, u32 size);


/*
 * The following structures are released for the convenience of  archive implementers. 
 * With the file system API,  there is no need to use these directly. 
 */

typedef	struct	FSFileLink
{
	struct FSFile	*prev;
	struct FSFile	*next;
} FSFileLink;

typedef	struct	FSArchiveFAT
{
	u32	top;
	u32	bottom;
} FSArchiveFAT;

typedef	struct	FSArchiveFNT
{
	u32	start;
	u16	index;
	u16	parent;
} FSArchiveFNT;


/*
 * Archive structure
 * The archive implementer creates an instance of the structure. 
 * Then, it can add the archive to the file system with this procedure: 
 *
 * (1) Initialize with FS_InitArchive()
 * (2) Save the name with FS_RegisterArchiveName() 
 * (3) Load the archive into the system with FS_LoadArchive().
 *
 * If the archive is dynamic: 
 *
 * (4) Unload the archive from the system with FS_UnloadArchive() 
 *
 * Then, it can be used again.
 * If you want to change an archive name dynamically: 
 *
 * (5) Release the name with FS_ReleaseArchiveName() 
 *
 * Then, repeat the process from (2). 
 */

typedef	struct	FSArchive
{
/* private: */

	/* File system management variables */
	union {
		/* A unique name to identify the archive. 
		 A name of 1-3 alphanumeric chars. Not case sensitive. */
		char	ptr[FS_ARCHIVE_NAME_LEN_MAX + 1];
		u32	pack;
	} name;
	/* Archive linked list in system */
	struct	FSArchive	*next;
	struct	FSArchive	*prev;

	/* Control variables for command processing status */
	OSThreadQueue	sync_q;	/* sync processing (SeekDir etc.) */
	OSThreadQueue	stat_q;	/* Change status (Suspend etc.) */
	u32	flag;	/* Internal status flag  (FS_ARCHIVE_FLAG_*) */

	FSFileLink	list;	/* List of commands waiting to execute */

	/* Offsets based on ROM file format  */
	u32	base;		/* Base offset */
	u32	fat;		/* FAT offset */
	u32	fat_size;	/* FAT size */
	u32	fnt;		/* FNT offset */
	u32	fnt_size;	/* FNT size */
	u32	fat_bak;	/* FAT base offset before preload */
	u32	fnt_bak;	/* FNT base offset before preload */
	void	*load_mem;	/* Loaded table memory */

	/* Basic operation callback */
	FS_ARCHIVE_READ_FUNC	read_func;
	FS_ARCHIVE_WRITE_FUNC	write_func;

	/* Internal callback for table access.
	 (Because it does not use FS_IsArchiveTableLoaded() internally each time) */
	FS_ARCHIVE_READ_FUNC	table_func;

	/* User procedure (FS_ARCHIVE_PROC_*) */
	FS_ARCHIVE_PROC_FUNC	proc;	
	u32	proc_flag;

} FSArchive;


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

/* Initialization ***********************************************************/

/* Initialize the archive structure */
void	FS_InitArchive(FSArchive *p_arc);


/* Information *************************************************************/

/* Obtains archive name */
static inline const char*	FS_GetArchiveName(const FSArchive *p_arc)
{
	return	p_arc->name.ptr;
}

/* Obtains the base offset of the archive */
static inline u32	FS_GetArchiveBase(const FSArchive *p_arc)
{
	return	p_arc->base;
}

/* Obtains the FAT offset of the archive */
static inline u32	FS_GetArchiveFAT(const FSArchive *p_arc)
{
	return	p_arc->fat;
}

/* Obtains the FNT offset of the archive */
static inline u32	FS_GetArchiveFNT(const FSArchive *p_arc)
{
	return	p_arc->fnt;
}

/* Obtains the specified location offset from the base of the archive */
static inline u32	FS_GetArchiveOffset(const FSArchive *p_arc, u32 pos)
{
	return	p_arc->base + pos;
}

/* Determines if the archive has been loaded into the current file system */
static inline BOOL	FS_IsArchiveLoaded(volatile const FSArchive *p_arc)
{
	return	(p_arc->flag & FS_ARCHIVE_FLAG_LOADED) ? TRUE : FALSE;
}

/* Determines if the archive has preloaded the current table */
static inline BOOL	FS_IsArchiveTableLoaded(volatile const FSArchive *p_arc)
{
	return	(p_arc->flag & FS_ARCHIVE_FLAG_TABLE_LOAD) ? TRUE : FALSE;
}

/* Determines if the archive is currently being suspended */
static inline BOOL	FS_IsArchiveSuspended(volatile const FSArchive *p_arc)
{
	return	(p_arc->flag & FS_ARCHIVE_FLAG_SUSPEND) ? TRUE : FALSE;
}

/* Searches the archive that has a specified name */
FSArchive*	FS_FindArchive(const char *name, int name_len);


/* Control *************************************************************/

/*
 * Save the archive name and associate it with the archive.
 * The archive itself is not loaded into the file system. 
 * The archive name  "rom" is reserved.
 */
BOOL	FS_RegisterArchiveName(FSArchive *p_arc,
	const char *name, u32 name_len);

/*
 * Release a saved archive name. 
 * It must be unloaded from the file system. 
 */
void	FS_ReleaseArchiveName(FSArchive *p_arc);

/*
 * Load the archive into the file system. 
 * The name must be already saved. 
 */
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);

/*
 * Unload the archive from the file system.  
 * It will block until all currently running tasks are completed. 
 */
BOOL	FS_UnloadArchive(FSArchive *p_arc);

/* Preload FNT and FAT to speed up most access. */
u32	FS_LoadArchiveTables(FSArchive *p_arc, void *p_mem, u32 max_size);

/* Release the FNT and FAT preload memory. */
void*	FS_UnloadArchiveTables(FSArchive *p_arc);

/*
 * Stops archive processing itself. 
 * If already in normal state, returns TRUE; If suspended, returns FALSE.
 */
BOOL	FS_SuspendArchive(FSArchive *p_arc);

/*
 * Resumes processing of suspended archive.
 *  If already in normal state, returns TRUE; If suspended, returns FALSE.
 */
BOOL	FS_ResumeArchive(FSArchive *p_arc);

/*
 * Sets 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);

/*
 * Notify that asynchronous archive processing is complete. 
  */
void	FS_NotifyArchiveAsyncEnd(FSArchive *p_arc, FSResult ret);


/********************************************************************/


#ifdef __cplusplus
} /* extern "C" */
#endif


#endif	/* NITRO_FS_ARCHIVE_H_ */
