2019-05-24 19:58:32 +02:00
# define _CRT_SECURE_NO_WARNINGS
# include <fcntl.h>
2020-05-11 04:55:57 +02:00
# ifdef _WIN32
2019-05-24 19:58:32 +02:00
# include <direct.h>
2020-05-11 04:55:57 +02:00
# endif
2019-05-24 19:58:32 +02:00
# include "common.h"
2020-04-17 15:31:11 +02:00
2019-05-24 19:58:32 +02:00
# include "FileMgr.h"
2019-06-13 01:12:37 +02:00
const char * _psGetUserFilesFolder ( ) ;
2019-05-24 19:58:32 +02:00
/*
* Windows FILE is BROKEN for GTA .
*
* We need to support mapping between LF and CRLF for text files
* but we do NOT want to end the file at the first sight of a SUB character .
* So here is a simple implementation of a FILE interface that works like GTA expects .
*/
struct myFILE
{
bool isText ;
FILE * file ;
} ;
# define NUMFILES 20
static myFILE myfiles [ NUMFILES ] ;
2020-05-11 04:55:57 +02:00
# if !defined(_WIN32)
# include <dirent.h>
# include <errno.h>
# include <unistd.h>
# include "crossplatform.h"
# define _getcwd getcwd
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
void mychdir ( char const * path )
{
2020-07-26 00:11:13 +02:00
char * r = ( char * ) alloca ( strlen ( path ) + 4 ) ;
2020-05-11 04:55:57 +02:00
if ( casepath ( path , r ) )
{
chdir ( r ) ;
}
else
{
errno = ENOENT ;
}
}
# else
# define mychdir chdir
# endif
2019-05-24 19:58:32 +02:00
/* Force file to open as binary but remember if it was text mode */
static int
myfopen ( const char * filename , const char * mode )
{
int fd ;
char realmode [ 10 ] , * p ;
for ( fd = 1 ; fd < NUMFILES ; fd + + )
if ( myfiles [ fd ] . file = = nil )
goto found ;
return 0 ; // no free fd
found :
myfiles [ fd ] . isText = strchr ( mode , ' b ' ) = = nil ;
p = realmode ;
while ( * mode )
if ( * mode ! = ' t ' & & * mode ! = ' b ' )
* p + + = * mode + + ;
else
mode + + ;
* p + + = ' b ' ;
* p = ' \0 ' ;
2020-05-11 04:55:57 +02:00
# if !defined(_WIN32)
char * newPath = strdup ( filename ) ;
// Normally casepath() fixes backslashes, but if the mode is sth other than r/rb it will create new file with backslashes on linux, so fix backslashes here
char * nextBs ;
while ( nextBs = strstr ( newPath , " \\ " ) ) {
* nextBs = ' / ' ;
}
# else
const char * newPath = filename ;
# endif
myfiles [ fd ] . file = fopen ( newPath , realmode ) ;
// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
# if !defined(_WIN32)
if ( ! myfiles [ fd ] . file ) {
2020-07-26 00:11:13 +02:00
char * r = ( char * ) alloca ( strlen ( newPath ) + 4 ) ;
2020-05-11 04:55:57 +02:00
if ( casepath ( newPath , r ) )
{
myfiles [ fd ] . file = fopen ( r , realmode ) ;
}
}
free ( newPath ) ;
# endif
2019-05-24 19:58:32 +02:00
if ( myfiles [ fd ] . file = = nil )
return 0 ;
return fd ;
}
2019-06-13 01:12:37 +02:00
static int
2019-05-24 19:58:32 +02:00
myfclose ( int fd )
{
2019-06-13 01:12:37 +02:00
int ret ;
2019-05-24 19:58:32 +02:00
assert ( fd < NUMFILES ) ;
if ( myfiles [ fd ] . file ) {
2019-06-13 01:12:37 +02:00
ret = fclose ( myfiles [ fd ] . file ) ;
2019-05-24 19:58:32 +02:00
myfiles [ fd ] . file = nil ;
2019-06-13 01:12:37 +02:00
return ret ;
2019-05-24 19:58:32 +02:00
}
2019-06-13 01:12:37 +02:00
return EOF ;
2019-05-24 19:58:32 +02:00
}
static int
myfgetc ( int fd )
{
int c ;
c = fgetc ( myfiles [ fd ] . file ) ;
if ( myfiles [ fd ] . isText & & c = = 015 ) {
/* translate CRLF to LF */
c = fgetc ( myfiles [ fd ] . file ) ;
if ( c = = 012 )
return c ;
ungetc ( c , myfiles [ fd ] . file ) ;
return 015 ;
}
return c ;
}
static int
myfputc ( int c , int fd )
{
/* translate LF to CRLF */
if ( myfiles [ fd ] . isText & & c = = 012 )
fputc ( 015 , myfiles [ fd ] . file ) ;
return fputc ( c , myfiles [ fd ] . file ) ;
}
static char *
myfgets ( char * buf , int len , int fd )
{
int c ;
char * p ;
p = buf ;
len - - ; // NUL byte
while ( len - - ) {
c = myfgetc ( fd ) ;
if ( c = = EOF ) {
if ( p = = buf )
return nil ;
break ;
}
* p + + = c ;
if ( c = = ' \n ' )
break ;
}
* p = ' \0 ' ;
return buf ;
}
2020-07-22 13:56:28 +02:00
static size_t
2019-05-24 19:58:32 +02:00
myfread ( void * buf , size_t elt , size_t n , int fd )
{
if ( myfiles [ fd ] . isText ) {
char * p ;
size_t i ;
int c ;
n * = elt ;
p = ( char * ) buf ;
for ( i = 0 ; i < n ; i + + ) {
c = myfgetc ( fd ) ;
if ( c = = EOF )
break ;
* p + + = c ;
}
return i / elt ;
}
return fread ( buf , elt , n , myfiles [ fd ] . file ) ;
}
2020-07-22 13:56:28 +02:00
static size_t
2019-05-24 19:58:32 +02:00
myfwrite ( void * buf , size_t elt , size_t n , int fd )
{
if ( myfiles [ fd ] . isText ) {
char * p ;
size_t i ;
int c ;
n * = elt ;
p = ( char * ) buf ;
for ( i = 0 ; i < n ; i + + ) {
c = * p + + ;
myfputc ( c , fd ) ;
if ( feof ( myfiles [ fd ] . file ) ) // is this right?
break ;
}
return i / elt ;
}
return fwrite ( buf , elt , n , myfiles [ fd ] . file ) ;
}
static int
myfseek ( int fd , long offset , int whence )
{
return fseek ( myfiles [ fd ] . file , offset , whence ) ;
}
static int
myfeof ( int fd )
{
2019-06-13 11:57:43 +02:00
return feof ( myfiles [ fd ] . file ) ;
// return ferror(myfiles[fd].file);
2019-05-24 19:58:32 +02:00
}
2020-04-17 07:54:14 +02:00
char CFileMgr : : ms_rootDirName [ 128 ] = { ' \0 ' } ;
char CFileMgr : : ms_dirName [ 128 ] ;
2019-05-24 19:58:32 +02:00
void
CFileMgr : : Initialise ( void )
{
_getcwd ( ms_rootDirName , 128 ) ;
strcat ( ms_rootDirName , " \\ " ) ;
}
void
CFileMgr : : ChangeDir ( const char * dir )
{
if ( * dir = = ' \\ ' ) {
strcpy ( ms_dirName , ms_rootDirName ) ;
dir + + ;
}
if ( * dir ! = ' \0 ' ) {
strcat ( ms_dirName , dir ) ;
// BUG in the game it seems, it's off by one
if ( dir [ strlen ( dir ) - 1 ] ! = ' \\ ' )
strcat ( ms_dirName , " \\ " ) ;
}
2020-05-11 04:55:57 +02:00
mychdir ( ms_dirName ) ;
2019-05-24 19:58:32 +02:00
}
void
CFileMgr : : SetDir ( const char * dir )
{
strcpy ( ms_dirName , ms_rootDirName ) ;
if ( * dir ! = ' \0 ' ) {
strcat ( ms_dirName , dir ) ;
// BUG in the game it seems, it's off by one
if ( dir [ strlen ( dir ) - 1 ] ! = ' \\ ' )
strcat ( ms_dirName , " \\ " ) ;
}
2020-05-11 04:55:57 +02:00
mychdir ( ms_dirName ) ;
2019-05-24 19:58:32 +02:00
}
void
CFileMgr : : SetDirMyDocuments ( void )
{
SetDir ( " " ) ; // better start at the root if user directory is relative
2020-05-11 04:55:57 +02:00
mychdir ( _psGetUserFilesFolder ( ) ) ;
2019-05-24 19:58:32 +02:00
}
2020-07-22 13:56:28 +02:00
size_t
2019-05-24 19:58:32 +02:00
CFileMgr : : LoadFile ( const char * file , uint8 * buf , int unused , const char * mode )
{
int fd ;
2020-07-22 13:56:28 +02:00
size_t n , len ;
2019-05-24 19:58:32 +02:00
fd = myfopen ( file , mode ) ;
if ( fd = = 0 )
return 0 ;
len = 0 ;
do {
n = myfread ( buf + len , 1 , 0x4000 , fd ) ;
if ( n < 0 )
return - 1 ;
len + = n ;
} while ( n = = 0x4000 ) ;
buf [ len ] = 0 ;
myfclose ( fd ) ;
return len ;
}
int
CFileMgr : : OpenFile ( const char * file , const char * mode )
{
return myfopen ( file , mode ) ;
}
int
CFileMgr : : OpenFileForWriting ( const char * file )
{
return OpenFile ( file , " wb " ) ;
}
2020-07-22 13:56:28 +02:00
size_t
2019-08-16 20:17:15 +02:00
CFileMgr : : Read ( int fd , const char * buf , int len )
2019-05-24 19:58:32 +02:00
{
2019-08-16 20:17:15 +02:00
return myfread ( ( void * ) buf , 1 , len , fd ) ;
2019-05-24 19:58:32 +02:00
}
2020-07-22 13:56:28 +02:00
size_t
2019-08-16 20:17:15 +02:00
CFileMgr : : Write ( int fd , const char * buf , int len )
2019-05-24 19:58:32 +02:00
{
2019-08-16 20:17:15 +02:00
return myfwrite ( ( void * ) buf , 1 , len , fd ) ;
2019-05-24 19:58:32 +02:00
}
bool
CFileMgr : : Seek ( int fd , int offset , int whence )
{
return ! ! myfseek ( fd , offset , whence ) ;
}
2019-06-28 21:24:22 +02:00
bool
2019-05-24 19:58:32 +02:00
CFileMgr : : ReadLine ( int fd , char * buf , int len )
{
2019-06-28 21:24:22 +02:00
return myfgets ( buf , len , fd ) ! = nil ;
2019-05-24 19:58:32 +02:00
}
2019-06-13 01:12:37 +02:00
int
2019-05-24 19:58:32 +02:00
CFileMgr : : CloseFile ( int fd )
{
2019-06-13 01:12:37 +02:00
return myfclose ( fd ) ;
2019-05-24 19:58:32 +02:00
}
int
CFileMgr : : GetErrorReadWrite ( int fd )
{
return myfeof ( fd ) ;
}