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