This repository has been archived by the owner. It is now read-only.
Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
with
330 additions
and 1 deletion.
- +294 −0 src/FileMgr.cpp
- +21 −0 src/FileMgr.h
- +3 −0 src/common.h
- +12 −1 src/main.cpp
There are no files selected for viewing
| @@ -0,0 +1,294 @@ | |||
| #define _CRT_SECURE_NO_WARNINGS | |||
| #include <fcntl.h> | |||
| #include <direct.h> | |||
| #include "common.h" | |||
| #include "patcher.h" | |||
| #include "FileMgr.h" | |||
|
|
|||
| /* | |||
| * 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]; | |||
|
|
|||
| /* 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'; | |||
| myfiles[fd].file = fopen(filename, realmode); | |||
| if(myfiles[fd].file == nil) | |||
| return 0; | |||
| return fd; | |||
| } | |||
|
|
|||
| static void | |||
| myfclose(int fd) | |||
| { | |||
| assert(fd < NUMFILES); | |||
| if(myfiles[fd].file){ | |||
| fclose(myfiles[fd].file); | |||
| myfiles[fd].file = nil; | |||
| } | |||
| } | |||
|
|
|||
| 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; | |||
| } | |||
|
|
|||
| static int | |||
| 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); | |||
| } | |||
|
|
|||
| static int | |||
| 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) | |||
| { | |||
| return feof(myfiles[fd].file); | |||
| } | |||
|
|
|||
|
|
|||
| char *CFileMgr::ms_rootDirName = (char*)0x5F18F8; | |||
| char *CFileMgr::ms_dirName = (char*)0x713CA8; | |||
|
|
|||
| 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, "\\"); | |||
| } | |||
| chdir(ms_dirName); | |||
| } | |||
|
|
|||
| 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, "\\"); | |||
| } | |||
| chdir(ms_dirName); | |||
| } | |||
|
|
|||
| void | |||
| CFileMgr::SetDirMyDocuments(void) | |||
| { | |||
| SetDir(""); // better start at the root if user directory is relative | |||
| chdir(GetUserDirectory()); | |||
| } | |||
|
|
|||
| int | |||
| CFileMgr::LoadFile(const char *file, uint8 *buf, int unused, const char *mode) | |||
| { | |||
| int fd; | |||
| int n, len; | |||
|
|
|||
| 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"); | |||
| } | |||
|
|
|||
| int | |||
| CFileMgr::Read(int fd, char *buf, int len) | |||
| { | |||
| return myfread(buf, 1, len, fd); | |||
| } | |||
|
|
|||
| int | |||
| CFileMgr::Write(int fd, char *buf, int len) | |||
| { | |||
| return myfwrite(buf, 1, len, fd); | |||
| } | |||
|
|
|||
| bool | |||
| CFileMgr::Seek(int fd, int offset, int whence) | |||
| { | |||
| return !!myfseek(fd, offset, whence); | |||
| } | |||
|
|
|||
| char* | |||
| CFileMgr::ReadLine(int fd, char *buf, int len) | |||
| { | |||
| return myfgets(buf, len, fd); | |||
| } | |||
|
|
|||
| void | |||
| CFileMgr::CloseFile(int fd) | |||
| { | |||
| myfclose(fd); | |||
| } | |||
|
|
|||
| int | |||
| CFileMgr::GetErrorReadWrite(int fd) | |||
| { | |||
| return myfeof(fd); | |||
| } | |||
|
|
|||
| STARTPATCHES | |||
| InjectHook(0x478F80, CFileMgr::Initialise, PATCH_JUMP); | |||
| InjectHook(0x478FB0, CFileMgr::ChangeDir, PATCH_JUMP); | |||
| InjectHook(0x479020, CFileMgr::SetDir, PATCH_JUMP); | |||
| InjectHook(0x479080, CFileMgr::SetDirMyDocuments, PATCH_JUMP); | |||
| InjectHook(0x479090, CFileMgr::LoadFile, PATCH_JUMP); | |||
| InjectHook(0x479100, CFileMgr::OpenFile, PATCH_JUMP); | |||
| InjectHook(0x479120, CFileMgr::OpenFileForWriting, PATCH_JUMP); | |||
| InjectHook(0x479140, CFileMgr::Read, PATCH_JUMP); | |||
| InjectHook(0x479160, CFileMgr::Write, PATCH_JUMP); | |||
| InjectHook(0x479180, CFileMgr::Seek, PATCH_JUMP); | |||
| InjectHook(0x4791D0, CFileMgr::ReadLine, PATCH_JUMP); | |||
| InjectHook(0x479200, CFileMgr::CloseFile, PATCH_JUMP); | |||
| InjectHook(0x479210, CFileMgr::GetErrorReadWrite, PATCH_JUMP); | |||
| ENDPATCHES | |||
| @@ -0,0 +1,21 @@ | |||
| #pragma once | |||
|
|
|||
| class CFileMgr | |||
| { | |||
| static char *ms_rootDirName; //[128]; | |||
| static char *ms_dirName; //[128]; | |||
| public: | |||
| static void Initialise(void); | |||
| static void ChangeDir(const char *dir); | |||
| static void SetDir(const char *dir); | |||
| static void SetDirMyDocuments(void); | |||
| static int LoadFile(const char *file, uint8 *buf, int unused, const char *mode); | |||
| static int OpenFile(const char *file, const char *mode); | |||
| static int OpenFileForWriting(const char *file); | |||
| static int Read(int fd, char *buf, int len); | |||
| static int Write(int fd, char *buf, int len); | |||
| static bool Seek(int fd, int offset, int whence); | |||
| static char *ReadLine(int fd, char *buf, int len); | |||
| static void CloseFile(int fd); | |||
| static int GetErrorReadWrite(int fd); | |||
| }; | |||