2019-05-24 17:58:32 +00:00
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#include <fcntl.h>
|
2020-05-11 02:55:57 +00:00
|
|
|
#ifdef _WIN32
|
2019-05-24 17:58:32 +00:00
|
|
|
#include <direct.h>
|
2020-05-11 02:55:57 +00:00
|
|
|
#endif
|
2019-05-24 17:58:32 +00:00
|
|
|
#include "common.h"
|
2020-07-26 17:59:58 +00:00
|
|
|
#include "crossplatform.h"
|
2020-04-17 13:31:11 +00:00
|
|
|
|
2019-05-24 17:58:32 +00:00
|
|
|
#include "FileMgr.h"
|
|
|
|
|
2019-06-12 23:12:37 +00:00
|
|
|
const char *_psGetUserFilesFolder();
|
|
|
|
|
2019-05-24 17:58:32 +00: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 02:55:57 +00:00
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#define _getcwd getcwd
|
|
|
|
|
|
|
|
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
|
|
|
|
void mychdir(char const *path)
|
|
|
|
{
|
2020-07-26 17:59:58 +00:00
|
|
|
char* r = casepath(path, false);
|
|
|
|
if (r) {
|
2020-05-11 02:55:57 +00:00
|
|
|
chdir(r);
|
2020-07-26 17:59:58 +00:00
|
|
|
free(r);
|
|
|
|
} else {
|
2020-05-11 02:55:57 +00:00
|
|
|
errno = ENOENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define mychdir chdir
|
|
|
|
#endif
|
|
|
|
|
2019-05-24 17:58:32 +00: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 02:55:57 +00:00
|
|
|
|
2020-07-26 17:59:58 +00:00
|
|
|
myfiles[fd].file = fcaseopen(filename, realmode);
|
2019-05-24 17:58:32 +00:00
|
|
|
if(myfiles[fd].file == nil)
|
|
|
|
return 0;
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2019-06-12 23:12:37 +00:00
|
|
|
static int
|
2019-05-24 17:58:32 +00:00
|
|
|
myfclose(int fd)
|
|
|
|
{
|
2019-06-12 23:12:37 +00:00
|
|
|
int ret;
|
2019-05-24 17:58:32 +00:00
|
|
|
assert(fd < NUMFILES);
|
|
|
|
if(myfiles[fd].file){
|
2019-06-12 23:12:37 +00:00
|
|
|
ret = fclose(myfiles[fd].file);
|
2019-05-24 17:58:32 +00:00
|
|
|
myfiles[fd].file = nil;
|
2019-06-12 23:12:37 +00:00
|
|
|
return ret;
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
2019-06-12 23:12:37 +00:00
|
|
|
return EOF;
|
2019-05-24 17:58:32 +00: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 11:56:28 +00:00
|
|
|
static size_t
|
2019-05-24 17:58:32 +00:00
|
|
|
myfread(void *buf, size_t elt, size_t n, int fd)
|
|
|
|
{
|
|
|
|
if(myfiles[fd].isText){
|
2020-10-04 19:39:54 +00:00
|
|
|
unsigned char *p;
|
2019-05-24 17:58:32 +00:00
|
|
|
size_t i;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
n *= elt;
|
2020-10-04 19:39:54 +00:00
|
|
|
p = (unsigned char*)buf;
|
2019-05-24 17:58:32 +00:00
|
|
|
for(i = 0; i < n; i++){
|
|
|
|
c = myfgetc(fd);
|
|
|
|
if(c == EOF)
|
|
|
|
break;
|
2020-10-04 19:39:54 +00:00
|
|
|
*p++ = (unsigned char)c;
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
return i / elt;
|
|
|
|
}
|
|
|
|
return fread(buf, elt, n, myfiles[fd].file);
|
|
|
|
}
|
|
|
|
|
2020-07-22 11:56:28 +00:00
|
|
|
static size_t
|
2019-05-24 17:58:32 +00:00
|
|
|
myfwrite(void *buf, size_t elt, size_t n, int fd)
|
|
|
|
{
|
|
|
|
if(myfiles[fd].isText){
|
2020-10-04 19:39:54 +00:00
|
|
|
unsigned char *p;
|
2019-05-24 17:58:32 +00:00
|
|
|
size_t i;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
n *= elt;
|
2020-10-04 19:39:54 +00:00
|
|
|
p = (unsigned char*)buf;
|
2019-05-24 17:58:32 +00:00
|
|
|
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 09:57:43 +00:00
|
|
|
return feof(myfiles[fd].file);
|
|
|
|
// return ferror(myfiles[fd].file);
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-17 05:54:14 +00:00
|
|
|
char CFileMgr::ms_rootDirName[128] = {'\0'};
|
|
|
|
char CFileMgr::ms_dirName[128];
|
2019-05-24 17:58:32 +00: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 02:55:57 +00:00
|
|
|
mychdir(ms_dirName);
|
2019-05-24 17:58:32 +00: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 02:55:57 +00:00
|
|
|
mychdir(ms_dirName);
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CFileMgr::SetDirMyDocuments(void)
|
|
|
|
{
|
|
|
|
SetDir(""); // better start at the root if user directory is relative
|
2020-05-11 02:55:57 +00:00
|
|
|
mychdir(_psGetUserFilesFolder());
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 02:23:37 +00:00
|
|
|
ssize_t
|
2019-05-24 17:58:32 +00:00
|
|
|
CFileMgr::LoadFile(const char *file, uint8 *buf, int unused, const char *mode)
|
|
|
|
{
|
|
|
|
int fd;
|
2020-12-27 02:23:37 +00:00
|
|
|
ssize_t n, len;
|
2019-05-24 17:58:32 +00:00
|
|
|
|
|
|
|
fd = myfopen(file, mode);
|
|
|
|
if(fd == 0)
|
2020-12-27 02:23:37 +00:00
|
|
|
return -1;
|
2019-05-24 17:58:32 +00:00
|
|
|
len = 0;
|
|
|
|
do{
|
|
|
|
n = myfread(buf + len, 1, 0x4000, fd);
|
2020-12-27 02:23:37 +00:00
|
|
|
#ifndef FIX_BUGS
|
|
|
|
if (n < 0)
|
2019-05-24 17:58:32 +00:00
|
|
|
return -1;
|
2020-12-27 02:23:37 +00:00
|
|
|
#endif
|
2019-05-24 17:58:32 +00:00
|
|
|
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 11:56:28 +00:00
|
|
|
size_t
|
2020-12-27 02:23:37 +00:00
|
|
|
CFileMgr::Read(int fd, const char *buf, ssize_t len)
|
2019-05-24 17:58:32 +00:00
|
|
|
{
|
2019-08-16 18:17:15 +00:00
|
|
|
return myfread((void*)buf, 1, len, fd);
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 11:56:28 +00:00
|
|
|
size_t
|
2020-12-27 02:23:37 +00:00
|
|
|
CFileMgr::Write(int fd, const char *buf, ssize_t len)
|
2019-05-24 17:58:32 +00:00
|
|
|
{
|
2019-08-16 18:17:15 +00:00
|
|
|
return myfwrite((void*)buf, 1, len, fd);
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CFileMgr::Seek(int fd, int offset, int whence)
|
|
|
|
{
|
|
|
|
return !!myfseek(fd, offset, whence);
|
|
|
|
}
|
|
|
|
|
2019-06-28 19:24:22 +00:00
|
|
|
bool
|
2019-05-24 17:58:32 +00:00
|
|
|
CFileMgr::ReadLine(int fd, char *buf, int len)
|
|
|
|
{
|
2019-06-28 19:24:22 +00:00
|
|
|
return myfgets(buf, len, fd) != nil;
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
2019-06-12 23:12:37 +00:00
|
|
|
int
|
2019-05-24 17:58:32 +00:00
|
|
|
CFileMgr::CloseFile(int fd)
|
|
|
|
{
|
2019-06-12 23:12:37 +00:00
|
|
|
return myfclose(fd);
|
2019-05-24 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CFileMgr::GetErrorReadWrite(int fd)
|
|
|
|
{
|
|
|
|
return myfeof(fd);
|
|
|
|
}
|