summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/FileMgr.cpp294
-rw-r--r--src/FileMgr.h21
-rw-r--r--src/common.h3
-rw-r--r--src/main.cpp13
4 files changed, 330 insertions, 1 deletions
diff --git a/src/FileMgr.cpp b/src/FileMgr.cpp
new file mode 100644
index 00000000..3aad9794
--- /dev/null
+++ b/src/FileMgr.cpp
@@ -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
diff --git a/src/FileMgr.h b/src/FileMgr.h
new file mode 100644
index 00000000..a77ae6fa
--- /dev/null
+++ b/src/FileMgr.h
@@ -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);
+};
diff --git a/src/common.h b/src/common.h
index e55e5b52..272e5017 100644
--- a/src/common.h
+++ b/src/common.h
@@ -5,6 +5,7 @@
#pragma warning(disable: 4244) // int to float
#pragma warning(disable: 4800) // int to bool
#pragma warning(disable: 4838) // narrowing conversion
+#pragma warning(disable: 4996) // POSIX names
#include <stdint.h>
#include <math.h>
@@ -61,6 +62,8 @@ extern RsGlobalType &RsGlobal;
#define SCREENW (RsGlobal.maximumWidth)
#define SCREENH (RsGlobal.maximumHeight)
+char *GetUserDirectory(void);
+
struct GlobalScene
{
RpWorld *world;
diff --git a/src/main.cpp b/src/main.cpp
index b17a30a3..b7d1d151 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,5 +1,6 @@
-#include "common.h"
+#include <direct.h>
#include <Windows.h>
+#include "common.h"
#include "patcher.h"
#include "Renderer.h"
#include "debugmenu_public.h"
@@ -36,6 +37,16 @@ mysrand(unsigned int seed)
myrand_seed = seed;
}
+// platform stuff
+char*
+GetUserDirectory(void)
+{
+ static char path[MAX_PATH];
+ strcpy(path, "userfiles");
+ mkdir(path);
+ return path;
+}
+
int (*open_script_orig)(const char *path, const char *mode);
int