From 5c507cc35c84b5211ee695eb186e31d2ba0dc8f3 Mon Sep 17 00:00:00 2001
From: Peak <48360696+PeakKS@users.noreply.github.com>
Date: Sat, 13 Apr 2024 14:14:52 -0400
Subject: [PATCH] Implement File.Size (#2131)

* Implement file size method for already open files

* Switch from fseek/ftell to fileno/fstat size reading
---
 bridge/include/IFileSystemBridge.h |  1 +
 core/logic/smn_filesystem.cpp      | 39 ++++++++++++++++++++++++++++++
 core/logic_bridge.cpp              |  4 +++
 plugins/include/files.inc          |  5 ++++
 4 files changed, 49 insertions(+)

diff --git a/bridge/include/IFileSystemBridge.h b/bridge/include/IFileSystemBridge.h
index 73fdec8d..4c62506f 100644
--- a/bridge/include/IFileSystemBridge.h
+++ b/bridge/include/IFileSystemBridge.h
@@ -44,6 +44,7 @@ public:
 	virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file) = 0;
 	virtual bool EndOfFile(FileHandle_t file) = 0;
 	virtual bool FileExists(const char *pFileName, const char *pPathID = 0) = 0;
+	virtual unsigned int Size(FileHandle_t file) = 0;
 	virtual unsigned int Size(const char *pFileName, const char *pPathID = 0) = 0;
 	virtual int Read(void* pOutput, int size, FileHandle_t file) = 0;
 	virtual int Write(void const* pInput, int size, FileHandle_t file) = 0;
diff --git a/core/logic/smn_filesystem.cpp b/core/logic/smn_filesystem.cpp
index 032f951e..3d9b302d 100644
--- a/core/logic/smn_filesystem.cpp
+++ b/core/logic/smn_filesystem.cpp
@@ -73,6 +73,7 @@ class FileObject
 public:
 	virtual ~FileObject()
 	{}
+	virtual size_t Size() = 0;
 	virtual size_t Read(void *pOut, int size) = 0;
 	virtual char *ReadLine(char *pOut, int size) = 0;
 	virtual size_t Write(const void *pData, int size) = 0;
@@ -119,6 +120,10 @@ public:
 		return true;
 	}
 
+	size_t Size() override {
+		return (size_t)bridge->filesystem->Size(handle_);
+	}
+
 	size_t Read(void *pOut, int size) override {
 		return (size_t)bridge->filesystem->Read(pOut, size, handle_);
 	}
@@ -183,6 +188,30 @@ public:
 		return unlink(path) == 0;
 	}
 
+	size_t Size() override {
+#ifdef PLATFORM_WINDOWS
+		struct _stat s;
+		int fd = _fileno(fp_);
+		if (fd == -1)
+			return -1;
+		if (_fstat(fd, &s) != 0)
+			return -1;
+		if (s.st_mode & S_IFREG)
+			return static_cast<size_t>(s.st_size);
+		return -1;
+#elif defined PLATFORM_POSIX
+		struct stat s;
+		int fd = fileno(fp_);
+		if (fd == -1)
+			return -1;
+		if (fstat(fd, &s) != 0)
+			return -1;
+		if (S_ISREG(s.st_mode))
+			return static_cast<size_t>(s.st_size);
+		return -1;
+#endif
+	}
+
 	size_t Read(void *pOut, int size) override {
 		return fread(pOut, 1, size, fp_);
 	}
@@ -1125,6 +1154,15 @@ static cell_t sm_RemoveGameLogHook(IPluginContext *pContext, const cell_t *param
 	return 1;
 }
 
+static cell_t File_Size(IPluginContext *pContext, const cell_t *params)
+{
+	OpenHandle<FileObject> file(pContext, params[1], g_FileType);
+	if (!file.Ok())
+		return -1;
+
+	return file->Size();
+}
+
 template <typename T>
 static cell_t File_ReadTyped(IPluginContext *pContext, const cell_t *params)
 {
@@ -1197,6 +1235,7 @@ REGISTER_NATIVES(filesystem)
 	{"File.Seek",				sm_FileSeek},
 	{"File.Flush",				sm_FlushFile},
 	{"File.Position.get",		sm_FilePosition},
+	{"File.Size",				File_Size},
 	{"File.ReadInt8",			File_ReadTyped<int8_t>},
 	{"File.ReadUint8",			File_ReadTyped<uint8_t>},
 	{"File.ReadInt16",			File_ReadTyped<int16_t>},
diff --git a/core/logic_bridge.cpp b/core/logic_bridge.cpp
index a718c323..4cc1128a 100644
--- a/core/logic_bridge.cpp
+++ b/core/logic_bridge.cpp
@@ -138,6 +138,10 @@ public:
 	{
 		return filesystem->FileExists(pFileName, pPathID);
 	}
+	unsigned int Size(FileHandle_t file) override
+	{
+		return filesystem->Size(file);
+	}
 	unsigned int Size(const char *pFileName, const char *pPathID = 0) override
 	{
 		return filesystem->Size(pFileName, pPathID);
diff --git a/plugins/include/files.inc b/plugins/include/files.inc
index 1d8fd3b3..78efd65c 100644
--- a/plugins/include/files.inc
+++ b/plugins/include/files.inc
@@ -112,6 +112,11 @@ methodmap File < Handle
 		CloseHandle(this);
 	}
 
+	// Get the file size in bytes.
+	//
+	// @return                File size in bytes, -1 on error.
+	public native int Size();
+
 	// Reads a line of text from a file.
 	//
 	// @param buffer          String buffer to hold the line.