mirror of
https://github.com/thunderbrewhq/bc.git
synced 2025-12-12 01:52:30 +00:00
feat(file): implement filesystem utilities
This commit is contained in:
parent
5e6af0ea70
commit
bd65df59e9
26 changed files with 4163 additions and 5 deletions
|
|
@ -1,10 +1,23 @@
|
|||
file(GLOB BC_SOURCES
|
||||
"*.cpp"
|
||||
"lock/*.cpp"
|
||||
"os/*.cpp"
|
||||
"file/*.cpp"
|
||||
"time/*.cpp"
|
||||
"system/*.cpp"
|
||||
"file/system/*.cpp"
|
||||
"time/system/*.cpp"
|
||||
)
|
||||
|
||||
if(DEFINED WHOA_SYSTEM_WIN)
|
||||
file(GLOB BC_FILE_SYSTEM_SOURCES "file/system/win/*.cpp")
|
||||
endif()
|
||||
|
||||
if(DEFINED WHOA_SYSTEM_LINUX OR WHOA_SYSTEM_MAC)
|
||||
file(GLOB BC_FILE_SYSTEM_SOURCES "file/system/posix/*.cpp")
|
||||
endif()
|
||||
|
||||
list(APPEND BC_SOURCES ${BC_FILE_SYSTEM_SOURCES})
|
||||
|
||||
add_library(bc STATIC
|
||||
${BC_SOURCES}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@
|
|||
(void)0
|
||||
#endif
|
||||
|
||||
#define BLIZZARD_VALIDATE(x, y, ...) \
|
||||
if (!(x)) { \
|
||||
Blizzard::Debug::Assert(!y, __FILE__, __LINE__); \
|
||||
return __VA_ARGS__; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
namespace Blizzard {
|
||||
namespace Debug {
|
||||
|
||||
|
|
|
|||
235
bc/String.cpp
235
bc/String.cpp
|
|
@ -1,7 +1,61 @@
|
|||
#include "bc/String.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
int32_t Blizzard::String::Copy(char* dst, const char* src, size_t len) {
|
||||
namespace Blizzard {
|
||||
namespace String {
|
||||
|
||||
int32_t Append(char* buf, const char* appended, size_t cap) {
|
||||
const char* dx = nullptr;
|
||||
int bytesAppended = 0;
|
||||
char* outBytes = nullptr;
|
||||
char* ceiling = nullptr;
|
||||
char inByte = '\0';
|
||||
|
||||
if (!cap || !buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ceiling = buf + (cap - 1);
|
||||
|
||||
if (buf > ceiling) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inByte = *buf;
|
||||
outBytes = buf;
|
||||
|
||||
while (inByte != '\0') {
|
||||
outBytes = outBytes + 1;
|
||||
if (ceiling < outBytes) {
|
||||
return outBytes - buf;
|
||||
}
|
||||
inByte = *outBytes;
|
||||
}
|
||||
|
||||
if ((outBytes <= ceiling) && appended != nullptr) {
|
||||
if (outBytes < ceiling) {
|
||||
inByte = *appended;
|
||||
|
||||
while (inByte != '\0') {
|
||||
*outBytes = inByte;
|
||||
outBytes = outBytes + 1;
|
||||
if (ceiling <= outBytes) {
|
||||
break;
|
||||
}
|
||||
appended = appended + 1;
|
||||
inByte = *appended;
|
||||
}
|
||||
}
|
||||
*outBytes = '\0';
|
||||
}
|
||||
|
||||
return int32_t(reinterpret_cast<uintptr_t>(outBytes) - reinterpret_cast<uintptr_t>(buf));
|
||||
}
|
||||
|
||||
int32_t Copy(char* dst, const char* src, size_t len) {
|
||||
if (!len || !dst) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -28,7 +82,7 @@ int32_t Blizzard::String::Copy(char* dst, const char* src, size_t len) {
|
|||
}
|
||||
|
||||
v4 = (v5++)[1];
|
||||
} while ( v4 );
|
||||
} while (v4);
|
||||
|
||||
result = v6 - dst;
|
||||
} else {
|
||||
|
|
@ -41,7 +95,89 @@ int32_t Blizzard::String::Copy(char* dst, const char* src, size_t len) {
|
|||
return result;
|
||||
}
|
||||
|
||||
uint32_t Blizzard::String::Length(const char* str) {
|
||||
// Find first occurence of char ch within string str
|
||||
// returns ptr to first occurence, or null if it is not found
|
||||
char* Find(char* str, char ch, size_t len) {
|
||||
// Check if the string is null or empty.
|
||||
if (str == nullptr || len == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ptr = str;
|
||||
auto end = str + len;
|
||||
|
||||
// Loop through the string, looking for the character.
|
||||
while (ptr < end) {
|
||||
if (*ptr == '\0' && ch != '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (*ptr == ch) {
|
||||
// Return the address of the first occurrence.
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Increment the pointer.
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// The character was not found.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* FindFilename(const char* str) {
|
||||
char ch = 0;
|
||||
const char* result = nullptr;
|
||||
auto ptr = str;
|
||||
|
||||
if (str == nullptr) {
|
||||
return "";
|
||||
}
|
||||
|
||||
do {
|
||||
do {
|
||||
result = ptr;
|
||||
ch = *str;
|
||||
str = str + 1;
|
||||
ptr = str;
|
||||
} while (ch == '/');
|
||||
} while ((ch == '\\') || (ptr = result, ch != '\0'));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Format a string into char* dest, not exceeding uint32_t length.
|
||||
void Format(char* dst, size_t capacity, const char* format, ...) {
|
||||
if (!dst || capacity == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!format && capacity >= 1) {
|
||||
*dst = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
auto formatNative = format;
|
||||
|
||||
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
||||
constexpr size_t translatedSize = 2048;
|
||||
|
||||
// POSIX formatting convention requires %ll for 64-bit printing
|
||||
// Search-and-replace all instances of %I64 with %ll
|
||||
char translated[translatedSize] = {0};
|
||||
|
||||
Translate(format, translated, translatedSize, "%I64", "%ll");
|
||||
|
||||
formatNative = translated;
|
||||
#endif
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
vsnprintf(dst, capacity, formatNative, args);
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
uint32_t Length(const char* str) {
|
||||
if (str) {
|
||||
return strlen(str);
|
||||
}
|
||||
|
|
@ -49,6 +185,97 @@ uint32_t Blizzard::String::Length(const char* str) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Blizzard::String::MemFill(void* dst, uint32_t len, uint8_t fill) {
|
||||
int32_t MemCompare(void* p1, void *p2, size_t len) {
|
||||
return memcmp(p1, p2, len);
|
||||
}
|
||||
|
||||
void MemCopy(void* dst, const void* src, size_t len) {
|
||||
memmove(dst, src, len);
|
||||
}
|
||||
|
||||
void MemFill(void* dst, uint32_t len, uint8_t fill) {
|
||||
memset(dst, fill, len);
|
||||
}
|
||||
|
||||
void Translate(const char* src, char* dest, size_t destSize, const char* pattern, const char* replacement) {
|
||||
if (dest == nullptr || pattern == nullptr || replacement == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (src == nullptr) {
|
||||
src = dest;
|
||||
}
|
||||
|
||||
// Cap-1 because we need to always affix a null character.
|
||||
auto destCeiling = dest + destSize - 1;
|
||||
auto srcCeiling = src + Length(src) + 1;
|
||||
|
||||
auto patternLen = Length(pattern);
|
||||
auto replacementLen = Length(replacement);
|
||||
|
||||
// Current read pointer
|
||||
auto srcPtr = src;
|
||||
// Current write pointer
|
||||
auto destPtr = dest;
|
||||
|
||||
// Process string byte by byte
|
||||
// Until dest ceiling is reached
|
||||
while (destPtr < destCeiling && srcPtr < srcCeiling) {
|
||||
auto byte = *srcPtr;
|
||||
|
||||
if (byte == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
// If this byte is found at the start of pattern
|
||||
if (byte == *pattern) {
|
||||
// Calculate size of replacement bytes to copy
|
||||
// Read size must not read outside of bounds
|
||||
size_t copySize = replacementLen;
|
||||
if ((destPtr + copySize) >= destCeiling) {
|
||||
copySize = destCeiling - destPtr;
|
||||
}
|
||||
|
||||
// Look for the rest of the pattern
|
||||
auto substring = strstr(srcPtr, pattern);
|
||||
// If the read pointer is indeed pointing to an instance of the pattern,
|
||||
if (substring == srcPtr) {
|
||||
// Copy replacement data instead of original pattern.
|
||||
MemCopy(destPtr, replacement, copySize);
|
||||
destPtr += replacementLen;
|
||||
srcPtr += patternLen;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// copy a single byte
|
||||
*destPtr = *srcPtr;
|
||||
srcPtr++;
|
||||
destPtr++;
|
||||
}
|
||||
|
||||
*destPtr = '\0';
|
||||
}
|
||||
|
||||
void VFormat(char* dst, size_t capacity, const char* format, va_list args) {
|
||||
char buffer[0x800] = {0};
|
||||
|
||||
auto formatNative = format;
|
||||
|
||||
#if !defined(WHOA_SYSTEM_WIN)
|
||||
Translate(format, buffer, 0x800, "%I64", "%ll");
|
||||
formatNative = buffer;
|
||||
#endif
|
||||
|
||||
if (format == nullptr) {
|
||||
if (capacity != 0) {
|
||||
*dst = '\0';
|
||||
}
|
||||
} else {
|
||||
vsnprintf(dst, capacity, formatNative, args);
|
||||
dst[capacity - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace String
|
||||
} // namespace Blizzard
|
||||
|
|
|
|||
|
|
@ -3,15 +3,63 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
#define BC_FILE_SYSTEM_PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define BC_FILE_SYSTEM_PATH_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
namespace Blizzard {
|
||||
namespace String {
|
||||
|
||||
// Types
|
||||
|
||||
template<size_t Cap>
|
||||
class QuickFormat {
|
||||
public:
|
||||
char buffer[Cap];
|
||||
|
||||
QuickFormat(const char* format, ...);
|
||||
const char* Str();
|
||||
};
|
||||
|
||||
// Functions
|
||||
int32_t Append(char* dst, const char* src, size_t cap);
|
||||
|
||||
int32_t Copy(char* dst, const char* src, size_t len);
|
||||
|
||||
char* Find(char* str, char ch, size_t len);
|
||||
|
||||
const char* FindFilename(const char* str);
|
||||
|
||||
void Format(char* dest, size_t capacity, const char* format, ...);
|
||||
|
||||
uint32_t Length(const char* str);
|
||||
|
||||
void MemFill(void* dst, uint32_t len, uint8_t fill);
|
||||
|
||||
void MemCopy(void* dst, const void* src, size_t len);
|
||||
|
||||
int32_t MemCompare(void* p1, void *p2, size_t len);
|
||||
|
||||
void Translate(const char* src, char* dest, size_t destSize, const char* pattern, const char* replacement);
|
||||
|
||||
void VFormat(char* dst, size_t capacity, const char* format, va_list args);
|
||||
|
||||
template <size_t Cap>
|
||||
QuickFormat<Cap>::QuickFormat(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
VFormat(this->buffer, Cap, format, args);
|
||||
}
|
||||
|
||||
template <size_t Cap>
|
||||
const char* QuickFormat<Cap>::Str() {
|
||||
return static_cast<const char*>(this->buffer);
|
||||
}
|
||||
|
||||
} // namespace String
|
||||
} // namespace Blizzard
|
||||
|
||||
|
|
|
|||
52
bc/file/Defines.hpp
Normal file
52
bc/file/Defines.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#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
|
||||
#define BC_FILE_ATTRIBUTE_READONLY 0x01
|
||||
#define BC_FILE_ATTRIBUTE_HIDDEN 0x02
|
||||
#define BC_FILE_ATTRIBUTE_SYSTEM 0x04
|
||||
#define BC_FILE_ATTRIBUTE_ARCHIVE 0x08
|
||||
#define BC_FILE_ATTRIBUTE_TEMPORARY 0x10
|
||||
#define BC_FILE_ATTRIBUTE_NORMAL 0x20
|
||||
#define BC_FILE_ATTRIBUTE_DIRECTORY 0x40
|
||||
|
||||
// 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_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
|
||||
#define BC_FILE_SEEK_START 0
|
||||
#define BC_FILE_SEEK_CURRENT 1
|
||||
#define BC_FILE_SEEK_END 2
|
||||
|
||||
// Error handling utilities
|
||||
#define BC_FILE_SET_ERROR(errorcode) ::Blizzard::File::SetLastError(static_cast<int32_t>(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)
|
||||
|
||||
#endif
|
||||
332
bc/file/File.cpp
Normal file
332
bc/file/File.cpp
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
#include "bc/file/File.hpp"
|
||||
#include "bc/file/Filesystem.hpp"
|
||||
#include "bc/file/system/Stacked.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cinttypes>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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<uint32_t>(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 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<size_t>(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<size_t>(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
|
||||
54
bc/file/File.hpp
Normal file
54
bc/file/File.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#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/file/system/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 Delete(const char* path);
|
||||
|
||||
bool Exists(const char* path);
|
||||
|
||||
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 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
|
||||
21
bc/file/Filesystem.cpp
Normal file
21
bc/file/Filesystem.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include "bc/file/Filesystem.hpp"
|
||||
#include <cstddef>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace File {
|
||||
|
||||
bool Filesystem::Do(Filesystem::Call fscall, System_File::Stacked::FileParms* parms) {
|
||||
uint32_t callindex = static_cast<uint32_t>(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<Filesystem::CallFunc>(this->funcs[callindex]);
|
||||
// Do call
|
||||
return func(this, parms);
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
} // namespace Blizzard
|
||||
|
||||
66
bc/file/Filesystem.hpp
Normal file
66
bc/file/Filesystem.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef BC_FILE_FILESYSTEM_HPP
|
||||
#define BC_FILE_FILESYSTEM_HPP
|
||||
|
||||
#include "bc/file/system/Types.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace File {
|
||||
|
||||
// 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<size_t>(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);
|
||||
};
|
||||
|
||||
} // namespace File
|
||||
} // namespace Blizzard
|
||||
|
||||
#endif
|
||||
194
bc/file/Path.cpp
Normal file
194
bc/file/Path.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#include "bc/file/Path.hpp"
|
||||
#include "bc/String.hpp"
|
||||
#include "bc/Memory.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace File {
|
||||
namespace Path {
|
||||
|
||||
// Classes
|
||||
|
||||
QuickNative::QuickNative(const char* path) {
|
||||
this->size = 0;
|
||||
this->fatpath = nullptr;
|
||||
this->fastpath[0] = '\0';
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Null byte
|
||||
constexpr size_t reserved = 1;
|
||||
|
||||
this->size = String::Length(path) + reserved;
|
||||
|
||||
if (this->size < BC_FILE_MAX_PATH) {
|
||||
// (fast)
|
||||
MakeNativePath(path, this->fastpath, BC_FILE_MAX_PATH);
|
||||
} else {
|
||||
// (slow)
|
||||
this->fatpath = reinterpret_cast<char*>(Memory::Allocate(this->size));
|
||||
MakeNativePath(path, this->fatpath, this->size);
|
||||
}
|
||||
}
|
||||
|
||||
QuickNative::~QuickNative() {
|
||||
if (this->fatpath != nullptr) {
|
||||
Memory::Free(this->fatpath);
|
||||
}
|
||||
}
|
||||
|
||||
const char* QuickNative::Str() {
|
||||
if (this->fatpath != nullptr) {
|
||||
return this->fatpath;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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
|
||||
51
bc/file/Path.hpp
Normal file
51
bc/file/Path.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef BC_FILE_PATH_HPP
|
||||
#define BC_FILE_PATH_HPP
|
||||
|
||||
#include "bc/file/Defines.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
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* fatpath;
|
||||
|
||||
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
|
||||
76
bc/file/Types.hpp
Normal file
76
bc/file/Types.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef BC_FILE_TYPES_HPP
|
||||
#define BC_FILE_TYPES_HPP
|
||||
|
||||
#include "bc/file/system/Defines.hpp"
|
||||
#include "bc/time/Time.hpp"
|
||||
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace File {
|
||||
|
||||
// Types
|
||||
|
||||
// Used by SetCacheMode
|
||||
enum Mode {
|
||||
setperms = 4,
|
||||
settimes = 16,
|
||||
nocache = 64
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class ProcessDirParms {
|
||||
public:
|
||||
const char* root = nullptr;
|
||||
const char* item = nullptr;
|
||||
bool itemIsDirectory = false;
|
||||
void* param = nullptr;
|
||||
};
|
||||
|
||||
typedef bool (*ProcessDirCallback)(const ProcessDirParms&);
|
||||
|
||||
class StreamRecord {
|
||||
public:
|
||||
static constexpr size_t s_padPath = 80;
|
||||
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
HANDLE filehandle;
|
||||
#endif
|
||||
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
||||
// File descriptor
|
||||
int32_t 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];
|
||||
|
||||
};
|
||||
|
||||
} // namespace File
|
||||
} // namespace Blizzard
|
||||
|
||||
#endif
|
||||
146
bc/os/File.cpp
Normal file
146
bc/os/File.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "bc/os/File.hpp"
|
||||
#include "bc/file/Defines.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
|
||||
HOSFILE OsCreateFile(const char* fileName, uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition, uint32_t flagsAndAttributes, uint32_t extendedFileType) {
|
||||
// Ensure sanity
|
||||
BLIZZARD_VALIDATE(fileName, "invalid filename", nullptr);
|
||||
BLIZZARD_VALIDATE(desiredAccess != 0, "invalid desired access");
|
||||
BLIZZARD_VALIDATE(createDisposition <= OS_TRUNCATE_EXISTING, "invalid create disposition", nullptr);
|
||||
|
||||
// Read/write flags
|
||||
if (desiredAccess & OS_GENERIC_READ) {
|
||||
flags |= BC_FILE_OPEN_READ;
|
||||
}
|
||||
if (desiredAccess & OS_GENERIC_WRITE) {
|
||||
flags |= BC_FILE_OPEN_WRITE;
|
||||
}
|
||||
|
||||
// Allow other users to access the file in read and/or write mode
|
||||
if (shareMode & OS_FILE_SHARE_READ) {
|
||||
flags |= BC_FILE_OPEN_SHARE_READ;
|
||||
}
|
||||
if (shareMode & OS_FILE_SHARE_WRITE) {
|
||||
flags |= BC_FILE_OPEN_SHARE_WRITE;
|
||||
}
|
||||
|
||||
// Convert createDisposition into BC flags
|
||||
switch (createDisposition) {
|
||||
case OS_CREATE_NEW:
|
||||
// flags |= 0xC00;
|
||||
flags |= BC_FILE_OPEN_CREATE | BC_FILE_OPEN_MUST_NOT_EXIST;
|
||||
break;
|
||||
case OS_CREATE_ALWAYS:
|
||||
// flags |= 0x400;
|
||||
flags |= BC_FILE_OPEN_CREATE;
|
||||
break;
|
||||
case OS_OPEN_EXISTING:
|
||||
// flags |= 0x1000
|
||||
flags |= BC_FILE_OPEN_MUST_EXIST;
|
||||
break;
|
||||
case OS_OPEN_ALWAYS:
|
||||
// flags |= 0x200;
|
||||
flags |= BC_FILE_OPEN_ALWAYS;
|
||||
break;
|
||||
case OS_TRUNCATE_EXISTING:
|
||||
// flags |= 0x100;
|
||||
flags |= BC_FILE_OPEN_TRUNCATE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Open file
|
||||
Blizzard::File::StreamRecord* stream;
|
||||
bool success = Blizzard::File::Open(fileName, flags, &stream);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set attributes
|
||||
OsSetFileAttributes(fileName, flagsAndAttributes);
|
||||
return stream;
|
||||
}
|
||||
|
||||
int32_t OsSetFileAttributes(const char* fileName, uint32_t attributes) {
|
||||
BLIZZARD_ASSERT(fileName);
|
||||
|
||||
// Translate OS file attribute bits into BlizzardCore attribute bits.
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (attributes & OS_FILE_ATTRIBUTE_READONLY) {
|
||||
// flags |= 1;
|
||||
flags |= BC_FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_HIDDEN) {
|
||||
// flags |= 2;
|
||||
flags |= BC_FILE_ATTRIBUTE_HIDDEN;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_SYSTEM) {
|
||||
// flags |= 4
|
||||
flags |= BC_FILE_ATTRIBUTE_SYSTEM;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_ARCHIVE) {
|
||||
// flags |= 8;
|
||||
flags |= BC_FILE_ATTRIBUTE_ARCHIVE;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_TEMPORARY) {
|
||||
// flags |= 0x10;
|
||||
flags |= BC_FILE_ATTRIBUTE_TEMPORARY;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_NORMAL) {
|
||||
// flags |= 0x20;
|
||||
flags |= BC_FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
if (attributes & OS_FILE_ATTRIBUTE_DIRECTORY) {
|
||||
// flags |= 0x40;
|
||||
flags |= BC_FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
|
||||
return Blizzard::File::SetAttributes(fileName, flags);
|
||||
}
|
||||
|
||||
uint64_t OsGetFileSize(HOSFILE fileHandle) {
|
||||
auto info = Blizzard::File::GetInfo(reinterpret_cast<Blizzard::File::StreamRecord*>(fileHandle));
|
||||
return info->size;
|
||||
}
|
||||
|
||||
int32_t OsReadFile(HOSFILE fileHandle, void* buffer, size_t bytesToRead, size_t* bytesRead) {
|
||||
BLIZZARD_ASSERT(buffer);
|
||||
BLIZZARD_ASSERT(bytesToRead);
|
||||
|
||||
return Blizzard::File::Read(fileHandle, buffer, bytesToRead, bytesRead);
|
||||
}
|
||||
|
||||
int32_t OsWriteFile(HOSFILE fileHandle, void* buffer, size_t bytesToWrite, size_t* bytesWritten) {
|
||||
if (buffer != nullptr && bytesToWrite != 0) {
|
||||
return Blizzard::File::Write(fileHandle, buffer, bytesToWrite, bytesWritten);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod) {
|
||||
BLIZZARD_ASSERT(moveMethod <= BC_FILE_SEEK_END);
|
||||
|
||||
int64_t position;
|
||||
|
||||
if (moveMethod != 0 && !Blizzard::File::GetPos(fileHandle, position)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t seeks[3] = {
|
||||
BC_FILE_SEEK_START,
|
||||
BC_FILE_SEEK_CURRENT,
|
||||
BC_FILE_SEEK_END
|
||||
};
|
||||
|
||||
if (!Blizzard::File::SetPos(fileHandle, position, seeks[moveMethod])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return position + distanceToMove;
|
||||
}
|
||||
|
||||
void OsCloseFile(HOSFILE fileHandle) {
|
||||
Blizzard::File::Close(static_cast<Blizzard::File::StreamRecord*>(fileHandle));
|
||||
}
|
||||
65
bc/os/File.hpp
Normal file
65
bc/os/File.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef BC_OS_FILE_HPP
|
||||
#define BC_OS_FILE_HPP
|
||||
|
||||
#include "bc/file/Types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
|
||||
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 OsReadFile(HOSFILE fileHandle, void* buffer, uint32_t bytesToRead, uint32_t* bytesRead);
|
||||
|
||||
void OsCloseFile(HOSFILE fileHandle);
|
||||
|
||||
int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod);
|
||||
|
||||
#endif
|
||||
13
bc/system/file/Defines.hpp
Normal file
13
bc/system/file/Defines.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef BC_FILE_SYSTEM_DEFINES_HPP
|
||||
#define BC_FILE_SYSTEM_DEFINES_HPP
|
||||
|
||||
// Constants
|
||||
|
||||
// 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}
|
||||
|
||||
#endif
|
||||
291
bc/system/file/Stacked.cpp
Normal file
291
bc/system/file/Stacked.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
|
||||
#include "bc/file/Filesystem.hpp"
|
||||
#include "bc/file/system/System_File.hpp"
|
||||
#include "bc/file/system/Stacked.hpp"
|
||||
#include "bc/String.hpp"
|
||||
#include "bc/Memory.hpp"
|
||||
|
||||
namespace Blizzard {
|
||||
namespace System_File {
|
||||
namespace Stacked {
|
||||
|
||||
bool null_cd(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_close(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_create(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_cwd(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_dirwalk(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_exists(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_flush(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_getfileinfo(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_getfreespace(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_getpos(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_getrootchars(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_isabspath(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_isreadonly(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_makeabspath(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_mkdir(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_move(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_copy(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_open(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_read(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_readp(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_rmdir(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_setcachemode(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_seteof(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_setfileinfo(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_setpos(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_unlink(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_write(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_writep(File::Filesystem* fs, FileParms* parms) {
|
||||
return false;
|
||||
}
|
||||
bool null_shutdown(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 = {
|
||||
&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
|
||||
}
|
||||
};
|
||||
|
||||
// Base stack: this stack contains the base System_File functions.
|
||||
static 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
|
||||
}
|
||||
};
|
||||
|
||||
// File initialization stack: this is the default
|
||||
static 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
|
||||
}
|
||||
};
|
||||
|
||||
// Manager will be initialized upon first use
|
||||
static File::Filesystem* s_manager = &s_fileinit;
|
||||
|
||||
// Manager getter
|
||||
File::Filesystem* Manager() {
|
||||
return s_manager;
|
||||
}
|
||||
|
||||
// Push a filesystem stack to manager linked list
|
||||
void Push(File::Filesystem* fs) {
|
||||
auto stack = reinterpret_cast<File::Filesystem*>(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;
|
||||
|
||||
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<File::Filesystem*>(Memory::Allocate(sizeof(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));
|
||||
|
||||
// Hoist 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<File::Filesystem::CallFunc*>(reinterpret_cast<uintptr_t>(s_manager) + parms->offset);
|
||||
return func(fs, parms);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stacked
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
85
bc/system/file/Stacked.hpp
Normal file
85
bc/system/file/Stacked.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef BC_FILE_SYSTEM_STACKED_HPP
|
||||
#define BC_FILE_SYSTEM_STACKED_HPP
|
||||
|
||||
#include "bc/file/Types.hpp"
|
||||
#include "bc/file/Filesystem.hpp"
|
||||
#include "bc/file/File.hpp"
|
||||
#include "bc/file/system/System_File.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace System_File {
|
||||
namespace Stacked {
|
||||
|
||||
// Functions
|
||||
bool Open(FileParms* parms);
|
||||
|
||||
bool file_init(File::Filesystem* fs, FileParms* parms);
|
||||
|
||||
File::Filesystem* Manager();
|
||||
|
||||
void HoistAll();
|
||||
|
||||
void Push(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);
|
||||
|
||||
} // namespace Stacked
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
|
||||
#endif
|
||||
51
bc/system/file/System_File.hpp
Normal file
51
bc/system/file/System_File.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef BC_FILE_SYSTEM_SYSTEM_FILE_HPP
|
||||
#define BC_FILE_SYSTEM_SYSTEM_FILE_HPP
|
||||
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/file/system/Types.hpp"
|
||||
#include "bc/file/Filesystem.hpp"
|
||||
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
#include "bc/file/system/win/Support.hpp"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Blizzard {
|
||||
namespace System_File {
|
||||
|
||||
// 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);
|
||||
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
|
||||
#endif
|
||||
70
bc/system/file/Types.hpp
Normal file
70
bc/system/file/Types.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef BC_FILE_SYSTEM_TYPES_HPP
|
||||
#define BC_FILE_SYSTEM_TYPES_HPP
|
||||
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/file/Types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
namespace Stacked {
|
||||
|
||||
// FileParms stores parameters useful to all filesystem interactions
|
||||
// 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;
|
||||
};
|
||||
|
||||
} // namespace Stacked
|
||||
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
|
||||
#endif
|
||||
602
bc/system/file/posix/Stacked.cpp
Normal file
602
bc/system/file/posix/Stacked.cpp
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
#include "bc/file/system/Stacked.hpp"
|
||||
#include "bc/file/Path.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/Memory.hpp"
|
||||
#include "bc/String.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Blizzard {
|
||||
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, 511);
|
||||
} else {
|
||||
fd = ::open(pathNative.Str(), flags);
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
BC_FILE_SET_ERROR_MSG(100 + errno, "Posix 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);
|
||||
if (fileData == nullptr) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_OOM);
|
||||
return false;
|
||||
}
|
||||
|
||||
String::MemFill(fileData, recordSize, 0);
|
||||
|
||||
auto file = reinterpret_cast<File::StreamRecord*>(fileData);
|
||||
|
||||
file->flags = flags;
|
||||
file->filefd = fd;
|
||||
|
||||
String::Copy(file->path, parms->filename, pathNative.Size());
|
||||
|
||||
File::GetFileInfo(file);
|
||||
|
||||
parms->stream = 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 GetFreeSpace(FileParms* parms) {
|
||||
auto dirpath = parms->filename;
|
||||
|
||||
if (dirpath == nullptr || *dirpath == '\0') {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
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;
|
||||
}
|
||||
|
||||
bool ProcessDirFast(FileParms* parms) {
|
||||
auto dirpath = parms->filename;
|
||||
File::Path::QuickNative dirpathNative(dirpath);
|
||||
|
||||
// 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);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stores names temporarily
|
||||
char name[256] = {0};
|
||||
|
||||
bool status = false;
|
||||
|
||||
File::ProcessDirParms walkparms = {};
|
||||
walkparms.root = parms->filename;
|
||||
walkparms.param = parms->param;
|
||||
walkparms.item = name;
|
||||
|
||||
struct dirent* ent = nullptr;
|
||||
|
||||
while ((ent = ::readdir(directory)) != nullptr) {
|
||||
String::Copy(name, ent->d_name, 256);
|
||||
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::closedir(directory);
|
||||
return status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
File::StreamRecord* stream = nullptr;
|
||||
|
||||
auto flags = BC_FILE_OPEN_READ | BC_FILE_OPEN_WRITE | BC_FILE_OPEN_MUST_EXIST;
|
||||
|
||||
bool opened = File::Open(filename, flags, stream);
|
||||
if (!opened) {
|
||||
return true;
|
||||
}
|
||||
|
||||
File::Close(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
// realpath() based
|
||||
bool MakeAbsolutePath(FileParms* parms) {
|
||||
auto unkflag = parms->flag;
|
||||
|
||||
BC_FILE_PATH(basepathfast);
|
||||
BC_FILE_PATH(univpathfast);
|
||||
char* basepath = nullptr;
|
||||
char* univpath = nullptr;
|
||||
|
||||
if (parms->directorySize < BC_FILE_MAX_PATH) {
|
||||
basepath = basepathfast;
|
||||
} else {
|
||||
basepath = reinterpret_cast<char*>(Memory::Allocate(parms->directorySize));
|
||||
}
|
||||
|
||||
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<char*>(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<char*>(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<int32_t>(previous_character - local_1054)) + 1);
|
||||
if (parms->directorySize <= BC_FILE_MAX_PATH) {
|
||||
next_slash = next_slash_fast;
|
||||
} else {
|
||||
next_slash = reinterpret_cast<char*>(Memory::Allocate(parms->directorySize));
|
||||
}
|
||||
|
||||
|
||||
if (::realpath(copied_univ_path, next_slash) == nullptr) {
|
||||
temp_directory = temp_directory + (static_cast<int32_t>(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;
|
||||
}
|
||||
|
||||
// Create a full directory path
|
||||
bool CreateDirectory(FileParms* parms) {
|
||||
if (parms->filename == nullptr) {
|
||||
// FileError(8)
|
||||
BC_FILE_SET_ERROR(8);
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[BC_FILE_MAX_PATH] = {};
|
||||
struct stat sb;
|
||||
|
||||
// Copy path
|
||||
File::Path::MakeNativePath(parms->filename, tmp, BC_FILE_MAX_PATH);
|
||||
|
||||
auto len = String::Length(tmp);
|
||||
|
||||
// Remove trailing slash
|
||||
if(tmp[len - 1] == '/') {
|
||||
tmp[len - 1] = '\0';
|
||||
}
|
||||
|
||||
// check if path exists and is a directory
|
||||
if (::stat(tmp, &sb) == 0) {
|
||||
if (S_ISDIR(sb.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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, 511) < 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
// not a directory
|
||||
return false;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
|
||||
// check remaining path existence
|
||||
if (::stat(tmp, &sb) != 0) {
|
||||
// path does not exist, create directory
|
||||
if (::mkdir(tmp, 511) < 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
// not a directory
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Move(FileParms* parms) {
|
||||
File::Path::QuickNative source(parms->filename);
|
||||
File::Path::QuickNative destination(parms->destination);
|
||||
|
||||
struct stat st;
|
||||
|
||||
// Fail if destination already exists.
|
||||
int32_t status = ::stat(destination.Str(), &st);
|
||||
if (status == 0) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
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) {
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to remove an empty directory.
|
||||
bool RemoveDirectory(FileParms* parms) {
|
||||
auto dir = parms->filename;
|
||||
|
||||
// Must have directory path to remove directory.
|
||||
if (!dir) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
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;
|
||||
}
|
||||
|
||||
// Perform truncation.
|
||||
auto status = ::ftruncate(file->filefd, static_cast<off_t>(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);
|
||||
#endif
|
||||
|
||||
if (status != 0) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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(), 444);
|
||||
} else {
|
||||
status = ::chmod(path.Str(), 511);
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
parms->mode &= ~(File::Mode::setperms);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Stacked
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
512
bc/system/file/posix/System_File.cpp
Normal file
512
bc/system/file/posix/System_File.cpp
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
#include "bc/file/system/System_File.hpp"
|
||||
#include "bc/file/system/Stacked.hpp"
|
||||
#include "bc/file/Path.hpp"
|
||||
#include "bc/String.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/Memory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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<uint32_t>(info.st_dev);
|
||||
infoptr->size = static_cast<uint64_t>(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<uint64_t>(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/<your volume>
|
||||
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) {
|
||||
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<uintptr_t>(volume) - reinterpret_cast<uintptr_t>(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
|
||||
size_t sz_copybuffer = std::min(sz_source, size_t(BC_FILE_SYSTEM_COPYBUFFER_SIZE));
|
||||
auto u8_copybuffer = reinterpret_cast<uint8_t*>(Memory::Allocate(sz_copybuffer));
|
||||
|
||||
// Loop through the source file, reading segments into copybuffer
|
||||
for (size_t index = 0; index < sz_source; index += sz_copybuffer) {
|
||||
// How many bytes to read
|
||||
size_t sz_bytesToRead = 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, 0);
|
||||
if (status) {
|
||||
// Write copied segment to destination file
|
||||
status = File::Write(st_destination, u8_copybuffer, sz_bytesRead, &sz_bytesWritten, 0);
|
||||
}
|
||||
|
||||
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(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;
|
||||
}
|
||||
|
||||
// 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, data, slice);
|
||||
} else {
|
||||
// Read absolute
|
||||
result = pread(file->filefd, 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;
|
||||
}
|
||||
|
||||
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 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 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;
|
||||
}
|
||||
|
||||
// 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, data, slice);
|
||||
} else {
|
||||
// Write at an absolute file position
|
||||
result = pwrite(file->filefd, 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;
|
||||
}
|
||||
|
||||
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 Write(parms->stream, parms->param, -1, &parms->size);
|
||||
}
|
||||
|
||||
bool WriteP(File::Filesystem* fs, Stacked::FileParms* parms) {
|
||||
return 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
|
||||
811
bc/system/file/win/Stacked.cpp
Normal file
811
bc/system/file/win/Stacked.cpp
Normal file
|
|
@ -0,0 +1,811 @@
|
|||
#include "bc/file/Defines.hpp"
|
||||
#include "bc/file/File.hpp"
|
||||
#include "bc/file/Path.hpp"
|
||||
#include "bc/file/system/Stacked.hpp"
|
||||
#include "bc/file/system/win/WinFile.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/Memory.hpp"
|
||||
#include "bc/String.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
/********************************
|
||||
* Begin Win32 Stacked functions *
|
||||
*********************************/
|
||||
|
||||
namespace Blizzard {
|
||||
namespace System_File {
|
||||
namespace Stacked {
|
||||
|
||||
bool SetWorkingDirectory(FileParms* parms) {
|
||||
BLIZZARD_ASSERT(parms->filename);
|
||||
return ::SetCurrentDirectory(parms->filename) != 0;
|
||||
}
|
||||
|
||||
bool Close(FileParms* parms) {
|
||||
auto file = parms->stream;
|
||||
BLIZZARD_ASSERT(file != nullptr);
|
||||
|
||||
::CloseHandle(file->filehandle);
|
||||
Memory::Free(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetWorkingDirectory(FileParms* parms) {
|
||||
if (!parms->directory || !parms->directorySize) {
|
||||
// System_File::FileError(8)
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto size = static_cast<DWORD>(parms->directorySize);
|
||||
auto directory = static_cast<LPSTR>(parms->directory);
|
||||
|
||||
::GetCurrentDirectory(size, directory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcessDirFast(FileParms* parms) {
|
||||
// Get parameters
|
||||
auto unkflag = parms->flag;
|
||||
auto directory = parms->filename;
|
||||
auto callback = parms->callback;
|
||||
auto param = parms->param;
|
||||
|
||||
// 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);
|
||||
|
||||
if (hFindFile == INVALID_HANDLE_VALUE) {
|
||||
BC_FILE_SET_ERROR_MSG(BC_FILE_ERROR_INVALID_ARGUMENT, "Win32 ProcessDirFast - %s", directory);
|
||||
return false;
|
||||
}
|
||||
|
||||
BC_FILE_PATH(currentPath);
|
||||
processDirParms.item = currentPath;
|
||||
|
||||
while (true) {
|
||||
// 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;
|
||||
|
||||
if (!callback(processDirParms)) {
|
||||
::FindClose(hFindFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!::FindNextFile(hFindFile, &findData)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::FindClose(hFindFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Exists(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(static_cast<LPCSTR>(filepathNative.Str()));
|
||||
|
||||
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;
|
||||
|
||||
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<uint64_t>(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<char *>(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) {
|
||||
auto path = parms->filename;
|
||||
|
||||
char temp[300] = {0};
|
||||
auto p = path;
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '\\' || *p == '/') {
|
||||
count++;
|
||||
int32_t len = p - path;
|
||||
if (len == 0) {
|
||||
len = 1;
|
||||
}
|
||||
String::Copy(temp, path, len);
|
||||
temp[len] = '\0';
|
||||
|
||||
if (::GetFileAttributes(temp) == INVALID_FILE_ATTRIBUTES) {
|
||||
if (!::CreateDirectory(temp, nullptr)) {
|
||||
if (::GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
// No directories were made because there are no path separators.
|
||||
// Make only one directory:
|
||||
|
||||
String::Copy(temp, path, 300);
|
||||
|
||||
if (::GetFileAttributes(temp) == INVALID_FILE_ATTRIBUTES) {
|
||||
if (!::CreateDirectory(temp, 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
|
||||
size_t sz_copybuffer = std::min(sz_source, uint64_t(BC_FILE_SYSTEM_COPYBUFFER_SIZE));
|
||||
auto u8_copybuffer = reinterpret_cast<uint8_t*>(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 = 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, 0);
|
||||
if (status) {
|
||||
// Write copied segment to destination file
|
||||
status = File::Write(st_destination, u8_copybuffer, sz_bytesRead, &sz_bytesWritten, 0);
|
||||
}
|
||||
|
||||
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 = 0;
|
||||
if (err = ::GetLastError()) {
|
||||
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<File::StreamRecord*>(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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (offset > -1) {
|
||||
if (!File::SetPos(file, offset, BC_FILE_SEEK_START)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD dwRead = 0;
|
||||
|
||||
BOOL ok = ::ReadFile(file->filehandle, data, *bytes, &dwRead, nullptr);
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
*bytes = static_cast<size_t>(dwRead);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Read(FileParms* parms) {
|
||||
return Read(parms->stream, parms->param, -1, &parms->size);
|
||||
}
|
||||
|
||||
bool ReadP(FileParms* parms) {
|
||||
return 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);
|
||||
return false;
|
||||
}
|
||||
|
||||
File::Path::QuickNative pathNative(dirpath);
|
||||
|
||||
BOOL ok = ::RemoveDirectory(dirpath);
|
||||
|
||||
return ok != 0;
|
||||
}
|
||||
|
||||
bool SetCacheMode(FileParms* parms) {
|
||||
auto file = parms->stream;
|
||||
|
||||
if (file == nullptr) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file->filehandle == INVALID_HANDLE_VALUE) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_HANDLE);
|
||||
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;
|
||||
|
||||
if (file == nullptr || file->filehandle == INVALID_HANDLE_VALUE) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save current pos
|
||||
int64_t originalpos = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool SetAttributes(FileParms* parms) {
|
||||
auto info = parms->info;
|
||||
|
||||
if (info == nullptr) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
parms->mode &= ~(File::Mode::setperms);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPos(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 offset = parms->position;
|
||||
auto whence = parms->whence;
|
||||
|
||||
auto offsetLow = static_cast<LONG>(offset);
|
||||
auto offsetHigh = static_cast<LONG>(offset >> 32);
|
||||
|
||||
DWORD res = ::SetFilePointer(file->filehandle, offsetLow, &offsetHigh, static_cast<DWORD>(whence));
|
||||
if (res == INVALID_SET_FILE_POINTER) {
|
||||
res = GetLastError();
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Delete(FileParms* parms) {
|
||||
auto deletePath = parms->filename;
|
||||
|
||||
if (deletePath == nullptr) {
|
||||
BC_FILE_SET_ERROR(BC_FILE_ERROR_INVALID_ARGUMENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace Stacked
|
||||
} // namespace System_File
|
||||
} // namespace Blizzard
|
||||
|
||||
/********************************
|
||||
* End of Win32 Stacked functions *
|
||||
*********************************/
|
||||
12
bc/system/file/win/Support.hpp
Normal file
12
bc/system/file/win/Support.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#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
|
||||
142
bc/system/file/win/System_File.cpp
Normal file
142
bc/system/file/win/System_File.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#include "bc/file/system/System_File.hpp"
|
||||
#include "bc/file/system/Stacked.hpp"
|
||||
#include "bc/file/File.hpp"
|
||||
#include "bc/file/Defines.hpp"
|
||||
#include "bc/Debug.hpp"
|
||||
#include "bc/String.hpp"
|
||||
|
||||
#include <storm/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
|
||||
181
bc/system/file/win/WinFile.cpp
Normal file
181
bc/system/file/win/WinFile.cpp
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
#include "bc/file/Defines.hpp"
|
||||
#include "bc/file/system/win/Utils.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
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<uint64_t>(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<uint32_t>(winInfo->dwVolumeSerialNumber);
|
||||
info->size = (static_cast<uint64_t>(winInfo->nFileSizeHigh) << 32ULL) | static_cast<uint64_t>(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<uint64_t>(winInfo->nFileSizeHigh) << 32ULL) | static_cast<uint64_t>(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 shareRead = flags & BC_FILE_OPEN_SHARE_WRITE;
|
||||
bool write = flags & BC_FILE_OPEN_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 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
|
||||
36
bc/system/file/win/WinFile.hpp
Normal file
36
bc/system/file/win/WinFile.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef BC_FILE_SYSTEM_WIN_UTILS_HPP
|
||||
#define BC_FILE_SYSTEM_WIN_UTILS_HPP
|
||||
|
||||
#include "bc/file/Types.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue