From 0506a8b0ab1b16759e409fddd56d543d80c563b0 Mon Sep 17 00:00:00 2001
From: Thomas Huth
Date: Sun, 1 Nov 2026 17:06:10 +0100
Subject: [PATCH] Replace functions from file.c with the VFS file functions for
libretro
---
src/CMakeLists.txt | 26 ++--
src/retro/CMakeLists.txt | 1 +
src/retro/file_retro.c | 306 ++++++++++++++++++++++++++++++++++++++
tools/hmsa/CMakeLists.txt | 3 +-
4 files changed, 324 insertions(+), 12 deletions(-)
create mode 100644 src/retro/file_retro.c
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 754b68448..9708c0d92 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,7 @@
# all C-sources from this directory except for:
# - SOURCES_HMSA: sources "hatari" shares with "hmsa"
+# - SOURCES_FILE: file access functions that are only linked into the main exe
# - unzip.c: added separately if ZLIB_FOUND
set(SOURCES
acia.c
@@ -76,14 +77,17 @@ endif()
# Some code is shared with the hmsa tool, so we list this separately:
set(SOURCES_HMSA
createBlankImage.c
- file.c
- file_archive.c
filename.c
floppies/dim.c
floppies/msa.c
floppies/st.c
utils.c)
+# File access functions for the main executable - not used on libretro:
+set(SOURCES_FILE
+ file.c
+ file_archive.c)
+
file(GLOB TOS_IMG_FILE tos.img LIST_DIRECTORIES false)
# When building for macOS, define specific sources for gui and resources
@@ -152,6 +156,10 @@ add_library(CoreHmsa OBJECT ${SOURCES_HMSA})
add_subdirectory(debug)
+if(ENABLE_HATARI OR ENABLE_TOOLS)
+ add_library(CoreFile OBJECT ${SOURCES_FILE})
+endif()
+
if(ENABLE_HATARI OR ENABLE_LIBRETRO)
add_subdirectory(cpu)
add_subdirectory(falcon)
@@ -172,6 +180,7 @@ if(ENABLE_HATARI)
set(APP_TARGET_OBJECTS
$
+ $
$
$
$
@@ -331,6 +340,10 @@ if(ENABLE_HATARI)
target_link_libraries(${APP_NAME} ${SDL2_LIBRARIES})
+ if(LibArchive_FOUND)
+ target_link_libraries(${APP_NAME} ${LibArchive_LIBRARIES})
+ endif(LibArchive_FOUND)
+
if(Math_FOUND AND NOT APPLE)
target_link_libraries(${APP_NAME} ${MATH_LIBRARY})
endif()
@@ -363,15 +376,6 @@ if(ZLIB_FOUND)
endif(ENABLE_LIBRETRO)
endif(ZLIB_FOUND)
-if(LibArchive_FOUND)
- if(ENABLE_HATARI)
- target_link_libraries(${APP_NAME} ${LibArchive_LIBRARIES})
- endif(ENABLE_HATARI)
- if(ENABLE_LIBRETRO)
- target_link_libraries(retro-hatari ${LibArchive_LIBRARIES})
- endif(ENABLE_LIBRETRO)
-endif(LibArchive_FOUND)
-
if(PNG_FOUND)
if(ENABLE_HATARI)
target_link_libraries(${APP_NAME} ${PNG_LIBRARY})
diff --git a/src/retro/CMakeLists.txt b/src/retro/CMakeLists.txt
index 9a7a5ff2d..277c9c670 100644
--- a/src/retro/CMakeLists.txt
+++ b/src/retro/CMakeLists.txt
@@ -4,6 +4,7 @@ include_directories(. ../.. ../includes ../debug ../falcon)
add_library(UiRetro OBJECT
audio.c
disk_control.c
+ file_retro.c
harddisk.c
joy_ui.c
gui_event.c
diff --git a/src/retro/file_retro.c b/src/retro/file_retro.c
new file mode 100644
index 000000000..20752847f
--- /dev/null
+++ b/src/retro/file_retro.c
@@ -0,0 +1,306 @@
+/*
+ Hatari - file_retro.c
+
+ This file is distributed under the GNU General Public License, version 2
+ or at your option any later version. Read the file gpl.txt for details.
+
+ Common file access functions for the libretro build
+*/
+const char File_fileid[] = "Hatari file_retro.c";
+
+#include
+#include
+
+#include "main.h"
+#include "file.h"
+#include "file_archive.h"
+#include "vfs.h"
+
+/**
+ * Read file from disk into allocated buffer and return the buffer
+ * unmodified, or NULL for error. If pFileSize is non-NULL, read
+ * file size is set to that.
+ */
+uint8_t *File_ReadAsIs(const char *pszFileName, long *pFileSize)
+{
+ uint8_t *pFile = NULL;
+ size_t filesize = 0;
+ VFS_FILE hVfsFile;
+
+ hVfsFile = VFS_fopen(pszFileName, "rb");
+ if (hVfsFile != NULL)
+ {
+ filesize = VFS_fsize(hVfsFile);
+ if (filesize > 0)
+ {
+ pFile = malloc(filesize);
+ if (pFile)
+ filesize = (long)VFS_fread(pFile, 1, filesize, hVfsFile);
+ }
+ VFS_fclose(hVfsFile);
+ }
+
+ /* Store size of file we read in (or 0 if failed) */
+ if (pFileSize)
+ *pFileSize = filesize;
+
+ return pFile; /* Return to where read in/allocated */
+}
+
+
+/**
+ * Read file from disk into allocated buffer and return the buffer or
+ * NULL for error. If file is missing, given additional compression
+ * extension are checked too. If file has one of the supported
+ * compression extension, its contents are uncompressed (in case of
+ * ZIP, first file in it is read). If pFileSize is non-NULL, read
+ * file size is set to that.
+ */
+uint8_t *File_Read(const char *pszFileName, long *pFileSize, const char * const ppszExts[])
+{
+ char *filepath = NULL;
+ uint8_t *pFile = NULL;
+ long FileSize = 0;
+
+ /* Does the file exist? If not, see if can scan for other extensions and try these */
+ if (!File_Exists(pszFileName) && ppszExts)
+ {
+ /* Try other extensions, if succeeds, returns correct one */
+ filepath = File_FindPossibleExtFileName(pszFileName, ppszExts);
+ }
+ if (!filepath)
+ filepath = strdup(pszFileName);
+
+ /* It is a normal file */
+ pFile = File_ReadAsIs(filepath, &FileSize);
+
+ free(filepath);
+
+ /* Store size of file we read in (or 0 if failed) */
+ if (pFileSize)
+ *pFileSize = FileSize;
+
+ return pFile; /* Return to where read in/allocated */
+}
+
+
+/**
+ * Save file to disk, return FALSE if errors
+ */
+bool File_Save(const char *pszFileName, const uint8_t *pAddress, size_t Size, bool bQueryOverwrite)
+{
+ VFS_FILE hVfsFile;
+ bool bRet = false;
+
+ hVfsFile = VFS_fopen(pszFileName, "wb");
+ if (hVfsFile != NULL)
+ {
+ /* Write data, set success flag */
+ if (VFS_fwrite(pAddress, 1, Size, hVfsFile) == Size)
+ bRet = true;
+ VFS_fclose(hVfsFile);
+ }
+
+ return bRet;
+}
+
+
+/**
+ * Return size of file, -1 if error
+ */
+off_t File_Length(const char *pszFileName)
+{
+ int64_t len = VFS_FileLength(pszFileName);
+
+ return (len >= 0) ? (off_t)len : (off_t)-1;
+}
+
+
+/**
+ * Return TRUE if file exists, is readable or writable at least and is not
+ * a directory.
+ */
+bool File_Exists(const char *filename)
+{
+ return VFS_FileExists(filename);
+}
+
+
+/**
+ * Return TRUE if directory exists.
+ */
+bool File_DirExists(const char *path)
+{
+ return VFS_DirExists(path);
+}
+
+
+/**
+ * Find if file exists, and if so ask user if OK to overwrite
+ */
+bool File_QueryOverwrite(const char *pszFileName)
+{
+ return true; /* Query dialog not implemented in libretro */
+}
+
+
+/**
+ * Open given filename in given mode and handle "stdout" & "stderr"
+ * filenames specially. Return FILE* to the opened file or NULL on error.
+ */
+FILE *File_Open(const char *path, const char *mode)
+{
+ int wr = 0, rd = 0;
+ FILE *fp;
+
+ /* empty name signifies file that shouldn't be opened/enabled */
+ if (!path || !*path)
+ return NULL;
+
+ /* Open a normal log file */
+ fp = fopen(path, mode);
+ if (!fp)
+ fprintf(stderr, "Can't open file '%s' (wr=%i, rd=%i):\n %s\n",
+ path, wr, rd, strerror(errno));
+
+ return fp;
+}
+
+
+/**
+ * Close given FILE pointer and return the closed pointer
+ * as NULL for the idiom "fp = File_Close(fp);"
+ */
+FILE *File_Close(FILE *fp)
+{
+ if (fp)
+ fclose(fp);
+
+ return NULL;
+}
+
+
+/**
+ * Takes advisory, exclusive lock on given file (FILE *).
+ * Returns false if locking fails (e.g. another Hatari
+ * instance has already file open for writing).
+ */
+bool File_Lock(FILE *fp)
+{
+ return true; /* TODO: Not implemented for libretro yet */
+}
+
+
+/**
+ * Releases advisory, exclusive lock on given file (FILE *).
+ */
+void File_UnLock(FILE *fp)
+{
+ /* TODO */
+}
+
+
+/**
+ * Check if input is available at the specified file descriptor.
+ */
+bool File_InputAvailable(FILE *fp)
+{
+ return false; /* TODO: Not implemented for libretro yet */
+}
+
+
+/**
+ * A simple wrapper around getcwd()
+ */
+char *File_GetCWD(char *buf, size_t bufsize)
+{
+ return getcwd(buf, bufsize);
+}
+
+
+#if defined(WIN32)
+static TCHAR szTempFileName[MAX_PATH];
+
+/**
+ * Get temporary filename for Windows
+ */
+static char* WinTmpFile(void)
+{
+ DWORD dwRetVal = 0;
+ UINT uRetVal = 0;
+ TCHAR lpTempPathBuffer[MAX_PATH];
+
+ /* Gets the temp path env string (no guarantee it's a valid path) */
+ dwRetVal = GetTempPath(MAX_PATH, /* length of the buffer */
+ lpTempPathBuffer); /* buffer for path */
+ if (dwRetVal > MAX_PATH || (dwRetVal == 0))
+ {
+ fprintf(stderr, "GetTempPath failed.\n");
+ return NULL;
+ }
+
+ /* Generates a temporary file name */
+ uRetVal = GetTempFileName(lpTempPathBuffer, /* directory for tmp files */
+ TEXT("HATARI"), /* temp file name prefix */
+ 0, /* create unique name */
+ szTempFileName); /* buffer for name */
+ if (uRetVal == 0)
+ {
+ fprintf(stderr, "GetTempFileName failed.\n");
+ return NULL;
+ }
+ return (char*)szTempFileName;
+}
+#endif
+
+/**
+ * Open a temporary file. This is a wrapper for tmpfile() that can be used
+ * on Windows, too. If *psFileName gets set by this function, the caller
+ * should delete the file manually when it is not needed anymore.
+ */
+FILE *File_OpenTempFile(char **psFileName)
+{
+ FILE *fh;
+ char *psTmpName = NULL;
+
+ fh = tmpfile(); /* Open temporary file */
+
+#if defined(WIN32)
+ if (!fh)
+ {
+ /* Unfortunately tmpfile() needs administrative privileges on
+ * Windows, so if it failed, let's work around this issue. */
+ psTmpName = WinTmpFile();
+ fh = fopen(psTmpName, "w+b");
+ }
+#endif
+
+ if (psFileName)
+ {
+ *psFileName = psTmpName;
+ }
+
+ return fh;
+}
+
+
+uint8_t *File_ZlibRead(const char *filename, long *size)
+{
+ return NULL;
+}
+
+bool Archive_FileNameIsSupported(const char *filename)
+{
+ return false;
+}
+
+uint8_t *Archive_ReadDisk(int drive, const char *fname, const char *archpath,
+ long *imagesize, int *imagetype)
+{
+ return NULL;
+}
+
+bool Archive_WriteDisk(int drive, const char *fname, unsigned char *buf, int size)
+{
+ return false;
+}
diff --git a/tools/hmsa/CMakeLists.txt b/tools/hmsa/CMakeLists.txt
index 62843832e..6be64d778 100644
--- a/tools/hmsa/CMakeLists.txt
+++ b/tools/hmsa/CMakeLists.txt
@@ -4,7 +4,8 @@ include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src/includes
set(HMSA_SOURCES hmsa.c floppy.c)
-add_executable(hmsa ${HMSA_SOURCES} $)
+add_executable(hmsa ${HMSA_SOURCES}
+ $ $)
if(Math_FOUND)
target_link_libraries(hmsa ${MATH_LIBRARY})