📄 resmgr.c
字号:
/* ----------------------------------------------------------------------
Resource Manager
Version 2.05 Released 03/26/97
Written by Kevin Ray (x301) (c) 1996 Spectrum Holobyte
----------------------------------------------------------------------
The resource manager's main function is to provide a single entry
point for file i/o from within an application. Besides this
obvious requirement, this implementation also provides these
added functional blocks:
1) file extraction from archive (zip) files.
2) virtual file system that allows easy patching of data.
3) caching beyond the minimal i/o buffers & mscdex.
4) asynchronous read/writes.
5) event logging (for general debugging as well as to
provide a table from which to sort the contents of
a CD to minimize seeks).
6) Significant debugging options.
For the most part, all of the exported functions are syntactically
similiar to the low-level i/o functions (io.h). Obvious exceptions
to this rule are the functions for reading the contents of a
directory. Since the standard dos functions _findfirst, _findnext,
_findclose have always been cumbersome and 'unnatural', this
implementation uses the metaphor used by the UNIX opendir
command (Roger Fujii's suggestion). For more information, look
at the comment above the ResOpenDirectory function.
There are several compiler options that can be selected to build
various flavors of the Resource Manager. Debugging, obviously,
can be included or excluded. Within the general debugging
category, Event Logging, Parameter Checking and use of a debug
window can all be included or excluded. Each of these latter
options requires that the RES_DEBUG_VERSION be defined and
non-zero (just use 'YES' in the header file).
Since the bafoons at Microsoft have deemed multithreading to be
such an awesome feature (like they invented it - it's not like
there aren't multithreaded operating systems on even lowly 8-bit
microcontrollers) this feature can be disabled. Obviously this
may seem somewhat rediculous considering that asynch i/o is the
main feature you want threads for, but MS Visual C++ shows some
terribly worrisome behaviors with threads. For instance, the
compiler doesn't recognize _beginthread() even with <process.h>
included unless you have the link option /MT selected. Why does
the compiler care? And there is no kill() so callbacks being
instantiated from a thread are problematic since the callbacks
context is always that of the thread. Therefore, if you want
to hide this functionality from the others using ResMgr, you
can turn it off.
In summary, here are the different buid options:
debugging on or off
multithreading on or off
standalone version capability
flat or hierarchical database (explained below)
event logging
parameter checking
To use this library, you need to do just a few things. Here is
a synopsis of the functions you'll need to call and the order
in which to call them:
1) ResInit initialize the resource manager
2) ResCreatePath give the resource manager somewhere to
look for files
3) ResAttach (optional) open an archive file
4) ResOpenFile,
ResReadFile, perform file i/o
ResCloseFile
5) ResExit shut-down the resource manager
History:
03/18/96 KBR Created/Thieved (unzip/inflate from Fujii via gcc).
04/01/96 KBR Integrated unzip/inflate under virtual filesystem.
TBD: CD statistic caching (head movements, warning tracks, etc).
---------------------------------------------------------------------- */
#include "lists.h" /* list manipulation functions (+list.cpp) */
#include "resmgr.h" /* exported prototypes & type definitions */
//#include "memmgr.h"
#include "omni.h"
#include <stdio.h> /* low-level file i/o (+io.h) */
#include <string.h>
#include <memory.h>
#include <sys/stat.h> /* _S_IWRITE */
#include <stdarg.h>
#include <assert.h>
#if USE_WINDOWS
# include <io.h>
# include <direct.h>
# include <process.h> /* _beginthread() MUST SET C++ OPTIONS UNDER
MSVC SETTINGS */
# include <windows.h> /* all this for MessageBox (may move to debug.cpp)*/
#endif /* USE_WINDOWS */
#include "unzip.h"
#ifdef USE_SH_POOLS
#undef MemFree
#undef MemFreePtr
#undef MemMalloc
#include "Smartheap\Include\smrtheap.h"
MEM_POOL gResmgrMemPool = NULL;
#endif
#define SHOULD_I_CALL(idx,retval) if( RES_CALLBACK[(idx)] )\
retval = ( *(RES_CALLBACK[(idx)]));
#define SHOULD_I_CALL_WITH(idx,param,retval) if( RES_CALLBACK[(idx)] )\
retval = ((*(RES_CALLBACK[(idx)]))(param));
#if( RES_STREAMING_IO )
RES_EXPORT FILE * __cdecl _getstream( void );
RES_EXPORT FILE * __cdecl _openfile( const char *, const char *, int, FILE * );
RES_EXPORT void __cdecl _getbuf( FILE * );
RES_EXPORT int __cdecl _flush( FILE * );
RES_EXPORT long __cdecl ftell( FILE * );
RES_EXPORT long __cdecl _ftell_lk( FILE * );
extern void __cdecl _getbuf( FILE * );
extern int __cdecl _flush( FILE * str );
#define EINVAL 22
#define _IOYOURBUF 0x0100
#define _INTERNAL_BUFSIZ 4096
#define _IOARCHIVE 0x00010000
#define _IOLOOSE 0x00020000
#define _SMALL_BUFSIZ 512 /* from stdio.h */
#define _INTERNAL_BUFSIZ 4096 /* from stdio.h */
#define _IOSETVBUF 0x0400 /* from file2.h */
#define _SH_DENYNO 0x40 /* from share.h */
#define anybuf(s) ((s)->_flag & (_IOMYBUF|_IONBF|_IOYOURBUF))
#define inuse(s) ((s)->_flag & (_IOREAD|_IOWRT|_IORW))
#define SHOULD_I_CALL(idx,retval) if( RES_CALLBACK[(idx)] )\
retval = ( *(RES_CALLBACK[(idx)]));
#define SHOULD_I_CALL_WITH(idx,param,retval) if( RES_CALLBACK[(idx)] )\
retval = ((*(RES_CALLBACK[(idx)]))(param));
#endif /* RES_STREAMING_IO */
#define RES_INIT_DIRECTORY_SIZE 8 /* initial size for buffer used in ResCountDirectory */
/* realloc'ed as needed */
/* ----------------------------------------------------------------------
FLAT MODEL VS. HIERARCHICAL MODEL
----------------------------------------------------------------------
There are two different implementation models that can be chosen from.
The first model is one in which all files are peers of one another.
Regardless of where the file resides, the flat model treats all files
as if they were to be found in a single directory. If there happens
to be two files with the same name, but in two different directories,
the file which resides in the directory which was LAST processed is the
file that is entered into the database (in effect, it overrides the
existence of the previous file).
The second implementation model is hierarchical. In this model
directory paths are used to differentiate between identically named
files. This is the only model which supports wildcarding within a
directory (\objects\*.3ds). I'm not sure how usefull this model is, but
considering what a pain it was to implement, I hope it somehow is. Since
one of the most important uses of the Resource Manager is to allow
projects to be 'patched' by simply copying newer data to a higher
priority directory, I exposed an 'override' function so that this
functionality is still present in the hierarchical model. In essence, if
there is a file c:\game\foo.dat and another file c:\object\foo.dat, if
I override with a file called c:\game\patches\foo.dat I have replaced both
\game\foo.dat AND \object\foo.dat with the file found in c:\game\patches --
obviously this requires that more attention be paid to reducing these
complexities and avoiding possible pitfalls.
Flat model: Single hash table
All filenames are peers
Last file entered takes precedence
'Override' is automatic
Hierarchical model: Hash table for each directory
Supports multiple instances of a like-named file
'Override' files manually by calling override
In the flat model, files within a compressed archive are globbed right
along within the same table as all other files. The hierarchical model
will interpret any directory information within the archive file if it
exists, if not all files will go into their own hash table (as if all the
files within the archive were in the same directory).
The flat model is the default.
---------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
U T I L I T Y M A C R O S
---------------------------------------------------------------------- */
#ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#define RES_STRING_SET( dst, src, ptr ); { strcpy( ptr, src ); \
dst = ptr; \
ptr += strlen(src)+1; }
#define NOTHING -1
#define FLAG_TEST(a,b) ( a & b )
#define FLAG_SET(a,b) ( a |= b )
#define FLAG_UNSET(a,b) ( a ^= ~b )
#define HI_WORD(a) ((a)>>16)
#define LO_WORD(a) ((a)&0x0ffff)
#define SET_HIWORD(a,b) { a |= ((b)<<16); }
#define SET_LOWORD(a,b) { a |= (b)&0x0ffff; }
#define WRITTEN_TO_FLAG -1
#ifdef _MT
extern void __cdecl _unlock_file( FILE * );
extern void __cdecl _lock_file( FILE * );
extern void __cdecl _lock_fhandle( int );
extern void __cdecl _unlock_fhandle( int );
# define LOCK_STREAM(a) { _lock_file(a); LOG( "+ %d\n", __LINE__ ); }
# define UNLOCK_STREAM(a) { _unlock_file(a); LOG( "- %d\n", __LINE__ ); }
#else
# define LOCK_STREAM(a)
# define UNLOCK_STREAM(a)
#endif
enum /* compression methods */
{
METHOD_STORED = 0, /* just stored in archive (no compression) */
METHOD_DEFLATED, /* pkzip style 'deflation' */
METHOD_LOOSE /* loose file on harddrive */
};
#define EMPTY(a) ((a) == NOTHING)
/* ----------------------------------------------------------------------
T Y P E D E F I N I T I O N S
----------------------------------------------------------------------- */
typedef struct ASYNCH_DATA /* created when spawning a read/write thread */
{
int file; /* handle of file performing i/o on */
void * buffer; /* ptr to buffer used to read to/write from */
size_t size; /* bytes to read -or- bytes to write */
PFV callback; /* callback to issue upon completion */
} ASYNCH_DATA;
/* ----------------------------------------------------------------------
D E B U G S P E C I F I C I T E M S
---------------------------------------------------------------------- */
#if( RES_DEBUG_VERSION )
# include "errno.h"
/* ---- DEBUG FUNCTIONS ---- */
void
dbg_analyze_hash( HASH_TABLE * hsh ),
dbg_print( HASH_ENTRY * entry ),
dbg_dir( HASH_TABLE * hsh ),
dbg_device( DEVICE_ENTRY * dev );
STRING RES_ERR_OR_MSGS[] =
{
"Not enough memory", /* Debug - Verbose Error Messages */
"Incorrect parameter",
"Path not found",
"File sharing (network error)",
"There is no matching cd number",
"File already closed",
"File not found",
"Can't attrib directory",
"Can't attrib file",
"Directory already exists",
"Not a directory",
"Could not delete",
"Could not change directory",
"Must create search path",
"Could not spawn thread",
"Could not open file",
"Problem reading file",
"Problem writing file",
"System path has not been created yet",
"Cannot interpret file/path",
"Unknown archive",
"Too many open files",
"Illegal file handle",
"Cannot delete file",
"Cannot open archive file",
"Corrupt archive file",
"Unknown error",
"Directory already in search path",
"Destination directory not known (must add first)",
"Cannot write to an archive",
"Unsupported compression type",
"Too many directories"
};
int
RES_ERR_COUNT = sizeof( RES_ERR_OR_MSGS ) / sizeof( RES_ERR_OR_MSGS[0] );
void
_say_error( int error, const char * msg, int line, const char * filename );
# define SAY_ERROR(a,b) _say_error((a),(b), __LINE__, __FILE__ )
int RES_DEBUG_FLAG = TRUE; /* run-time toggle for debugging */
int RES_DEBUG_LOGGING = FALSE; /* are we currently logging events? */
int RES_DEBUG_OPEN_LOG = FALSE; /* is a log file open? */
int RES_DEBUG_FILE = -1; /* file handle for logging events */
#if( RES_DEBUG_LOG )
# define IF_LOG(a) a
# define LOG ResDbgPrintf
#else
# define IF_LOG(a)
# define LOG
#endif
# define IF_DEBUG(a) a
#else /* RES_DEBUG_VERSION ? */
# define IF_DEBUG(a)
# define IF_LOG(a)
# define LOG
# define SAY_ERROR(a,b) {RES_DEBUG_ERRNO=(a);}
#endif
#if( RES_DEBUG_VERSION == 0 )
# if( RES_DEBUG_PARAMS )
# error RES_DEBUG_VERSION must be TRUE to use RES_DEBUG_PARAMS
# endif
#endif
#define CREATE_LOCK(a) CreateMutex( NULL, FALSE, a );
#define REQUEST_LOCK(a) WaitForSingleObject(a, INFINITE);
#define RELEASE_LOCK(a) ReleaseMutex(a);
#define DESTROY_LOCK(a) CloseHandle(a);
/* ----------------------------------------------------------------------
S T A T I C D A T A
---------------------------------------------------------------------- */
#if (RES_MULTITHREAD)
static HANDLE GLOCK=0;
#endif
HASH_TABLE /* For a flat model, this is the only hash table, */
* GLOBAL_HASH_TABLE = NULL; /* for a hierarchical model, this is the hashed */
/* version of a root directory */
FILE_ENTRY
* FILE_HANDLES = NULL; /* Slots for open file handles */
PFI
RES_CALLBACK[ NUMBER_OF_CALLBACKS ];
PRIVATE
LIST
* ARCHIVE_LIST = NULL;
PRIVATE
LIST
* OPEN_DIR_LIST = NULL;
char
* RES_PATH[ RES_DIR_LAST ],
* GLOBAL_SEARCH_PATH[ MAX_DIRECTORIES ];
char
GLOBAL_INIT_PATH[ _MAX_PATH ],
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -