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); | ||
| }; |