mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-24 13:05:12 +00:00
Windows platform enhancements (#84)
* Enabling portable paths for Windows (custom user preferences dir, drag'n'drop paths) * Updated the windows messages loop for DXGI window API * Fixed international keyboard layouts text input in DXGI
This commit is contained in:
parent
4967c911cc
commit
e2c15afc68
7 changed files with 275 additions and 115 deletions
|
@ -52,7 +52,7 @@ bool fs_init(const char *writepath) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// we shall not progress any further if the path is inaccessible
|
// we shall not progress any further if the path is inaccessible
|
||||||
if ('\0' == fs_writepath[0]) {
|
if (('\0' == fs_writepath[0]) || !fs_sys_dir_exists(fs_writepath)) {
|
||||||
sys_fatal("Could not access the User Preferences directory.");
|
sys_fatal("Could not access the User Preferences directory.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,13 @@
|
||||||
#include "gfx_dxgi.h"
|
#include "gfx_dxgi.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "pc/mods/mod_import.h"
|
#include "pc/mods/mod_import.h"
|
||||||
#ifdef DISCORD_SDK
|
#include "pc/rom_checker.h"
|
||||||
#include "pc/discord/discord.h"
|
#include "pc/network/version.h"
|
||||||
#endif
|
#include "pc/configfile.h"
|
||||||
#include "pc/network/version.h"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "../configfile.h"
|
#include "pc/pc_main.h"
|
||||||
#include "../pc_main.h"
|
|
||||||
|
|
||||||
#include "gfx_window_manager_api.h"
|
#include "gfx_window_manager_api.h"
|
||||||
#include "gfx_rendering_api.h"
|
#include "gfx_rendering_api.h"
|
||||||
|
@ -91,7 +89,6 @@ static struct {
|
||||||
bool sync_interval_means_frames_to_wait;
|
bool sync_interval_means_frames_to_wait;
|
||||||
UINT length_in_vsync_frames;
|
UINT length_in_vsync_frames;
|
||||||
|
|
||||||
void (*run_one_game_iter)(void);
|
|
||||||
bool (*on_key_down)(int scancode);
|
bool (*on_key_down)(int scancode);
|
||||||
bool (*on_key_up)(int scancode);
|
bool (*on_key_up)(int scancode);
|
||||||
void (*on_all_keys_up)(void);
|
void (*on_all_keys_up)(void);
|
||||||
|
@ -244,100 +241,122 @@ static void gfx_dxgi_on_resize(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onkeydown(WPARAM w_param, LPARAM l_param) {
|
static void gfx_dxgi_on_key_down(WPARAM w_param, LPARAM l_param) {
|
||||||
int key = ((l_param >> 16) & 0x1ff);
|
int key = ((l_param >> 16) & 0x1ff);
|
||||||
if (inTextInput) {
|
if (dxgi.on_key_down != nullptr) { dxgi.on_key_down(key); }
|
||||||
const int keyboardScanCode = (l_param >> 16) & 0x00ff;
|
|
||||||
const int virtualKey = w_param;
|
|
||||||
|
|
||||||
BYTE keyboardState[256];
|
|
||||||
GetKeyboardState(keyboardState);
|
|
||||||
|
|
||||||
WORD ascii = 0;
|
|
||||||
const int len = ToAscii(virtualKey, keyboardScanCode, keyboardState, &ascii, 0);
|
|
||||||
if (len > 0) {
|
|
||||||
dxgi.on_text_input((char*)&ascii);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dxgi.on_key_down != nullptr) {
|
|
||||||
dxgi.on_key_down(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static void onkeyup(WPARAM w_param, LPARAM l_param) {
|
static void gfx_dxgi_on_key_up(WPARAM w_param, LPARAM l_param) {
|
||||||
int key = ((l_param >> 16) & 0x1ff);
|
int key = ((l_param >> 16) & 0x1ff);
|
||||||
if (dxgi.on_key_up != nullptr) {
|
if (dxgi.on_key_up != nullptr) { dxgi.on_key_up(key); }
|
||||||
dxgi.on_key_up(key);
|
}
|
||||||
|
|
||||||
|
static void gfx_dxgi_on_text_input(wchar_t code_unit) {
|
||||||
|
if (inTextInput && (!IS_HIGH_SURROGATE(code_unit)) && (!IS_LOW_SURROGATE(code_unit))) {
|
||||||
|
char utf8_buffer[3 + 1];
|
||||||
|
if (code_unit >= 0x0800) { // 3-byte encoding
|
||||||
|
utf8_buffer[0] = 0xe0 | ((code_unit >> 12) & 0x0f);
|
||||||
|
utf8_buffer[1] = 0x80 | ((code_unit >> 6) & 0x3f);
|
||||||
|
utf8_buffer[2] = 0x80 | (code_unit & 0x3f);
|
||||||
|
utf8_buffer[3] = '\0';
|
||||||
|
} else if (code_unit >= 0x0080) { // 2-byte encoding
|
||||||
|
utf8_buffer[0] = 0xc0 | ((code_unit >> 6) & 0x1f);
|
||||||
|
utf8_buffer[1] = 0x80 | (code_unit & 0x3f);
|
||||||
|
utf8_buffer[2] = '\0';
|
||||||
|
} else { // 1-byte encoding
|
||||||
|
if (code_unit < ' ') { return; } // skipping control chars
|
||||||
|
utf8_buffer[0] = (char)code_unit;
|
||||||
|
utf8_buffer[1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
dxgi.on_text_input(utf8_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param) {
|
static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param) {
|
||||||
|
WCHAR wcsFileName[MAX_PATH];
|
||||||
|
char szFileName[MAX_PATH];
|
||||||
|
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case WM_SIZE:
|
case WM_SIZE: {
|
||||||
gfx_dxgi_on_resize();
|
gfx_dxgi_on_resize();
|
||||||
break;
|
return 0;
|
||||||
case WM_DESTROY:
|
}
|
||||||
|
case WM_CLOSE: {
|
||||||
|
DestroyWindow(h_wnd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_DESTROY: {
|
||||||
game_exit();
|
game_exit();
|
||||||
break;
|
PostQuitMessage(0);
|
||||||
case WM_PAINT:
|
return 0;
|
||||||
if (dxgi.showing_error) {
|
|
||||||
return DefWindowProcW(h_wnd, message, w_param, l_param);
|
|
||||||
} else {
|
|
||||||
if (dxgi.run_one_game_iter != nullptr) {
|
|
||||||
dxgi.run_one_game_iter();
|
|
||||||
}
|
}
|
||||||
}
|
case WM_ACTIVATEAPP: {
|
||||||
break;
|
|
||||||
case WM_ACTIVATEAPP:
|
|
||||||
if (dxgi.on_all_keys_up != nullptr) {
|
if (dxgi.on_all_keys_up != nullptr) {
|
||||||
dxgi.on_all_keys_up();
|
dxgi.on_all_keys_up();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WM_KEYDOWN:
|
}
|
||||||
onkeydown(w_param, l_param);
|
case WM_KEYDOWN: {
|
||||||
break;
|
gfx_dxgi_on_key_down(w_param, l_param);
|
||||||
case WM_KEYUP:
|
return 0;
|
||||||
onkeyup(w_param, l_param);
|
}
|
||||||
break;
|
case WM_KEYUP: {
|
||||||
case WM_SYSKEYDOWN:
|
gfx_dxgi_on_key_up(w_param, l_param);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_CHAR: {
|
||||||
|
// some keyboard input translated to a single UTF-16LE code unit
|
||||||
|
gfx_dxgi_on_text_input((wchar_t)w_param);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_SYSKEYDOWN: {
|
||||||
if ((w_param == VK_RETURN) && ((l_param & 1 << 30) == 0)) {
|
if ((w_param == VK_RETURN) && ((l_param & 1 << 30) == 0)) {
|
||||||
toggle_borderless_window_full_screen(!dxgi.is_full_screen);
|
toggle_borderless_window_full_screen(!dxgi.is_full_screen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONDOWN: {
|
||||||
|
if (!gRomIsValid) {
|
||||||
|
OPENFILENAMEW ofn;
|
||||||
|
ZeroMemory(&ofn, sizeof(ofn));
|
||||||
|
ofn.lStructSize = sizeof(ofn);
|
||||||
|
ofn.hwndOwner = h_wnd;
|
||||||
|
ofn.lpstrFilter = L"N64 ROM files (*.z64)\0*.z64\0";
|
||||||
|
ofn.lpstrFile = wcsFileName;
|
||||||
|
ofn.nMaxFile = MAX_PATH;
|
||||||
|
ofn.lpstrTitle = L"Select the \"Super Mario 64 (U) [!]\" ROM file..";
|
||||||
|
ofn.Flags = (OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
|
||||||
|
|
||||||
|
wcsFileName[0] = L'\0';
|
||||||
|
if (GetOpenFileNameW(&ofn) && sys_windows_short_path_from_wcs(szFileName, MAX_PATH, wcsFileName)) {
|
||||||
|
rom_on_drop_file(szFileName);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
return DefWindowProcW(h_wnd, message, w_param, l_param);
|
|
||||||
}
|
}
|
||||||
case WM_DROPFILES: {
|
case WM_DROPFILES: {
|
||||||
HDROP hDrop = (HDROP)w_param;
|
HDROP hDrop = (HDROP)w_param;
|
||||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
UINT nFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||||
for (UINT i = 0; i < nFiles; i++)
|
for (UINT i = 0; i < nFiles; i++) {
|
||||||
{
|
if (0 != DragQueryFileW(hDrop, i, wcsFileName, MAX_PATH)) {
|
||||||
char szFileName[MAX_PATH] = { 0 };
|
if (sys_windows_short_path_from_wcs(szFileName, MAX_PATH, wcsFileName)) {
|
||||||
DragQueryFile(hDrop, i, szFileName, MAX_PATH);
|
if (!gRomIsValid) {
|
||||||
|
rom_on_drop_file(szFileName);
|
||||||
|
} else if (gGameInited) {
|
||||||
mod_import_file(szFileName);
|
mod_import_file(szFileName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
DragFinish(hDrop);
|
DragFinish(hDrop);
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return DefWindowProcW(h_wnd, message, w_param, l_param);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configWindow.reset) {
|
|
||||||
dxgi.last_maximized_state = false;
|
|
||||||
configWindow.reset = false;
|
|
||||||
configWindow.x = WAPI_WIN_CENTERPOS;
|
|
||||||
configWindow.y = WAPI_WIN_CENTERPOS;
|
|
||||||
configWindow.w = DESIRED_SCREEN_WIDTH;
|
|
||||||
configWindow.h = DESIRED_SCREEN_HEIGHT;
|
|
||||||
configWindow.fullscreen = false;
|
|
||||||
configWindow.settings_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configWindow.settings_changed) {
|
|
||||||
configWindow.settings_changed = false;
|
|
||||||
update_screen_settings();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProcW(h_wnd, message, w_param, l_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_dxgi_init(const char *window_title) {
|
static void gfx_dxgi_init(const char *window_title) {
|
||||||
|
@ -406,16 +425,7 @@ static void gfx_dxgi_set_keyboard_callbacks(bool (*on_key_down)(int scancode), b
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_dxgi_main_loop(void (*run_one_game_iter)(void)) {
|
static void gfx_dxgi_main_loop(void (*run_one_game_iter)(void)) {
|
||||||
dxgi.run_one_game_iter = run_one_game_iter;
|
run_one_game_iter();
|
||||||
|
|
||||||
MSG msg;
|
|
||||||
while (GetMessage(&msg, nullptr, 0, 0)) {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
#ifdef DISCORD_SDK
|
|
||||||
discord_update();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_dxgi_get_dimensions(uint32_t *width, uint32_t *height) {
|
static void gfx_dxgi_get_dimensions(uint32_t *width, uint32_t *height) {
|
||||||
|
@ -424,11 +434,28 @@ static void gfx_dxgi_get_dimensions(uint32_t *width, uint32_t *height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_dxgi_handle_events(void) {
|
static void gfx_dxgi_handle_events(void) {
|
||||||
/*MSG msg;
|
MSG msg;
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessageW(&msg);
|
||||||
}*/
|
if (msg.message == WM_QUIT) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configWindow.reset) {
|
||||||
|
dxgi.last_maximized_state = false;
|
||||||
|
configWindow.reset = false;
|
||||||
|
configWindow.x = WAPI_WIN_CENTERPOS;
|
||||||
|
configWindow.y = WAPI_WIN_CENTERPOS;
|
||||||
|
configWindow.w = DESIRED_SCREEN_WIDTH;
|
||||||
|
configWindow.h = DESIRED_SCREEN_HEIGHT;
|
||||||
|
configWindow.fullscreen = false;
|
||||||
|
configWindow.settings_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configWindow.settings_changed) {
|
||||||
|
configWindow.settings_changed = false;
|
||||||
|
update_screen_settings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t qpc_to_us(uint64_t qpc) {
|
static uint64_t qpc_to_us(uint64_t qpc) {
|
||||||
|
|
|
@ -186,13 +186,22 @@ static void gfx_sdl_onkeyup(int scancode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_sdl_ondropfile(char* path) {
|
static void gfx_sdl_ondropfile(char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
char portable_path[SYS_MAX_PATH];
|
||||||
|
if (sys_windows_short_path_from_mbs(portable_path, SYS_MAX_PATH, path)) {
|
||||||
|
if (!gRomIsValid) {
|
||||||
|
rom_on_drop_file(portable_path);
|
||||||
|
} else if (gGameInited) {
|
||||||
|
mod_import_file(portable_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (!gRomIsValid) {
|
if (!gRomIsValid) {
|
||||||
rom_on_drop_file(path);
|
rom_on_drop_file(path);
|
||||||
return;
|
} else if (gGameInited) {
|
||||||
}
|
|
||||||
if (gGameInited) {
|
|
||||||
mod_import_file(path);
|
mod_import_file(path);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfx_sdl_handle_events(void) {
|
static void gfx_sdl_handle_events(void) {
|
||||||
|
|
|
@ -168,7 +168,14 @@ bool str_ends_with(const char* string, const char* suffix) {
|
||||||
|
|
||||||
if (suffixLength > stringLength) { return false; }
|
if (suffixLength > stringLength) { return false; }
|
||||||
|
|
||||||
return !strcmp(&string[stringLength - suffixLength], suffix);
|
#ifdef _WIN32
|
||||||
|
// Paths on Windows are case-insensitive and might have
|
||||||
|
// upper-case or mixed-case endings.
|
||||||
|
return (0 == _stricmp(&(string[stringLength - suffixLength]), suffix));
|
||||||
|
#else
|
||||||
|
// Always expecting lower-case file paths and extensions
|
||||||
|
return (0 == strcmp(&(string[stringLength - suffixLength]), suffix));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -353,17 +353,28 @@ int main(int argc, char *argv[]) {
|
||||||
// handle terminal arguments
|
// handle terminal arguments
|
||||||
if (!parse_cli_opts(argc, argv)) { return 0; }
|
if (!parse_cli_opts(argc, argv)) { return 0; }
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#ifdef _WIN32
|
||||||
// handle Windows console
|
// handle Windows console
|
||||||
if (!gCLIOpts.console) {
|
if (gCLIOpts.console) {
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
} else {
|
||||||
FreeConsole();
|
FreeConsole();
|
||||||
freopen("NUL", "w", stdout);
|
freopen("NUL", "w", stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char* userPath = gCLIOpts.savePath[0] ? gCLIOpts.savePath : sys_user_path();
|
#ifdef _WIN32
|
||||||
fs_init(userPath);
|
if (gCLIOpts.savePath[0]) {
|
||||||
|
char portable_path[SYS_MAX_PATH] = {};
|
||||||
|
sys_windows_short_path_from_mbs(portable_path, SYS_MAX_PATH, gCLIOpts.savePath);
|
||||||
|
fs_init(portable_path);
|
||||||
|
} else {
|
||||||
|
fs_init(sys_user_path());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fs_init(gCLIOpts.savePath[0] ? gCLIOpts.savePath : sys_user_path());
|
||||||
|
#endif
|
||||||
|
|
||||||
configfile_load();
|
configfile_load();
|
||||||
|
|
||||||
legacy_folder_handler();
|
legacy_folder_handler();
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
@ -82,18 +82,120 @@ void sys_fatal(const char *fmt, ...) {
|
||||||
sys_fatal_impl(msg);
|
sys_fatal_impl(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#ifdef _WIN32
|
||||||
|
|
||||||
static BOOL sys_windows_short_path(LPSTR destPath, SIZE_T destSize, LPWSTR wideLongPath)
|
static bool sys_windows_pathname_is_portable(const wchar_t *name, size_t size)
|
||||||
{
|
{
|
||||||
WCHAR wideShortPath[SYS_MAX_PATH];
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
wchar_t c = name[i];
|
||||||
|
|
||||||
|
// character outside the ASCII printable range
|
||||||
|
if ((c < L' ') || (c > L'~')) { return false; }
|
||||||
|
|
||||||
|
// characters unallowed in filenames
|
||||||
|
switch (c) {
|
||||||
|
// skipping ':', as it will appear with the drive specifier
|
||||||
|
case L'<': case L'>': case L'/': case L'\\':
|
||||||
|
case L'"': case L'|': case L'?': case L'*':
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static wchar_t *sys_windows_pathname_get_delim(const wchar_t *name)
|
||||||
|
{
|
||||||
|
const wchar_t *sep1 = wcschr(name, L'/');
|
||||||
|
const wchar_t *sep2 = wcschr(name, L'\\');
|
||||||
|
|
||||||
|
if (NULL == sep1) { return (wchar_t*)sep2; }
|
||||||
|
if (NULL == sep2) { return (wchar_t*)sep1; }
|
||||||
|
|
||||||
|
return (sep1 < sep2) ? (wchar_t*)sep1 : (wchar_t*)sep2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sys_windows_short_path_from_wcs(char *destPath, size_t destSize, const wchar_t *wcsLongPath)
|
||||||
|
{
|
||||||
|
wchar_t wcsShortPath[SYS_MAX_PATH]; // converted with WinAPI
|
||||||
|
wchar_t wcsPortablePath[SYS_MAX_PATH]; // non-unicode parts replaced back with long forms
|
||||||
|
|
||||||
// Convert the Long Path in Wide Format to the alternate short form.
|
// Convert the Long Path in Wide Format to the alternate short form.
|
||||||
// It will still point to already existing directory.
|
// It will still point to already existing directory or file.
|
||||||
if (0 == GetShortPathNameW(wideLongPath, wideShortPath, SYS_MAX_PATH)) { return FALSE; }
|
if (0 == GetShortPathNameW(wcsLongPath, wcsShortPath, SYS_MAX_PATH)) { return FALSE; }
|
||||||
|
|
||||||
|
// Scanning the paths side-by-side, to keep the portable (ASCII)
|
||||||
|
// parts of the absolute path unchanged (in the long form)
|
||||||
|
wcsPortablePath[0] = L'\0';
|
||||||
|
const wchar_t *longPart = wcsLongPath;
|
||||||
|
wchar_t *shortPart = wcsShortPath;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int longLength;
|
||||||
|
int shortLength;
|
||||||
|
const wchar_t *sourcePart;
|
||||||
|
int sourceLength;
|
||||||
|
int bufferLength;
|
||||||
|
|
||||||
|
const wchar_t *longDelim = sys_windows_pathname_get_delim(longPart);
|
||||||
|
wchar_t *shortDelim = sys_windows_pathname_get_delim(shortPart);
|
||||||
|
|
||||||
|
if (NULL == longDelim) {
|
||||||
|
longLength = wcslen(longPart); // final part of the scanned path
|
||||||
|
} else {
|
||||||
|
longLength = longDelim - longPart; // ptr diff measured in WCHARs
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL == shortDelim) {
|
||||||
|
shortLength = wcslen(shortPart); // final part of the scanned path
|
||||||
|
} else {
|
||||||
|
shortLength = shortDelim - shortPart; // ptr diff measured in WCHARs
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys_windows_pathname_is_portable(longPart, longLength)) {
|
||||||
|
// take the original name (subdir or filename)
|
||||||
|
sourcePart = longPart;
|
||||||
|
sourceLength = longLength;
|
||||||
|
} else {
|
||||||
|
// take the converted alternate (short) name
|
||||||
|
sourcePart = shortPart;
|
||||||
|
sourceLength = shortLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take into account the slash-or-backslash separator
|
||||||
|
if (L'\0' != sourcePart[sourceLength]) { sourceLength++; }
|
||||||
|
|
||||||
|
// how many WCHARs are still left in the buffer
|
||||||
|
bufferLength = (SYS_MAX_PATH - 1) - wcslen(wcsPortablePath);
|
||||||
|
if (sourceLength > bufferLength) { return false; }
|
||||||
|
|
||||||
|
wcsncat(wcsPortablePath, sourcePart, sourceLength);
|
||||||
|
|
||||||
|
// path end reached?
|
||||||
|
if ((NULL == longDelim) || (NULL == shortDelim)) { break; }
|
||||||
|
|
||||||
|
// compare the next name
|
||||||
|
longPart = longDelim + 1;
|
||||||
|
shortPart = shortDelim + 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Short Path can be safely represented by the US-ASCII Charset.
|
// Short Path can be safely represented by the US-ASCII Charset.
|
||||||
return (WideCharToMultiByte(CP_ACP, 0, wideShortPath, (-1), destPath, destSize, NULL, NULL) > 0);
|
return (WideCharToMultiByte(CP_ACP, 0, wcsPortablePath, (-1), destPath, destSize, NULL, NULL) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sys_windows_short_path_from_mbs(char *destPath, size_t destSize, const char *mbsLongPath)
|
||||||
|
{
|
||||||
|
// Converting the absolute path in UTF-8 format (MultiByte String)
|
||||||
|
// to an alternate (portable) format usable on Windows.
|
||||||
|
// Assuming the given paths points to an already existing file or folder.
|
||||||
|
|
||||||
|
wchar_t wcsWidePath[SYS_MAX_PATH];
|
||||||
|
|
||||||
|
if (MultiByteToWideChar(CP_UTF8, 0, mbsLongPath, (-1), wcsWidePath, SYS_MAX_PATH) > 0)
|
||||||
|
{
|
||||||
|
return sys_windows_short_path_from_wcs(destPath, destSize, wcsWidePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sys_user_path(void)
|
const char *sys_user_path(void)
|
||||||
|
@ -142,7 +244,7 @@ const char *sys_user_path(void)
|
||||||
if (ERROR_ALREADY_EXISTS != GetLastError()) { return NULL; }
|
if (ERROR_ALREADY_EXISTS != GetLastError()) { return NULL; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return sys_windows_short_path(shortPath, SYS_MAX_PATH, widePath) ? shortPath : NULL;
|
return sys_windows_short_path_from_wcs(shortPath, SYS_MAX_PATH, widePath) ? shortPath : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sys_exe_path(void)
|
const char *sys_exe_path(void)
|
||||||
|
@ -157,7 +259,7 @@ const char *sys_exe_path(void)
|
||||||
if (NULL != lastBackslash) { *lastBackslash = L'\0'; }
|
if (NULL != lastBackslash) { *lastBackslash = L'\0'; }
|
||||||
else { return NULL; }
|
else { return NULL; }
|
||||||
|
|
||||||
return sys_windows_short_path(shortPath, SYS_MAX_PATH, widePath) ? shortPath : NULL;
|
return sys_windows_short_path_from_wcs(shortPath, SYS_MAX_PATH, widePath) ? shortPath : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sys_fatal_impl(const char *msg) {
|
static void sys_fatal_impl(const char *msg) {
|
||||||
|
|
|
@ -15,6 +15,10 @@ char *sys_strlwr(char *src);
|
||||||
int sys_strcasecmp(const char *s1, const char *s2);
|
int sys_strcasecmp(const char *s1, const char *s2);
|
||||||
|
|
||||||
// path stuff
|
// path stuff
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool sys_windows_short_path_from_wcs(char *destPath, size_t destSize, const wchar_t *wcsLongPath);
|
||||||
|
bool sys_windows_short_path_from_mbs(char* destPath, size_t destSize, const char *mbsLongPath);
|
||||||
|
#endif
|
||||||
const char *sys_user_path(void);
|
const char *sys_user_path(void);
|
||||||
const char *sys_exe_path(void);
|
const char *sys_exe_path(void);
|
||||||
const char *sys_file_extension(const char *fpath);
|
const char *sys_file_extension(const char *fpath);
|
||||||
|
|
Loading…
Reference in a new issue