Assorted portability enhancements (#1800)

* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Change includes of "../types.h" to "types.h"

- Makes it easier to directly include these headers in downstream projects

* Change an include of "../Wifi.h" to "Wifi.h"

* Allow CommonFuncs.cpp to compile on Android

* Tidy up some logging calls

- Use Platform::Log in LAN_Socket.cpp
- Soften some warnings to Debug logs (since they don't necessarily represent problems)

* Add Platform::EnterGBAMode

- Gracefully stop the emulator if trying to enter GBA mode

* Soften some logs that most players won't care about

* Soften some more logs

* Introduce Platform wrappers for file operations

* Fix pointer spacing

* Fix more style nits

* Log the errno when ftruncate fails

* Fix FileSeek offset argument

- With an s32 offset, we couldn't access files larger than 2GB

* Revise Platform::StopEmu to address feedback

- Remove Platform::EnterGBAMode in favor of adding a reason to Platform::StopEmu
- Also rename Platform::StopEmu to Platform::SignalStop
- Add an optional argument to NDS::Stop
- Use the new argument everywhere that the console stops itself

* Rename FileGetString to FileReadLine

- It conveys the meaning better

* Rename FileSeekOrigin::Set to Start

- It conveys the meaning better

* Change definition of FileGetString to FileReadLine

- Oops, almost forgot it

* Rename FlushFile to FileFlush

- To remain consistent with the other File functions

* Add a FileType usage

* Fix line break in FileSeekOrigin

* Document Platform::DeInit

* Clarify that StopReason::Unknown doesn't always mean an error

* Move and document FileType::HostFile

* Remove Platform::OpenDataFile

- Nothing currently uses it

* Refactor Platform::OpenFile and Platform::OpenLocalFile to accept a FileMode enum instead of a string

- The enum is converted to fopen flags under the hood
- The file type is used to decide whether to add the "b" flag
- Some helper functions are exposed for the benefit of consistent behavior among frontends
- Equivalent behavior is maintained

* Fix a tab that should be spaces

* Use Windows' 64-bit implementations of fseek/ftell

* Move Platform::IsBinaryFile to Platform.cpp

- It could vary by frontend

* Remove an unused FileType

* Rename an enum constant

* Document various Platform items

* Use Platform::DynamicLibrary to load libandroid

- And clean it up at the end

* Fix a typo

* Pass the correct filetype to FATStorage

- Since it can be used for DSI NAND images or for SD cards

* Remove Platform::FileType
This commit is contained in:
Jesse Talavera-Greenberg
2023-08-18 16:50:57 -04:00
committed by GitHub
parent f454eba3c3
commit ee55677086
36 changed files with 787 additions and 443 deletions

View File

@ -28,12 +28,64 @@ namespace Platform
{
void Init(int argc, char** argv);
/**
* Frees all resources that were allocated in \c Init
* or by any other \c Platform function.
*/
void DeInit();
void StopEmu();
enum StopReason {
/**
* The emulator stopped for some unspecified reason.
* Not necessarily an error.
*/
Unknown,
// instance ID, for local multiplayer
/**
* The emulator stopped due to an external call to \c NDS::Stop,
* most likely because the user stopped the game manually.
*/
External,
/**
* The emulator stopped because it tried to enter GBA mode,
* which melonDS does not support.
*/
GBAModeNotSupported,
/**
* The emulator stopped because of an error in the emulated console,
* not necessarily because of an error in melonDS.
*/
BadExceptionRegion,
/**
* The emulated console shut itself down normally,
* likely because its system settings were adjusted
* or its "battery" ran out.
*/
PowerOff,
};
/**
* Signals to the frontend that no more frames should be requested.
* Frontends should not call this directly;
* use \c NDS::Stop instead.
*/
void SignalStop(StopReason reason);
/**
* @returns The ID of the running melonDS instance if running in local multiplayer mode,
* or 0 if not.
*/
int InstanceID();
/**
* @returns A suffix that should be appended to all instance-specific paths
* if running in local multiplayer mode,
* or the empty string if not.
*/
std::string InstanceFileSuffix();
// configuration values
@ -82,6 +134,8 @@ enum ConfigEntry
Firm_Message,
Firm_MAC,
WifiSettingsPath,
AudioBitDepth,
DSi_FullBIOSBoot
@ -92,43 +146,154 @@ bool GetConfigBool(ConfigEntry entry);
std::string GetConfigString(ConfigEntry entry);
bool GetConfigArray(ConfigEntry entry, void* data);
// fopen() wrappers
// * OpenFile():
// simple fopen() wrapper that supports UTF8.
// can be optionally restricted to only opening a file that already exists.
// * OpenLocalFile():
// opens files local to the emulator (melonDS.ini, BIOS, firmware, ...)
// For Windows builds, or portable UNIX builds it checks, by order of priority:
// * current working directory
// * emulator directory (essentially where the melonDS executable is) if supported
// * any platform-specific application data directories
// in create mode, if the file doesn't exist, it will be created in the emulator
// directory if supported, or in the current directory otherwise
// For regular UNIX builds, the user's configuration directory is always used.
// * OpenDataFile():
// Opens a file that was installed alongside melonDS on UNIX systems in /usr/share, etc.
// Looks in the user's data directory first, then the system's.
// If on Windows or a portable UNIX build, this simply calls OpenLocalFile().
/**
* Denotes how a file will be opened and accessed.
* Flags may or may not correspond to the operating system's file API.
*/
enum FileMode : unsigned {
None = 0,
FILE* OpenFile(const std::string& path, const std::string& mode, bool mustexist=false);
FILE* OpenLocalFile(const std::string& path, const std::string& mode);
FILE* OpenDataFile(const std::string& path);
/**
* Opens a file for reading.
* Either this or \c Write must be set.
* Similar to \c "r" in \c fopen.
*/
Read = 0b00'00'01,
inline bool FileExists(const std::string& name)
/**
* Opens a file for writing, creating it if it doesn't exist.
* Will truncate existing files unless \c Preserve is set.
* Either this or \c Read must be set.
* Similar to <tt>fopen</tt>'s \c "w" flag.
*/
Write = 0b00'00'10,
/**
* Opens an existing file as-is without truncating it.
* The file may still be created unless \c NoCreate is set.
* @note This flag has no effect if \c Write is not set.
*/
Preserve = 0b00'01'00,
/**
* Do not create the file if it doesn't exist.
* @note This flag has no effect if \c Write is not set.
*/
NoCreate = 0b00'10'00,
/**
* Opens a file in text mode,
* rather than the default binary mode.
* Text-mode files may have their line endings converted
* to match the operating system,
* and may also be line-buffered.
*/
Text = 0b01'00'00,
/**
* Opens a file for reading and writing.
* Equivalent to <tt>Read | Write</tt>.
*/
ReadWrite = Read | Write,
/**
* Opens a file for reading and writing
* without truncating it or creating a new one.
* Equivalent to <tt>Read | Write | Preserve | NoCreate</tt>.
*/
ReadWriteExisting = Read | Write | Preserve | NoCreate,
/**
* Opens a file for reading in text mode.
* Equivalent to <tt>Read | Text</tt>.
*/
ReadText = Read | Text,
/**
* Opens a file for writing in text mode,
* creating it if it doesn't exist.
* Equivalent to <tt>Write | Text</tt>.
*/
WriteText = Write | Text,
};
/**
* Denotes the origin of a seek operation.
* Similar to \c fseek's \c SEEK_* constants.
*/
enum class FileSeekOrigin
{
FILE* f = OpenFile(name, "rb");
if (!f) return false;
fclose(f);
return true;
}
Start,
Current,
End,
};
inline bool LocalFileExists(const std::string& name)
{
FILE* f = OpenLocalFile(name, "rb");
if (!f) return false;
fclose(f);
return true;
}
/**
* Opaque handle for a file object.
* This can be implemented as a struct defined by the frontend,
* or as a simple pointer cast.
* The core will never look inside this struct,
* but frontends may do so freely.
*/
struct FileHandle;
// Simple fopen() wrapper that supports UTF8.
// Can be optionally restricted to only opening a file that already exists.
FileHandle* OpenFile(const std::string& path, FileMode mode);
// opens files local to the emulator (melonDS.ini, BIOS, firmware, ...)
// For Windows builds, or portable UNIX builds it checks, by order of priority:
// * current working directory
// * emulator directory (essentially where the melonDS executable is) if supported
// * any platform-specific application data directories
// in create mode, if the file doesn't exist, it will be created in the emulator
// directory if supported, or in the current directory otherwise
// For regular UNIX builds, the user's configuration directory is always used.
FileHandle* OpenLocalFile(const std::string& path, FileMode mode);
/// Returns true if the given file exists.
bool FileExists(const std::string& name);
bool LocalFileExists(const std::string& name);
/** Close a file opened with \c OpenFile.
* @returns \c true if the file was closed successfully, false otherwise.
* @post \c file is no longer valid and should not be used.
* The underlying object may still be allocated (e.g. if the frontend refcounts files),
* but that's an implementation detail.
* @see fclose
*/
bool CloseFile(FileHandle* file);
/// @returns \c true if there is no more data left to read in this file,
/// \c false if there is still data left to read or if there was an error.
/// @see feof
bool IsEndOfFile(FileHandle* file);
/// @see fgets
bool FileReadLine(char* str, int count, FileHandle* file);
/// @see fseek
bool FileSeek(FileHandle* file, s64 offset, FileSeekOrigin origin);
/// @see rewind
void FileRewind(FileHandle* file);
/// @see fread
u64 FileRead(void* data, u64 size, u64 count, FileHandle* file);
/// @see fflush
bool FileFlush(FileHandle* file);
/// @see fwrite
u64 FileWrite(const void* data, u64 size, u64 count, FileHandle* file);
/// @see fprintf
u64 FileWriteFormatted(FileHandle* file, const char* fmt, ...);
/// @returns The length of the file in bytes, or 0 if there was an error.
/// @note If this function checks the length by using \c fseek and \c ftell
/// (or local equivalents), it must leave the stream position as it was found.
u64 FileLength(FileHandle* file);
enum LogLevel
{
@ -201,6 +366,28 @@ void Camera_Start(int num);
void Camera_Stop(int num);
void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv);
struct DynamicLibrary;
/**
* @param lib The name of the library to load.
* @return A handle to the loaded library, or \c nullptr if the library could not be loaded.
*/
DynamicLibrary* DynamicLibrary_Load(const char* lib);
/**
* Releases a loaded library.
* Pointers to functions in the library will be invalidated.
* @param lib The library to unload.
*/
void DynamicLibrary_Unload(DynamicLibrary* lib);
/**
* Loads a function from a library.
* @param lib The library to load the function from.
* @param name The name of the function to load.
* @return A pointer to the loaded function, or \c nullptr if the function could not be loaded.
*/
void* DynamicLibrary_LoadFunction(DynamicLibrary* lib, const char* name);
}
#endif // PLATFORM_H