summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/file/winrtfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/file/winrtfile.cpp')
-rw-r--r--src/osd/modules/file/winrtfile.cpp465
1 files changed, 465 insertions, 0 deletions
diff --git a/src/osd/modules/file/winrtfile.cpp b/src/osd/modules/file/winrtfile.cpp
new file mode 100644
index 00000000000..14cb08aa7c4
--- /dev/null
+++ b/src/osd/modules/file/winrtfile.cpp
@@ -0,0 +1,465 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Vas Crabb
+//============================================================
+//
+// winfile.c - Win32 OSD core file access functions
+//
+//============================================================
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "winfile.h"
+
+#include "../../windows/winutil.h"
+
+// MAMEOS headers
+#include "strconv.h"
+#include "unicode.h"
+
+// MAME headers
+#include "osdcore.h"
+
+#include <cassert>
+#include <cstring>
+
+// standard windows headers
+#include <windows.h>
+#include <winioctl.h>
+#include <tchar.h>
+#include <shlwapi.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+
+namespace {
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+class win_osd_file : public osd_file
+{
+public:
+ win_osd_file(win_osd_file const &) = delete;
+ win_osd_file(win_osd_file &&) = delete;
+ win_osd_file& operator=(win_osd_file const &) = delete;
+ win_osd_file& operator=(win_osd_file &&) = delete;
+
+ win_osd_file(HANDLE handle) : m_handle(handle)
+ {
+ assert(m_handle);
+ assert(INVALID_HANDLE_VALUE != m_handle);
+ }
+
+ virtual ~win_osd_file() override
+ {
+ FlushFileBuffers(m_handle);
+ CloseHandle(m_handle);
+ }
+
+ virtual error read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override
+ {
+ // attempt to set the file pointer
+ LARGE_INTEGER largeOffset;
+ largeOffset.QuadPart = offset;
+ if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN))
+ return win_error_to_file_error(GetLastError());
+
+ // then perform the read
+ DWORD result = 0;
+ if (!ReadFile(m_handle, buffer, length, &result, nullptr))
+ return win_error_to_file_error(GetLastError());
+
+ actual = result;
+ return error::NONE;
+ }
+
+ virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override
+ {
+ // attempt to set the file pointer
+ LARGE_INTEGER largeOffset;
+ largeOffset.QuadPart = offset;
+ if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN))
+ return win_error_to_file_error(GetLastError());
+
+ // then perform the write
+ DWORD result = 0;
+ if (!WriteFile(m_handle, buffer, length, &result, nullptr))
+ return win_error_to_file_error(GetLastError());
+
+ actual = result;
+ return error::NONE;
+ }
+
+ virtual error truncate(std::uint64_t offset) override
+ {
+ // attempt to set the file pointer
+ LARGE_INTEGER largeOffset;
+ largeOffset.QuadPart = offset;
+ if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN))
+ return win_error_to_file_error(GetLastError());
+
+ // then perform the truncation
+ if (!SetEndOfFile(m_handle))
+ return win_error_to_file_error(GetLastError());
+ else
+ return error::NONE;
+ }
+
+ virtual error flush() override
+ {
+ // shouldn't be any userspace buffers on the file handle
+ return error::NONE;
+ }
+
+private:
+ HANDLE m_handle;
+};
+
+
+
+//============================================================
+// INLINE FUNCTIONS
+//============================================================
+
+inline bool is_path_to_physical_drive(char const *path)
+{
+ return (_strnicmp(path, "\\\\.\\physicaldrive", 17) == 0);
+}
+
+
+
+//============================================================
+// create_path_recursive
+//============================================================
+
+DWORD create_path_recursive(TCHAR *path)
+{
+ // if there's still a separator, and it's not the root, nuke it and recurse
+ TCHAR *sep = _tcsrchr(path, '\\');
+ if (sep && (sep > path) && (sep[0] != ':') && (sep[-1] != '\\'))
+ {
+ *sep = 0;
+ create_path_recursive(path);
+ *sep = '\\';
+ }
+
+ // if the path already exists, we're done
+ WIN32_FILE_ATTRIBUTE_DATA fileinfo;
+ if (GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo))
+ return NO_ERROR;
+ else if (!CreateDirectory(path, nullptr))
+ return GetLastError();
+ else
+ return NO_ERROR;
+}
+
+} // anonymous namespace
+
+
+
+//============================================================
+// osd_open
+//============================================================
+
+osd_file::error osd_file::open(std::string const &orig_path, uint32_t openflags, ptr &file, std::uint64_t &filesize)
+{
+ std::string path;
+ try { osd_subst_env(path, orig_path); }
+ catch (...) { return error::OUT_OF_MEMORY; }
+
+ if (win_check_socket_path(path))
+ return win_open_socket(path, openflags, file, filesize);
+ else if (win_check_ptty_path(path))
+ return win_open_ptty(path, openflags, file, filesize);
+
+ // convert path to TCHAR
+ osd::text::tstring t_path = osd::text::to_tstring(path);
+
+ // convert the path into something Windows compatible (the actual interesting part appears
+ // to have been commented out???)
+ for (auto iter = t_path.begin(); iter != t_path.end(); iter++)
+ *iter = /* ('/' == *iter) ? '\\' : */ *iter;
+
+ // select the file open modes
+ DWORD disposition, access, sharemode;
+ if (openflags & OPEN_FLAG_WRITE)
+ {
+ disposition = (!is_path_to_physical_drive(path.c_str()) && (openflags & OPEN_FLAG_CREATE)) ? CREATE_ALWAYS : OPEN_EXISTING;
+ access = (openflags & OPEN_FLAG_READ) ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
+ sharemode = FILE_SHARE_READ;
+ }
+ else if (openflags & OPEN_FLAG_READ)
+ {
+ disposition = OPEN_EXISTING;
+ access = GENERIC_READ;
+ sharemode = FILE_SHARE_READ;
+ }
+ else
+ {
+ return error::INVALID_ACCESS;
+ }
+
+ // attempt to open the file
+ HANDLE h = CreateFile2(t_path.c_str(), access, sharemode, disposition, nullptr);
+ if (INVALID_HANDLE_VALUE == h)
+ {
+ DWORD err = GetLastError();
+ // create the path if necessary
+ if ((ERROR_PATH_NOT_FOUND == err) && (openflags & OPEN_FLAG_CREATE) && (openflags & OPEN_FLAG_CREATE_PATHS))
+ {
+ auto pathsep = t_path.rfind('\\');
+ if (pathsep != decltype(t_path)::npos)
+ {
+ // create the path up to the file
+ t_path[pathsep] = 0;
+ err = create_path_recursive(&t_path[0]);
+ t_path[pathsep] = '\\';
+
+ // attempt to reopen the file
+ if (err == NO_ERROR)
+ {
+ h = CreateFile2(t_path.c_str(), access, sharemode, disposition, nullptr);
+ err = GetLastError();
+ }
+ }
+ }
+
+ // if we still failed, clean up and free
+ if (INVALID_HANDLE_VALUE == h)
+ return win_error_to_file_error(err);
+ }
+
+ // get the file size
+ FILE_STANDARD_INFO file_info;
+ GetFileInformationByHandleEx(h, FileStandardInfo, &file_info, sizeof(file_info));
+ auto lower = file_info.EndOfFile.QuadPart;
+
+ if (INVALID_FILE_SIZE == lower)
+ {
+ DWORD const err = GetLastError();
+ if (NO_ERROR != err)
+ {
+ CloseHandle(h);
+ return win_error_to_file_error(err);
+ }
+ }
+
+ try
+ {
+ file = std::make_unique<win_osd_file>(h);
+ filesize = lower;
+ return error::NONE;
+ }
+ catch (...)
+ {
+ CloseHandle(h);
+ return error::OUT_OF_MEMORY;
+ }
+}
+
+
+
+//============================================================
+// osd_openpty
+//============================================================
+
+osd_file::error osd_file::openpty(ptr &file, std::string &name)
+{
+ return error::FAILURE;
+}
+
+
+
+//============================================================
+// osd_rmfile
+//============================================================
+
+osd_file::error osd_file::remove(std::string const &filename)
+{
+ osd::text::tstring tempstr = osd::text::to_tstring(filename);
+
+ error filerr = error::NONE;
+ if (!DeleteFile(tempstr.c_str()))
+ filerr = win_error_to_file_error(GetLastError());
+
+ return filerr;
+}
+
+
+
+//============================================================
+// osd_get_physical_drive_geometry
+//============================================================
+
+bool osd_get_physical_drive_geometry(const char *filename, uint32_t *cylinders, uint32_t *heads, uint32_t *sectors, uint32_t *bps)
+{
+ return false;
+}
+
+
+
+//============================================================
+// osd_stat
+//============================================================
+
+std::unique_ptr<osd::directory::entry> osd_stat(const std::string &path)
+{
+ // convert the path to TCHARs
+ osd::text::tstring t_path = osd::text::to_tstring(path);
+
+ // is this path a root directory (e.g. - C:)?
+ WIN32_FIND_DATA find_data;
+ std::memset(&find_data, 0, sizeof(find_data));
+ if (isalpha(path[0]) && (path[1] == ':') && (path[2] == '\0'))
+ {
+ // need to do special logic for root directories
+ if (!GetFileAttributesEx(t_path.c_str(), GetFileExInfoStandard, &find_data.dwFileAttributes))
+ find_data.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+ }
+ else
+ {
+ // attempt to find the first file
+ HANDLE find = FindFirstFileEx(t_path.c_str(), FindExInfoStandard, &find_data, FindExSearchNameMatch, nullptr, 0);
+ if (find == INVALID_HANDLE_VALUE)
+ return nullptr;
+ FindClose(find);
+ }
+
+ // create an osd::directory::entry; be sure to make sure that the caller can
+ // free all resources by just freeing the resulting osd::directory::entry
+ osd::directory::entry *result;
+ try { result = reinterpret_cast<osd::directory::entry *>(::operator new(sizeof(*result) + path.length() + 1)); }
+ catch (...) { return nullptr; }
+ new (result) osd::directory::entry;
+
+ strcpy(((char *) result) + sizeof(*result), path.c_str());
+ result->name = ((char *) result) + sizeof(*result);
+ result->type = win_attributes_to_entry_type(find_data.dwFileAttributes);
+ result->size = find_data.nFileSizeLow | ((uint64_t) find_data.nFileSizeHigh << 32);
+ result->last_modified = win_time_point_from_filetime(&find_data.ftLastWriteTime);
+
+ return std::unique_ptr<osd::directory::entry>(result);
+}
+
+
+//============================================================
+// osd_get_full_path
+//============================================================
+
+osd_file::error osd_get_full_path(std::string &dst, std::string const &path)
+{
+ // convert the path to TCHARs
+ osd::text::tstring t_path = osd::text::to_tstring(path);
+
+ // cannonicalize the path
+ TCHAR buffer[MAX_PATH];
+ if (!GetFullPathName(t_path.c_str(), ARRAY_LENGTH(buffer), buffer, nullptr))
+ return win_error_to_file_error(GetLastError());
+
+ // convert the result back to UTF-8
+ osd::text::from_tstring(dst, buffer);
+ return osd_file::error::NONE;
+}
+
+
+
+//============================================================
+// osd_is_absolute_path
+//============================================================
+
+bool osd_is_absolute_path(std::string const &path)
+{
+ // very dumb hack here that will be imprecise
+ if (strlen(path.c_str()) >= 2 && path[1] == ':')
+ return true;
+
+ return false;
+
+}
+
+
+
+//============================================================
+// osd_get_volume_name
+//============================================================
+
+const char *osd_get_volume_name(int idx)
+{
+ return nullptr;
+}
+
+
+
+//============================================================
+// osd_is_valid_filename_char
+//============================================================
+
+bool osd_is_valid_filename_char(char32_t uchar)
+{
+ return osd_is_valid_filepath_char(uchar)
+ && uchar != '/'
+ && uchar != '\\'
+ && uchar != ':';
+}
+
+
+
+//============================================================
+// osd_is_valid_filepath_char
+//============================================================
+
+bool osd_is_valid_filepath_char(char32_t uchar)
+{
+ return uchar >= 0x20
+ && uchar != '<'
+ && uchar != '>'
+ && uchar != '\"'
+ && uchar != '|'
+ && uchar != '?'
+ && uchar != '*'
+ && !(uchar >= '\x7F' && uchar <= '\x9F')
+ && uchar_isvalid(uchar);
+}
+
+
+
+//============================================================
+// win_error_to_file_error
+//============================================================
+
+osd_file::error win_error_to_file_error(DWORD error)
+{
+ osd_file::error filerr;
+
+ // convert a Windows error to a osd_file::error
+ switch (error)
+ {
+ case ERROR_SUCCESS:
+ filerr = osd_file::error::NONE;
+ break;
+
+ case ERROR_OUTOFMEMORY:
+ filerr = osd_file::error::OUT_OF_MEMORY;
+ break;
+
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_FILENAME_EXCED_RANGE:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_NAME:
+ filerr = osd_file::error::NOT_FOUND;
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ filerr = osd_file::error::ACCESS_DENIED;
+ break;
+
+ case ERROR_SHARING_VIOLATION:
+ filerr = osd_file::error::ALREADY_OPEN;
+ break;
+
+ default:
+ filerr = osd_file::error::FAILURE;
+ break;
+ }
+ return filerr;
+}