PageRenderTime 109ms CodeModel.GetById 3ms app.highlight 94ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/3rdparty/fullfat/ff_file.c

https://bitbucket.org/arty/arty-newcc-reactos
C | 1767 lines | 1236 code | 262 blank | 269 comment | 270 complexity | f3f265c6fa49b176486f3984a3d55cee MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*****************************************************************************
   2 *  FullFAT - High Performance, Thread-Safe Embedded FAT File-System         *
   3 *  Copyright (C) 2009  James Walmsley (james@worm.me.uk)                    *
   4 *                                                                           *
   5 *  This program is free software: you can redistribute it and/or modify     *
   6 *  it under the terms of the GNU General Public License as published by     *
   7 *  the Free Software Foundation, either version 3 of the License, or        *
   8 *  (at your option) any later version.                                      *
   9 *                                                                           *
  10 *  This program is distributed in the hope that it will be useful,          *
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
  13 *  GNU General Public License for more details.                             *
  14 *                                                                           *
  15 *  You should have received a copy of the GNU General Public License        *
  16 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.    *
  17 *                                                                           *
  18 *  IMPORTANT NOTICE:                                                        *
  19 *  =================                                                        *
  20 *  Alternative Licensing is available directly from the Copyright holder,   *
  21 *  (James Walmsley). For more information consult LICENSING.TXT to obtain   *
  22 *  a Commercial license.                                                    *
  23 *                                                                           *
  24 *  See RESTRICTIONS.TXT for extra restrictions on the use of FullFAT.       *
  25 *                                                                           *
  26 *  Removing the above notice is illegal and will invalidate this license.   *
  27 *****************************************************************************
  28 *  See http://worm.me.uk/fullfat for more information.                      *
  29 *  Or  http://fullfat.googlecode.com/ for latest releases and the wiki.     *
  30 *****************************************************************************/
  31
  32/**
  33 *	@file		ff_file.c
  34 *	@author		James Walmsley
  35 *	@ingroup	FILEIO
  36 *
  37 *	@defgroup	FILEIO FILE I/O Access
  38 *	@brief		Provides an interface to allow File I/O on a mounted IOMAN.
  39 *
  40 *	Provides file-system interfaces for the FAT file-system.
  41 **/
  42
  43#include "ff_file.h"
  44#include "ff_string.h"
  45#include "ff_config.h"
  46
  47#ifdef FF_UNICODE_SUPPORT
  48#include <wchar.h>
  49#endif
  50
  51/**
  52 *	@public
  53 *	@brief	Converts STDIO mode strings into the equivalent FullFAT mode.
  54 *
  55 *	@param	Mode	The mode string e.g. "rb" "rb+" "w" "a" "r" "w+" "a+" etc
  56 *
  57 *	@return	Returns the mode bits that should be passed to the FF_Open function.
  58 **/
  59FF_T_UINT8 FF_GetModeBits(FF_T_INT8 *Mode) {
  60	FF_T_UINT8 ModeBits = 0x00;
  61	while(*Mode) {
  62		switch(*Mode) {
  63			case 'r':	// Allow Read
  64			case 'R':
  65				ModeBits |= FF_MODE_READ;
  66				break;
  67
  68			case 'w':	// Allow Write
  69			case 'W':
  70				ModeBits |= FF_MODE_WRITE;
  71				ModeBits |= FF_MODE_CREATE;	// Create if not exist.
  72				ModeBits |= FF_MODE_TRUNCATE;
  73				break;
  74
  75			case 'a':	// Append new writes to the end of the file.
  76			case 'A':
  77				ModeBits |= FF_MODE_WRITE;
  78				ModeBits |= FF_MODE_APPEND;
  79				ModeBits |= FF_MODE_CREATE;	// Create if not exist.
  80				break;
  81
  82			case '+':	// Update the file, don't Append!
  83				ModeBits |= FF_MODE_READ;	// RW Mode
  84				ModeBits |= FF_MODE_WRITE;	// RW Mode
  85				break;
  86
  87			/*case 'D':	// Internal use only!
  88				ModeBits |= FF_MODE_DIR;
  89				break;*/
  90
  91			default:	// b|B flags not supported (Binary mode is native anyway).
  92				break;
  93		}
  94		Mode++;
  95	}
  96
  97	return ModeBits;
  98}
  99
 100/**
 101 * FF_Open() Mode Information
 102 * - FF_MODE_WRITE
 103 *   - Allows WRITE access to the file.
 104 *   .
 105 * - FF_MODE_READ
 106 *   - Allows READ access to the file.
 107 *   .
 108 * - FF_MODE_CREATE
 109 *   - Create file if it doesn't exist.
 110 *   .
 111 * - FF_MODE_TRUNCATE
 112 *   - Erase the file if it already exists and overwrite.
 113 *   *
 114 * - FF_MODE_APPEND
 115 *   - Causes all writes to occur at the end of the file. (Its impossible to overwrite other data in a file with this flag set).
 116 *   . 
 117 * .
 118 *
 119 * Some sample modes:
 120 * - (FF_MODE_WRITE | FF_MODE_CREATE | FF_MODE_TRUNCATE)
 121 *   - Write access to the file. (Equivalent to "w").
 122 *   .
 123 * - (FF_MODE_WRITE | FF_MODE_READ)
 124 *   - Read and Write access to the file. (Equivalent to "rb+").
 125 *   .
 126 * - (FF_MODE_WRITE | FF_MODE_READ | FF_MODE_APPEND | FF_MODE_CREATE)
 127 *   - Read and Write append mode access to the file. (Equivalent to "a+").
 128 *   .
 129 * .
 130 * Be careful when choosing modes. For those using FF_Open() at the application layer
 131 * its best to use the provided FF_GetModeBits() function, as this complies to the same
 132 * behaviour as the stdio.h fopen() function.
 133 *
 134 **/
 135
 136
 137/**
 138 *	@public
 139 *	@brief	Opens a File for Access
 140 *
 141 *	@param	pIoman		FF_IOMAN object that was created by FF_CreateIOMAN().
 142 *	@param	path		Path to the File or object.
 143 *	@param	Mode		Access Mode required. Modes are a little complicated, the function FF_GetModeBits()
 144 *	@param	Mode		will convert a stdio Mode string into the equivalent Mode bits for this parameter.
 145 *	@param	pError		Pointer to a signed byte for error checking. Can be NULL if not required.
 146 *	@param	pError		To be checked when a NULL pointer is returned.
 147 *
 148 *	@return	NULL pointer on Error, in which case pError should be checked for more information.
 149 *	@return	pError can be:
 150 **/
 151#ifdef FF_UNICODE_SUPPORT
 152FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_WCHAR *path, FF_T_UINT8 Mode, FF_ERROR *pError) {
 153#else
 154FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_INT8 *path, FF_T_UINT8 Mode, FF_ERROR *pError) {
 155#endif
 156	FF_FILE		*pFile;
 157	FF_FILE		*pFileChain;
 158	FF_DIRENT	Object;
 159	FF_T_UINT32 DirCluster, FileCluster;
 160	FF_T_UINT32	nBytesPerCluster;
 161#ifdef FF_UNICODE_SUPPORT
 162	FF_T_WCHAR	filename[FF_MAX_FILENAME];
 163#else
 164	FF_T_INT8	filename[FF_MAX_FILENAME];
 165#endif
 166	FF_ERROR	Error;
 167
 168	FF_T_UINT16	i;
 169
 170	if(pError) {
 171		*pError = 0;
 172	}
 173	
 174	if(!pIoman) {
 175		if(pError) {
 176			*pError = FF_ERR_NULL_POINTER;
 177		}
 178		return (FF_FILE *)NULL;
 179	}
 180	pFile = FF_MALLOC(sizeof(FF_FILE));
 181	if(!pFile) {
 182		if(pError) {
 183			*pError = FF_ERR_NOT_ENOUGH_MEMORY;
 184		}
 185		return (FF_FILE *)NULL;
 186	}
 187
 188	// Get the Mode Bits.
 189	pFile->Mode = Mode;
 190
 191#ifdef FF_UNICODE_SUPPORT
 192	i = (FF_T_UINT16) wcslen(path);
 193#else 
 194	i = (FF_T_UINT16) strlen(path);
 195#endif
 196
 197	while(i != 0) {
 198		if(path[i] == '\\' || path[i] == '/') {
 199			break;
 200		}
 201		i--;
 202	}
 203#ifdef FF_UNICODE_SUPPORT
 204	wcsncpy(filename, (path + i + 1), FF_MAX_FILENAME);
 205#else
 206	strncpy(filename, (path + i + 1), FF_MAX_FILENAME);
 207#endif
 208
 209	if(i == 0) {
 210		i = 1;
 211	}
 212	
 213
 214	DirCluster = FF_FindDir(pIoman, path, i, &Error);
 215	if(Error) {
 216		if(pError) {
 217			*pError = Error;
 218		}
 219		FF_FREE(pFile);
 220		return (FF_FILE *) NULL;
 221	}
 222
 223	if(DirCluster) {
 224
 225		FileCluster = FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object, &Error);
 226		if(Error) {
 227			if(pError) {
 228				*pError = Error;	
 229			}
 230			FF_FREE(pFile);
 231			return (FF_FILE *) NULL;
 232		}
 233
 234		if(!FileCluster) {	// If 0 was returned, it might be because the file has no allocated cluster
 235#ifdef FF_UNICODE_SUPPORT
 236			if(wcslen(filename) == wcslen(Object.FileName)) {
 237				if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) wcslen(filename)) == FF_TRUE) {
 238#else
 239			if(strlen(filename) == strlen(Object.FileName)) {
 240				if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) strlen(filename)) == FF_TRUE) {
 241#endif
 242					// The file really was found!
 243					FileCluster = 1;
 244				} 
 245			}
 246		}
 247
 248		if(!FileCluster) {
 249			if((pFile->Mode & FF_MODE_CREATE)) {
 250				FileCluster = FF_CreateFile(pIoman, DirCluster, filename, &Object, &Error);
 251				if(Error) {
 252					if(pError) {
 253						*pError = Error;
 254					}
 255					FF_FREE(pFile);
 256					return (FF_FILE *) NULL;
 257				}
 258				Object.CurrentItem += 1;
 259			}
 260		}
 261		
 262		if(FileCluster) {
 263			if(Object.Attrib == FF_FAT_ATTR_DIR) {
 264				if(!(pFile->Mode & FF_MODE_DIR)) {
 265					// Not the object, File Not Found!
 266					FF_FREE(pFile);
 267					if(pError) {
 268						*pError = FF_ERR_FILE_OBJECT_IS_A_DIR;
 269					}
 270					return (FF_FILE *) NULL;
 271				}
 272			}
 273			
 274			//---------- Ensure Read-Only files don't get opened for Writing.
 275			if((pFile->Mode & FF_MODE_WRITE) || (pFile->Mode & FF_MODE_APPEND)) {
 276				if((Object.Attrib & FF_FAT_ATTR_READONLY)) {
 277					FF_FREE(pFile);
 278					if(pError) {
 279						*pError = FF_ERR_FILE_IS_READ_ONLY;
 280					}
 281					return (FF_FILE *) NULL;
 282				}
 283			}
 284			pFile->pIoman				= pIoman;
 285			pFile->FilePointer			= 0;
 286			pFile->ObjectCluster		= Object.ObjectCluster;
 287			pFile->Filesize				= Object.Filesize;
 288			pFile->CurrentCluster		= 0;
 289			pFile->AddrCurrentCluster	= pFile->ObjectCluster;
 290			//pFile->Mode					= Mode;
 291			pFile->Next					= NULL;
 292			pFile->DirCluster			= DirCluster;
 293			pFile->DirEntry				= Object.CurrentItem - 1;
 294			nBytesPerCluster			= pFile->pIoman->pPartition->SectorsPerCluster / pIoman->BlkSize;
 295			pFile->iChainLength			= 0;
 296			pFile->iEndOfChain			= 0;
 297			pFile->FileDeleted			= FF_FALSE;
 298
 299			// File Permission Processing
 300			// Only "w" and "w+" mode strings can erase a file's contents.
 301			// Any other combinations will not cause an erase.
 302			if((pFile->Mode & FF_MODE_TRUNCATE)) {
 303				pFile->Filesize = 0;
 304				pFile->FilePointer = 0;
 305			}
 306
 307			/*
 308				Add pFile onto the end of our linked list of FF_FILE objects.
 309			*/
 310			FF_PendSemaphore(pIoman->pSemaphore);
 311			{
 312				if(!pIoman->FirstFile) {
 313					pIoman->FirstFile = pFile;
 314				} else {
 315					pFileChain = (FF_FILE *) pIoman->FirstFile;
 316					do {
 317						if(pFileChain->ObjectCluster == pFile->ObjectCluster) {
 318							// File is already open! DON'T ALLOW IT!
 319							FF_ReleaseSemaphore(pIoman->pSemaphore);
 320							FF_FREE(pFile);
 321							if(pError) {
 322								*pError = FF_ERR_FILE_ALREADY_OPEN;
 323							}
 324							return (FF_FILE *) NULL;
 325						}
 326						if(!pFileChain->Next) {
 327							pFileChain->Next = pFile;
 328							break;
 329						}
 330						pFileChain = (FF_FILE *) pFileChain->Next;
 331					}while(pFileChain != NULL);
 332				}
 333			}
 334			FF_ReleaseSemaphore(pIoman->pSemaphore);
 335
 336			return pFile;
 337		}else {
 338			FF_FREE(pFile);
 339			if(pError) {
 340				*pError = FF_ERR_FILE_NOT_FOUND;
 341			}
 342			return (FF_FILE *) NULL;
 343		} 
 344	}
 345	if(pError) {
 346		*pError = FF_ERR_FILE_INVALID_PATH;
 347	}
 348
 349	FF_FREE(pFile);
 350
 351	return (FF_FILE *)NULL;
 352}
 353
 354
 355/**
 356 *	@public
 357 *	@brief	Tests if a Directory contains any other files or folders.
 358 *
 359 *	@param	pIoman	FF_IOMAN object returned from the FF_CreateIOMAN() function.
 360 *
 361 **/
 362#ifdef FF_UNICODE_SUPPORT
 363FF_T_BOOL FF_isDirEmpty(FF_IOMAN *pIoman, const FF_T_WCHAR *Path) {
 364#else
 365FF_T_BOOL FF_isDirEmpty(FF_IOMAN *pIoman, const FF_T_INT8 *Path) {
 366#endif
 367	
 368	FF_DIRENT	MyDir;
 369	FF_ERROR	RetVal = FF_ERR_NONE;
 370	FF_T_UINT8	i = 0;
 371
 372	if(!pIoman) {
 373		return FF_FALSE;
 374	}
 375	
 376	RetVal = FF_FindFirst(pIoman, &MyDir, Path);
 377	while(RetVal == 0) {
 378		i++;
 379		RetVal = FF_FindNext(pIoman, &MyDir);
 380		if(i > 2) {
 381			return FF_FALSE;
 382		}
 383	}
 384
 385	return FF_TRUE;
 386}
 387
 388#ifdef FF_UNICODE_SUPPORT
 389FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_WCHAR *path) {
 390#else
 391FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_INT8 *path) {
 392#endif
 393	FF_FILE 			*pFile;
 394	FF_ERROR 			Error = FF_ERR_NONE;
 395	FF_T_UINT8 			EntryBuffer[32];
 396	FF_FETCH_CONTEXT	FetchContext;
 397	FF_T_SINT8 			RetVal = FF_ERR_NONE;
 398#ifdef FF_PATH_CACHE
 399	FF_T_UINT32 i;
 400#endif
 401
 402	if(!pIoman) {
 403		return FF_ERR_NULL_POINTER;
 404	}
 405
 406	pFile = FF_Open(pIoman, path, FF_MODE_DIR, &Error);
 407
 408	if(!pFile) {
 409		return Error;	// File in use or File not found!
 410	}
 411
 412	pFile->FileDeleted = FF_TRUE;
 413	
 414	FF_lockDIR(pIoman);
 415	{
 416		if(FF_isDirEmpty(pIoman, path)) {
 417			FF_lockFAT(pIoman);
 418			{
 419				Error = FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0);	// 0 to delete the entire chain!
 420			}
 421			FF_unlockFAT(pIoman);
 422
 423			if(Error) {
 424				FF_unlockDIR(pIoman);
 425				FF_Close(pFile);
 426				return Error;				
 427			}
 428
 429			// Initialise the dirent Fetch Context object for faster removal of dirents.
 430
 431			Error = FF_InitEntryFetch(pIoman, pFile->DirCluster, &FetchContext);
 432			if(Error) {
 433				FF_unlockDIR(pIoman);
 434				FF_Close(pFile);
 435				return Error;
 436			}
 437			
 438			// Edit the Directory Entry! (So it appears as deleted);
 439			Error = FF_RmLFNs(pIoman, pFile->DirEntry, &FetchContext);
 440			if(Error) {
 441				FF_CleanupEntryFetch(pIoman, &FetchContext);
 442				FF_unlockDIR(pIoman);
 443				FF_Close(pFile);
 444				return Error;
 445			}
 446			Error = FF_FetchEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer);
 447			if(Error) {
 448				FF_CleanupEntryFetch(pIoman, &FetchContext);
 449				FF_unlockDIR(pIoman);
 450				FF_Close(pFile);
 451				return Error;
 452			}
 453			EntryBuffer[0] = 0xE5;
 454			Error = FF_PushEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer);
 455			if(Error) {
 456				FF_CleanupEntryFetch(pIoman, &FetchContext);
 457				FF_unlockDIR(pIoman);
 458				FF_Close(pFile);
 459				return Error;
 460			}
 461#ifdef FF_PATH_CACHE
 462			FF_PendSemaphore(pIoman->pSemaphore);	// Thread safety on shared object!
 463			{
 464				for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) {
 465#ifdef FF_UNICODE_SUPPORT
 466					if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)wcslen(path))) {
 467#else
 468					if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)strlen(path))) {
 469#endif
 470						pIoman->pPartition->PathCache[i].Path[0] = '\0';
 471						pIoman->pPartition->PathCache[i].DirCluster = 0;
 472						FF_ReleaseSemaphore(pIoman->pSemaphore);
 473					}
 474				}
 475			}
 476			FF_ReleaseSemaphore(pIoman->pSemaphore);
 477#endif
 478			
 479			Error = FF_IncreaseFreeClusters(pIoman, pFile->iChainLength);
 480			if(Error) {
 481				FF_CleanupEntryFetch(pIoman, &FetchContext);
 482				FF_unlockDIR(pIoman);
 483				FF_Close(pFile);
 484				return Error;
 485			}
 486
 487			FF_CleanupEntryFetch(pIoman, &FetchContext);
 488
 489			Error = FF_FlushCache(pIoman);
 490			if(Error) {
 491				FF_unlockDIR(pIoman);
 492				FF_Close(pFile);
 493				return Error;
 494			}
 495		} else {
 496			RetVal = FF_ERR_DIR_NOT_EMPTY;
 497		}
 498	}
 499	FF_unlockDIR(pIoman);
 500	Error = FF_Close(pFile); // Free the file pointer resources
 501
 502	if(Error) {
 503		return Error;
 504	}
 505
 506	// File is now lost!
 507	return RetVal;
 508}
 509
 510#ifdef FF_UNICODE_SUPPORT
 511FF_ERROR FF_RmFile(FF_IOMAN *pIoman, const FF_T_WCHAR *path) {
 512#else
 513FF_ERROR FF_RmFile(FF_IOMAN *pIoman, const FF_T_INT8 *path) {
 514#endif
 515	FF_FILE *pFile;
 516	FF_ERROR Error = FF_ERR_NONE;
 517	FF_T_UINT8 EntryBuffer[32];
 518	FF_FETCH_CONTEXT FetchContext;
 519
 520	pFile = FF_Open(pIoman, path, FF_MODE_READ, &Error);
 521
 522	if(!pFile) {
 523		return Error;	// File in use or File not found!
 524	}
 525
 526	pFile->FileDeleted = FF_TRUE;
 527
 528	if(pFile->ObjectCluster) {	// Ensure there is actually a cluster chain to delete!
 529		FF_lockFAT(pIoman);	// Lock the FAT so its thread-safe.
 530		{
 531			Error = FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0);	// 0 to delete the entire chain!
 532		}
 533		FF_unlockFAT(pIoman);
 534
 535		if(Error) {
 536			FF_Close(pFile);
 537			return Error;
 538		}
 539	}
 540
 541	// Edit the Directory Entry! (So it appears as deleted);
 542	FF_lockDIR(pIoman);
 543	{
 544		Error = FF_InitEntryFetch(pIoman, pFile->DirCluster, &FetchContext);
 545		if(Error) {
 546			FF_unlockDIR(pIoman);
 547			FF_Close(pFile);
 548			return Error;
 549		}
 550		Error = FF_RmLFNs(pIoman, pFile->DirEntry, &FetchContext);
 551		if(Error) {
 552			FF_CleanupEntryFetch(pIoman, &FetchContext);
 553			FF_unlockDIR(pIoman);
 554			FF_Close(pFile);
 555			return Error;
 556		}
 557		Error = FF_FetchEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer);
 558		if(Error) {
 559			FF_CleanupEntryFetch(pIoman, &FetchContext);
 560			FF_unlockDIR(pIoman);
 561			FF_Close(pFile);
 562			return Error;
 563		}
 564		EntryBuffer[0] = 0xE5;
 565		
 566		Error = FF_PushEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer);
 567		if(Error) {
 568			FF_CleanupEntryFetch(pIoman, &FetchContext);
 569			FF_unlockDIR(pIoman);
 570			FF_Close(pFile);
 571			return Error;
 572		}
 573
 574		FF_CleanupEntryFetch(pIoman, &FetchContext);
 575	}
 576	FF_unlockDIR(pIoman);
 577
 578	Error = FF_FlushCache(pIoman);
 579	if(Error) {
 580		FF_Close(pFile);
 581		return Error;
 582	}
 583	
 584	Error = FF_Close(pFile); // Free the file pointer resources
 585	return Error;
 586}
 587
 588/**
 589 *	@public
 590 *	@brief	Moves a file or directory from source to destination.
 591 *
 592 *	@param	pIoman				The FF_IOMAN object pointer.
 593 *	@param	szSourceFile		String of the source file to be moved or renamed.
 594 *	@param	szDestinationFile	String of the destination file to where the source should be moved or renamed.
 595 *
 596 *	@return	FF_ERR_NONE on success.
 597 *	@return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.
 598 *	@return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).
 599 *	@return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.
 600 *	@return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.
 601 *
 602 **/
 603#ifdef FF_UNICODE_SUPPORT
 604FF_ERROR FF_Move(FF_IOMAN *pIoman, const FF_T_WCHAR *szSourceFile, const FF_T_WCHAR *szDestinationFile) {
 605#else
 606FF_ERROR FF_Move(FF_IOMAN *pIoman, const FF_T_INT8 *szSourceFile, const FF_T_INT8 *szDestinationFile) {
 607#endif
 608	FF_ERROR	Error;
 609	FF_FILE		*pSrcFile, *pDestFile;
 610	FF_DIRENT	MyFile;
 611	FF_T_UINT8	EntryBuffer[32];
 612	FF_T_UINT16 i;
 613	FF_T_UINT32	DirCluster;
 614	FF_FETCH_CONTEXT	FetchContext;
 615
 616	if(!pIoman) {
 617		return FF_ERR_NULL_POINTER;
 618	}
 619
 620	// Check destination file doesn't exist!
 621	pDestFile = FF_Open(pIoman, szDestinationFile, FF_MODE_READ, &Error);
 622
 623	if(pDestFile || (Error == FF_ERR_FILE_OBJECT_IS_A_DIR)) {
 624		FF_Close(pDestFile);
 625		return FF_ERR_FILE_DESTINATION_EXISTS;	// YES -- FAIL
 626	}
 627
 628	pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_READ, &Error);
 629
 630	if(Error == FF_ERR_FILE_OBJECT_IS_A_DIR) {
 631		// Open a directory for moving!
 632		pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_DIR, &Error);
 633	}
 634
 635	if(!pSrcFile) {
 636		return Error;
 637	}
 638
 639	if(pSrcFile) {
 640		// Create the new dirent.
 641		Error = FF_InitEntryFetch(pIoman, pSrcFile->DirCluster, &FetchContext);
 642		if(Error) {
 643			FF_Close(pSrcFile);
 644			return Error;
 645		}
 646		Error = FF_FetchEntryWithContext(pIoman, pSrcFile->DirEntry, &FetchContext, EntryBuffer);
 647		if(Error) {
 648			FF_Close(pSrcFile);
 649			FF_CleanupEntryFetch(pIoman, &FetchContext);
 650			return Error;
 651		}
 652		//FF_FetchEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
 653		MyFile.Attrib			= FF_getChar(EntryBuffer,  (FF_T_UINT16)(FF_FAT_DIRENT_ATTRIB));
 654		MyFile.Filesize			= pSrcFile->Filesize;
 655		MyFile.ObjectCluster	= pSrcFile->ObjectCluster;
 656		MyFile.CurrentItem		= 0;
 657
 658#ifdef FF_UNICODE_SUPPORT
 659		i = (FF_T_UINT16) wcslen(szDestinationFile);
 660#else
 661		i = (FF_T_UINT16) strlen(szDestinationFile);
 662#endif
 663
 664		while(i != 0) {
 665			if(szDestinationFile[i] == '\\' || szDestinationFile[i] == '/') {
 666				break;
 667			}
 668			i--;
 669		}
 670
 671#ifdef FF_UNICODE_SUPPORT
 672		wcsncpy(MyFile.FileName, (szDestinationFile + i + 1), FF_MAX_FILENAME);
 673#else
 674		strncpy(MyFile.FileName, (szDestinationFile + i + 1), FF_MAX_FILENAME);
 675#endif
 676
 677		if(i == 0) {
 678			i = 1;
 679		}
 680		
 681
 682		DirCluster = FF_FindDir(pIoman, szDestinationFile, i, &Error);
 683		if(Error) {
 684			FF_Close(pSrcFile);
 685			FF_CleanupEntryFetch(pIoman, &FetchContext);
 686			return Error;
 687		}
 688		
 689		if(DirCluster) {
 690
 691			// Destination Dir was found, we can now create the new entry.
 692			Error = FF_CreateDirent(pIoman, DirCluster, &MyFile);
 693			if(Error) {
 694				FF_Close(pSrcFile);
 695				FF_CleanupEntryFetch(pIoman, &FetchContext);
 696				return Error;	// FAILED
 697			}
 698
 699			// Edit the Directory Entry! (So it appears as deleted);
 700			FF_lockDIR(pIoman);
 701			{
 702
 703				Error = FF_RmLFNs(pIoman, pSrcFile->DirEntry, &FetchContext);
 704				if(Error) {
 705					FF_unlockDIR(pIoman);
 706					FF_Close(pSrcFile);
 707					FF_CleanupEntryFetch(pIoman, &FetchContext);
 708					return Error;
 709				}
 710				Error = FF_FetchEntryWithContext(pIoman, pSrcFile->DirEntry, &FetchContext, EntryBuffer);
 711				if(Error) {
 712					FF_unlockDIR(pIoman);
 713					FF_Close(pSrcFile);
 714					FF_CleanupEntryFetch(pIoman, &FetchContext);
 715					return Error;
 716				}
 717				EntryBuffer[0] = 0xE5;
 718				//FF_PushEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
 719				Error = FF_PushEntryWithContext(pIoman, pSrcFile->DirEntry, &FetchContext, EntryBuffer);
 720				if(Error) {
 721					FF_unlockDIR(pIoman);
 722					FF_Close(pSrcFile);
 723					FF_CleanupEntryFetch(pIoman, &FetchContext);
 724					return Error;
 725				}
 726				FF_CleanupEntryFetch(pIoman, &FetchContext);
 727			}
 728			FF_unlockDIR(pIoman);
 729			FF_Close(pSrcFile);
 730
 731			FF_FlushCache(pIoman);
 732
 733			return FF_ERR_NONE;
 734		}
 735
 736		return FF_ERR_FILE_DIR_NOT_FOUND;
 737
 738	}
 739		
 740	return FF_ERR_FILE_SOURCE_NOT_FOUND; // Source not found!
 741}
 742
 743
 744/**
 745 *	@public
 746 *	@brief	Get's the next Entry based on the data recorded in the FF_DIRENT object.
 747 *
 748 *	@param	pFile	FF_FILE object that was created by FF_Open().
 749 *
 750 *	@return FF_TRUE if End of File was reached. FF_FALSE if not.
 751 *	@return FF_FALSE if a null pointer was provided.
 752 *
 753 **/
 754FF_T_BOOL FF_isEOF(FF_FILE *pFile) {
 755	if(!pFile) {
 756		return FF_FALSE;
 757	}
 758	if(pFile->FilePointer >= pFile->Filesize) {
 759		return FF_TRUE;
 760	} else {
 761		return FF_FALSE;
 762	}
 763}
 764
 765static FF_T_UINT32 FF_GetSequentialClusters(FF_IOMAN *pIoman, FF_T_UINT32 StartCluster, FF_T_UINT32 Limit, FF_ERROR *pError) {
 766	FF_T_UINT32 CurrentCluster;
 767	FF_T_UINT32 NextCluster = StartCluster;
 768	FF_T_UINT32 i = 0;
 769
 770	*pError = FF_ERR_NONE;
 771
 772	do {
 773		CurrentCluster = NextCluster;
 774		NextCluster = FF_getFatEntry(pIoman, CurrentCluster, pError);
 775		if(*pError) {
 776			return 0;
 777		}
 778		if(NextCluster == (CurrentCluster + 1)) {
 779			i++;
 780		} else {
 781			break;
 782		}
 783
 784		if(Limit) {
 785			if(i == Limit) {
 786				break;
 787			}
 788		}
 789	}while(NextCluster == (CurrentCluster + 1));
 790
 791	return i;
 792}
 793
 794static FF_ERROR FF_ReadClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
 795	FF_T_UINT32 ulSectors;
 796	FF_T_UINT32 SequentialClusters = 0;
 797	FF_T_UINT32 nItemLBA;
 798	FF_T_SINT32 slRetVal;
 799	FF_ERROR	Error;
 800
 801	while(Count != 0) {
 802		if((Count - 1) > 0) {
 803			SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1), &Error);
 804			if(Error) {
 805				return Error;
 806			}
 807		}
 808		ulSectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster;
 809		nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster);
 810		nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA);
 811
 812		slRetVal = FF_BlockRead(pFile->pIoman, nItemLBA, ulSectors, buffer);
 813		if(slRetVal < 0) {
 814			return slRetVal;
 815		}
 816		
 817		Count -= (SequentialClusters + 1);
 818		pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1), &Error);
 819		if(Error) {
 820			return Error;
 821		}
 822		pFile->CurrentCluster += (SequentialClusters + 1);
 823		buffer += ulSectors * pFile->pIoman->BlkSize;
 824		SequentialClusters = 0;
 825	}
 826
 827	return FF_ERR_NONE;
 828}
 829
 830
 831static FF_ERROR FF_ExtendFile(FF_FILE *pFile, FF_T_UINT32 Size) {
 832	FF_IOMAN	*pIoman = pFile->pIoman;
 833	FF_T_UINT32 nBytesPerCluster = pIoman->pPartition->BlkSize * pIoman->pPartition->SectorsPerCluster;
 834	FF_T_UINT32 nTotalClustersNeeded = Size / nBytesPerCluster;
 835	FF_T_UINT32 nClusterToExtend; 
 836	FF_T_UINT32 CurrentCluster, NextCluster;
 837	FF_T_UINT32	i;
 838	FF_DIRENT	OriginalEntry;
 839	FF_ERROR	Error;
 840
 841	if((pFile->Mode & FF_MODE_WRITE) != FF_MODE_WRITE) {
 842		return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
 843	}
 844
 845	if(pFile->Filesize == 0 && pFile->ObjectCluster == 0) {	// No Allocated clusters.
 846		// Create a Cluster chain!
 847		pFile->AddrCurrentCluster = FF_CreateClusterChain(pFile->pIoman, &Error);
 848
 849		if(Error) {
 850			return Error;
 851		}
 852
 853		Error = FF_GetEntry(pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
 854		
 855		if(!Error) {
 856			OriginalEntry.ObjectCluster = pFile->AddrCurrentCluster;
 857			Error = FF_PutEntry(pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
 858		} else {
 859			return Error;
 860		}
 861
 862		if(Error) {
 863			return Error;
 864		}
 865
 866		pFile->ObjectCluster = pFile->AddrCurrentCluster;
 867		pFile->iChainLength = 1;
 868		pFile->CurrentCluster = 0;
 869		pFile->iEndOfChain = pFile->AddrCurrentCluster;
 870	}
 871	
 872	if(Size % nBytesPerCluster) {
 873		nTotalClustersNeeded += 1;
 874	}
 875
 876	if(pFile->iChainLength == 0) {	// First extension requiring the chain length, 
 877		pFile->iChainLength = FF_GetChainLength(pIoman, pFile->ObjectCluster, &pFile->iEndOfChain, &Error);
 878		if(Error) {
 879			return Error;
 880		}
 881	}
 882
 883	nClusterToExtend = (nTotalClustersNeeded - pFile->iChainLength);
 884
 885	if(nTotalClustersNeeded > pFile->iChainLength) {
 886
 887		NextCluster = pFile->AddrCurrentCluster;
 888		FF_lockFAT(pIoman);
 889		{
 890			for(i = 0; i <= nClusterToExtend; i++) {
 891				CurrentCluster = FF_FindEndOfChain(pIoman, NextCluster, &Error);
 892				if(Error) {
 893					FF_unlockFAT(pIoman);
 894					FF_DecreaseFreeClusters(pIoman, i);
 895					return Error;
 896				}
 897				NextCluster = FF_FindFreeCluster(pIoman, &Error);
 898				if(Error) {
 899					FF_unlockFAT(pIoman);
 900					FF_DecreaseFreeClusters(pIoman, i);
 901					return Error;
 902				}
 903				if(!NextCluster) {
 904					FF_unlockFAT(pIoman);
 905					FF_DecreaseFreeClusters(pIoman, i);
 906					return FF_ERR_FAT_NO_FREE_CLUSTERS;
 907				}
 908				
 909				Error = FF_putFatEntry(pIoman, CurrentCluster, NextCluster);
 910				if(Error) {
 911					FF_unlockFAT(pIoman);
 912					FF_DecreaseFreeClusters(pIoman, i);
 913					return Error;
 914				}
 915				Error = FF_putFatEntry(pIoman, NextCluster, 0xFFFFFFFF);
 916				if(Error) {
 917					FF_unlockFAT(pIoman);
 918					FF_DecreaseFreeClusters(pIoman, i);
 919					return Error;
 920				}
 921			}
 922			
 923			pFile->iEndOfChain = FF_FindEndOfChain(pIoman, NextCluster, &Error);
 924			if(Error) {
 925				FF_unlockFAT(pIoman);
 926				FF_DecreaseFreeClusters(pIoman, i);
 927				return Error;
 928			}
 929		}
 930		FF_unlockFAT(pIoman);
 931		
 932		pFile->iChainLength += i;
 933		Error = FF_DecreaseFreeClusters(pIoman, i);	// Keep Tab of Numbers for fast FreeSize()
 934		if(Error) {
 935			return Error;
 936		}
 937	}
 938
 939	return FF_ERR_NONE;
 940}
 941
 942static FF_ERROR FF_WriteClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
 943	FF_T_UINT32 ulSectors;
 944	FF_T_UINT32 SequentialClusters = 0;
 945	FF_T_UINT32 nItemLBA;
 946	FF_T_SINT32 slRetVal;
 947	FF_ERROR	Error;
 948
 949	while(Count != 0) {
 950		if((Count - 1) > 0) {
 951			SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1), &Error);
 952			if(Error) {
 953				return Error;
 954			}
 955		}
 956		ulSectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster;
 957		nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster);
 958		nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA);
 959
 960		slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, ulSectors, buffer);
 961
 962		if(slRetVal < 0) {
 963			return slRetVal;
 964		}
 965		
 966		Count -= (SequentialClusters + 1);
 967		pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1), &Error);
 968		if(Error) {
 969			return Error;
 970		}
 971		pFile->CurrentCluster += (SequentialClusters + 1);
 972		buffer += ulSectors * pFile->pIoman->BlkSize;
 973		SequentialClusters = 0;
 974	}
 975
 976	return 0;
 977}
 978
 979/**
 980 *	@public
 981 *	@brief	Equivalent to fread()
 982 *
 983 *	@param	pFile		FF_FILE object that was created by FF_Open().
 984 *	@param	ElementSize	The size of an element to read.
 985 *	@param	Count		The number of elements to read.
 986 *	@param	buffer		A pointer to a buffer of adequate size to be filled with the requested data.
 987 *
 988 *	@return Number of bytes read.
 989 *
 990 **/
 991FF_T_SINT32 FF_Read(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
 992	FF_T_UINT32 nBytes = ElementSize * Count;
 993	FF_T_UINT32	nBytesRead = 0;
 994	FF_T_UINT32 nBytesToRead;
 995	FF_IOMAN	*pIoman;
 996	FF_BUFFER	*pBuffer;
 997	FF_T_UINT32 nRelBlockPos;
 998	FF_T_UINT32	nItemLBA;
 999	FF_T_SINT32	RetVal = 0;
1000	FF_T_UINT16	sSectors;
1001	FF_T_UINT32 nRelClusterPos;
1002	FF_T_UINT32 nBytesPerCluster;
1003	FF_T_UINT32	nClusterDiff;
1004	FF_ERROR	Error;
1005
1006	if(!pFile) {
1007		return FF_ERR_NULL_POINTER;
1008	}
1009
1010	if(!(pFile->Mode & FF_MODE_READ)) {
1011		return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE;
1012	}
1013
1014	pIoman = pFile->pIoman;
1015
1016	if(pFile->FilePointer == pFile->Filesize) {
1017		return 0;
1018	}
1019
1020	if((pFile->FilePointer + nBytes) > pFile->Filesize) {
1021		nBytes = pFile->Filesize - pFile->FilePointer;
1022	}
1023	
1024	nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1025	if(nClusterDiff) {
1026		if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1027			pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1028			if(Error) {
1029				return Error;
1030			}
1031			pFile->CurrentCluster += nClusterDiff;
1032		}
1033	}
1034
1035	nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block.
1036	
1037	nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1038	nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1039
1040	if((nRelBlockPos + nBytes) < pIoman->BlkSize) {	// Bytes to read are within a block and less than a block size.
1041		pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
1042		{
1043			if(!pBuffer) {
1044				return FF_ERR_DEVICE_DRIVER_FAILED;
1045			}
1046			memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytes);
1047		}
1048		FF_ReleaseBuffer(pIoman, pBuffer);
1049
1050		pFile->FilePointer += nBytes;
1051		
1052		return nBytes;		// Return the number of bytes read.
1053
1054	} else {
1055
1056		//---------- Read (memcpy) to a Sector Boundary
1057		if(nRelBlockPos != 0) {	// Not on a sector boundary, at this point the LBA is known.
1058			nBytesToRead = pIoman->BlkSize - nRelBlockPos;
1059			pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
1060			{
1061				if(!pBuffer) {
1062					return FF_ERR_DEVICE_DRIVER_FAILED;
1063				}
1064				// Here we copy to the sector boudary.
1065				memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytesToRead);
1066			}
1067			FF_ReleaseBuffer(pIoman, pBuffer);
1068
1069			nBytes				-= nBytesToRead;
1070			nBytesRead			+= nBytesToRead;
1071			pFile->FilePointer	+= nBytesToRead;
1072			buffer				+= nBytesToRead;
1073			
1074		}
1075
1076		//---------- Read to a Cluster Boundary
1077		
1078		nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1);
1079		nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize);
1080		if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary
1081			
1082			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1083			if(nClusterDiff) {
1084				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1085					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1086					if(Error) {
1087						return Error;
1088					}
1089					pFile->CurrentCluster += nClusterDiff;
1090				}
1091			}
1092		
1093			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1094			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1095
1096			sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize));
1097			
1098			RetVal = FF_BlockRead(pIoman, nItemLBA, (FF_T_UINT32) sSectors, buffer);
1099			
1100			nBytesToRead		 = sSectors * pIoman->BlkSize;
1101			nBytes				-= nBytesToRead;
1102			buffer				+= nBytesToRead;
1103			nBytesRead			+= nBytesToRead;
1104			pFile->FilePointer	+= nBytesToRead;
1105
1106		}
1107
1108		//---------- Read Clusters
1109		if(nBytes >= nBytesPerCluster) {
1110			//----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
1111			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1112			if(nClusterDiff) {
1113				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1114					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1115					if(Error) {
1116						return Error;
1117					}
1118					pFile->CurrentCluster += nClusterDiff;
1119				}
1120			}
1121			//----- End of Contributor fix.
1122
1123			RetVal = FF_ReadClusters(pFile, (nBytes / nBytesPerCluster), buffer);
1124			if(RetVal < 0) {
1125				return RetVal;
1126			}
1127			nBytesToRead = (nBytesPerCluster *  (nBytes / nBytesPerCluster));
1128
1129			pFile->FilePointer	+= nBytesToRead;
1130
1131			nBytes			-= nBytesToRead;
1132			buffer			+= nBytesToRead;
1133			nBytesRead		+= nBytesToRead;
1134		}
1135
1136		//---------- Read Remaining Blocks
1137		if(nBytes >= pIoman->BlkSize) {
1138			sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize);
1139			
1140			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1141			if(nClusterDiff) {
1142				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1143					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1144					if(Error) {
1145						return Error;
1146					}
1147					pFile->CurrentCluster += nClusterDiff;
1148				}
1149			}
1150			
1151			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1152			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1153
1154			RetVal = FF_BlockRead(pIoman, nItemLBA, (FF_T_UINT32) sSectors, buffer);
1155
1156			if(RetVal < 0) {
1157				return RetVal;
1158			}
1159			
1160			nBytesToRead = sSectors * pIoman->BlkSize;
1161			pFile->FilePointer	+= nBytesToRead;
1162			nBytes				-= nBytesToRead;
1163			buffer				+= nBytesToRead;
1164			nBytesRead			+= nBytesToRead;
1165		}
1166
1167		//---------- Read (memcpy) Remaining Bytes
1168		if(nBytes > 0) {
1169			
1170			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1171			if(nClusterDiff) {
1172				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1173					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1174					if(Error) {
1175						return Error;
1176					}
1177					pFile->CurrentCluster += nClusterDiff;
1178				}
1179			}
1180			
1181			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1182			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1183			pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
1184			{
1185				if(!pBuffer) {
1186					return FF_ERR_DEVICE_DRIVER_FAILED;
1187				}
1188				memcpy(buffer, pBuffer->pBuffer, nBytes);
1189			}
1190			FF_ReleaseBuffer(pIoman, pBuffer);
1191
1192			nBytesToRead = nBytes;
1193			pFile->FilePointer	+= nBytesToRead;
1194			nBytes				-= nBytesToRead;
1195			buffer				+= nBytesToRead;
1196			nBytesRead			+= nBytesToRead;
1197
1198		}
1199	}
1200
1201	return nBytesRead;
1202}
1203
1204
1205
1206
1207/**
1208 *	@public
1209 *	@brief	Equivalent to fgetc()
1210 *
1211 *	@param	pFile		FF_FILE object that was created by FF_Open().
1212 *
1213 *	@return The character that was read (cast as a 32-bit interger). -1 on EOF.
1214 *	@return -2 If a null file pointer was provided.
1215 *	@return -3 Device access failed.
1216 *
1217 **/
1218FF_T_SINT32 FF_GetC(FF_FILE *pFile) {
1219	FF_T_UINT32		fileLBA;
1220	FF_BUFFER		*pBuffer;
1221	FF_T_UINT8		retChar;
1222	FF_T_UINT32		relMinorBlockPos;
1223	FF_T_UINT32     clusterNum;
1224	FF_T_UINT32		nClusterDiff;
1225	FF_ERROR		Error;
1226	
1227	
1228	if(!pFile) {
1229		return FF_ERR_NULL_POINTER;
1230	}
1231
1232	if(!(pFile->Mode & FF_MODE_READ)) {
1233		return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE;
1234	}
1235	
1236	if(pFile->FilePointer >= pFile->Filesize) {
1237		return -1; // EOF!	
1238	}
1239
1240	relMinorBlockPos	= FF_getMinorBlockEntry(pFile->pIoman, pFile->FilePointer, 1);
1241	clusterNum			= FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1242
1243	nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1244	if(nClusterDiff) {
1245		if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1246			pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1247			if(Error) {
1248				return Error;
1249			}
1250			pFile->CurrentCluster += nClusterDiff;
1251		}
1252	}
1253	
1254
1255	fileLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster)	+ FF_getMajorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1256	fileLBA = FF_getRealLBA (pFile->pIoman, fileLBA)		+ FF_getMinorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1257	
1258	pBuffer = FF_GetBuffer(pFile->pIoman, fileLBA, FF_MODE_READ);
1259	{
1260		if(!pBuffer) {
1261			return FF_ERR_DEVICE_DRIVER_FAILED;
1262		}
1263		retChar = pBuffer->pBuffer[relMinorBlockPos];
1264	}
1265	FF_ReleaseBuffer(pFile->pIoman, pBuffer);
1266
1267	pFile->FilePointer += 1;
1268
1269	return (FF_T_INT32) retChar;
1270}
1271
1272
1273/**
1274 * @public
1275 * @brief	Gets a Line from a Text File, but no more than ulLimit charachters. The line will be NULL terminated.
1276 *
1277 *			The behaviour of this function is undefined when called on a binary file.
1278 *			It should just read in ulLimit bytes of binary, and ZERO terminate the line.
1279 *
1280 *			This function works for both UNIX line feeds, and Windows CRLF type files.
1281 *
1282 * @param	pFile	The FF_FILE object pointer.
1283 * @param	szLine	The charachter buffer where the line should be stored.
1284 * @param	ulLimit	This should be the max number of charachters that szLine can hold.
1285 *
1286 * @return	The number of charachters read from the line, on success.
1287 * @return	0 when no more lines are available, or when ulLimit is 0.
1288 * @return	FF_ERR_NULL_POINTER if pFile or szLine are NULL;
1289 *
1290 **/
1291FF_T_SINT32 FF_GetLine(FF_FILE *pFile, FF_T_INT8 *szLine, FF_T_UINT32 ulLimit) {
1292	FF_T_SINT32 c;
1293	FF_T_UINT32 i;
1294
1295	if(!pFile || !szLine) {
1296		return FF_ERR_NULL_POINTER;
1297	}
1298
1299	for(i = 0; i < (ulLimit - 1) && (c=FF_GetC(pFile)) >= 0 && c != '\n'; ++i) {
1300		if(c == '\r') {
1301			i--;
1302		} else {
1303			szLine[i] = (FF_T_INT8) c;
1304		}
1305	}
1306
1307	szLine[i] = '\0';
1308	return i;
1309}
1310
1311FF_T_UINT32 FF_Tell(FF_FILE *pFile) {
1312	return pFile->FilePointer;
1313}
1314
1315
1316/**
1317 *	@public
1318 *	@brief	Writes data to a File.
1319 *
1320 *	@param	pFile			FILE Pointer.
1321 *	@param	ElementSize		Size of an Element of Data to be copied. (in bytes). 
1322 *	@param	Count			Number of Elements of Data to be copied. (ElementSize * Count must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best.
1323 *	@param	buffer			Byte-wise buffer containing the data to be written.
1324 *
1325 *	@return
1326 **/
1327FF_T_SINT32 FF_Write(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
1328	FF_T_UINT32 nBytes = ElementSize * Count;
1329	FF_T_UINT32	nBytesWritten = 0;
1330	FF_T_UINT32 nBytesToWrite;
1331	FF_IOMAN	*pIoman;
1332	FF_BUFFER	*pBuffer;
1333	FF_T_UINT32 nRelBlockPos;
1334	FF_T_UINT32	nItemLBA;
1335	FF_T_SINT32	slRetVal = 0;
1336	FF_T_UINT16	sSectors;
1337	FF_T_UINT32 nRelClusterPos;
1338	FF_T_UINT32 nBytesPerCluster, nClusterDiff, nClusters;
1339	FF_ERROR	Error;
1340
1341	if(!pFile) {
1342		return FF_ERR_NULL_POINTER;
1343	}
1344
1345	if(!(pFile->Mode & FF_MODE_WRITE)) {
1346		return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
1347	}
1348
1349	// Make sure a write is after the append point.
1350	if((pFile->Mode & FF_MODE_APPEND)) {
1351		if(pFile->FilePointer < pFile->Filesize) {
1352			FF_Seek(pFile, 0, FF_SEEK_END);
1353		}
1354	}
1355
1356	pIoman = pFile->pIoman;
1357
1358	nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize);
1359
1360	// Extend File for atleast nBytes!
1361	// Handle file-space allocation
1362	Error = FF_ExtendFile(pFile, pFile->FilePointer + nBytes);
1363
1364	if(Error) {
1365		return Error;	
1366	}
1367
1368	nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block.
1369	
1370	nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1371	if(nClusterDiff) {
1372		if(pFile->CurrentCluster != FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1373			pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1374			if(Error) {
1375				return Error;
1376			}
1377			pFile->CurrentCluster += nClusterDiff;
1378		}
1379	}
1380	
1381	nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1382	nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1383
1384	if((nRelBlockPos + nBytes) < pIoman->BlkSize) {	// Bytes to read are within a block and less than a block size.
1385		pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1386		{
1387			if(!pBuffer) {
1388				return FF_ERR_DEVICE_DRIVER_FAILED;
1389			}
1390			memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytes);
1391		}
1392		FF_ReleaseBuffer(pIoman, pBuffer);
1393
1394		pFile->FilePointer += nBytes;
1395		nBytesWritten = nBytes;
1396		//return nBytes;		// Return the number of bytes read.
1397
1398	} else {
1399
1400		//---------- Write (memcpy) to a Sector Boundary
1401		if(nRelBlockPos != 0) {	// Not on a sector boundary, at this point the LBA is known.
1402			nBytesToWrite = pIoman->BlkSize - nRelBlockPos;
1403			pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1404			{
1405				if(!pBuffer) {
1406					return FF_ERR_DEVICE_DRIVER_FAILED;
1407				}
1408				// Here we copy to the sector boudary.
1409				memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytesToWrite);
1410			}
1411			FF_ReleaseBuffer(pIoman, pBuffer);
1412
1413			nBytes				-= nBytesToWrite;
1414			nBytesWritten		+= nBytesToWrite;
1415			pFile->FilePointer	+= nBytesToWrite;
1416			buffer				+= nBytesToWrite;
1417		}
1418
1419		//---------- Write to a Cluster Boundary
1420		
1421		nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1);
1422		if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary
1423			
1424			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1425			if(nClusterDiff) {
1426				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1427					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1428					if(Error) {
1429						return Error;
1430					}
1431					pFile->CurrentCluster += nClusterDiff;
1432				}
1433			}
1434		
1435			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1436			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1437
1438			sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize));
1439
1440			slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, sSectors, buffer);
1441			if(slRetVal < 0) {
1442				return slRetVal;
1443			}
1444			
1445			nBytesToWrite		 = sSectors * pIoman->BlkSize;
1446			nBytes				-= nBytesToWrite;
1447			buffer				+= nBytesToWrite;
1448			nBytesWritten		+= nBytesToWrite;
1449			pFile->FilePointer	+= nBytesToWrite;
1450
1451		}
1452
1453		//---------- Write Clusters
1454		if(nBytes >= nBytesPerCluster) {
1455			//----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
1456			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1457			if(nClusterDiff) {
1458				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1459					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1460					if(Error) {
1461						return Error;
1462					}
1463					pFile->CurrentCluster += nClusterDiff;
1464				}
1465			}
1466			//----- End of Contributor fix.
1467
1468			nClusters = (nBytes / nBytesPerCluster);
1469			
1470			slRetVal = FF_WriteClusters(pFile, nClusters, buffer);
1471			if(slRetVal < 0) {
1472				return slRetVal;
1473			}
1474			
1475			nBytesToWrite = (nBytesPerCluster *  nClusters);
1476			
1477			pFile->FilePointer	+= nBytesToWrite;
1478
1479			nBytes				-= nBytesToWrite;
1480			buffer				+= nBytesToWrite;
1481			nBytesWritten		+= nBytesToWrite;
1482		}
1483
1484		//---------- Write Remaining Blocks
1485		if(nBytes >= pIoman->BlkSize) {
1486			sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize);
1487			
1488			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1489			if(nClusterDiff) {
1490				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1491					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1492					if(Error) {
1493						return Error;
1494					}
1495					pFile->CurrentCluster += nClusterDiff;
1496				}
1497			}			
1498			
1499			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1500			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1501			
1502			slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, sSectors, buffer);
1503			if(slRetVal < 0) {
1504				return slRetVal;
1505			}
1506			
1507			nBytesToWrite = sSectors * pIoman->BlkSize;
1508			pFile->FilePointer	+= nBytesToWrite;
1509			nBytes				-= nBytesToWrite;
1510			buffer				+= nBytesToWrite;
1511			nBytesWritten		+= nBytesToWrite;
1512		}
1513
1514		//---------- Write (memcpy) Remaining Bytes
1515		if(nBytes > 0) {
1516			
1517			nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1518			if(nClusterDiff) {
1519				if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1520					pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1521					if(Error) {
1522						return Error;
1523					}
1524					pFile->CurrentCluster += nClusterDiff;
1525				}
1526			}
1527			
1528			nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1529			nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1530			pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1531			{
1532				if(!pBuffer) {
1533					return FF_ERR_DEVICE_DRIVER_FAILED;
1534				}
1535				memcpy(pBuffer->pBuffer, buffer, nBytes);
1536			}
1537			FF_ReleaseBuffer(pIoman, pBuffer);
1538
1539			nBytesToWrite = nBytes;
1540			pFile->FilePointer	+= nBytesToWrite;
1541			nBytes				-= nBytesToWrite;
1542			buffer				+= nBytesToWrite;
1543			nBytesWritten			+= nBytesToWrite;
1544
1545		}
1546	}
1547
1548	if(pFile->FilePointer > pFile->Filesize) {
1549		pFile->Filesize = pFile->FilePointer;
1550	}
1551
1552	return nBytesWritten;
1553}
1554
1555
1556/**
1557 *	@public
1558 *	@brief	Writes a char to a FILE.
1559 *
1560 *	@param	pFile		FILE Pointer.
1561 *	@param	pa_cValue	Char to be placed in the file.
1562 *
1563 *	@return	Returns the value written to the file, or a value less than 0.
1564 *
1565 **/
1566FF_T_SINT32 FF_PutC(FF_FILE *pFile, FF_T_UINT8 pa_cValue) {
1567	FF_BUFFER	*pBuffer;
1568	FF_T_UINT32 iItemLBA;
1569	FF_T_UINT32 iRelPos;
1570	FF_T_UINT32 nClusterDiff;
1571	FF_ERROR	Error;
1572	
1573	if(!pFile) {	// Ensure we don't have a Null file pointer on a Public interface.
1574		return FF_ERR_NULL_POINTER;
1575	}
1576
1577	if(!(pFile->Mode & FF_MODE_WRITE)) {
1578		return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
1579	}
1580
1581	// Make sure a write is after the append point.
1582	if((pFile->Mode & FF_MODE_APPEND)) {
1583		if(pFile->FilePointer < pFile->Filesize) {
1584			FF_Seek(pFile, 0, FF_SEEK_END);
1585		}
1586	}
1587
1588	iRelPos = FF_getMinorBlockEntry(pFile->pIoman, pFile->FilePointer, 1);
1589	
1590	// Handle File Space Allocation.
1591	Error = FF_ExtendFile(pFile, pFile->FilePointer + 1);
1592	if(Error) {
1593		return Error;
1594	}
1595	
1596	nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1597	if(nClusterDiff) {
1598		if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1599			pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error);
1600			if(Error) {
1601				return Error;
1602			}
1603			pFile->CurrentCluster += nClusterDiff;
1604		}
1605	}
1606
1607	iItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster) + FF_getMajorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1608	iItemLBA = FF_getRealLBA (pFile->pIoman, iItemLBA)					+ FF_getMinorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1609	
1610	pBuffer = FF_GetBuffer(pFile->pIoman, iItemLBA, FF_MODE_WRITE);
1611	{
1612		if(!pBuffer) {
1613			return FF_ERR_DEVICE_DRIVER_FAILED;
1614		}
1615		FF_putChar(pBuffer->pBuffer, (FF_T_UINT16) iRelPos, pa_cValue);
1616	}
1617	FF_ReleaseBuffer(pFile->pIoman, pBuffer);
1618
1619	pFile->FilePointer += 1;
1620	if(pFile->Filesize < (pFile->FilePointer)) {
1621		pFile->Filesize += 1;
1622	}
1623	return pa_cValue;
1624}
1625
1626
1627
1628/**
1629 *	@public
1630 *	@brief	Equivalent to fseek()
1631 *
1632 *	@param	pFile		FF_FILE object that was created by FF_Open().
1633 *	@param	Offset		An integer (+/-) to seek to, from the specified origin.
1634 *	@param	Origin		Where to seek from. (FF_SEEK_SET seek from start, FF_SEEK_CUR seek from current position, or FF_SEEK_END seek from end of file).
1635 *
1636 *	@return 0 on Sucess, 
1637 *	@return -2 if offset results in an invalid position in the file. 
1638 *	@return FF_ERR_NULL_POINTER if a FF_FILE pointer was not recieved.
1639 *	@return -3 if an invalid origin was provided.
1640 *	
1641 **/
1642FF_ERROR FF_Seek(FF_FILE *pFile, FF_T_SINT32 Offset, FF_T_INT8 Origin) {
1643	
1644	FF_ERROR	Error;
1645
1646	if(!pFile) {
1647		return FF_ERR_NULL_POINTER;
1648	}
1649
1650	Error = FF_FlushCache(pFile->pIoman);
1651	if(Error) {
1652		return Error;
1653	}
1654
1655	switch(Origin) {
1656		case FF_SEEK_SET:
1657			if((FF_T_UINT32) Offset <= pFile->Filesize && Offset >= 0) {
1658				pFile->FilePointer = Offset;
1659				pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1660				pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster, &Error);
1661				if(Error) {
1662					return Error;
1663				}
1664			} else {
1665				return -2;
1666			}
1667			break;
1668
1669		case FF_SEEK_CUR:
1670			if((Offset + pFile->FilePointer) <= pFile->Filesize && (Offset + (FF_T_SINT32) pFile->FilePointer) >= 0) {
1671				pFile->FilePointer = Offset + pFile->FilePointer;
1672				pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1673				pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster, &Error);
1674				if(Error) {
1675					return Error;
1676				}
1677			} else {
1678				return -2;
1679			}
1680			break;
1681	
1682		case FF_SEEK_END:
1683			if((Offset + (FF_T_SINT32) pFile->Filesize) >= 0 && (Offset + pFile->Filesize) <= pFile->Filesize) {
1684				pFile->FilePointer = Offset + pFile->Filesize;
1685				pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1686				pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster, &Error);
1687				if(Error) {
1688					return Error;
1689				}
1690			} else {
1691				return -2;
1692			}
1693			break;
1694
1695		default:
1696			return -3;
1697		
1698	}
1699
1700	return 0;
1701}
1702
1703
1704/**
1705 *	@public
1706 *	@brief	Equivalent to fclose()
1707 *
1708 *	@param	pFile		FF_FILE object that was created by FF_Open().
1709 *
1710 *	@return 0 on sucess.
1711 *	@return -1 if a null pointer was provided.
1712 *
1713 **/
1714FF_ERROR FF_Close(FF_FILE *pFile) {
1715
1716	FF_FILE		*pFileChain;
1717	FF_DIRENT	OriginalEntry;
1718	FF_ERROR	Error;
1719
1720	if(!pFile) {
1721		return FF_ERR_NULL_POINTER;	
1722	}
1723	// UpDate Dirent if File-size has changed?
1724
1725	// Update the Dirent!
1726	Error = FF_GetEntry(pFile->pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
1727	if(Error) {
1728		return Error;
1729	}
1730	
1731	if(!pFile->FileDeleted) {
1732		if(pFile->Filesize != OriginalEntry.Filesize) {
1733			OriginalEntry.Filesize = pFile->Filesize;
1734			Error = FF_PutEntry(pFile->pIoman, pFile-

Large files files are truncated, but you can click here to view the full file