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.

Edit this page on GitHub Updated at Tue, Aug 1, 2023