1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-17 12:03:07 +01:00

Merge pull request #9629 from IntelOrca/fix/utf8-support-windows

Fix UTF-8 support on Windows
This commit is contained in:
Ted John
2019-07-23 22:15:56 +01:00
committed by GitHub
23 changed files with 173 additions and 255 deletions

View File

@@ -32,7 +32,7 @@ using namespace OpenRCT2::Ui;
static char** GetCommandLineArgs(int argc, wchar_t** argvW);
static void FreeCommandLineArgs(int argc, char** argv);
static char* ConvertUTF16toUTF8(const wchar_t* src);
static char* ConvertWideChartoUTF8(const wchar_t* src);
DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t** argvW)
{
@@ -43,6 +43,8 @@ DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t** argvW)
return -1;
}
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
int exitCode = NormalisedMain(argc, const_cast<const char**>(argv));
FreeCommandLineArgs(argc, argv);
@@ -61,7 +63,7 @@ static char** GetCommandLineArgs(int argc, wchar_t** argvW)
// Convert to UTF-8
for (int i = 0; i < argc; i++)
{
argv[i] = ConvertUTF16toUTF8(argvW[i]);
argv[i] = ConvertWideChartoUTF8(argvW[i]);
}
return argv;
@@ -77,7 +79,7 @@ static void FreeCommandLineArgs(int argc, char** argv)
free(argv);
}
static char* ConvertUTF16toUTF8(const wchar_t* src)
static char* ConvertWideChartoUTF8(const wchar_t* src)
{
int srcLen = lstrlenW(src);
int sizeReq = WideCharToMultiByte(CP_UTF8, 0, src, srcLen, nullptr, 0, nullptr, nullptr);

View File

@@ -87,23 +87,23 @@ namespace OpenRCT2::Ui
void ShowMessageBox(SDL_Window* window, const std::string& message) override
{
HWND hwnd = GetHWND(window);
std::wstring messageW = String::ToUtf16(message);
std::wstring messageW = String::ToWideChar(message);
MessageBoxW(hwnd, messageW.c_str(), L"OpenRCT2", MB_OK);
}
void OpenFolder(const std::string& path) override
{
std::wstring pathW = String::ToUtf16(path);
std::wstring pathW = String::ToWideChar(path);
ShellExecuteW(NULL, L"open", pathW.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) override
{
std::wstring wcFilename = String::ToUtf16(desc.DefaultFilename);
std::wstring wcFilename = String::ToWideChar(desc.DefaultFilename);
wcFilename.resize(std::max<size_t>(wcFilename.size(), MAX_PATH));
std::wstring wcTitle = String::ToUtf16(desc.Title);
std::wstring wcInitialDirectory = String::ToUtf16(desc.InitialDirectory);
std::wstring wcTitle = String::ToWideChar(desc.Title);
std::wstring wcInitialDirectory = String::ToWideChar(desc.InitialDirectory);
std::wstring wcFilters = GetFilterString(desc.Filters);
// Set open file name options
@@ -162,7 +162,7 @@ namespace OpenRCT2::Ui
LPMALLOC lpMalloc;
if (SUCCEEDED(CoInitializeEx(0, COINIT_APARTMENTTHREADED)) && SUCCEEDED(SHGetMalloc(&lpMalloc)))
{
std::wstring titleW = String::ToUtf16(title);
std::wstring titleW = String::ToWideChar(title);
BROWSEINFOW bi = {};
bi.lpszTitle = titleW.c_str();
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_NONEWFOLDERBUTTON;
@@ -211,7 +211,7 @@ namespace OpenRCT2::Ui
std::wstringstream filtersb;
for (auto filter : filters)
{
filtersb << String::ToUtf16(filter.Name) << '\0' << String::ToUtf16(filter.Pattern) << '\0';
filtersb << String::ToWideChar(filter.Name) << '\0' << String::ToWideChar(filter.Pattern) << '\0';
}
return filtersb.str();
}

View File

@@ -213,7 +213,7 @@ static std::string GetChangelogText()
{
auto path = GetChangelogPath();
#if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = String::ToUtf16(path);
auto pathW = String::ToWideChar(path);
auto fs = std::ifstream(pathW, std::ios::in);
#else
auto fs = std::ifstream(path, std::ios::in);

View File

@@ -13,6 +13,7 @@
#include "OpenRCT2.h"
#include "core/Imaging.h"
#include "core/String.hpp"
#include "drawing/Drawing.h"
#include "drawing/ImageImporter.h"
#include "localisation/Language.h"
@@ -68,11 +69,9 @@ static uint8_t* spriteFileData;
static FILE* fopen_utf8(const char* path, const char* mode)
{
wchar_t* pathW = utf8_to_widechar(path);
wchar_t* modeW = utf8_to_widechar(mode);
FILE* file = _wfopen(pathW, modeW);
free(pathW);
free(modeW);
auto pathW = String::ToWideChar(path);
auto modeW = String::ToWideChar(mode);
auto file = _wfopen(pathW.c_str(), modeW.c_str());
return file;
}
@@ -640,20 +639,19 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
}
// Resolve absolute sprite path
char* imagePath = platform_get_absolute_path(json_string_value(path), directoryPath);
auto imagePath = platform_get_absolute_path(json_string_value(path), directoryPath);
rct_g1_element spriteElement;
uint8_t* buffer;
int bufferLength;
if (!sprite_file_import(
imagePath, x_offset == nullptr ? 0 : json_integer_value(x_offset),
imagePath.c_str(), x_offset == nullptr ? 0 : json_integer_value(x_offset),
y_offset == nullptr ? 0 : json_integer_value(y_offset), keep_palette, forceBmp, &spriteElement, &buffer,
&bufferLength, gSpriteMode))
{
fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath);
fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str());
json_decref(sprite_list);
free(imagePath);
return -1;
}
@@ -661,7 +659,6 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
{
fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath);
json_decref(sprite_list);
free(imagePath);
return -1;
}
@@ -683,16 +680,14 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
if (!sprite_file_save(spriteFilePath))
{
fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath);
fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath.c_str());
json_decref(sprite_list);
free(imagePath);
return -1;
}
if (!silent)
fprintf(stdout, "Added: %s\n", imagePath);
fprintf(stdout, "Added: %s\n", imagePath.c_str());
free(imagePath);
sprite_file_close();
}

View File

@@ -504,6 +504,7 @@ namespace OpenRCT2
bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail) final override
{
log_verbose("Context::LoadParkFromFile(%s)", path.c_str());
try
{
auto fs = FileStream(path, FILE_MODE_OPEN);

View File

@@ -31,10 +31,6 @@ using namespace Numerics;
using utf8 = char;
using utf8string = utf8*;
using const_utf8string = const utf8*;
#ifdef _WIN32
using utf16 = wchar_t;
using utf16string = utf16*;
#endif
// Define MAX_PATH for various headers that don't want to include system headers
// just for MAX_PATH

View File

@@ -362,7 +362,7 @@ namespace Config
auto playerName = reader->GetString("player_name", "");
if (playerName.empty())
{
playerName = String::ToStd(platform_get_username());
playerName = platform_get_username();
if (playerName.empty())
{
playerName = "Player";

View File

@@ -49,7 +49,7 @@ namespace File
std::vector<uint8_t> result;
#if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = String::ToUtf16(std::string(path));
auto pathW = String::ToWideChar(std::string(path));
std::ifstream fs(pathW, std::ios::in | std::ios::binary);
#else
std::ifstream fs(std::string(path), std::ios::in | std::ios::binary);
@@ -125,8 +125,8 @@ namespace File
{
uint64_t lastModified = 0;
#ifdef _WIN32
auto pathW = utf8_to_widechar(path.c_str());
auto hFile = CreateFileW(pathW, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
auto pathW = String::ToWideChar(path.c_str());
auto hFile = CreateFileW(pathW.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
FILETIME ftCreate, ftAccess, ftWrite;
@@ -136,7 +136,6 @@ namespace File
}
CloseHandle(hFile);
}
free(pathW);
#else
struct stat statInfo
{

View File

@@ -227,11 +227,11 @@ public:
void GetDirectoryChildren(std::vector<DirectoryChild>& children, const std::string& path) override
{
std::string pattern = path + "\\*";
wchar_t* wPattern = utf8_to_widechar(pattern.c_str());
auto pattern = path + "\\*";
auto wPattern = String::ToWideChar(pattern.c_str());
WIN32_FIND_DATAW findData;
HANDLE hFile = FindFirstFileW(wPattern, &findData);
HANDLE hFile = FindFirstFileW(wPattern.c_str(), &findData);
if (hFile != INVALID_HANDLE_VALUE)
{
do
@@ -244,8 +244,6 @@ public:
} while (FindNextFileW(hFile, &findData));
FindClose(hFile);
}
Memory::Free(wPattern);
}
private:
@@ -253,10 +251,7 @@ private:
{
DirectoryChild result;
utf8* name = widechar_to_utf8(child->cFileName);
result.Name = std::string(name);
Memory::Free(name);
result.Name = String::ToUtf8(child->cFileName);
if (child->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
result.Type = DIRECTORY_CHILD_TYPE::DC_DIRECTORY;

View File

@@ -70,11 +70,9 @@ public:
}
#ifdef _WIN32
wchar_t* pathW = utf8_to_widechar(path);
wchar_t* modeW = utf8_to_widechar(mode);
_file = _wfopen(pathW, modeW);
free(pathW);
free(modeW);
auto pathW = String::ToWideChar(path);
auto modeW = String::ToWideChar(mode);
_file = _wfopen(pathW.c_str(), modeW.c_str());
#else
if (fileMode == FILE_MODE_OPEN)
{

View File

@@ -305,7 +305,7 @@ namespace Imaging
default:
{
#if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = String::ToUtf16(path);
auto pathW = String::ToWideChar(path);
std::ifstream fs(pathW, std::ios::binary);
#else
std::ifstream fs(path.data(), std::ios::binary);
@@ -331,7 +331,7 @@ namespace Imaging
case IMAGE_FORMAT::PNG:
{
#if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = String::ToUtf16(path);
auto pathW = String::ToWideChar(path);
std::ofstream fs(pathW, std::ios::binary);
#else
std::ofstream fs(path.data(), std::ios::binary);

View File

@@ -171,19 +171,17 @@ namespace Path
utf8* GetAbsolute(utf8* buffer, size_t bufferSize, const utf8* relativePath)
{
#ifdef _WIN32
wchar_t* relativePathW = utf8_to_widechar(relativePath);
auto relativePathW = String::ToWideChar(relativePath);
wchar_t absolutePathW[MAX_PATH];
DWORD length = GetFullPathNameW(relativePathW, (DWORD)std::size(absolutePathW), absolutePathW, nullptr);
Memory::Free(relativePathW);
DWORD length = GetFullPathNameW(relativePathW.c_str(), (DWORD)std::size(absolutePathW), absolutePathW, nullptr);
if (length == 0)
{
return String::Set(buffer, bufferSize, relativePath);
}
else
{
utf8* absolutePath = widechar_to_utf8(absolutePathW);
String::Set(buffer, bufferSize, absolutePath);
Memory::Free(absolutePath);
auto absolutePath = String::ToUtf8(absolutePathW);
String::Set(buffer, bufferSize, absolutePath.c_str());
return buffer;
}
#else

View File

@@ -97,7 +97,7 @@ namespace String
#endif
}
std::wstring ToUtf16(const std::string_view& src)
std::wstring ToWideChar(const std::string_view& src)
{
#ifdef _WIN32
int srcLen = (int)src.size();
@@ -706,7 +706,7 @@ namespace String
std::string ToUpper(const std::string_view& src)
{
#ifdef _WIN32
auto srcW = ToUtf16(src);
auto srcW = ToWideChar(src);
// Measure how long the destination needs to be
auto requiredSize = LCMapStringEx(

View File

@@ -37,7 +37,7 @@ namespace String
std::string StdFormat_VA(const utf8* format, va_list args);
std::string StdFormat(const utf8* format, ...);
std::string ToUtf8(const std::wstring_view& src);
std::wstring ToUtf16(const std::string_view& src);
std::wstring ToWideChar(const std::string_view& src);
bool IsNullOrEmpty(const utf8* str);
int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false);

View File

@@ -101,8 +101,6 @@ bool utf8_is_codepoint_start(const utf8* text);
void utf8_remove_format_codes(utf8* text, bool allowcolours);
int32_t utf8_get_codepoint_length(int32_t codepoint);
int32_t utf8_length(const utf8* text);
wchar_t* utf8_to_widechar(const utf8* src);
utf8* widechar_to_utf8(const wchar_t* src);
std::string rct2_to_utf8(const std::string_view& src, RCT2LanguageId languageId);
std::string utf8_to_rct2(const std::string_view& src);

View File

@@ -137,44 +137,6 @@ int32_t utf8_length(const utf8* text)
return count;
}
wchar_t* utf8_to_widechar(const utf8* src)
{
wchar_t* result = (wchar_t*)malloc((utf8_length(src) + 1) * sizeof(wchar_t));
wchar_t* dst = result;
const utf8* ch = src;
int32_t codepoint;
while ((codepoint = utf8_get_next(ch, &ch)) != 0)
{
if ((uint32_t)codepoint > 0xFFFF)
{
*dst++ = '?';
}
else
{
*dst++ = codepoint;
}
}
*dst = 0;
return result;
}
utf8* widechar_to_utf8(const wchar_t* src)
{
utf8* result = (utf8*)malloc((wcslen(src) * 4) + 1);
utf8* dst = result;
for (; *src != 0; src++)
{
dst = utf8_write_codepoint(dst, *src);
}
*dst++ = 0;
size_t size = (size_t)(dst - result);
return (utf8*)realloc(result, size);
}
/**
* Returns a pointer to the null terminator of the given UTF-8 string.
*/

View File

@@ -1356,8 +1356,8 @@ void Network::BeginChatLog()
_chatLogPath = BeginLog(directory, "", _chatLogFilenameFormat);
# if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = std::unique_ptr<wchar_t>(utf8_to_widechar(_chatLogPath.c_str()));
_chat_log_fs.open(pathW.get(), std::ios::out | std::ios::app);
auto pathW = String::ToWideChar(_chatLogPath.c_str());
_chat_log_fs.open(pathW.c_str(), std::ios::out | std::ios::app);
# else
_chat_log_fs.open(_chatLogPath, std::ios::out | std::ios::app);
# endif
@@ -1382,8 +1382,8 @@ void Network::BeginServerLog()
_serverLogPath = BeginLog(directory, ServerName, _serverLogFilenameFormat);
# if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = std::unique_ptr<wchar_t>(utf8_to_widechar(_serverLogPath.c_str()));
_server_log_fs.open(pathW.get(), std::ios::out | std::ios::app | std::ios::binary);
auto pathW = String::ToWideChar(_serverLogPath.c_str());
_server_log_fs.open(pathW.c_str(), std::ios::out | std::ios::app | std::ios::binary);
# else
_server_log_fs.open(_serverLogPath, std::ios::out | std::ios::app | std::ios::binary);
# endif

View File

@@ -68,7 +68,7 @@ static bool UploadMinidump(const std::map<std::wstring, std::wstring>& files, in
}
else
{
parameters[L"commit"] = String::ToUtf16(gVersionInfoFull);
parameters[L"commit"] = String::ToWideChar(gVersionInfoFull);
}
int timeout = 10000;
bool success = google_breakpad::HTTPUpload::SendRequest(url, parameters, files, &timeout, &response, &error);
@@ -150,18 +150,17 @@ static bool OnCrash(
wprintf(L"Commit: %s\n", _wszCommitSha1Short);
bool savedGameDumped = false;
utf8* saveFilePathUTF8 = widechar_to_utf8(saveFilePath);
auto saveFilePathUTF8 = String::ToUtf8(saveFilePath);
try
{
auto exporter = std::make_unique<S6Exporter>();
exporter->Export();
exporter->SaveGame(saveFilePathUTF8);
exporter->SaveGame(saveFilePathUTF8.c_str());
savedGameDumped = true;
}
catch (const std::exception&)
{
}
free(saveFilePathUTF8);
// Compress the save
if (savedGameDumped)
@@ -181,19 +180,16 @@ static bool OnCrash(
fclose(dest);
}
utf8* configFilePathUTF8 = widechar_to_utf8(configFilePath);
if (config_save(configFilePathUTF8))
auto configFilePathUTF8 = String::ToUtf8(configFilePath);
if (config_save(configFilePathUTF8.c_str()))
{
uploadFiles[L"attachment_config.ini"] = configFilePath;
}
free(configFilePathUTF8);
std::string screenshotPath = screenshot_dump();
if (!screenshotPath.empty())
{
wchar_t* screenshotPathWchar = utf8_to_widechar(screenshotPath.c_str());
auto screenshotPathW = std::wstring(screenshotPathWchar);
free(screenshotPathWchar);
auto screenshotPathW = String::ToWideChar(screenshotPath.c_str());
uploadFiles[L"attachment_screenshot.png"] = screenshotPathW;
}
@@ -270,11 +266,7 @@ static std::wstring GetDumpDirectory()
{
char userDirectory[MAX_PATH];
platform_get_user_directory(userDirectory, nullptr, sizeof(userDirectory));
wchar_t* userDirectoryW = utf8_to_widechar(userDirectory);
auto result = std::wstring(userDirectoryW);
free(userDirectoryW);
auto result = String::ToWideChar(userDirectory);
return result;
}

View File

@@ -50,7 +50,7 @@ namespace Platform
std::string GetEnvironmentVariable(const std::string& name)
{
std::wstring result;
auto wname = String::ToUtf16(name);
auto wname = String::ToWideChar(name);
wchar_t wvalue[256];
auto valueSize = GetEnvironmentVariableW(wname.c_str(), wvalue, (DWORD)std::size(wvalue));
if (valueSize < std::size(wvalue))

View File

@@ -21,6 +21,7 @@
# include "../OpenRCT2.h"
# include "../config/Config.h"
# include "../core/Path.hpp"
# include "../core/String.hpp"
# include "../localisation/Date.h"
# include "../localisation/Language.h"
# include "../util/Util.h"
@@ -97,20 +98,19 @@ void platform_get_time_local(rct2_time* out_time)
static size_t platform_utf8_to_multibyte(const utf8* path, char* buffer, size_t buffer_size)
{
wchar_t* wpath = utf8_to_widechar(path);
auto wpath = String::ToWideChar(path);
setlocale(LC_CTYPE, "UTF-8");
size_t len = wcstombs(NULL, wpath, 0);
size_t len = wcstombs(NULL, wpath.c_str(), 0);
bool truncated = false;
if (len > buffer_size - 1)
{
truncated = true;
len = buffer_size - 1;
}
wcstombs(buffer, wpath, len);
wcstombs(buffer, wpath.c_str(), len);
buffer[len] = '\0';
if (truncated)
log_warning("truncated string %s", buffer);
free(wpath);
return len;
}
@@ -286,19 +286,29 @@ bool platform_directory_delete(const utf8* path)
return true;
}
utf8* platform_get_absolute_path(const utf8* relative_path, const utf8* base_path)
std::string platform_get_absolute_path(const utf8* relative_path, const utf8* base_path)
{
utf8 path[MAX_PATH];
std::string result;
if (relative_path != nullptr)
{
std::string pathToResolve;
if (base_path == nullptr)
{
pathToResolve = std::string(relative_path);
}
else
{
pathToResolve = std::string(base_path) + std::string("/") + relative_path;
}
if (base_path != nullptr)
{
snprintf(path, MAX_PATH, "%s/%s", base_path, relative_path);
auto realpathResult = realpath(pathToResolve.c_str(), nullptr);
if (realpathResult != nullptr)
{
result = std::string(realpathResult);
free(realpathResult);
}
}
else
{
safe_strcpy(path, base_path, MAX_PATH);
}
return realpath(path, NULL);
return result;
}
bool platform_lock_single_instance()
@@ -482,18 +492,15 @@ datetime64 platform_get_datetime_now_utc()
return utcNow;
}
utf8* platform_get_username()
std::string platform_get_username()
{
struct passwd* pw = getpwuid(getuid());
if (pw)
std::string result;
auto pw = getpwuid(getuid());
if (pw != nullptr)
{
return pw->pw_name;
}
else
{
return nullptr;
result = std::string(pw->pw_name);
}
return result;
}
bool platform_process_is_elevated()

View File

@@ -85,18 +85,16 @@ void platform_get_time_local(rct2_time* out_time)
bool platform_file_exists(const utf8* path)
{
wchar_t* wPath = utf8_to_widechar(path);
DWORD result = GetFileAttributesW(wPath);
auto wPath = String::ToWideChar(path);
DWORD result = GetFileAttributesW(wPath.c_str());
DWORD error = GetLastError();
free(wPath);
return !(result == INVALID_FILE_ATTRIBUTES && (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND));
}
bool platform_directory_exists(const utf8* path)
{
wchar_t* wPath = utf8_to_widechar(path);
DWORD dwAttrib = GetFileAttributesW(wPath);
free(wPath);
auto wPath = String::ToWideChar(path);
DWORD dwAttrib = GetFileAttributesW(wPath.c_str());
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
@@ -127,27 +125,20 @@ bool platform_ensure_directory_exists(const utf8* path)
if (platform_directory_exists(path))
return 1;
wchar_t* wPath = utf8_to_widechar(path);
BOOL success = CreateDirectoryW(wPath, nullptr);
free(wPath);
return success == TRUE;
auto wPath = String::ToWideChar(path);
auto success = CreateDirectoryW(wPath.c_str(), nullptr);
return success != FALSE;
}
bool platform_directory_delete(const utf8* path)
{
wchar_t pszFrom[MAX_PATH];
wchar_t* wPath = utf8_to_widechar(path);
wcsncpy(pszFrom, wPath, MAX_PATH);
// Needs to be double-null terminated for some weird reason
pszFrom[wcslen(wPath) + 1] = 0;
free(wPath);
// Needs to be double-null terminated as pFrom is a null terminated array of strings
auto wPath = String::ToWideChar(path) + L"\0";
SHFILEOPSTRUCTW fileop;
fileop.hwnd = nullptr; // no status display
fileop.wFunc = FO_DELETE; // delete operation
fileop.pFrom = pszFrom; // source file name as double null terminated string
fileop.pFrom = wPath.c_str(); // source file name as double null terminated string
fileop.pTo = nullptr; // no destination needed
fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user
@@ -189,30 +180,25 @@ int32_t platform_get_drives()
bool platform_file_copy(const utf8* srcPath, const utf8* dstPath, bool overwrite)
{
wchar_t* wSrcPath = utf8_to_widechar(srcPath);
wchar_t* wDstPath = utf8_to_widechar(dstPath);
BOOL success = CopyFileW(wSrcPath, wDstPath, overwrite ? FALSE : TRUE);
free(wSrcPath);
free(wDstPath);
return success == TRUE;
auto wSrcPath = String::ToWideChar(srcPath);
auto wDstPath = String::ToWideChar(dstPath);
auto success = CopyFileW(wSrcPath.c_str(), wDstPath.c_str(), overwrite ? FALSE : TRUE);
return success != FALSE;
}
bool platform_file_move(const utf8* srcPath, const utf8* dstPath)
{
wchar_t* wSrcPath = utf8_to_widechar(srcPath);
wchar_t* wDstPath = utf8_to_widechar(dstPath);
BOOL success = MoveFileW(wSrcPath, wDstPath);
free(wSrcPath);
free(wDstPath);
return success == TRUE;
auto wSrcPath = String::ToWideChar(srcPath);
auto wDstPath = String::ToWideChar(dstPath);
auto success = MoveFileW(wSrcPath.c_str(), wDstPath.c_str());
return success != FALSE;
}
bool platform_file_delete(const utf8* path)
{
wchar_t* wPath = utf8_to_widechar(path);
BOOL success = DeleteFileW(wPath);
free(wPath);
return success == TRUE;
auto wPath = String::ToWideChar(path);
auto success = DeleteFileW(wPath.c_str());
return success != FALSE;
}
bool platform_get_steam_path(utf8* outPath, size_t outSize)
@@ -236,10 +222,9 @@ bool platform_get_steam_path(utf8* outPath, size_t outSize)
result = RegQueryValueExW(hKey, L"SteamPath", nullptr, &type, (LPBYTE)wSteamPath, &size);
if (result == ERROR_SUCCESS)
{
utf8* utf8SteamPath = widechar_to_utf8(wSteamPath);
safe_strcpy(outPath, utf8SteamPath, outSize);
auto utf8SteamPath = String::ToUtf8(wSteamPath);
safe_strcpy(outPath, utf8SteamPath.c_str(), outSize);
safe_strcat_path(outPath, "steamapps\\common", outSize);
free(utf8SteamPath);
}
free(wSteamPath);
RegCloseKey(hKey);
@@ -340,24 +325,22 @@ uint16_t platform_get_locale_language()
time_t platform_file_get_modified_time(const utf8* path)
{
WIN32_FILE_ATTRIBUTE_DATA data;
wchar_t* wPath = utf8_to_widechar(path);
BOOL result = GetFileAttributesExW(wPath, GetFileExInfoStandard, &data);
free(wPath);
if (!result)
return 0;
FILETIME localFileTime;
result = FileTimeToLocalFileTime(&data.ftLastWriteTime, &localFileTime);
if (!result)
return 0;
ULARGE_INTEGER ull;
ull.LowPart = localFileTime.dwLowDateTime;
ull.HighPart = localFileTime.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
WIN32_FILE_ATTRIBUTE_DATA data{};
auto wPath = String::ToWideChar(path);
auto result = GetFileAttributesExW(wPath.c_str(), GetFileExInfoStandard, &data);
if (result != FALSE)
{
FILETIME localFileTime{};
result = FileTimeToLocalFileTime(&data.ftLastWriteTime, &localFileTime);
if (result != FALSE)
{
ULARGE_INTEGER ull{};
ull.LowPart = localFileTime.dwLowDateTime;
ull.HighPart = localFileTime.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
}
}
return 0;
}
uint8_t platform_get_locale_currency()
@@ -468,9 +451,8 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size)
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Fonts, 0, nullptr, &fontFolder)))
{
// Convert wchar to utf8, then copy the font folder path to the buffer.
utf8* outPathTemp = widechar_to_utf8(fontFolder);
safe_strcpy(buffer, outPathTemp, size);
free(outPathTemp);
auto outPathTemp = String::ToUtf8(fontFolder);
safe_strcpy(buffer, outPathTemp.c_str(), size);
CoTaskMemFree(fontFolder);
@@ -491,24 +473,30 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size)
}
# endif // NO_TTF
utf8* platform_get_absolute_path(const utf8* relativePath, const utf8* basePath)
std::string platform_get_absolute_path(const utf8* relativePath, const utf8* basePath)
{
utf8 path[MAX_PATH];
safe_strcpy(path, basePath, sizeof(path));
safe_strcat_path(path, relativePath, sizeof(path));
wchar_t* pathW = utf8_to_widechar(path);
wchar_t fullPathW[MAX_PATH];
DWORD fullPathLen = GetFullPathNameW(pathW, (DWORD)std::size(fullPathW), fullPathW, nullptr);
free(pathW);
if (fullPathLen == 0)
std::string result;
if (relativePath != nullptr)
{
return nullptr;
}
std::string pathToResolve;
if (basePath == nullptr)
{
pathToResolve = std::string(relativePath);
}
else
{
pathToResolve = std::string(basePath) + std::string("\\") + relativePath;
}
return widechar_to_utf8(fullPathW);
auto pathToResolveW = String::ToWideChar(pathToResolve);
wchar_t fullPathW[MAX_PATH]{};
auto fullPathLen = GetFullPathNameW(pathToResolveW.c_str(), (DWORD)std::size(fullPathW), fullPathW, nullptr);
if (fullPathLen != 0)
{
result = String::ToUtf8(fullPathW);
}
}
return result;
}
datetime64 platform_get_datetime_now_utc()
@@ -524,18 +512,16 @@ datetime64 platform_get_datetime_now_utc()
return utcNow;
}
utf8* platform_get_username()
std::string platform_get_username()
{
static wchar_t usernameW[UNLEN + 1];
std::string result;
wchar_t usernameW[UNLEN + 1]{};
DWORD usernameLength = UNLEN + 1;
if (!GetUserNameW(usernameW, &usernameLength))
if (GetUserNameW(usernameW, &usernameLength))
{
return nullptr;
result = String::ToUtf8(usernameW);
}
static std::string username;
username = widechar_to_utf8(usernameW);
return username.data();
return result;
}
bool platform_process_is_elevated()
@@ -565,15 +551,11 @@ bool platform_process_is_elevated()
# define SOFTWARE_CLASSES L"Software\\Classes"
# define MUI_CACHE L"Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache"
static void get_progIdName(wchar_t* dst, const utf8* extension)
static std::wstring get_progIdName(const std::string_view& extension)
{
utf8 progIdName[128];
safe_strcpy(progIdName, OPENRCT2_NAME, sizeof(progIdName));
safe_strcat(progIdName, extension, sizeof(progIdName));
wchar_t* progIdNameW = utf8_to_widechar(progIdName);
lstrcpyW(dst, progIdNameW);
free(progIdNameW);
auto progIdName = std::string(OPENRCT2_NAME) + std::string(extension);
auto progIdNameW = String::ToWideChar(progIdName);
return progIdNameW;
}
static bool windows_setup_file_association(
@@ -587,13 +569,11 @@ static bool windows_setup_file_association(
GetModuleFileNameW(nullptr, exePathW, (DWORD)std::size(exePathW));
GetModuleFileNameW(plaform_get_dll_module(), dllPathW, (DWORD)std::size(dllPathW));
wchar_t* extensionW = utf8_to_widechar(extension);
wchar_t* fileTypeTextW = utf8_to_widechar(fileTypeText);
wchar_t* commandTextW = utf8_to_widechar(commandText);
wchar_t* commandArgsW = utf8_to_widechar(commandArgs);
wchar_t progIdNameW[128];
get_progIdName(progIdNameW, extension);
auto extensionW = String::ToWideChar(extension);
auto fileTypeTextW = String::ToWideChar(fileTypeText);
auto commandTextW = String::ToWideChar(commandText);
auto commandArgsW = String::ToWideChar(commandArgs);
auto progIdNameW = get_progIdName(extension);
bool result = false;
HKEY hKey = nullptr;
@@ -606,18 +586,18 @@ static bool windows_setup_file_association(
}
// [hRootKey\.ext]
if (RegSetValueW(hRootKey, extensionW, REG_SZ, progIdNameW, 0) != ERROR_SUCCESS)
if (RegSetValueW(hRootKey, extensionW.c_str(), REG_SZ, progIdNameW.c_str(), 0) != ERROR_SUCCESS)
{
goto fail;
}
if (RegCreateKeyW(hRootKey, progIdNameW, &hKey) != ERROR_SUCCESS)
if (RegCreateKeyW(hRootKey, progIdNameW.c_str(), &hKey) != ERROR_SUCCESS)
{
goto fail;
}
// [hRootKey\OpenRCT2.ext]
if (RegSetValueW(hKey, nullptr, REG_SZ, fileTypeTextW, 0) != ERROR_SUCCESS)
if (RegSetValueW(hKey, nullptr, REG_SZ, fileTypeTextW.c_str(), 0) != ERROR_SUCCESS)
{
goto fail;
}
@@ -637,14 +617,14 @@ static bool windows_setup_file_association(
}
// [hRootKey\OpenRCT2.sv6\shell\open]
if (RegSetValueW(hKey, L"shell\\open", REG_SZ, commandTextW, 0) != ERROR_SUCCESS)
if (RegSetValueW(hKey, L"shell\\open", REG_SZ, commandTextW.c_str(), 0) != ERROR_SUCCESS)
{
goto fail;
}
// [hRootKey\OpenRCT2.sv6\shell\open\command]
wchar_t szCommandW[MAX_PATH];
printResult = swprintf_s(szCommandW, MAX_PATH, L"\"%s\" %s", exePathW, commandArgsW);
printResult = swprintf_s(szCommandW, MAX_PATH, L"\"%s\" %s", exePathW, commandArgsW.c_str());
assert(printResult >= 0);
if (RegSetValueW(hKey, L"shell\\open\\command", REG_SZ, szCommandW, 0) != ERROR_SUCCESS)
{
@@ -653,10 +633,6 @@ static bool windows_setup_file_association(
result = true;
fail:
free(extensionW);
free(fileTypeTextW);
free(commandTextW);
free(commandArgsW);
RegCloseKey(hKey);
RegCloseKey(hRootKey);
return result;
@@ -672,9 +648,8 @@ static void windows_remove_file_association(const utf8* extension)
RegDeleteTreeA(hRootKey, extension);
// [hRootKey\OpenRCT2.ext]
wchar_t progIdName[128];
get_progIdName(progIdName, extension);
RegDeleteTreeW(hRootKey, progIdName);
auto progIdName = get_progIdName(extension);
RegDeleteTreeW(hRootKey, progIdName.c_str());
RegCloseKey(hRootKey);
}

View File

@@ -99,7 +99,7 @@ bool platform_original_rct1_data_exists(const utf8* path);
time_t platform_file_get_modified_time(const utf8* path);
bool platform_ensure_directory_exists(const utf8* path);
bool platform_directory_delete(const utf8* path);
utf8* platform_get_absolute_path(const utf8* relative_path, const utf8* base_path);
std::string platform_get_absolute_path(const utf8* relative_path, const utf8* base_path);
bool platform_lock_single_instance();
bool platform_place_string_on_clipboard(utf8* target);
@@ -113,7 +113,7 @@ uint32_t platform_get_ticks();
void platform_sleep(uint32_t ms);
void platform_get_openrct_data_path(utf8* outPath, size_t outSize);
void platform_get_user_directory(utf8* outPath, const utf8* subDirectory, size_t outSize);
utf8* platform_get_username();
std::string platform_get_username();
bool platform_open_common_file_dialog(utf8* outFilename, file_dialog_desc* desc, size_t outSize);
utf8* platform_open_directory_browser(const utf8* title);
uint8_t platform_get_locale_currency();

View File

@@ -1189,11 +1189,11 @@ int32_t scenario_save(const utf8* path, int32_t flags)
{
if (flags & S6_SAVE_FLAG_SCENARIO)
{
log_verbose("saving scenario");
log_verbose("scenario_save(%s, SCENARIO)", path);
}
else
{
log_verbose("saving game");
log_verbose("scenario_save(%s, SAVED GAME)", path);
}
if (!(flags & S6_SAVE_FLAG_AUTOMATIC))