mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
fix(input): read the input file from paths with non-ASCII characters
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e34c479210
commit
b8ed78902f
3 files changed
+70
-8
No files matched your search
@@ -214,13 +214,32 @@ std::uint8_t stickByte(std::int32_t axis) {
|
||||
return static_cast<std::uint8_t>(((axis + 32768) * 255 + 32767) / 65535);
|
||||
}
|
||||
|
||||
std::filesystem::path pathFromUtf8(std::string_view text) {
|
||||
return std::filesystem::path(std::u8string(reinterpret_cast<const char8_t*>(text.data()), text.size()));
|
||||
}
|
||||
|
||||
std::string pathToUtf8(const std::filesystem::path& path) {
|
||||
const auto text = path.u8string();
|
||||
return std::string(reinterpret_cast<const char*>(text.data()), text.size());
|
||||
}
|
||||
|
||||
std::filesystem::path configuredPath() {
|
||||
#ifdef _WIN32
|
||||
const wchar_t* configured = _wgetenv(L"ANYPS5_INPUT_CONFIG");
|
||||
return configured != nullptr ? std::filesystem::path(configured) : std::filesystem::path{};
|
||||
#else
|
||||
const char* configured = std::getenv("ANYPS5_INPUT_CONFIG");
|
||||
return configured != nullptr ? pathFromUtf8(configured) : std::filesystem::path{};
|
||||
#endif
|
||||
}
|
||||
|
||||
[[noreturn]] void invalidLine(const std::filesystem::path& path, std::size_t line, const std::string& reason) {
|
||||
throw std::runtime_error("Pad: invalid input mapping " + path.string() + ":" + std::to_string(line) + ": " + reason);
|
||||
throw std::runtime_error("Pad: invalid input mapping " + pathToUtf8(path) + ":" + std::to_string(line) + ": " + reason);
|
||||
}
|
||||
|
||||
std::filesystem::path defaultConfigPath() {
|
||||
std::unique_ptr<char, decltype(&SDL_free)> basePath(SDL_GetBasePath(), SDL_free);
|
||||
if (basePath) return std::filesystem::path(basePath.get()) / "anyps5-input.ini";
|
||||
if (basePath) return pathFromUtf8(basePath.get()) / "anyps5-input.ini";
|
||||
return "anyps5-input.ini";
|
||||
}
|
||||
|
||||
@@ -244,13 +263,13 @@ std::array<std::uint8_t, 2> Pad::StickWithDeadzone(std::int16_t x, std::int16_t
|
||||
Pad::InputConfiguration Pad::LoadInputMapping() {
|
||||
InputConfiguration configuration{{InputMapping.begin(), InputMapping.end()}, {}};
|
||||
auto& bindings = configuration.bindings;
|
||||
const char* configuredPath = std::getenv("ANYPS5_INPUT_CONFIG");
|
||||
const bool explicitPath = configuredPath != nullptr && configuredPath[0] != '\0';
|
||||
const std::filesystem::path path = explicitPath ? configuredPath : defaultConfigPath();
|
||||
const auto configured = configuredPath();
|
||||
const bool explicitPath = !configured.empty();
|
||||
const std::filesystem::path path = explicitPath ? configured : defaultConfigPath();
|
||||
std::ifstream file(path);
|
||||
if (!file) {
|
||||
if (explicitPath || std::filesystem::exists(path)) {
|
||||
throw std::runtime_error("Pad: cannot read input mapping '" + path.string() + "'");
|
||||
throw std::runtime_error("Pad: cannot read input mapping '" + pathToUtf8(path) + "'");
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
@@ -301,7 +320,7 @@ Pad::InputConfiguration Pad::LoadInputMapping() {
|
||||
invalidLine(path, lineNumber, error.what());
|
||||
}
|
||||
}
|
||||
if (file.bad()) throw std::runtime_error("Pad: cannot read input mapping '" + path.string() + "'");
|
||||
if (file.bad()) throw std::runtime_error("Pad: cannot read input mapping '" + pathToUtf8(path) + "'");
|
||||
|
||||
return configuration;
|
||||
}
|
||||
@@ -105,6 +105,48 @@ void mouseLookReject() {
|
||||
require(rejects("MouseDecayMs = 20\nmousedecayms = 30\n"), "a mouse setting given twice is rejected");
|
||||
}
|
||||
|
||||
void setConfigPath(const std::filesystem::path& path) {
|
||||
#ifdef _WIN32
|
||||
require(_wputenv_s(L"ANYPS5_INPUT_CONFIG", path.wstring().c_str()) == 0, "set ANYPS5_INPUT_CONFIG");
|
||||
#else
|
||||
require(::setenv("ANYPS5_INPUT_CONFIG", path.string().c_str(), 1) == 0, "set ANYPS5_INPUT_CONFIG");
|
||||
#endif
|
||||
}
|
||||
|
||||
void nonAsciiPaths() {
|
||||
const std::u8string name = u8"anyps5-input-\u00fc-\u65e5\u672c-" + std::u8string(root.filename().u8string());
|
||||
const auto directory = root / std::filesystem::path(name);
|
||||
std::filesystem::create_directories(directory);
|
||||
const auto file = directory / "anyps5-input.ini";
|
||||
{
|
||||
std::ofstream stream(file, std::ios::binary | std::ios::trunc);
|
||||
stream << "MouseSensitivity = 3\n";
|
||||
require(stream.good(), "write the input mapping in a non-ASCII directory");
|
||||
}
|
||||
setConfigPath(file);
|
||||
require(Pad::LoadInputMapping().settings.mouseSensitivity == 3.0, "ANYPS5_INPUT_CONFIG may name a path with non-ASCII characters");
|
||||
{
|
||||
std::ofstream stream(file, std::ios::binary | std::ios::trunc);
|
||||
stream << "MouseSensitivity = fast\n";
|
||||
}
|
||||
std::string message;
|
||||
try {
|
||||
Pad::LoadInputMapping();
|
||||
} catch (const std::runtime_error& error) {
|
||||
message = error.what();
|
||||
}
|
||||
const auto utf8Name = std::string(reinterpret_cast<const char*>(name.data()), name.size());
|
||||
require(message.find(utf8Name) != std::string::npos, "an error names the path in UTF-8");
|
||||
setConfigPath(directory / "missing.ini");
|
||||
message.clear();
|
||||
try {
|
||||
Pad::LoadInputMapping();
|
||||
} catch (const std::runtime_error& error) {
|
||||
message = error.what();
|
||||
}
|
||||
require(message.find(utf8Name) != std::string::npos, "a missing file names the path in UTF-8");
|
||||
}
|
||||
|
||||
void deadzoneOffMatchesRawSticks() {
|
||||
for (const std::int16_t axis : {std::int16_t{-32768}, std::int16_t{-20000}, std::int16_t{-1}, std::int16_t{0}, std::int16_t{1}, std::int16_t{1234}, std::int16_t{32767}}) {
|
||||
const auto stick = Pad::StickWithDeadzone(axis, axis, 0);
|
||||
@@ -141,6 +183,7 @@ int main() try {
|
||||
mouseLookDefault();
|
||||
mouseLookParse();
|
||||
mouseLookReject();
|
||||
nonAsciiPaths();
|
||||
deadzoneOffMatchesRawSticks();
|
||||
deadzoneShapesSticks();
|
||||
std::filesystem::remove_all(root);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Controller, keyboard and mouse input
|
||||
|
||||
AnyPS5 accepts SDL-mapped game controllers as well as its built-in keyboard and mouse bindings. Xbox-style controllers and PlayStation controllers recognized by SDL use the standard PS button layout; sticks and analog triggers are passed through, and controllers can be connected or disconnected while the game is running. The first recognized controller is used. AnyPS5 uses its built-in keyboard and mouse bindings when no configuration file is present. To change selected keyboard and mouse bindings, create `anyps5-input.ini` beside the generated game executable. Set `ANYPS5_INPUT_CONFIG` to use a file at another path.
|
||||
AnyPS5 accepts SDL-mapped game controllers as well as its built-in keyboard and mouse bindings. Xbox-style controllers and PlayStation controllers recognized by SDL use the standard PS button layout; sticks and analog triggers are passed through, and controllers can be connected or disconnected while the game is running. The first recognized controller is used. AnyPS5 uses its built-in keyboard and mouse bindings when no configuration file is present. To change selected keyboard and mouse bindings, create `anyps5-input.ini` beside the generated game executable. Set `ANYPS5_INPUT_CONFIG` to use a file at another path. Both the game directory and that path may contain any characters, such as `ü` or `日本`.
|
||||
|
||||
For an Xbox-layout controller, A/B/X/Y map to Cross/Circle/Square/Triangle, LB/RB map to L1/R1, and LT/RT map to analog L2/R2. Start maps to Options. D-pad and stick-click buttons are supported. On a PlayStation controller, the touchpad click button is forwarded; finger touch coordinates are not implemented.
|
||||
|
||||
|
||||
Reference in new issue
Block a user