forked from cafeberry/cafeberry
feat: add smartcmd dedicated server
major bug: when connecting to the server a second time, it gets softlocked and keeps repeating ticking connection A\n in debug logs until it times out.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user