Initial port configuration (needs cleanup)

This commit is contained in:
Phillip Stephens 2022-05-08 01:50:21 -07:00
parent f8d9207aaa
commit 10d4bbf297
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
8 changed files with 334 additions and 91 deletions

View File

@ -89,6 +89,7 @@ set(RUNTIME_SOURCES_B
Streams/ContainerReaders.hpp Streams/ContainerReaders.hpp
Streams/CTextInStream.hpp Streams/CTextInStream.cpp Streams/CTextInStream.hpp Streams/CTextInStream.cpp
Streams/CTextOutStream.hpp Streams/CTextOutStream.cpp Streams/CTextOutStream.hpp Streams/CTextOutStream.cpp
Streams/CFileOutStream.hpp Streams/CFileOutStream.cpp
CGameAllocator.hpp CGameAllocator.cpp CGameAllocator.hpp CGameAllocator.cpp
CMemoryCardSys.hpp CMemoryCardSys.cpp CMemoryCardSys.hpp CMemoryCardSys.cpp
CScannableObjectInfo.hpp CScannableObjectInfo.cpp CScannableObjectInfo.hpp CScannableObjectInfo.cpp

View File

@ -1,15 +1,64 @@
#include "Runtime/ImGuiControllerConfig.hpp" #include "Runtime/ImGuiControllerConfig.hpp"
#include "Runtime/Streams/CFileOutStream.hpp"
#include "Runtime/Streams/ContainerReaders.hpp"
#include "Runtime/Streams/ContainerWriters.hpp"
#include "aurora/pad.hpp" #include "aurora/pad.hpp"
#include "aurora/aurora.hpp"
#include <imgui.h> #include <imgui.h>
namespace metaforce { namespace metaforce {
ImGuiControllerConfig::Button::Button(CInputStream& in)
: button(in.Get<s32>())
, uvX(in.Get<u32>())
, uvY(in.Get<u32>())
, width(in.Get<u32>())
, height(in.Get<u32>())
, offX(in.Get<float>())
, offY(in.Get<float>()) {}
void ImGuiControllerConfig::Button::PutTo(COutputStream& out) const {
out.Put(button);
out.Put(uvX);
out.Put(uvY);
out.Put(width);
out.Put(height);
out.Put(offX);
out.Put(offY);
}
ImGuiControllerConfig::ControllerAtlas::ControllerAtlas(CInputStream& in) : name(in.Get<std::string>()) {
u32 vidPidCount = in.Get<u32>();
vidPids.reserve(vidPidCount);
for (u32 i = 0; i < vidPidCount; ++i) {
u16 vid = static_cast<u16>(in.Get<s16>());
u16 pid = static_cast<u16>(in.Get<s16>());
vidPids.emplace_back(vid, pid);
}
atlasFile = in.Get<std::string>();
read_vector(buttons, in);
};
void ImGuiControllerConfig::ControllerAtlas::PutTo(COutputStream& out) const {
out.Put(name);
out.Put(static_cast<u32>(vidPids.size()));
for (const auto& vidPid : vidPids) {
out.Put(vidPid.first);
out.Put(vidPid.second);
}
write_vector(buttons, out);
}
void ImGuiControllerConfig::show(bool& visible) { void ImGuiControllerConfig::show(bool& visible) {
/** TODO: /** TODO:
* - Implement multiple controllers * - Implement multiple controllers
* - Implement setting controller ports * - Implement setting controller ports (except for the GameCube adapter, which is hard coded)
* - Implement fancy graphical UI * - Implement fancy graphical UI
*/ */
@ -18,57 +67,128 @@ void ImGuiControllerConfig::show(bool& visible) {
} }
if (m_pendingMapping != nullptr) { if (m_pendingMapping != nullptr) {
s32 nativeButton = PADGetNativeButtonPressed(0); s32 nativeButton = PADGetNativeButtonPressed(m_pendingPort);
if (nativeButton != -1) { if (nativeButton != -1) {
m_pendingMapping->nativeButton = nativeButton; m_pendingMapping->nativeButton = nativeButton;
m_pendingMapping = nullptr; m_pendingMapping = nullptr;
m_pendingPort = -1;
PADBlockInput(false); PADBlockInput(false);
} }
} }
std::vector<std::string> controllers;
controllers.push_back("None");
for (u32 i = 0; i < PADCount(); ++i) {
controllers.push_back(fmt::format(FMT_STRING("{}-{}"), PADGetNameForControllerIndex(i), i));
}
m_pendingValid = false;
if (ImGui::Begin("Controller Config", &visible)) { if (ImGui::Begin("Controller Config", &visible)) {
ImGui::Text("%s", PADGetName(0)); if (ImGui::CollapsingHeader("Ports")) {
for (u32 i = 0; i < 4; ++i) {
ImGui::PushID(fmt::format(FMT_STRING("PortConf-{}"), i).c_str());
s32 index = PADGetIndexForPort(i);
int sel = 0;
std::string name = "None";
const char* tmpName = PADGetName(i);
bool changed = false;
if (tmpName != nullptr) {
name = fmt::format(FMT_STRING("{}-{}"), tmpName, index);
}
if (ImGui::BeginCombo(fmt::format(FMT_STRING("Port {}"), i + 1).c_str(), name.c_str())) {
for (u32 j = 0; const auto& s : controllers) {
if (ImGui::Selectable(s.c_str(), name == s)) {
sel = j;
changed = true;
}
++j;
}
ImGui::EndCombo();
}
if (changed) {
if (sel > 0) {
PADSetPortForIndex(sel - 1, i);
} else if (sel == 0) {
PADClearPort(i);
}
}
ImGui::PopID();
}
}
if (ImGui::BeginTabBar("Controllers")) {
for (u32 i = 0; i < 4; ++i) {
if (ImGui::BeginTabItem(fmt::format(FMT_STRING("Port {}"), i + 1).c_str())) {
ImGui::PushID(fmt::format(FMT_STRING("Port_{}"), i + 1).c_str());
/* If the tab is changed while pending for input, cancel the pending port */
if (m_pendingMapping != nullptr && m_pendingPort != i) {
m_pendingMapping = nullptr;
m_pendingValid = false;
m_pendingPort = -1;
}
u32 vid, pid;
PADGetVidPid(i, &vid, &pid);
if (vid == 0 && pid == 0) {
ImGui::EndTabItem();
ImGui::PopID();
continue;
}
ImGui::Text("%s", PADGetName(i));
u32 buttonCount = 0; u32 buttonCount = 0;
PADButtonMapping* mapping = PADGetButtonMappings(0, &buttonCount); PADButtonMapping* mapping = PADGetButtonMappings(i, &buttonCount);
if (mapping != nullptr) { if (mapping != nullptr) {
for (u32 i = 0; i < buttonCount; ++i) { for (u32 m = 0; m < buttonCount; ++m) {
bool pressed = ImGui::Button(PADGetButtonName(mapping[i].padButton)); const char* padName = PADGetButtonName(mapping[m].padButton);
if (padName == nullptr) {
continue;
}
ImGui::PushID(padName);
bool pressed = ImGui::Button(padName);
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("%s", PADGetNativeButtonName(mapping[i].nativeButton)); ImGui::Text("%s", PADGetNativeButtonName(mapping[m].nativeButton));
if (pressed && m_pendingMapping == nullptr) { if (pressed && m_pendingMapping == nullptr) {
m_pendingMapping = &mapping[i]; m_pendingMapping = &mapping[m];
m_pendingPort = i;
PADBlockInput(true); PADBlockInput(true);
} }
if (m_pendingMapping == &mapping[i]) { if (m_pendingMapping == &mapping[m]) {
m_pendingValid = true;
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text(" - Waiting for button..."); ImGui::Text(" - Waiting for button...");
} }
ImGui::PopID();
} }
} }
if (ImGui::CollapsingHeader("Dead-zones")) { if (ImGui::CollapsingHeader("Dead-zones")) {
PADDeadZones* deadZones = PADGetDeadZones(0); PADDeadZones* deadZones = PADGetDeadZones(i);
ImGui::Checkbox("Use Dead-zones", &deadZones->useDeadzones); ImGui::Checkbox("Use Dead-zones", &deadZones->useDeadzones);
s32 tmp = deadZones->stickDeadZone; float tmp = static_cast<float>(deadZones->stickDeadZone * 100.f) / 32767.f;
if (ImGui::DragInt("Left Stick", &tmp)) { if (ImGui::DragFloat("Left Stick", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->stickDeadZone = tmp; deadZones->stickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
} }
tmp = deadZones->substickDeadZone; tmp = static_cast<float>(deadZones->substickDeadZone * 100.f) / 32767.f;
if (ImGui::DragInt("Right Stick", &tmp)) { if (ImGui::DragFloat("Right Stick", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->substickDeadZone = tmp; deadZones->substickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
} }
ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers); ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers);
tmp = deadZones->leftTriggerActivationZone; tmp = static_cast<float>(deadZones->leftTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragInt("Left Trigger Activation", &tmp)) { if (ImGui::DragFloat("Left Trigger Activation", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->leftTriggerActivationZone = tmp; deadZones->leftTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
} }
tmp = deadZones->rightTriggerActivationZone; tmp = static_cast<float>(deadZones->rightTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragInt("Right Trigger Activation", &tmp)) { if (ImGui::DragFloat("Right Trigger Activation", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->rightTriggerActivationZone = tmp; deadZones->rightTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
} }
} }
ImGui::PopID();
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::Button("Display Editor")) { if (ImGui::Button("Display Editor")) {
@ -96,6 +216,23 @@ void ImGuiControllerConfig::showEditor(bool& visible) {
} }
if (ImGui::Begin("Controller Atlas Editor", &visible)) { if (ImGui::Begin("Controller Atlas Editor", &visible)) {
/* TODO: Atlas editor */
ImGui::Separator();
if (ImGui::Button("Save Controller Database")) {
CFileOutStream out("ControllerAtlases.ctrdb");
out.WriteUint32(SLITTLE('CTDB'));
out.WriteUint32(1); // Version
write_vector(m_controllerAtlases, out);
}
ImGui::SameLine();
if (ImGui::Button("Export") && m_currentAtlas != nullptr) {
CFileOutStream out("test.ctratlas");
out.Put(SLITTLE('CTRA'));
out.Put(1); // Version
out.Put(*m_currentAtlas);
}
/* TODO: Import logic */
} }
ImGui::End(); ImGui::End();
} }

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "Runtime/GCNTypes.hpp" #include "Runtime/GCNTypes.hpp"
#include "Runtime/Streams/CInputStream.hpp"
#include "Runtime/Streams/COutputStream.hpp"
#include "aurora/pad.hpp" #include "aurora/pad.hpp"
#include <array> #include <array>
@ -11,20 +13,28 @@
namespace metaforce { namespace metaforce {
class ImGuiControllerConfig { class ImGuiControllerConfig {
struct Button { struct Button {
u32 button; // the SDL button this entry corresponds to s32 button = -1; // the SDL button this entry corresponds to
u32 uvX; // Offset if icon image in atlas from left (in pixels) u32 uvX = 0; // Offset if icon image in atlas from left (in pixels)
u32 uvY; // Offset if icon image in atlas from top (in pixels) u32 uvY = 0; // Offset if icon image in atlas from top (in pixels)
u32 width; // Width of button image (in pixels) u32 width = 32; // Width of button image (in pixels)
u32 height; // Height of button image (in pixels) u32 height = 32; // Height of button image (in pixels)
float offX; // Offset from left of config window float offX = 0.f; // Offset from left of config window
float offY; // Offset from top of config window float offY = 0.f; // Offset from top of config window
Button() = default;
explicit Button(CInputStream& in);
void PutTo(COutputStream& in) const;
}; };
struct ControllerMapping { struct ControllerAtlas {
std::string name; std::string name;
std::pair<u32, u32> vidPid; std::vector<std::pair<u16, u16>> vidPids;
std::string atlasFile; // Path to atlas relative to controller definition std::string atlasFile; // Path to atlas relative to controller definition
std::vector<Button> buttons; std::vector<Button> buttons;
ControllerAtlas() = default;
explicit ControllerAtlas(CInputStream& in);
void PutTo(COutputStream& out) const;
}; };
public: public:
@ -34,9 +44,12 @@ private:
void showEditor(bool& visible); void showEditor(bool& visible);
PADButtonMapping* m_pendingMapping = nullptr; PADButtonMapping* m_pendingMapping = nullptr;
s32 m_pendingPort = 0;
bool m_pendingValid = false;
bool m_editorVisible = false; bool m_editorVisible = false;
std::array<ControllerMapping*, 4> m_controllers;
std::vector<ControllerMapping> m_mappings; ControllerAtlas* m_currentAtlas = nullptr;
std::vector<ControllerAtlas> m_controllerAtlases;
}; };
} // namespace metaforce } // namespace metaforce

View File

@ -30,8 +30,11 @@ void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
if (i == 0) { if (i == 0) {
firstController = true; firstController = true;
} }
m_lastInput = CFinalInput(i, dt, cont, xc_leftDiv, x10_rightDiv); auto tmp = CFinalInput(i, dt, cont, xc_leftDiv, x10_rightDiv);
queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, m_lastInput)); if (i == 0) {
m_lastInput = tmp;
}
queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, tmp));
++availSlot; ++availSlot;
} }

View File

@ -0,0 +1,21 @@
#include "Runtime/Streams/CFileOutStream.hpp"
namespace metaforce {
CFileOutStream::CFileOutStream(std::string_view name, u32 blockLen) : COutputStream(blockLen) {
m_file = fopen(name.data(), "wbe");
}
CFileOutStream::~CFileOutStream() {
Flush();
if (m_file) {
fclose(m_file);
}
}
void CFileOutStream::Write(const u8* ptr, u32 len) {
if (!m_file) {
return;
}
fwrite(ptr, 1, len, m_file);
}
} // namespace metaforce

View File

@ -0,0 +1,16 @@
#pragma once
#include "Runtime/Streams/COutputStream.hpp"
#include <cstdio>
namespace metaforce {
class CFileOutStream final : public COutputStream {
FILE* m_file;
public:
explicit CFileOutStream(std::string_view name, u32 blockLen = 4096);
virtual ~CFileOutStream();
protected:
void Write(const u8* ptr, u32 len);
};
} // namespace metaforce

View File

@ -97,9 +97,15 @@ struct PADButtonMapping {
PAD::BUTTON padButton; PAD::BUTTON padButton;
}; };
s32 PADGetCount(); /* Returns the total number of controllers */
void PADGetVidPid(u32 idx, u32* vid, u32* pid); u32 PADCount();
const char* PADGetName(u32 idx); /* Returns the controller name for the given index into the controller map */
const char* PADGetNameForControllerIndex(u32 idx);
void PADSetPortForIndex(u32 index, s32 port);
s32 PADGetIndexForPort(u32 port);
void PADGetVidPid(u32 port, u32* vid, u32* pid);
void PADClearPort(u32 port);
const char* PADGetName(u32 port);
void PADSetButtonMapping(u32 port, PADButtonMapping mapping); void PADSetButtonMapping(u32 port, PADButtonMapping mapping);
void PADSetAllButtonMappings(u32 port, PADButtonMapping buttons[12]); void PADSetAllButtonMappings(u32 port, PADButtonMapping buttons[12]);
PADButtonMapping* PADGetButtonMappings(u32 port, u32* buttonCount); PADButtonMapping* PADGetButtonMappings(u32 port, u32* buttonCount);

View File

@ -22,7 +22,7 @@ struct GameController {
PADDeadZones m_deadZones; PADDeadZones m_deadZones;
u16 m_vid = 0; u16 m_vid = 0;
u16 m_pid = 0; u16 m_pid = 0;
std::array<PADButtonMapping, 12> m_mapping; std::array<PADButtonMapping, 12> m_mapping{};
bool m_mappingLoaded = false; bool m_mappingLoaded = false;
constexpr bool operator==(const GameController& other) const { constexpr bool operator==(const GameController& other) const {
return m_controller == other.m_controller && m_index == other.m_index; return m_controller == other.m_controller && m_index == other.m_index;
@ -37,20 +37,24 @@ GameController* get_controller_for_player(u32 player) noexcept {
} }
} }
#if 0
/* If we don't have a controller assigned to this port use the first unassigned controller */ /* If we don't have a controller assigned to this port use the first unassigned controller */
if (!g_GameControllers.empty()) { if (!g_GameControllers.empty()) {
int32_t availIndex = -1; int32_t availIndex = -1;
for (const auto& controller : g_GameControllers) { GameController* ct = nullptr;
if (player_index(controller.second.m_index) == -1) { for (auto& controller : g_GameControllers) {
availIndex = controller.second.m_index; if (player_index(controller.first) == -1) {
availIndex = controller.first;
ct = &controller.second;
break; break;
} }
} }
if (availIndex != -1) { if (availIndex != -1) {
set_player_index(g_GameControllers.begin()->second.m_index, player); set_player_index(availIndex, player);
return get_controller_for_player(player); return ct;
} }
} }
#endif
return nullptr; return nullptr;
} }
@ -389,6 +393,71 @@ static const std::array<PADButtonMapping, 12> mDefaultButtons{{
void PADSetSpec(s32 spec) {} void PADSetSpec(s32 spec) {}
void PADInit() {} void PADInit() {}
aurora::input::GameController* __PADGetControllerForIndex(u32 idx) {
if (idx >= aurora::input::g_GameControllers.size()) {
return nullptr;
}
u32 tmp = 0;
auto iter = aurora::input::g_GameControllers.begin();
while (tmp < idx) {
++iter;
++tmp;
}
if (iter == aurora::input::g_GameControllers.end()) {
return nullptr;
}
return &iter->second;
}
u32 PADCount() { return aurora::input::g_GameControllers.size(); }
const char* PADGetNameForControllerIndex(u32 idx) {
auto* ctrl = __PADGetControllerForIndex(idx);
if (ctrl == nullptr) {
return nullptr;
}
return SDL_GameControllerName(ctrl->m_controller);
}
void PADSetPortForIndex(u32 idx, s32 port) {
auto* ctrl = __PADGetControllerForIndex(idx);
auto* dest = aurora::input::get_controller_for_player(port);
if (ctrl == nullptr) {
return;
}
if (dest != nullptr) {
SDL_GameControllerSetPlayerIndex(dest->m_controller, -1);
}
SDL_GameControllerSetPlayerIndex(ctrl->m_controller, port);
}
s32 PADGetIndexForPort(u32 port) {
auto* ctrl = aurora::input::get_controller_for_player(port);
if (ctrl == nullptr) {
return -1;
}
s32 index = 0;
for (auto iter = aurora::input::g_GameControllers.begin(); iter != aurora::input::g_GameControllers.end();
++iter, ++index) {
if (&iter->second == ctrl) {
break;
}
}
return index;
}
void PADClearPort(u32 port) {
auto* ctrl = aurora::input::get_controller_for_player(port);
if (ctrl == nullptr) {
return;
}
SDL_GameControllerSetPlayerIndex(ctrl->m_controller, -1);
}
void __PADLoadMapping(aurora::input::GameController* controller) { void __PADLoadMapping(aurora::input::GameController* controller) {
s32 playerIndex = SDL_GameControllerGetPlayerIndex(controller->m_controller); s32 playerIndex = SDL_GameControllerGetPlayerIndex(controller->m_controller);
if (playerIndex == -1) { if (playerIndex == -1) {
@ -402,11 +471,9 @@ void __PADLoadMapping(aurora::input::GameController* controller) {
controller->m_mappingLoaded = true; controller->m_mappingLoaded = true;
FILE* file = auto path = fmt::format(FMT_STRING("{}/{}_{:04X}_{:04X}.controller"), basePath, PADGetName(playerIndex),
fopen(fmt::format(FMT_STRING("{}/{}_{:04X}_{:04X}.controller"), basePath, controller->m_vid, controller->m_pid);
aurora::input::controller_name(controller->m_index), controller->m_vid, controller->m_pid) FILE* file = fopen(path.c_str(), "rbe");
.c_str(),
"rbe");
if (file == nullptr) { if (file == nullptr) {
return; return;
} }
@ -434,7 +501,7 @@ void __PADLoadMapping(aurora::input::GameController* controller) {
} }
fread(&controller->m_deadZones, 1, sizeof(PADDeadZones), file); fread(&controller->m_deadZones, 1, sizeof(PADDeadZones), file);
fread(&controller->m_mapping, 1, sizeof(PADButtonMapping), file); fread(&controller->m_mapping, 1, sizeof(PADButtonMapping) * controller->m_mapping.size(), file);
fclose(file); fclose(file);
} }
@ -455,9 +522,6 @@ u32 PADRead(PAD::Status* status) {
if (!controller->m_mappingLoaded) { if (!controller->m_mappingLoaded) {
__PADLoadMapping(controller); __PADLoadMapping(controller);
#ifndef NDEBUG
PADSerializeMappings();
#endif
} }
status[i].xa_err = PAD::ERR_NONE; status[i].xa_err = PAD::ERR_NONE;
std::for_each(controller->m_mapping.begin(), controller->m_mapping.end(), std::for_each(controller->m_mapping.begin(), controller->m_mapping.end(),
@ -735,26 +799,10 @@ void PADClampCircle(PAD::Status* status) {
} }
} }
s32 PADGetCount() { return aurora::input::g_GameControllers.size(); } void PADGetVidPid(u32 port, u32* vid, u32* pid) {
aurora::input::GameController* __PADGetController(s32 idx) {
auto iter = aurora::input::g_GameControllers.begin();
s32 i = 0;
for (; aurora::input::g_GameControllers.begin() != aurora::input::g_GameControllers.end() && i < idx; ++iter, ++i) {}
if (iter == aurora::input::g_GameControllers.end()) {
return nullptr;
}
return &iter->second;
}
void PADGetVidPid(u32 idx, u32* vid, u32* pid) {
*vid = 0; *vid = 0;
*pid = 0; *pid = 0;
if (idx < 0 || idx >= aurora::input::g_GameControllers.size()) { auto* controller = aurora::input::get_controller_for_player(port);
return;
}
auto* controller = __PADGetController(idx);
if (controller == nullptr) { if (controller == nullptr) {
return; return;
} }
@ -763,8 +811,8 @@ void PADGetVidPid(u32 idx, u32* vid, u32* pid) {
*pid = controller->m_pid; *pid = controller->m_pid;
} }
const char* PADGetName(u32 idx) { const char* PADGetName(u32 port) {
auto* controller = __PADGetController(idx); auto* controller = aurora::input::get_controller_for_player(port);
if (controller == nullptr) { if (controller == nullptr) {
return nullptr; return nullptr;
} }
@ -916,6 +964,4 @@ void PADRestoreDefaultMapping(u32 port) {
controller->m_mapping = mDefaultButtons; controller->m_mapping = mDefaultButtons;
} }
void PADBlockInput(bool block) { void PADBlockInput(bool block) { gBlockPAD = block; }
gBlockPAD = block;
}