2022-05-08 04:20:52 +00:00
|
|
|
#include "Runtime/ImGuiControllerConfig.hpp"
|
|
|
|
|
2022-07-29 20:16:55 +00:00
|
|
|
#include "Runtime/RetroTypes.hpp"
|
2022-05-08 08:50:21 +00:00
|
|
|
#include "Runtime/Streams/CFileOutStream.hpp"
|
|
|
|
#include "Runtime/Streams/ContainerReaders.hpp"
|
|
|
|
#include "Runtime/Streams/ContainerWriters.hpp"
|
|
|
|
|
2022-05-08 04:20:52 +00:00
|
|
|
#include <imgui.h>
|
|
|
|
|
|
|
|
namespace metaforce {
|
2022-05-08 08:50:21 +00:00
|
|
|
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);
|
|
|
|
}
|
2022-05-08 04:20:52 +00:00
|
|
|
|
|
|
|
void ImGuiControllerConfig::show(bool& visible) {
|
2022-05-08 08:50:21 +00:00
|
|
|
|
2022-05-08 04:20:52 +00:00
|
|
|
/** TODO:
|
|
|
|
* - Implement multiple controllers
|
2022-05-08 08:50:21 +00:00
|
|
|
* - Implement setting controller ports (except for the GameCube adapter, which is hard coded)
|
2022-05-08 04:20:52 +00:00
|
|
|
* - Implement fancy graphical UI
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!visible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pendingMapping != nullptr) {
|
2022-05-08 08:50:21 +00:00
|
|
|
s32 nativeButton = PADGetNativeButtonPressed(m_pendingPort);
|
2022-05-08 04:20:52 +00:00
|
|
|
if (nativeButton != -1) {
|
|
|
|
m_pendingMapping->nativeButton = nativeButton;
|
|
|
|
m_pendingMapping = nullptr;
|
2022-05-08 08:50:21 +00:00
|
|
|
m_pendingPort = -1;
|
2022-05-08 04:20:52 +00:00
|
|
|
PADBlockInput(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-08 08:50:21 +00:00
|
|
|
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;
|
2022-05-08 04:20:52 +00:00
|
|
|
if (ImGui::Begin("Controller Config", &visible)) {
|
2022-05-08 08:50:21 +00:00
|
|
|
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();
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 08:50:21 +00:00
|
|
|
if (changed) {
|
|
|
|
if (sel > 0) {
|
|
|
|
PADSetPortForIndex(sel - 1, i);
|
|
|
|
} else if (sel == 0) {
|
|
|
|
PADClearPort(i);
|
|
|
|
}
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
2022-05-08 08:50:21 +00:00
|
|
|
ImGui::PopID();
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-08 08:50:21 +00:00
|
|
|
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;
|
2022-05-10 04:59:57 +00:00
|
|
|
PADBlockInput(false);
|
2022-05-08 08:50:21 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
PADButtonMapping* mapping = PADGetButtonMappings(i, &buttonCount);
|
|
|
|
if (mapping != nullptr) {
|
|
|
|
for (u32 m = 0; m < buttonCount; ++m) {
|
|
|
|
const char* padName = PADGetButtonName(mapping[m].padButton);
|
|
|
|
if (padName == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ImGui::PushID(padName);
|
|
|
|
bool pressed = ImGui::Button(padName);
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%s", PADGetNativeButtonName(mapping[m].nativeButton));
|
2022-05-08 04:20:52 +00:00
|
|
|
|
2022-05-08 08:50:21 +00:00
|
|
|
if (pressed && m_pendingMapping == nullptr) {
|
|
|
|
m_pendingMapping = &mapping[m];
|
|
|
|
m_pendingPort = i;
|
|
|
|
PADBlockInput(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pendingMapping == &mapping[m]) {
|
|
|
|
m_pendingValid = true;
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text(" - Waiting for button...");
|
|
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::CollapsingHeader("Dead-zones")) {
|
|
|
|
PADDeadZones* deadZones = PADGetDeadZones(i);
|
|
|
|
ImGui::Checkbox("Use Dead-zones", &deadZones->useDeadzones);
|
|
|
|
float tmp = static_cast<float>(deadZones->stickDeadZone * 100.f) / 32767.f;
|
|
|
|
if (ImGui::DragFloat("Left Stick", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
|
|
|
|
deadZones->stickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
|
|
|
|
}
|
|
|
|
tmp = static_cast<float>(deadZones->substickDeadZone * 100.f) / 32767.f;
|
|
|
|
if (ImGui::DragFloat("Right Stick", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
|
|
|
|
deadZones->substickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
|
|
|
|
}
|
|
|
|
ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers);
|
|
|
|
tmp = static_cast<float>(deadZones->leftTriggerActivationZone * 100.f) / 32767.f;
|
|
|
|
if (ImGui::DragFloat("Left Trigger Activation", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
|
|
|
|
deadZones->leftTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
|
|
|
|
}
|
|
|
|
tmp = static_cast<float>(deadZones->rightTriggerActivationZone * 100.f) / 32767.f;
|
|
|
|
if (ImGui::DragFloat("Right Trigger Activation", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
|
|
|
|
deadZones->rightTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
2022-05-08 08:50:21 +00:00
|
|
|
ImGui::EndTabBar();
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::Button("Display Editor")) {
|
|
|
|
m_editorVisible = true;
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Save Mappings")) {
|
|
|
|
PADSerializeMappings();
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Restore Defaults")) {
|
|
|
|
for (u32 i = 0; i < 4; ++i) {
|
|
|
|
PADRestoreDefaultMapping(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-08 04:26:55 +00:00
|
|
|
ImGui::End();
|
2022-05-08 04:20:52 +00:00
|
|
|
|
|
|
|
showEditor(m_editorVisible);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImGuiControllerConfig::showEditor(bool& visible) {
|
|
|
|
if (!visible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::Begin("Controller Atlas Editor", &visible)) {
|
2022-05-08 08:50:21 +00:00
|
|
|
/* 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 */
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
2022-05-08 04:26:55 +00:00
|
|
|
ImGui::End();
|
2022-05-08 04:20:52 +00:00
|
|
|
}
|
|
|
|
} // namespace metaforce
|