diff --git a/host/gpu/vulkan_renderer.cpp b/host/gpu/vulkan_renderer.cpp index 43aa31e..d27efd2 100644 --- a/host/gpu/vulkan_renderer.cpp +++ b/host/gpu/vulkan_renderer.cpp @@ -421,6 +421,10 @@ struct PadTuning { bool trace{false}; }; +// The SceCtrlButtons bit the game confirms with: the console's confirm +// button, as sceUtilityGetSystemParamInt reports it (0 circle, 1 cross). +std::uint32_t confirm_button_bit() { return portablekit::game().confirm_button == 1u ? 0x4000u : 0x2000u; } + // Read on every poll, so the in-game menu's changes apply at once. PadTuning pad_tuning() { static const bool trace = portablekit::env("TRACE_PAD") != nullptr; @@ -435,8 +439,7 @@ PadTuning pad_tuning() { value.right_stick_mode = player.right_stick; value.invert_x = player.invert_camera_x; value.invert_y = player.invert_camera_y; - // A PlayStation pad already carries the PSP's own face buttons, so the - // positional mapping puts confirm on circle where the prompts want it. + // Which face button presses the game's confirm button; see read_gamepad. value.confirm_south = player.confirm_south; value.trace = trace; return value; @@ -460,10 +463,15 @@ void read_gamepad(SDL_Gamepad *device, PadState &pad, int &analog_x, int &analog held(SDL_GAMEPAD_BUTTON_BACK, 0x0001u); held(SDL_GAMEPAD_BUTTON_NORTH, 0x1000u); held(SDL_GAMEPAD_BUTTON_WEST, 0x8000u); - // The game prompts "circle Enter / cross Back", and on a PlayStation pad - // those are the same two buttons in the same two places. - held(SDL_GAMEPAD_BUTTON_SOUTH, tuning.confirm_south ? 0x2000u : 0x4000u); - held(SDL_GAMEPAD_BUTTON_EAST, tuning.confirm_south ? 0x4000u : 0x2000u); + // The game confirms with the button the console's setting names (circle + // on a Japanese console, cross elsewhere) and goes back with the other. + // The player's setting says which of south and east confirms, so on a + // PlayStation pad the buttons land where the game's prompts show them + // whenever the setting matches the console. + const std::uint32_t confirm = confirm_button_bit(); + const std::uint32_t back = confirm ^ 0x6000u; + held(SDL_GAMEPAD_BUTTON_SOUTH, tuning.confirm_south ? confirm : back); + held(SDL_GAMEPAD_BUTTON_EAST, tuning.confirm_south ? back : confirm); // The PSP triggers are digital, and games hold L or R for long stretches, // so the analog triggers press the same bits as the shoulders. @@ -1686,8 +1694,9 @@ struct VulkanRenderer::Impl { gamepad_id = id; const char *name = SDL_GetGamepadName(device); const PadTuning tuning = pad_tuning(); - std::cout << "[pad] " << (name != nullptr ? name : "gamepad") << " connected; confirm on " - << (tuning.confirm_south ? "the south button" : "circle") << ", right stick " + std::cout << "[pad] " << (name != nullptr ? name : "gamepad") << " connected; confirm (" + << (confirm_button_bit() == 0x4000u ? "cross" : "circle") << ") on the " + << (tuning.confirm_south ? "south" : "east") << " button, right stick " << (tuning.right_stick_mode == settings::RightStick::DPad ? "as D-pad" : tuning.right_stick_mode == settings::RightStick::Camera ? "as camera" : "off") @@ -4762,7 +4771,7 @@ void VulkanRenderer::Impl::sample_pad(bool focused) { }(); if (auto_confirm != 0u) { const std::uint64_t phase = impl_->frames % auto_confirm; - if (phase < auto_confirm / 8u) pad.buttons |= 0x2000u; // circle + if (phase < auto_confirm / 8u) pad.buttons |= confirm_button_bit(); } // Buttons held when the game got its input back stay hidden from it until // they are released. diff --git a/host/hle/hle_system.cpp b/host/hle/hle_system.cpp index 7f2d097..b53ce8d 100644 --- a/host/hle/hle_system.cpp +++ b/host/hle/hle_system.cpp @@ -33,6 +33,13 @@ std::uint64_t guest_unix_us() { return boot_unix_us() + kernel().now_us(); } void success(Runtime &, AllegrexContext &ctx) { kernel().finish(ctx, 0u); } +// The console settings a game asks for and tells the system, under +// TRACE_KERNEL: which language and which confirm button it ends up using. +bool trace_system_params() { + static const bool enabled = portablekit::env("TRACE_KERNEL") != nullptr; + return enabled; +} + void register_utils(HleRegistrar &hle) { // The host has no data cache to keep coherent. Phantasy Star Portable 2 // Infinity (NPJH50332) uses WritebackInvalidateRange. @@ -243,7 +250,12 @@ void register_platform(HleRegistrar &hle) { log_once("npdrm-edata", "[npdrm] sceNpDrmEdataSetupKey (UNVERIFIED: no game traced yet)"); kernel().finish(ctx, 0u); }); - hle.add("sceImpose", "sceImposeSetLanguageMode", success); + hle.add("sceImpose", "sceImposeSetLanguageMode", [](Runtime &, AllegrexContext &ctx) { + if (trace_system_params()) + std::cout << "[sysparam] sceImposeSetLanguageMode language=" << arg(ctx, 0) << " button=" << arg(ctx, 1) + << "\n"; + kernel().finish(ctx, 0u); + }); // (int *language, int *button): the console's, as GetSystemParamInt says. hle.add("sceImpose", "sceImposeGetLanguageMode", [](Runtime &rt, AllegrexContext &ctx) { if (arg(ctx, 0) != 0u) rt.memory().store32(arg(ctx, 0), portablekit::game().system_language); @@ -283,6 +295,8 @@ void register_platform(HleRegistrar &hle) { case 9u: value = portablekit::game().confirm_button; break; default: break; } + if (trace_system_params()) + std::cout << "[sysparam] sceUtilityGetSystemParamInt id=" << arg(ctx, 0) << " -> " << value << "\n"; rt.memory().store32(arg(ctx, 1), value); kernel().finish(ctx, 0u); }); diff --git a/host/input/bindings.cpp b/host/input/bindings.cpp index e23565d..1d1348f 100644 --- a/host/input/bindings.cpp +++ b/host/input/bindings.cpp @@ -48,8 +48,8 @@ constexpr const char *kSeparator = " / "; constexpr ActionInfo kInfo[kActions] = { {"stick_up", "Move forward"}, {"stick_left", "Move left"}, {"stick_down", "Move back"}, - {"stick_right", "Move right"}, {"triangle", "△"}, {"circle", "○ (confirm)"}, - {"cross", "× (back)"}, {"square", "□"}, {"l", "L"}, + {"stick_right", "Move right"}, {"triangle", "△"}, {"circle", "○"}, + {"cross", "×"}, {"square", "□"}, {"l", "L"}, {"r", "R"}, {"start", "START"}, {"select", "SELECT"}, {"dpad_up", "D-pad up"}, {"dpad_left", "D-pad left"}, {"dpad_down", "D-pad down"}, {"dpad_right", "D-pad right"}, {"camera_up", "Camera up"}, {"camera_left", "Camera left"}, diff --git a/host/settings/settings.cpp b/host/settings/settings.cpp index 8f825b6..2f44519 100644 --- a/host/settings/settings.cpp +++ b/host/settings/settings.cpp @@ -461,6 +461,9 @@ std::string game_default_name() { Settings defaults_for(Platform platform) { Settings values{}; + // Confirm where a console of the game's region has it: cross, the south + // button, outside Japan; circle, the east button, in Japan. + values.confirm_south = game().confirm_button == 1u; if (platform == Platform::Android) { values.aspect = Aspect::Fill; values.fullscreen = true; diff --git a/host/settings/settings.hpp b/host/settings/settings.hpp index dc2d92b..f1b89e2 100644 --- a/host/settings/settings.hpp +++ b/host/settings/settings.hpp @@ -64,7 +64,10 @@ struct Settings { bool mute{}; // Controls - bool confirm_south{}; // confirm (circle) on the south face button + // The game's confirm button on the south face button rather than the + // east one. The default follows the console the profile describes: south + // where cross confirms, east where circle does. + bool confirm_south{}; float dead_zone{0.15f}; float trigger{0.25f}; // What LT/RT (L2/R2) press past the trigger point: 0 is L and R like the diff --git a/host/ui/input_script.cpp b/host/ui/input_script.cpp index b43d0c4..bcc230f 100644 --- a/host/ui/input_script.cpp +++ b/host/ui/input_script.cpp @@ -207,7 +207,7 @@ void sort_steps(State &s) { } // Reads the lines appended to the live file since the last call. Their frames -// count from now, so a line `30:pad a` presses ○ half a second after it is read. +// count from now, so a line `30:pad a` presses the south button half a second after it is read. void read_live(State &s) { std::ifstream file(s.live_path, std::ios::binary); if (!file) return; diff --git a/host/ui/menu.cpp b/host/ui/menu.cpp index b71f547..263be41 100644 --- a/host/ui/menu.cpp +++ b/host/ui/menu.cpp @@ -586,9 +586,12 @@ void Menu::controls() { info_row("Connected", pad == nullptr ? "No gamepad; the keyboard and mouse drive the game" : (name != nullptr ? name : "Gamepad")); } - if (choice_row("Confirm button", s.confirm_south ? "Bottom (Western)" : "Right, ○ (Japanese)", - options_for("input.confirm", "Which face button confirms, in the game and in this menu. The " - "game's prompts show ○ to confirm and × to go back."))) { + const bool cross_confirms = portablekit::game().confirm_button == 1u; + if (choice_row("Confirm button", s.confirm_south ? "Bottom (Western)" : "Right (Japanese)", + options_for("input.confirm", + std::string("Which face button confirms, in the game and in this menu. The game's " + "prompts show ") + + (cross_confirms ? "× to confirm and ○ to go back." : "○ to confirm and × to go back.")))) { s.confirm_south = !s.confirm_south; settings::save(); } @@ -788,7 +791,12 @@ void Menu::controls() { const RowOptions o{false, {}, "Press a key or a mouse button to add it, or one it has already to remove it; Esc " "cancels. A key taken from another control leaves that one."}; - if (value_row(input::info(action).label, value, o) && !binding_) { + std::string label = input::info(action).label; + // The game's confirm and back buttons, as the console says. + const bool cross_confirms = portablekit::game().confirm_button == 1u; + if (action == input::Action::Circle) label += cross_confirms ? " (back)" : " (confirm)"; + if (action == input::Action::Cross) label += cross_confirms ? " (confirm)" : " (back)"; + if (value_row(label.c_str(), value, o) && !binding_) { binding_ = action; layer.begin_binding_capture(); }