FastFile
Overview
Source DirectX 5 SDK
readme.txt
FastFile
--------
FastFile provides a way to create a very fast way to access large numbers
of files.
You use FFCREATE.EXE to create a single flat file. Just run FFCREATE in
the directory that contains all of the files you wish to access. A file
will be created at the root (RESULT.FF) that is the FastFile. (you can
specify a filename on the command line to override the default of RESULT.FF;
if you do so, make sure that the result file is NOT generated in the
current directory).
Once you have created your FastFile, you can use the FastFile routines to
access it:
BOOL FastFileInit( LPSTR fname, int max_handles ):
Call to initialize access to a FastFile.
fname : name of FastFile
max_handles: maximum number of file handles you want to have open at the
same time.
returns TRUE if succeeds, FALSE otherwise
void FastFileFini( void ):
Call when you are finished accessing your FastFile
HFASTFILE FastFileOpen( LPSTR name ):
Call to open an individual file in a FastFile (read-only access is supported)
name: name if individual file
returns a handle, or NULL if fail
BOOL FastFileClose( HFASTFILE hff ):
Call to close an individual file
hff: handle to an individual file
returns TRUE if succeeded, FALSE otherwise
BOOL FastFileRead( HFASTFILE hff, LPVOID ptr, int size )
Call to read from an individual file
hff: handle to an individual file
ptr: buffer to copy data
size: size of data to copy
returns TRUE if succeeded, FALSE otherwise
BOOL FastFileSeek( HFASTFILE hff, int off, int type )
Call to seek to an offset in an individual file
hff: handle to an individual file
off: offset to seek to
type: seek type: SEEK_SET (from start), SEEK_CUR (from current pos),
or SEEK_END (from end)
returns TRUE if succeeded, FALSE otherwise
long FastFileTell( HFASTFILE hff )
Call to get the current position in an individual file
hff: handle to an individual file
returns current position if succeeded, -1 otherwise
fastfile.c
/*==========================================================================
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: fastfile.c
* Content: Fast file I/O for large numbers of files.
* Uses a single file built using FFCREATE.EXE; this file
* contains a directory + all the files.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*
***************************************************************************/
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <search.h>
#include <stdio.h>
#include "fastfile.h"
#include "ffent.h"
#ifdef DEBUG
#define ODS( a ) OutputDebugString( a )
#else
#define ODS( a )
#endif
#ifdef __WATCOMC__
#define _stricmp stricmp
#endif
typedef struct {
BOOL inuse;
LONG pos;
LONG size;
LPFILEENTRY pfe;
} FILEHANDLE, FAR *LPFILEHANDLE;
static int LockCount;
static HANDLE hFile;
static HANDLE hFileMapping;
static LPFILEENTRY pFE;
static LPBYTE pBase;
static DWORD dwFECnt;
static LPFILEHANDLE lpFH;
static DWORD dwFHCnt;
static long lFileEnd;
/*
* Compare
*
* bsearch comparison routine
*/
int Compare( LPFILEENTRY p1, LPFILEENTRY p2 )
{
return( _stricmp( (p1)->name,(p2)->name ) );
} /* Compare */
/*
* FastFileInit
*
* Initialize for fast file access. The master file and maximum number
* of open "files" are specified.
*/
BOOL FastFileInit( LPSTR fname, int max_handles )
{
HRSRC h;
LockCount = 0;
FastFileFini();
/*
* get a file handle array
*/
lpFH = LocalAlloc( LPTR, max_handles * sizeof( FILEHANDLE ) );
if( lpFH == NULL ) {
return FALSE;
}
dwFHCnt = max_handles;
/*
* try our resourse file first
*/
if (h = FindResource(NULL, fname, RT_RCDATA))
{
pBase = LockResource(LoadResource(NULL, h));
if (pBase == NULL)
{
ODS( "FastFileInit: unable to lock resource\r\n" );
FastFileFini();
return FALSE;
}
ODS( "FastFileInit: opened resource: "); ODS(fname); ODS("\r\n");
}
else
{
/*
* create a memory mapped file for the master file
*/
hFile = CreateFile( fname, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 );
if( hFile == NULL || hFile == (HANDLE)HFILE_ERROR )
{
ODS( "FastFileInit: CreateFile(" ); ODS( fname ); ODS( ")\r\n" );
hFile = NULL;
FastFileFini();
return FALSE;
}
hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
if( hFileMapping == NULL ) {
ODS( "FastFileInit: CreateFileMapping Failed\r\n" );
FastFileFini();
return FALSE;
}
pBase = MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 );
if( pBase == NULL ) {
ODS( "FastFileInit: MapViewOfFile Failed\r\n" );
FastFileFini();
return FALSE;
}
}
/*
* get initial data from the memory mapped file
*/
dwFECnt = *((DWORD *) pBase);
pFE = (LPFILEENTRY) (pBase + 4);
lFileEnd = pFE[dwFECnt-1].offset;
return TRUE;
} /* FastFileInit */
/*
* FastFileFini
*
* Clean up resources
*/
void FastFileFini( void )
{
//
// dont unmap things if we have locks out standing
//
if (LockCount != 0)
return;
if( hFileMapping != NULL && pBase ) {
UnmapViewOfFile( pBase );
}
if( hFileMapping != NULL ) {
CloseHandle( hFileMapping );
hFileMapping = NULL;
}
if( hFile != NULL ) {
CloseHandle( hFile );
hFile = NULL;
}
if( lpFH != NULL ) {
LocalFree( lpFH );
lpFH = NULL;
}
dwFHCnt = 0;
pBase = NULL;
dwFECnt = 0;
pFE = NULL;
} /* FastFileFini */
/*
* FastFileOpen
*
* Search the directory for the file, and return a file handle if found.
*/
HFASTFILE FastFileOpen( LPSTR name )
{
FILEENTRY fe;
LPFILEENTRY pfe;
if( pFE == NULL ) {
ODS( "FastFileOpen: not initialized\r\n" );
return NULL;
}
if( name == NULL || name[0] == 0 ) {
ODS( "FastFileOpen: invalid name\r\n" );
return NULL;
}
strcpy( fe.name, name );
pfe = bsearch( &fe, pFE, dwFECnt, sizeof( FILEENTRY ), (LPVOID) Compare );
if( pfe != NULL ) {
DWORD i;
for( i=0;i<dwFHCnt;i++ ) {
if( !lpFH[i].inuse ) {
lpFH[i].inuse = TRUE;
lpFH[i].pos = pfe->offset;
lpFH[i].size = (pfe+1)->offset - pfe->offset;
lpFH[i].pfe = pfe;
return &lpFH[i];
}
}
ODS( "FastFileOpen: Out of file handles\r\n" );
} else {
ODS( "FastFileOpen: File \"" ); ODS( name ); ODS( "\" not found\r\n" );
}
return NULL;
} /* FastFileOpen */
/*
* FastFileClose
*
* Mark a fast file handle as closed
*/
BOOL FastFileClose( LPFILEHANDLE pfh )
{
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileClose: invalid handle\r\n" );
return FALSE;
}
pfh->inuse = FALSE;
return TRUE;
} /* FastFileClose */
/*
* FastFileLock
*
* return a memory pointer into a fast file
*/
LPVOID FastFileLock( LPFILEHANDLE pfh, int pos, int size )
{
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileLock: invalid handle\r\n" );
return NULL;
}
if( size < 0 ) {
ODS( "FastFileLock: invalid size\r\n" );
return NULL;
}
if( (pos + size) > ((pfh->pfe)+1)->offset ) {
ODS( "FastFileLock: read past end of file\r\n" );
return NULL;
}
LockCount++;
return pBase + pfh->pos + pos;
} /* FastFileLock */
/*
* FastFileUnlock
*
*/
BOOL FastFileUnlock( LPFILEHANDLE pfh, int pos, int size )
{
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileUnlock: invalid handle\r\n" );
return FALSE;
}
if( size < 0 ) {
ODS( "FastFileUnlock: invalid size\r\n" );
return FALSE;
}
if( (pos + size) > ((pfh->pfe)+1)->offset ) {
ODS( "FastFileUnlock: read past end of file\r\n" );
return FALSE;
}
LockCount--;
return TRUE;
} /* FastFileUnlock */
/*
* FastFileRead
*
* read from a fast file (memcpy!)
*/
BOOL FastFileRead( LPFILEHANDLE pfh, LPVOID ptr, int size )
{
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileRead: invalid handle\r\n" );
return FALSE;
}
if( size < 0 ) {
ODS( "FastFileRead: invalid size\r\n" );
return FALSE;
}
if( (pfh->pos + size) > ((pfh->pfe)+1)->offset ) {
ODS( "FastFileRead: read past end of file\r\n" );
return FALSE;
}
memcpy( ptr, pBase + pfh->pos, size );
pfh->pos += size;
return TRUE;
} /* FastFileRead */
/*
* FastFileSeek
*
* Set to a new position in a fast file. Uses standard SEEK_SET, SEEK_CUR,
* SEEK_END definitions.
*/
BOOL FastFileSeek( LPFILEHANDLE pfh, int off, int how )
{
LPFILEENTRY pfe;
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileSeek: invalid handle\r\n" );
return FALSE;
}
pfe = pfh->pfe;
if( how == SEEK_SET ) {
if( off < 0 || off >= pfh->size ) {
ODS( "FastFileSeek: Invalid offset\r\n" );
return FALSE;
}
off += pfe->offset;
} else if( how == SEEK_END ) {
if( off < 0 || off >= pfh->size ) {
ODS( "FastFileSeek: Invalid offset\r\n" );
return FALSE;
}
off = (pfe+1)->offset - off;
} else if( how == SEEK_CUR ) {
off = pfh->pos + off;
if( off < pfe->offset || off >= (pfe+1)->offset ) {
ODS( "FastFileSeek: Invalid offset\r\n" );
return FALSE;
}
} else {
ODS( "FastFileSeek: Invalid 'how' field\r\n" );
return FALSE;
}
pfh->pos = off;
return TRUE;
} /* FastFileSeek */
/*
* FastFileTell
*
* Get the current position in a fast file
*/
long FastFileTell( LPFILEHANDLE pfh )
{
if( pfh == NULL || pfh->inuse != TRUE ) {
ODS( "FastFileTell: invalid handle\r\n" );
return -1;
}
return pfh->pos - pfh->pfe->offset;
} /* FastFileTell */
fastfile.h
/*==========================================================================
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: fastfile.h
* Content: Definitions for fastfile access.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*
***************************************************************************/
typedef LPVOID HFASTFILE;
extern BOOL FastFileInit( LPSTR fname, int max_handles );
extern void FastFileFini( void );
extern HFASTFILE FastFileOpen( LPSTR name );
extern BOOL FastFileClose( HFASTFILE pfe );
extern BOOL FastFileRead( HFASTFILE pfh, LPVOID ptr, int size );
extern BOOL FastFileSeek( HFASTFILE pfe, int off, int how );
extern long FastFileTell( HFASTFILE pfe );
extern LPVOID FastFileLock( HFASTFILE pfe, int off, int len );
extern BOOL FastFileUnlock( HFASTFILE pfe, int off, int len );
ffcreate.c
/*==========================================================================
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: ffcreate.c
* Content: Fast file I/O for large numbers of files.
* Turns all files in a directory into a single file.
* This single file contains a directory + all the files.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*
***************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <io.h>
#include <malloc.h>
#ifdef __WATCOMC__
#define _open open
#define _close close
#define _lseek lseek
#define _read read
#define _write write
#define _stricmp stricmp
#define _S_IREAD S_IREAD
#define _S_IWRITE S_IWRITE
#endif
#include "ffent.h"
#define BLOCK_SIZE 16*1024
/*
* Compare
*
* quicksort comparison routine
*/
int Compare( const LPFILEENTRY p1, const LPFILEENTRY p2 )
{
return( _stricmp( (p1)->name,(p2)->name ) );
}
/*
* main
*/
main( int argc, char *argv[] )
{
HANDLE dir;
WIN32_FIND_DATA fd;
int out;
int in;
unsigned long cnt;
unsigned long tmp;
LPFILEENTRY pfe;
int i;
int bytes;
int outbytes;
char *buff;
char *fname;
char *dename;
long pos;
/*
* get i/o buffer
*/
buff = malloc( BLOCK_SIZE );
if( buff == NULL ) {
printf( "Out of memory!\n" );
exit( 1 );
}
/*
* get fastfile name, open file
*/
if( argc < 2 ) {
fname = "\\result.ff";
} else {
fname = argv[1];
}
printf( "Creating FastFile \"%s\"\n", fname );
out = _open( fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY,
_S_IREAD | _S_IWRITE );
if( out < 0 ) {
printf( "Could not open file \"%s\"", fname );
exit( 1 );
}
/*
* build a header
*/
cnt = 0;
printf( "Pass 1: building header\n" );
dir = FindFirstFile( "*.*", &fd );
if( dir == NULL ) {
printf( "Could not open current directory\n" );
_close( out );
exit( 1 );
}
pfe = NULL;
while( 1 ) {
if( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
cnt++;
pfe = realloc( pfe, (cnt+1) * sizeof( FILEENTRY ) );
memset( &pfe[cnt-1], 0, sizeof( FILEENTRY )*2 );
if( pfe == NULL ) {
printf( "Out of memory!\n" );
_close( out );
exit( 1 );
}
dename = fd.cAlternateFileName;
if( dename[0] == 0 ) {
dename = fd.cFileName;
}
printf( "File %d: %s \r", cnt, dename );
pfe[cnt-1].offset = 0;
strcpy( pfe[cnt-1].name, dename );
}
if( !FindNextFile( dir, &fd ) ) {
break;
}
}
FindClose( dir );
if( cnt == 0 ) {
printf( "No files found!\n" );
exit( 0 );
}
/*
* sort the directory
*/
qsort( pfe, cnt, sizeof( FILEENTRY ), (LPVOID) Compare );
/*
* write the number of directory entries + the directory
*/
tmp = cnt+1;
bytes = _write( out, &tmp, sizeof( tmp ) );
if( bytes != sizeof( tmp ) ) {
printf( "Error writing output file\n" );
_close( out );
exit( 1 );
}
bytes = _write( out, pfe, tmp * sizeof( FILEENTRY ) );
if( bytes != (int) (tmp * sizeof( FILEENTRY )) ) {
printf( "Error writing output file\n" );
_close( out );
exit( 1 );
}
/*
* now read all of the files one by one and add them to the fastfile
*/
printf( "Pass 2: adding data files \n" );
for( i=0;i<(int)cnt;i++ ) {
/*
* save current file position
*/
pfe[i].offset = _lseek( out, 0, SEEK_CUR );
if( pfe[i].offset < 0 ) {
printf( "\nSeek error on output file\n" );
_close( out );
exit( 1 );
}
/*
* open next file to add
*/
in = _open( pfe[i].name, O_RDONLY | O_BINARY, 0 );
printf( "File %d: \"%s\", offset=%ld \r",
i+1, pfe[i].name, pfe[i].offset );
if( in < 0 ) {
printf( "\nError opening file %s\n", pfe[i].name );
_close( out );
exit( 1 );
}
/*
* copy the data in the file
*/
while( 1 ) {
bytes = _read( in, buff, BLOCK_SIZE );
if( bytes == 0 ) {
break;
}
if( bytes < 0 ) {
printf( "\nError reading file %s\n", pfe[i].name );
_close( in );
_close( out );
exit( 1 );
}
outbytes = _write( out, buff, bytes );
if( bytes != outbytes ) {
printf( "\nError writing output file\n" );
_close( in );
_close( out );
exit( 1 );
}
if( bytes < BLOCK_SIZE ) {
break;
}
}
_close( in );
}
/*
* get position of file end
*/
pfe[i].offset = _lseek( out, 0, SEEK_CUR );
/*
* seek to the start of the directory (right after the # of entries)
*/
pos = _lseek( out, sizeof( tmp ), SEEK_SET );
if( pos != sizeof( tmp ) ) {
printf( "Seek error on output file\n" );
_close( out );
exit( 1 );
}
/*
* re-write the directory with the offsets setup
*/
bytes = _write( out, pfe, tmp * sizeof( FILEENTRY ) );
if( bytes != (int) (tmp * sizeof( FILEENTRY )) ) {
printf( "Error writing output file\n" );
_close( out );
exit( 1 );
}
/*
* all done
*/
printf( "FastFile \"%s\" created: \n", fname );
printf( " %ld files\n", tmp );
printf( " %ld total file size\n", pfe[i].offset );
_close( out );
return 0;
} /* main */
ffcreate.def
NAME FFCREATE
DESCRIPTION 'FastFile create program (C) Microsoft 1995'
SECTIONS
.bss READ WRITE
.data READ WRITE
.idata READ WRITE
.rdata READ WRITE
ffent.h
/*==========================================================================
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: ffent.h
* Content: FastFile entry definition (internal)
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*
***************************************************************************/
#pragma pack(1)
typedef struct {
long offset;
char name[13];
} FILEENTRY, *LPFILEENTRY;
#pragma pack()
makefile
!include "..\win32.mak"
!if "$(nodebug)" == "1"
OBJ_DIR = Retail
!else
OBJ_DIR = Debug
!endif
all: mkdir $(OBJ_DIR)\fastfile.lib $(OBJ_DIR)\ffcreate.exe
$(OBJ_DIR)\fastfile.obj: fastfile.c fastfile.h
$(cc) $(cdebug) $(cflags) -Fo$(OBJ_DIR)\fastfile.obj $(cvars) fastfile.c
$(OBJ_DIR)\fastfile.lib: $(OBJ_DIR)\fastfile.obj
$(implib) $(OBJ_DIR)\fastfile.Obj -name:fastfile.Lib \
-out:$(OBJ_DIR)\fastfile.lib
$(OBJ_DIR)\ffcreate.obj: ffcreate.c ffent.h
$(cc) $(cdebug) $(cflags) -Fo$(OBJ_DIR)\ffcreate.obj $(cvars) ffcreate.c
$(OBJ_DIR)\ffcreate.exe: $(OBJ_DIR)\ffcreate.obj ffcreate.def
$(link) $(linkdebug) $(conflags) -out:$(OBJ_DIR)\ffcreate.exe \
$(OBJ_DIR)\ffcreate.obj \
$(conlibs)
mkdir:
if not exist $(OBJ_DIR)\NUL md $(OBJ_DIR)
# Rules for cleaning out those old files
clean:
-echo y | del Retail
-echo y | del Debug
-rd Retail
-rd Debug
JS Parser
Generated from Fastfile.ksy.