mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 21:00:17 +02:00
482 lines
12 KiB
C++
482 lines
12 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#define _FILE_DEFINED
|
|
struct FILE;
|
|
#define _SECIMP
|
|
#define _CRTIMP
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <mutex>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
#include <cerrno>
|
|
#include <io.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <wchar.h>
|
|
#include <windef.h>
|
|
#include <winternl.h>
|
|
#include <winbase.h>
|
|
#include <winerror.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <handleapi.h>
|
|
#include <fileapi.h>
|
|
#include <errhandlingapi.h>
|
|
#include <wine/debug.h>
|
|
#include "../Priv.h"
|
|
|
|
struct FILE {
|
|
HANDLE Handle {INVALID_HANDLE_VALUE};
|
|
int FileHandle {-1};
|
|
bool Append {false};
|
|
|
|
FILE(HANDLE Handle, int FileHandle, bool Append)
|
|
: Handle {Handle}
|
|
, FileHandle {FileHandle}
|
|
, Append {Append} {}
|
|
|
|
~FILE() {
|
|
CloseHandle(Handle);
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
std::mutex FileTableLock;
|
|
std::vector<std::unique_ptr<FILE>> OpenFileTable;
|
|
|
|
|
|
int ErrnoReturn(int Value) {
|
|
errno = Value;
|
|
return -1;
|
|
}
|
|
|
|
DWORD OpenFlagToAccess(int OpenFlag) {
|
|
if (OpenFlag & _O_RDONLY) {
|
|
return GENERIC_READ;
|
|
}
|
|
if (OpenFlag & _O_WRONLY) {
|
|
return GENERIC_WRITE;
|
|
}
|
|
if (OpenFlag & _O_RDWR) {
|
|
return GENERIC_READ | GENERIC_WRITE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DWORD OpenFlagToCreation(int OpenFlag) {
|
|
if ((OpenFlag & (_O_TRUNC | _O_CREAT)) == (_O_TRUNC | _O_CREAT)) {
|
|
return CREATE_ALWAYS;
|
|
}
|
|
if ((OpenFlag & (_O_EXCL | _O_CREAT)) == (_O_EXCL | _O_CREAT)) {
|
|
return CREATE_NEW;
|
|
}
|
|
if (OpenFlag & _O_TRUNC) {
|
|
return TRUNCATE_EXISTING;
|
|
}
|
|
if (OpenFlag & _O_CREAT) {
|
|
return OPEN_ALWAYS;
|
|
}
|
|
return OPEN_EXISTING;
|
|
}
|
|
|
|
int AllocateFile(std::unique_ptr<FILE>&& File) {
|
|
std::scoped_lock Lock {FileTableLock};
|
|
auto It = std::find(OpenFileTable.begin(), OpenFileTable.end(), nullptr);
|
|
if (It == OpenFileTable.end()) {
|
|
It = OpenFileTable.emplace(OpenFileTable.end(), std::move(File));
|
|
} else {
|
|
*It = std::move(File);
|
|
}
|
|
size_t Idx = std::distance(OpenFileTable.begin(), It);
|
|
if (Idx >= std::numeric_limits<int>::max()) {
|
|
std::terminate();
|
|
}
|
|
(*It)->FileHandle = static_cast<int>(Idx);
|
|
return (*It)->FileHandle;
|
|
}
|
|
|
|
FILE* GetFile(int FileHandle) {
|
|
std::scoped_lock Lock {FileTableLock};
|
|
return OpenFileTable[FileHandle].get();
|
|
}
|
|
|
|
void RemoveFile(int FileHandle) {
|
|
std::scoped_lock Lock {FileTableLock};
|
|
OpenFileTable[FileHandle].reset();
|
|
}
|
|
|
|
DWORD OriginToMoveMethod(int Origin) {
|
|
switch (Origin) {
|
|
case SEEK_SET: return FILE_BEGIN;
|
|
case SEEK_CUR: return FILE_CURRENT;
|
|
case SEEK_END: return FILE_END;
|
|
}
|
|
UNIMPLEMENTED();
|
|
}
|
|
} // namespace
|
|
|
|
// io.h File Operatons
|
|
DLLEXPORT_FUNC(int, _wsopen, (const wchar_t* Filename, int OpenFlag, int ShareFlag, ...)) {
|
|
DWORD Attrs = 0;
|
|
if (OpenFlag & _O_CREAT) {
|
|
va_list VA;
|
|
int PermMode;
|
|
va_start(VA, ShareFlag);
|
|
PermMode = va_arg(VA, int);
|
|
va_end(VA);
|
|
if (!(PermMode & _S_IWRITE)) {
|
|
Attrs = FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
}
|
|
auto access = OpenFlagToAccess(OpenFlag);
|
|
ULONG sharing = FILE_SHARE_READ;
|
|
if (access == GENERIC_WRITE) {
|
|
sharing |= FILE_SHARE_WRITE;
|
|
}
|
|
|
|
if (ShareFlag == _SH_DENYRW) {
|
|
sharing = 0;
|
|
} else if (ShareFlag == _SH_DENYWR) {
|
|
sharing &= ~FILE_SHARE_WRITE;
|
|
} else if (ShareFlag == _SH_DENYRD) {
|
|
sharing &= ~FILE_SHARE_READ;
|
|
}
|
|
|
|
HANDLE Handle = CreateFileW(Filename, access, sharing, nullptr, OpenFlagToCreation(OpenFlag), Attrs, nullptr);
|
|
if (Handle != INVALID_HANDLE_VALUE) {
|
|
return AllocateFile(std::make_unique<FILE>(Handle, -1, OpenFlag & _O_APPEND));
|
|
}
|
|
|
|
if (GetLastError() == ERROR_FILE_EXISTS) {
|
|
return ErrnoReturn(EEXIST);
|
|
}
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
|
return ErrnoReturn(ENOENT);
|
|
}
|
|
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
|
return ErrnoReturn(EACCES);
|
|
}
|
|
return ErrnoReturn(ENOENT);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _wopen, (const wchar_t* Filename, int OpenFlag, ...)) {
|
|
if (OpenFlag & _O_CREAT) {
|
|
va_list VA;
|
|
int PermMode;
|
|
va_start(VA, OpenFlag);
|
|
PermMode = va_arg(VA, int);
|
|
va_end(VA);
|
|
return _wsopen(Filename, OpenFlag, _SH_DENYNO, PermMode);
|
|
}
|
|
return _wsopen(Filename, OpenFlag, _SH_DENYNO);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _sopen, (const char* Filename, int OpenFlag, int ShareFlag, ...)) {
|
|
UNICODE_STRING FilenameW;
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&FilenameW, Filename)) {
|
|
return ErrnoReturn(EINVAL);
|
|
}
|
|
int ret = 0;
|
|
if (OpenFlag & _O_CREAT) {
|
|
va_list VA;
|
|
int PermMode;
|
|
va_start(VA, ShareFlag);
|
|
PermMode = va_arg(VA, int);
|
|
va_end(VA);
|
|
ret = _wopen(FilenameW.Buffer, OpenFlag, ShareFlag, PermMode);
|
|
} else {
|
|
ret = _wopen(FilenameW.Buffer, OpenFlag, ShareFlag);
|
|
}
|
|
RtlFreeUnicodeString(&FilenameW);
|
|
return ret;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _open, (const char* Filename, int OpenFlag, ...)) {
|
|
if (OpenFlag & _O_CREAT) {
|
|
va_list VA;
|
|
int PermMode;
|
|
va_start(VA, OpenFlag);
|
|
PermMode = va_arg(VA, int);
|
|
va_end(VA);
|
|
return _sopen(Filename, OpenFlag, _SH_DENYNO, PermMode);
|
|
}
|
|
return _sopen(Filename, OpenFlag, _SH_DENYNO);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, open, (const char* Filename, int OpenFlag, ...)) {
|
|
if (OpenFlag & _O_CREAT) {
|
|
va_list VA;
|
|
int PermMode;
|
|
va_start(VA, OpenFlag);
|
|
PermMode = va_arg(VA, int);
|
|
va_end(VA);
|
|
return _open(Filename, OpenFlag, PermMode);
|
|
}
|
|
return _open(Filename, OpenFlag);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _close, (int FileHandle)) {
|
|
RemoveFile(FileHandle);
|
|
return 0;
|
|
}
|
|
|
|
int close(int FileHandle) {
|
|
return _close(FileHandle);
|
|
}
|
|
|
|
int64_t _lseeki64(int FileHandle, int64_t Offset, int Origin) {
|
|
LARGE_INTEGER Res;
|
|
SetFilePointerEx(GetFile(FileHandle)->Handle, LARGE_INTEGER {.QuadPart = Offset}, &Res, OriginToMoveMethod(Origin));
|
|
return Res.QuadPart;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(long, _lseek, (int FileHandle, long Offset, int Origin)) {
|
|
LARGE_INTEGER Res;
|
|
SetFilePointerEx(GetFile(FileHandle)->Handle, LARGE_INTEGER {.QuadPart = Offset}, &Res, OriginToMoveMethod(Origin));
|
|
return Res.QuadPart;
|
|
}
|
|
|
|
long lseek(int FileHandle, long Offset, int Origin) {
|
|
return _lseek(FileHandle, Offset, Origin);
|
|
}
|
|
|
|
int64_t _telli64(int FileHandle) {
|
|
LARGE_INTEGER Res;
|
|
SetFilePointerEx(GetFile(FileHandle)->Handle, LARGE_INTEGER {}, &Res, FILE_CURRENT);
|
|
return Res.QuadPart;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _read, (int FileHandle, void* DstBuf, unsigned int MaxCharCount)) {
|
|
DWORD Read;
|
|
ReadFile(GetFile(FileHandle)->Handle, DstBuf, MaxCharCount, &Read, nullptr);
|
|
return static_cast<int>(Read);
|
|
}
|
|
|
|
int read(int FileHandle, void* DstBuf, unsigned int MaxCharCount) {
|
|
return _read(FileHandle, DstBuf, MaxCharCount);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _write, (int FileHandle, const void* Buf, unsigned int MaxCharCount)) {
|
|
DWORD Written;
|
|
FILE* File = GetFile(FileHandle);
|
|
if (File->Append) {
|
|
SetFilePointerEx(File->Handle, LARGE_INTEGER {}, nullptr, FILE_END);
|
|
}
|
|
WriteFile(File->Handle, Buf, MaxCharCount, &Written, nullptr);
|
|
return static_cast<int>(Written);
|
|
}
|
|
|
|
int write(int FileHandle, const void* Buf, unsigned int MaxCharCount) {
|
|
return _write(FileHandle, Buf, MaxCharCount);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, ftruncate, (int FileHandle, off_t Length)) {
|
|
FILE_END_OF_FILE_INFO Info {.EndOfFile = {.QuadPart = Length}};
|
|
if (!SetFileInformationByHandle(GetFile(FileHandle)->Handle, FileEndOfFileInfo, &Info, sizeof(Info))) {
|
|
return ErrnoReturn(EINVAL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int _chsize(int FileHandle, long Length) {
|
|
return ftruncate(FileHandle, Length);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _isatty, (int _FileHandle)) {
|
|
return 0;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(intptr_t, _get_osfhandle, (int _FileHandle)) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
namespace {
|
|
template<typename TStr, typename TChar, TStr (*StrchrFunc)(TStr, TChar)>
|
|
int ModeToOpenFlag(TStr Mode) {
|
|
int OpenFlag = 0;
|
|
if (StrchrFunc(Mode, 'a')) {
|
|
OpenFlag |= _O_RDWR | _O_CREAT | _O_APPEND;
|
|
} else if (StrchrFunc(Mode, 'r')) {
|
|
if (StrchrFunc(Mode, '+')) {
|
|
OpenFlag |= _O_RDWR;
|
|
} else {
|
|
OpenFlag |= _O_RDONLY;
|
|
}
|
|
} else {
|
|
OpenFlag |= _O_RDWR | _O_CREAT | _O_TRUNC;
|
|
}
|
|
if (StrchrFunc(Mode, 'x')) {
|
|
OpenFlag |= _O_EXCL;
|
|
}
|
|
return OpenFlag;
|
|
}
|
|
} // namespace
|
|
|
|
// stdio.h File Operations
|
|
DLLEXPORT_FUNC(FILE*, _wfopen, (const wchar_t* __restrict__ Filename, const wchar_t* __restrict__ Mode)) {
|
|
int OpenFlag = ModeToOpenFlag<const wchar_t*, wchar_t, &wcschr>(Mode);
|
|
int Ret = _wopen(Filename, OpenFlag, _S_IWRITE | _S_IREAD);
|
|
if (Ret == -1) {
|
|
return nullptr;
|
|
}
|
|
return GetFile(Ret);
|
|
}
|
|
|
|
FILE* fopen(const char* __restrict__ Filename, const char* __restrict__ Mode) {
|
|
int OpenFlag = ModeToOpenFlag<const char*, int, &strchr>(Mode);
|
|
int Ret = _open(Filename, OpenFlag, _S_IWRITE | _S_IREAD);
|
|
if (Ret == -1) {
|
|
return nullptr;
|
|
}
|
|
return GetFile(Ret);
|
|
}
|
|
|
|
FILE* fdopen(int _FileHandle, const char* _Mode) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int fclose(FILE* File) {
|
|
RemoveFile(File->FileHandle);
|
|
return 0;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _fseeki64, (FILE * File, _off64_t Offset, int Origin)) {
|
|
SetFilePointerEx(File->Handle, LARGE_INTEGER {.QuadPart = Offset}, nullptr, OriginToMoveMethod(Origin));
|
|
return 0;
|
|
}
|
|
|
|
int fseek(FILE* File, long Offset, int Origin) {
|
|
return _fseeki64(File, Offset, Origin);
|
|
}
|
|
|
|
DLLEXPORT_FUNC(_off64_t, _ftelli64, (FILE * File)) {
|
|
LARGE_INTEGER Res;
|
|
SetFilePointerEx(File->Handle, LARGE_INTEGER {}, &Res, FILE_CURRENT);
|
|
return Res.QuadPart;
|
|
}
|
|
|
|
long ftell(FILE* File) {
|
|
return static_cast<long>(_ftelli64(File));
|
|
}
|
|
|
|
size_t fread(void* __restrict__ DstBuf, size_t ElementSize, size_t Count, FILE* __restrict__ File) {
|
|
DWORD Read;
|
|
ReadFile(File->Handle, DstBuf, ElementSize * Count, &Read, nullptr);
|
|
return static_cast<size_t>(Read);
|
|
}
|
|
|
|
size_t fwrite(const void* __restrict__ Str, size_t Size, size_t Count, FILE* __restrict__ File) {
|
|
DWORD Written;
|
|
if (File->Append) {
|
|
SetFilePointerEx(File->Handle, LARGE_INTEGER {}, nullptr, FILE_END);
|
|
}
|
|
WriteFile(File->Handle, Str, Size * Count, &Written, nullptr);
|
|
return static_cast<size_t>(Written);
|
|
}
|
|
|
|
void setbuf(FILE* __restrict__ _File, char* __restrict__ _Buffer) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int fflush(FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int fprintf(FILE* __restrict__, const char* __restrict__, ...) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int vfprintf(FILE* __restrict__, const char* __restrict__, va_list) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int ungetc(int _Ch, FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
wint_t fgetwc(FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
wint_t fputwc(wchar_t _Ch, FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int fputc(int _Ch, FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int fputs(const char* __restrict__ _Str, FILE* __restrict__ _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int getc(FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void _lock_file(FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
wint_t ungetwc(wint_t _Ch, FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void _unlock_file(FILE* _File) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
DLLEXPORT_FUNC(FILE*, __acrt_iob_func, (unsigned index)) {
|
|
return nullptr;
|
|
}
|
|
|
|
DLLEXPORT_FUNC(int, _fileno, (FILE * _File)) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int access(const char* Path, int AccessMode) {
|
|
UNICODE_STRING PathW;
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&PathW, Path)) {
|
|
return ErrnoReturn(EINVAL);
|
|
}
|
|
|
|
UNICODE_STRING NTPath;
|
|
bool Success = RtlDosPathNameToNtPathName_U(PathW.Buffer, &NTPath, nullptr, nullptr);
|
|
RtlFreeUnicodeString(&PathW);
|
|
if (!Success) {
|
|
return ErrnoReturn(EINVAL);
|
|
}
|
|
|
|
OBJECT_ATTRIBUTES ObjAttributes;
|
|
InitializeObjectAttributes(&ObjAttributes, &NTPath, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
|
|
|
|
FILE_BASIC_INFORMATION Info;
|
|
Success = !NtQueryAttributesFile(&ObjAttributes, &Info);
|
|
RtlFreeUnicodeString(&NTPath);
|
|
|
|
if (!Success) {
|
|
return ErrnoReturn(ENOENT);
|
|
}
|
|
|
|
if ((AccessMode & W_OK) && (Info.FileAttributes & FILE_ATTRIBUTE_READONLY)) {
|
|
return ErrnoReturn(EACCES);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rename(const char* _OldFilename, const char* _NewFilename) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int printf(const char* Format, ...) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
int puts(const char* Str) {
|
|
UNIMPLEMENTED();
|
|
}
|