add: add FileUtils

Moved file operations to `utils`.
This commit is contained in:
kuwacom 2026-03-08 10:13:36 +09:00
parent 49f41b8bce
commit da284c1387
7 changed files with 195 additions and 0 deletions

View file

@ -101,6 +101,7 @@ list(APPEND MINECRAFT_SERVER_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandList.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandList.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandTp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandTp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandGamemode.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandGamemode.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/FileUtils.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/vendor/linenoise/linenoise.c" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/vendor/linenoise/linenoise.c"
) )

View file

@ -0,0 +1,146 @@
#include "stdafx.h"
#include "FileUtils.h"
#include "StringUtils.h"
#include <io.h>
#include <stdio.h>
namespace ServerRuntime
{
namespace FileUtils
{
namespace
{
static std::wstring ToWidePath(const std::string &filePath)
{
return StringUtils::Utf8ToWide(filePath);
}
}
unsigned long long GetCurrentUtcFileTime()
{
FILETIME now = {};
GetSystemTimeAsFileTime(&now);
ULARGE_INTEGER value = {};
value.LowPart = now.dwLowDateTime;
value.HighPart = now.dwHighDateTime;
return value.QuadPart;
}
bool ReadTextFile(const std::string &filePath, std::string *outText)
{
if (outText == nullptr)
{
return false;
}
outText->clear();
const std::wstring widePath = ToWidePath(filePath);
if (widePath.empty())
{
return false;
}
FILE *inFile = nullptr;
if (_wfopen_s(&inFile, widePath.c_str(), L"rb") != 0 || inFile == nullptr)
{
return false;
}
if (fseek(inFile, 0, SEEK_END) != 0)
{
fclose(inFile);
return false;
}
long fileSize = ftell(inFile);
if (fileSize < 0)
{
fclose(inFile);
return false;
}
if (fseek(inFile, 0, SEEK_SET) != 0)
{
fclose(inFile);
return false;
}
if (fileSize == 0)
{
fclose(inFile);
return true;
}
outText->resize((size_t)fileSize);
size_t bytesRead = fread(&(*outText)[0], 1, (size_t)fileSize, inFile);
fclose(inFile);
if (bytesRead != (size_t)fileSize)
{
outText->clear();
return false;
}
return true;
}
bool WriteTextFileAtomic(const std::string &filePath, const std::string &text)
{
const std::wstring widePath = ToWidePath(filePath);
if (widePath.empty())
{
return false;
}
const std::wstring tmpPath = widePath + L".tmp";
FILE *outFile = nullptr;
if (_wfopen_s(&outFile, tmpPath.c_str(), L"wb") != 0 || outFile == nullptr)
{
return false;
}
if (!text.empty())
{
size_t bytesWritten = fwrite(text.data(), 1, text.size(), outFile);
if (bytesWritten != text.size())
{
fclose(outFile);
DeleteFileW(tmpPath.c_str());
return false;
}
}
if (fflush(outFile) != 0 || _commit(_fileno(outFile)) != 0)
{
fclose(outFile);
DeleteFileW(tmpPath.c_str());
return false;
}
fclose(outFile);
DWORD attrs = GetFileAttributesW(widePath.c_str());
if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0))
{
// Replace the destination without deleting the last known-good file first.
if (ReplaceFileW(widePath.c_str(), tmpPath.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr))
{
return true;
}
}
if (MoveFileExW(tmpPath.c_str(), widePath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
return true;
}
// Keep the temp file on failure so the original file remains recoverable and the caller can inspect the write result.
return false;
}
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include <string>
namespace ServerRuntime
{
namespace FileUtils
{
/**
* Reads the full UTF-8 path target into memory without interpreting JSON or line endings
* UTF-8
*/
bool ReadTextFile(const std::string &filePath, std::string *outText);
/**
* Writes text through a same-directory temporary file and publishes it with a single replacement step
*
*/
bool WriteTextFileAtomic(const std::string &filePath, const std::string &text);
/**
* Returns the current UTC timestamp encoded in Windows FILETIME units for expiry comparisons
* UTC時刻をWindows FILETIME単位で返す
*/
unsigned long long GetCurrentUtcFileTime();
}
}

View file

@ -37,6 +37,7 @@ namespace ServerRuntime
int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
if (wideCount <= 0) if (wideCount <= 0)
{ {
// Fall back to the current ANSI code page so legacy non-UTF-8 inputs remain readable.
wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0); wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
if (wideCount <= 0) if (wideCount <= 0)
{ {
@ -60,6 +61,19 @@ namespace ServerRuntime
return Utf8ToWide(value.c_str()); return Utf8ToWide(value.c_str());
} }
std::string StripUtf8Bom(const std::string &value)
{
if (value.size() >= 3 &&
(unsigned char)value[0] == 0xEF &&
(unsigned char)value[1] == 0xBB &&
(unsigned char)value[2] == 0xBF)
{
return value.substr(3);
}
return value;
}
std::string TrimAscii(const std::string &value) std::string TrimAscii(const std::string &value)
{ {
size_t start = 0; size_t start = 0;

View file

@ -9,6 +9,7 @@ namespace ServerRuntime
std::string WideToUtf8(const std::wstring &value); std::string WideToUtf8(const std::wstring &value);
std::wstring Utf8ToWide(const char *value); std::wstring Utf8ToWide(const char *value);
std::wstring Utf8ToWide(const std::string &value); std::wstring Utf8ToWide(const std::string &value);
std::string StripUtf8Bom(const std::string &value);
std::string TrimAscii(const std::string &value); std::string TrimAscii(const std::string &value);
std::string ToLowerAscii(const std::string &value); std::string ToLowerAscii(const std::string &value);

View file

@ -657,6 +657,7 @@
<ClCompile Include="Console\ServerCliEngine.cpp" /> <ClCompile Include="Console\ServerCliEngine.cpp" />
<ClCompile Include="Console\ServerCliParser.cpp" /> <ClCompile Include="Console\ServerCliParser.cpp" />
<ClCompile Include="Console\ServerCliRegistry.cpp" /> <ClCompile Include="Console\ServerCliRegistry.cpp" />
<ClCompile Include="Common\FileUtils.cpp" />
<ClCompile Include="Common\StringUtils.cpp" /> <ClCompile Include="Common\StringUtils.cpp" />
<ClCompile Include="..\Minecraft.Client\glWrapper.cpp" /> <ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
<ClCompile Include="ServerLogger.cpp" /> <ClCompile Include="ServerLogger.cpp" />
@ -684,6 +685,7 @@
<ClInclude Include="Console\ServerCliEngine.h" /> <ClInclude Include="Console\ServerCliEngine.h" />
<ClInclude Include="Console\ServerCliParser.h" /> <ClInclude Include="Console\ServerCliParser.h" />
<ClInclude Include="Console\ServerCliRegistry.h" /> <ClInclude Include="Console\ServerCliRegistry.h" />
<ClInclude Include="Common\FileUtils.h" />
<ClInclude Include="Common\StringUtils.h" /> <ClInclude Include="Common\StringUtils.h" />
<ClInclude Include="ServerLogger.h" /> <ClInclude Include="ServerLogger.h" />
<ClInclude Include="ServerProperties.h" /> <ClInclude Include="ServerProperties.h" />

View file

@ -48,6 +48,9 @@
<ClCompile Include="Windows64\ServerMain.cpp"> <ClCompile Include="Windows64\ServerMain.cpp">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Common\FileUtils.cpp">
<Filter>Server\Common</Filter>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" /> <ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" /> <ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" /> <ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
@ -572,6 +575,9 @@
<ClInclude Include="Console\ServerCliRegistry.h"> <ClInclude Include="Console\ServerCliRegistry.h">
<Filter>Server\Console</Filter> <Filter>Server\Console</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Common\FileUtils.h">
<Filter>Server\Common</Filter>
</ClInclude>
<ClInclude Include="Common\StringUtils.h"> <ClInclude Include="Common\StringUtils.h">
<Filter>Server\Common</Filter> <Filter>Server\Common</Filter>
</ClInclude> </ClInclude>