mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-04-25 17:46:51 +00:00
163 lines
3.8 KiB
C++
163 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
#include "StringHelpers.h"
|
|
|
|
namespace PortableFileIO
|
|
{
|
|
enum class BinaryReadStatus
|
|
{
|
|
ok,
|
|
not_found,
|
|
too_large,
|
|
read_error,
|
|
};
|
|
|
|
struct BinaryReadResult
|
|
{
|
|
BinaryReadStatus status;
|
|
std::size_t bytesRead;
|
|
std::size_t fileSize;
|
|
};
|
|
|
|
inline std::FILE *OpenBinaryFileForRead(const std::wstring &path)
|
|
{
|
|
#if defined(_WIN32)
|
|
return _wfopen(path.c_str(), L"rb");
|
|
#else
|
|
const std::string nativePath = wstringtofilename(path);
|
|
return std::fopen(nativePath.c_str(), "rb");
|
|
#endif
|
|
}
|
|
|
|
inline bool Seek(std::FILE *file, std::size_t offset, int origin)
|
|
{
|
|
#if defined(_WIN32)
|
|
return _fseeki64(file, static_cast<__int64>(offset), origin) == 0;
|
|
#else
|
|
return fseeko(file, static_cast<off_t>(offset), origin) == 0;
|
|
#endif
|
|
}
|
|
|
|
inline __int64 Tell(std::FILE *file)
|
|
{
|
|
#if defined(_WIN32)
|
|
return _ftelli64(file);
|
|
#else
|
|
return static_cast<__int64>(ftello(file));
|
|
#endif
|
|
}
|
|
|
|
inline BinaryReadResult ReadBinaryFile(const std::wstring &path, void *buffer, std::size_t capacity)
|
|
{
|
|
std::FILE *stream = OpenBinaryFileForRead(path);
|
|
if (stream == NULL)
|
|
{
|
|
return { BinaryReadStatus::not_found, 0, 0 };
|
|
}
|
|
|
|
if (!Seek(stream, 0, SEEK_END))
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, 0 };
|
|
}
|
|
|
|
const __int64 endPosition = Tell(stream);
|
|
if (endPosition < 0)
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, 0 };
|
|
}
|
|
|
|
const std::size_t fileSize = static_cast<std::size_t>(endPosition);
|
|
if (fileSize > capacity)
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::too_large, 0, fileSize };
|
|
}
|
|
|
|
if (!Seek(stream, 0, SEEK_SET))
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, fileSize };
|
|
}
|
|
|
|
const std::size_t bytesRead = std::fread(buffer, 1, fileSize, stream);
|
|
const bool failed = std::ferror(stream) != 0;
|
|
std::fclose(stream);
|
|
if (failed || bytesRead != fileSize)
|
|
{
|
|
return { BinaryReadStatus::read_error, bytesRead, fileSize };
|
|
}
|
|
|
|
return { BinaryReadStatus::ok, bytesRead, fileSize };
|
|
}
|
|
|
|
inline BinaryReadResult ReadBinaryFileSegment(const std::wstring &path, std::size_t offset, void *buffer, std::size_t bytesToRead)
|
|
{
|
|
std::FILE *stream = OpenBinaryFileForRead(path);
|
|
if (stream == NULL)
|
|
{
|
|
return { BinaryReadStatus::not_found, 0, 0 };
|
|
}
|
|
|
|
if (!Seek(stream, 0, SEEK_END))
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, 0 };
|
|
}
|
|
|
|
const __int64 endPosition = Tell(stream);
|
|
if (endPosition < 0)
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, 0 };
|
|
}
|
|
|
|
const std::size_t fileSize = static_cast<std::size_t>(endPosition);
|
|
if ((offset > fileSize) || (bytesToRead > (fileSize - offset)))
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::too_large, 0, fileSize };
|
|
}
|
|
|
|
if (!Seek(stream, offset, SEEK_SET))
|
|
{
|
|
std::fclose(stream);
|
|
return { BinaryReadStatus::read_error, 0, fileSize };
|
|
}
|
|
|
|
const std::size_t bytesRead = std::fread(buffer, 1, bytesToRead, stream);
|
|
const bool failed = std::ferror(stream) != 0;
|
|
std::fclose(stream);
|
|
if (failed || bytesRead != bytesToRead)
|
|
{
|
|
return { BinaryReadStatus::read_error, bytesRead, fileSize };
|
|
}
|
|
|
|
return { BinaryReadStatus::ok, bytesRead, fileSize };
|
|
}
|
|
|
|
inline bool WriteBinaryFile(const std::wstring &path, const void *buffer, std::size_t bytesToWrite)
|
|
{
|
|
#if defined(_WIN32)
|
|
std::FILE *stream = _wfopen(path.c_str(), L"wb");
|
|
#else
|
|
const std::string nativePath = wstringtofilename(path);
|
|
std::FILE *stream = std::fopen(nativePath.c_str(), "wb");
|
|
#endif
|
|
if (stream == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::size_t bytesWritten = std::fwrite(buffer, 1, bytesToWrite, stream);
|
|
const bool failed = std::ferror(stream) != 0 || bytesWritten != bytesToWrite;
|
|
const bool closeFailed = std::fclose(stream) != 0;
|
|
return !failed && !closeFailed;
|
|
}
|
|
}
|