mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
overlay: redesign Cards tab (#593)
* add `-card0` `-card1` options at the top of Cards tab to clearly indicate that they take precedence over card files * reorganize UI / clean up * remove ability to directly edit the contents of `card0.txt` / `card1.txt` as that wasn't very intuitive; instead, encourage users to use overrides directly or use an external text editor (`Edit` button opens notepad)
This commit is contained in:
@@ -58,6 +58,7 @@ namespace overlay::windows {
|
|||||||
const auto PROJECT_URL = "https://spice2x.github.io";
|
const auto PROJECT_URL = "https://spice2x.github.io";
|
||||||
|
|
||||||
constexpr ImVec4 TEXT_COLOR_GREEN(0.f, 1.f, 0.f, 1.f);
|
constexpr ImVec4 TEXT_COLOR_GREEN(0.f, 1.f, 0.f, 1.f);
|
||||||
|
constexpr uint32_t OPTION_INPUT_TEXT_WIDTH = 512;
|
||||||
|
|
||||||
std::unique_ptr<AsioDriverList> asio_driver_list;
|
std::unique_ptr<AsioDriverList> asio_driver_list;
|
||||||
|
|
||||||
@@ -202,25 +203,6 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::write_card(int player) {
|
|
||||||
|
|
||||||
// get path
|
|
||||||
auto bindings = ::Config::getInstance().getKeypadBindings(this->games_selected_name);
|
|
||||||
std::filesystem::path path;
|
|
||||||
if (!bindings.card_paths[player].empty()) {
|
|
||||||
path = bindings.card_paths[player];
|
|
||||||
} else {
|
|
||||||
path = player > 0 ? "card1.txt" : "card0.txt";
|
|
||||||
}
|
|
||||||
|
|
||||||
// write file
|
|
||||||
std::ofstream f(path);
|
|
||||||
if (f) {
|
|
||||||
f.write(this->keypads_card_number[player], strlen(this->keypads_card_number[player]));
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::build_content() {
|
void Config::build_content() {
|
||||||
|
|
||||||
// if standalone then fullscreen window
|
// if standalone then fullscreen window
|
||||||
@@ -3694,14 +3676,121 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void Config::build_cards() {
|
void Config::build_cards() {
|
||||||
|
|
||||||
|
constexpr float TEXT_INPUT_WIDTH = 240.f;
|
||||||
|
|
||||||
// early quit
|
// early quit
|
||||||
if (this->games_selected < 0 || this->games_selected_name.empty()) {
|
if (this->games_selected < 0 || this->games_selected_name.empty()) {
|
||||||
ImGui::Text("Please select a game first.");
|
ImGui::Text("Please select a game first.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Card overrides");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::SeparatorText("Card files (card0.txt / card1.txt)");
|
ImGui::TextUnformatted(
|
||||||
|
"Specify hardcoded card numbers here. This will always take priority when Insert Card is pressed.");
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// read in values from options
|
||||||
|
auto options = games::get_options(this->games_selected_name);
|
||||||
|
for (int player = 0; player < 2; player++) {
|
||||||
|
ImGui::PushID(("OverrideCardP" + to_string(player)).c_str());
|
||||||
|
|
||||||
|
// intentionally using the same buffer length as options tab
|
||||||
|
char buffer[OPTION_INPUT_TEXT_WIDTH];
|
||||||
|
bool card_changed = false;
|
||||||
|
|
||||||
|
// read in values from options
|
||||||
|
auto &option = (player == 0)
|
||||||
|
? options->at(launcher::Options::Player1Card)
|
||||||
|
: options->at(launcher::Options::Player2Card);
|
||||||
|
if (option.is_active()) {
|
||||||
|
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||||
|
} else {
|
||||||
|
buffer[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool tree_open = ImGui::TreeNodeEx("##OverrideCardTree", ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
if (this->keypads_card_override_valid[player]) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, TEXT_COLOR_GREEN);
|
||||||
|
}
|
||||||
|
ImGui::SameLine(0.0f, 0.0f);
|
||||||
|
ImGui::Text("Player %i (-card%i)", player + 1, player);
|
||||||
|
if (this->keypads_card_override_valid[player]) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree_open) {
|
||||||
|
// card number box
|
||||||
|
ImGui::PushStyleColor(
|
||||||
|
ImGuiCol_Text,
|
||||||
|
this->keypads_card_override_valid[player] ? ImVec4(1.f, 1.f, 1.f, 1.f) :
|
||||||
|
ImVec4(1.f, 0.f, 0.f, 1.f));
|
||||||
|
ImGui::SetNextItemWidth(TEXT_INPUT_WIDTH);
|
||||||
|
ImGui::InputTextWithHint("##OverrideCard", "E004010000000000",
|
||||||
|
buffer, sizeof(buffer) - 1,
|
||||||
|
ImGuiInputTextFlags_CharsUppercase |
|
||||||
|
ImGuiInputTextFlags_CharsHexadecimal |
|
||||||
|
ImGuiInputTextFlags_EscapeClearsAll);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
card_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if valid on every frame;
|
||||||
|
// do this after the widget and remember the result for next frame
|
||||||
|
this->keypads_card_override_valid[player] = false;
|
||||||
|
const auto buffer_len = strlen(buffer);
|
||||||
|
if (buffer_len == 16) {
|
||||||
|
this->keypads_card_override_valid[player] = validate_ea_card(buffer);
|
||||||
|
} else if (buffer_len == 0) {
|
||||||
|
this->keypads_card_override_valid[player] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate button
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Generate")) {
|
||||||
|
generate_ea_card(buffer);
|
||||||
|
card_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.is_active()) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Clear")) {
|
||||||
|
buffer[0] = '\0';
|
||||||
|
card_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad card number warning
|
||||||
|
if (!this->keypads_card_override_valid[player] && buffer_len > 0) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Invalid card number");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player == 0) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Spacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
if (card_changed) {
|
||||||
|
this->options_dirty = true;
|
||||||
|
option.value = buffer;
|
||||||
|
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Card from text files");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextUnformatted(
|
||||||
|
"Use text files on disk; its content will be read when Insert Card is pressed.");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// get bindings and copy paths
|
// get bindings and copy paths
|
||||||
@@ -3718,133 +3807,148 @@ namespace overlay::windows {
|
|||||||
// card settings for each player
|
// card settings for each player
|
||||||
for (int player = 0; player < 2; player++) {
|
for (int player = 0; player < 2; player++) {
|
||||||
|
|
||||||
|
// check overrides first
|
||||||
|
const auto &override = (player == 0)
|
||||||
|
? options->at(launcher::Options::Player1Card)
|
||||||
|
: options->at(launcher::Options::Player2Card);
|
||||||
|
|
||||||
// custom ID and title
|
// custom ID and title
|
||||||
ImGui::PushID(("KeypadP" + to_string(player)).c_str());
|
ImGui::PushID(("KeypadP" + to_string(player)).c_str());
|
||||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Player %i", player + 1);
|
const bool tree_open = ImGui::TreeNodeEx("##KeypadTreeNode", ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
const bool card_content_valid =
|
||||||
// card path
|
!override.is_active() &&
|
||||||
std::string hint = "card" + to_string(player) + ".txt";
|
this->keypads_card_file_contents_valid[player] &&
|
||||||
if (ImGui::InputTextWithHint("Card Path", hint.c_str(),
|
this->keypads_card_number[player][0] != 0;
|
||||||
this->keypads_card_path[player], sizeof(this->keypads_card_path[0]) - 1))
|
if (card_content_valid) {
|
||||||
{
|
ImGui::PushStyleColor(ImGuiCol_Text, TEXT_COLOR_GREEN);
|
||||||
bindings.card_paths[player] = this->keypads_card_path[player];
|
}
|
||||||
bindings_updated = true;
|
ImGui::SameLine(0.0f, 0.0f);
|
||||||
|
ImGui::Text("Player %i (card%i.txt)", player + 1, player);
|
||||||
|
if (card_content_valid) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
// help marker
|
if (tree_open) {
|
||||||
ImGui::SameLine();
|
if (override.is_active()) {
|
||||||
ImGui::HelpMarker("Leave this empty to use the card file in your game directory.\n"
|
ImGui::TextDisabled("Disabled - override is in use.");
|
||||||
"Hint: You can place 'card0.txt' (P1) / 'card1.txt' (P2) into the root of your USB "
|
} else {
|
||||||
"flash drive and it will trigger a card insert when you connect it!");
|
// card path
|
||||||
|
std::string hint = "using .\\card" + to_string(player) + ".txt (default)";
|
||||||
|
ImGui::SetNextItemWidth(TEXT_INPUT_WIDTH);
|
||||||
|
if (ImGui::InputTextWithHint("File Path", hint.c_str(),
|
||||||
|
this->keypads_card_path[player], sizeof(this->keypads_card_path[0]) - 1))
|
||||||
|
{
|
||||||
|
bindings.card_paths[player] = this->keypads_card_path[player];
|
||||||
|
bindings_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
// card path file selector
|
// help marker
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Open...")) {
|
ImGui::HelpMarker("Leave this empty to use the card file in your game directory.\n"
|
||||||
|
"Hint: You can place 'card0.txt' (P1) / 'card1.txt' (P2) into the root of your USB "
|
||||||
|
"flash drive and it will trigger a card insert when you connect it!");
|
||||||
|
|
||||||
// standalone version opens native file browser
|
// card path file selector
|
||||||
if (cfg::CONFIGURATOR_STANDALONE && !keypads_card_select) {
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Open...")) {
|
||||||
|
|
||||||
// run in separate thread otherwise we get a crash
|
// standalone version opens native file browser
|
||||||
keypads_card_select_done = false;
|
if (cfg::CONFIGURATOR_STANDALONE && !keypads_card_select) {
|
||||||
keypads_card_select = new std::thread([this, bindings, player, game] {
|
|
||||||
|
|
||||||
// open dialog to get path
|
// run in separate thread otherwise we get a crash
|
||||||
auto ofn_path = std::make_unique<wchar_t[]>(512);
|
keypads_card_select_done = false;
|
||||||
ofn_path[0] = L'\0';
|
keypads_card_select = new std::thread([this, bindings, player, game] {
|
||||||
OPENFILENAMEW ofn {};
|
|
||||||
ofn.lStructSize = sizeof(ofn);
|
|
||||||
ofn.lpstrFile = ofn_path.get();
|
|
||||||
ofn.nMaxFile = 512;
|
|
||||||
ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR;
|
|
||||||
ofn.lpstrDefExt = L"txt";
|
|
||||||
ofn.lpstrInitialDir = L".";
|
|
||||||
|
|
||||||
// check for success
|
// open dialog to get path
|
||||||
auto guard = rawinput::set_os_window_focus_guard();
|
auto ofn_path = std::make_unique<wchar_t[]>(512);
|
||||||
if (GetSaveFileNameW(&ofn)) {
|
ofn_path[0] = L'\0';
|
||||||
|
OPENFILENAMEW ofn {};
|
||||||
|
ofn.lStructSize = sizeof(ofn);
|
||||||
|
ofn.lpstrFile = ofn_path.get();
|
||||||
|
ofn.nMaxFile = 512;
|
||||||
|
ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR;
|
||||||
|
ofn.lpstrDefExt = L"txt";
|
||||||
|
ofn.lpstrInitialDir = L".";
|
||||||
|
|
||||||
// update card path
|
// check for success
|
||||||
auto new_bindings = bindings;
|
auto guard = rawinput::set_os_window_focus_guard();
|
||||||
new_bindings.card_paths[player] = std::filesystem::path(ofn_path.get());
|
if (GetSaveFileNameW(&ofn)) {
|
||||||
::Config::getInstance().updateBinding(game, new_bindings);
|
|
||||||
eamuse_update_keypad_bindings();
|
|
||||||
|
|
||||||
read_card(player);
|
// update card path
|
||||||
} else {
|
auto new_bindings = bindings;
|
||||||
auto error = CommDlgExtendedError();
|
new_bindings.card_paths[player] = std::filesystem::path(ofn_path.get());
|
||||||
if (error) {
|
::Config::getInstance().updateBinding(game, new_bindings);
|
||||||
log_warning("cfg", "failed to get save file name: {}", error);
|
eamuse_update_keypad_bindings();
|
||||||
} else {
|
|
||||||
log_warning("cfg", "failed to get save file name");
|
read_card(player);
|
||||||
}
|
} else {
|
||||||
|
auto error = CommDlgExtendedError();
|
||||||
|
if (error) {
|
||||||
|
log_warning("cfg", "failed to get save file name: {}", error);
|
||||||
|
} else {
|
||||||
|
log_warning("cfg", "failed to get save file name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
keypads_card_select_done = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up
|
// in-game version opens ImGui file browser
|
||||||
keypads_card_select_done = true;
|
if (!cfg::CONFIGURATOR_STANDALONE && !this->keypads_card_select_browser[player].IsOpened()) {
|
||||||
});
|
this->keypads_card_select_browser[player].SetTitle("Card Select");
|
||||||
}
|
this->keypads_card_select_browser[player].SetTypeFilters({".txt", "*"});
|
||||||
|
// this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
||||||
// in-game version opens ImGui file browser
|
this->keypads_card_select_browser[player].Open();
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE && !this->keypads_card_select_browser[player].IsOpened()) {
|
}
|
||||||
this->keypads_card_select_browser[player].SetTitle("Card Select");
|
|
||||||
this->keypads_card_select_browser[player].SetTypeFilters({".txt", "*"});
|
|
||||||
// this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
|
||||||
this->keypads_card_select_browser[player].Open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear button
|
|
||||||
if (!bindings.card_paths[player].empty()) {
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Clear")) {
|
|
||||||
bindings.card_paths[player] = "";
|
|
||||||
bindings_updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify card number
|
|
||||||
auto card_valid = true;
|
|
||||||
if (this->keypads_card_number[player][0] != 0) {
|
|
||||||
for (int n = 0; n < 16; n++) {
|
|
||||||
char c = this->keypads_card_number[player][n];
|
|
||||||
bool digit = c >= '0' && c <= '9';
|
|
||||||
bool character_big = c >= 'A' && c <= 'F';
|
|
||||||
bool character_small = c >= 'a' && c <= 'f';
|
|
||||||
if (!digit && !character_big && !character_small) {
|
|
||||||
card_valid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Edit")) {
|
||||||
|
std::filesystem::path path;
|
||||||
|
if (!bindings.card_paths[player].empty()) {
|
||||||
|
path = bindings.card_paths[player];
|
||||||
|
} else {
|
||||||
|
path = (player > 0) ? "card1.txt" : "card0.txt";
|
||||||
|
}
|
||||||
|
// using notepad here in case the file doesn't exist
|
||||||
|
launch_shell("notepad.exe", path.string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear button
|
||||||
|
if (!bindings.card_paths[player].empty()) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Clear")) {
|
||||||
|
bindings.card_paths[player] = "";
|
||||||
|
bindings_updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify card number
|
||||||
|
this->keypads_card_file_contents_valid[player] = true;
|
||||||
|
if (this->keypads_card_number[player][0] != 0) {
|
||||||
|
this->keypads_card_file_contents_valid[player] =
|
||||||
|
validate_ea_card(this->keypads_card_number[player]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// card number box
|
||||||
|
ImGui::PushStyleColor(
|
||||||
|
ImGuiCol_Text,
|
||||||
|
this->keypads_card_file_contents_valid[player] ? ImVec4(1.f, 1.f, 1.f, 1.f) :
|
||||||
|
ImVec4(1.f, 0.f, 0.f, 1.f));
|
||||||
|
ImGui::SetNextItemWidth(TEXT_INPUT_WIDTH);
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
if (this->keypads_card_number[player][0] != 0) {
|
||||||
|
ImGui::Text("Card from file: %s", this->keypads_card_number[player]);
|
||||||
|
} else {
|
||||||
|
ImGui::TextUnformatted("Failed to read file.");
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// card number box
|
ImGui::TreePop();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
|
||||||
card_valid ? ImVec4(1.f, 1.f, 1.f, 1.f) :
|
|
||||||
ImVec4(1.f, 0.f, 0.f, 1.f));
|
|
||||||
ImGui::InputTextWithHint("Card Number", "E004010000000000",
|
|
||||||
this->keypads_card_number[player], sizeof(this->keypads_card_number[0]) - 1,
|
|
||||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsHexadecimal);
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
// write card after edit
|
|
||||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
||||||
write_card(player);
|
|
||||||
read_card(1 - player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// help marker
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::HelpMarker(
|
|
||||||
"Click on Generate button to randomize a valid card number and automatically it save to specified file.");
|
|
||||||
|
|
||||||
// generate button
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Generate")) {
|
|
||||||
// don't know why this file insists on using 18 chars to store the card ID
|
|
||||||
char new_card[17];
|
|
||||||
generate_ea_card(new_card);
|
|
||||||
strcpy_s(this->keypads_card_number[player], new_card);
|
|
||||||
write_card(player);
|
|
||||||
read_card(1 - player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// render card select browser
|
// render card select browser
|
||||||
@@ -3881,12 +3985,14 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::SeparatorText("NFC card reader status");
|
ImGui::Separator();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "NFC card reader status");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::TextWrapped("Test NFC card readers and card insertions over API");
|
ImGui::TextWrapped("Test NFC card readers and card insertions over API.");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker(
|
ImGui::HelpMarker(
|
||||||
"Enable card readers in Advanced tab, under Card Readers section, and restart. "
|
"Enable card readers in Advanced tab, under Card Readers section, and restart. "
|
||||||
@@ -3897,31 +4003,35 @@ namespace overlay::windows {
|
|||||||
// show scanned card numbers
|
// show scanned card numbers
|
||||||
for (int player = 0; player < 2; player++) {
|
for (int player = 0; player < 2; player++) {
|
||||||
ImGui::PushID(("CardReaderDisp" + to_string(player)).c_str());
|
ImGui::PushID(("CardReaderDisp" + to_string(player)).c_str());
|
||||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Last card detected for player %i", player + 1);
|
if (ImGui::TreeNodeEx(
|
||||||
|
fmt::format("Last card detected for player {}", player + 1).c_str(),
|
||||||
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
|
||||||
char card_uid[8];
|
char card_uid[8];
|
||||||
const bool card_present = eamuse_scanned_card_peek_noninvasive(player, card_uid);
|
const bool card_present = eamuse_scanned_card_peek_noninvasive(player, card_uid);
|
||||||
if (card_present) {
|
if (card_present) {
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
const auto card_str = bin2hex(card_uid, 8);
|
const auto card_str = bin2hex(card_uid, 8);
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
"%s %s %s %s",
|
"%s %s %s %s",
|
||||||
card_str.substr(0, 4).c_str(),
|
card_str.substr(0, 4).c_str(),
|
||||||
card_str.substr(4, 4).c_str(),
|
card_str.substr(4, 4).c_str(),
|
||||||
card_str.substr(8, 4).c_str(),
|
card_str.substr(8, 4).c_str(),
|
||||||
card_str.substr(12, 4).c_str()
|
card_str.substr(12, 4).c_str()
|
||||||
);
|
);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Copy")) {
|
if (ImGui::Button("Copy")) {
|
||||||
clipboard::copy_text(card_str);
|
clipboard::copy_text(card_str);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Clear")) {
|
||||||
|
eamuse_scanned_card_clear(player);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::TextDisabled("Card not present");
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::TreePop();
|
||||||
if (ImGui::Button("Clear")) {
|
|
||||||
eamuse_scanned_card_clear(player);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
ImGui::TextDisabled("Card not present");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
@@ -3938,7 +4048,9 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::SeparatorText("More tips");
|
ImGui::Separator();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "More tips");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
ImGui::TextWrapped("To debug card reader issues, run spice.exe -cfg in command line and check the log.");
|
ImGui::TextWrapped("To debug card reader issues, run spice.exe -cfg in command line and check the log.");
|
||||||
@@ -3948,6 +4060,19 @@ namespace overlay::windows {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Config::validate_ea_card(char card_number[16]) {
|
||||||
|
for (int n = 0; n < 16; n++) {
|
||||||
|
char c = card_number[n];
|
||||||
|
bool digit = c >= '0' && c <= '9';
|
||||||
|
bool character_big = c >= 'A' && c <= 'F';
|
||||||
|
bool character_small = c >= 'a' && c <= 'f';
|
||||||
|
if (!digit && !character_big && !character_small) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Config::build_option_value_picker_title(const OptionDefinition& definition) {
|
std::string Config::build_option_value_picker_title(const OptionDefinition& definition) {
|
||||||
// need to make these all unique since they are also used as ID
|
// need to make these all unique since they are also used as ID
|
||||||
// if not unique, append ## per ImGui rules to create unique ones
|
// if not unique, append ## per ImGui rules to create unique ones
|
||||||
@@ -4404,7 +4529,7 @@ namespace overlay::windows {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OptionType::Text: {
|
case OptionType::Text: {
|
||||||
char buffer[512];
|
char buffer[OPTION_INPUT_TEXT_WIDTH];
|
||||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||||
buffer[sizeof(buffer) - 1] = '\0';
|
buffer[sizeof(buffer) - 1] = '\0';
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ namespace overlay::windows {
|
|||||||
bool keypads_card_select_done = false;
|
bool keypads_card_select_done = false;
|
||||||
ImGui::FileBrowser keypads_card_select_browser[2];
|
ImGui::FileBrowser keypads_card_select_browser[2];
|
||||||
char keypads_card_number[2][18] {};
|
char keypads_card_number[2][18] {};
|
||||||
|
bool keypads_card_override_valid[2] = { false, false };
|
||||||
|
bool keypads_card_file_contents_valid[2] = { false, false };
|
||||||
|
|
||||||
// presets tab
|
// presets tab
|
||||||
std::vector<ControllerTemplate> templates_cache;
|
std::vector<ControllerTemplate> templates_cache;
|
||||||
@@ -195,12 +197,13 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void set_alternating_row_colors(const int row_index);
|
void set_alternating_row_colors(const int row_index);
|
||||||
|
|
||||||
|
bool validate_ea_card(char card_number[16]);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config(SpiceOverlay *overlay);
|
Config(SpiceOverlay *overlay);
|
||||||
~Config() override;
|
~Config() override;
|
||||||
|
|
||||||
void read_card(int player = -1);
|
void read_card(int player = -1);
|
||||||
void write_card(int player);
|
|
||||||
void build_content() override;
|
void build_content() override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user