aboutsummaryrefslogtreecommitdiff
path: root/src/libparsec/sys_file.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
commitc068f22329d5cc722622a2183bbb22eef2093df7 (patch)
tree12d56c1aede67988a55e241364606bfbb4dba933 /src/libparsec/sys_file.cpp
downloadopenparsec-main.tar.xz
openparsec-main.zip
initial importHEADmain
Diffstat (limited to 'src/libparsec/sys_file.cpp')
-rw-r--r--src/libparsec/sys_file.cpp892
1 files changed, 892 insertions, 0 deletions
diff --git a/src/libparsec/sys_file.cpp b/src/libparsec/sys_file.cpp
new file mode 100644
index 0000000..65f0541
--- /dev/null
+++ b/src/libparsec/sys_file.cpp
@@ -0,0 +1,892 @@
+/*
+ * PARSEC - File System Code
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $
+ *
+ * Orginally written by:
+ * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// C library
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// compilation flags/debug support
+#include "config.h"
+#include "debug.h"
+
+// general definitions
+#include "general.h"
+#include "gd_heads.h"
+#include "objstruc.h"
+
+// global externals
+#include "globals.h"
+
+// i/o system calls
+#include "sys_io.h"
+
+// local module header
+#include "sys_file.h"
+
+// proprietary module headers
+#ifdef PARSEC_SERVER
+ #include "con_aux_sv.h"
+ #include "con_ext_sv.h"
+ #include "con_main_sv.h"
+#else // !PARSEC_SERVER
+ #include "con_aux.h"
+ #include "con_ext.h"
+ #include "con_main.h"
+ #include "e_demo.h"
+#endif // !PARSEC_SERVER
+
+
+#include "sys_path.h"
+#include "sys_swap.h"
+
+
+// generic string paste area --------------------------------------------------
+//
+#define PASTE_STR_LEN 255
+static char paste_str[ PASTE_STR_LEN + 1 ];
+
+
+// default maximum number of data packages ------------------------------------
+//
+#define DEFAULT_MAX_PACKAGES 32
+
+
+// current and maximum number of data packages --------------------------------
+//
+PUBLIC int num_data_packages = 0;
+static int max_data_packages = DEFAULT_MAX_PACKAGES;
+
+
+// data package info tables ---------------------------------------------------
+//
+PUBLIC char* package_filename[ DEFAULT_MAX_PACKAGES ];
+static size_t package_filebase[ DEFAULT_MAX_PACKAGES ];
+static packageheader_s package_header[ DEFAULT_MAX_PACKAGES ];
+static pfileinfo_s* package_items[ DEFAULT_MAX_PACKAGES ];
+static size_t package_headersize[ DEFAULT_MAX_PACKAGES ];
+static size_t package_numitems[ DEFAULT_MAX_PACKAGES ];
+
+
+// register a data package ----------------------------------------------------
+//
+int SYS_RegisterPackage( const char *packname, size_t baseofs, char *prefix )
+{
+ ASSERT( packname != NULL );
+// ASSERT( prefix != NULL );
+
+ // guard against overflow
+ if ( num_data_packages >= max_data_packages ) {
+ return FALSE;
+ }
+
+ int packid = num_data_packages;
+
+ // may be necessary if error occurred earlier on
+ if ( package_filename[ packid ] != NULL ) {
+ FREEMEM( package_filename[ packid ] );
+ package_filename[ packid ] = NULL;
+ }
+
+ // must be done here since it is not required of the caller to convert the
+ // supplied path prior to calling this function (to ease mod registration)
+ char *path = SYSs_ProcessPathString( (char*)packname );
+
+ // alloc filename
+ package_filename[ packid ] = (char *) ALLOCMEM( strlen( path ) + 1 );
+ if ( package_filename[ packid ] == NULL ) {
+ return FALSE;
+ }
+
+ // store base info
+ strcpy( package_filename[ packid ], path );
+ package_filebase[ packid ] = baseofs;
+
+ // open for reading at specified offset
+ FILE *fp = fopen( package_filename[ packid ], "rb" );
+ if ( fp == NULL ) {
+ return FALSE;
+ }
+ if ( fseek( fp, package_filebase[ packid ], SEEK_SET ) != 0 ) {
+ fclose( fp );
+ return FALSE;
+ }
+
+ // read fixed-size part of header
+ size_t bytesread = fread( &package_header[ packid ], 1, sizeof( packageheader_s ), fp );
+ if ( bytesread != sizeof( packageheader_s ) ) {
+ fclose( fp );
+ return FALSE;
+ }
+
+ // swap endianness of fixed-size part of header
+ SYS_SwapPackageHeader( &package_header[ packid ] );
+
+ // fetch size of remaining header and number of contained items
+ package_headersize[ packid ] = package_header[ packid ].headersize;
+ package_numitems[ packid ] = package_header[ packid ].numitems;
+
+ // may be necessary if error occurred earlier on
+ if ( package_items[ packid ] != NULL ) {
+ FREEMEM( package_items[ packid ] );
+ package_items[ packid ] = NULL;
+ }
+
+ // read file list
+ pfileinfodisk_s *diskitems = (pfileinfodisk_s *) ALLOCMEM( package_headersize[ packid ] );
+ if ( diskitems == NULL ) {
+ fclose( fp );
+ return FALSE;
+ }
+ bytesread = fread( diskitems, 1, package_headersize[ packid ], fp );
+ if ( bytesread != package_headersize[ packid ] ) {
+ fclose( fp );
+ return FALSE;
+ }
+
+ // swap endianness of file info list
+ pfileinfodisk_s *items = diskitems;
+ unsigned int curit = 0;
+ for ( curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+ SYS_SwapPFileInfo( &items[ curit ] );
+ }
+
+ fclose( fp );
+ num_data_packages++;
+
+ // copy file info list for use in memory
+ package_items[ packid ] = (pfileinfo_s *) ALLOCMEM( package_numitems[ packid ] * sizeof( pfileinfo_s ) );
+ if ( package_items[ packid ] == NULL ) {
+ return FALSE;
+ }
+
+ char *prefixpath = NULL;
+ if ( prefix != NULL ) {
+
+ // must be done to ensure files containing path delimiters
+ // can be found later on when comparing with paths that have
+ // also been processed to match the host system conventions
+ strncpy( paste_str, prefix, PASTE_STR_LEN - 1 );
+ paste_str[ PASTE_STR_LEN - 1 ] = 0;
+ strcat( paste_str, "/" );
+ prefixpath = SYSs_ProcessPathString( paste_str );
+ }
+
+ int sizenames = 0;
+ size_t prefixlen = ( prefixpath != NULL ) ? ( strlen( prefixpath ) + 1 ) : 0;
+
+ // count total storage amount for filenames
+ for ( curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+ sizenames += prefixlen + strlen( diskitems[ curit ].file ) + 1;
+ }
+
+ // allocate filename storage
+ char *namelist = (char *) ALLOCMEM( sizenames );
+ if ( namelist == NULL ) {
+ return FALSE;
+ }
+
+ // copy filenames into storage and pfileinfodisk_s into pfileinfo_s
+ for ( curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+
+ if ( prefix == NULL || prefixpath == NULL ) {
+ strcpy( namelist, diskitems[ curit ].file );
+ } else {
+ strcpy( namelist, prefixpath );
+ strcat( namelist, diskitems[ curit ].file );
+ }
+
+ pfileinfodisk_s * diskitem = &diskitems[ curit ];
+
+ package_items[ packid ][ curit ].file = namelist;
+ package_items[ packid ][ curit ].foffset = diskitem->foffset;
+ package_items[ packid ][ curit ].flength = diskitem->flength;
+ package_items[ packid ][ curit ].fp = (FILE *) diskitem->fp;
+ package_items[ packid ][ curit ].fcurpos = diskitem->fcurpos;
+
+ namelist += prefixlen + strlen( diskitems[ curit ].file ) + 1;
+ }
+
+ FREEMEM( diskitems );
+
+ return TRUE;
+}
+
+
+// override all files in old package with newer versions in new package -------
+//
+int SYS_OverridePackage( const char* oldpackname, const char* newpackname )
+{
+ //NOTE:
+ // packages must have already been registered
+
+ int oldid = -1;
+ int newid = -1;
+
+ // find old package
+ for ( int packid = 0; packid < num_data_packages; packid++ ) {
+
+ if ( stricmp( package_filename[ packid ], oldpackname ) == 0 ) {
+
+ oldid = packid;
+
+ } else if ( stricmp( package_filename[ packid ], newpackname ) == 0 ) {
+
+ newid = packid;
+ }
+ }
+
+ // packages must exist and be different
+ ASSERT( oldid != -1 );
+ ASSERT( newid != -1 );
+ ASSERT( newid != oldid );
+
+ if ( oldid == -1 ||
+ newid == -1 ||
+ oldid == newid ) {
+ return FALSE;
+ }
+
+ // replace all items found in both packages with a dummy name in old package
+
+ pfileinfo_s *newitems = package_items[ newid ];
+
+ // scan all items of new package
+ for ( unsigned int curit = 0; curit < package_numitems[ newid ]; curit++ ) {
+
+ // scan all items of old package
+ pfileinfo_s *olditems = package_items[ oldid ];
+
+ for ( unsigned int curoldit = 0; curoldit < package_numitems[ oldid ]; curoldit++ ) {
+
+ if ( stricmp( newitems[ curit ].file, olditems[ curoldit ].file ) == 0 ) {
+
+ strcpy( olditems[ curoldit ].file, "dummy.old" );
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+// acquire scripts contained in package files ---------------------------------
+//
+int SYS_AcquirePackageScripts( int comtype )
+{
+ // scan all registered packages
+ for ( int packid = 0; packid < num_data_packages; packid++ ) {
+
+ // scan all items of this package
+ for ( unsigned int curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+
+ // guard against overflow of maximum number of scripts
+ if ( num_external_commands >= MAX_EXTERNAL_COMMANDS )
+ break;
+
+ int scriptid = num_external_commands;
+ pfileinfo_s *item = &package_items[ packid ][ curit ];
+
+ // extension is mandatory
+ long len = strlen( item->file ) - 4;
+
+// ASSERT( ( len >= 0 ) && ( len <= COMMAND_NAME_ALLOC_LEN ) );
+ if ( len < 0 )
+ continue;
+
+ // check script extension
+ if ( strcmp( item->file + len, CON_FILE_EXTENSION ) != 0 )
+ continue;
+
+ // store base name
+ strncpy( external_commands[ scriptid ], item->file, len );
+ external_commands[ scriptid ][ len ] = 0;
+
+ // set type and convert to lower-case
+ external_command_types[ scriptid ] = comtype;
+ strlwr( external_commands[ scriptid ] );
+
+ num_external_commands++;
+ }
+ }
+
+ return TRUE;
+}
+
+
+// acquire demos contained in package files -----------------------------------
+//
+int SYS_AcquirePackageDemos()
+{
+#ifdef PARSEC_CLIENT
+ // scan all registered packages
+ for ( unsigned int packid = 0; packid < (unsigned int)num_data_packages; packid++ ) {
+
+ // scan all items of this package
+ for ( unsigned int curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+
+ // guard against overflow of registered demos
+ if ( num_registered_demos >= max_registered_demos )
+ break;
+
+ int demoid = num_registered_demos;
+ pfileinfo_s *item = &package_items[ packid ][ curit ];
+
+ // extension is mandatory
+ long len = strlen( item->file ) - 4;
+// ASSERT( ( len >= 0 ) && ( len <= COMMAND_NAME_ALLOC_LEN ) );
+ if ( len < 0 )
+ continue;
+
+ // check demo extension
+ if ( strcmp( item->file + len, CON_FILE_COMPILED_EXTENSION ) != 0 )
+ continue;
+
+ // store demo name
+ if ( registered_demo_names[ demoid ] != NULL ) {
+ FREEMEM( registered_demo_names[ demoid ] );
+ registered_demo_names[ demoid ] = NULL;
+ }
+ registered_demo_names[ demoid ] = (char *) ALLOCMEM( len + 1 );
+ if ( registered_demo_names[ demoid ] == NULL )
+ continue;
+ strncpy( registered_demo_names[ demoid ], item->file, len );
+ registered_demo_names[ demoid ][ len ] = 0;
+
+ // convert to lower-case
+ strlwr( registered_demo_names[ demoid ] );
+
+ num_registered_demos++;
+ }
+ }
+#endif // PARSEC_CLIENT
+ return TRUE;
+}
+
+
+// determine file length of real system file ----------------------------------
+//
+PRIVATE
+long int SYS_SysGetFileLength( const char *filename )
+{
+ ASSERT( filename != NULL );
+
+ FILE *fp = fopen( filename, "rb" );
+ if ( fp == NULL )
+ return -1L;
+
+ if ( fseek( fp, 0, SEEK_END ) != 0 )
+ return -1L;
+
+ long int siz = ftell( fp );
+
+ fclose( fp );
+ return siz;
+}
+
+// check the internal version agains the pscdata*.dat version
+//
+int SYS_CheckDataVersion(){
+ // attempt to open the version file
+ FILE *ver_file = SYS_fopen("psdatver.txt", "rb");
+ if(ver_file == NULL) {
+ MSGOUT("Unable to open psdatver.txt. Maybe you need to update your pscdata*.dat files?");
+ return false;
+ }
+
+ char verstr[4];
+ // attempt to read the file into the verstr buffer
+ int read_rc = SYS_fread((void *)verstr, sizeof(char), 3, ver_file);
+ verstr[3] = '\0';
+ if (read_rc <= 0) {
+ MSGOUT("Unable to read psdatver.txt. Maybe you need to update your pscdata*.dat files?");
+ return false;
+ }
+
+ // now let's try to convert the string to a integer so we can check the version
+ //int data_version = 0;
+ //data_version = (int)strtol(verstr, NULL, 10);
+ MSGOUT("pscdata version %s, internal version %s", verstr, PSCDATA_VERSION);
+ if(strcmp(verstr, PSCDATA_VERSION)) {
+ MSGOUT("psdatver.txt version mismatch: Internal version %s does not match data file version %s.", verstr, PSCDATA_VERSION);
+ return false;
+ }
+
+ SYS_fclose(ver_file);
+
+ return true;
+}
+
+
+// fetch item return structure for the following two routines -----------------
+//
+struct fetchitem_s {
+
+ int packid;
+ pfileinfo_s* item;
+};
+
+#define FETCH_CACHE_MASK 0x03
+#define FETCH_CACHE_SIZE ( FETCH_CACHE_MASK + 1 )
+
+static int _fetch_cache_pos = 0;
+static fetchitem_s _fetch_item;
+static fetchitem_s _fetch_cache[ FETCH_CACHE_SIZE ];
+
+
+// fetch item by file name ----------------------------------------------------
+//
+PRIVATE
+fetchitem_s *FetchItemByName( const char *filename )
+{
+ ASSERT( filename != NULL );
+
+ // scan all registered packages
+ for ( int packid = 0; packid < num_data_packages; packid++ ) {
+
+ // scan all items of this package
+ pfileinfo_s *items = package_items[ packid ];
+
+ for ( unsigned int curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+ if ( stricmp( items[ curit ].file, filename ) == 0 ) {
+ _fetch_item.packid = packid;
+ _fetch_item.item = &items[ curit ];
+ return &_fetch_item;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+// fetch item by file pointer -------------------------------------------------
+//
+PRIVATE
+fetchitem_s *FetchItemByFile( FILE *fp )
+{
+ ASSERT( fp != NULL );
+
+ // try cache first
+ for ( int cp = 0; cp < FETCH_CACHE_SIZE; cp++ ) {
+ if ( _fetch_cache[ cp ].item == NULL ) {
+ continue;
+ }
+ if ( _fetch_cache[ cp ].item->fp == fp ) {
+ return &_fetch_cache[ cp ];
+ }
+ }
+
+ // scan all registered packages
+ for ( int packid = 0; packid < num_data_packages; packid++ ) {
+
+ // scan all items of this package
+ pfileinfo_s *items = package_items[ packid ];
+ unsigned int curit = 0;
+ for ( curit = 0; curit < package_numitems[ packid ]; curit++ ) {
+ if ( items[ curit ].fp == fp )
+ break;
+ }
+
+ if ( curit < package_numitems[ packid ] ) {
+
+ // enter into cache
+ int cp = _fetch_cache_pos;
+ _fetch_cache[ cp ].packid = packid;
+ _fetch_cache[ cp ].item = &items[ curit ];
+
+ _fetch_cache_pos++;
+ _fetch_cache_pos &= FETCH_CACHE_MASK;
+
+ // return info for found item
+ return &_fetch_cache[ cp ];
+ }
+ }
+
+ return NULL;
+}
+
+
+// determine file length ------------------------------------------------------
+//
+long int SYS_GetFileLength( const char *filename )
+{
+ ASSERT( filename != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return SYS_SysGetFileLength( filename );
+ }
+
+ fetchitem_s *fetchitem = FetchItemByName( filename );
+ if ( fetchitem != NULL ) {
+ return fetchitem->item->flength;
+ }
+
+ // fallback
+ return SYS_SysGetFileLength( filename );
+}
+
+
+// open file ------------------------------------------------------------------
+//
+FILE *SYS_fopen( const char *filename, const char *mode )
+{
+ ASSERT( filename != NULL );
+ ASSERT( mode != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return fopen( filename, mode );
+ }
+
+ // open package for reading at correct offset if file found as item
+ fetchitem_s *fetchitem = FetchItemByName( filename );
+ if ( fetchitem != NULL ) {
+
+ // open package file
+ FILE *fp = fopen( package_filename[ fetchitem->packid ], mode );
+
+ // this may indeed happen if too few handles available
+ ASSERT( fp != NULL );
+ if ( fp == NULL ) {
+ return NULL;
+ }
+
+ // set read position to start of item in package
+ size_t readpos = package_filebase[ fetchitem->packid ];
+ readpos += fetchitem->item->foffset;
+ fseek( fp, readpos, SEEK_SET );
+
+ // init item state
+ fetchitem->item->fp = fp;
+ fetchitem->item->fcurpos = 0;
+
+ return fp;
+ }
+
+ // fallback
+ return fopen( filename, mode );
+}
+
+
+// close file -----------------------------------------------------------------
+//
+int SYS_fclose( FILE *fp )
+{
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return fclose( fp );
+ }
+
+ // clear file pointer if file found as item
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+ fetchitem->item->fp = NULL;
+ }
+
+ // close file (package file/real file)
+ return fclose( fp );
+}
+
+
+// read from file -------------------------------------------------------------
+//
+size_t SYS_fread( void *buf, size_t elsize, size_t nelem, FILE *fp )
+{
+ ASSERT( buf != NULL );
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return fread( buf, elsize, nelem, fp );
+ }
+
+ // read file from package if found as item
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+
+ // size in bytes
+ size_t readsize = nelem * elsize;
+
+ // make sure we are not reading past eof
+ pfileinfo_s *item = fetchitem->item;
+ size_t beyond = item->fcurpos + readsize;
+ if ( beyond > item->flength ) {
+ readsize = ( item->flength > item->fcurpos ) ?
+ item->flength - item->fcurpos : 0;
+ }
+
+ // read data
+ if ( readsize == 0 )
+ return 0;
+ size_t bytesread = fread( buf, 1, readsize, fp );
+ item->fcurpos += bytesread;
+ return bytesread;
+ }
+
+ // fallback
+ return fread( buf, elsize, nelem, fp );
+}
+
+
+// seek to file position ------------------------------------------------------
+//
+int SYS_fseek( FILE *fp, long int offset, int whence )
+{
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return fseek( fp, offset, whence );
+ }
+
+ // position in package if file found as item
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+
+ switch ( whence ) {
+
+ case SEEK_SET:
+ fetchitem->item->fcurpos = (dword) offset;
+ break;
+
+ case SEEK_CUR:
+ fetchitem->item->fcurpos += offset;
+ break;
+
+ case SEEK_END:
+ fetchitem->item->fcurpos = fetchitem->item->flength;
+ fetchitem->item->fcurpos += offset;
+ break;
+
+ default:
+ return -1;
+ }
+
+ long int packofs = package_filebase[ fetchitem->packid ];
+ packofs += fetchitem->item->foffset;
+ packofs += fetchitem->item->fcurpos;
+ return fseek( fp, packofs, SEEK_SET );
+ }
+
+ // fallback
+ return fseek( fp, offset, whence );
+}
+
+
+// tell file position ---------------------------------------------------------
+//
+long int SYS_ftell( FILE *fp )
+{
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return ftell( fp );
+ }
+
+ // position in package if file found as item
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+ return fetchitem->item->fcurpos;
+ }
+
+ // fallback
+ return ftell( fp );
+}
+
+
+// get eof state of file ------------------------------------------------------
+//
+int SYS_feof( FILE *fp )
+{
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_DATA_FILES ) {
+ return feof( fp );
+ }
+
+ //NOTE:
+ // the semantics of this function when used for files contained in
+ // packages is not exactly identical to the semantics of feof().
+ // the eof state is directly queried using the current (read) position
+ // in the file, whereas feof() doesn't return eof until a read operation
+ // has actually been performed beyond eof. therefore it will also return
+ // eof if the read position has only been set with SYS_fseek(), but not
+ // yet been used by SYS_fread(), which differs from the reference behavior.
+
+ // query position in package if file found as item
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+ return ( fetchitem->item->fcurpos >= fetchitem->item->flength );
+ }
+
+ // fallback
+ return feof( fp );
+}
+
+
+// last character read by Item_getc() -----------------------------------------
+//
+static int last_c;
+
+
+// getc() for package file ----------------------------------------------------
+//
+inline
+int Item_getc( pfileinfo_s *item )
+{
+ if ( item->fcurpos >= item->flength ) {
+ last_c = EOF;
+ } else {
+ last_c = getc( item->fp );
+ item->fcurpos++;
+ }
+
+ return last_c;
+}
+
+
+// ungetc() for package file --------------------------------------------------
+//
+inline
+int Item_ungetc( pfileinfo_s *item )
+{
+ if ( last_c != EOF ) {
+ ungetc( last_c, item->fp );
+ item->fcurpos--;
+ }
+
+ return last_c;
+}
+
+
+// read a line of text from a file --------------------------------------------
+//
+char *SYS_fgets( char *string, int n, FILE *fp )
+{
+ ASSERT( string != NULL );
+ ASSERT( fp != NULL );
+
+ if ( AUX_DISABLE_PACKAGE_SCRIPTS ) {
+ return fgets( string, n, fp );
+ }
+
+ fetchitem_s *fetchitem = FetchItemByFile( fp );
+ if ( fetchitem != NULL ) {
+
+ // n includes the terminator
+ if ( n <= 0 ) {
+ return NULL;
+ }
+
+ // read until eol, eof, or string mem end
+ int c = EOF;
+ char *output = NULL;
+ for ( output = string; --n > 0; ) {
+
+ c = Item_getc( fetchitem->item );
+
+ // check for eof
+ if ( c == EOF ) {
+ if ( output == string ) {
+ return NULL;
+ } else {
+ break;
+ }
+ }
+
+ // store read character
+ *output++ = c;
+
+ if ( c == 0x0d ) {
+ break;
+ }
+ if ( c == 0x0a ) {
+ break;
+ }
+ }
+
+ // try to read 0x0a after 0x0d
+ if ( c == 0x0d ) {
+
+ if ( Item_getc( fetchitem->item ) == 0x0a ) {
+ output[ -1 ] = '\n';
+ } else {
+ Item_ungetc( fetchitem->item );
+ }
+
+ // try to read 0x0d after 0x0a
+ } else if ( c == 0x0a ) {
+
+ if ( Item_getc( fetchitem->item ) == 0x0d ) {
+ output[ -1 ] = '\n';
+ } else {
+ Item_ungetc( fetchitem->item );
+ }
+ }
+
+ // terminate string
+ *output = 0;
+ return string;
+ }
+
+ // fallback
+ return fgets( string, n, fp );
+}
+
+
+// open file (operating system level) -----------------------------------------
+//
+int SYS_open( const char *path, int access )
+{
+ // dummy
+ return 0;
+}
+
+
+// close file (operating system level) ----------------------------------------
+//
+int SYS_close( int handle )
+{
+ // dummy
+ return 1;
+}
+
+
+// read file (operating system level) -----------------------------------------
+//
+int SYS_read( int handle, char *buffer, int len )
+{
+ // dummy
+ return 0;
+}
+
+
+// get file length (operating system level) -----------------------------------
+//
+long int SYS_filelength( int handle )
+{
+ // dummy
+ return 0;
+}
+
+
+