//
// Asura_Platform_FileAccess.cpp
//
// Created by KJFL - Wednesday, February 04, 2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "FileAccess/Asura_FileAccess.h"
// Asura headers
#include "Core/Asura_Maths.h"
#include "Core/Asura_PlatformBasics.h"
#include "Core/Asura_String.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_PSP_ScreenLog.h"
#include "FileAccess/Asura_PSP_FileIO.h"
// PSP headers
#include <iofilemgr.h>
#include <kerror.h>

#define ASURA_PSP_FILE_BUFFER_SIZE (32 * 1024)

// Buffer containing next <buffer size> bytes to be read
static char*			g_apcFileBuffer[ASURA_MAX_NO_OF_OPEN_FILES];
// How far we are in the buffer
static unsigned int		g_auPositionInBuffer[ASURA_MAX_NO_OF_OPEN_FILES];
// How far we are in the file (marks END address of the buffer)
static unsigned int		g_auBufferPositionInFile[ASURA_MAX_NO_OF_OPEN_FILES];
// Remaining bytes in the file to be read
static unsigned int		g_auBytesToEnd[ASURA_MAX_NO_OF_OPEN_FILES];
static unsigned int		g_auFileSize[ASURA_MAX_NO_OF_OPEN_FILES];

// PSP file handle
static int				g_aiFileHandle[ASURA_MAX_NO_OF_OPEN_FILES];
static Asura_TickCount	g_axStartTime[ASURA_MAX_NO_OF_OPEN_FILES];


//////////////////////////////////////////////////////////////////////////
// Internal open/close functions - needed for re-use...
//////////////////////////////////////////////////////////////////////////
static bool Open_Internal (const Asura_Handle iHandle, const char* szFilename, const bool bAllowRetry)
{
	char	szTotalFilename[256];

	Asura_PSP_FileIO::GetFullPath(szFilename, 256, szTotalFilename);
	Asura_PSP_ScreenLog::Log("Opening %s", szTotalFilename);
	// Open the file
	g_aiFileHandle[iHandle] = Asura_PSP_FileIO::Open(szTotalFilename, SCE_O_RDONLY, 0777, bAllowRetry);
	g_axStartTime[iHandle]	= Asura_Timers::GetCurTicks();
	if (g_aiFileHandle[iHandle] < 0)
	{
		Asura_PSP_ScreenLog::Log("IO returned error 0x%x for %s", g_aiFileHandle[iHandle], szTotalFilename);
		Asura_Assertf(0, ("IO returned error 0x%x while attempting to load file '%s'.", g_aiFileHandle[iHandle], szFilename));
		return false;
	}
	const int iFileSize = Asura_PSP_FileIO::GetFileSize(szTotalFilename);
	if(iFileSize < 0)
	{
		Asura_PSP_ScreenLog::Log("Negative filesize for %s", szTotalFilename);
		return false;
	}
	g_auFileSize[iHandle] = iFileSize;
	g_auBytesToEnd[iHandle] = iFileSize;
	g_auBufferPositionInFile[iHandle] = 0;
	g_auPositionInBuffer[iHandle] = 0;
	Asura_PSP_ScreenLog::Log("Successfully opened %s", szTotalFilename);
	return true;
}

static bool Close_Internal (const Asura_Handle iFileHandle)
{
	Asura_OutputToDebugger( ("Finished loading at file position %i (time = %.3fs)\n", g_auBufferPositionInFile[iFileHandle], Asura_Timers::TicksToSeconds(Asura_Timers::GetElapsedTicks(g_axStartTime[iFileHandle]))) );
	Asura_PSP_ScreenLog::Log("Finished loading at file position %i (time = %.3fs)\n", g_auBufferPositionInFile[iFileHandle], Asura_Timers::TicksToSeconds(Asura_Timers::GetElapsedTicks(g_axStartTime[iFileHandle])));
	const int	iRet	= Asura_PSP_FileIO::Close(g_aiFileHandle[iFileHandle]);

	return(iRet >= 0);
}

//////////////////////////////////////////////////////////////////////////
// General functions...
//////////////////////////////////////////////////////////////////////////
void Asura_FileAccess::Platform_Initialise( void )
{
	Asura_PSP_FileIO::Initialise();

	// Allocate buffers
	for(int i = 0; i < ASURA_MAX_NO_OF_OPEN_FILES; i++)
	{
		// ART - 06/02/2007 - D-Cache is 64 byte aligned so make sure that our loading
		// buffers match this in order to avoid any issues when writing data back into
		// the D-cache from elsewhere. There has been a mysterious bug where a few bytes
		// of the load data seems to have been corrupted by a piece of code which amounts
		// to "Asura_Render::BeginFrame()" followed by "Asura_Render::EndFrame()".
		g_apcFileBuffer[i] = static_cast<char*>(Asura_AllocateAlign(ASURA_PSP_FILE_BUFFER_SIZE, ASURA_ALIGN_64_BYTE));
	} 
}

void Asura_FileAccess::Platform_Shutdown( void )
{
	// Free buffers
	for(int i = 0; i < ASURA_MAX_NO_OF_OPEN_FILES; i++)
	{
		Asura_DeallocateAlign(g_apcFileBuffer[i]);
	}
	Asura_PSP_FileIO::Shutdown();
}

bool Asura_FileAccess::Platform_Open( Asura_Handle iHandle, const char* szFilename )
{
	if(IsFileRaw(iHandle))
	{
		return(Open_Internal(iHandle, szFilename, false));
	}
	else
	{
		if(Open_Internal(iHandle, szFilename, true))
		{
			Asura_OutputToDebugger( ("Loading: %s (filesize %i)\n", szFilename, g_auFileSize[iHandle]) );

			int iSizeRead;
			// Uses small buffer (size set at top of this file)
			if( g_auBytesToEnd[iHandle] < ASURA_PSP_FILE_BUFFER_SIZE)
			{
				// Read in entire file
				iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iHandle], g_apcFileBuffer[ iHandle ], g_auFileSize[iHandle], true);

				if ( iSizeRead != g_auFileSize[iHandle] )
				{
					Asura_Assert(false, "Asura_PSP_FileIO::Read() failed during Asura_FileAccess::Platform_Open");
					return false;
				}
			}
			else
			{
				// Read in the first ASURA_PSP_FILE_BUFFER_SIZE bytes
				iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iHandle], g_apcFileBuffer[ iHandle ], ASURA_PSP_FILE_BUFFER_SIZE, true);

				if ( iSizeRead != ASURA_PSP_FILE_BUFFER_SIZE )
				{
					Asura_Assert(false, "Asura_PSP_FileIO::Read() failed during Asura_FileAccess::Platform_Open");
					return false;
				}
			}
			g_auBufferPositionInFile[iHandle]	 = iSizeRead;
			g_auBytesToEnd[iHandle]				-= iSizeRead;
			g_auPositionInBuffer[iHandle]		 = 0;
			Asura_PSP_ScreenLog::Log("Successfully opened %s", szFilename);
			return true;
		}
		else
		{
			return(false);
		}
	}
}

bool Asura_FileAccess::Platform_Close( Asura_Handle iFileHandle )
{
	return(Close_Internal(iFileHandle));
}

#ifndef ASURA_PSP_UMD
#define USE_FAST_BUFFERED_LOAD (1)
#endif

bool Asura_FileAccess::Platform_DiscExists(void)
{
	return(Asura_PSP_FileIO::DiscExists());
}

bool Asura_FileAccess::Platform_FileExists( const char* szFilename )
{
	char	szTotalFilename[256];

	Asura_PSP_FileIO::GetFullPath(szFilename, 256, szTotalFilename);
	return(Asura_PSP_FileIO::FileExists(szTotalFilename));
}

u_int Asura_FileAccess::Platform_GetFileSize (const char* szFilename)
{
	char	szTotalFilename[256];

	Asura_PSP_FileIO::GetFullPath(szFilename, 256, szTotalFilename);
	return(Asura_PSP_FileIO::GetFileSize(szTotalFilename));
}

u_int Asura_FileAccess::Platform_GetFileSize (const Asura_Handle iFileHandle)
{
	return(g_auFileSize[iFileHandle]);
}

bool Asura_FileAccess::Platform_SeekFile(Asura_Handle iFileHandle, u_int uPosition)
{
	const SceUID	iSCEHandle	= g_aiFileHandle[iFileHandle];

	if (iSCEHandle < 0)
	{
		Asura_Assert(false, "Failed to seek due to invalid file-handle.");
		return(false);
	}
	else
	{
		const u_int	uPositionSeekedTo	= Asura_PSP_FileIO::Lseek(iSCEHandle, uPosition, SCE_SEEK_SET);

		// Did we succeed in our seek?
		if(uPositionSeekedTo == uPosition)
		{
			if(IsFileRaw(iFileHandle))
			{
				g_auBufferPositionInFile[iFileHandle]	= uPosition;
				g_auBytesToEnd[iFileHandle]				= g_auFileSize[iFileHandle]-uPosition;
				g_auPositionInBuffer[iFileHandle]		= 0;
				return(true);
			}
			else
			{
				int	iSizeRead;

				g_auBytesToEnd[iFileHandle]	= g_auFileSize[iFileHandle] - uPosition;

				// Uses small buffer (size set at top of this file)
				if( g_auBytesToEnd[iFileHandle] < ASURA_PSP_FILE_BUFFER_SIZE)
				{
					// Read in entire file
					iSizeRead = Asura_PSP_FileIO::Read(iSCEHandle, g_apcFileBuffer[iFileHandle], g_auBytesToEnd[iFileHandle]);

					if (iSizeRead != g_auBytesToEnd[iFileHandle])
					{
						return(false);
					}
				}
				else
				{
					// Read in the first ASURA_PSP_FILE_BUFFER_SIZE bytes
					iSizeRead	= Asura_PSP_FileIO::Read(iSCEHandle, g_apcFileBuffer[iFileHandle], ASURA_PSP_FILE_BUFFER_SIZE);

					if (iSizeRead != ASURA_PSP_FILE_BUFFER_SIZE)
					{
						return(false);
					}
				}
				g_auBufferPositionInFile[iFileHandle]	 = iSizeRead;
				g_auBytesToEnd[iFileHandle]				-= iSizeRead;
				g_auPositionInBuffer[iFileHandle]		 = 0;
				return(true);
			}
		}
		else
		{
			Asura_Assert(false, "Seek failed.");
			return(false);
		}
	}
}

bool Asura_FileAccess::Platform_ReadData( Asura_Handle iFileHandle, void* pvDest, u_int uNumBytes )
{
	if(IsFileRaw(iFileHandle))
	{
		const SceUID	iSCEHandle	= g_aiFileHandle[iFileHandle];

		if (iSCEHandle < 0)
		{
			Asura_Assert(false, "Failed to seek due to invalid file-handle.");
			return(false);
		}
		else
		{
			char*	pcBuffer	= g_apcFileBuffer[iFileHandle];
			char*	pcDest		= static_cast<char*>(pvDest);

			while(uNumBytes > 0)
			{
				const u_int	uBytesToRead	= Asura_Maths::Min(uNumBytes, static_cast<u_int>(ASURA_PSP_FILE_BUFFER_SIZE));
				int			iNumBytesRead	= Asura_PSP_FileIO::Read(iSCEHandle, pcBuffer, uBytesToRead, false);

				if(iNumBytesRead == uBytesToRead)
				{
					// Copy to the destination...
					Asura_Mem::Copy(pcDest, pcBuffer, uBytesToRead);

					// Update the variables...
					pcDest						+= uBytesToRead;
					uNumBytes					-= uBytesToRead;
					g_auBytesToEnd[iFileHandle]	-= uBytesToRead;
				}
				else
				{
					return(false);
				}
			}

			return(true);
		}
	}
	else
	{
#ifdef USE_FAST_BUFFERED_LOAD
		unsigned int iBytesRemaining = ASURA_PSP_FILE_BUFFER_SIZE - g_auPositionInBuffer[iFileHandle];
		char* pcSrc = g_apcFileBuffer[iFileHandle] + g_auPositionInBuffer[iFileHandle];
		char* pcDest = (char*)pvDest;
		if(uNumBytes < iBytesRemaining)
		{
			// Staying within this block
			Asura_Mem::Copy(pcDest, pcSrc, uNumBytes);
			g_auPositionInBuffer[iFileHandle] += uNumBytes;
			return true;
		}
		// Copy to end of this buffer block
		uNumBytes -= iBytesRemaining;
		Asura_Mem::Copy(pcDest, pcSrc, iBytesRemaining);
		pcDest += iBytesRemaining;

		int iSizeRead;

		if(uNumBytes >= ASURA_PSP_FILE_BUFFER_SIZE)
		{
			// We have at least one entire block to read
			unsigned int uBytesToRead = ASURA_PSP_FILE_BUFFER_SIZE
				* (unsigned int) (uNumBytes/ASURA_PSP_FILE_BUFFER_SIZE);
			iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], pcDest, uBytesToRead);
			if ( iSizeRead == uBytesToRead )
			{
				pcDest += uBytesToRead;
				g_auBytesToEnd[iFileHandle] -= uBytesToRead;
				g_auBufferPositionInFile[iFileHandle] += uBytesToRead;
				uNumBytes -= uBytesToRead;
			}
			else
			{
				Asura_Assertf(false, ("this block: incomplete read handle 0x%x read %d instead of %d",
					g_aiFileHandle[iFileHandle],iSizeRead,uBytesToRead));
				return(false);
			}
		}
		// We're going to end up in the next block. Now read in the buffer
		if(g_auBytesToEnd[iFileHandle] < ASURA_PSP_FILE_BUFFER_SIZE)
		{
			// Read remaining bytes to end of file
			iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], g_auBytesToEnd[iFileHandle]);
			if ( iSizeRead != g_auBytesToEnd[iFileHandle] )
			{
				Asura_Assertf(false, ("next block: incomplete read, handle 0x%x read %d instead of %d",
					g_aiFileHandle[iFileHandle],iSizeRead,g_auBytesToEnd[iFileHandle]));
				return(false);
			}
		}
		else
		{
			// Read in next <buffer size> bytes
			iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], ASURA_PSP_FILE_BUFFER_SIZE);
			if ( iSizeRead != ASURA_PSP_FILE_BUFFER_SIZE )
			{
				Asura_Assertf(false, ("next block whole: incomplete read for handle 0x%x read %d instead of %d",
					g_aiFileHandle[iFileHandle],iSizeRead,ASURA_PSP_FILE_BUFFER_SIZE));
				return(false);
			}
		}
		pcSrc = g_apcFileBuffer[iFileHandle];
		// Finally, copy last piece of data in if we need it.
		if(uNumBytes)
		{
			Asura_Mem::Copy(pcDest, pcSrc, uNumBytes);
		}
		g_auBufferPositionInFile[iFileHandle] += iSizeRead;
		g_auBytesToEnd[iFileHandle] -= iSizeRead;
		g_auPositionInBuffer[iFileHandle] = uNumBytes;
		return true;
#else//USE_FAST_BUFFERED_LOAD
		if(!uNumBytes) return true;
		unsigned int iBytesRemaining = ASURA_PSP_FILE_BUFFER_SIZE - g_auPositionInBuffer[iFileHandle];
		char* src = g_apcFileBuffer[iFileHandle] + g_auPositionInBuffer[iFileHandle];
		char* dest = (char*)pvDest;
		while(uNumBytes >= iBytesRemaining)
		{
			int	iSizeRead;

			// Copy entire buffer into destination
			Asura_Mem::Copy(dest, src, iBytesRemaining);
			dest += iBytesRemaining;
			uNumBytes -= iBytesRemaining;
			if(g_auBytesToEnd[iFileHandle] < ASURA_PSP_FILE_BUFFER_SIZE)
			{
				// Read remaining bytes to end of file
				iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], g_auBytesToEnd[ iFileHandle ]);

				if ( iSizeRead != g_auBytesToEnd[ iFileHandle ] )
				{
					return false;
				}
			}
			else
			{
				// Read in next ASURA_PSP_FILE_BUFFER_SIZE bytes
				iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], ASURA_PSP_FILE_BUFFER_SIZE);
				if ( iSizeRead != ASURA_PSP_FILE_BUFFER_SIZE )
				{
					return false;
				}
			}
			g_auBufferPositionInFile[iFileHandle] += iSizeRead;
			g_auBytesToEnd[iFileHandle] -= iSizeRead;
			g_auPositionInBuffer[iFileHandle] = 0;
			iBytesRemaining = ASURA_PSP_FILE_BUFFER_SIZE;
			src = g_apcFileBuffer[iFileHandle];
		}
		if(uNumBytes)
		{
			// Copy remaining bytes.
			Asura_Mem::Copy(dest, src, uNumBytes);
			g_auPositionInBuffer[iFileHandle] += uNumBytes;
		}

		return true;
#endif//!USE_FAST_BUFFERED_LOAD
	}
}

bool Asura_FileAccess::Platform_SkipUncompressedData( Asura_Handle iFileHandle, u_int uNumBytes )
{
	if(IsFileRaw(iFileHandle))
	{
		const SceUID	iSCEHandle	= g_aiFileHandle[iFileHandle];

		if (iSCEHandle < 0)
		{
			Asura_Assert(false, "Failed to seek due to invalid file-handle.");
			return(false);
		}
		else
		{
			char*	pcBuffer	= g_apcFileBuffer[iFileHandle];

			while(uNumBytes > 0)
			{
				const u_int	uBytesToRead	= Asura_Maths::Min(uNumBytes, static_cast<u_int>(ASURA_PSP_FILE_BUFFER_SIZE));
				int			iNumBytesRead	= Asura_PSP_FileIO::Read(iSCEHandle, pcBuffer, uBytesToRead, false);

				if(iNumBytesRead == uBytesToRead)
				{
					// Update the variables...
					uNumBytes					-= uBytesToRead;
					g_auBytesToEnd[iFileHandle]	-= uBytesToRead;
				}
				else
				{
					return(false);
				}
			}

			return(true);
		}
	}
	else
	{
		unsigned int iBytesRemaining = ASURA_PSP_FILE_BUFFER_SIZE - g_auPositionInBuffer[iFileHandle];
		if(uNumBytes < iBytesRemaining)
		{
			// Staying within this block
			g_auPositionInBuffer[iFileHandle] += uNumBytes;
			return true;
		}
		// Skip to end of this buffer block
		uNumBytes -= iBytesRemaining;
		if(uNumBytes >= ASURA_PSP_FILE_BUFFER_SIZE)
		{
			// We have blocks to skip
			unsigned int uBlocksToSkip = uNumBytes / ASURA_PSP_FILE_BUFFER_SIZE;

			int iNewPos = Asura_PSP_FileIO::Lseek(g_aiFileHandle[iFileHandle], uBlocksToSkip * ASURA_PSP_FILE_BUFFER_SIZE, SEEK_CUR);

			if ( iNewPos == g_auBufferPositionInFile[iFileHandle] + uBlocksToSkip * ASURA_PSP_FILE_BUFFER_SIZE)
			{
				g_auBytesToEnd[iFileHandle] -= uBlocksToSkip * ASURA_PSP_FILE_BUFFER_SIZE;
				g_auBufferPositionInFile[iFileHandle] += uBlocksToSkip * ASURA_PSP_FILE_BUFFER_SIZE;
				uNumBytes -= uBlocksToSkip * ASURA_PSP_FILE_BUFFER_SIZE;
			}
			else
			{
				return false;
			}
		}
		int	iSizeRead;

		// We're going to end up in the next block. Just read in the buffer
		if(g_auBytesToEnd[iFileHandle] < ASURA_PSP_FILE_BUFFER_SIZE)
		{
			// Read remaining bytes to end of file
			iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], g_auBytesToEnd[ iFileHandle ]);
			if ( iSizeRead != g_auBytesToEnd[ iFileHandle ] )
			{
				return false;
			}
		}
		else
		{
			// Read in next ASURA_PSP_FILE_BUFFER_SIZE bytes
			iSizeRead = Asura_PSP_FileIO::Read(g_aiFileHandle[iFileHandle], g_apcFileBuffer[ iFileHandle ], ASURA_PSP_FILE_BUFFER_SIZE);
			if ( iSizeRead != ASURA_PSP_FILE_BUFFER_SIZE )
			{
				return false;
			}
		}
		g_auBufferPositionInFile[iFileHandle] += iSizeRead;
		g_auBytesToEnd[iFileHandle] -= iSizeRead;
		g_auPositionInBuffer[iFileHandle] = uNumBytes;
		return true;
	}
}

//////////////////////////////////////////////////////////////////////////
// Asynchronous load (not supported)
//////////////////////////////////////////////////////////////////////////
bool Asura_FileAccess::Platform_LoadAsynchronous(Asura_Handle iHandle,const char* szFilename)
{	
	Asura_Assert( 0,"Asynchronous loading not supported");
    return true;	
}

enum ASURA_ASYNCLOADING_STATUS Asura_FileAccess::Platform_GetAsynchronousLoadingStatus(Asura_Handle iFileHandle)
{
	Asura_Assert( 0,"Asynchronous loading not supported");
	return ASURA_ASYNCLOADING_STATUS_ERROR;
}

// Load from network functions (not supported)
void Asura_FileAccess::PlatformGetNetworkDir( const char* szDir, char* szOutputBuffer, u_int uOutputBufferSize, bool bWarnIfOnlyLocal )
{
	Asura_Assert(0, "Load from network not supported");
}

void Asura_FileAccess::PlatformGetNetworkFilename( const char* szFilename, char* szOutputBuffer,u_int uOutputBufferSize )
{
	//network loading not enabled.. just return the original filename
	Asura_Assert(szFilename,"");
	Asura_Assert(strlen(szFilename)<uOutputBufferSize,"");
	strcpy(szOutputBuffer,szFilename);
	return;
}

bool Asura_FileAccess::PlatformUsingNetworkFile( const char* szFilename )
{
	return false;
}

const char* Asura_FileAccess::PlatformRemoveNetworkOrExecutableDir( const char* szPath )
{
	return szPath;
}

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_FileAccess::Platform_OpenFromMemory().
// PARAMETERS   iFileHandle - effectively, index into open file array
//              pMem        - pointer to buffer containing file contents
//              uSize       - size of buffer
// RETURNS      false, as we don't support this method on this platform.
// DESCRIPTION  sets up file reading from memory that already contains a file.
// AUTHOR       Mike Healey - 17.08.2005.
//
//////////////////////////////////////////////////////////////////////////////
bool Asura_FileAccess::Platform_OpenFromMemory( Asura_Handle iFileHandle, const void* pMem, u_int uSize )
{
	return false;
}
// End function Asura_FileAccess::Platform_OpenFromMemory().

void Asura_FileAccess::Platform_FindFiles( const char* szDirectory, const char* szExtension, void (*pfnCallback)( const char* szFilename ), bool bRecursive /* = true */ )
{
	// (AAM - 04.03.2009) This code mostly taken from Vanilla_DevelopmentFrontEnd::
	// Platform_FindFiles() which was removed

	Asura_Assert( pfnCallback, "NULL callback passed to Asura_FileAccess::Platform_FindFiles()." );
	if( !pfnCallback ) return;

	Asura_Char szCurrentPath[256];
	Asura_Char szFullPath[256];
	int iRet;
	bool bWhileOk = true;
	SceIoDirent xContents;
	struct SceIoStat xStat;
	SceUID g_xFileDescriptor;
	//open directory
	Asura_PSP_FileIO::GetFullPath( szDirectory, 256, szFullPath );
	g_xFileDescriptor = Asura_PSP_FileIO::OpenDir( szFullPath );

	while( bWhileOk )
	{
		iRet = Asura_PSP_FileIO::ReadDir( g_xFileDescriptor, &xContents );
		if( iRet < 0 )
		{
			Asura_Assert( false, "Error during reading on PSP (ReadDir)" );

			Asura_PSP_FileIO::CloseDir( g_xFileDescriptor );
			bWhileOk = false;
		}
		else if( iRet == 0 ) //finished reading context of directory
		{
			Asura_PSP_FileIO::CloseDir( g_xFileDescriptor );
			bWhileOk = false;
		}
		else
		{
			if( xContents.d_name &&
				strlen( xContents.d_name ) > 2 ) // ignore . and ..
			{
				Asura_PrintfToBfr( szCurrentPath, "%s\\%s", 256, true, szDirectory, xContents.d_name );		
				Asura_PSP_FileIO::GetFullPath( szCurrentPath, 256, szFullPath );
				Asura_Assert(Asura_PSP_FileIO::Getstat( szFullPath, &xStat ) == 0, "Error during getting stat (Getstat)" );

				if( xStat.st_mode & SCE_STM_FREG ) //regular file
				{
					if( !szExtension || Asura_StringUtil::EndsWith( szCurrentPath, szExtension, true ) )
					{
						pfnCallback( szCurrentPath);
					}
				}
				else if( (xStat.st_mode & SCE_STM_FDIR) && bRecursive ) //directory
				{
					FindFiles( szCurrentPath, szExtension, pfnCallback, bRecursive );
				}
			}
		}
	}//while
}
