From 5d96890167b98ab5d43ac31388ca6ef5c4e6e0ed Mon Sep 17 00:00:00 2001 From: superp00t Date: Sat, 15 Mar 2025 22:23:38 -0400 Subject: [PATCH] refactor(file): overhaul filesystem for better accuracy, add some new features too --- bc/File.hpp | 29 + bc/file/Close.cpp | 19 + bc/file/Close.hpp | 16 + bc/file/Copy.cpp | 20 + bc/file/Copy.hpp | 13 + bc/file/CreateDirectory.cpp | 19 + bc/file/CreateDirectory.hpp | 15 + bc/file/Defines.hpp | 27 +- bc/file/Delete.cpp | 19 + bc/file/Delete.hpp | 13 + bc/file/Error.cpp | 28 + bc/file/Error.hpp | 17 + bc/file/Exists.cpp | 34 + bc/file/Exists.hpp | 21 + bc/file/File.cpp | 371 ------- bc/file/File.hpp | 60 -- bc/file/Filesystem.cpp | 21 - bc/file/Filesystem.hpp | 74 +- bc/file/GetFileInfo.cpp | 61 ++ bc/file/GetFileInfo.hpp | 22 + bc/file/GetPos.cpp | 25 + bc/file/GetPos.hpp | 17 + bc/file/GetWorkingDirectory.cpp | 21 + bc/file/GetWorkingDirectory.hpp | 15 + bc/file/IsAbsolutePath.cpp | 19 + bc/file/IsAbsolutePath.hpp | 12 + bc/file/MakeAbsolutePath.cpp | 22 + bc/file/MakeAbsolutePath.hpp | 15 + bc/file/Move.cpp | 19 + bc/file/Move.hpp | 13 + bc/file/Open.cpp | 25 + bc/file/Open.hpp | 16 + bc/file/Path.hpp | 1 - bc/file/ProcessDirFast.cpp | 22 + bc/file/ProcessDirFast.hpp | 15 + bc/file/Read.cpp | 41 + bc/file/Read.hpp | 19 + bc/file/RemoveDirectory.cpp | 90 ++ bc/file/RemoveDirectory.hpp | 18 + bc/file/SetAttributes.cpp | 23 + bc/file/SetAttributes.hpp | 16 + bc/file/SetPos.cpp | 20 + bc/file/SetPos.hpp | 17 + bc/file/SetWorkingDirectory.cpp | 19 + bc/file/SetWorkingDirectory.hpp | 13 + bc/file/SimpleGlob.cpp | 72 ++ bc/file/SimpleGlob.hpp | 12 + bc/file/Types.hpp | 68 +- bc/file/Write.cpp | 57 + bc/file/Write.hpp | 22 + bc/file/path/Path.cpp | 225 ---- bc/file/path/Path.hpp | 51 - bc/file/path/Posix.cpp | 75 -- bc/file/path/Posix.hpp | 6 - bc/os/File.hpp | 90 +- bc/os/file/CloseFile.cpp | 6 + bc/os/file/CloseFile.hpp | 8 + bc/os/file/CopyFile.cpp | 12 + bc/os/file/CopyFile.hpp | 8 + bc/os/file/CreateDirectory.cpp | 12 + bc/os/file/CreateDirectory.hpp | 8 + bc/os/file/CreateFile.cpp | 67 ++ bc/os/file/CreateFile.hpp | 10 + bc/os/file/Defines.hpp | 10 + bc/os/file/DeleteFile.cpp | 12 + bc/os/file/DeleteFile.hpp | 8 + bc/os/file/DirectoryExists.cpp | 6 + bc/os/file/DirectoryExists.hpp | 8 + bc/os/file/FileExists.cpp | 6 + bc/os/file/FileExists.hpp | 8 + bc/os/file/FileList.cpp | 73 ++ bc/os/file/FileList.hpp | 17 + bc/os/file/GetCurrentDirectory.cpp | 12 + bc/os/file/GetCurrentDirectory.hpp | 8 + bc/os/file/GetDownloadFolder.cpp | 10 + bc/os/file/GetDownloadFolder.hpp | 6 + bc/os/file/GetFileAttributes.cpp | 17 + bc/os/file/GetFileAttributes.hpp | 8 + bc/os/file/GetFileSize.cpp | 8 + bc/os/file/GetFileSize.hpp | 8 + bc/os/file/MoveFile.cpp | 12 + bc/os/file/MoveFile.hpp | 8 + bc/os/file/ReadFile.cpp | 13 + bc/os/file/ReadFile.hpp | 9 + bc/os/file/RemoveDirectory.cpp | 12 + bc/os/file/RemoveDirectory.hpp | 8 + bc/os/file/RemoveDirectoryRecurse.cpp | 12 + bc/os/file/RemoveDirectoryRecurse.hpp | 8 + bc/os/file/SetCurrentDirectory.cpp | 13 + bc/os/file/SetCurrentDirectory.hpp | 8 + bc/os/file/SetFileAttributes.cpp | 13 + bc/os/file/SetFileAttributes.hpp | 8 + bc/os/file/SetFilePointer.cpp | 28 + bc/os/file/SetFilePointer.hpp | 9 + bc/os/file/Types.hpp | 40 + bc/os/file/WriteFile.cpp | 13 + bc/os/file/WriteFile.hpp | 9 + bc/system/file/Defines.hpp | 19 +- bc/system/file/Stacked.cpp | 360 +++---- bc/system/file/Stacked.hpp | 95 +- bc/system/file/System_File.cpp | 331 ++++++ bc/system/file/System_File.hpp | 67 +- bc/system/file/Types.hpp | 70 +- bc/system/file/posix/Stacked.cpp | 1167 +++++++++++--------- bc/system/file/posix/Support.hpp | 6 + bc/system/file/posix/System_File.cpp | 524 --------- bc/system/file/win/Stacked.cpp | 1416 +++++++++++++------------ bc/system/file/win/Support.hpp | 12 - bc/system/file/win/System_File.cpp | 144 --- bc/system/file/win/WinFile.cpp | 192 ---- bc/system/file/win/WinFile.hpp | 36 - 111 files changed, 3708 insertions(+), 3420 deletions(-) create mode 100644 bc/File.hpp create mode 100644 bc/file/Close.cpp create mode 100644 bc/file/Close.hpp create mode 100644 bc/file/Copy.cpp create mode 100644 bc/file/Copy.hpp create mode 100644 bc/file/CreateDirectory.cpp create mode 100644 bc/file/CreateDirectory.hpp create mode 100644 bc/file/Delete.cpp create mode 100644 bc/file/Delete.hpp create mode 100644 bc/file/Error.cpp create mode 100644 bc/file/Error.hpp create mode 100644 bc/file/Exists.cpp create mode 100644 bc/file/Exists.hpp delete mode 100644 bc/file/File.cpp delete mode 100644 bc/file/File.hpp delete mode 100644 bc/file/Filesystem.cpp create mode 100644 bc/file/GetFileInfo.cpp create mode 100644 bc/file/GetFileInfo.hpp create mode 100644 bc/file/GetPos.cpp create mode 100644 bc/file/GetPos.hpp create mode 100644 bc/file/GetWorkingDirectory.cpp create mode 100644 bc/file/GetWorkingDirectory.hpp create mode 100644 bc/file/IsAbsolutePath.cpp create mode 100644 bc/file/IsAbsolutePath.hpp create mode 100644 bc/file/MakeAbsolutePath.cpp create mode 100644 bc/file/MakeAbsolutePath.hpp create mode 100644 bc/file/Move.cpp create mode 100644 bc/file/Move.hpp create mode 100644 bc/file/Open.cpp create mode 100644 bc/file/Open.hpp delete mode 100644 bc/file/Path.hpp create mode 100644 bc/file/ProcessDirFast.cpp create mode 100644 bc/file/ProcessDirFast.hpp create mode 100644 bc/file/Read.cpp create mode 100644 bc/file/Read.hpp create mode 100644 bc/file/RemoveDirectory.cpp create mode 100644 bc/file/RemoveDirectory.hpp create mode 100644 bc/file/SetAttributes.cpp create mode 100644 bc/file/SetAttributes.hpp create mode 100644 bc/file/SetPos.cpp create mode 100644 bc/file/SetPos.hpp create mode 100644 bc/file/SetWorkingDirectory.cpp create mode 100644 bc/file/SetWorkingDirectory.hpp create mode 100644 bc/file/SimpleGlob.cpp create mode 100644 bc/file/SimpleGlob.hpp create mode 100644 bc/file/Write.cpp create mode 100644 bc/file/Write.hpp delete mode 100644 bc/file/path/Path.cpp delete mode 100644 bc/file/path/Path.hpp delete mode 100644 bc/file/path/Posix.cpp delete mode 100644 bc/file/path/Posix.hpp create mode 100644 bc/os/file/CloseFile.cpp create mode 100644 bc/os/file/CloseFile.hpp create mode 100644 bc/os/file/CopyFile.cpp create mode 100644 bc/os/file/CopyFile.hpp create mode 100644 bc/os/file/CreateDirectory.cpp create mode 100644 bc/os/file/CreateDirectory.hpp create mode 100644 bc/os/file/CreateFile.cpp create mode 100644 bc/os/file/CreateFile.hpp create mode 100644 bc/os/file/Defines.hpp create mode 100644 bc/os/file/DeleteFile.cpp create mode 100644 bc/os/file/DeleteFile.hpp create mode 100644 bc/os/file/DirectoryExists.cpp create mode 100644 bc/os/file/DirectoryExists.hpp create mode 100644 bc/os/file/FileExists.cpp create mode 100644 bc/os/file/FileExists.hpp create mode 100644 bc/os/file/FileList.cpp create mode 100644 bc/os/file/FileList.hpp create mode 100644 bc/os/file/GetCurrentDirectory.cpp create mode 100644 bc/os/file/GetCurrentDirectory.hpp create mode 100644 bc/os/file/GetDownloadFolder.cpp create mode 100644 bc/os/file/GetDownloadFolder.hpp create mode 100644 bc/os/file/GetFileAttributes.cpp create mode 100644 bc/os/file/GetFileAttributes.hpp create mode 100644 bc/os/file/GetFileSize.cpp create mode 100644 bc/os/file/GetFileSize.hpp create mode 100644 bc/os/file/MoveFile.cpp create mode 100644 bc/os/file/MoveFile.hpp create mode 100644 bc/os/file/ReadFile.cpp create mode 100644 bc/os/file/ReadFile.hpp create mode 100644 bc/os/file/RemoveDirectory.cpp create mode 100644 bc/os/file/RemoveDirectory.hpp create mode 100644 bc/os/file/RemoveDirectoryRecurse.cpp create mode 100644 bc/os/file/RemoveDirectoryRecurse.hpp create mode 100644 bc/os/file/SetCurrentDirectory.cpp create mode 100644 bc/os/file/SetCurrentDirectory.hpp create mode 100644 bc/os/file/SetFileAttributes.cpp create mode 100644 bc/os/file/SetFileAttributes.hpp create mode 100644 bc/os/file/SetFilePointer.cpp create mode 100644 bc/os/file/SetFilePointer.hpp create mode 100644 bc/os/file/Types.hpp create mode 100644 bc/os/file/WriteFile.cpp create mode 100644 bc/os/file/WriteFile.hpp create mode 100644 bc/system/file/System_File.cpp create mode 100644 bc/system/file/posix/Support.hpp delete mode 100644 bc/system/file/posix/System_File.cpp delete mode 100644 bc/system/file/win/Support.hpp delete mode 100644 bc/system/file/win/System_File.cpp delete mode 100644 bc/system/file/win/WinFile.cpp delete mode 100644 bc/system/file/win/WinFile.hpp diff --git a/bc/File.hpp b/bc/File.hpp new file mode 100644 index 0000000..331bd5b --- /dev/null +++ b/bc/File.hpp @@ -0,0 +1,29 @@ +#ifndef BC_FILE_HPP +#define BC_FILE_HPP + +#include "bc/file/Defines.hpp" +#include "bc/file/Error.hpp" + +#include "bc/file/SimpleGlob.hpp" + +#include "bc/file/Close.hpp" +#include "bc/file/Copy.hpp" +#include "bc/file/CreateDirectory.hpp" +#include "bc/file/Delete.hpp" +#include "bc/file/Exists.hpp" +#include "bc/file/GetFileInfo.hpp" +#include "bc/file/GetPos.hpp" +#include "bc/file/GetWorkingDirectory.hpp" +#include "bc/file/IsAbsolutePath.hpp" +#include "bc/file/MakeAbsolutePath.hpp" +#include "bc/file/Move.hpp" +#include "bc/file/Open.hpp" +#include "bc/file/ProcessDirFast.hpp" +#include "bc/file/Read.hpp" +#include "bc/file/RemoveDirectory.hpp" +#include "bc/file/SetAttributes.hpp" +#include "bc/file/SetPos.hpp" +#include "bc/file/SetWorkingDirectory.hpp" +#include "bc/file/Write.hpp" + +#endif diff --git a/bc/file/Close.cpp b/bc/file/Close.cpp new file mode 100644 index 0000000..3bdcddc --- /dev/null +++ b/bc/file/Close.cpp @@ -0,0 +1,19 @@ +#include "bc/file/Close.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool Close(StreamRecord* file) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, close); + parms.file = file; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->close(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Close.hpp b/bc/file/Close.hpp new file mode 100644 index 0000000..bc74ec1 --- /dev/null +++ b/bc/file/Close.hpp @@ -0,0 +1,16 @@ +#ifndef BC_FILE_CLOSE_HPP +#define BC_FILE_CLOSE_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// close +bool Close(StreamRecord* file); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Copy.cpp b/bc/file/Copy.cpp new file mode 100644 index 0000000..cfc2480 --- /dev/null +++ b/bc/file/Copy.cpp @@ -0,0 +1,20 @@ +#include "bc/file/Copy.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool Copy(const char* src, const char* dst, bool overwrite) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, copy); + parms.name = src; + parms.newname = dst; + parms.overwrite = overwrite; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->copy(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Copy.hpp b/bc/file/Copy.hpp new file mode 100644 index 0000000..d5111e0 --- /dev/null +++ b/bc/file/Copy.hpp @@ -0,0 +1,13 @@ +#ifndef BC_FILE_COPY_HPP +#define BC_FILE_COPY_HPP + +namespace Blizzard { +namespace File { + +// copy +bool Copy(const char* src, const char* dst, bool overwrite); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/CreateDirectory.cpp b/bc/file/CreateDirectory.cpp new file mode 100644 index 0000000..88aa89d --- /dev/null +++ b/bc/file/CreateDirectory.cpp @@ -0,0 +1,19 @@ +#include "bc/file/CreateDirectory.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool CreateDirectory(const char* name, bool recurse) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, mkdir); + parms.name = name; + parms.recurse = recurse; + parms.set_acl = 0; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->mkdir(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/CreateDirectory.hpp b/bc/file/CreateDirectory.hpp new file mode 100644 index 0000000..fdf177a --- /dev/null +++ b/bc/file/CreateDirectory.hpp @@ -0,0 +1,15 @@ +#ifndef BC_FILE_CREATE_DIRECTORY_HPP +#define BC_FILE_CREATE_DIRECTORY_HPP + +#include + +namespace Blizzard { +namespace File { + +// mkdir +bool CreateDirectory(const char* name, bool recurse); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Defines.hpp b/bc/file/Defines.hpp index ca79690..167a02e 100644 --- a/bc/file/Defines.hpp +++ b/bc/file/Defines.hpp @@ -1,22 +1,9 @@ #ifndef BC_FILE_DEFINES_HPP #define BC_FILE_DEFINES_HPP -// How many bytes to when translating stacked filesystem names to large, native file paths #define BC_FILE_MAX_PATH 1024 -// File open/creation flags. -// See Blizzard::File::Open() for more info -#define BC_FILE_OPEN_READ 0x0001 -#define BC_FILE_OPEN_WRITE 0x0002 -#define BC_FILE_OPEN_SHARE_READ 0x0004 -#define BC_FILE_OPEN_SHARE_WRITE 0x0008 -#define BC_FILE_OPEN_TRUNCATE 0x0100 -#define BC_FILE_OPEN_ALWAYS 0x0200 -#define BC_FILE_OPEN_CREATE 0x0400 -#define BC_FILE_OPEN_MUST_NOT_EXIST 0x0800 -#define BC_FILE_OPEN_MUST_EXIST 0x1000 - -// File attribute flags +// file attribute flags #define BC_FILE_ATTRIBUTE_READONLY 0x01 #define BC_FILE_ATTRIBUTE_HIDDEN 0x02 #define BC_FILE_ATTRIBUTE_SYSTEM 0x04 @@ -25,28 +12,28 @@ #define BC_FILE_ATTRIBUTE_NORMAL 0x20 #define BC_FILE_ATTRIBUTE_DIRECTORY 0x40 -// File error codes +// file error codes #define BC_FILE_ERROR_GENERIC_FAILURE 0 #define BC_FILE_ERROR_ACCESS_DENIED 1 #define BC_FILE_ERROR_FILE_NOT_FOUND 2 #define BC_FILE_ERROR_BAD_FILE 3 #define BC_FILE_ERROR_BUSY 4 -#define BC_FILE_ERROR_OOM 5 -#define BC_FILE_ERROR_INVALID_HANDLE 6 +#define BC_FILE_ERROR_READ 5 +#define BC_FILE_ERROR_WRITE 6 #define BC_FILE_ERROR_END_OF_FILE 7 #define BC_FILE_ERROR_INVALID_ARGUMENT 8 #define BC_FILE_ERROR_UNIMPLEMENTED 9 #define BC_FILE_ERROR_NO_SPACE_ON_DEVICE 11 -// File SetPos whence +// file SetPos whence #define BC_FILE_SEEK_START 0 #define BC_FILE_SEEK_CURRENT 1 #define BC_FILE_SEEK_END 2 -// Error handling utilities +// error handling utilities #define BC_FILE_SET_ERROR(errorcode) ::Blizzard::File::SetLastError(static_cast(errorcode)) #define BC_FILE_SET_ERROR_MSG(errorcode, pfmt, ...) \ ::Blizzard::File::SetLastError(errorcode); \ - ::Blizzard::File::AddToLastErrorStack(errorcode, ::Blizzard::String::QuickFormat<1024>(pfmt, ##__VA_ARGS__).Str(), 1) + ::Blizzard::File::AddToLastErrorStack(errorcode, ::Blizzard::String::QuickFormat<1024>(pfmt, ##__VA_ARGS__).ToString(), 1) #endif diff --git a/bc/file/Delete.cpp b/bc/file/Delete.cpp new file mode 100644 index 0000000..cc5743c --- /dev/null +++ b/bc/file/Delete.cpp @@ -0,0 +1,19 @@ +#include "bc/file/Delete.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool Delete(const char* name) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, unlink); + parms.name = name; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->unlink(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard \ No newline at end of file diff --git a/bc/file/Delete.hpp b/bc/file/Delete.hpp new file mode 100644 index 0000000..d1e97c8 --- /dev/null +++ b/bc/file/Delete.hpp @@ -0,0 +1,13 @@ +#ifndef BC_FILE_DELETE_HPP +#define BC_FILE_DELETE_HPP + +namespace Blizzard { +namespace File { + +// unlink +bool Delete(const char* name); + +} // namespace File +} // namespace Blizzard + +#endif \ No newline at end of file diff --git a/bc/file/Error.cpp b/bc/file/Error.cpp new file mode 100644 index 0000000..a1f99d6 --- /dev/null +++ b/bc/file/Error.cpp @@ -0,0 +1,28 @@ +#include "bc/file/Error.hpp" + +#include +#include + +namespace Blizzard { +namespace File { + +// Functions + +void SetLastError(int32_t errorcode) { + // TODO +} + +void AddToLastErrorStack(int32_t errorcode, const char* msg, int32_t a3) { + // TODO: use proper logging + + char empty[] = ""; + + if (!msg) { + msg = empty; + } + + fprintf(stderr, "[bc/file] %" PRIi32 ": '%s'\n", errorcode, msg); +} + +} +} diff --git a/bc/file/Error.hpp b/bc/file/Error.hpp new file mode 100644 index 0000000..0755c30 --- /dev/null +++ b/bc/file/Error.hpp @@ -0,0 +1,17 @@ +#ifndef BC_FILE_ERROR_HPP +#define BC_FILE_ERROR_HPP + +#include + +namespace Blizzard { +namespace File { + +// Functions +void SetLastError(int32_t errorcode); + +void AddToLastErrorStack(int32_t errorcode, const char* msg, int32_t param_3); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Exists.cpp b/bc/file/Exists.cpp new file mode 100644 index 0000000..9ae99b3 --- /dev/null +++ b/bc/file/Exists.cpp @@ -0,0 +1,34 @@ +#include "bc/file/Exists.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +uint32_t Exists(const char* name) { + System_File::Stacked::FileParms parms; + + parms.op = offsetof(Filesystem, exists); + parms.name = name; + parms.info = &parms.noinfo; + + auto fs = System_File::Stacked::s_manager; + + if (name && fs && fs->exists(fs, &parms)) { + return parms.info->filetype; + } else { + return 0; + } +} + +bool IsFile(const char* name) { + return Exists(name) == 1; +} + +bool IsDirectory(const char* name) { + return Exists(name) == 2; +} + +} // namespace File +} // namespace Blizzard + + diff --git a/bc/file/Exists.hpp b/bc/file/Exists.hpp new file mode 100644 index 0000000..36775de --- /dev/null +++ b/bc/file/Exists.hpp @@ -0,0 +1,21 @@ +#ifndef BC_FILE_EXISTS_HPP +#define BC_FILE_EXISTS_HPP + +#include + +namespace Blizzard { +namespace File { + +// exists +uint32_t Exists(const char* name); + +// exists +bool IsFile(const char* name); + +// exists +bool IsDirectory(const char* name); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/File.cpp b/bc/file/File.cpp deleted file mode 100644 index cf6664f..0000000 --- a/bc/file/File.cpp +++ /dev/null @@ -1,371 +0,0 @@ -#include "bc/file/File.hpp" -#include "bc/file/Filesystem.hpp" -#include "bc/system/file/Stacked.hpp" - -#include -#include - -namespace Blizzard { -namespace File { - -// Functions - -void SetLastError(int32_t errorcode) { - // TODO -} - -void AddToLastErrorStack(int32_t errorcode, const char* msg, int32_t a3) { - // TODO: use proper logging - - char empty[] = ""; - - if (!msg) { - msg = empty; - } - - fprintf(stderr, "[bc/file] %" PRIi32 ": '%s'\n", errorcode, msg); -} - -// In BlizzardCore parlance, a file only exists if its path points to a regular file. (not a directory) -// i.e. directories are not considered to be file objects. -bool Exists(const char* path) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - FileInfo info = {}; - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - parms.info = &info; - - auto status = manager->Do(Filesystem::Call::Exists, &parms); - - return status; -} - -bool Flush(StreamRecord* stream) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.stream = stream; - - auto status = manager->Do(Filesystem::Call::Flush, &parms); - - return status; -} - -// Alias of Exists. -bool IsFile(const char* path) { - return Exists(path); -} - -// Calls Exists internally, only checking whether it has the directory attribute instead of normal attribute -bool IsDirectory(const char* path) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - FileInfo info = {}; - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - parms.info = &info; - - auto status = manager->Do(Filesystem::Call::Exists, &parms); - - return info.attributes & BC_FILE_ATTRIBUTE_DIRECTORY; -} - -// Delete a file. -bool Delete(const char* path) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - - auto status = manager->Do(Filesystem::Call::Delete, &parms); - return status; -} - -bool IsAbsolutePath(const char* path) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - return manager->Do(Filesystem::Call::IsAbsolutePath, &parms); -} - -// Get file information about path, writing information to the passed info pointer. -bool GetFileInfo(const char* path, FileInfo* info) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - parms.info = info; - - auto status = manager->Do(Filesystem::Call::GetFileInfo, &parms); - - return status; -} - -// Get file information from a StreamRecord, writing information to the passed info pointer. -bool GetFileInfo(StreamRecord* stream, FileInfo* info) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.stream = stream; - parms.info = info; - - return manager->Do(Filesystem::Call::GetFileInfo, &parms); -} - -bool GetWorkingDirectory(char* path, size_t capacity) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.directory = path; - parms.directorySize = capacity; - - return manager->Do(Filesystem::Call::GetWorkingDirectory, &parms); -} - -bool SetWorkingDirectory(const char* path) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - - return manager->Do(Filesystem::Call::SetWorkingDirectory, &parms); -} - -// Get file information from a stream record, returning a file info pointer owned by StreamRecord -// The FileInfo ptr returned is invalidated after a call to File::Close(stream) -FileInfo* GetFileInfo(StreamRecord* stream) { - static FileInfo s_noinfo = {}; - - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return &s_noinfo; - } - - System_File::Stacked::FileParms parms = {}; - parms.stream = stream; - parms.info = nullptr; - - auto status = manager->Do(Filesystem::Call::GetFileInfo, &parms); - - if (status) { - return parms.info; - } - - return &s_noinfo; -} - -bool MakeAbsolutePath(const char* rel, char* result, int32_t capacity, bool unkflag) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.filename = rel; - parms.flag = unkflag; - parms.directory = result; - parms.directorySize = capacity; - - return manager->Do(Filesystem::Call::MakeAbsolutePath, &parms); -} - -// Open a filename according to flags (see Defines.hpp) -// if successful, will return true and referenced file object will be set -bool Open(const char* filename, uint32_t flags, StreamRecord*& file) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.filename = filename; - parms.flag = flags; - - auto status = manager->Do(Filesystem::Call::Open, &parms); - - if (status) { - file = parms.stream; - } - - return status; -} - -bool SetAttributes(const char* filename, uint32_t attributes) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = filename; - parms.flag = static_cast(attributes); - parms.mode = File::Mode::setperms; - - return manager->Do(Filesystem::Call::SetAttributes, &parms); -} - -bool Close(StreamRecord* stream) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.stream = stream; - - auto status = manager->Do(Filesystem::Call::Close, &parms); - return status; -} - -bool Copy(const char* source, const char* destination, bool overwrite) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = source; - parms.destination = destination; - - return manager->Do(Filesystem::Call::Copy, &parms); -} - -bool CreateDirectory(const char* path, bool recursive) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - parms.filename = path; - parms.flag = recursive; - - return manager->Do(Filesystem::Call::CreateDirectory, &parms); -} - -bool ProcessDirFast(const char* path, void* param, ProcessDirCallback callback, bool unkflag) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.filename = path; - parms.param = param; - parms.flag = unkflag; - parms.callback = callback; - - return manager->Do(Filesystem::Call::ProcessDirFast, &parms); -} - -bool Read(StreamRecord* file, void* buffer, size_t bytesToRead, size_t* bytesRead) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.stream = file; - parms.param = buffer; - parms.size = bytesToRead; - - auto status = manager->Do(Filesystem::Call::Read, &parms); - - if (bytesRead) { - *bytesRead = static_cast(parms.size); - } - - return status; -} - -bool Write(StreamRecord* file, void* buffer, size_t bytesToWrite, size_t* bytesWritten) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.stream = file; - parms.param = buffer; - parms.size = bytesToWrite; - - auto status = manager->Do(Filesystem::Call::Write, &parms); - - if (bytesWritten) { - *bytesWritten = static_cast(parms.size); - } - - return status; -} - -bool SetPos(StreamRecord* file, int64_t pos, int32_t whence) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.stream = file; - parms.position = pos; - parms.whence = whence; - - return manager->Do(Filesystem::Call::SetPos, &parms); -} - -bool GetPos(StreamRecord* file, int64_t& pos) { - auto manager = System_File::Stacked::Manager(); - if (!manager) { - return false; - } - - System_File::Stacked::FileParms parms = {}; - - parms.stream = file; - parms.position = pos; - - auto status = manager->Do(Filesystem::Call::GetPos, &parms); - if (status) { - pos = parms.position; - } - return status; -} - -} // namespace File -} // namespace Blizzard diff --git a/bc/file/File.hpp b/bc/file/File.hpp deleted file mode 100644 index 3545107..0000000 --- a/bc/file/File.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef BC_FILE_FILE_HPP -#define BC_FILE_FILE_HPP - -#include "bc/Time.hpp" -#include "bc/file/Defines.hpp" -#include "bc/file/Types.hpp" -#include "bc/system/file/System_File.hpp" - -namespace Blizzard { -namespace File { - -// Functions -void SetLastError(int32_t errorcode); - -void AddToLastErrorStack(int32_t errorcode, const char* msg, int32_t param_3); - -bool Copy(const char* source, const char* destination, bool overwrite); - -bool CreateDirectory(const char* path, bool recursive); - -bool Delete(const char* path); - -bool Exists(const char* path); - -bool Flush(StreamRecord* stream); - -bool IsFile(const char* path); - -bool IsDirectory(const char* path); - -bool IsAbsolutePath(const char* path); - -bool GetFileInfo(const char* path, FileInfo* info); -bool GetFileInfo(StreamRecord* stream, FileInfo* info); -FileInfo* GetFileInfo(StreamRecord* stream); - -bool GetWorkingDirectory(char* path, size_t capacity); - -bool SetWorkingDirectory(const char* path); - -bool MakeAbsolutePath(const char* rel, char* result, int32_t capacity, bool unkflag); - -bool Open(const char* filename, uint32_t flags, StreamRecord*& stream); - -bool Close(StreamRecord* stream); - -bool SetAttributes(const char* filename, uint32_t attributes); - -bool Read(StreamRecord* stream, void* buffer, size_t bytesToRead, size_t* bytesRead); - -bool Write(StreamRecord* stream, void* buffer, size_t bytesToWrite, size_t* bytesWritten); - -bool SetPos(StreamRecord* stream, int64_t pos, int32_t whence); - -bool GetPos(StreamRecord* stream, int64_t& pos); - -} // namespace File -} // namespace Blizzard - -#endif diff --git a/bc/file/Filesystem.cpp b/bc/file/Filesystem.cpp deleted file mode 100644 index e4334ba..0000000 --- a/bc/file/Filesystem.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "bc/file/Filesystem.hpp" -#include - -namespace Blizzard { -namespace File { - -bool Filesystem::Do(Filesystem::Call fscall, System_File::Stacked::FileParms* parms) { - uint32_t callindex = static_cast(fscall); - // Mark byte offset of Call function. - // Allows the filesystem stack to resume normal activity in case of a fallback - parms->offset = offsetof(Filesystem, funcs) + (callindex * sizeof(uintptr_t)); - // Get filesystem call from array - - auto func = reinterpret_cast(this->funcs[callindex]); - // Do call - return func(this, parms); -} - -} // namespace File -} // namespace Blizzard - diff --git a/bc/file/Filesystem.hpp b/bc/file/Filesystem.hpp index 008df43..613f545 100644 --- a/bc/file/Filesystem.hpp +++ b/bc/file/Filesystem.hpp @@ -7,57 +7,49 @@ namespace Blizzard { namespace File { +class Filesystem; + +typedef bool (*Operation)(Filesystem* fs, System_File::Stacked::FileParms* parms); + // Filesystem describes the layout of a virtual filesystem. // It is linked to other Filesystems in a list structure. class Filesystem { public: - enum class Call : uint32_t { - SetWorkingDirectory, - Close, - Create, - GetWorkingDirectory, - ProcessDirFast, - Exists, - Flush, - GetFileInfo, - GetFreeSpace, - GetPos, - GetRootChars, - IsAbsolutePath, - IsReadOnly, - MakeAbsolutePath, - CreateDirectory, - Move, - Copy, - Open, - Read, - ReadP, - RemoveDirectory, - SetCacheMode, - SetEOF, - SetAttributes, - SetPos, - Delete, - Write, - WriteP, - Shutdown, - EndOfCalls - }; - - // I hate C++ enums - static constexpr size_t s_numCalls = static_cast(Call::EndOfCalls); - - // One signature for all filesystem calls - typedef bool (*CallFunc)(Filesystem*, System_File::Stacked::FileParms*); - // The source filesystem (where it was copied from) // You can tell if base == this, that it is the original stack and not part of the filesystem manager Filesystem* base; // The next filesystem (towards lower filesystems) Filesystem* next; - CallFunc funcs[s_numCalls]; - bool Do(Call call, System_File::Stacked::FileParms* parms); + Operation cd; + Operation close; + Operation create; + Operation cwd; + Operation dirwalk; + Operation exists; + Operation flush; + Operation getfileinfo; + Operation getfreespace; + Operation getpos; + Operation getrootchars; + Operation isabspath; + Operation isreadonly; + Operation makeabspath; + Operation mkdir; + Operation move; + Operation copy; + Operation open; + Operation read; + Operation readp; + Operation rmdir; + Operation setcachemode; + Operation seteof; + Operation setfileinfo; + Operation setpos; + Operation unlink; + Operation write; + Operation writep; + Operation shutdown; }; } // namespace File diff --git a/bc/file/GetFileInfo.cpp b/bc/file/GetFileInfo.cpp new file mode 100644 index 0000000..5ec67bb --- /dev/null +++ b/bc/file/GetFileInfo.cpp @@ -0,0 +1,61 @@ +#include "bc/file/GetFileInfo.hpp" +#include "bc/file/Types.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool GetFileInfo(const char* name, FileInfo* info) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, getfileinfo); + parms.name = name; + parms.file = nullptr; + parms.setinfo = 0; + parms.getinfo = 0xFFFFFFFF; + parms.info = &parms.noinfo; + + auto fs = System_File::Stacked::s_manager; + if (fs && fs->getfileinfo(fs, &parms)) { + *info = *parms.info; + return true; + } + + return false; +} + +FileInfo* GetFileInfo(StreamRecord* file) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, getfileinfo); + parms.name = nullptr; + parms.file = file; + parms.setinfo = 0; + parms.getinfo = 0xFFFFFFFF; + parms.info = nullptr; + + auto fs = System_File::Stacked::s_manager; + if (fs && fs->getfileinfo(fs, &parms)) { + return parms.info; + } + + static FileInfo s_noinfo = {}; + return &s_noinfo; +} + +bool GetFileInfo(StreamRecord* file, FileInfo* info) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, getfileinfo); + parms.name = nullptr; + parms.file = file; + parms.info = nullptr; + + auto fs = System_File::Stacked::s_manager; + if (fs && fs->getfileinfo(fs, &parms)) { + *info = *parms.info; + return true; + } + + return false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/GetFileInfo.hpp b/bc/file/GetFileInfo.hpp new file mode 100644 index 0000000..d0b508c --- /dev/null +++ b/bc/file/GetFileInfo.hpp @@ -0,0 +1,22 @@ +#ifndef BC_FILE_GET_FILE_INFO_HPP +#define BC_FILE_GET_FILE_INFO_HPP + +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// getfileinfo +bool GetFileInfo(const char* name, FileInfo* info); + +// getfileinfo +FileInfo* GetFileInfo(StreamRecord* file); + +// getfileinfo +bool GetFileInfo(StreamRecord* file, FileInfo* info); + +} // namespace File +} // namespace Blizzard + + +#endif diff --git a/bc/file/GetPos.cpp b/bc/file/GetPos.cpp new file mode 100644 index 0000000..5eb92a2 --- /dev/null +++ b/bc/file/GetPos.cpp @@ -0,0 +1,25 @@ +#include "bc/file/GetPos.hpp" +#include "bc/file/Defines.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool GetPos(StreamRecord* file, int64_t& offset) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, getpos); + parms.file = file; + parms.offset = offset; + parms.whence = BC_FILE_SEEK_CURRENT; + + auto fs = System_File::Stacked::s_manager; + if (!fs || !fs->setpos(fs, &parms)) { + return false; + } + offset = parms.offset; + return true; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/GetPos.hpp b/bc/file/GetPos.hpp new file mode 100644 index 0000000..741e3af --- /dev/null +++ b/bc/file/GetPos.hpp @@ -0,0 +1,17 @@ +#ifndef BC_FILE_GET_POS_HPP +#define BC_FILE_GET_POS_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// getpos +bool GetPos(StreamRecord* file, int64_t& offset); + +} // namespace File +} // namespace Blizzard + + +#endif diff --git a/bc/file/GetWorkingDirectory.cpp b/bc/file/GetWorkingDirectory.cpp new file mode 100644 index 0000000..9e58415 --- /dev/null +++ b/bc/file/GetWorkingDirectory.cpp @@ -0,0 +1,21 @@ +#include "bc/file/GetWorkingDirectory.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool GetWorkingDirectory(char* buffer, int32_t buffersize) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, cwd); + parms.buffer = buffer; + parms.buffersize = buffersize; + + *buffer = '\0'; + auto fs = System_File::Stacked::s_manager; + return fs ? fs->cwd(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/GetWorkingDirectory.hpp b/bc/file/GetWorkingDirectory.hpp new file mode 100644 index 0000000..51f41e7 --- /dev/null +++ b/bc/file/GetWorkingDirectory.hpp @@ -0,0 +1,15 @@ +#ifndef BC_FILE_GET_WORKING_DIRECTORY_HPP +#define BC_FILE_GET_WORKING_DIRECTORY_HPP + +#include + +namespace Blizzard { +namespace File { + +// cwd +bool GetWorkingDirectory(char* buffer, int32_t buffersize); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/IsAbsolutePath.cpp b/bc/file/IsAbsolutePath.cpp new file mode 100644 index 0000000..16f64e1 --- /dev/null +++ b/bc/file/IsAbsolutePath.cpp @@ -0,0 +1,19 @@ +#include "bc/file/IsAbsolutePath.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool IsAbsolutePath(const char* name) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, isabspath); + parms.name = name; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->isabspath(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard \ No newline at end of file diff --git a/bc/file/IsAbsolutePath.hpp b/bc/file/IsAbsolutePath.hpp new file mode 100644 index 0000000..652ef66 --- /dev/null +++ b/bc/file/IsAbsolutePath.hpp @@ -0,0 +1,12 @@ +#ifndef BC_FILE_IS_ABSOLUTE_PATH_HPP +#define BC_FILE_IS_ABSOLUTE_PATH_HPP + +namespace Blizzard { +namespace File { + +bool IsAbsolutePath(const char* name); + +} // namespace File +} // namespace Blizzard + +#endif \ No newline at end of file diff --git a/bc/file/MakeAbsolutePath.cpp b/bc/file/MakeAbsolutePath.cpp new file mode 100644 index 0000000..eb5a9ec --- /dev/null +++ b/bc/file/MakeAbsolutePath.cpp @@ -0,0 +1,22 @@ +#include "bc/file/MakeAbsolutePath.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool MakeAbsolutePath(const char* name, char* buffer, int32_t buffersize, bool canonicalize) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, makeabspath); + parms.name = name; + parms.buffer = buffer; + parms.buffersize = buffersize; + parms.canonicalize = canonicalize; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->makeabspath(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard \ No newline at end of file diff --git a/bc/file/MakeAbsolutePath.hpp b/bc/file/MakeAbsolutePath.hpp new file mode 100644 index 0000000..f3cb0f1 --- /dev/null +++ b/bc/file/MakeAbsolutePath.hpp @@ -0,0 +1,15 @@ +#ifndef BC_FILE_MAKE_ABSOLUTE_PATH_HPP +#define BC_FILE_MAKE_ABSOLUTE_PATH_HPP + +#include + +namespace Blizzard { +namespace File { + +// makeabspath +bool MakeAbsolutePath(const char* name, char* buffer, int32_t buffersize, bool canonicalize); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Move.cpp b/bc/file/Move.cpp new file mode 100644 index 0000000..9f9ecee --- /dev/null +++ b/bc/file/Move.cpp @@ -0,0 +1,19 @@ +#include "bc/file/Move.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool Move(const char* src, const char* dst) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, move); + parms.name = src; + parms.newname = dst; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->move(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Move.hpp b/bc/file/Move.hpp new file mode 100644 index 0000000..1d894eb --- /dev/null +++ b/bc/file/Move.hpp @@ -0,0 +1,13 @@ +#ifndef BC_FILE_MOVE_HPP +#define BC_FILE_MOVE_HPP + +namespace Blizzard { +namespace File { + +// move +bool Move(const char* src, const char* dst); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Open.cpp b/bc/file/Open.cpp new file mode 100644 index 0000000..af62d15 --- /dev/null +++ b/bc/file/Open.cpp @@ -0,0 +1,25 @@ +#include "bc/file/Open.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool Open(const char* name, int32_t mode, StreamRecord*& file) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, open); + parms.name = name; + parms.mode = mode; + parms.file = nullptr; + + auto fs = System_File::Stacked::s_manager; + if (!fs || !fs->open(fs, &parms)) { + return false; + } + file = parms.file; + return true; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Open.hpp b/bc/file/Open.hpp new file mode 100644 index 0000000..62ff593 --- /dev/null +++ b/bc/file/Open.hpp @@ -0,0 +1,16 @@ +#ifndef BC_FILE_OPEN_HPP +#define BC_FILE_OPEN_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// open +bool Open(const char* name, int32_t mode, StreamRecord*& file); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Path.hpp b/bc/file/Path.hpp deleted file mode 100644 index f23a5c2..0000000 --- a/bc/file/Path.hpp +++ /dev/null @@ -1 +0,0 @@ -#include "bc/file/path/Path.hpp" diff --git a/bc/file/ProcessDirFast.cpp b/bc/file/ProcessDirFast.cpp new file mode 100644 index 0000000..3f87b3c --- /dev/null +++ b/bc/file/ProcessDirFast.cpp @@ -0,0 +1,22 @@ +#include "bc/file/ProcessDirFast.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool ProcessDirFast(const char* name, void* param, ProcessDirCallback callback, bool nofollow) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, dirwalk); + parms.name = name; + parms.dirwalkparam = param; + parms.dirwalkcallback = callback; + // parms.unk88 = false; + parms.mode = nofollow; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->dirwalk(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/ProcessDirFast.hpp b/bc/file/ProcessDirFast.hpp new file mode 100644 index 0000000..46d580d --- /dev/null +++ b/bc/file/ProcessDirFast.hpp @@ -0,0 +1,15 @@ +#ifndef BC_FILE_PROCESS_DIR_FAST_HPP +#define BC_FILE_PROCESS_DIR_FAST_HPP + +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// dirwalk +bool ProcessDirFast(const char* name, void* param, ProcessDirCallback callback, bool flag); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/Read.cpp b/bc/file/Read.cpp new file mode 100644 index 0000000..3dcf6f0 --- /dev/null +++ b/bc/file/Read.cpp @@ -0,0 +1,41 @@ +#include "bc/file/Read.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool Read(StreamRecord* file, void* buffer, int32_t* count) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, read); + parms.file = file; + parms.data = buffer; + parms.count = *count; + *count = 0; + + auto fs = System_File::Stacked::s_manager; + if (!fs || !fs->read(fs, &parms)) { + return false; + } + *count = parms.count; + return true; +} + +bool Read(StreamRecord* file, void* buffer, int64_t offset, int32_t* count) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, readp); + parms.file = file; + parms.data = buffer; + parms.offset = offset; + parms.count = *count; + *count = 0; + + auto fs = System_File::Stacked::s_manager; + if (!fs || !fs->readp(fs, &parms)) { + return false; + } + *count = parms.count; + return true; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Read.hpp b/bc/file/Read.hpp new file mode 100644 index 0000000..169ee5d --- /dev/null +++ b/bc/file/Read.hpp @@ -0,0 +1,19 @@ +#ifndef BC_FILE_READ_HPP +#define BC_FILE_READ_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// read +bool Read(StreamRecord* file, void* buffer, int32_t* count); + +// readp +bool Read(StreamRecord* file, void* buffer, int64_t offset, int32_t* count); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/RemoveDirectory.cpp b/bc/file/RemoveDirectory.cpp new file mode 100644 index 0000000..2268a00 --- /dev/null +++ b/bc/file/RemoveDirectory.cpp @@ -0,0 +1,90 @@ +#include "bc/file/RemoveDirectory.hpp" +#include "bc/file/ProcessDirFast.hpp" +#include "bc/file/Defines.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/file/SetAttributes.hpp" +#include "bc/file/Exists.hpp" +#include "bc/file/Delete.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/String.hpp" + +namespace Blizzard { +namespace File { + +bool RemoveDirectory(const char* name) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, rmdir); + parms.name = name; + parms.recurse = false; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->rmdir(fs, &parms) : false; +} + +bool RemoveDirectoryAndContents(const char* name, bool noreadonly) { + class Internal { + public: + struct RemoveDirectoryRecurseData { + bool unk00; + bool noreadonly; + }; + + static bool RemoveDirectoryRecurse(RemoveDirectoryRecurseData* data, const char* name, bool noreadonly) { + data->unk00 = true; + data->noreadonly = noreadonly; + + return File::ProcessDirFast(name, static_cast(data), Callback, false) && data->unk00; + } + + static bool Callback(const ProcessDirParms& dirwalkparms) { + char name[BC_FILE_MAX_PATH]; + + auto rmdirdata = reinterpret_cast(dirwalkparms.param); + + bool removeditem = false; + + if (dirwalkparms.isdir) { + String::Format(name, BC_FILE_MAX_PATH, "%s%s/", dirwalkparms.dir, dirwalkparms.item); + removeditem = File::RemoveDirectoryAndContents(name, true); + } else { + String::Format(name, BC_FILE_MAX_PATH, "%s%s", dirwalkparms.dir, dirwalkparms.item); + + if (rmdirdata->noreadonly) { + File::SetAttributes(name, BC_FILE_ATTRIBUTE_NORMAL); + } + + // TODO: ??? + // if (sub_44EFB0(name)) { + // return true; + // } + + removeditem = File::Delete(name); + } + + if (!removeditem) { + rmdirdata->unk00 = false; + } + + return true; + } + }; + + if (File::Exists(name) != 2) { + return false; + } + + char path[BC_FILE_MAX_PATH]; + String::Copy(path, name, BC_FILE_MAX_PATH); + String::ForceTrailingSeparator(path, BC_FILE_MAX_PATH, '\0'); + + Internal::RemoveDirectoryRecurseData rmdirdata; + if (!Internal::RemoveDirectoryRecurse(&rmdirdata, path, noreadonly)) { + return false; + } + + return File::RemoveDirectory(name); +} + +} // namespace File +} // namespace Blizzard + diff --git a/bc/file/RemoveDirectory.hpp b/bc/file/RemoveDirectory.hpp new file mode 100644 index 0000000..27c5857 --- /dev/null +++ b/bc/file/RemoveDirectory.hpp @@ -0,0 +1,18 @@ +#ifndef BC_FILE_REMOVE_DIRECTORY_HPP +#define BC_FILE_REMOVE_DIRECTORY_HPP + +#include + +namespace Blizzard { +namespace File { + +// rmdir +bool RemoveDirectory(const char* name); + +// rmdir (-r) +bool RemoveDirectoryAndContents(const char* name, bool a2); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/SetAttributes.cpp b/bc/file/SetAttributes.cpp new file mode 100644 index 0000000..0359892 --- /dev/null +++ b/bc/file/SetAttributes.cpp @@ -0,0 +1,23 @@ +#include "bc/file/SetAttributes.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/file/Defines.hpp" +#include "bc/system/file/Stacked.hpp" +#include + +namespace Blizzard { +namespace File { + +bool SetAttributes(const char* name, int32_t attributes) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, setfileinfo); + parms.name = name; + parms.setinfo = BC_SYSTEM_FILE_INFO_ATTRIBUTES; + parms.noinfo.attributes = attributes; + parms.info = &parms.noinfo; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->setfileinfo(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/SetAttributes.hpp b/bc/file/SetAttributes.hpp new file mode 100644 index 0000000..1315cc3 --- /dev/null +++ b/bc/file/SetAttributes.hpp @@ -0,0 +1,16 @@ +#ifndef BC_FILE_SET_ATTRIBUTES_HPP +#define BC_FILE_SET_ATTRIBUTES_HPP + +#include + +namespace Blizzard { +namespace File { + +// setfileinfo +bool SetAttributes(const char* name, int32_t attributes); + +} // namespace File +} // namespace Blizzard + + +#endif diff --git a/bc/file/SetPos.cpp b/bc/file/SetPos.cpp new file mode 100644 index 0000000..294bcc5 --- /dev/null +++ b/bc/file/SetPos.cpp @@ -0,0 +1,20 @@ +#include "bc/file/SetPos.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" + +namespace Blizzard { +namespace File { + +bool SetPos(StreamRecord* file, int64_t offset, int32_t whence) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, setpos); + parms.file = file; + parms.offset = offset; + parms.whence = whence; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->setpos(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/SetPos.hpp b/bc/file/SetPos.hpp new file mode 100644 index 0000000..cf83eae --- /dev/null +++ b/bc/file/SetPos.hpp @@ -0,0 +1,17 @@ +#ifndef BC_FILE_SET_POS_HPP +#define BC_FILE_SET_POS_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// setpos +bool SetPos(StreamRecord* file, int64_t offset, int32_t whence); + +} // namespace File +} // namespace Blizzard + + +#endif diff --git a/bc/file/SetWorkingDirectory.cpp b/bc/file/SetWorkingDirectory.cpp new file mode 100644 index 0000000..11e5b10 --- /dev/null +++ b/bc/file/SetWorkingDirectory.cpp @@ -0,0 +1,19 @@ +#include "bc/file/SetWorkingDirectory.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool SetWorkingDirectory(const char* name) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, cd); + parms.name = name; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->cd(fs, &parms) : false; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/SetWorkingDirectory.hpp b/bc/file/SetWorkingDirectory.hpp new file mode 100644 index 0000000..e631e7e --- /dev/null +++ b/bc/file/SetWorkingDirectory.hpp @@ -0,0 +1,13 @@ +#ifndef BC_FILE_SET_WORKING_DIRECTORY_HPP +#define BC_FILE_SET_WORKING_DIRECTORY_HPP + +namespace Blizzard { +namespace File { + +// cd +bool SetWorkingDirectory(const char* name); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/SimpleGlob.cpp b/bc/file/SimpleGlob.cpp new file mode 100644 index 0000000..c8455a2 --- /dev/null +++ b/bc/file/SimpleGlob.cpp @@ -0,0 +1,72 @@ +#include "bc/file/SimpleGlob.hpp" + +#include +#include + +namespace Blizzard { +namespace File { + +bool SimpleGlob(const char* name, const char* pattern) { + auto p = pattern; + auto n = name; + + while (true) { + if (*p == '\0') { + return !*n; + } + if (!*n && *p != '*') { + return false; + } + if (*p == '*') { + break; + } + if (*p == '?') { + if (!*n) { + return false; + } + } else { + if (*p == '\\' && p[1]) { + p++; + } + if (*p != *n) { + if (tolower(static_cast(*n)) != tolower(static_cast(*p))) { + return false; + } + } + } + + n++; + p++; + } + + while (*p == '*') { + p++; + } + + if (*p) { + if (*p != '?' && *p != '\\') { + if (!*n) { + return false; + } + while (*p != *n) { + if (!*++n) { + return false; + } + } + } + if (!*n) { + return false; + } + + while (!SimpleGlob(n, p)) { + n++; + if (*n == '\0') { + return false; + } + } + } + return true; +} + +} // namespace File +} // namespace Blizzard \ No newline at end of file diff --git a/bc/file/SimpleGlob.hpp b/bc/file/SimpleGlob.hpp new file mode 100644 index 0000000..40aa5b6 --- /dev/null +++ b/bc/file/SimpleGlob.hpp @@ -0,0 +1,12 @@ +#ifndef BC_FILE_SIMPLE_GLOB_HPP +#define BC_FILE_SIMPLE_GLOB_HPP + +namespace Blizzard { +namespace File { + +bool SimpleGlob(const char* name, const char* pattern); + +} // namespace File +} // namespace Blizzard + +#endif \ No newline at end of file diff --git a/bc/file/Types.hpp b/bc/file/Types.hpp index 93c4bbd..ed39117 100644 --- a/bc/file/Types.hpp +++ b/bc/file/Types.hpp @@ -5,7 +5,7 @@ #include "bc/time/Time.hpp" #if defined(WHOA_SYSTEM_WIN) -#include +typedef void* HANDLE; #endif #include @@ -15,59 +15,57 @@ namespace File { // Types -// Used by SetCacheMode enum Mode { - setperms = 4, - settimes = 16, - nocache = 64 + read = 0x0001, + write = 0x0002, + shareread = 0x0004, + sharewrite = 0x0008, + nocache = 0x0040, + temporary = 0x0080, + truncate = 0x0100, + append = 0x0200, + create = 0x0400, + mustnotexist = 0x0800, + mustexist = 0x1000 }; class FileInfo { public: - Time::TimeRec* time; - // The device ID storing this file. - uint32_t device; - // Tell if a file is a directory - // read-only, hidden - // See file/Defines.hpp for more - uint32_t attributes; - // Size in bytes - uint64_t size; - // Note that these are Y2K time, not Unix - Time::Timestamp accessTime; - Time::Timestamp modificationTime; - Time::Timestamp attributeModificationTime; + const char* name; + int32_t unk04; + uint64_t size; + int32_t attributes; + Time::Timestamp createtime; + Time::Timestamp writetime; + Time::Timestamp accesstime; + int32_t filetype; + int32_t normal; }; class ProcessDirParms { public: - const char* root = nullptr; - const char* item = nullptr; - bool itemIsDirectory = false; - void* param = nullptr; + const char* dir; + const char* item; + void* param; + bool isdir; }; typedef bool (*ProcessDirCallback)(const ProcessDirParms&); class StreamRecord { public: - static constexpr size_t s_padPath = 80; - #if defined(WHOA_SYSTEM_WIN) - HANDLE filehandle; + HANDLE filehandle; #endif #if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) - // File descriptor - int32_t filefd; + int filefd; #endif - // Open flags - uint32_t flags; - // Determines whether info object is initialized - bool hasInfo; - FileInfo info; - // The path of the opened file. - char path[s_padPath]; - + int32_t mode; + bool haveinfo; + uint32_t unk0C; + Blizzard::File::FileInfo info; + int32_t* unk48; + const char* name; }; } // namespace File diff --git a/bc/file/Write.cpp b/bc/file/Write.cpp new file mode 100644 index 0000000..65a5251 --- /dev/null +++ b/bc/file/Write.cpp @@ -0,0 +1,57 @@ +#include "bc/file/Write.hpp" +#include "bc/file/Filesystem.hpp" +#include "bc/file/Types.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +namespace Blizzard { +namespace File { + +bool Write(StreamRecord* file, const void* data, int32_t count) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, write); + parms.file = file; + parms.data = const_cast(data); + parms.offset = -1LL; + parms.count = count; + + auto fs = System_File::Stacked::s_manager; + return fs ? fs->write(fs, &parms) : false; +} + +bool Write(StreamRecord* file, const void* data, int32_t* count) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, write); + parms.file = file; + parms.data = const_cast(data); + parms.offset = -1LL; + parms.count = *count; + + auto fs = System_File::Stacked::s_manager; + if (!fs || fs->write(fs, &parms)) { + return false; + } + + *count = parms.count; + return true; +} + +bool Write(StreamRecord* file, const void* data, int64_t offset, int32_t* count) { + System_File::Stacked::FileParms parms; + parms.op = offsetof(Filesystem, writep); + parms.file = file; + parms.data = const_cast(data); + parms.offset = offset; + parms.count = *count; + + auto fs = System_File::Stacked::s_manager; + if (!fs || fs->writep(fs, &parms)) { + return false; + } + + *count = parms.count; + return true; +} + +} // namespace File +} // namespace Blizzard diff --git a/bc/file/Write.hpp b/bc/file/Write.hpp new file mode 100644 index 0000000..5cf6533 --- /dev/null +++ b/bc/file/Write.hpp @@ -0,0 +1,22 @@ +#ifndef BC_FILE_WRITE_HPP +#define BC_FILE_WRITE_HPP + +#include +#include "bc/file/Types.hpp" + +namespace Blizzard { +namespace File { + +// write +bool Write(StreamRecord* file, const void* data, int32_t count); + +// write +bool Write(StreamRecord* file, const void* data, int32_t* count); + +// writep +bool Write(StreamRecord* file, const void* data, int64_t offset, int32_t* count); + +} // namespace File +} // namespace Blizzard + +#endif diff --git a/bc/file/path/Path.cpp b/bc/file/path/Path.cpp deleted file mode 100644 index 27154c0..0000000 --- a/bc/file/path/Path.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "bc/file/path/Path.hpp" -#include "bc/file/path/Posix.hpp" -#include "bc/String.hpp" -#include "bc/Memory.hpp" -#include "bc/Debug.hpp" - -#include - -namespace Blizzard { -namespace File { -namespace Path { - -// Classes - -QuickNative::QuickNative(const char* path) { - this->size = 0; - this->slowpath = nullptr; - this->fastpath[0] = '\0'; - - if (!path) { - return; - } - - // Null byte - constexpr size_t reserved = 1; - - this->size = String::Length(path) + reserved; - - char* nativePath = nullptr; - - if (this->size < BC_FILE_MAX_PATH) { - // (fast) - nativePath = this->fastpath; - MakeNativePath(path, this->fastpath, BC_FILE_MAX_PATH); - } else { - // (slow) - this->slowpath = reinterpret_cast(Memory::Allocate(this->size)); - nativePath = this->slowpath; - MakeNativePath(path, this->slowpath, this->size); - } - -#if defined(WHOA_SYSTEM_LINUX) - // Linux typically does not involve a case-sensitive filesystem - // For compatibility, try to find if this path exists in another case - // This is much slower - - // The size of the resolved path. Maybe be larger due to "./" - size_t resolvedSize = this->size + 2; - - auto resolvedPath = static_cast(Memory::Allocate(resolvedSize)); - resolvedPath[resolvedSize-1] = '\0'; - - bool wasResolved = ResolvePosixCasePath(nativePath, resolvedPath); - if (!wasResolved) { - Memory::Free(resolvedPath); - return; - } - - if (this->slowpath != nullptr) { - Memory::Free(this->slowpath); - } - - this->slowpath = resolvedPath; -#endif -} - -QuickNative::~QuickNative() { - if (this->slowpath != nullptr) { - Memory::Free(this->slowpath); - } -} - -const char* QuickNative::Str() { - if (this->slowpath != nullptr) { - return this->slowpath; - } - - return this->fastpath; -} - -size_t QuickNative::Size() { - return this->size; -} - -// Functions - -void ForceTrailingSeparator(char* buf, size_t bufMax, char sep) { - if (buf == nullptr) { - return; - } - - // If no separator character is provided, infer correct separator based on runes - if (sep == '\0') { - auto ptr = buf; - - char ch; - - while (true) { - ch = *ptr++; - // Make inference based on what data we have. - if (ch == '/' || ch == '\\') { - sep = ch; - break; - } - - if (ch == '\0') { - // Fail safe to the system's path separator. - sep = BC_FILE_SYSTEM_PATH_SEPARATOR; - break; - } - } - } - - // Buffer needs at least two characters. - if (bufMax < 2) { - return; - } - - // Slice off the end of the path buffer, so that any existing* trailing separator is discarded. - auto len = String::Length(buf); - char ch = '\0'; - if (len > 0) { - ch = buf[len - 1]; - } - switch (ch) { - case '/': - case '\\': - len--; - break; - default: - break; - } - - // Add (back*) trailing separator - BLIZZARD_ASSERT(len <= bufMax - 2); - buf[len] = sep; - buf[len + 1] = '\0'; // pad null -} - -// Convert path to DOS-style. -// the function will iterate through the path string, converting all occurrences of forward slashes (/) to backslashes (\) -bool MakeBackslashPath(const char* path, char* result, size_t capacity) { - size_t i = 0; - size_t c = capacity - 1; - - while (i <= c) { - if (path[i] == '\0') { - result[i] = '\0'; - return true; - } - - result[i] = path[i] == '/' ? '\\' : path[i]; - - i++; - } - - result[0] = '\0'; - return false; -} - -// Make a path string consistent before the last path separator. -bool MakeConsistentPath(const char* path, char* result, size_t capacity) { - if (!result || (capacity < 1)) { - return false; - } - - if (!path || (capacity == 1)) { - *result = '\0'; - return false; - } - - for (auto i = capacity - 1; i != -1; i--) { - auto ch = path[i]; - - switch (ch) { - case '\\': - return MakeBackslashPath(path, result, capacity); - case '/': - return MakeUnivPath(path, result, capacity); - case '\0': - default: - break; - } - } - - String::Copy(result, path, capacity); - return true; -} - -// Convert any path string into something that can be used on the current OS. -bool MakeNativePath(const char* path, char* result, size_t capacity) { -#if defined(WHOA_SYSTEM_WIN) - return MakeWindowsPath(path, result, capacity); -#else - return MakeUnivPath(path, result, capacity); -#endif -} - -// Convert a path string into something UNIX-friendly. -bool MakeUnivPath(const char* path, char* result, size_t capacity) { - size_t i = 0; // path and result index - size_t c = capacity - 1; // ceiling - - while (i <= c) { - if (path[i] == '\0') { - result[i] = '\0'; - return true; - } - - result[i] = path[i] == '\\' ? '/' : path[i]; - - i++; - } - - result[0] = '\0'; - return false; -} - -bool MakeWindowsPath(const char* path, char* result, size_t capacity) { - return MakeBackslashPath(path, result, capacity); -} - -} // namespace Path -} // namespace File -} // namespace Blizzard diff --git a/bc/file/path/Path.hpp b/bc/file/path/Path.hpp deleted file mode 100644 index fad9a2a..0000000 --- a/bc/file/path/Path.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef BC_FILE_PATH_PATH_HPP -#define BC_FILE_PATH_PATH_HPP - -#include "bc/file/Defines.hpp" - -#include - -namespace Blizzard { -namespace File { -namespace Path { - -// Classes - -// Quick native path translation on the stack. -// Can be slow in some instances, as it resorts to heap allocation if path size >= BC_FILE_MAX_PATH -// Note: this name is an invention, however its behavior is repeated in all System_File implementations. -// So it probably did exist as an inlined class. -class QuickNative { - private: - size_t size; - // stack allocation - char fastpath[BC_FILE_MAX_PATH]; - // heap - char* slowpath; - - public: - QuickNative(const char* path); - ~QuickNative(); - const char* Str(); - size_t Size(); -}; - -// Functions - -void ForceTrailingSeparator(char* buf, size_t bufMax, char sep); - -bool MakeBackslashPath(const char* path, char* result, size_t capacity); - -bool MakeConsistentPath(const char* path, char* result, size_t capacity); - -bool MakeNativePath(const char* path, char* result, size_t capacity); - -bool MakeUnivPath(const char* path, char* result, size_t capacity); - -bool MakeWindowsPath(const char* path, char* result, size_t capacity); - -} // namespace Path -} // namespace File -} // namespace Blizzard - -#endif diff --git a/bc/file/path/Posix.cpp b/bc/file/path/Posix.cpp deleted file mode 100644 index 445314e..0000000 --- a/bc/file/path/Posix.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "bc/file/path/Posix.hpp" - -#if defined(WHOA_SYSTEM_LINUX) -#include -#include -#include -#include -#include - -// adapted from https://github.com/OneSadCookie/fcaseopen/blob/master/fcaseopen.c -bool ResolvePosixCasePath(const char* path, char* r) { - auto l = strlen(path); - auto p = static_cast(alloca(l + 1)); - strcpy(p, path); - size_t rl = 0; - - DIR* d; - if (p[0] == '/') { - d = opendir("/"); - p = p + 1; - } else { - d = opendir("."); - r[0] = '.'; - r[1] = 0; - rl = 1; - } - - int32_t last = 0; - auto c = strsep(&p, "/"); - while (c) { - if (!d) { - return false; - } - - if (last) { - closedir(d); - return false; - } - - r[rl] = '/'; - rl += 1; - r[rl] = 0; - - struct dirent* e = readdir(d); - while (e) { - if (strcasecmp(c, e->d_name) == 0) { - strcpy(r + rl, e->d_name); - rl += strlen(e->d_name); - - closedir(d); - d = opendir(r); - - break; - } - - e = readdir(d); - } - - if (!e) { - strcpy(r + rl, c); - rl += strlen(c); - last = 1; - } - - c = strsep(&p, "/"); - } - - if (d) { - closedir(d); - } - - return true; -} - -#endif diff --git a/bc/file/path/Posix.hpp b/bc/file/path/Posix.hpp deleted file mode 100644 index f4b0b3a..0000000 --- a/bc/file/path/Posix.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef BC_FILE_PATH_POSIX_HPP -#define BC_FILE_PATH_POSIX_HPP - -bool ResolvePosixCasePath(const char* path, char* r); - -#endif diff --git a/bc/os/File.hpp b/bc/os/File.hpp index 7d26294..9cc2913 100644 --- a/bc/os/File.hpp +++ b/bc/os/File.hpp @@ -1,75 +1,25 @@ #ifndef BC_OS_FILE_HPP #define BC_OS_FILE_HPP -#include "bc/file/Types.hpp" - -#include - -enum EOSFileDisposition { - OS_CREATE_NEW = 1, - OS_CREATE_ALWAYS = 2, - OS_OPEN_EXISTING = 3, - OS_OPEN_ALWAYS = 4, - OS_TRUNCATE_EXISTING = 5 -}; - -enum EOSFileAccess { - OS_GENERIC_ALL = 0x10000000, - OS_GENERIC_EXECUTE = 0x20000000, - OS_GENERIC_WRITE = 0x40000000, - OS_GENERIC_READ = 0x80000000 -}; - -enum EOSFileShare { - OS_FILE_SHARE_READ = 0x00000001, - OS_FILE_SHARE_WRITE = 0x00000002 -}; - -enum EOSFileFlagsAndAttributes { - OS_FILE_ATTRIBUTE_READONLY = 0x1, - OS_FILE_ATTRIBUTE_HIDDEN = 0x2, - OS_FILE_ATTRIBUTE_SYSTEM = 0x4, - OS_FILE_ATTRIBUTE_DIRECTORY = 0x10, - OS_FILE_ATTRIBUTE_ARCHIVE = 0x20, - OS_FILE_ATTRIBUTE_NORMAL = 0x80, - OS_FILE_ATTRIBUTE_TEMPORARY = 0x100, - OS_FILE_ATTRIBUTE_OFFLINE = 0x1000, - OS_FILE_ATTRIBUTE_ENCRYPTED = 0x4000, - - OS_FILE_FLAG_OPEN_NO_RECALL = 0x00100000, - OS_FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000, - OS_FILE_FLAG_POSIX_SEMANTICS = 0x01000000, - OS_FILE_FLAG_BACKUP_SEMANTICS = 0x02000000, - OS_FILE_FLAG_DELETE_ON_CLOSE = 0x04000000, - OS_FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000, - OS_FILE_FLAG_RANDOM_ACCESS = 0x10000000, - OS_FILE_FLAG_NO_BUFFERING = 0x20000000, - OS_FILE_FLAG_OVERLAPPED = 0x40000000, - OS_FILE_FLAG_WRITE_THROUGH = 0x80000000 -}; - -typedef Blizzard::File::StreamRecord* HOSFILE; - -constexpr HOSFILE HOSFILE_INVALID = nullptr; - -HOSFILE OsCreateFile(const char* fileName, uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition, uint32_t flagsAndAttributes, uint32_t extendedFileType); - -int32_t OsSetFileAttributes(const char* fileName, uint32_t attributes); - -uint64_t OsGetFileSize(HOSFILE fileHandle); - -int32_t OsWriteFile(HOSFILE fileHandle, void* buffer, size_t bytesToWrite, size_t* bytesWritten); - -int32_t OsReadFile(HOSFILE fileHandle, void* buffer, size_t bytesToRead, size_t* bytesRead); - -void OsCloseFile(HOSFILE fileHandle); - -int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod); - -int32_t OsSetCurrentDirectory(const char* pathName); - -int32_t OsGetCurrentDirectory(size_t pathLen, char* pathName); - -int32_t OsCreateDirectory(const char* pathName, int32_t recursive); +#include "bc/os/file/CloseFile.hpp" +#include "bc/os/file/CopyFile.hpp" +#include "bc/os/file/CreateDirectory.hpp" +#include "bc/os/file/CreateFile.hpp" +#include "bc/os/file/DeleteFile.hpp" +#include "bc/os/file/DirectoryExists.hpp" +#include "bc/os/file/FileExists.hpp" +#include "bc/os/file/FileList.hpp" +#include "bc/os/file/GetCurrentDirectory.hpp" +#include "bc/os/file/GetDownloadFolder.hpp" +#include "bc/os/file/GetFileAttributes.hpp" +#include "bc/os/file/GetFileSize.hpp" +#include "bc/os/file/MoveFile.hpp" +#include "bc/os/file/ReadFile.hpp" +#include "bc/os/file/RemoveDirectory.hpp" +#include "bc/os/file/RemoveDirectoryRecurse.hpp" +#include "bc/os/file/SetCurrentDirectory.hpp" +#include "bc/os/file/SetFileAttributes.hpp" +#include "bc/os/file/SetFilePointer.hpp" +#include "bc/os/file/WriteFile.hpp" #endif diff --git a/bc/os/file/CloseFile.cpp b/bc/os/file/CloseFile.cpp new file mode 100644 index 0000000..1920eba --- /dev/null +++ b/bc/os/file/CloseFile.cpp @@ -0,0 +1,6 @@ +#include "bc/os/file/CloseFile.hpp" +#include "bc/file/Close.hpp" + +void OsCloseFile(HOSFILE fileHandle) { + Blizzard::File::Close(reinterpret_cast(fileHandle)); +} \ No newline at end of file diff --git a/bc/os/file/CloseFile.hpp b/bc/os/file/CloseFile.hpp new file mode 100644 index 0000000..a8b8f3c --- /dev/null +++ b/bc/os/file/CloseFile.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_CLOSE_FILE_HPP +#define BC_OS_FILE_CLOSE_FILE_HPP + +#include "bc/os/file/Types.hpp" + +void OsCloseFile(HOSFILE fileHandle); + +#endif diff --git a/bc/os/file/CopyFile.cpp b/bc/os/file/CopyFile.cpp new file mode 100644 index 0000000..99ca6a1 --- /dev/null +++ b/bc/os/file/CopyFile.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/CopyFile.hpp" +#include "bc/os/file/Defines.hpp" +#include "bc/file/Copy.hpp" + +int32_t OsCopyFile(const char* existingFileName, const char* newFileName, int32_t failIfExists) { + if (!existingFileName || !newFileName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::Copy(existingFileName, newFileName, failIfExists == 0); +} \ No newline at end of file diff --git a/bc/os/file/CopyFile.hpp b/bc/os/file/CopyFile.hpp new file mode 100644 index 0000000..cf30477 --- /dev/null +++ b/bc/os/file/CopyFile.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_COPY_FILE_HPP +#define BC_OS_FILE_COPY_FILE_HPP + +#include + +int32_t OsCopyFile(const char* existingFileName, const char* newFileName, int32_t failIfExists); + +#endif \ No newline at end of file diff --git a/bc/os/file/CreateDirectory.cpp b/bc/os/file/CreateDirectory.cpp new file mode 100644 index 0000000..9b3076c --- /dev/null +++ b/bc/os/file/CreateDirectory.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/CreateDirectory.hpp" +#include "bc/file/CreateDirectory.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsCreateDirectory(const char* pathName, int32_t recursive) { + if (pathName == nullptr) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::CreateDirectory(pathName, recursive != 0); +} \ No newline at end of file diff --git a/bc/os/file/CreateDirectory.hpp b/bc/os/file/CreateDirectory.hpp new file mode 100644 index 0000000..b5ebf8c --- /dev/null +++ b/bc/os/file/CreateDirectory.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_CREATE_DIRECTORY_HPP +#define BC_OS_FILE_CREATE_DIRECTORY_HPP + +#include + +int32_t OsCreateDirectory(const char* pathName, int32_t recursive); + +#endif diff --git a/bc/os/file/CreateFile.cpp b/bc/os/file/CreateFile.cpp new file mode 100644 index 0000000..66b2350 --- /dev/null +++ b/bc/os/file/CreateFile.cpp @@ -0,0 +1,67 @@ +#include "bc/os/file/CreateFile.hpp" +#include "bc/os/file/SetFileAttributes.hpp" +#include "bc/Debug.hpp" +#include "bc/file/Open.hpp" +#include "bc/os/file/Types.hpp" + +int32_t OsCreateFileMode(uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition) { + using Mode = Blizzard::File::Mode; + + int32_t mode; + + if (desiredAccess & OS_GENERIC_READ) { + mode |= Mode::read; + } + if (desiredAccess & OS_GENERIC_WRITE) { + mode |= Mode::write; + } + + if (shareMode & OS_FILE_SHARE_READ) { + mode |= Mode::shareread; + } + if (shareMode & OS_FILE_SHARE_WRITE) { + mode |= Mode::sharewrite; + } + + switch (createDisposition) { + case OS_CREATE_NEW: + // mode |= 0xC00; + mode |= (Mode::create|Mode::mustnotexist); + break; + case OS_CREATE_ALWAYS: + // mode |= 0x400; + mode |= Mode::create; + break; + case OS_OPEN_EXISTING: + // mode |= 0x1000 + mode |= Mode::mustexist; + break; + case OS_OPEN_ALWAYS: + // mode |= 0x200; + mode |= Mode::append; + break; + case OS_TRUNCATE_EXISTING: + // mode |= 0x100; + mode |= Mode::truncate; + break; + } + + return mode; +} + +HOSFILE OsCreateFile(const char* fileName, uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition, uint32_t flagsAndAttributes, uint32_t extendedFileType) { + BLIZZARD_VALIDATE(fileName, "invalid filename", HOSFILE_INVALID); + BLIZZARD_VALIDATE(desiredAccess != 0, "invalid desired access", HOSFILE_INVALID); + BLIZZARD_VALIDATE(createDisposition <= OS_TRUNCATE_EXISTING, "invalid create disposition", HOSFILE_INVALID); + + Blizzard::File::StreamRecord* file; + if (!Blizzard::File::Open(fileName, OsCreateFileMode(desiredAccess, shareMode, createDisposition), file)) { + return HOSFILE_INVALID; + } + + if ((flagsAndAttributes & OS_FILE_ATTRIBUTE_NORMAL) == 0) { + OsSetFileAttributes(fileName, flagsAndAttributes); + } + + return reinterpret_cast(file); +} \ No newline at end of file diff --git a/bc/os/file/CreateFile.hpp b/bc/os/file/CreateFile.hpp new file mode 100644 index 0000000..b2ebe94 --- /dev/null +++ b/bc/os/file/CreateFile.hpp @@ -0,0 +1,10 @@ +#ifndef BC_OS_FILE_CREATE_FILE_HPP +#define BC_OS_FILE_CREATE_FILE_HPP + +#include + +#include "bc/os/file/Types.hpp" + +HOSFILE OsCreateFile(const char* fileName, uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition, uint32_t flagsAndAttributes, uint32_t extendedFileType); + +#endif \ No newline at end of file diff --git a/bc/os/file/Defines.hpp b/bc/os/file/Defines.hpp new file mode 100644 index 0000000..eaebda2 --- /dev/null +++ b/bc/os/file/Defines.hpp @@ -0,0 +1,10 @@ +#ifndef BC_OS_FILE_DEFINES_HPP +#define BC_OS_FILE_DEFINES_HPP + +#define OS_FILE_ERROR_INVALID_PARAMETER 0x57 + +#include "bc/file/Error.hpp" + +#define OS_FILE_SET_LAST_ERROR(err) ::Blizzard::File::SetLastError(err) + +#endif diff --git a/bc/os/file/DeleteFile.cpp b/bc/os/file/DeleteFile.cpp new file mode 100644 index 0000000..03e753e --- /dev/null +++ b/bc/os/file/DeleteFile.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/DeleteFile.hpp" +#include "bc/os/file/Defines.hpp" +#include "bc/file/Delete.hpp" + +int32_t OsDeleteFile(const char* fileName) { + if (!fileName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::Delete(fileName); +} \ No newline at end of file diff --git a/bc/os/file/DeleteFile.hpp b/bc/os/file/DeleteFile.hpp new file mode 100644 index 0000000..af15fd6 --- /dev/null +++ b/bc/os/file/DeleteFile.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_DELETE_FILE_HPP +#define BC_OS_FILE_DELETE_FILE_HPP + +#include + +int32_t OsDeleteFile(const char* fileName); + +#endif diff --git a/bc/os/file/DirectoryExists.cpp b/bc/os/file/DirectoryExists.cpp new file mode 100644 index 0000000..56df625 --- /dev/null +++ b/bc/os/file/DirectoryExists.cpp @@ -0,0 +1,6 @@ +#include "bc/os/file/DirectoryExists.hpp" +#include "bc/file/Exists.hpp" + +int32_t OsDirectoryExists(const char* dirName) { + return Blizzard::File::IsDirectory(dirName); +} \ No newline at end of file diff --git a/bc/os/file/DirectoryExists.hpp b/bc/os/file/DirectoryExists.hpp new file mode 100644 index 0000000..3bf04b5 --- /dev/null +++ b/bc/os/file/DirectoryExists.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_DIRECTORY_EXISTS_HPP +#define BC_OS_FILE_DIRECTORY_EXISTS_HPP + +#include + +int32_t OsDirectoryExists(const char* dirName); + +#endif \ No newline at end of file diff --git a/bc/os/file/FileExists.cpp b/bc/os/file/FileExists.cpp new file mode 100644 index 0000000..b1ae631 --- /dev/null +++ b/bc/os/file/FileExists.cpp @@ -0,0 +1,6 @@ +#include "bc/os/file/FileExists.hpp" +#include "bc/file/Exists.hpp" + +int32_t OsFileExists(const char* path) { + return Blizzard::File::IsFile(path); +} \ No newline at end of file diff --git a/bc/os/file/FileExists.hpp b/bc/os/file/FileExists.hpp new file mode 100644 index 0000000..909b99a --- /dev/null +++ b/bc/os/file/FileExists.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_FILE_EXISTS_HPP +#define BC_OS_FILE_FILE_EXISTS_HPP + +#include + +int32_t OsFileExists(const char* path); + +#endif diff --git a/bc/os/file/FileList.cpp b/bc/os/file/FileList.cpp new file mode 100644 index 0000000..1f255b0 --- /dev/null +++ b/bc/os/file/FileList.cpp @@ -0,0 +1,73 @@ +#include "bc/os/file/FileList.hpp" +#include "bc/file/Defines.hpp" +#include "bc/file/GetFileInfo.hpp" +#include "bc/file/MakeAbsolutePath.hpp" +#include "bc/file/ProcessDirFast.hpp" +#include "bc/file/SimpleGlob.hpp" +#include "bc/file/Types.hpp" +#include "bc/string/Copy.hpp" +#include "bc/string/Path.hpp" + +int32_t OsFileList(const char* dir, const char* pattern, OsFileListCallback callback, void* param, int32_t flags) { + class Internal { + public: + struct FileListParms { + OsFileListCallback callback; + void* param; + uint32_t flags; + const char* pattern; + }; + + static bool Callback(const Blizzard::File::ProcessDirParms& parms) { + char path[260]; + OS_FILE_DATA data; + Blizzard::String::Copy(data.fileName, parms.item, sizeof(data.fileName)); + Blizzard::String::JoinPath(path, sizeof(path), parms.dir, parms.item); + + auto list = static_cast(parms.param); + + if (Blizzard::File::SimpleGlob(parms.item, list->pattern)) { + Blizzard::File::FileInfo info; + if (Blizzard::File::GetFileInfo(path, &info)) { + // TODO: consider making this 64 bits + data.size = static_cast(info.size); + data.flags = 0; + + if (info.attributes & BC_FILE_ATTRIBUTE_DIRECTORY) { + data.flags |= 0x10; + } + if (info.attributes & BC_FILE_ATTRIBUTE_READONLY) { + data.flags |= 0x01; + } + if (info.attributes & BC_FILE_ATTRIBUTE_HIDDEN) { + data.flags |= 0x02; + } + + if (list->flags && ((info.attributes & BC_FILE_ATTRIBUTE_HIDDEN) == 0)) { + if (list->callback(data, list->param)) { + return false; + } + } + } + } + + return true; + } + }; + + char finddir[260]; + char findpath[260]; + Blizzard::String::Copy(finddir, dir, 260); + + if (!Blizzard::File::MakeAbsolutePath(finddir, findpath, 260, false)) { + return false; + } + + Internal::FileListParms parms; + parms.param = param; + parms.flags = flags; + parms.callback = callback; + parms.pattern = pattern; + + return Blizzard::File::ProcessDirFast(findpath, static_cast(&parms), Internal::Callback, false); +} \ No newline at end of file diff --git a/bc/os/file/FileList.hpp b/bc/os/file/FileList.hpp new file mode 100644 index 0000000..6740703 --- /dev/null +++ b/bc/os/file/FileList.hpp @@ -0,0 +1,17 @@ +#ifndef BC_OS_FILE_FILE_LIST_HPP +#define BC_OS_FILE_FILE_LIST_HPP + +#include + +class OS_FILE_DATA { + public: + uint32_t size; + uint32_t flags; + char fileName[260]; +}; + +typedef int32_t (*OsFileListCallback)(const OS_FILE_DATA& data, void* param); + +int32_t OsFileList(const char* dir, const char* pattern, OsFileListCallback callback, void* param, int32_t flags); + +#endif \ No newline at end of file diff --git a/bc/os/file/GetCurrentDirectory.cpp b/bc/os/file/GetCurrentDirectory.cpp new file mode 100644 index 0000000..f975197 --- /dev/null +++ b/bc/os/file/GetCurrentDirectory.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/GetCurrentDirectory.hpp" +#include "bc/file/GetWorkingDirectory.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsGetCurrentDirectory(uint32_t buffersize, char* buffer) { + if (!buffer) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::GetWorkingDirectory(buffer, buffersize); +} diff --git a/bc/os/file/GetCurrentDirectory.hpp b/bc/os/file/GetCurrentDirectory.hpp new file mode 100644 index 0000000..28e1c8a --- /dev/null +++ b/bc/os/file/GetCurrentDirectory.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_GET_CURRENT_DIRECTORY_HPP +#define BC_OS_FILE_GET_CURRENT_DIRECTORY_HPP + +#include + +int32_t OsGetCurrentDirectory(uint32_t buffersize, char* buffer); + +#endif \ No newline at end of file diff --git a/bc/os/file/GetDownloadFolder.cpp b/bc/os/file/GetDownloadFolder.cpp new file mode 100644 index 0000000..3fa5edc --- /dev/null +++ b/bc/os/file/GetDownloadFolder.cpp @@ -0,0 +1,10 @@ +#include "bc/os/file/GetDownloadFolder.hpp" +#include "bc/file/GetWorkingDirectory.hpp" + +const char* OsFileGetDownloadFolder() { + static char s_downloadfolder[260] = { 0 }; + if (s_downloadfolder[0] == '\0') { + Blizzard::File::GetWorkingDirectory(s_downloadfolder, 260); + } + return s_downloadfolder; +} \ No newline at end of file diff --git a/bc/os/file/GetDownloadFolder.hpp b/bc/os/file/GetDownloadFolder.hpp new file mode 100644 index 0000000..c8884f1 --- /dev/null +++ b/bc/os/file/GetDownloadFolder.hpp @@ -0,0 +1,6 @@ +#ifndef BC_OS_FILE_GET_DOWNLOAD_FOLDER_HPP +#define BC_OS_FILE_GET_DOWNLOAD_FOLDER_HPP + +const char* OsFileGetDownloadFolder(); + +#endif \ No newline at end of file diff --git a/bc/os/file/GetFileAttributes.cpp b/bc/os/file/GetFileAttributes.cpp new file mode 100644 index 0000000..758526d --- /dev/null +++ b/bc/os/file/GetFileAttributes.cpp @@ -0,0 +1,17 @@ +#include "bc/os/file/GetFileAttributes.hpp" +#include "bc/file/GetFileInfo.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsGetFileAttributes(const char* fileName) { + if (!fileName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + Blizzard::File::FileInfo info; + if (!Blizzard::File::GetFileInfo(fileName, &info)) { + return -1; + } + + return info.attributes; +} \ No newline at end of file diff --git a/bc/os/file/GetFileAttributes.hpp b/bc/os/file/GetFileAttributes.hpp new file mode 100644 index 0000000..3b0ee31 --- /dev/null +++ b/bc/os/file/GetFileAttributes.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_GET_FILE_ATTRIBUTES_HPP +#define BC_OS_FILE_GET_FILE_ATTRIBUTES_HPP + +#include + +int32_t OsGetFileAttributes(const char* fileName); + +#endif diff --git a/bc/os/file/GetFileSize.cpp b/bc/os/file/GetFileSize.cpp new file mode 100644 index 0000000..131db27 --- /dev/null +++ b/bc/os/file/GetFileSize.cpp @@ -0,0 +1,8 @@ +#include "bc/os/file/GetFileSize.hpp" +#include "bc/file/GetFileInfo.hpp" +#include + +uint64_t OsGetFileSize(HOSFILE fileHandle) { + auto info = Blizzard::File::GetFileInfo(reinterpret_cast(fileHandle)); + return info ? info->size : std::numeric_limits::max(); +} \ No newline at end of file diff --git a/bc/os/file/GetFileSize.hpp b/bc/os/file/GetFileSize.hpp new file mode 100644 index 0000000..0b1053c --- /dev/null +++ b/bc/os/file/GetFileSize.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_GET_FILE_SIZE_HPP +#define BC_OS_FILE_GET_FILE_SIZE_HPP + +#include "bc/os/file/Types.hpp" + +uint64_t OsGetFileSize(HOSFILE fileHandle); + +#endif \ No newline at end of file diff --git a/bc/os/file/MoveFile.cpp b/bc/os/file/MoveFile.cpp new file mode 100644 index 0000000..abe7755 --- /dev/null +++ b/bc/os/file/MoveFile.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/MoveFile.hpp" +#include "bc/os/file/Defines.hpp" +#include "bc/file/Move.hpp" + +int32_t OsMoveFile(const char* existingFileName, const char* newFileName) { + if (!existingFileName || !newFileName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::Move(existingFileName, newFileName); +} \ No newline at end of file diff --git a/bc/os/file/MoveFile.hpp b/bc/os/file/MoveFile.hpp new file mode 100644 index 0000000..7a782fa --- /dev/null +++ b/bc/os/file/MoveFile.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_MOVE_FILE_HPP +#define BC_OS_FILE_MOVE_FILE_HPP + +#include + +int32_t OsMoveFile(const char* existingFileName, const char* newFileName); + +#endif \ No newline at end of file diff --git a/bc/os/file/ReadFile.cpp b/bc/os/file/ReadFile.cpp new file mode 100644 index 0000000..cde9755 --- /dev/null +++ b/bc/os/file/ReadFile.cpp @@ -0,0 +1,13 @@ +#include "bc/os/file/ReadFile.hpp" +#include "bc/os/file/Defines.hpp" +#include "bc/file/Read.hpp" + +int32_t OsReadFile(HOSFILE fileHandle, void* buffer, uint32_t bytesToRead, uint32_t* bytesRead) { + if (!buffer || !bytesRead) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + *bytesRead = bytesToRead; + return Blizzard::File::Read(reinterpret_cast(fileHandle), buffer, reinterpret_cast(bytesRead)); +} \ No newline at end of file diff --git a/bc/os/file/ReadFile.hpp b/bc/os/file/ReadFile.hpp new file mode 100644 index 0000000..2966338 --- /dev/null +++ b/bc/os/file/ReadFile.hpp @@ -0,0 +1,9 @@ +#ifndef BC_OS_FILE_READ_FILE_HPP +#define BC_OS_FILE_READ_FILE_HPP + +#include "bc/os/file/Types.hpp" +#include + +int32_t OsReadFile(HOSFILE fileHandle, void* buffer, uint32_t bytesToRead, uint32_t* bytesRead); + +#endif \ No newline at end of file diff --git a/bc/os/file/RemoveDirectory.cpp b/bc/os/file/RemoveDirectory.cpp new file mode 100644 index 0000000..4f50932 --- /dev/null +++ b/bc/os/file/RemoveDirectory.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/RemoveDirectory.hpp" +#include "bc/file/RemoveDirectory.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsRemoveDirectory(const char* pathName) { + if (!pathName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::RemoveDirectory(pathName); +} \ No newline at end of file diff --git a/bc/os/file/RemoveDirectory.hpp b/bc/os/file/RemoveDirectory.hpp new file mode 100644 index 0000000..75b77f7 --- /dev/null +++ b/bc/os/file/RemoveDirectory.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_REMOVE_DIRECTORY_HPP +#define BC_OS_FILE_REMOVE_DIRECTORY_HPP + +#include + +int32_t OsRemoveDirectory(const char* pathName); + +#endif \ No newline at end of file diff --git a/bc/os/file/RemoveDirectoryRecurse.cpp b/bc/os/file/RemoveDirectoryRecurse.cpp new file mode 100644 index 0000000..fe3dbea --- /dev/null +++ b/bc/os/file/RemoveDirectoryRecurse.cpp @@ -0,0 +1,12 @@ +#include "bc/os/file/RemoveDirectoryRecurse.hpp" +#include "bc/file/RemoveDirectory.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsRemoveDirectoryRecurse(const char* pathName, uint32_t flags) { + if (!pathName) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::RemoveDirectoryAndContents(pathName, (flags & 0x1) != 0); +} \ No newline at end of file diff --git a/bc/os/file/RemoveDirectoryRecurse.hpp b/bc/os/file/RemoveDirectoryRecurse.hpp new file mode 100644 index 0000000..bec6b5c --- /dev/null +++ b/bc/os/file/RemoveDirectoryRecurse.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_REMOVE_DIRECTORY_HPP +#define BC_OS_FILE_REMOVE_DIRECTORY_HPP + +#include + +int32_t OsRemoveDirectoryRecurse(const char* pathName, uint32_t flags); + +#endif \ No newline at end of file diff --git a/bc/os/file/SetCurrentDirectory.cpp b/bc/os/file/SetCurrentDirectory.cpp new file mode 100644 index 0000000..ba3aa2e --- /dev/null +++ b/bc/os/file/SetCurrentDirectory.cpp @@ -0,0 +1,13 @@ +#include "bc/os/file/SetCurrentDirectory.hpp" +#include "bc/os/file/Defines.hpp" +#include "bc/file/SetWorkingDirectory.hpp" + +int32_t OsSetCurrentDirectory(const char* pathName) { + if (pathName == nullptr) { + // SetLastError(0x57); + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::SetWorkingDirectory(pathName); +} \ No newline at end of file diff --git a/bc/os/file/SetCurrentDirectory.hpp b/bc/os/file/SetCurrentDirectory.hpp new file mode 100644 index 0000000..7a98941 --- /dev/null +++ b/bc/os/file/SetCurrentDirectory.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_SET_CURRENT_DIRECTORY_HPP +#define BC_OS_FILE_SET_CURRENT_DIRECTORY_HPP + +#include + +int32_t OsSetCurrentDirectory(const char* pathName); + +#endif \ No newline at end of file diff --git a/bc/os/file/SetFileAttributes.cpp b/bc/os/file/SetFileAttributes.cpp new file mode 100644 index 0000000..660fa53 --- /dev/null +++ b/bc/os/file/SetFileAttributes.cpp @@ -0,0 +1,13 @@ +#include "bc/os/file/SetFileAttributes.hpp" +#include "bc/file/SetAttributes.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsSetFileAttributes(const char* fileName, uint32_t attributes) { + if (!fileName) { + // SetLastError(0x57); + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + return Blizzard::File::SetAttributes(fileName, attributes); +} \ No newline at end of file diff --git a/bc/os/file/SetFileAttributes.hpp b/bc/os/file/SetFileAttributes.hpp new file mode 100644 index 0000000..8b2e535 --- /dev/null +++ b/bc/os/file/SetFileAttributes.hpp @@ -0,0 +1,8 @@ +#ifndef BC_OS_FILE_SET_FILE_ATTRIBUTES_HPP +#define BC_OS_FILE_SET_FILE_ATTRIBUTES_HPP + +#include + +int32_t OsSetFileAttributes(const char* fileName, uint32_t attributes); + +#endif \ No newline at end of file diff --git a/bc/os/file/SetFilePointer.cpp b/bc/os/file/SetFilePointer.cpp new file mode 100644 index 0000000..732c671 --- /dev/null +++ b/bc/os/file/SetFilePointer.cpp @@ -0,0 +1,28 @@ +#include "bc/os/file/SetFilePointer.hpp" +#include "bc/Debug.hpp" +#include "bc/file/GetPos.hpp" +#include "bc/file/SetPos.hpp" +#include "bc/file/Defines.hpp" + +int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod) { + BLIZZARD_ASSERT(moveMethod <= 2); + + auto file = reinterpret_cast(fileHandle); + + int64_t offset; + if (moveMethod != 0 && !Blizzard::File::GetPos(file, offset)) { + return -1LL; + } + + uint32_t whence[] = { + BC_FILE_SEEK_START, + BC_FILE_SEEK_CURRENT, + BC_FILE_SEEK_END + }; + + if (!Blizzard::File::SetPos(file, offset, whence[moveMethod])) { + return -1LL; + } + + return offset + distanceToMove; +} \ No newline at end of file diff --git a/bc/os/file/SetFilePointer.hpp b/bc/os/file/SetFilePointer.hpp new file mode 100644 index 0000000..e6fc399 --- /dev/null +++ b/bc/os/file/SetFilePointer.hpp @@ -0,0 +1,9 @@ +#ifndef BC_OS_FILE_SET_FILE_POINTER_HPP +#define BC_OS_FILE_SET_FILE_POINTER_HPP + +#include +#include "bc/os/file/Types.hpp" + +int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod); + +#endif \ No newline at end of file diff --git a/bc/os/file/Types.hpp b/bc/os/file/Types.hpp new file mode 100644 index 0000000..bd44e08 --- /dev/null +++ b/bc/os/file/Types.hpp @@ -0,0 +1,40 @@ +#ifndef BC_OS_FILE_TYPES_HPP +#define BC_OS_FILE_TYPES_HPP + +#include + +#define OS_FILE_ATTRIBUTE_NORMAL 0x80 + +// this is passed into last argument of OsCreateFile +#define OS_FILE_TYPE_DEFAULT 0x3F3F3F3F + +enum EOSFileDisposition { + OS_CREATE_NEW = 1, + OS_CREATE_ALWAYS = 2, + OS_OPEN_EXISTING = 3, + OS_OPEN_ALWAYS = 4, + OS_TRUNCATE_EXISTING = 5 +}; + +enum EOSFileAccess { + OS_GENERIC_ALL = 0x10000000, + OS_GENERIC_EXECUTE = 0x20000000, + OS_GENERIC_WRITE = 0x40000000, + OS_GENERIC_READ = 0x80000000 +}; + +enum EOSFileShare { + OS_FILE_SHARE_READ = 0x00000001, + OS_FILE_SHARE_WRITE = 0x00000002 +}; + +// TODO: generate a proper handle here +struct HOSFILE__ { + int32_t unused; +}; + +typedef HOSFILE__* HOSFILE; + +constexpr HOSFILE HOSFILE_INVALID = nullptr; + +#endif \ No newline at end of file diff --git a/bc/os/file/WriteFile.cpp b/bc/os/file/WriteFile.cpp new file mode 100644 index 0000000..536fc02 --- /dev/null +++ b/bc/os/file/WriteFile.cpp @@ -0,0 +1,13 @@ +#include "bc/os/file/WriteFile.hpp" +#include "bc/file/Write.hpp" +#include "bc/os/file/Defines.hpp" + +int32_t OsWriteFile(HOSFILE fileHandle, void* buffer, uint32_t bytesToWrite, uint32_t* bytesWritten) { + if (!buffer || !bytesWritten) { + OS_FILE_SET_LAST_ERROR(OS_FILE_ERROR_INVALID_PARAMETER); + return 0; + } + + *bytesWritten = bytesToWrite; + return Blizzard::File::Write(reinterpret_cast(fileHandle), buffer, reinterpret_cast(bytesWritten)); +} \ No newline at end of file diff --git a/bc/os/file/WriteFile.hpp b/bc/os/file/WriteFile.hpp new file mode 100644 index 0000000..f0a4228 --- /dev/null +++ b/bc/os/file/WriteFile.hpp @@ -0,0 +1,9 @@ +#ifndef BC_OS_FILE_WRITE_FILE_HPP +#define BC_OS_FILE_WRITE_FILE_HPP + +#include "bc/os/file/Types.hpp" +#include + +int32_t OsWriteFile(HOSFILE fileHandle, void* buffer, uint32_t bytesToWrite, uint32_t* bytesWritten); + +#endif \ No newline at end of file diff --git a/bc/system/file/Defines.hpp b/bc/system/file/Defines.hpp index 80116b9..bc5de77 100644 --- a/bc/system/file/Defines.hpp +++ b/bc/system/file/Defines.hpp @@ -1,13 +1,14 @@ -#ifndef BC_FILE_SYSTEM_DEFINES_HPP -#define BC_FILE_SYSTEM_DEFINES_HPP +#ifndef BC_SYSTEM_FILE_DEFINES_HPP +#define BC_SYSTEM_FILE_DEFINES_HPP -// Constants +// how much data to buffer when copying with File::Copy +#define BC_SYSTEM_FILE_COPYBUFFER_SIZE 0xA00000LL -// How much data to buffer when copying with File::Copy -#define BC_FILE_SYSTEM_COPYBUFFER_SIZE 0xA00000UL - -// Utility macros - -#define BC_FILE_PATH(localname) char localname[BC_FILE_MAX_PATH] = {0} +// this bit field controls which file information is set by SetFileInfo +#define BC_SYSTEM_FILE_INFO_UNK_0 0x01 +#define BC_SYSTEM_FILE_INFO_UNK_1 0x02 +#define BC_SYSTEM_FILE_INFO_ATTRIBUTES 0x04 +#define BC_SYSTEM_FILE_INFO_UNK_3 0x08 +#define BC_SYSTEM_FILE_INFO_TIMES 0x10 #endif diff --git a/bc/system/file/Stacked.cpp b/bc/system/file/Stacked.cpp index acd3a00..ff5b178 100644 --- a/bc/system/file/Stacked.cpp +++ b/bc/system/file/Stacked.cpp @@ -5,287 +5,291 @@ #include "bc/String.hpp" #include "bc/Memory.hpp" -namespace Blizzard { namespace System_File { namespace Stacked { -bool null_cd(File::Filesystem* fs, FileParms* parms) { +bool null_cd(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_close(File::Filesystem* fs, FileParms* parms) { +bool null_close(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_create(File::Filesystem* fs, FileParms* parms) { +bool null_create(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_cwd(File::Filesystem* fs, FileParms* parms) { +bool null_cwd(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_dirwalk(File::Filesystem* fs, FileParms* parms) { +bool null_dirwalk(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_exists(File::Filesystem* fs, FileParms* parms) { +bool null_exists(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_flush(File::Filesystem* fs, FileParms* parms) { +bool null_flush(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_getfileinfo(File::Filesystem* fs, FileParms* parms) { +bool null_getfileinfo(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_getfreespace(File::Filesystem* fs, FileParms* parms) { +bool null_getfreespace(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_getpos(File::Filesystem* fs, FileParms* parms) { +bool null_getpos(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_getrootchars(File::Filesystem* fs, FileParms* parms) { +bool null_getrootchars(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_isabspath(File::Filesystem* fs, FileParms* parms) { +bool null_isabspath(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_isreadonly(File::Filesystem* fs, FileParms* parms) { +bool null_isreadonly(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_makeabspath(File::Filesystem* fs, FileParms* parms) { +bool null_makeabspath(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_mkdir(File::Filesystem* fs, FileParms* parms) { +bool null_mkdir(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_move(File::Filesystem* fs, FileParms* parms) { +bool null_move(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_copy(File::Filesystem* fs, FileParms* parms) { +bool null_copy(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_open(File::Filesystem* fs, FileParms* parms) { +bool null_open(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_read(File::Filesystem* fs, FileParms* parms) { +bool null_read(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_readp(File::Filesystem* fs, FileParms* parms) { +bool null_readp(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_rmdir(File::Filesystem* fs, FileParms* parms) { +bool null_rmdir(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_setcachemode(File::Filesystem* fs, FileParms* parms) { +bool null_setcachemode(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_seteof(File::Filesystem* fs, FileParms* parms) { +bool null_seteof(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_setfileinfo(File::Filesystem* fs, FileParms* parms) { +bool null_setfileinfo(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_setpos(File::Filesystem* fs, FileParms* parms) { +bool null_setpos(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_unlink(File::Filesystem* fs, FileParms* parms) { +bool null_unlink(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_write(File::Filesystem* fs, FileParms* parms) { +bool null_write(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_writep(File::Filesystem* fs, FileParms* parms) { +bool null_writep(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -bool null_shutdown(File::Filesystem* fs, FileParms* parms) { +bool null_shutdown(Blizzard::File::Filesystem* fs, FileParms* parms) { return false; } -// Null stack: this stack does absolutely nothing. Its only purpose is to store fallback functions. -static File::Filesystem s_nullstack = { +// null stack: does absolutely nothing +// only purpose is to store fallback functions +static Blizzard::File::Filesystem s_nullstack = { &s_nullstack, nullptr, - { - null_cd, - null_close, - null_create, - null_cwd, - null_dirwalk, - null_exists, - null_flush, - null_getfileinfo, - null_getfreespace, - null_getpos, - null_getrootchars, - null_isabspath, - null_isreadonly, - null_makeabspath, - null_mkdir, - null_move, - null_copy, - null_open, - null_read, - null_readp, - null_rmdir, - null_setcachemode, - null_seteof, - null_setfileinfo, - null_setpos, - null_unlink, - null_write, - null_writep, - null_shutdown - } + null_cd, + null_close, + null_create, + null_cwd, + null_dirwalk, + null_exists, + null_flush, + null_getfileinfo, + null_getfreespace, + null_getpos, + null_getrootchars, + null_isabspath, + null_isreadonly, + null_makeabspath, + null_mkdir, + null_move, + null_copy, + null_open, + null_read, + null_readp, + null_rmdir, + null_setcachemode, + null_seteof, + null_setfileinfo, + null_setpos, + null_unlink, + null_write, + null_writep, + null_shutdown }; -// Base stack: this stack contains the base System_File functions. -static File::Filesystem s_basestack = { +// base stack: contains the base System_File functions. +static Blizzard::File::Filesystem s_basestack = { &s_basestack, nullptr, - { - System_File::SetWorkingDirectory, - System_File::Close, - System_File::Create, - System_File::GetWorkingDirectory, - System_File::ProcessDirFast, - System_File::Exists, - System_File::Flush, - System_File::GetFileInfo, - System_File::GetFreeSpace, - System_File::GetPos, - System_File::GetRootChars, - System_File::IsAbsolutePath, - System_File::IsReadOnly, - System_File::MakeAbsolutePath, - System_File::CreateDirectory, - System_File::Move, - System_File::Copy, - System_File::Open, - System_File::Read, - System_File::ReadP, - System_File::RemoveDirectory, - System_File::SetCacheMode, - System_File::SetEOF, - System_File::SetAttributes, - System_File::SetPos, - System_File::Delete, - System_File::Write, - System_File::WriteP, - System_File::Shutdown - } + System_File::SetWorkingDirectory, + System_File::Close, + System_File::Create, + System_File::GetWorkingDirectory, + System_File::ProcessDirFast, + System_File::Exists, + System_File::Flush, + System_File::GetFileInfo, + System_File::GetFreeSpace, + System_File::GetPos, + System_File::GetRootChars, + System_File::IsAbsolutePath, + System_File::IsReadOnly, + System_File::MakeAbsolutePath, + System_File::CreateDirectory, + System_File::Move, + System_File::Copy, + System_File::Open, + System_File::Read, + System_File::ReadP, + System_File::RemoveDirectory, + System_File::SetCacheMode, + System_File::SetEOF, + System_File::SetAttributes, + System_File::SetPos, + System_File::Delete, + System_File::Write, + System_File::WriteP, + System_File::Shutdown }; // File initialization stack: this is the default -static File::Filesystem s_fileinit = { +static Blizzard::File::Filesystem s_fileinit = { &s_fileinit, nullptr, - { - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init, - file_init - } + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init, + file_init }; -// Manager will be initialized upon first use -static File::Filesystem* s_manager = &s_fileinit; +// manager will be initialized upon first use +Blizzard::File::Filesystem* s_manager = &s_fileinit; -// Manager getter -File::Filesystem* Manager() { - return s_manager; -} +// push a Filesystem onto the stack +void Push(Blizzard::File::Filesystem* fs) { + auto head = reinterpret_cast(Blizzard::Memory::Allocate(sizeof(Blizzard::File::Filesystem))); + Blizzard::String::MemFill(head, sizeof(Blizzard::File::Filesystem), 0); + Blizzard::String::MemCopy(head, fs, sizeof(Blizzard::File::Filesystem)); -// Push a filesystem stack to manager linked list -void Push(File::Filesystem* fs) { - auto stack = reinterpret_cast(Memory::Allocate(sizeof(File::Filesystem))); - String::MemFill(stack, sizeof(File::Filesystem), 0); - - // Add stack - String::MemCopy(stack, fs, sizeof(File::Filesystem)); - stack->next = s_manager; - - s_manager = stack; + head->next = s_manager; + s_manager = head; HoistAll(); } // file_init is part of the default filesystem stack -// The first use of s_manager will call this function only once; it is a fallback function in case s_basestack's funcs are not yet hoisted. -bool file_init(File::Filesystem* fs, FileParms* parms) { - // Allocate a null stack, replacing what was previously a pointer to s_fileinit - s_manager = reinterpret_cast(Memory::Allocate(sizeof(File::Filesystem))); +// the first use of s_manager will call this function only once; it is a fallback function in case s_basestack's funcs are not yet hoisted. +bool file_init(Blizzard::File::Filesystem* fs, FileParms* parms) { + // allocate a null stack, replacing what was previously a pointer to s_fileinit + s_manager = reinterpret_cast(Blizzard::Memory::Allocate(sizeof(Blizzard::File::Filesystem))); if (s_manager == nullptr) { return false; } - // Add null fs calls to stack - // After Push/HoistAll, if a filesystem call is unimplemented, the func from s_nullstack gets called instead. - String::MemCopy(s_manager, &s_nullstack, sizeof(File::Filesystem)); + // add null fs calls to stack + // after Push/HoistAll, if a filesystem call is unimplemented, the func from s_nullstack gets called instead. + Blizzard::String::MemCopy(s_manager, &s_nullstack, sizeof(Blizzard::File::Filesystem)); - // Hoist basic file functions + // add basic file functions Push(&s_basestack); - // fs manager is now ready to use! - // Execute the original call. - if (s_manager != nullptr) { - auto func = *reinterpret_cast(reinterpret_cast(s_manager) + parms->offset); - return func(fs, parms); + if (s_manager == nullptr) { + return false; } - return false; -} - -// Ensures that the filesystem manager will always have a corresponding function address for enum Call call. -void Hoist(uint32_t call) { - if (s_manager->funcs[call] == nullptr) { - auto topStack = s_manager; - auto prev = s_manager; - auto next = s_manager->next; - while (next && topStack->funcs[call] == nullptr) { - topStack->funcs[call] = next->funcs[call]; - prev = next; - next = next->next; - } - - // Remove call from lower stack - prev->funcs[call] = nullptr; - } + // do the original operation + auto op = *reinterpret_cast(reinterpret_cast(s_manager) + parms->op); + return op(fs, parms); } // Hoist all functions in the filesystem stack to the top, i.e. the manager void HoistAll() { - for (uint32_t call = 0; call < File::Filesystem::s_numCalls; call++) { - Hoist(call); + #define HOIST_OP(op) if (s_manager->op == nullptr) { \ + auto cur = s_manager; \ + while (cur->next) { \ + cur->op = cur->next->op; \ + cur = cur->next; \ + } \ } + + HOIST_OP(cd); + HOIST_OP(close); + HOIST_OP(create); + HOIST_OP(cwd); + HOIST_OP(dirwalk); + HOIST_OP(exists); + HOIST_OP(flush); + HOIST_OP(getfileinfo); + HOIST_OP(getfreespace); + HOIST_OP(getpos); + HOIST_OP(getrootchars); + HOIST_OP(isabspath); + HOIST_OP(isreadonly); + HOIST_OP(makeabspath); + HOIST_OP(mkdir); + HOIST_OP(move); + HOIST_OP(copy); + HOIST_OP(open); + HOIST_OP(read); + HOIST_OP(readp); + HOIST_OP(rmdir); + HOIST_OP(setcachemode); + HOIST_OP(seteof); + HOIST_OP(setfileinfo); + HOIST_OP(setpos); + HOIST_OP(unlink); + HOIST_OP(write); + HOIST_OP(writep); + HOIST_OP(shutdown); + + #undef HOIST_OP } } // namespace Stacked } // namespace System_File -} // namespace Blizzard diff --git a/bc/system/file/Stacked.hpp b/bc/system/file/Stacked.hpp index aed0b78..ab3a940 100644 --- a/bc/system/file/Stacked.hpp +++ b/bc/system/file/Stacked.hpp @@ -3,83 +3,54 @@ #include "bc/file/Types.hpp" #include "bc/file/Filesystem.hpp" -#include "bc/file/File.hpp" #include "bc/system/file/System_File.hpp" #include -namespace Blizzard { namespace System_File { namespace Stacked { +extern Blizzard::File::Filesystem* s_manager; + // Functions -bool Open(FileParms* parms); - -bool file_init(File::Filesystem* fs, FileParms* parms); - -File::Filesystem* Manager(); +bool file_init(Blizzard::File::Filesystem* fs, FileParms* parms); void HoistAll(); -void Push(File::Filesystem* fs); +void Push(Blizzard::File::Filesystem* fs); // Stacked file functions -bool SetWorkingDirectory(FileParms* parms); - -bool Close(FileParms* parms); - -bool GetWorkingDirectory(FileParms* parms); - -bool ProcessDirFast(FileParms* parms); - -bool Exists(FileParms* parms); - -bool Flush(FileParms* parms); - -bool GetFileInfo(FileParms* parms); - -bool GetFreeSpace(FileParms* parms); - -bool GetPos(FileParms* parms); - -bool GetRootChars(FileParms* parms); - -bool IsAbsolutePath(FileParms* parms); - -bool IsReadOnly(FileParms* parms); - -bool MakeAbsolutePath(FileParms* parms); - -bool CreateDirectory(FileParms* parms); - -bool Move(FileParms* parms); - -bool Copy(FileParms* parms); - -bool Open(FileParms* parms); - -bool Read(FileParms* parms); - -bool ReadP(FileParms* parms); - -bool RemoveDirectory(FileParms* parms); - -bool SetCacheMode(FileParms* parms); - -bool SetEOF(FileParms* parms); - -bool SetAttributes(FileParms* parms); - -bool SetPos(FileParms* parms); - -bool Delete(FileParms* parms); - -bool Write(FileParms* parms); - -bool WriteP(FileParms* parms); +bool (SetWorkingDirectory)(FileParms* parms); +bool (Close)(FileParms* parms); +bool (Create)(FileParms* parms); +bool (GetWorkingDirectory)(FileParms* parms); +bool (ProcessDirFast)(FileParms* parms); +bool (Exists)(FileParms* parms); +bool (Flush)(FileParms* parms); +bool (GetFileInfo)(FileParms* parms); +bool (GetFreeSpace)(FileParms* parms); +bool (GetPos)(FileParms* parms); +bool (GetRootChars)(FileParms* parms); +bool (IsAbsolutePath)(FileParms* parms); +bool (IsReadOnly)(FileParms* parms); +bool (MakeAbsolutePath)(FileParms* parms); +bool (CreateDirectory)(FileParms* parms); +bool (Move)(FileParms* parms); +bool (Copy)(FileParms* parms); +bool (Open)(FileParms* parms); +bool (Read)(FileParms* parms); +bool (ReadP)(FileParms* parms); +bool (RemoveDirectory)(FileParms* parms); +bool (SetCacheMode)(FileParms* parms); +bool (SetEOF)(FileParms* parms); +bool (SetAttributes)(FileParms* parms); +bool (SetPos)(FileParms* parms); +bool (Delete)(FileParms* parms); +bool (Write)(FileParms* parms); +bool (WriteP)(FileParms* parms); +bool (Shutdown)(FileParms* parms); } // namespace Stacked } // namespace System_File -} // namespace Blizzard #endif diff --git a/bc/system/file/System_File.cpp b/bc/system/file/System_File.cpp new file mode 100644 index 0000000..9b77b34 --- /dev/null +++ b/bc/system/file/System_File.cpp @@ -0,0 +1,331 @@ +#include "bc/system/file/System_File.hpp" +#include "bc/Lock.hpp" +#include "bc/File.hpp" +#include "bc/String.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/system/file/Types.hpp" + +#if defined(WHOA_SYSTEM_WIN) +#include +#include +#include +#endif + +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) +#include +#include +#endif + +namespace System_File { + +bool s_EnableFileLocks = false; + +bool (SetWorkingDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::SetWorkingDirectory(parms); +} + +bool (Close)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Close(parms); +} + +bool (Create)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Create(parms); +} + +bool (GetWorkingDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::GetWorkingDirectory(parms); +} + +bool (ProcessDirFast)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::ProcessDirFast(parms); +} + +bool (Exists)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Exists(parms); +} + +bool (Flush)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Flush(parms); +} + +bool (GetFileInfo)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::GetFileInfo(parms); +} + +#undef GetFreeSpace +bool (GetFreeSpace)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::GetFreeSpace(parms); +} + +bool (GetPos)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::GetPos(parms); +} + +bool (GetRootChars)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::GetRootChars(parms); +} + +bool (IsAbsolutePath)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::IsAbsolutePath(parms); +} + +bool (IsReadOnly)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::IsReadOnly(parms); +} + +bool (MakeAbsolutePath)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::MakeAbsolutePath(parms); +} + +bool (CreateDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::CreateDirectory(parms); +} + +bool (Move)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Move(parms); +} + +bool (Copy)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Copy(parms); +} + +bool (Open)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Open(parms); +} + +bool (RemoveDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::RemoveDirectory(parms); +} + +bool (SetCacheMode)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::SetCacheMode(parms); +} + +bool (SetEOF)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::SetEOF(parms); +} + +bool (SetAttributes)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::SetAttributes(parms); +} + +bool (SetPos)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::SetPos(parms); +} + +bool (Delete)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Delete(parms); +} + +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) + +bool (Read)(Blizzard::File::StreamRecord* file, void* data, int64_t offset, int32_t* count) { + // file must be open to read + BLIZZARD_ASSERT(file != nullptr && file->filefd != -1); + + if (count == nullptr || *count == 0) { + return true; + } + + auto n = *count; + auto c = n; + + while (n > 0) { + auto m = offset < 0 ? ::read(file->filefd, data, n) : ::pread(file->filefd, data, n, offset); + if (m == 0) { + BC_FILE_SET_ERROR_MSG(7, "Posix Read - End of File - %s", file->name); + return false; + } + if (m == -1) { + if (errno == EINTR) { + continue; + } + BC_FILE_SET_ERROR_MSG(100 + errno, "Posix Read - %s", file->name); + return false; + } + data = static_cast(data) + m; + n -= m; + } + + *count = c - n; + + return n == 0; +} + +bool (Read)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return System_File::Read(parms->file, parms->data, -1, &parms->count); +} + +bool (ReadP)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return System_File::Read(parms->file, parms->data, parms->offset, &parms->count); +} + +bool (Write)(Blizzard::File::StreamRecord* file, const void* data, int64_t offset, int32_t* count) { + // file descriptor must be initialized + BLIZZARD_ASSERT(file != nullptr && file->filefd != -1); + + if (count == nullptr || *count == 0) { + return true; + } + + auto c = *count; + auto n = c; + + while (n > 0) { + auto m = offset < 0 ? ::write(file->filefd, data, n) : ::pwrite(file->filefd, data, n, offset); + if (m < 0) { + BC_FILE_SET_ERROR_MSG(100 + errno, "Posix Write - %s", file->name); + return false; + } + + if (offset > -1) { + offset += m; + } + + n -= m; + data = static_cast(data) + m; + } + + *count = c - n; + + return n == 0; +} + +bool (Write)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return System_File::Write(parms->file, parms->data, -1LL, &parms->count); +} + +bool (WriteP)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return System_File::Write(parms->file, parms->data, parms->offset, &parms->count); +} + +#endif // defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) + +#if defined(WHOA_SYSTEM_WIN) + +bool read_init(Blizzard::File::StreamRecord*, void*, LPOVERLAPPED, LPDWORD); +bool write_init(Blizzard::File::StreamRecord*, const void*, LPOVERLAPPED, LPDWORD); + +bool (*read_func)(Blizzard::File::StreamRecord*, void*, LPOVERLAPPED, LPDWORD) = read_init; +bool (*write_func)(Blizzard::File::StreamRecord*, const void*, LPOVERLAPPED, LPDWORD) = write_init; + +static Blizzard::Lock::DoOnceData s_read_init; +static Blizzard::Lock::DoOnceData s_write_init; + +static Blizzard::Lock::Mutex s_io_lock; + +bool read_overlapped(Blizzard::File::StreamRecord* file, void* data, LPOVERLAPPED overlapped, LPDWORD count) { + if (file->filehandle == INVALID_HANDLE_VALUE) { + BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + return false; + } + + if (*count == 0) { + return true; + } + + if (!::ReadFile(file->filehandle, data, *count, count, overlapped)) { + BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_READ, "Win32 Read - %s", file->name); + return false; + } + + return true; +} + +bool read_locked(Blizzard::File::StreamRecord* file, void* data, LPOVERLAPPED overlapped, LPDWORD count) { + Blizzard::Lock::MutexEnter(s_io_lock); + if (!Blizzard::File::SetPos( + file, + (static_cast(overlapped->OffsetHigh) << 32) | static_cast(overlapped->Offset), + BC_FILE_SEEK_START)) { + BC_FILE_SET_ERROR(9); + Blizzard::Lock::MutexLeave(s_io_lock); + return false; + } + auto ok = read_overlapped(file, data, nullptr, count); + Blizzard::Lock::MutexLeave(s_io_lock); + return ok; +} + +bool write_overlapped(Blizzard::File::StreamRecord* file, const void* data, LPOVERLAPPED overlapped, LPDWORD count) { + if (file->filehandle == nullptr) { + BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + return false; + } + + if (*count == 0) { + return true; + } + + if (!::WriteFile(file->filehandle, data, *count, count, overlapped)) { + BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_WRITE, "Win32 Write - %s", file->name); + return false; + } + + return true; +} + +bool write_locked(Blizzard::File::StreamRecord* file, const void* data, LPOVERLAPPED overlapped, LPDWORD count) { + Blizzard::Lock::MutexEnter(s_io_lock); + if (!Blizzard::File::SetPos( + file, + (static_cast(overlapped->OffsetHigh) << 32) | static_cast(overlapped->Offset), + BC_FILE_SEEK_START)) { + BC_FILE_SET_ERROR(9); + Blizzard::Lock::MutexLeave(s_io_lock); + return false; + } + auto ok = write_overlapped(file, data, nullptr, count); + Blizzard::Lock::MutexLeave(s_io_lock); + return ok; +} + +void detect_io_mode(void*) { + // if Windows is >= 5.0 + // read using the overlapped IO method + // if Windows < 5.0 + // each ReadP/WriteP call is locked and calls File::SetPos + OSVERSIONINFO version_info = {}; + version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (::GetVersionEx(&version_info) && version_info.dwMajorVersion >= 5) { + read_func = read_overlapped; + write_func = write_overlapped; + } else { + Blizzard::Lock::MutexCreate(s_io_lock); + read_func = read_locked; + write_func = write_locked; + } +} + +bool read_init(Blizzard::File::StreamRecord* file, void* data, LPOVERLAPPED overlapped, LPDWORD count) { + Blizzard::Lock::DoOnce(s_read_init, detect_io_mode, nullptr); + return read_func(file, data, overlapped, count); +} + +bool write_init(Blizzard::File::StreamRecord* file, void* data, LPOVERLAPPED overlapped, LPDWORD count) { + Blizzard::Lock::DoOnce(s_write_init, detect_io_mode, nullptr); + return write_func(file, data, overlapped, count); +} + +bool Write(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return write_func(parms->file, parms->data, nullptr, reinterpret_cast(&parms->count)); +} + +bool WriteP(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + OVERLAPPED overlapped = {}; + overlapped.Offset = static_cast(parms->offset & 0xFFFFFFFF); + overlapped.OffsetHigh = static_cast((parms->offset >> 32) & 0xFFFFFFFF); + overlapped.Internal = 0; + overlapped.InternalHigh = 0; + overlapped.hEvent = nullptr; + + return write_func(parms->file, parms->data, &overlapped, reinterpret_cast(&parms->count)); +} + +#endif // defined(WHOA_SYSTEM_WIN) + +bool Shutdown(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms) { + return Stacked::Shutdown(parms); +} + +} // namespace System_File diff --git a/bc/system/file/System_File.hpp b/bc/system/file/System_File.hpp index a91f16f..cbc2c34 100644 --- a/bc/system/file/System_File.hpp +++ b/bc/system/file/System_File.hpp @@ -4,48 +4,43 @@ #include "bc/Debug.hpp" #include "bc/system/file/Types.hpp" #include "bc/file/Filesystem.hpp" - -#if defined(WHOA_SYSTEM_WIN) -#include "bc/system/file/win/Support.hpp" -#endif - #include -namespace Blizzard { namespace System_File { +extern bool s_EnableFileLocks; + // Fs calls -bool SetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms); -bool Close(File::Filesystem* fs, Stacked::FileParms* parms); -bool Create(File::Filesystem* fs, Stacked::FileParms* parms); -bool GetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms); -bool ProcessDirFast(File::Filesystem* fs, Stacked::FileParms* parms); -bool Exists(File::Filesystem* fs, Stacked::FileParms* parms); -bool Flush(File::Filesystem* fs, Stacked::FileParms* parms); -bool GetFileInfo(File::Filesystem* fs, Stacked::FileParms* parms); -bool GetFreeSpace(File::Filesystem* fs, Stacked::FileParms* parms); -bool GetPos(File::Filesystem* fs, Stacked::FileParms* parms); -bool GetRootChars(File::Filesystem* fs, Stacked::FileParms* parms); -bool IsAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms); -bool IsReadOnly(File::Filesystem* fs, Stacked::FileParms* parms); -bool MakeAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms); -bool CreateDirectory(File::Filesystem* fs, Stacked::FileParms* parms); -bool Move(File::Filesystem* fs, Stacked::FileParms* parms); -bool Copy(File::Filesystem* fs, Stacked::FileParms* parms); -bool Open(File::Filesystem* fs, Stacked::FileParms* parms); -bool Read(File::Filesystem* fs, Stacked::FileParms* parms); -bool ReadP(File::Filesystem* fs, Stacked::FileParms* parms); -bool RemoveDirectory(File::Filesystem* fs, Stacked::FileParms* parms); -bool SetCacheMode(File::Filesystem* fs, Stacked::FileParms* parms); -bool SetEOF(File::Filesystem* fs, Stacked::FileParms* parms); -bool SetAttributes(File::Filesystem* fs, Stacked::FileParms* parms); -bool SetPos(File::Filesystem* fs, Stacked::FileParms* parms); -bool Delete(File::Filesystem* fs, Stacked::FileParms* parms); -bool Write(File::Filesystem* fs, Stacked::FileParms* parms); -bool WriteP(File::Filesystem* fs, Stacked::FileParms* parms); -bool Shutdown(File::Filesystem* fs, Stacked::FileParms* parms); +bool (SetWorkingDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Close)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Create)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (GetWorkingDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (ProcessDirFast)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Exists)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Flush)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (GetFileInfo)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (GetFreeSpace)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (GetPos)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (GetRootChars)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (IsAbsolutePath)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (IsReadOnly)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (MakeAbsolutePath)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (CreateDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Move)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Copy)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Open)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Read)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (ReadP)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (RemoveDirectory)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (SetCacheMode)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (SetEOF)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (SetAttributes)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (SetPos)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Delete)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Write)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (WriteP)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); +bool (Shutdown)(Blizzard::File::Filesystem* fs, Stacked::FileParms* parms); } // namespace System_File -} // namespace Blizzard #endif diff --git a/bc/system/file/Types.hpp b/bc/system/file/Types.hpp index f8aa1ff..a651647 100644 --- a/bc/system/file/Types.hpp +++ b/bc/system/file/Types.hpp @@ -3,20 +3,17 @@ #include "bc/Debug.hpp" #include "bc/file/Types.hpp" - #include -namespace Blizzard { - namespace System_File { class FileError { public: - // Error codes can be any value, even negative. - // Look for common error codes in Defines.hpp - // on POSIX, errorcode could be a POSIX error offset +100. - int32_t errorcode; - Debug::ErrorStackRecord* stack; + // Error codes can be any value, even negative. + // Look for common error codes in Defines.hpp + // on POSIX, errorcode could be a POSIX error offset +100. + int32_t errorcode; + Blizzard::Debug::ErrorStackRecord* stack; }; namespace Stacked { @@ -25,46 +22,31 @@ namespace Stacked { // so all fs calls can use one signature class FileParms { public: - // The byte offset to the filesystem action func of this call. - uintptr_t offset; - // The "source" file object - const char* filename; - // The "destination" file object, or just a user parameter. - union { - void* param; - const char* destination; - }; - // Supplies the handle/fd of a file - File::StreamRecord* stream; - // Flag or boolean field. - uint32_t flag; - // Stores additional status bits. - uint32_t mode; - File::FileInfo* info; - // Beginning and end used when querying slices: such as GetRootChars - union { - // The specified position of the interaction - int64_t position; - int64_t beginning; - }; - union { - int32_t whence; - int64_t end; - }; - union { - // size_t is not guaranteed to be 64-bit - // size64 is included here as well to allow interactions on files > 4 GB in size. - uint64_t size64; - size_t size; - }; - File::ProcessDirCallback callback; - char* directory; - size_t directorySize; + uint32_t op; + const char* name; + const char* newname; + Blizzard::File::StreamRecord* file; + Blizzard::File::FileInfo* info; + Blizzard::File::FileInfo noinfo; + uint32_t setinfo; + uint32_t getinfo; + int32_t mode; + void* data; + int32_t count; + int64_t offset; + int32_t whence; + char* buffer; + int32_t buffersize; + bool recurse; + bool canonicalize; + void* dirwalkparam; + Blizzard::File::ProcessDirCallback dirwalkcallback; + bool overwrite; + bool set_acl; }; } // namespace Stacked } // namespace System_File -} // namespace Blizzard #endif diff --git a/bc/system/file/posix/Stacked.cpp b/bc/system/file/posix/Stacked.cpp index 9fe330c..4b0432c 100644 --- a/bc/system/file/posix/Stacked.cpp +++ b/bc/system/file/posix/Stacked.cpp @@ -1,641 +1,802 @@ + +#include "bc/file/Defines.hpp" #if defined(WHOA_SYSTEM_LINUX) || defined(WHOA_SYSTEM_MAC) #include "bc/system/file/Stacked.hpp" -#include "bc/file/Path.hpp" #include "bc/Debug.hpp" +#include "bc/File.hpp" #include "bc/Memory.hpp" #include "bc/String.hpp" - +#include "bc/system/file/posix/Support.hpp" #include #include +#include #include #include +#include #include -#include #include #include +#include #include -namespace Blizzard { +#define PATH(name) Blizzard::String::QuickNativePath(name).ToString() + namespace System_File { namespace Stacked { -bool Open(FileParms* parms) { - File::Path::QuickNative pathNative(parms->filename); - - // Build POSIX flags based on flags from FileParms - bool read = parms->flag & BC_FILE_OPEN_READ; - bool write = parms->flag & BC_FILE_OPEN_WRITE; - bool mustNotExist = parms->flag & BC_FILE_OPEN_MUST_NOT_EXIST; - bool mustExist = parms->flag & BC_FILE_OPEN_MUST_EXIST; - bool create = parms->flag & BC_FILE_OPEN_CREATE; - bool truncate = parms->flag & BC_FILE_OPEN_TRUNCATE; - - BLIZZARD_ASSERT(read || write); - - int32_t flags = 0; - - if (read && !write) { - flags = O_RDONLY; - } else if (!read && write) { - flags = O_WRONLY; - } else if (read && write) { - flags = O_RDWR; - } - - int32_t fd = -1; - - if (create) { - flags |= O_CREAT; - - if (mustNotExist) { - flags |= O_EXCL; - } - - fd = ::open(pathNative.Str(), flags, 0777); - } else { - fd = ::open(pathNative.Str(), flags); - } - - if (fd == -1) { - BC_FILE_SET_ERROR_MSG(100 + errno, "Posix Open - %s", parms->filename); +// cd +bool SetWorkingDirectory(FileParms* parms) { + if (!parms->name) { + BC_FILE_SET_ERROR(8); return false; } - // Successfully opened file handle. Allocate StreamRecord + path str at the end. - auto recordSize = (sizeof(File::StreamRecord) - File::StreamRecord::s_padPath) + (1 + pathNative.Size()); - auto fileData = Memory::Allocate(recordSize); - if (fileData == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_OOM); - return false; - } + char name[BC_FILE_MAX_PATH]; + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); - String::MemFill(fileData, recordSize, 0); + return ::chdir(name) == 0; +} - auto file = reinterpret_cast(fileData); +// close +bool Close(FileParms* parms) { + auto file = parms->file; + BLIZZARD_ASSERT(file != nullptr); - file->flags = flags; - file->filefd = fd; - - String::Copy(file->path, parms->filename, pathNative.Size()); - - File::GetFileInfo(file); - - parms->stream = file; + ::close(file->filefd); + Blizzard::Memory::Free(file); return true; } -bool Exists(FileParms* parms) { - BC_FILE_PATH(buffer); - - auto filepath = parms->filename; - auto empty = ""; - size_t len = 0; - struct stat info = {}; - bool exists = false; - - File::Path::QuickNative filepathNative(filepath); - - auto status = ::stat(filepathNative.Str(), &info); - - if (status != -1) { - parms->info->attributes = 0; - // Collect attributes. - if (S_ISDIR(info.st_mode)) { - parms->info->attributes |= BC_FILE_ATTRIBUTE_DIRECTORY; - } - - if (S_ISREG(info.st_mode)) { - exists = true; - parms->info->attributes |= BC_FILE_ATTRIBUTE_NORMAL; - } - } - - return exists; +// ?? +bool Create(FileParms* parms) { + BC_FILE_SET_ERROR(BC_FILE_ERROR_UNIMPLEMENTED); + return false; } -bool GetFreeSpace(FileParms* parms) { - auto dirpath = parms->filename; - - if (dirpath == nullptr || *dirpath == '\0') { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); +// cwd +bool GetWorkingDirectory(FileParms* parms) { + if (!parms->buffer || parms->buffersize <= 0) { + BC_FILE_SET_ERROR(8); return false; } - File::Path::QuickNative dirpathNative(dirpath); - - struct statvfs sv; - if (::statvfs(dirpathNative.Str(), &sv) != 0) { - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_INVALID_ARGUMENT, "Posix GetFreeSpace - %s", dirpath); - return false; - } - - parms->size64 = sv.f_bavail * sv.f_frsize; - return true; + *parms->buffer = '\0'; + return ::getcwd(parms->buffer, parms->buffersize) != 0; } +// dirwalk bool ProcessDirFast(FileParms* parms) { - auto dirpath = parms->filename; - File::Path::QuickNative dirpathNative(dirpath); + char name[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + } else { + name[0] = '\0'; + } - // Call back to this function when processing - File::ProcessDirCallback callback = parms->callback; - - // Open directory - auto directory = ::opendir(dirpathNative.Str()); - if (!directory) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + auto dir = ::opendir(name); + if (!dir) { + BC_FILE_SET_ERROR(8); return false; } - // Stores names temporarily - char name[256] = {0}; + Blizzard::File::ProcessDirParms dirwalkparms; + dirwalkparms.dir = name; + dirwalkparms.param = parms->dirwalkparam; - bool status = false; + auto ignorelinks = parms->mode != 0; - File::ProcessDirParms walkparms = {}; - walkparms.root = parms->filename; - walkparms.param = parms->param; - walkparms.item = name; + char link[BC_FILE_MAX_PATH]; + char linkname[BC_FILE_MAX_PATH]; + struct dirent* item; - struct dirent* ent = nullptr; + while ((item = ::readdir(dir))) { + auto n = item->d_name; + if (!(n[0] == '.' && n[1] == '\0') && !(n[0] == '.' && n[1] == '.')) { + dirwalkparms.item = item->d_name; - while ((ent = ::readdir(directory)) != nullptr) { - String::Copy(name, ent->d_name, 256); + if (item->d_type == DT_REG) { + dirwalkparms.isdir = false; + } else if (item->d_type == DT_DIR) { + dirwalkparms.isdir = true; + } else if (item->d_type == DT_LNK) { + if (ignorelinks) { + dirwalkparms.isdir = false; + } else { + Blizzard::String::Copy(link, name, BC_FILE_MAX_PATH); + Blizzard::String::Append(link, "/", BC_FILE_MAX_PATH); + Blizzard::String::Append(link, item->d_name, BC_FILE_MAX_PATH); + if (!::realpath(link, linkname)) { + continue; + } + auto filetype = Blizzard::File::Exists(linkname); + if (filetype == 1) { + dirwalkparms.isdir = false; + } else if (filetype == 2) { + dirwalkparms.isdir = 1; + } else { + if (filetype == 0) { + continue; + } + } + } + } - auto isDotCurrent = (name[0] == '.' && name[1] == '\0'); - auto isDotParent = (name[0] == '.' && name[1] == '.' && name[2] == '\0'); - - if (!(isDotCurrent || isDotParent)) { - walkparms.itemIsDirectory = ent->d_type == DT_DIR; - status = callback(walkparms); - if (status) { + if (!parms->dirwalkcallback(dirwalkparms)) { break; } } } - ::closedir(directory); - return status; + ::closedir(dir); + return true; } -bool IsReadOnly(FileParms* parms) { - auto manager = Manager(); - auto filename = parms->filename; - - if (!filename || !manager || !File::Exists(filename)) { - // FileError(8) - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; +// exists +bool Exists(FileParms* parms) { + struct stat info; + if (::stat(PATH(parms->name), &info) == -1) { + parms->info->filetype = 0; + } else { + parms->info->filetype = S_ISDIR(info.st_mode) ? 2 : 1; } + return true; +} - File::StreamRecord* stream = nullptr; +// flush +bool Flush(FileParms* parms) { + auto file = parms->file; + BLIZZARD_ASSERT(file != nullptr); - auto flags = BC_FILE_OPEN_READ | BC_FILE_OPEN_WRITE | BC_FILE_OPEN_MUST_EXIST; +#if defined(WHOA_SYSTEM_MAC) + // from BSD syscall manual: + // "Note that while fsync() will flush all data from the host to the drive + // (i.e. the "permanent storage device"), the drive itself may not physi- + // cally write the data to the platters for quite some time and it may be + // written in an out-of-order sequence." - bool opened = File::Open(filename, flags, stream); - if (!opened) { + // "The F_FULLFSYNC fcntl asks the drive to flush all buffered data to permanent storage." + auto status = ::fcntl(file->filefd, F_FULLFSYNC, 0); + + if (status == 0) { return true; } +#endif + // Attempts to transfer all writes in the cache buffer to the underlying storage device. + // fsync() does not return until the transfer is complete, or until an error is detected. + return ::fsync(file->filefd) == 0; +} - File::Close(stream); +// getfileinfo +bool GetFileInfo(FileParms* parms) { + struct stat info; + char name[BC_FILE_MAX_PATH]; + + if (!parms->name) { + auto file = parms->file; + + if (file) { + if (!file->haveinfo) { + if (::fstat(file->filefd, &info) == -1) { + file->info.filetype = 0; + } else { + static_assert(sizeof(info.st_size) >= sizeof(int64_t), "stat size should hold at least 64 bits"); + if (file->unk48 == nullptr || *file->unk48 == 0) { + file->info.size = info.st_size; + } + + auto modtime = Blizzard::Time::FromUnixTime(info.st_mtim.tv_sec); + + file->info.createtime = modtime; + file->info.writetime = modtime; + file->info.filetype = S_ISDIR(info.st_mode) ? 2 : 0; + + file->haveinfo = true; + } + } + parms->info = &file->info; + if (!file->info.name) { + file->info.name = file->name; + } + return file->haveinfo; + } + } else { + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + + if (::stat(name, &info) != -1) { + auto infoptr = parms->info; + infoptr->size = static_cast(info.st_size); + auto modtime = Blizzard::Time::FromUnixTime(info.st_mtim.tv_sec); + infoptr->createtime = modtime; + infoptr->writetime = modtime; + infoptr->name = nullptr; + infoptr->normal = S_ISREG(info.st_mode) ? 1 : 0; + infoptr->attributes = S_ISDIR(info.st_mode) ? BC_FILE_ATTRIBUTE_DIRECTORY : BC_FILE_ATTRIBUTE_NORMAL; + infoptr->filetype = S_ISDIR(info.st_mode) ? 2 : 1; + return false; + } + + parms->info->filetype = 0; + } + + BC_FILE_SET_ERROR(8); return false; } -// realpath() based -bool MakeAbsolutePath(FileParms* parms) { - auto unkflag = parms->flag; +// getfreespace +bool GetFreeSpace(FileParms* parms) { + if (parms->name && *parms->name) { + char name[BC_FILE_MAX_PATH]; + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); - BC_FILE_PATH(basepathfast); - BC_FILE_PATH(univpathfast); - char* basepath = nullptr; - char* univpath = nullptr; + struct statfs fsinfo; - if (parms->directorySize < BC_FILE_MAX_PATH) { - basepath = basepathfast; - } else { - basepath = reinterpret_cast(Memory::Allocate(parms->directorySize)); + auto terminator = name; + while (name[0] && ::statfs(name, &fsinfo)) { + terminator = Blizzard::String::FindFilename(name); + if (name == terminator) { + goto fail; + } + + terminator[-1] = '\0'; + } + + parms->offset = static_cast(fsinfo.f_bavail) * static_cast(fsinfo.f_bsize); + return true; } - if (!File::IsAbsolutePath(parms->filename)) { - // If the path is not absolute already, pack current working dir to the base path. - File::GetWorkingDirectory(basepath, parms->directorySize); - - // Force a slash to the end of the base path, so that we can append univpath - File::Path::ForceTrailingSeparator(basepath, parms->directorySize, BC_FILE_SYSTEM_PATH_SEPARATOR); - } - - String::Append(basepath, parms->filename, parms->directorySize); - - if (parms->directorySize <= BC_FILE_MAX_PATH) { - univpath = univpathfast; - } else { - univpath = reinterpret_cast(Memory::Allocate(parms->directorySize)); - } - - File::Path::MakeNativePath(basepath, univpath, parms->directorySize); - - const char* local_1060 = nullptr; - const char* local_1054 = nullptr; - char* temp_directory = nullptr; - size_t temp_size = 0; - char* copied_univ_path = nullptr; - char* previous_character = nullptr; - char* next_slash = nullptr; - BC_FILE_PATH(next_slash_fast); - BC_FILE_PATH(temp_directory_fast); - char current_byte = 0; - size_t len = 0; - size_t copied_len = 0; - - auto after_first_slash = univpath + 1; - loop_start: - do { - next_slash = String::Find(after_first_slash, '/', parms->directorySize); - if (next_slash == nullptr) { - len = String::Length(univpath); - if (((len > 2) && (univpath[len - 1] == '.')) && (univpath[len - 2] == '/')) { - univpath[len - 1] = '\0'; - } - - if (unkflag) { - temp_size = parms->directorySize; - - if (temp_size <= BC_FILE_MAX_PATH) { - temp_directory = temp_directory_fast; - } else { - temp_directory = reinterpret_cast(Memory::Allocate(temp_size)); - } - - local_1060 = temp_directory; - local_1054 = univpath; - - after_first_slash = univpath; - copied_univ_path = temp_directory; - eat_byte: - current_byte = *after_first_slash; - if (current_byte != '\0') { - current_byte_must_be_forward_slash: - if (current_byte != '/') { - break; - } - goto do_workingdir_buffer_realpath; - } - - copy_universalpath: - String::Copy(univpath, copied_univ_path, parms->directorySize); - if ((copied_univ_path != local_1060) && (copied_univ_path != nullptr)) { - Memory::Free(copied_univ_path); - } - } - - String::Copy(parms->directory, univpath, parms->directorySize); - current_byte = *parms->directory; - - if (basepath != basepathfast && basepath != nullptr) { - // 0x1a211d - Memory::Free(basepath); - } - if (univpath != univpathfast && univpath != nullptr) { - // 0x1a2137 - Memory::Free(univpath); - } - - return current_byte != '\0'; - } - - if (next_slash[1] != '.') { - copy_or_increment_resume_loop: - if ((univpath < previous_character) && (after_first_slash == next_slash)) { - String::Copy(next_slash,next_slash + 1,parms->directorySize); - } else { - after_first_slash = next_slash + 1; - previous_character = next_slash; - } - goto loop_start; - } - - if (next_slash[2] == '.') { - if (next_slash[3] != '/') { - goto copy_or_increment_resume_loop; - } - String::Copy(after_first_slash, next_slash + 4, parms->directorySize); - } else { - if (next_slash[2] != '/') { - goto copy_or_increment_resume_loop; - } - String::Copy(next_slash + 1, next_slash + 3, parms->directorySize); - } - } while(true); - - after_first_slash = after_first_slash + 1; - current_byte = *after_first_slash; - - if (current_byte == '\0') { - do_workingdir_buffer_realpath: - previous_character = after_first_slash + 1; - String::Copy(temp_directory, local_1054, (static_cast(previous_character - local_1054)) + 1); - if (parms->directorySize <= BC_FILE_MAX_PATH) { - next_slash = next_slash_fast; - } else { - next_slash = reinterpret_cast(Memory::Allocate(parms->directorySize)); - } - - - if (::realpath(copied_univ_path, next_slash) == nullptr) { - temp_directory = temp_directory + (static_cast(previous_character - local_1054)); - } else { - String::Copy(copied_univ_path,next_slash,parms->directorySize); - if ((*after_first_slash == '/') || ((*after_first_slash == '\0' && (after_first_slash[-1] == '/')))) { - File::Path::ForceTrailingSeparator(copied_univ_path, parms->directorySize, '/'); - } - temp_directory = copied_univ_path; - copied_len = String::Length(copied_univ_path); - temp_directory = temp_directory + copied_len; - } - - if (*after_first_slash != '\0') { - after_first_slash = previous_character; - local_1054 = previous_character; - } - - if ((next_slash == next_slash_fast) || (next_slash == nullptr)) { - goto eat_byte; - } - - Memory::Free(next_slash); - current_byte = *after_first_slash; - if (current_byte == '\0') { - goto copy_universalpath; - } - } - goto current_byte_must_be_forward_slash; +fail: + BC_FILE_SET_ERROR(8); + return false; } -// Create a full directory path -bool CreateDirectory(FileParms* parms) { - if (parms->filename == nullptr) { +// getpos +bool GetPos(FileParms* parms) { + auto offset = ::lseek(parms->file->filefd, 0, SEEK_CUR); + if (offset == -1) { // FileError(8) BC_FILE_SET_ERROR(8); return false; } - auto recursive = parms->flag != 0; + parms->offset = static_cast(offset); - char tmp[BC_FILE_MAX_PATH] = {}; - struct stat sb; + return true; +} - // Copy path - File::Path::MakeNativePath(parms->filename, tmp, BC_FILE_MAX_PATH); +// getrootchars +bool GetRootChars(FileParms* parms) { + auto name = parms->name; - auto len = String::Length(tmp); + if (*name == '/' || *name == '\\') { + parms->offset = 1; - // Remove trailing slash - if(tmp[len - 1] == '/') { - tmp[len - 1] = '\0'; - } +#if defined(WHOA_SYSTEM_MAC) + if (Blizzard::String::EqualI(name + 1, "Volumes", 7) && (name[8] == '/' || name[8] == '\\')) { + auto volume = name + 9; - // check if path exists and is a directory - if (::stat(tmp, &sb) == 0) { - if (S_ISDIR(sb.st_mode)) { - return true; - } - } - - if (!recursive) { - if (::mkdir(tmp, 0777) < 0) { - return false; - } - } else { - // Loop through path and call mkdir on path elements - for (auto p = tmp + 1; *p != '\0'; p++) { - if (*p == '/') { - *p = 0; - // test path - if (::stat(tmp, &sb) != 0) { - // path does not exist, create directory - if (::mkdir(tmp, 0777) < 0) { - return false; - } - } else if (!S_ISDIR(sb.st_mode)) { - // not a directory - return false; + while (*volume) { + volume++; + if (*volume == '/' || *volume == '\\') { + break; + } + } + + parms->offset = volume - name; + } +#endif + } else { + parms->offset = 0; + } + + return true; +} + +// isabspath +bool IsAbsolutePath(FileParms* parms) { + return *parms->name == '/' || *parms->name == '\\'; +} + +// isreadonly +bool IsReadOnly(FileParms* parms) { + if (!Blizzard::File::IsFile(parms->name)) { + return false; + } + Blizzard::File::StreamRecord* file; + if (!Blizzard::File::Open(parms->name, Blizzard::File::Mode::write | Blizzard::File::Mode::sharewrite | Blizzard::File::Mode::mustexist, file)) { + return true; + } + Blizzard::File::Close(file); + + return false; +} + +// makeabspath +bool MakeAbsolutePath(FileParms* parms) { + char namebuffer[BC_FILE_MAX_PATH]; + auto name = parms->buffersize > BC_FILE_MAX_PATH + ? static_cast(Blizzard::Memory::Allocate(parms->buffersize)) + : namebuffer; + + *name = '\0'; + if (!Blizzard::File::IsAbsolutePath(parms->name)) { + Blizzard::File::GetWorkingDirectory(name, parms->buffersize); + Blizzard::String::ForceTrailingSeparator(name, parms->buffersize, '/'); + } + Blizzard::String::Append(name, parms->name, parms->buffersize); + char resultbuffer[BC_FILE_MAX_PATH]; + auto result = parms->buffersize > BC_FILE_MAX_PATH + ? static_cast(Blizzard::Memory::Allocate(parms->buffersize)) + : resultbuffer; + Blizzard::String::MakeUnivPath(name, result, parms->buffersize); + + auto cur = result + 1; + auto dst = result; + + char* slash; + // TODO: deduplicate + while ((slash = Blizzard::String::Find(cur, '/', parms->buffersize))) { + if (slash[1] != '.') { + if (dst > result && cur == slash) { + Blizzard::String::Copy(slash, slash + 1, parms->buffersize); + } else { + dst = slash; + cur = slash + 1; + } + } else if (slash[2] == '.') { + if (slash[3] == '/') { + Blizzard::String::Copy(cur, slash + 4, parms->buffersize); + } else { + if (dst > result && cur == slash) { + Blizzard::String::Copy(slash, slash + 1, parms->buffersize); + } else { + dst = slash; + cur = slash + 1; + } + } + } else { + if (slash[2] == '/') { + Blizzard::String::Copy(slash + 1, slash + 3, parms->buffersize); + } else { + if (dst > result && cur == slash) { + Blizzard::String::Copy(slash, slash + 1, parms->buffersize); + } else { + dst = slash; + cur = slash + 1; } - *p = '/'; } } } - // check remaining path existence - if (::stat(tmp, &sb) != 0) { - // path does not exist, create directory - if (::mkdir(tmp, 0777) < 0) { - return false; + auto length = Blizzard::String::Length(result); + if (length > 2) { + if (result[length - 1] == '.' && result[length - 2] == '/') { + result[length - 1] = '\0'; } - } else if (!S_ISDIR(sb.st_mode)) { - // not a directory + } + + if (parms->canonicalize) { + char canonicalbuffer[BC_FILE_MAX_PATH]; + char resolvedbuffer[BC_FILE_MAX_PATH]; + auto canonical = parms->buffersize > BC_FILE_MAX_PATH + ? static_cast(Blizzard::Memory::Allocate(parms->buffersize)) + : canonicalbuffer; + auto cur = canonical; + + auto src = result; + auto begin = src; + while (*src) { + while (*src && *src != '/') { + src++; + } + + auto v15 = (src + 1) - begin; + Blizzard::String::Copy(cur, begin, v15 + 1); + + auto resolved = parms->buffersize > BC_FILE_MAX_PATH ? static_cast(Blizzard::Memory::Allocate(parms->buffersize)) : resolvedbuffer; + if (::realpath(canonical, resolved)) { + Blizzard::String::Copy(canonical, resolved, parms->buffersize); + if (src[0] == '/' || src[0] == '\0' && src[-1] == '/') { + Blizzard::String::ForceTrailingSeparator(canonical, parms->buffersize, '/'); + } + cur = canonical + Blizzard::String::Length(canonical); + } else { + cur += v15; + } + + if (*src) { + src = src + 1; + begin = src; + } + + if (resolved != resolvedbuffer) { + Blizzard::Memory::Free(resolved); + } + } + + Blizzard::String::Copy(result, canonical, parms->buffersize); + if (canonical != canonicalbuffer) { + Blizzard::Memory::Free(canonical); + } + } + + Blizzard::String::Copy(parms->buffer, result, parms->buffersize); + + if (name != namebuffer) { + Blizzard::Memory::Free(name); + } + if (result != resultbuffer) { + Blizzard::Memory::Free(result); + } + + return *parms->buffer != '\0'; +} + +// mkdir +bool CreateDirectory(FileParms* parms) { + if (!parms->name) { + BC_FILE_SET_ERROR(8); return false; } + char name[BC_FILE_MAX_PATH]; + char dir[BC_FILE_MAX_PATH]; + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + + if (parms->recurse) { + auto path = name; + while (*path) { + while (*path && *path != '/') { + path++; + } + + auto length = path - name; + Blizzard::String::Copy(dir, name, length < BC_FILE_MAX_PATH ? length : BC_FILE_MAX_PATH); + + if (!Blizzard::File::IsDirectory(dir)) { + if (::mkdir(parms->name, 0777) != 0) { + return false; + } + } + + if (*path) { + path++; + } + } + return true; + } + + if (!Blizzard::File::IsDirectory(parms->name)) { + return ::mkdir(parms->name, 0777) == 0; + } + return true; } +// move bool Move(FileParms* parms) { - File::Path::QuickNative source(parms->filename); - File::Path::QuickNative destination(parms->destination); + struct stat info; - struct stat st; + char name[BC_FILE_MAX_PATH]; + char newname[BC_FILE_MAX_PATH]; - // Fail if destination already exists. - int32_t status = ::stat(destination.Str(), &st); - if (status == 0) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + Blizzard::String::MakeNativePath(parms->newname, newname, BC_FILE_MAX_PATH); + + if (::stat(newname, &info) == 0) { + BC_FILE_SET_ERROR(8); return false; } - // See if we can just rename the file. Pretty fast if we can avoid copying. - status = ::rename(source.Str(), destination.Str()); - if (status == 0) { + // if on the same device, just rename the file + if (::rename(name, newname) == 0) { + return true; + } + if (errno != EXDEV) { + // there's some other reason why rename wouldn't work, besides being on different devices + BC_FILE_SET_ERROR(8); + return false; + } + + if (Blizzard::File::Copy(name, newname, false)) { + Blizzard::File::Delete(name); + } + return true; +} + +// copy +bool Copy(FileParms* parms) { + char name[BC_FILE_MAX_PATH]; + char newname[BC_FILE_MAX_PATH]; + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + Blizzard::String::MakeNativePath(parms->newname, newname, BC_FILE_MAX_PATH); + + Blizzard::File::StreamRecord* src; + Blizzard::File::StreamRecord* dst; + + if (!Blizzard::File::Open(name, Blizzard::File::Mode::read | Blizzard::File::Mode::shareread | Blizzard::File::Mode::mustexist, src)) { + BC_FILE_SET_ERROR(2); // TODO: find out what 2 is + return false; + } + + if (!Blizzard::File::Open(name, Blizzard::File::Mode::write | (parms->overwrite ? Blizzard::File::Mode::create : Blizzard::File::Mode::create | Blizzard::File::Mode::mustnotexist), dst)) { + BC_FILE_SET_ERROR(4); // TODO: find out what 4 is + return false; + } + + auto size = Blizzard::File::GetFileInfo(src)->size; + auto copybuffersize = size > BC_SYSTEM_FILE_COPYBUFFER_SIZE ? BC_SYSTEM_FILE_COPYBUFFER_SIZE : size; + auto copybuffer = Blizzard::Memory::Allocate(copybuffersize); + + int64_t offset = 0; + + bool result = true; + + while (offset < size) { + int32_t count = size - offset >= BC_SYSTEM_FILE_COPYBUFFER_SIZE ? BC_SYSTEM_FILE_COPYBUFFER_SIZE : size - offset; + if (!Blizzard::File::Read(src, copybuffer, offset, &count)) { + result = false; + break; + } + + if (!Blizzard::File::Write(dst, copybuffer, offset, &count)) { + result = false; + break; + } + + offset += static_cast(count); + } + + Blizzard::Memory::Free(copybuffer); + Blizzard::File::Close(src); + Blizzard::File::Close(dst); + if (result == false) { + Blizzard::File::Delete(newname); + } + return result; +} + +bool LockFileDescriptorForWrite(int filefd) { + struct flock lock = {}; + lock.l_type = F_WRLCK; + + if (::fcntl(filefd, F_SETLK, &lock) == 0) { return true; } - // If rename failed due to cross-device linking (can't rename to a different device) - // Copy the file from one device to another (slow) - if (errno == EXDEV) { - if (File::Copy(parms->filename, parms->destination, false)) { - // Source is deleted once File::Copy is successful. - File::Delete(parms->filename); + if (errno == ENOTSUP) { + if (::flock(filefd, LOCK_EX | LOCK_NB) == 0) { return true; } - return true; } + BC_FILE_SET_ERROR_MSG(4, "Posix Open"); + ::close(filefd); return false; } -// Attempts to remove an empty directory. -bool RemoveDirectory(FileParms* parms) { - auto dir = parms->filename; +// open +bool Open(FileParms* parms) { + Blizzard::String::QuickNativePath name(parms->name); - // Must have directory path to remove directory. - if (!dir) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + auto read = (parms->mode & Blizzard::File::Mode::read) != 0; + auto write = (parms->mode & Blizzard::File::Mode::write) != 0; + + BLIZZARD_ASSERT(read || write); + if (!read && !write) { + BC_FILE_SET_ERROR_MSG(8, "Posix Open - Bad file mode - %s", name.ToString()); return false; } - // Convert to native path. - File::Path::QuickNative dirNative(dir); - - // Attempt rmdir - return ::rmdir(dirNative.Str()) == 0; -} - -// Change file EOF to a new offset @ parms->position and according to parms->whence. -// DESTROYING any existing data at offset >= parms->position -bool SetEOF(FileParms* parms) { - auto file = parms->stream; - - // Seek to truncation point based on position and whence - // then, read computed truncation point into parms->position - if (!File::SetPos(file, parms->position, parms->whence) || - !File::GetPos(file, parms->position)) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; + int flags; + if (!read || !write) { + flags = read ? O_RDONLY : O_WRONLY; + } else { + flags = O_RDWR; } - // Perform truncation. - auto status = ::ftruncate(file->filefd, static_cast(parms->position)); - if (status != -1) { - // Success! - - // presumably this is to invalidate stream's fileinfo (which is no longer recent) - file->hasInfo = false; - return true; - } - - // Some POSIX error has occurred. - - // Can occur as user may have tried to to resize to a large parms->position - if (errno == ENOSPC) { - // Code(9) - BC_FILE_SET_ERROR(BC_FILE_ERROR_NO_SPACE_ON_DEVICE); - return false; - } - - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; -} - -// Changes file attributes of object at parms->filename -// Sets filemode with chmod() -// Changes update times with futimes() -bool SetAttributes(FileParms* parms) { - BC_FILE_PATH(path); - - auto info = parms->info; - - if (info == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - uint32_t mode = parms->mode; - auto attributes = info->attributes; - int32_t status = 0; - auto file = parms->stream; - - if (mode & File::Mode::settimes) { -#if defined(WHOA_SYSTEM_MAC) - // Use BSD function futimes - struct timeval tvs[2]; - - tvs[0].tv_sec = Time::ToUnixTime(info->modificationTime); - tvs[0].tv_usec = 0; - - tvs[1].tv_sec = tvs[0].tv_sec; - tvs[1].tv_usec = 0; - - // Attempt to apply times to file descriptor. - status = ::futimes(file->filefd, tvs); -#else - // use Linux equivalent futimens - struct timespec tsp[2]; - tsp[0].tv_sec = Time::ToUnixTime(info->modificationTime); - tsp[0].tv_nsec = 0; - - tsp[1].tv_sec = tsp[0].tv_sec; - tsp[1].tv_nsec = 0; - status = ::futimens(file->filefd, tsp); +#if defined(WHOA_SYSTEM_LINUX) + flags |= (parms->mode & Blizzard::File::Mode::nocache) ? O_DIRECT : 0; #endif - if (status != 0) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } + flags |= ((parms->mode & Blizzard::File::Mode::truncate) != 0 ? O_TRUNC : 0) | ((parms->mode & Blizzard::File::Mode::mustexist) == 0 ? O_CREAT : 0) | ((parms->mode & Blizzard::File::Mode::mustnotexist) != 0 ? O_EXCL : 0) | ((parms->mode & Blizzard::File::Mode::create) != 0 ? O_CREAT | O_TRUNC : 0); - // If successful, also apply these changes to FileInfo inside StreamRecord. - file->info.accessTime = info->modificationTime; - file->info.modificationTime = info->modificationTime; - parms->mode &= ~(File::Mode::settimes); + auto filefd = flags & O_CREAT ? ::open(name.ToString(), flags, 0777) : ::open(name.ToString(), flags); + if (filefd == -1) { + printf("opened? '%s'\n", name.ToString()); + BC_FILE_SET_ERROR_MSG(8, "Posix Open - %s", name.ToString()); + return false; } - if (mode & File::Mode::setperms) { - File::Path::QuickNative path(parms->filename); - - // Get unix permissions - struct stat info = {}; - auto status = ::stat(path.Str(), &info); - if (status == -1) { - // Can't set attributes on a nonexistent file ૮ ・ﻌ・ა - return false; - } - - if (attributes & BC_FILE_ATTRIBUTE_READONLY) { - status = ::chmod(path.Str(), info.st_mode & 0222); - } else { - status = ::chmod(path.Str(), info.st_mode & 0777); - } - - if (status != 0) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - parms->mode &= ~(File::Mode::setperms); + if (System_File::s_EnableFileLocks) { + if ((parms->mode & Blizzard::File::Mode::shareread) == 0 && (parms->mode & Blizzard::File::Mode::write) != 0) { + if (!LockFileDescriptorForWrite(filefd)) { + return false; + } + } } + if (parms->mode & Blizzard::File::Mode::append) { + ::lseek(filefd, 0, SEEK_END); + } + + struct stat info; + if (::fstat(filefd, &info) != 0 || S_ISDIR(info.st_mode)) { + BC_FILE_SET_ERROR_MSG(2, "Posix Read - File not found - %s", name.ToString()); + ::close(filefd); + return false; + } + +#if defined(WHOA_SYSTEM_MAC) + if (parms->mode & Blizzard::File::Mode::nocache) { + ::fcntl(filefd, F_NOCACHE, 1); + } +#endif + + if (parms->mode & Blizzard::File::Mode::temporary) { + ::unlink(name.ToString()); + } + + auto file = parms->file; + + if (!file) { + auto namelength = Blizzard::String::Length(name.ToString()) + 1; + file = reinterpret_cast(Blizzard::Memory::Allocate(sizeof(Blizzard::File::StreamRecord) + namelength)); + auto filename = reinterpret_cast(file) + sizeof(Blizzard::File::StreamRecord); + file->name = filename; + Blizzard::String::Copy(filename, name.ToString(), namelength); + + } + + file->filefd = filefd; + file->haveinfo = false; + file->mode = parms->mode; + + // if (!file->unk48 || !*file->unk48) { + file->info.size = info.st_size; + // } + + auto modtime = Blizzard::Time::FromUnixTime(info.st_mtim.tv_sec); + + file->info.createtime = modtime; + file->info.writetime = modtime; + file->info.filetype = S_ISDIR(info.st_mode) ? 2 : 0; + + file->haveinfo = true; + + parms->file = file; + return true; } -bool SetPos(FileParms* parms) { - auto pos = parms->position; - auto whence = parms->whence; - auto file = parms->stream; +// read +// readp - auto result = ::lseek(file->filefd, static_cast(pos), whence); +// rmdir +bool RemoveDirectory(FileParms* parms) { + char name[BC_FILE_MAX_PATH]; + if (!parms->name) { + BC_FILE_SET_ERROR(8); + return false; + } + Blizzard::String::MakeNativePath(parms->name, name, BC_FILE_MAX_PATH); + return ::rmdir(name) == 0; +} - if (result == -1) { - if (errno == ENOSPC) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_NO_SPACE_ON_DEVICE); - return false; - } +// setcachemode +bool SetCacheMode(FileParms* parms) { +#if defined(WHOA_SYSTEM_MAC) + auto mode = parms->mode; + BLIZZARD_ASSERT(0 == (mode & ~Blizzard::File::Mode::nocache)); + Blizzard::File::Flush(parms->file); + return 0 == fcntl(parms->file->filefd, F_NOCACHE, mode & Blizzard::File::Mode::nocache); +#endif - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); +#if defined(WHOA_SYSTEM_LINUX) + // TODO: implement equivalent fcntl + BC_FILE_SET_ERROR(8); + return false; +#endif +} + +// seteof +bool SetEOF(FileParms* parms) { + if (!Blizzard::File::SetPos(parms->file, parms->offset, parms->whence)) { + BC_FILE_SET_ERROR(8); + return false; + } + + int64_t offset; + if (!Blizzard::File::GetPos(parms->file, offset)) { + BC_FILE_SET_ERROR(8); + return false; + } + + parms->offset = offset; + if (::ftruncate(parms->file->filefd, offset) == -1) { + BC_FILE_SET_ERROR(8); return false; } return true; } -bool Delete(FileParms* parms) { - File::Path::QuickNative path(parms->filename); +// setfileinfo +bool SetAttributes(FileParms* parms) { + auto file = parms->file; - return ::unlink(path.Str()) != -1; + if (parms->setinfo & BC_SYSTEM_FILE_INFO_TIMES) { + struct timeval times[2]; + + times[0].tv_sec = Blizzard::Time::ToUnixTime(file->info.writetime); + times[0].tv_usec = 0; + + times[1].tv_sec = times[0].tv_sec; + times[1].tv_usec = 0; + + if (::futimes(file->filefd, times) != 0) { + BC_FILE_SET_ERROR(8); + return false; + } + + parms->setinfo &= ~(BC_SYSTEM_FILE_INFO_TIMES); + } + + if (parms->setinfo & BC_SYSTEM_FILE_INFO_ATTRIBUTES) { + struct stat info; + if (::stat(PATH(parms->name), &info) == -1) { + BC_FILE_SET_ERROR(8); + return false; + } + + auto mode = parms->info->attributes & BC_FILE_ATTRIBUTE_READONLY + ? info.st_mode & (S_IFSOCK | S_IFLNK | S_IFIFO | S_ISUID | S_ISGID | S_ISVTX | 0555) + : info.st_mode | 0222; + + if (::chmod(PATH(parms->name), mode) != 0) { + BC_FILE_SET_ERROR(8); + return false; + } + + parms->setinfo &= ~(BC_SYSTEM_FILE_INFO_ATTRIBUTES); + } + + return parms->setinfo == 0; +} + +// setpos +bool SetPos(FileParms* parms) { + auto file = parms->file; + auto offset = static_cast(parms->offset); + auto whence = static_cast(parms->whence); + if (whence != 0) { + whence = (whence != 1) + 1; + } + + if (::lseek(file->filefd, offset, whence) == -1) { + BC_FILE_SET_ERROR(8); + return false; + } + + return true; +} + +// unlink +bool Delete(FileParms* parms) { + return ::unlink(PATH(parms->name)) != -1; +} + +// write +// writep + +// Filesystem::shutdown; +bool Shutdown(FileParms* parms) { + // ? + return true; } } // namespace Stacked } // namespace System_File -} // namespace Blizzard #endif diff --git a/bc/system/file/posix/Support.hpp b/bc/system/file/posix/Support.hpp new file mode 100644 index 0000000..826bc19 --- /dev/null +++ b/bc/system/file/posix/Support.hpp @@ -0,0 +1,6 @@ +#ifndef BC_SYSTEM_FILE_POSIX_SUPPORT_HPP +#define BC_SYSTEM_FILE_POSIX_SUPPORT_HPP + +#define _FILE_OFFSET_BITS 64 + +#endif \ No newline at end of file diff --git a/bc/system/file/posix/System_File.cpp b/bc/system/file/posix/System_File.cpp deleted file mode 100644 index ed045fb..0000000 --- a/bc/system/file/posix/System_File.cpp +++ /dev/null @@ -1,524 +0,0 @@ -#if defined(WHOA_SYSTEM_LINUX) || defined(WHOA_SYSTEM_MAC) - -#include "bc/system/file/System_File.hpp" -#include "bc/system/file/Stacked.hpp" -#include "bc/file/Path.hpp" -#include "bc/String.hpp" -#include "bc/Debug.hpp" -#include "bc/Memory.hpp" - -#include -#include -#include -#include -#include - -namespace Blizzard { -namespace System_File { - -/***************************************************** -* Begin POSIX-compatible System_File stack functions * -******************************************************/ - -// Change current process's working directory to parms->filename. -bool SetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - BLIZZARD_ASSERT(parms->filename); - - auto len = Blizzard::String::Length(parms->filename) + 1; - char wd[BC_FILE_MAX_PATH] = {0}; - File::Path::MakeNativePath(parms->filename, wd, len); - - return chdir(wd) == 0; -} - -// Close parms->stream file descriptor. -bool Close(File::Filesystem* fs, Stacked::FileParms* parms) { - auto file = parms->stream; - BLIZZARD_ASSERT(file != nullptr); - - close(file->filefd); - Memory::Free(file); - - return true; -} - -// Maybe unimplemented because Open already implements a creation flag -// so Create is unecessary? -bool Create(File::Filesystem* fs, Stacked::FileParms* parms) { - // System_File::FileError(9) - BC_FILE_SET_ERROR(BC_FILE_ERROR_UNIMPLEMENTED); - - return false; -} - -// Get the process's working directory to parms->directory. -// parms->directory comes with a cap specified by parms->directorySize. -bool GetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - if (parms->directory && parms->directorySize > 0) { - *parms->directory = '\0'; - - return getcwd(parms->directory, parms->directorySize) != 0; - } - - // System_File::FileError(8) - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; -} - -// iterate through directory calling back to parms->callback -bool ProcessDirFast(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::ProcessDirFast(parms); -} - -// Boolean return here is simply whether a file (not a folder) Exists -bool Exists(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Exists(parms); -} - -// Causes all modified data and attributes of file descriptor to be moved to -// a permanent storage device. This normally results in all in-core modified -// copies of buffers for the associated file to be written to a disk. -bool Flush(File::Filesystem* fs, Stacked::FileParms* parms) { - auto file = parms->stream; - BLIZZARD_ASSERT(file != nullptr); - -#if defined(WHOA_SYSTEM_MAC) - // from BSD syscall manual: - // "Note that while fsync() will flush all data from the host to the drive - // (i.e. the "permanent storage device"), the drive itself may not physi- - // cally write the data to the platters for quite some time and it may be - // written in an out-of-order sequence." - - // "The F_FULLFSYNC fcntl asks the drive to flush all buffered data to permanent storage." - auto status = fcntl(file->filefd, F_FULLFSYNC, 0); - - if (status == 0) { - return true; - } -#endif - // Attempts to transfer all writes in the cache buffer to the underlying storage device. - // fsync() does not return until the transfer is complete, or until an error is detected. - return fsync(file->filefd) == 0; -} - -// Attempt to request information about a filepath or an opened StreamRecord -bool GetFileInfo(File::Filesystem* fs, Stacked::FileParms* parms) { - // Set up arguments - auto filename = parms->filename; - struct stat info; - int32_t status = -1; - File::FileInfo* infoptr = parms->info; - - // Instead of a filename, a file descriptor can be supplied. - if (filename == nullptr && parms->stream != nullptr) { - // Info may be available from the file descriptor - auto file = parms->stream; - - // Read stat from from stream fd if it exists - if (file) { - status = fstat(file->filefd, &info); - } - - // If an info struct was not supplied, - // Allow user to request file info pointer stored in StreamRecord - if (infoptr == nullptr) { - infoptr = &file->info; - parms->info = infoptr; - } - } else { - // Convert filename to native style. - File::Path::QuickNative path(filename); - // read stat from converted path - status = stat(path.Str(), &info); - } - - // Set existence bool - auto exists = status != -1; - - // Collect POSIX filesystem info into File::FileInfo structure - if (exists) { - // Collect attributes. - if (S_ISDIR(info.st_mode)) { - infoptr->attributes |= BC_FILE_ATTRIBUTE_DIRECTORY; - } - - if (S_ISREG(info.st_mode)) { - infoptr->attributes |= BC_FILE_ATTRIBUTE_NORMAL; - } - - mode_t readonly = 0444; - - if ((info.st_mode & readonly) == readonly) { - infoptr->attributes |= BC_FILE_ATTRIBUTE_READONLY; - } - - infoptr->device = static_cast(info.st_dev); - infoptr->size = static_cast(info.st_size); - - infoptr->accessTime = Time::FromUnixTime(info.st_atime); - infoptr->modificationTime = Time::FromUnixTime(info.st_mtime); - infoptr->attributeModificationTime = Time::FromUnixTime(info.st_ctime); - } - - return true; -} - -bool GetFreeSpace(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetFreeSpace(parms); -} - -// Get current read/write position @parms->position -bool GetPos(File::Filesystem* fs, Stacked::FileParms* parms) { - auto cur = lseek(parms->stream->filefd, 0, SEEK_CUR); - if (cur == -1) { - // FileError(8) - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - parms->position = static_cast(cur); - - return true; -} - -// Return an index @parms->position, pointing to the end of the "root chars" portion of a string. -// On MacOS, this would be /Volumes/ -bool GetRootChars(File::Filesystem* fs, Stacked::FileParms* parms) { - auto name = parms->filename; - - if (*name == '/' || *name == '\\') { - parms->position = 1; - -#if defined(WHOA_SYSTEM_MAC) - if (name + 1 != nullptr) { - auto cmp = strncasecmp(name + 1, "Volumes", 7); - auto volume = name + 9; - - auto ch = *volume; - - while(ch != '\0' && ch != '/' && ch != '\\') { - ch = *volume++; - } - - parms->position = reinterpret_cast(volume) - reinterpret_cast(name); - - } -#endif - } - - return true; -} - -// Returns true if parms->filename is not a relative path. -bool IsAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms) { - return *parms->filename == '/' || *parms->filename == '\\'; -} - -// Return true if @parms->filename is readonly. -bool IsReadOnly(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::IsReadOnly(parms); -} - -// Take source parms->filename path and prefix it with containing path. -bool MakeAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::MakeAbsolutePath(parms); -} - -// Create directory named parms->filename -bool CreateDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::CreateDirectory(parms); -} - -// Move a file from parms->filename to parms->destination -bool Move(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Move(parms); -} - -// Copy file data from parms->filename to parms->destination -// Must only return true if file was copied entirely (Move depends on this being accurate) -bool Copy(File::Filesystem* fs, Stacked::FileParms* parms) { - auto overwrite = parms->flag; - - // Set up virtual paths - auto source = parms->filename; - auto destination = parms->destination; - - // file pointers - File::StreamRecord* st_source = nullptr; - File::StreamRecord* st_destination = nullptr; - - // Flags for working with src and dst files - auto flag_source = BC_FILE_OPEN_READ | BC_FILE_OPEN_WRITE | BC_FILE_OPEN_MUST_EXIST; - auto flag_destination = BC_FILE_OPEN_WRITE | BC_FILE_OPEN_CREATE; - if (!overwrite) { - // User commands that we cannot overwrite. Fail if file already exists - flag_destination |= BC_FILE_OPEN_MUST_NOT_EXIST; - } else { - // We are supposed to overwrite, so truncate the file to 0 bytes. - flag_destination |= BC_FILE_OPEN_TRUNCATE; - } - - // Open source file to be copied - if (!File::Open(source, flag_source, st_source)) { - // FileError(2) - BC_FILE_SET_ERROR(BC_FILE_ERROR_FILE_NOT_FOUND); - return false; - } - - // Open (or create if it doesn't exist) destination file - if (!File::Open(destination, flag_destination, st_destination)) { - File::Close(st_source); - // FileError(4) - BC_FILE_SET_ERROR(BC_FILE_ERROR_BUSY); - return false; - } - - // Determine buffer copy size - auto sz_source = File::GetFileInfo(st_source)->size; - - // copybuffer size upper limit is BC_FILE_SYSTEM_COPYBUFFER_SIZE - uint64_t sz_copybuffer = std::min(sz_source, static_cast(BC_FILE_SYSTEM_COPYBUFFER_SIZE)); - auto u8_copybuffer = reinterpret_cast(Memory::Allocate(sz_copybuffer)); - - // Loop through the source file, reading segments into copybuffer - for (uint64_t index = 0; index < sz_source; index += sz_copybuffer) { - // How many bytes to read - size_t sz_bytesToRead = static_cast(sz_source - std::min(index+sz_copybuffer, sz_source)); - size_t sz_bytesRead = 0; - size_t sz_bytesWritten = 0; - // Read data segment into copybuffer - auto status = File::Read(st_source, u8_copybuffer, sz_bytesToRead, &sz_bytesRead); - if (status) { - // Write copied segment to destination file - status = File::Write(st_destination, u8_copybuffer, sz_bytesRead, &sz_bytesWritten); - } - - if (!status) { - // either read or write failed: cleanup copy buffer - Memory::Free(u8_copybuffer); - // close files - File::Close(st_source); - File::Close(st_destination); - // Delete malformed file - File::Delete(destination); - // return bad status, but without creating a file error. - return false; - } - } - - Memory::Free(u8_copybuffer); - // close files - File::Close(st_source); - File::Close(st_destination); - - // Success! - return true; - - // Success! - return true; -} - -bool Open(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Open(parms); -} - -bool Read(File::StreamRecord* file, void* data, int64_t offset, size_t* bytes) { - // File descriptor must be initialized! - BLIZZARD_ASSERT(file != nullptr && file->filefd != -1); - - if (bytes == nullptr || *bytes == 0) { - return true; - } - - auto u8_data = reinterpret_cast(data); - - // in bytes, the length of the user's read operation. - auto size = *bytes; - // byte index. - int32_t index = 0; - // read status (or count of bytes read) - int32_t result = 0; - - // Fully read the input file according to the count requested. - // Partial reads will be merged together in the end. - while (index < size) { - // Slice length - auto slice = size - index; - - if (offset < 0) { - // Read relative to file pointer - result = read(file->filefd, u8_data, slice); - } else { - // Read absolute - result = pread(file->filefd, u8_data, slice, offset); - } - - auto increment = result; - if (increment < 1) { - increment = 0; - } - - if (offset >= 0) { - offset += increment; - } - - index += increment; - - if (result < 0) { - // append any POSIX failure to the error log - BC_FILE_SET_ERROR_MSG(100 + errno, "Posix Read - %s", file->path); - return false; - } - - if (result == 0) { - // append unexpected EOF to the error log - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_END_OF_FILE, "Posix Read - End of File - %s", file->path); - return false; - } - - u8_data += increment; - } - - // How many bytes did we read - *bytes = size - index; - - return true; -} - -// Read from parms->stream to void* buffer(parms->param) -// Reading up to #parms->size bytes -// parms->size also stores the result of how many bytes were actually read -bool Read(File::Filesystem* fs, Stacked::FileParms* parms) { - // Position is negative, so actual write position is the current seek offset of the fd. - return System_File::Read(parms->stream, parms->param, -1, &parms->size); -} - -// Specify a file position when reading from a file -bool ReadP(File::Filesystem* fs, Stacked::FileParms* parms) { - return System_File::Read(parms->stream, parms->param, parms->position, &parms->size); -} - -// Remove an directory -bool RemoveDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::RemoveDirectory(parms); -} - -// Specify disk caching preferences for accessing stream @parms->stream -bool SetCacheMode(File::Filesystem* fs, Stacked::FileParms* parms) { - auto mode = parms->mode; - auto file = parms->stream; - -#if defined(WHOA_SYSTEM_MAC) - // Require user to have clean flags (weird) - BLIZZARD_ASSERT(0 == (mode & ~File::Mode::nocache)); - - File::Flush(file); - - return 0 == fcntl(file->filefd, F_NOCACHE, mode & File::Mode::nocache); -#endif - -#if defined(WHOA_SYSTEM_LINUX) - // TODO: implement equivalent fcntl - return false; -#endif -} - -// Set end of file to parms->position. -bool SetEOF(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetEOF(parms); -} - -// Set file permissions and read/write modes. -bool SetAttributes(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetAttributes(parms); -} - -// Set the file pointer -bool SetPos(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetPos(parms); -} - -// Delete a file -bool Delete(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Delete(parms); -} - -// Write system file specifying options. -bool Write(File::StreamRecord* file, void* data, int64_t offset, size_t* bytes) { - // File descriptor must be initialized! - BLIZZARD_ASSERT(file != nullptr && file->filefd != -1); - - if (bytes == nullptr || *bytes == 0) { - return true; - } - - auto u8_data = reinterpret_cast(data); - - // in bytes, the length of the user's write operation. - auto size = *bytes; - // byte index. - int32_t index = 0; - // write status (or count of bytes written) - int32_t result = 0; - - // Fully write data buffer to file. - while (index < size) { - // Slice length - auto slice = size - index; - - if (offset < 0) { - // Write relative to current file pointer - result = write(file->filefd, u8_data, slice); - } else { - // Write at an absolute file position - result = pwrite(file->filefd, u8_data, slice, offset); - } - - auto increment = result; - if (increment < 1) { - increment = 0; - } - - if (offset >= 0) { - offset += increment; - } - - index += increment; - - if (result == -1) { - // append any POSIX failure to the error log - BC_FILE_SET_ERROR_MSG(100 + errno, "Posix Write - %s", file->path); - return false; - } - - u8_data += increment; - } - - // How many bytes did we write - *bytes = size - index; - - return true; -} - -// Write parms->param data -bool Write(File::Filesystem* fs, Stacked::FileParms* parms) { - return System_File::Write(parms->stream, parms->param, -1, &parms->size); -} - -bool WriteP(File::Filesystem* fs, Stacked::FileParms* parms) { - return System_File::Write(parms->stream, parms->param, parms->position, &parms->size); -} - -bool Shutdown(File::Filesystem* fs, Stacked::FileParms* parms) { - // ? - return true; -} - -/*************************************************** -* End POSIX-compatible System_File stack functions * -****************************************************/ - -} // namespace System_File -} // namespace Blizzard - -#endif diff --git a/bc/system/file/win/Stacked.cpp b/bc/system/file/win/Stacked.cpp index f66a58a..15adf4b 100644 --- a/bc/system/file/win/Stacked.cpp +++ b/bc/system/file/win/Stacked.cpp @@ -1,105 +1,262 @@ #if defined(WHOA_SYSTEM_WIN) -#include "bc/file/Defines.hpp" -#include "bc/file/File.hpp" -#include "bc/file/Path.hpp" -#include "bc/system/file/Stacked.hpp" -#include "bc/system/file/win/WinFile.hpp" +#include "bc/string/QuickFormat.hpp" +#include "bc/File.hpp" #include "bc/Debug.hpp" #include "bc/Memory.hpp" #include "bc/String.hpp" +#include "bc/Unicode.hpp" +#include "bc/File.hpp" +#include "bc/system/file/Stacked.hpp" +#include "bc/time/Time.hpp" #include -/******************************** -* Begin Win32 Stacked functions * -*********************************/ +#include +#include + +#define PATH(name) Blizzard::String::QuickNativePath<300UL>(name).ToString() + +#define MAKEU64(high, low) static_cast(high) << 32ULL | static_cast(low) + +#define BREAKU64(qw, high, low) \ + high = static_cast(qw >> 32); \ + low = static_cast(qw) + +#define BREAKFILEPOINTER(qw, high, low) \ + high = static_cast(qw >> 32); \ + low = static_cast(qw) -namespace Blizzard { namespace System_File { + +// converts file name from local Windows format into UTF-8 +void FromNativeName(char* buffer, int32_t buffersize, const char* name) { + uint16_t widenamebuffer[300]; + auto len = name ? Blizzard::String::Length(name) : 0; + auto widename = len + 1 > 300 + ? static_cast(Blizzard::Memory::Allocate(2 * (len + 1))) + : widenamebuffer; + + widename[MultiByteToWideChar(CP_ACP, 0, name, len, reinterpret_cast(widename), len + 1)] = 0; + + Blizzard::Unicode::ConvertUTF16to8(reinterpret_cast(buffer), buffersize, widenamebuffer, 0xFFFFFFFF, nullptr, nullptr); + + if (widenamebuffer != widename) { + Blizzard::Memory::Free(widename); + } +} + +// translate Win32 file bits into Blizzard file bits +uint32_t FromNativeAttributes(DWORD a) { + uint32_t b = 0; + if (a & FILE_ATTRIBUTE_READONLY) { + b |= BC_FILE_ATTRIBUTE_READONLY; + } + + if (a & FILE_ATTRIBUTE_HIDDEN) { + b |= BC_FILE_ATTRIBUTE_HIDDEN; + } + + if (a & FILE_ATTRIBUTE_SYSTEM) { + b |= BC_FILE_ATTRIBUTE_SYSTEM; + } + + if (a & FILE_ATTRIBUTE_ARCHIVE) { + b |= BC_FILE_ATTRIBUTE_ARCHIVE; + } + + if (a & FILE_ATTRIBUTE_TEMPORARY) { + b |= BC_FILE_ATTRIBUTE_TEMPORARY; + } + + if (a & FILE_ATTRIBUTE_DIRECTORY) { + b |= BC_FILE_ATTRIBUTE_DIRECTORY; + } + + if ((a & FILE_ATTRIBUTE_NORMAL) != 0 || (a & FILE_ATTRIBUTE_DIRECTORY) == 0) { + b |= BC_FILE_ATTRIBUTE_NORMAL; + } + + return b; +} + +// translate Blizzard file attribute bits into Win32 attribute bits. +DWORD ToNativeAttributes(uint32_t a) { + DWORD b = 0; + + if (a & BC_FILE_ATTRIBUTE_READONLY) { + b |= FILE_ATTRIBUTE_READONLY; + } + + if (a & BC_FILE_ATTRIBUTE_HIDDEN) { + b |= FILE_ATTRIBUTE_HIDDEN; + } + + if (a & BC_FILE_ATTRIBUTE_SYSTEM) { + b |= FILE_ATTRIBUTE_SYSTEM; + } + + if (a & BC_FILE_ATTRIBUTE_ARCHIVE) { + b |= FILE_ATTRIBUTE_ARCHIVE; + } + + if (a & BC_FILE_ATTRIBUTE_TEMPORARY) { + b |= FILE_ATTRIBUTE_TEMPORARY; + } + + if (a & BC_FILE_ATTRIBUTE_NORMAL) { + b |= FILE_ATTRIBUTE_NORMAL; + } + + if (a & BC_FILE_ATTRIBUTE_DIRECTORY) { + b |= FILE_ATTRIBUTE_DIRECTORY; + } + + return b; +} + +// convert FILETIME (two 32-bit values) into 64-bit unsigned integer +inline uint64_t MakeFileTime(FILETIME ft) { + return MAKEU64(ft.dwHighDateTime, ft.dwLowDateTime); +} + +// convert 64-bit unsigned integer into FILETIME (two 32-bit values) +inline FILETIME BreakFileTime(uint64_t qw) { + FILETIME ft; + BREAKU64(qw, ft.dwHighDateTime, ft.dwLowDateTime); + return ft; +} + +void GetFileInfoByFile(Blizzard::File::StreamRecord* file) { + if (!file->haveinfo) { + file->haveinfo = false; + + DWORD filesizehigh; + auto filesize = static_cast(GetFileSize(file->filehandle, &filesizehigh)); + + if (filesize == INVALID_FILE_SIZE && GetLastError()) { + return; + } + + auto info = &file->info; + + // TODO: ?????? + if (!file->unk48 || !*file->unk48) { + info->size = MAKEU64(filesizehigh, filesize); + } + + WIN32_FILE_ATTRIBUTE_DATA fileinfo; + if (::GetFileAttributesEx(PATH(file->name), GetFileExInfoStandard, &fileinfo)) { + + info->createtime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftCreationTime)); + info->writetime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftLastWriteTime)); + info->accesstime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftLastAccessTime)); + + info->attributes = FromNativeAttributes(fileinfo.dwFileAttributes); + + info->normal = info->attributes & BC_FILE_ATTRIBUTE_NORMAL != 0; + + if (fileinfo.dwFileAttributes == 0xFFFFFFFF) { + info->filetype = 0; + } else { + info->filetype = ((info->attributes & BC_FILE_ATTRIBUTE_DIRECTORY) != 0) + 1; + } + } + } + + file->haveinfo = true; +} + +bool ToCreateFlags(int32_t mode, DWORD& sharemode, DWORD& desiredaccess, DWORD& creationdisposition, DWORD& flagsandattributes) { + sharemode = (mode >> 2) & 3; + desiredaccess = ((mode << 2) | mode & Blizzard::File::Mode::write) << 29; + creationdisposition = 0; + + if ((mode & Blizzard::File::Mode::truncate) == 0 && (mode & Blizzard::File::Mode::create) == 0) { + if ((mode & Blizzard::File::Mode::mustnotexist) == 0) { + creationdisposition = mode & Blizzard::File::Mode::mustexist ? OPEN_EXISTING : OPEN_ALWAYS; + } else { + creationdisposition = CREATE_NEW; + } + } else if (mode & Blizzard::File::Mode::mustnotexist) { + creationdisposition = CREATE_NEW; + } else { + creationdisposition = CREATE_ALWAYS; + } + + flagsandattributes = FILE_ATTRIBUTE_NORMAL | (mode & Blizzard::File::Mode::temporary ? FILE_ATTRIBUTE_TEMPORARY : 0) | (mode & Blizzard::File::Mode::nocache ? FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING : 0); + + return true; +} + namespace Stacked { -bool SetWorkingDirectory(FileParms* parms) { - BLIZZARD_ASSERT(parms->filename); - return ::SetCurrentDirectory(parms->filename) != 0; +// begin stacked file functions + +bool (SetWorkingDirectory)(FileParms* parms) { + BLIZZARD_ASSERT(parms->name); + + return ::SetCurrentDirectory(PATH(parms->name)) != 0; } -bool Close(FileParms* parms) { - auto file = parms->stream; - BLIZZARD_ASSERT(file != nullptr); +bool (Close)(FileParms* parms) { + auto file = parms->file; + if (file->filehandle != INVALID_HANDLE_VALUE) { + ::CloseHandle(file->filehandle); + } - ::CloseHandle(file->filehandle); - Memory::Free(file); + Blizzard::Memory::Free(file); return true; } -bool GetWorkingDirectory(FileParms* parms) { - if (!parms->directory || !parms->directorySize) { +bool (GetWorkingDirectory)(FileParms* parms) { + if (!parms->buffer || !parms->buffersize) { // System_File::FileError(8) - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + BC_FILE_SET_ERROR(8); return false; } - auto size = static_cast(parms->directorySize); - auto directory = static_cast(parms->directory); + char cwd[BC_FILE_MAX_PATH]; + ::GetCurrentDirectory(BC_FILE_MAX_PATH, cwd); - ::GetCurrentDirectory(size, directory); + FromNativeName(parms->buffer, parms->buffersize, cwd); return true; } -bool ProcessDirFast(FileParms* parms) { - // Get parameters - auto unkflag = parms->flag; - auto directory = parms->filename; - auto callback = parms->callback; - auto param = parms->param; +bool (ProcessDirFast)(FileParms* parms) { + char dirfindpattern[512]; + Blizzard::String::Format(dirfindpattern, 512, "%s\\*", parms->name); - // Set up walk parameters - File::ProcessDirParms processDirParms; - processDirParms.root = directory; - processDirParms.param = param; - - constexpr uint32_t formatSize = BC_FILE_MAX_PATH * 2; - char formatted[formatSize] = ""; - - // Tack on a backslash as well as an asterisk. - String::Format(formatted, formatSize, "%s\\*", processDirParms.root); - - // Convert potentially large path to universal format - BC_FILE_PATH(path); - if (!File::Path::MakeNativePath(formatted, path, formatSize)) { - return false; - } - - // WIN32_FIND_DATA findData; - - auto hFindFile = ::FindFirstFile(formatted, &findData); - + auto hFindFile = ::FindFirstFile(PATH(dirfindpattern), &findData); if (hFindFile == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_INVALID_ARGUMENT, "Win32 ProcessDirFast - %s", directory); + BC_FILE_SET_ERROR_MSG(8, "failed to open %s\n", parms->name); return false; } - BC_FILE_PATH(currentPath); - processDirParms.item = currentPath; + Blizzard::File::ProcessDirParms dirwalkparms; + dirwalkparms.dir = parms->name; + dirwalkparms.param = parms->dirwalkparam; + + char item[BC_FILE_MAX_PATH]; while (true) { - // Ignore .. and . + // ignore .. and . if (findData.cFileName[0] != '.' || findData.cFileName[1] && (findData.cFileName[1] != '.' || findData.cFileName[2])) { - String::Copy(currentPath, findData.cFileName, BC_FILE_MAX_PATH); - processDirParms.itemIsDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + FromNativeName(item, BC_FILE_MAX_PATH, findData.cFileName); + dirwalkparms.item = item; + dirwalkparms.isdir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - if (!callback(processDirParms)) { + if (!parms->dirwalkcallback(dirwalkparms)) { ::FindClose(hFindFile); return true; } + } - if (!::FindNextFile(hFindFile, &findData)) { - break; - } + if (!::FindNextFile(hFindFile, &findData)) { + break; } } @@ -107,638 +264,548 @@ bool ProcessDirFast(FileParms* parms) { return false; } -bool Exists(FileParms* parms) { - auto filepath = parms->filename; - if (!filepath) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; +bool (Exists)(FileParms* parms) { + char name[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeBackslashPath(parms->name, name, sizeof(name)); + } else { + name[0] = '\0'; } - - File::Path::QuickNative filepathNative(filepath); - - auto dwFileAttributes = ::GetFileAttributes(static_cast(filepathNative.Str())); + auto dwFileAttributes = ::GetFileAttributes(static_cast(PATH(name))); if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) { - parms->info->attributes = 0; - return false; - } - - uint32_t fileAttributes = WinFile::AttributesToBC(dwFileAttributes); - parms->info->attributes = fileAttributes; - - return fileAttributes & BC_FILE_ATTRIBUTE_NORMAL; -} - -bool Flush(FileParms* parms) { - auto file = parms->stream; - - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE ) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - auto status = ::FlushFileBuffers(file->filehandle); - return status != 0; -} - -bool GetFileInfo(FileParms* parms) { - auto file = parms->stream; - auto filepath = parms->filename; - auto info = parms->info; - - if (filepath == nullptr && file == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - // StreamRecord* file may be used to supply storage of file info. (StreamRecord*->info) - if (file) { - BLIZZARD_ASSERT(file->filehandle != INVALID_HANDLE_VALUE); - if (info == nullptr) { - // telling this function to save file info into StreamRecord - info = &file->info; - } - } - - if (filepath) { - // Fetch info based on filepath. - static File::FileInfo s_noinfo = {}; - if (info == nullptr) { - info = &s_noinfo; - } - - File::Path::QuickNative filepathNative(filepath); - - WIN32_FILE_ATTRIBUTE_DATA fileAttributeData = {}; - - // Read attributes - if (!::GetFileAttributesEx(filepathNative.Str(), GetFileExInfoStandard, &fileAttributeData)) { - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_INVALID_HANDLE, "Win32 GetFileInfo - GetFileAttributesExA failed"); - return false; - } - - return WinFile::AttributeFileInfoToBC(&fileAttributeData, info); - } - - // Fetch info using opened file handle - auto filehandle = file->filehandle; - - BY_HANDLE_FILE_INFORMATION byHandleInfo; - if (!::GetFileInformationByHandle(filehandle, &byHandleInfo)) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_HANDLE); - return false; - } - - file->hasInfo = true; - parms->info = info; - - return WinFile::HandleFileInfoToBC(&byHandleInfo, info); -} - -bool GetFreeSpace(FileParms* parms) { - auto filename = parms->filename; - - if (filename == nullptr || *filename == '\0') { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - auto len = String::Length(filename) + 2; - BC_FILE_PATH(systemPath); - // UNC convert - File::Path::MakeNativePath(filename, systemPath, BC_FILE_MAX_PATH); - - // GetDiskFreeSpaceExA will fail without a trailing backslash - File::Path::ForceTrailingSeparator(systemPath, BC_FILE_MAX_PATH, BC_FILE_SYSTEM_PATH_SEPARATOR); - - ULARGE_INTEGER freeBytesAvailableToCaller = {}; - ULARGE_INTEGER totalNumberOfBytes = {}; - ULARGE_INTEGER totalNumberOfFreeBytes = {}; - - if (!GetDiskFreeSpaceExA(systemPath, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - parms->size64 = static_cast(freeBytesAvailableToCaller.QuadPart); - return true; -} - -bool GetPos(FileParms* parms) { - auto file = parms->stream; - - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - LONG high = 0; - DWORD low = ::SetFilePointer(file->filehandle, 0, &high, FILE_CURRENT); - if (low == -1 && GetLastError()) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_HANDLE); - return false; - } - - ULARGE_INTEGER ul = {}; - ul.LowPart = low; - ul.HighPart = high; - - parms->position = ul.QuadPart; - return true; -} - -bool GetRootChars(FileParms* parms) { - char cVar1 = '\0'; - int32_t counter = 0; - int32_t size = 0; - char *buffer = nullptr; - const char *path = nullptr; - char unixPathFast[256] = {0}; - const char *end = nullptr; - - path = parms->filename; - counter = String::Length(path); - size = counter + 1; - if (size > 256) { - buffer = reinterpret_cast(Memory::Allocate(size)); - } else { - buffer = unixPathFast; - } - - // Invalid without error - if (size < 0x2) { - parms->end = 0; - parms->beginning = 0; - } else { - File::Path::MakeUnivPath(path, buffer, size); - path = buffer + 1; - // Get DOS canonical path - if (buffer[1] == ':') { - cVar1 = buffer[2]; - parms->beginning = 0; - parms->end = (cVar1 == '/') + 2; - } else { - // Get UNC path - if ((*buffer == '/') && (buffer[1] == '/')) { - counter = 0; - do { - path = strchr(path + 1, '$'); - if (path == nullptr) { - parms->end = size; - parms->beginning = 0; - if (buffer != unixPathFast) { - Memory::Free(buffer); - } - return true; - } - counter = counter + 1; - } while (counter < 2); - parms->end = intptr_t(path) + (1 - (intptr_t)buffer); - } else { - parms->end = 0; - } - parms->beginning = 0; - } - } - - if (buffer != unixPathFast) { - Memory::Free(buffer); - } - - return true; -} - -bool IsAbsolutePath(FileParms* parms) { - auto path = parms->filename; - - if (!path) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - auto first = path[0]; - - if ( - // UNC - ( - ((first == '\\') || (first == '/')) && - ((path[1] == '\\' || (path[1] == '/'))) - ) && - (path[2] != '\0')) { - return true; - } - - // DOS canonical - if (isalpha(first) && (path[1] == ':')) { - if (((path[2] == '\\') || (path[2] == '/')) && (path[3] != '\0')) { - return true; - } - } - - return false; -} - -bool IsReadOnly(FileParms* parms) { - auto filepath = parms->filename; - if (!filepath) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - File::Path::QuickNative filepathNative(filepath); - - auto dwFileAttributes = ::GetFileAttributes(filepathNative.Str()); - - if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - return (dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0; -} - -bool MakeAbsolutePath(FileParms* parms) { - BC_FILE_PATH(full); - - auto path = parms->filename; - - if (path == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - ::_fullpath(full, path, BC_FILE_MAX_PATH); - - File::Path::ForceTrailingSeparator(full, BC_FILE_MAX_PATH, BC_FILE_SYSTEM_PATH_SEPARATOR); - - String::Copy(parms->directory, full, parms->directorySize); - - return true; -} - -bool CreateDirectory(FileParms* parms) { - constexpr size_t temp_size = 300; - - auto path = parms->filename; - auto recursive = parms->flag != 0; - char temp_path[temp_size] = {0}; - - String::Copy(temp_path, path, temp_size); - - File::Path::ForceTrailingSeparator(temp_path, temp_size, '\\'); - - if (recursive) { - auto p = temp_path; - if (isalpha(p[0]) && p[1] == ':') { - p += 2; - } - - // Loop through path and call CreateDirectory on path elements - for (auto p = temp_path + 1; *p != '\0'; p++) { - if (*p == '\\') { - *p = 0; - - auto attributes = ::GetFileAttributes(temp_path); - - // test path - if (attributes == INVALID_FILE_ATTRIBUTES) { - // path does not exist, create directory - if (!::CreateDirectory(temp_path, nullptr)) { - return false; - } - } else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - // not a directory - return false; - } - - *p = '\\'; - } - } - } else { - // Create only the supplied directory. - if (::GetFileAttributes(temp_path) == INVALID_FILE_ATTRIBUTES) { - if (!::CreateDirectory(temp_path, nullptr)) { - if (::GetLastError() != ERROR_ALREADY_EXISTS) { - return false; - } - } - } - } - - return true; -} - -bool Move(FileParms* parms) { - auto source = parms->filename; - auto destination = parms->destination; - - File::Path::QuickNative sourceNative(source); - File::Path::QuickNative destinationNative(destination); - - BOOL ok = ::MoveFile(sourceNative.Str(), destinationNative.Str()); - - return ok != 0; -} - -bool Copy(FileParms* parms) { - auto source = parms->filename; - auto destination = parms->destination; - - auto overwrite = parms->flag; - - // file pointers - File::StreamRecord* st_source = nullptr; - File::StreamRecord* st_destination = nullptr; - - // Flags for working with src and dst files - auto flag_source = BC_FILE_OPEN_READ | BC_FILE_OPEN_MUST_EXIST; - auto flag_destination = BC_FILE_OPEN_WRITE | BC_FILE_OPEN_CREATE; - if (!overwrite) { - // User commands that we cannot overwrite. Fail if file already exists - flag_destination |= BC_FILE_OPEN_MUST_NOT_EXIST; - } else { - // We are supposed to overwrite, so truncate the file to 0 bytes. - flag_destination |= BC_FILE_OPEN_TRUNCATE; - } - - // Open source file to be copied - if (!File::Open(source, flag_source, st_source)) { - // FileError(2) - BC_FILE_SET_ERROR(BC_FILE_ERROR_FILE_NOT_FOUND); - return false; - } - - // Open (or create if it doesn't exist) destination file - if (!File::Open(destination, flag_destination, st_destination)) { - File::Close(st_source); - // FileError(4) - BC_FILE_SET_ERROR(BC_FILE_ERROR_BUSY); - return false; - } - - // Determine buffer copy size - auto sz_source = File::GetFileInfo(st_source)->size; - - // copybuffer size upper limit is BC_FILE_SYSTEM_COPYBUFFER_SIZE - uint64_t sz_copybuffer = std::min(sz_source, static_cast(BC_FILE_SYSTEM_COPYBUFFER_SIZE)); - auto u8_copybuffer = reinterpret_cast(Memory::Allocate(sz_copybuffer)); - - // Loop through the source file, reading segments into copybuffer - for (uint64_t index = 0; index < sz_source; index += sz_copybuffer) { - // How many bytes to read - size_t sz_bytesToRead = static_cast(sz_source - std::min(index+sz_copybuffer, sz_source)); - size_t sz_bytesRead = 0; - size_t sz_bytesWritten = 0; - // Read data segment into copybuffer - auto status = File::Read(st_source, u8_copybuffer, sz_bytesToRead, &sz_bytesRead); - if (status) { - // Write copied segment to destination file - status = File::Write(st_destination, u8_copybuffer, sz_bytesRead, &sz_bytesWritten); - } - - if (!status) { - // either read or write failed: cleanup copy buffer - Memory::Free(u8_copybuffer); - // close files - File::Close(st_source); - File::Close(st_destination); - // Delete malformed file - File::Delete(destination); - // return bad status, but without creating a file error. - return false; - } - } - - Memory::Free(u8_copybuffer); - // close files - File::Close(st_source); - File::Close(st_destination); - - // Success! - return true; -} - -bool Open(FileParms* parms) { - // Path convert - auto path = parms->filename; - File::Path::QuickNative pathNative(path); - - // Open file HANDLE - auto flags = parms->flag; - bool nocache = parms->mode & File::Mode::nocache; - HANDLE handle = WinFile::Open(pathNative.Str(), flags, nocache); - - if (handle == INVALID_HANDLE_VALUE) { - DWORD err = ::GetLastError(); - if (err) { - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_GENERIC_FAILURE, "Win32 Open %s", parms->filename); - } - return false; - } - - // Successfully opened file handle. Allocate StreamRecord + path str at the end. - auto recordSize = (sizeof(File::StreamRecord) - File::StreamRecord::s_padPath) + (1 + pathNative.Size()); - auto fileData = Memory::Allocate(recordSize); - // Memory could not be allocated - if (fileData == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_OOM); - return false; - } - // Clear extra data - String::MemFill(fileData, recordSize, 0); - - // Populate fields - auto file = reinterpret_cast(fileData); - file->flags = flags; - file->filehandle = handle; - String::Copy(file->path, path, pathNative.Size()); - File::GetFileInfo(file); - - parms->stream = file; - - return true; -} - -bool Read(File::StreamRecord* file, void* data, int64_t offset, size_t* bytes) { - // File descriptor must be initialized! - BLIZZARD_ASSERT(file != nullptr && file->filehandle != INVALID_HANDLE_VALUE); - - if (bytes == nullptr || *bytes == 0) { + parms->info->filetype = 0; return true; } - if (offset > -1) { - if (!File::SetPos(file, offset, BC_FILE_SEEK_START)) { + parms->info->filetype = dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? 2 : 1; + + return true; +} + +bool (Flush)(FileParms* parms) { + auto file = parms->file; + + if (file->filehandle == INVALID_HANDLE_VALUE) { + BC_FILE_SET_ERROR(8); + return false; + } + + return ::FlushFileBuffers(file->filehandle) != 0; +} + +bool (GetFileInfo)(FileParms* parms) { + if (parms->name) { + WIN32_FILE_ATTRIBUTE_DATA fileinfo; + if (!::GetFileAttributesEx(PATH(parms->name), GetFileExInfoStandard, &fileinfo)) { + BC_FILE_SET_ERROR(8); + return false; + } + + auto info = parms->info; + + info->size = MAKEU64(fileinfo.nFileSizeHigh, fileinfo.nFileSizeLow); + + info->createtime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftCreationTime)); + info->writetime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftLastWriteTime)); + info->accesstime = Blizzard::Time::FromFileTime(MakeFileTime(fileinfo.ftLastAccessTime)); + + info->name = nullptr; + + info->attributes = FromNativeAttributes(fileinfo.dwFileAttributes); + + info->normal = info->attributes & BC_FILE_ATTRIBUTE_NORMAL != 0; + + if (fileinfo.dwFileAttributes == 0xFFFFFFFF) { + info->filetype = 0; + } else { + info->filetype = ((info->attributes & BC_FILE_ATTRIBUTE_DIRECTORY) != 0) + 1; + } + return true; + } else if (parms->file) { + auto file = parms->file; + GetFileInfoByFile(file); + parms->info = &file->info; + if (!file->info.name) { + file->info.name = file->name; + } + return true; + } + + BC_FILE_SET_ERROR(8); + return false; +} + +bool (GetFreeSpace)(FileParms* parms) { + auto name = parms->name; + if (name == nullptr || *name == '\0') { + BC_FILE_SET_ERROR(8); + return false; + } + + auto pathchars = (Blizzard::String::FindFilename(name) + 1) - name; + if (pathchars > 260) { + pathchars = 260; + } + + char path[260]; + char shortpath[260]; + Blizzard::String::Copy(path, name, pathchars); + + ULARGE_INTEGER freebytesavailable; + ULARGE_INTEGER totalbytesavailable; + + auto shortpathchars = ::GetShortPathName(PATH(path), shortpath, 260); + if (shortpathchars && shortpathchars < 260) { + if (!GetDiskFreeSpaceEx(shortpath, &freebytesavailable, &totalbytesavailable, nullptr)) { + BC_FILE_SET_ERROR(8); + return false; + } + } else { + if (!GetDiskFreeSpaceEx(PATH(path), &freebytesavailable, &totalbytesavailable, nullptr)) { + BC_FILE_SET_ERROR(8); return false; } } - DWORD dwRead = 0; + parms->offset = static_cast(freebytesavailable.QuadPart); - BOOL ok = ::ReadFile(file->filehandle, data, *bytes, &dwRead, nullptr); + return true; +} - if (ok == 0) { - // append any Win32 failure to the error log - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_BAD_FILE, "Win32 Read %p - %s", GetLastError(), file->path); +bool (GetPos)(FileParms* parms) { + auto file = parms->file; + + if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { + BC_FILE_SET_ERROR(8); return false; } - *bytes = static_cast(dwRead); - - return true; -} - -bool Read(FileParms* parms) { - return System_File::Stacked::Read(parms->stream, parms->param, -1, &parms->size); -} - -bool ReadP(FileParms* parms) { - return System_File::Stacked::Read(parms->stream, parms->param, parms->position, &parms->size); -} - -bool RemoveDirectory(FileParms* parms) { - auto dirpath = parms->filename; - if (dirpath == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + LONG high = 0; + auto low = ::SetFilePointer(file->filehandle, 0, &high, FILE_CURRENT); + if (low == INVALID_SET_FILE_POINTER && GetLastError()) { + BC_FILE_SET_ERROR(8); return false; } - File::Path::QuickNative pathNative(dirpath); - - BOOL ok = ::RemoveDirectory(dirpath); - - return ok != 0; + parms->offset = MAKEU64(high, low); + return true; } -bool SetCacheMode(FileParms* parms) { - auto file = parms->stream; +bool (GetRootChars)(FileParms* parms) { + char pathbuffer[256]; + auto pathsize = Blizzard::String::Length(parms->name) + 1; + auto path = pathsize > 256 ? reinterpret_cast(Blizzard::Memory::Allocate(pathsize)) : pathbuffer; + + if (pathsize > 2) { + Blizzard::String::MakeUnivPath(parms->name, path, pathsize); + if (path[1] == ':') { + parms->offset = (path[2] == '/') + 2; + } else if (path[0] == '/' && path[1] == '/') { + uint32_t i = 0; + auto cur = path + 1; + for (uint32_t i = 0; i < 2; i++) { + cur = strchr(cur + 1, '/'); + if (!cur) { + parms->offset = pathsize; + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + return true; + } + } + parms->offset = (cur - path) + 1; + } else { + parms->offset = 0LL; + } + } + + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + return true; +} + +bool (IsAbsolutePath)(FileParms* parms) { + auto path = parms->name; + + auto first = path[0]; + // UNC + if (((first == '\\' || first == '/') && ((path[1] == '\\' || path[1] == '/'))) && path[2] != '\0') { + return true; + } + + // DOS canonical + if (isalpha(first) && (path[1] == ':') && (((path[2] == '\\') || (path[2] == '/')) && path[3] != '\0')) { + return true; + } + + BC_FILE_SET_ERROR(8); + return false; +} + +bool (IsReadOnly)(FileParms* parms) { + if (!parms->name) { + BC_FILE_SET_ERROR(8); + return false; + } + auto dwFileAttributes = ::GetFileAttributes(PATH(parms->name)); + if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) { + BC_FILE_SET_ERROR(8); + return false; + } + + return dwFileAttributes & FILE_ATTRIBUTE_READONLY; +} + +bool (MakeAbsolutePath)(FileParms* parms) { + if (!parms->name) { + BC_FILE_SET_ERROR(8); + return false; + } + + char full[BC_FILE_MAX_PATH]; + ::_fullpath(full, PATH(parms->name), BC_FILE_MAX_PATH); + FromNativeName(parms->buffer, parms->buffersize, full); + + Blizzard::String::ForceTrailingSeparator(parms->buffer, parms->buffersize, '\0'); + + return true; +} + +// avoid clobbering +// System_File::Stacked::CreateDirectory +// into +// System_File::Stacked::CreateDirectoryA +#undef CreateDirectory +bool (CreateDirectory)(FileParms* parms) { +#define CreateDirectory CreateDirectoryA + if (!parms->name) { + BC_FILE_SET_ERROR(8); + return false; + } + + char pathbuffer[260]; + auto pathsize = Blizzard::String::Length(parms->name) + 1; + auto path = pathsize > 260 ? reinterpret_cast(Blizzard::Memory::Allocate(pathsize)) : pathbuffer; + + Blizzard::String::MakeBackslashPath(parms->name, path, pathsize); + + SECURITY_ATTRIBUTES security_attr = {}; + security_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attr.bInheritHandle = FALSE; + + if (parms->set_acl) { + ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:(A;OICI;GA;;;BU)", OWNER_SECURITY_INFORMATION, &security_attr.lpSecurityDescriptor, nullptr); + } + + if (parms->recurse) { + char leadingpath[260]; + + for (auto s = path; *s && s < (s + Blizzard::String::Length(s)); s++) { + while (*s && *s != '\\') { + s++; + } + + auto leadingpathsize = (s - path) + 2; + if (leadingpathsize > 260) { + leadingpathsize = 260; + } + if (Blizzard::String::Copy(leadingpath, path, leadingpathsize)) { + BC_FILE_SET_ERROR(8); + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + return false; + } + + Blizzard::String::QuickNativePath<300UL> directorypath(leadingpath); + + if (::CreateDirectory(directorypath.ToString(), parms->set_acl ? &security_attr : nullptr)) { + if (*s == '\0') { + break; + } + } else { + auto last_err = ::GetLastError(); + + if ((last_err != ERROR_ALREADY_EXISTS && last_err != ERROR_ACCESS_DENIED)) { + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + BC_FILE_SET_ERROR(8); + return false; + } + + auto directoryattributes = GetFileAttributes(directorypath.ToString()); + if (directoryattributes == INVALID_FILE_ATTRIBUTES || (directoryattributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + BC_FILE_SET_ERROR(8); + return false; + } + } + } + } else { + if (!::CreateDirectory(PATH(path), parms->set_acl ? &security_attr : nullptr)) { + auto last_err = ::GetLastError(); + bool result = last_err == ERROR_ALREADY_EXISTS || last_err == ERROR_ACCESS_DENIED; + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + return result; + } + } + + if (path != pathbuffer) { + Blizzard::Memory::Free(path); + } + return true; +} + +bool (Move)(FileParms* parms) { + char src[BC_FILE_MAX_PATH]; + char dst[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeBackslashPath(parms->name, src, BC_FILE_MAX_PATH); + } else { + src[0] = '\0'; + } + if (parms->newname) { + Blizzard::String::MakeBackslashPath(parms->newname, dst, BC_FILE_MAX_PATH); + } else { + dst[0] = '\0'; + } + return ::MoveFile(PATH(src), PATH(dst)) != 0; +} + +bool (Copy)(FileParms* parms) { + char srcname[BC_FILE_MAX_PATH]; + char dstname[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeBackslashPath(parms->name, srcname, BC_FILE_MAX_PATH); + } else { + srcname[0] = '\0'; + } + if (parms->newname) { + Blizzard::String::MakeBackslashPath(parms->newname, dstname, BC_FILE_MAX_PATH); + } else { + dstname[0] = '\0'; + } + + Blizzard::File::StreamRecord* src; + Blizzard::File::StreamRecord* dst; + + if (!Blizzard::File::Open(srcname, Blizzard::File::Mode::read | Blizzard::File::Mode::shareread | Blizzard::File::Mode::mustexist, src)) { + BC_FILE_SET_ERROR(2); // TODO: find out what 2 is + return false; + } + + if (!Blizzard::File::Open(dstname, Blizzard::File::Mode::write | (parms->overwrite ? Blizzard::File::Mode::create : Blizzard::File::Mode::create | Blizzard::File::Mode::mustnotexist), dst)) { + BC_FILE_SET_ERROR(4); // TODO: find out what 4 is + return false; + } + + auto size = Blizzard::File::GetFileInfo(src)->size; + auto copybuffersize = size > BC_SYSTEM_FILE_COPYBUFFER_SIZE ? BC_SYSTEM_FILE_COPYBUFFER_SIZE : size; + auto copybuffer = Blizzard::Memory::Allocate(copybuffersize); + + int64_t offset = 0; + + bool result = true; + + while (offset < size) { + auto count = static_cast(size - offset >= BC_SYSTEM_FILE_COPYBUFFER_SIZE ? BC_SYSTEM_FILE_COPYBUFFER_SIZE : size - offset); + if (!Blizzard::File::Read(src, copybuffer, offset, &count)) { + result = false; + break; + } + + if (!Blizzard::File::Write(dst, copybuffer, offset, &count)) { + result = false; + break; + } + + offset += static_cast(count); + } + + Blizzard::Memory::Free(copybuffer); + Blizzard::File::Close(src); + Blizzard::File::Close(dst); + if (!result) { + Blizzard::File::Delete(dstname); + } + return result; +} + +bool (Open)(FileParms* parms) { + char name[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeBackslashPath(parms->name, name, BC_FILE_MAX_PATH); + } else { + name[0] = '\0'; + } + + DWORD sharemode; + DWORD desiredaccess; + DWORD creationdisposition; + DWORD flagsandattributes; + if (!ToCreateFlags(parms->mode, sharemode, desiredaccess, creationdisposition, flagsandattributes)) { + BC_FILE_SET_ERROR(8); + return false; + } + + auto filehandle = CreateFile( + PATH(name), + desiredaccess, + sharemode, + nullptr, + creationdisposition, + flagsandattributes, + nullptr); + if (filehandle == INVALID_HANDLE_VALUE) { + BC_FILE_SET_ERROR_MSG(4, "Win32 Open - %s", name); + return false; + } + + auto file = parms->file; if (file == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + // alloc(sizeof(StreamRecord) + len(name) + 1) + // block of memory holds file as well as C string of the filename + auto namesize = Blizzard::String::Length(name); + file = reinterpret_cast(Blizzard::Memory::Allocate(sizeof(Blizzard::File::StreamRecord) + 1 + namesize)); + // set the name pointer inside of StreamRecord to point to the end of StreamRecord (i.e. the start of the name cstring in the memory block) + auto filename = reinterpret_cast(file) + sizeof(Blizzard::File::StreamRecord); + file->name = filename; + // copy name into memory block + Blizzard::String::Copy(filename, name, namesize + 1); + } + + file->filehandle = filehandle; + file->mode = parms->mode; + file->haveinfo = false; + file->info.name = file->name; + GetFileInfoByFile(file); + + if (parms->mode & Blizzard::File::Mode::append) { + Blizzard::File::SetPos(file, file->info.size, BC_FILE_SEEK_START); + } + + parms->file = file; + return true; +} + +bool (RemoveDirectory)(FileParms* parms) { + if (parms->recurse) { + return Blizzard::File::RemoveDirectoryAndContents(parms->name, false); + } + if (!parms->name) { + BC_FILE_SET_ERROR(8); return false; } + char namebuffer[260]; + auto namesize = Blizzard::String::Length(parms->name); + auto name = namesize > 260 ? reinterpret_cast(Blizzard::Memory::Allocate(namesize)) : namebuffer; + Blizzard::String::MakeBackslashPath(parms->name, name, 260); + + auto removed = ::RemoveDirectory(PATH(name)); + + if (name != namebuffer) { + Blizzard::Memory::Free(name); + } + + return removed != 0; +} + +bool (SetCacheMode)(FileParms* parms) { + auto file = parms->file; + + parms->mode |= ~(Blizzard::File::Mode::nocache); + + DWORD sharemode; + DWORD desiredaccess; + DWORD creationdisposition; + DWORD flagsandattributes; + if (!ToCreateFlags(parms->mode, sharemode, desiredaccess, creationdisposition, flagsandattributes)) { + BC_FILE_SET_ERROR(8); + return false; + } + + ::CloseHandle(file->filehandle); + + file->filehandle = CreateFile(PATH(file->info.name), desiredaccess, sharemode, nullptr, creationdisposition, flagsandattributes, nullptr); if (file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_HANDLE); + BC_FILE_SET_ERROR_MSG(4, "Win32 Close"); return false; } - File::Path::QuickNative pathNative(file->path); - - // Close handle and reopen - CloseHandle(file->filehandle); - - bool nocache = (parms->mode & File::Mode::nocache) != 0; - - file->filehandle = WinFile::Open(pathNative.Str(), file->flags, nocache); - - if (file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR_MSG(4, "Win32 SetCacheMode - %s", file->path); - return false; - } - - if (nocache) { - parms->mode &= ~(File::Mode::nocache); - } - return true; } -bool SetEOF(FileParms* parms) { - auto file = parms->stream; +bool (SetEOF)(FileParms* parms) { + auto file = parms->file; - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + int64_t offset; + if (!Blizzard::File::GetPos(file, offset) || !Blizzard::File::SetPos(file, parms->offset, parms->whence)) { + BC_FILE_SET_ERROR(8); return false; } - // Save current pos - int64_t originalpos = 0; + auto status = ::SetEndOfFile(file->filehandle); + Blizzard::File::SetPos(file, offset, BC_FILE_SEEK_START); - if (!File::GetPos(file, originalpos)) { - return false; - } - - int64_t newpos = parms->position; - auto whence = parms->whence; - - if (!File::SetPos(file, newpos, whence)) { - return false; - } - - BOOL b = ::SetEndOfFile(file->filehandle); - - if (!b) { - BC_FILE_SET_ERROR_MSG(4, "Win32 SetEOF - %s", file->path); - return false; - } - - // Restore original pos - return File::SetPos(file, originalpos, BC_FILE_SEEK_START); + return status != 0; } -bool SetAttributes(FileParms* parms) { - auto info = parms->info; +bool (SetAttributes)(FileParms* parms) { + if (parms->setinfo & BC_SYSTEM_FILE_INFO_TIMES) { + auto info = parms->info; - if (info == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } + auto createtime = BreakFileTime(Blizzard::Time::ToFileTime(info->createtime)); + auto lastaccesstime = BreakFileTime(Blizzard::Time::ToFileTime(info->accesstime)); + auto lastwritetime = BreakFileTime(Blizzard::Time::ToFileTime(info->writetime)); - auto mode = parms->mode; - auto attributes = info->attributes; - int32_t status = 0; - auto file = parms->stream; - auto path = parms->filename; - - if (mode & File::Mode::settimes) { - auto modTime = info->modificationTime; - - FILETIME ft = WinFile::PackTime(Time::ToWinFiletime(modTime)); - - if (::SetFileTime(file->filehandle, nullptr, nullptr, &ft)) { - file->info.modificationTime = modTime; - - parms->mode &= ~(File::Mode::settimes); - } - } - - if (mode & File::Mode::setperms) { - File::Path::QuickNative pathNative(parms->filename); - - DWORD dwAttributes = WinFile::AttributesToWin(info->attributes); - - BOOL ok = ::SetFileAttributes(pathNative.Str(), dwAttributes); - - if (!ok) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + if (!::SetFileTime(parms->file->filehandle, &createtime, &lastaccesstime, &lastwritetime)) { + BC_FILE_SET_ERROR(8); return false; } - parms->mode &= ~(File::Mode::setperms); + parms->setinfo &= ~(BC_SYSTEM_FILE_INFO_TIMES); } - return true; + if (parms->setinfo & BC_SYSTEM_FILE_INFO_ATTRIBUTES) { + if (!::SetFileAttributes(PATH(parms->file->name), ToNativeAttributes(parms->info->attributes))) { + BC_FILE_SET_ERROR(8); + return false; + } + + parms->setinfo &= ~(BC_SYSTEM_FILE_INFO_ATTRIBUTES); + } + + return parms->setinfo == 0; } -bool SetPos(FileParms* parms) { - auto file = parms->stream; +bool (SetPos)(FileParms* parms) { + auto file = parms->file; - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); + if (file->filehandle == INVALID_HANDLE_VALUE) { + BC_FILE_SET_ERROR(8); return false; } - auto offset = parms->position; - auto whence = parms->whence; + LONG distancetomovelow; + LONG distancetomovehigh; + BREAKFILEPOINTER(parms->offset, distancetomovehigh, distancetomovelow); - auto offsetLow = static_cast(offset); - auto offsetHigh = static_cast(offset >> 32); + auto movemethod = static_cast(parms->whence); + if (movemethod > FILE_END) { + movemethod = FILE_END; + } - DWORD res = ::SetFilePointer(file->filehandle, offsetLow, &offsetHigh, static_cast(whence)); - if (res == INVALID_SET_FILE_POINTER) { - res = GetLastError(); - if (res != 0) { + if (::SetFilePointer(file->filehandle, distancetomovelow, &distancetomovehigh, movemethod) == INVALID_SET_FILE_POINTER) { + if (GetLastError()) { return false; } } @@ -746,76 +813,17 @@ bool SetPos(FileParms* parms) { return true; } -bool Delete(FileParms* parms) { - auto deletePath = parms->filename; - - if (deletePath == nullptr) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; +bool (Delete)(FileParms* parms) { + char name[BC_FILE_MAX_PATH]; + if (parms->name) { + Blizzard::String::MakeBackslashPath(parms->name, name, BC_FILE_MAX_PATH); + } else { + name[0] = '\0'; } - - File::Path::QuickNative deletePathNative(deletePath); - - BOOL ok = ::DeleteFile(deletePathNative.Str()); - - return ok != 0; -} - -bool Write(FileParms* parms) { - auto file = parms->stream; - auto data = parms->param; - auto size = parms->size; - - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - DWORD numberOfBytesWritten = 0; - - BOOL ok = WriteFile(file->filehandle, data, size, &numberOfBytesWritten, nullptr); - - return ok != 0; -} - -bool WriteP(FileParms* parms) { - auto file = parms->stream; - auto data = parms->param; - auto position = parms->position; - auto size = parms->size; - - if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) { - BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT); - return false; - } - - int64_t origpos = 0; - - if (!File::GetPos(file, origpos)) { - return false; - } - - if (!File::SetPos(file, position, BC_FILE_SEEK_START)) { - return false; - } - - DWORD numberOfBytesWritten = 0; - BOOL ok = WriteFile(file->filehandle, data, size, &numberOfBytesWritten, nullptr); - if (ok == 0) { - BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_BAD_FILE, "Win32 Write - %s", file->path); - - return false; - } - - return File::SetPos(file, origpos, BC_FILE_SEEK_START); + return ::DeleteFile(PATH(name)) != 0; } } // namespace Stacked } // namespace System_File -} // namespace Blizzard - -/******************************** -* End of Win32 Stacked functions * -*********************************/ #endif diff --git a/bc/system/file/win/Support.hpp b/bc/system/file/win/Support.hpp deleted file mode 100644 index 6989eb6..0000000 --- a/bc/system/file/win/Support.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef BC_FILE_SYSTEM_WIN_SUPPORT_HPP -#define BC_FILE_SYSTEM_WIN_SUPPORT_HPP - -// Lowercase windows.h macros fight with BlizzardCore file function names -// Get out of here -#if defined(WHOA_SYSTEM_WIN) -#if defined(GetFreeSpace) -#undef GetFreeSpace -#endif -#endif - -#endif diff --git a/bc/system/file/win/System_File.cpp b/bc/system/file/win/System_File.cpp deleted file mode 100644 index 57515f1..0000000 --- a/bc/system/file/win/System_File.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#if defined(WHOA_SYSTEM_WIN) - -#include "bc/system/file/System_File.hpp" -#include "bc/system/file/Stacked.hpp" -#include "bc/file/File.hpp" -#include "bc/file/Defines.hpp" -#include "bc/Debug.hpp" -#include "bc/String.hpp" - -namespace Blizzard { -namespace System_File { - -/****************************************** -* Begin Win32 System_File stack functions * -*******************************************/ - -// Change current process's working directory to parms->filename. -bool SetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetWorkingDirectory(parms); -} - -// Close parms->stream file handle. -bool Close(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Close(parms); -} - -bool Create(File::Filesystem* fs, Stacked::FileParms* parms) { - // System_File::FileError(9) - BC_FILE_SET_ERROR(BC_FILE_ERROR_UNIMPLEMENTED); - return false; -} - -bool GetWorkingDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetWorkingDirectory(parms); -} - -bool ProcessDirFast(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::ProcessDirFast(parms); -} - -bool Exists(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Exists(parms); -} - -bool Flush(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Flush(parms); -} - -bool GetFileInfo(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetFileInfo(parms); -} - -bool GetFreeSpace(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetFreeSpace(parms); -} - -bool GetPos(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetPos(parms); -} - -bool GetRootChars(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::GetRootChars(parms); -} - -bool IsAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::IsAbsolutePath(parms); -} - -bool IsReadOnly(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::IsReadOnly(parms); -} - -bool MakeAbsolutePath(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::MakeAbsolutePath(parms); -} - -bool CreateDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::CreateDirectory(parms); -} - -bool Move(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Move(parms); -} - -bool Copy(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Copy(parms); -} - -bool Open(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Open(parms); -} - -bool Read(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Read(parms); -} - -bool ReadP(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::ReadP(parms); -} - -bool RemoveDirectory(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::RemoveDirectory(parms); -} - -bool SetCacheMode(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetCacheMode(parms); -} - -bool SetEOF(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetEOF(parms); -} - -bool SetAttributes(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetAttributes(parms); -} - -bool SetPos(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::SetPos(parms); -} - -bool Delete(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Delete(parms); -} - -bool Write(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::Write(parms); -} - -bool WriteP(File::Filesystem* fs, Stacked::FileParms* parms) { - return Stacked::WriteP(parms); -} - -bool Shutdown(File::Filesystem* fs, Stacked::FileParms* parms) { - return false; -} - -/**************************************** -* End Win32 System_File stack functions * -*****************************************/ - -} // namespace System_File -} // namespace Blizzard - -#endif diff --git a/bc/system/file/win/WinFile.cpp b/bc/system/file/win/WinFile.cpp deleted file mode 100644 index 481fcea..0000000 --- a/bc/system/file/win/WinFile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#if defined(WHOA_SYSTEM_WIN) - -#include "bc/file/Defines.hpp" -#include "bc/system/file/win/WinFile.hpp" - -#include - -namespace Blizzard { -namespace WinFile { - -// Translate Win32 file bits into Blizzard file bits. -uint32_t AttributesToBC(DWORD dwFileAttributes) { - uint32_t fileAttributes = 0; - if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) { - fileAttributes |= BC_FILE_ATTRIBUTE_READONLY; - } - - if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { - fileAttributes |= BC_FILE_ATTRIBUTE_HIDDEN; - } - - if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { - fileAttributes |= BC_FILE_ATTRIBUTE_SYSTEM; - } - - if (dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) { - fileAttributes |= BC_FILE_ATTRIBUTE_TEMPORARY; - } - - if (dwFileAttributes & FILE_ATTRIBUTE_NORMAL) { - fileAttributes |= BC_FILE_ATTRIBUTE_NORMAL; - } - - if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - fileAttributes |= BC_FILE_ATTRIBUTE_DIRECTORY; - } - - return fileAttributes; -} - -// Translate Blizzard file attribute bits into Win32 attribute bits. -DWORD AttributesToWin(uint32_t fileAttributes) { - DWORD dwFileAttributes = 0; - - if (fileAttributes & BC_FILE_ATTRIBUTE_READONLY) { - dwFileAttributes |= FILE_ATTRIBUTE_READONLY; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_HIDDEN) { - dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_SYSTEM) { - dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_ARCHIVE) { - dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_TEMPORARY) { - dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_NORMAL) { - dwFileAttributes |= FILE_ATTRIBUTE_NORMAL; - } - - if (fileAttributes & BC_FILE_ATTRIBUTE_DIRECTORY) { - dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; - } - - return dwFileAttributes; -} - -// Convert FILETIME (two 32-bit values) into 64-bit unsigned integer. -uint64_t UnpackTime(FILETIME ft) { - ULARGE_INTEGER ul = {}; - ul.HighPart = ft.dwHighDateTime; - ul.LowPart = ft.dwLowDateTime; - return static_cast(ul.QuadPart); -} - -// Convert 64-bit unsigned integer into FILETIME (two 32-bit values) -FILETIME PackTime(uint64_t qw) { - ULARGE_INTEGER ul = {}; - ul.QuadPart = qw; - FILETIME ft = {}; - ft.dwHighDateTime = ul.HighPart; - ft.dwLowDateTime = ul.LowPart; - return ft; -} - -// Returns true if BY_HANDLE_FILE_INFORMATION is successfully converted to File::FileInfo. -bool HandleFileInfoToBC( - LPBY_HANDLE_FILE_INFORMATION winInfo, - Blizzard::File::FileInfo* info) { - if (!info) { - return false; - } - - info->attributes = AttributesToBC(winInfo->dwFileAttributes); - info->device = static_cast(winInfo->dwVolumeSerialNumber); - info->size = (static_cast(winInfo->nFileSizeHigh) << 32ULL) | static_cast(winInfo->nFileSizeLow); - - info->accessTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftLastAccessTime)); - info->modificationTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftLastWriteTime)); - info->attributeModificationTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftCreationTime)); - - return true; -} - -// Returns true if WIN32_FILE_ATTRIBUTE_DATA is successfully converted to File::FileInfo. -bool AttributeFileInfoToBC( - LPWIN32_FILE_ATTRIBUTE_DATA winInfo, - Blizzard::File::FileInfo* info) { - - if (!info) { - return false; - } - - info->attributes = AttributesToBC(winInfo->dwFileAttributes); - info->device = 0; - info->size = (static_cast(winInfo->nFileSizeHigh) << 32ULL) | static_cast(winInfo->nFileSizeLow); - - info->accessTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftLastAccessTime)); - info->modificationTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftLastWriteTime)); - info->attributeModificationTime = Blizzard::Time::FromWinFiletime(UnpackTime(winInfo->ftCreationTime)); - - return true; -} - -// Open a HANDLE to a file, using BlizzardCore flags. -HANDLE Open(const char* systemPath, uint32_t flags, bool nocache) { - // expand flags - bool read = flags & BC_FILE_OPEN_READ; - bool write = flags & BC_FILE_OPEN_WRITE; - bool shareRead = flags & BC_FILE_OPEN_SHARE_READ; - bool shareWrite = flags & BC_FILE_OPEN_SHARE_WRITE; - bool mustNotExist = flags & BC_FILE_OPEN_MUST_NOT_EXIST; - bool mustExist = flags & BC_FILE_OPEN_MUST_EXIST; - bool create = flags & BC_FILE_OPEN_CREATE; - bool truncate = flags & BC_FILE_OPEN_TRUNCATE; - - // BLIZZARD_ASSERT(read || write); - - // Start building arguments to CreateFile() - DWORD desiredAccess = 0; - DWORD shareMode = 0; - DWORD createDisposition = 0; - DWORD flagsAndAttributes = 0; - - if (nocache) { - flagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; - } - - // Setup desired access - if (read) { - desiredAccess |= GENERIC_READ; - } - if (write) { - desiredAccess |= GENERIC_WRITE; - } - - // Setup share policy - if (shareRead) { - shareMode |= FILE_SHARE_READ; - } - if (shareWrite) { - shareMode |= FILE_SHARE_WRITE; - } - - // Setup create disposition - if (create && mustNotExist) { - createDisposition = CREATE_NEW; - } else if (create && !mustNotExist) { - createDisposition = CREATE_ALWAYS; - } else if (truncate) { - createDisposition = TRUNCATE_EXISTING; - } else if (mustExist) { - createDisposition = OPEN_EXISTING; - } else { - createDisposition = OPEN_ALWAYS; - } - - return CreateFileA(systemPath, desiredAccess, shareMode, nullptr, createDisposition, flagsAndAttributes, 0); -} - -} // namespace WinFile -} // namespace Blizzard - -#endif diff --git a/bc/system/file/win/WinFile.hpp b/bc/system/file/win/WinFile.hpp deleted file mode 100644 index 5fef50a..0000000 --- a/bc/system/file/win/WinFile.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef BC_FILE_SYSTEM_WIN_UTILS_HPP -#define BC_FILE_SYSTEM_WIN_UTILS_HPP - -#include "bc/file/Types.hpp" - -#include -#include - -namespace Blizzard { -namespace WinFile { - -// Translate Win32 file bits into Blizzard file bits. -uint32_t AttributesToBC(DWORD dwFileAttributes); - -DWORD AttributesToWin(uint32_t attributes); - -uint64_t UnpackTime(FILETIME ft); - -FILETIME PackTime(uint64_t qw); - -// Returns true if BY_HANDLE_FILE_INFORMATION is successfully converted to File::FileInfo. -bool HandleFileInfoToBC( - LPBY_HANDLE_FILE_INFORMATION winInfo, - Blizzard::File::FileInfo* info); - -// Returns true if WIN32_FILE_ATTRIBUTE_DATA is successfully converted to File::FileInfo. -bool AttributeFileInfoToBC( - LPWIN32_FILE_ATTRIBUTE_DATA winInfo, - Blizzard::File::FileInfo* info); - -HANDLE Open(const char* systemPath, uint32_t flags, bool nocache); - -} // namespace WinFile -} // namespace Blizzard - -#endif