Update to spice2x-25-04-25 (pre-apply)
> broken commit
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
#include "camera_control.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <games/io.h>
|
||||
#include <strmif.h>
|
||||
|
||||
#include "games/iidx/camera.h"
|
||||
#include "games/iidx/local_camera.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
#include "misc/clipboard.h"
|
||||
|
||||
using namespace games::iidx;
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
CameraControl::CameraControl(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "IIDX Camera Control";
|
||||
this->flags |= ImGuiWindowFlags_AlwaysAutoResize;
|
||||
this->init_pos = ImVec2(40, 40);
|
||||
this->toggle_button = games::OverlayButtons::ToggleCameraControl;
|
||||
}
|
||||
|
||||
CameraControl::~CameraControl() {
|
||||
}
|
||||
|
||||
void CameraControl::build_content() {
|
||||
if (!CAMERA_READY) {
|
||||
ImGui::TextColored(ImVec4(1.f, 1.f, 0.f, 1.f), "%s", "Camera not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// camera combo box
|
||||
auto numCameras = LOCAL_CAMERA_LIST.size();
|
||||
|
||||
if (numCameras == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
IIDXLocalCamera *selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);
|
||||
auto selectedCameraChanged = ImGui::BeginCombo(
|
||||
"Source", selectedCamera->GetName().c_str()
|
||||
);
|
||||
|
||||
if (selectedCameraChanged) {
|
||||
for (size_t i = 0; i < numCameras; i++) {
|
||||
IIDXLocalCamera *cameraItem = LOCAL_CAMERA_LIST.at(i);
|
||||
const bool is_selected = (m_selectedCameraIndex == (int) i);
|
||||
if (ImGui::Selectable(cameraItem->GetName().c_str(), is_selected)) {
|
||||
m_selectedCameraIndex = i;
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (selectedCamera->GetFriendlyName().length() < 22) {
|
||||
ImGui::Text("%s", selectedCamera->GetFriendlyName().c_str());
|
||||
} else {
|
||||
ImGui::Text("%.19s...", selectedCamera->GetFriendlyName().c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(selectedCamera->GetSymLink().c_str());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
const auto s = selectedCamera->GetFriendlyName() + "\n" + selectedCamera->GetSymLink();
|
||||
clipboard::copy_text(s);
|
||||
}
|
||||
|
||||
// Render parameters
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Rendering");
|
||||
|
||||
// Media Type Selector
|
||||
int selectedMediaTypeIndex = selectedCamera->m_selectedMediaTypeIndex;
|
||||
auto numMediaTypes = selectedCamera->m_mediaTypeInfos.size();
|
||||
|
||||
auto selectedMediaTypeIndexChanged = ImGui::BeginCombo(
|
||||
"Media Type", selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).description.c_str()
|
||||
);
|
||||
|
||||
if (selectedMediaTypeIndexChanged) {
|
||||
for (size_t i = 0; i < numMediaTypes; i++) {
|
||||
const bool is_selected = (selectedMediaTypeIndex == (int) i);
|
||||
if (ImGui::Selectable(selectedCamera->m_mediaTypeInfos.at(i).description.c_str(), is_selected)) {
|
||||
selectedMediaTypeIndex = i;
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (selectedMediaTypeIndexChanged && selectedMediaTypeIndex != selectedCamera->m_selectedMediaTypeIndex) {
|
||||
selectedCamera->m_useAutoMediaType = false;
|
||||
selectedCamera->ChangeMediaType(selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).p_mediaType);
|
||||
}
|
||||
|
||||
// Auto media type
|
||||
bool isAutoMediaType = selectedCamera->m_useAutoMediaType;
|
||||
ImGui::SameLine(300);
|
||||
if (ImGui::Checkbox("Auto##MediaType", &isAutoMediaType)) {
|
||||
selectedCamera->m_useAutoMediaType = isAutoMediaType;
|
||||
if (isAutoMediaType) {
|
||||
selectedCamera->ChangeMediaType(selectedCamera->m_pAutoMediaType);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw mode
|
||||
int selectedDrawModeIndex = selectedCamera->m_drawMode;
|
||||
if (ImGui::BeginCombo("Draw Mode", DRAW_MODE_LABELS[selectedCamera->m_drawMode].c_str())) {
|
||||
for (size_t i = 0; i < DRAW_MODE_SIZE; i++) {
|
||||
const bool is_selected = (selectedDrawModeIndex == (int) i);
|
||||
if (ImGui::Selectable(DRAW_MODE_LABELS[i].c_str(), is_selected)) {
|
||||
selectedDrawModeIndex = i;
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Stretch: direct copy from source to destination\n\n"
|
||||
"Crop: Keep aspect ratio (16:9) and cut off horizontally or vertically\n\n"
|
||||
"Letterbox: Keep aspect ratio (16:9) and add black space horizontally or vertically\n\n"
|
||||
"Crop to 4:3: Crop to display as 4:3\n\n"
|
||||
"Letterbox to 4:3: Like Letterbox, but target 4:3"
|
||||
);
|
||||
|
||||
if (selectedDrawModeIndex != selectedCamera->m_drawMode) {
|
||||
selectedCamera->m_drawMode = (LocalCameraDrawMode)selectedDrawModeIndex;
|
||||
selectedCamera->UpdateDrawRect();
|
||||
}
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Checkbox("Horizontal Flip", &selectedCamera->m_flipHorizontal);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Vertical Flip", &selectedCamera->m_flipVertical);
|
||||
|
||||
// Camera control parameters
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Camera control");
|
||||
|
||||
// some high end webcams store settings on its onboard memory, with the user configuring it
|
||||
// via proprietary software outside of the game, so don't mess with it unless the user
|
||||
// explicitly wants to change things here
|
||||
ImGui::Checkbox("Allow manual control", &selectedCamera->m_allowManualControl);
|
||||
|
||||
ImGui::BeginDisabled(!selectedCamera->m_allowManualControl);
|
||||
IAMCameraControl *pCameraControl = selectedCamera->GetCameraControl();
|
||||
if (pCameraControl) {
|
||||
for (size_t i = 0; i < CAMERA_CONTROL_PROP_SIZE; i++) {
|
||||
CameraControlProp prop = {};
|
||||
selectedCamera->GetCameraControlProp(i, &prop);
|
||||
|
||||
auto value = prop.value;
|
||||
|
||||
bool isDisabled = (prop.defFlags == 0 || prop.valueFlags & CameraControl_Flags_Auto);
|
||||
ImGui::BeginDisabled(isDisabled);
|
||||
|
||||
int sliderFlag = ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoRoundToFormat;
|
||||
|
||||
bool isDefAuto = prop.defFlags & CameraControl_Flags_Auto;
|
||||
|
||||
if (ImGui::SliderInt(CAMERA_CONTROL_LABELS[i].c_str(), (int*) &value, prop.minValue, prop.maxValue, "%d", sliderFlag)) {
|
||||
selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (isDefAuto) {
|
||||
ImGui::SameLine(300);
|
||||
if (ImGui::CheckboxFlags(("Auto##" + CAMERA_CONTROL_LABELS[i]).c_str(), (int*) &prop.valueFlags, CameraControl_Flags_Auto)) {
|
||||
selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// reset button
|
||||
if (ImGui::Button("Reset")) {
|
||||
selectedCamera->ResetCameraControlProps();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// save button
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
this->config_save();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraControl::config_save() {
|
||||
camera_config_save();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include <strmif.h>
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class CameraControl : public Window {
|
||||
public:
|
||||
CameraControl(SpiceOverlay *overlay);
|
||||
~CameraControl() override;
|
||||
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
int m_selectedCameraIndex = 0;
|
||||
bool config_dirty = false;
|
||||
void config_save();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <random>
|
||||
#include <games/io.h>
|
||||
#include "card_manager.h"
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/writer.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/clipboard.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
@@ -14,104 +18,413 @@ namespace overlay::windows {
|
||||
|
||||
CardManager::CardManager(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "Card Manager";
|
||||
this->init_size = ImVec2(300, 200);
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
|
||||
this->init_size = ImVec2(420, 420);
|
||||
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
this->init_pos = ImVec2(40, 40);
|
||||
} else {
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
|
||||
}
|
||||
|
||||
this->toggle_button = games::OverlayButtons::ToggleCardManager;
|
||||
this->config_path = std::string(getenv("APPDATA")) + "\\spicetools_card_manager.json";
|
||||
if (fileutils::file_exists(this->config_path)) {
|
||||
|
||||
bool file_exists = false;
|
||||
this->config_path =
|
||||
fileutils::get_config_file_path("cardmanager", "spicetools_card_manager.json", &file_exists);
|
||||
if (file_exists) {
|
||||
this->config_load();
|
||||
}
|
||||
|
||||
// load -card0 / -card1
|
||||
// -card0 / -card1 override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
if (!CARD_OVERRIDES[0].empty()) {
|
||||
const CardEntry card0 = {
|
||||
.name = "P1 Default (-card0)",
|
||||
.id = CARD_OVERRIDES[0],
|
||||
.search_string = "p1 default (-card0)",
|
||||
.read_only = true,
|
||||
.color = {0.9f, 0.9f, 0.9f}
|
||||
};
|
||||
card_cmd_overrides[0].emplace(card0);
|
||||
this->loaded_card[0] = card0;
|
||||
}
|
||||
if (eamuse_get_game_keypads() > 1 && !CARD_OVERRIDES[1].empty()) {
|
||||
const CardEntry card1 = {
|
||||
.name = "P2 Default (-card1)",
|
||||
.id = CARD_OVERRIDES[1],
|
||||
.search_string = "p2 default (-card1)",
|
||||
.read_only = true,
|
||||
.color = {0.9f, 0.9f, 0.9f}
|
||||
};
|
||||
card_cmd_overrides[1].emplace(card1);
|
||||
this->loaded_card[1] = card1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardManager::~CardManager() {
|
||||
}
|
||||
|
||||
void CardManager::build_content() {
|
||||
|
||||
// get window size
|
||||
auto window_size = ImGui::GetWindowSize();
|
||||
|
||||
// name field
|
||||
ImGui::InputTextWithHint("Card Name", "Main Card",
|
||||
this->name_buffer, std::size(this->name_buffer));
|
||||
|
||||
// card field
|
||||
ImGui::InputTextWithHint("Card ID", "E0040123456789AB",
|
||||
this->card_buffer,
|
||||
std::size(this->card_buffer),
|
||||
ImGuiInputTextFlags_CharsHexadecimal
|
||||
| ImGuiInputTextFlags_CharsUppercase);
|
||||
|
||||
// add card button
|
||||
if (strlen(this->card_buffer) == 16) {
|
||||
if (ImGui::Button("Add Card", ImVec2(-1.f, 0.f))) {
|
||||
|
||||
// save entry
|
||||
CardEntry entry {
|
||||
.name = this->name_buffer,
|
||||
.id = this->card_buffer
|
||||
};
|
||||
this->cards.emplace_back(entry);
|
||||
this->config_dirty = true;
|
||||
|
||||
// clear input fields
|
||||
memset(this->name_buffer, 0, sizeof(this->name_buffer));
|
||||
memset(this->card_buffer, 0, sizeof(this->card_buffer));
|
||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Active card overrides");
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Click to insert card now, or press Insert Card key. Auto Card Insert will also use these cards.\n\n"
|
||||
"If no override is set, pressing Insert Card will read from card0.txt / card1.txt.");
|
||||
if (ImGui::BeginTable("CardSetTable", eamuse_get_game_keypads() > 1 ? 2 : 1, ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Player 1");
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Player 2");
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("Enter card identifier...");
|
||||
ImGui::TableNextRow();
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (eamuse_get_game_keypads() > (int)i) {
|
||||
ImGui::TableNextColumn();
|
||||
if (build_card(i) && this->loaded_card[i].has_value()) {
|
||||
insert_card_over_api(i, this->loaded_card[i].value());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// cards area
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Available cards");
|
||||
build_card_list();
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginChild("cards", ImVec2(0, window_size.y - 128))) {
|
||||
for (auto &card : this->cards) {
|
||||
ImGui::Spacing();
|
||||
build_footer();
|
||||
|
||||
// get card name
|
||||
std::string card_name = card.name;
|
||||
if (card.name.size() > 0) {
|
||||
card_name += " - ";
|
||||
build_card_editor();
|
||||
}
|
||||
|
||||
bool CardManager::build_card(int reader) {
|
||||
ImGui::PushID(reader);
|
||||
bool clicked = false;
|
||||
if (this->loaded_card[reader].has_value()) {
|
||||
const auto &card = this->loaded_card[reader].value();
|
||||
const ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
|
||||
float bg_luminance = (0.299f * card.color[0] + 0.587 * card.color[1] + 0.114 * card.color[2]);
|
||||
|
||||
// text color
|
||||
ImVec4 text_color;
|
||||
if (0.5f < bg_luminance) {
|
||||
text_color = ImVec4(0.f, 0.f, 0.f, 1.f); // black
|
||||
} else {
|
||||
text_color = ImVec4(1.f, 1.f, 1.f, 1.f); // white
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
|
||||
if (ImGui::Button(fmt::format(
|
||||
" {} \n {} {} {} {} ",
|
||||
card.name.empty() ? "<blank>" : card.name.substr(0, 19),
|
||||
card.id.substr(0, 4).c_str(),
|
||||
card.id.substr(4, 4).c_str(),
|
||||
card.id.substr(8, 4).c_str(),
|
||||
card.id.substr(12, 4).c_str()
|
||||
).c_str())) {
|
||||
|
||||
clicked = true;
|
||||
}
|
||||
ImGui::PopStyleColor(4);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button(" (No override set) \n"
|
||||
" xxxx xxxx xxxx xxxx ");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void CardManager::open_card_editor() {
|
||||
if (this->current_card) {
|
||||
const auto card = this->current_card;
|
||||
strcpy_s(this->name_buffer, std::size(this->name_buffer), card->name.c_str());
|
||||
strcpy_s(this->card_buffer, std::size(this->card_buffer), card->id.c_str());
|
||||
this->color_buffer[0] = card->color[0];
|
||||
this->color_buffer[1] = card->color[1];
|
||||
this->color_buffer[2] = card->color[2];
|
||||
ImGui::OpenPopup("Card Editor");
|
||||
}
|
||||
}
|
||||
|
||||
void CardManager::build_card_editor() {
|
||||
// new/edit card popup
|
||||
if (ImGui::BeginPopupModal("Card Editor", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
// card ID field (only editable for new cards)
|
||||
ImGui::BeginDisabled(this->current_card);
|
||||
ImGui::InputTextWithHint("Card ID", "E0040123456789AB",
|
||||
this->card_buffer,
|
||||
std::size(this->card_buffer),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
|
||||
ImGui::EndDisabled();
|
||||
if (this->current_card == nullptr) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Generate")) {
|
||||
generate_ea_card(this->card_buffer);
|
||||
}
|
||||
card_name += card.id;
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
clipboard::copy_text(this->current_card->id);
|
||||
}
|
||||
}
|
||||
|
||||
// draw entry
|
||||
ImGui::PushID(&card);
|
||||
if (ImGui::Selectable(card_name.c_str(), card.selected)) {
|
||||
// name field
|
||||
ImGui::InputTextWithHint("Card Name", "Main Card",
|
||||
this->name_buffer, std::size(this->name_buffer));
|
||||
|
||||
// unselect other cards
|
||||
for (auto &card_disable : this->cards) {
|
||||
card_disable.selected = false;
|
||||
// color
|
||||
ImGui::ColorEdit3("Color", this->color_buffer, ImGuiColorEditFlags_DisplayHex);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Random")) {
|
||||
generate_random_color();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// add/update button
|
||||
ImGui::BeginDisabled(strlen(this->card_buffer) != 16);
|
||||
if (ImGui::Button(this->current_card ? "Update Card" : "Save Card")) {
|
||||
if (this->current_card) {
|
||||
// update existing card
|
||||
this->current_card->name = strtrim(this->name_buffer);
|
||||
this->current_card->color[0] = this->color_buffer[0];
|
||||
this->current_card->color[1] = this->color_buffer[1];
|
||||
this->current_card->color[2] = this->color_buffer[2];
|
||||
generate_search_string(this->current_card);
|
||||
|
||||
// ensure loaded cards are kept up to date
|
||||
// note: does not handle cases where multiple cards have the same ID
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (this->loaded_card[i].has_value() &&
|
||||
!this->loaded_card[i].value().read_only &&
|
||||
this->loaded_card[i].value().id == this->current_card->id) {
|
||||
this->loaded_card[i] = *this->current_card;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// create a new card
|
||||
CardEntry card {
|
||||
.name = strtrim(this->name_buffer),
|
||||
.id = std::string(this->card_buffer),
|
||||
.color = {this->color_buffer[0], this->color_buffer[1], this->color_buffer[2]}
|
||||
};
|
||||
generate_search_string(&card);
|
||||
this->cards.emplace_back(card);
|
||||
|
||||
// mark this card as the selected one
|
||||
card.selected = true;
|
||||
this->current_card = &this->cards.back();
|
||||
}
|
||||
this->config_save();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// delete current card button
|
||||
if (this->current_card) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete Card")) {
|
||||
std::erase_if(this->cards, [&](CardEntry &card) {
|
||||
return &card == this->current_card;
|
||||
});
|
||||
this->current_card = nullptr;
|
||||
this->config_save();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void CardManager::build_card_selectable(CardEntry &card) {
|
||||
// generate card name
|
||||
std::string card_name = "";
|
||||
if (card.id.length() == 16) {
|
||||
card_name += card.id.substr(0, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(4, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(8, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(12, 4);
|
||||
} else {
|
||||
card_name += card.id;
|
||||
}
|
||||
if (!card.name.empty()) {
|
||||
card_name += " - ";
|
||||
card_name += card.name;
|
||||
}
|
||||
|
||||
ImGui::PushID(&card);
|
||||
|
||||
// color button
|
||||
ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
|
||||
ImGui::SmallButton(" ");
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// selectable item
|
||||
if (ImGui::Selectable(card_name.c_str(), this->current_card == &card)) {
|
||||
this->current_card = &card;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void CardManager::build_card_list() {
|
||||
|
||||
// search for card
|
||||
//
|
||||
// setting ImGuiInputTextFlags_CallbackCharFilter and pressing escape doesn't cause below
|
||||
// to return true, making it necessary to provide a callback...
|
||||
ImGui::SetNextItemWidth(240);
|
||||
if (ImGui::InputTextWithHint("", "Type here to search..", &this->search_filter)) {
|
||||
this->current_card = nullptr;
|
||||
this->search_filter_in_lower_case = strtolower(this->search_filter);
|
||||
}
|
||||
if (!this->search_filter.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear")) {
|
||||
this->search_filter.clear();
|
||||
this->search_filter_in_lower_case.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// toolbar
|
||||
|
||||
// set card as p1/p2
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (eamuse_get_game_keypads() > (int)i) {
|
||||
if (i != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PushID(i);
|
||||
ImGui::BeginDisabled(this->current_card == nullptr);
|
||||
if (ImGui::Button(i == 0 ? "Load P1" : "Load P2") && this->current_card) {
|
||||
this->loaded_card[i] = *this->current_card;
|
||||
log_info(
|
||||
"cardmanager",
|
||||
"[P{}] update override and insert card: {} ({})",
|
||||
i+1,
|
||||
this->current_card->id,
|
||||
this->current_card->name
|
||||
);
|
||||
|
||||
// update override
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
CARD_OVERRIDES[i] = this->current_card->id;
|
||||
|
||||
// insert card over api
|
||||
insert_card_over_api(i, this->loaded_card[i].value());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// insert P1 button
|
||||
if (ImGui::Button("Insert P1")) {
|
||||
auto card = this->cards_get_selected();
|
||||
uint8_t card_bin[8];
|
||||
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(0, card_bin);
|
||||
}
|
||||
// edit selected card
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(this->current_card == nullptr || this->current_card->read_only);
|
||||
if (ImGui::Button("Edit")) {
|
||||
open_card_editor();
|
||||
}
|
||||
|
||||
// insert P2 button
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
// move selected up/down the list
|
||||
if (this->search_filter.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Insert P2")) {
|
||||
auto card = this->cards_get_selected();
|
||||
uint8_t card_bin[8];
|
||||
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(1, card_bin);
|
||||
if (ImGui::Button("Move Up")) {
|
||||
for (auto it = this->cards.begin(); it != this->cards.end(); ++it) {
|
||||
if (&*it == this->current_card && it != this->cards.begin()) {
|
||||
std::iter_swap(it, it - 1);
|
||||
this->current_card = &*(it - 1);
|
||||
this->config_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Move Down")) {
|
||||
for (auto it = this->cards.begin(); it != this->cards.end(); ++it) {
|
||||
if (&*it == this->current_card && (it + 1) != this->cards.end()) {
|
||||
std::iter_swap(it, it + 1);
|
||||
this->current_card = &*(it + 1);
|
||||
this->config_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// cards list
|
||||
// use all available vertical space, minus height footer (a row of buttons and separator)
|
||||
if (ImGui::BeginChild(
|
||||
"cards",
|
||||
ImVec2(0, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() - 8.f))) {
|
||||
|
||||
// -card0 / -card1 override
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (card_cmd_overrides[i].has_value()) {
|
||||
build_card_selectable(card_cmd_overrides[i].value());
|
||||
}
|
||||
}
|
||||
|
||||
// cards from card manager JSON
|
||||
for (auto &card : this->cards) {
|
||||
|
||||
if (!this->search_filter_in_lower_case.empty() && !card.search_string.empty()) {
|
||||
const bool matched =
|
||||
card.search_string.find(this->search_filter_in_lower_case) != std::string::npos;
|
||||
|
||||
if (!matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
build_card_selectable(card);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void CardManager::build_footer() {
|
||||
|
||||
// add new card
|
||||
if (ImGui::Button("Add New Card")) {
|
||||
memset(this->name_buffer, 0, sizeof(this->name_buffer));
|
||||
memset(this->card_buffer, 0, sizeof(this->card_buffer));
|
||||
generate_random_color();
|
||||
this->current_card = nullptr;
|
||||
ImGui::OpenPopup("Card Editor");
|
||||
}
|
||||
|
||||
// save button
|
||||
@@ -123,24 +436,7 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
CardEntry *CardManager::cards_get_selected() {
|
||||
|
||||
// iterate cards
|
||||
for (auto &card : this->cards) {
|
||||
|
||||
// check if selected and return pointer
|
||||
if (card.selected) {
|
||||
return &card;
|
||||
}
|
||||
}
|
||||
|
||||
// no card selected
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CardManager::config_load() {
|
||||
log_info("cardmanager", "loading config");
|
||||
|
||||
// clear cards
|
||||
this->cards.clear();
|
||||
|
||||
@@ -192,8 +488,21 @@ namespace overlay::windows {
|
||||
// save entry
|
||||
CardEntry entry {
|
||||
.name = name->value.GetString(),
|
||||
.id = id->value.GetString()
|
||||
.id = id->value.GetString(),
|
||||
};
|
||||
generate_search_string(&entry);
|
||||
|
||||
// optional color
|
||||
auto color = card.FindMember("color");
|
||||
if (color != doc.MemberEnd() && color->value.IsArray()) {
|
||||
auto c = color->value.GetArray();
|
||||
if (c.Size() == 3 && c[0].IsFloat()) {
|
||||
entry.color[0] = c[0].GetFloat();
|
||||
entry.color[1] = c[1].GetFloat();
|
||||
entry.color[2] = c[2].GetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
this->cards.emplace_back(entry);
|
||||
|
||||
} else {
|
||||
@@ -242,19 +551,65 @@ namespace overlay::windows {
|
||||
Value card(kObjectType);
|
||||
card.AddMember("name", StringRef(entry.name.c_str()), doc.GetAllocator());
|
||||
card.AddMember("id", StringRef(entry.id.c_str()), doc.GetAllocator());
|
||||
|
||||
Value color(kArrayType);
|
||||
color.PushBack(entry.color[0], doc.GetAllocator());
|
||||
color.PushBack(entry.color[1], doc.GetAllocator());
|
||||
color.PushBack(entry.color[2], doc.GetAllocator());
|
||||
card.AddMember("color", color, doc.GetAllocator());
|
||||
|
||||
cards.PushBack(card, doc.GetAllocator());
|
||||
}
|
||||
|
||||
// build JSON
|
||||
// build JSON; using pretty writer so people can manually edit it
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
PrettyWriter<StringBuffer> writer(buffer);
|
||||
doc.Accept(writer);
|
||||
|
||||
// save to file
|
||||
if (fileutils::text_write(this->config_path, buffer.GetString())) {
|
||||
if (fileutils::write_config_file("cardmanager", this->config_path, buffer.GetString())) {
|
||||
this->config_dirty = false;
|
||||
} else {
|
||||
log_warning("cardmanager", "unable to save config file to {}", this->config_path);
|
||||
log_warning("cardmanager", "unable to save config file");
|
||||
}
|
||||
}
|
||||
|
||||
void CardManager::generate_search_string(CardEntry *card) {
|
||||
card->search_string = strtolower(card->name) + " " + strtolower(card->id);
|
||||
}
|
||||
|
||||
void CardManager::generate_random_color() {
|
||||
// these are colors on a hue wheel, starting from red
|
||||
static const char colors[48][7] = {
|
||||
"FF0000","FF2000","FF4000","FF6000","FF8000","FFAA00","FFCC00","FFEE00",
|
||||
"FFFF00","DDFF00","CCFF00","AAFF00","80FF00","60FF00","40FF00","20FF00",
|
||||
"00FF00","00FF20","00FF40","00FF60","00FF80","00FFAA","00FFCC","00FFDD",
|
||||
"00FFFF","00DDFF","00CCFF","0099FF","0080FF","0060FF","0040FF","0020FF",
|
||||
"0000FF","2000FF","4000FF","6000FF","8000FF","AA00FF","CC00FF","DD00FF",
|
||||
"FF00FF","FF00EE","FF00CC","FF00AA","FF0080","FF0060","FF0040","FF0020"
|
||||
};
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 generator(rd());
|
||||
std::uniform_int_distribution<> uniform(12, 36);
|
||||
|
||||
// skip, ignoring half the hue wheel close to current index
|
||||
static int index = 0;
|
||||
index = (index + uniform(generator)) % 48;
|
||||
|
||||
const auto hex = colors[index];
|
||||
|
||||
uint8_t r, g, b;
|
||||
std::sscanf(hex, "%02hhx%02hhx%02hhx", &r, &g, &b);
|
||||
this->color_buffer[0] = r / 255.f;
|
||||
this->color_buffer[1] = g / 255.f;
|
||||
this->color_buffer[2] = b / 255.f;
|
||||
}
|
||||
|
||||
void CardManager::insert_card_over_api(int reader, CardEntry &card) {
|
||||
uint8_t card_bin[8];
|
||||
if (card.id.length() == 16 && hex2bin(card.id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(reader, card_bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
struct CardEntry {
|
||||
std::string name = "unnamed";
|
||||
std::string id = "E004000000000000";
|
||||
bool selected = false;
|
||||
std::string id = "E004010000000000";
|
||||
std::string search_string = "";
|
||||
bool read_only = false;
|
||||
float color[3] {};
|
||||
};
|
||||
|
||||
class CardManager : public Window {
|
||||
@@ -20,14 +25,34 @@ namespace overlay::windows {
|
||||
|
||||
private:
|
||||
|
||||
std::string config_path;
|
||||
std::filesystem::path config_path;
|
||||
bool config_dirty = false;
|
||||
std::vector<CardEntry> cards;
|
||||
char name_buffer[65] {};
|
||||
char card_buffer[17] {};
|
||||
float color_buffer[3] {};
|
||||
|
||||
std::optional<CardEntry> card_cmd_overrides[2];
|
||||
|
||||
std::optional<CardEntry> loaded_card[2];
|
||||
CardEntry *current_card = nullptr;
|
||||
|
||||
std::string search_filter = "";
|
||||
std::string search_filter_in_lower_case = "";
|
||||
|
||||
CardEntry *cards_get_selected();
|
||||
void config_load();
|
||||
void config_save();
|
||||
|
||||
void generate_search_string(CardEntry *card);
|
||||
void generate_random_color();
|
||||
|
||||
bool build_card(int reader);
|
||||
void open_card_editor();
|
||||
void build_card_editor();
|
||||
void build_card_list();
|
||||
void build_card_selectable(CardEntry &card);
|
||||
void build_footer();
|
||||
|
||||
void insert_card_over_api(int reader, CardEntry &card);
|
||||
};
|
||||
}
|
||||
|
||||
+2343
-1433
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "cfg/game.h"
|
||||
#include "overlay/window.h"
|
||||
#include "rawinput/device.h"
|
||||
@@ -8,6 +10,21 @@
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
enum class ConfigTab {
|
||||
CONFIG_TAB_INVALID,
|
||||
CONFIG_TAB_BUTTONS,
|
||||
CONFIG_TAB_ANALOGS,
|
||||
CONFIG_TAB_OVERLAY,
|
||||
CONFIG_TAB_LIGHTS,
|
||||
CONFIG_TAB_CARDS,
|
||||
CONFIG_TAB_PATCHES,
|
||||
CONFIG_TAB_API,
|
||||
CONFIG_TAB_OPTIONS,
|
||||
CONFIG_TAB_ADVANCED,
|
||||
CONFIG_TAB_DEV,
|
||||
CONFIG_TAB_SEARCH,
|
||||
};
|
||||
|
||||
class Config : public Window {
|
||||
private:
|
||||
|
||||
@@ -17,15 +34,21 @@ namespace overlay::windows {
|
||||
std::vector<Game> games_list;
|
||||
std::vector<const char *> games_names;
|
||||
|
||||
// tabs ui
|
||||
ConfigTab tab_selected = ConfigTab::CONFIG_TAB_INVALID;
|
||||
|
||||
// buttons tab
|
||||
int buttons_page = 0;
|
||||
bool buttons_keyboard_state[0xFF];
|
||||
bool buttons_bind_active = false;
|
||||
bool buttons_many_active = false;
|
||||
std::string buttons_many_active_section = "";
|
||||
bool buttons_many_naive = false;
|
||||
int buttons_many_delay = 0;
|
||||
int buttons_many_index = -1;
|
||||
|
||||
void inc_buttons_many_index(int index_max);
|
||||
|
||||
// analogs tab
|
||||
std::vector<rawinput::Device *> analogs_devices;
|
||||
int analogs_devices_selected = -1;
|
||||
@@ -52,6 +75,8 @@ namespace overlay::windows {
|
||||
bool options_show_hidden = false;
|
||||
bool options_dirty = false;
|
||||
int options_category = 0;
|
||||
std::string search_filter = "";
|
||||
std::string search_filter_in_lower_case = "";
|
||||
|
||||
public:
|
||||
Config(SpiceOverlay *overlay);
|
||||
@@ -65,12 +90,15 @@ namespace overlay::windows {
|
||||
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||
void build_lights(const std::string &name, std::vector<Light> *lights);
|
||||
void build_cards();
|
||||
void build_options(std::vector<Option> *options, const std::string &category);
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_licenses();
|
||||
void build_launcher();
|
||||
void launch_url();
|
||||
void launch_shell(LPCSTR app, LPCSTR file);
|
||||
|
||||
static void build_page_selector(int *page);
|
||||
void build_menu(int *game_selected);
|
||||
void shutdown_system(bool force, bool reboot_instead);
|
||||
};
|
||||
}
|
||||
|
||||
+18
-12
@@ -178,20 +178,26 @@ namespace overlay::windows {
|
||||
static_cast<int>(ImGui::GetIO().DisplaySize.x),
|
||||
static_cast<int>(ImGui::GetIO().DisplaySize.y));
|
||||
|
||||
// metrics button
|
||||
this->metrics_open |= ImGui::Button("Metrics Window");
|
||||
if (this->metrics_open) {
|
||||
ImGui::ShowMetricsWindow(&this->metrics_open);
|
||||
if (ImGui::Button("ImGui Debug Log")) {
|
||||
overlay::SHOW_DEBUG_LOG_WINDOW = !overlay::SHOW_DEBUG_LOG_WINDOW;
|
||||
}
|
||||
|
||||
// removed for size (IMGUI_DISABLE_DEMO_WINDOWS)
|
||||
|
||||
// metrics button
|
||||
// this->metrics_open |= ImGui::Button("Metrics Window");
|
||||
// if (this->metrics_open) {
|
||||
// ImGui::ShowMetricsWindow(&this->metrics_open);
|
||||
// }
|
||||
|
||||
// demo button
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Demo Window")) {
|
||||
this->demo_open = true;
|
||||
}
|
||||
if (this->demo_open) {
|
||||
ImGui::ShowDemoWindow(&this->demo_open);
|
||||
}
|
||||
// ImGui::SameLine();
|
||||
// if (ImGui::Button("Demo Window")) {
|
||||
// this->demo_open = true;
|
||||
// }
|
||||
// if (this->demo_open) {
|
||||
// ImGui::ShowDemoWindow(&this->demo_open);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,7 +751,7 @@ namespace overlay::windows {
|
||||
if (ImGui::CollapsingHeader("Touch")) {
|
||||
|
||||
// status
|
||||
ImGui::Text("Status: %s", is_touch_available() ? "available" : "unavailable");
|
||||
ImGui::Text("Status: %s", is_touch_available("Control::touch_view") ? "available" : "unavailable");
|
||||
|
||||
// touch points
|
||||
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <map>
|
||||
|
||||
#include "drs_dancefloor.h"
|
||||
#include "games/io.h"
|
||||
#include "games/drs/drs.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
DRSDanceFloorDisplay::DRSDanceFloorDisplay(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "DANCERUSH Floor";
|
||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||
this->remove_window_padding = true;
|
||||
|
||||
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||
this->size_min = ImVec2(100, 128 + ImGui::GetFrameHeight());
|
||||
this->init_size = size_min;
|
||||
|
||||
this->resize_callback = this->keep_aspect_ratio;
|
||||
|
||||
this->flags = ImGuiWindowFlags_NoScrollbar
|
||||
| ImGuiWindowFlags_NoNavFocus
|
||||
| ImGuiWindowFlags_NoNavInputs
|
||||
| ImGuiWindowFlags_NoDocking
|
||||
| ImGuiWindowFlags_NoBackground;
|
||||
}
|
||||
|
||||
void DRSDanceFloorDisplay::calculate_initial_window() {
|
||||
this->init_size.x = 360;
|
||||
this->init_size.y = this->init_size.x * DRS_TAPELED_ROWS / DRS_TAPELED_COLS;
|
||||
|
||||
// horizontal right
|
||||
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
|
||||
// vertical center
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2;
|
||||
}
|
||||
|
||||
void DRSDanceFloorDisplay::build_content() {
|
||||
const auto draw_list = ImGui::GetWindowDrawList();
|
||||
const auto canvas_pos = ImGui::GetCursorScreenPos();
|
||||
const auto canvas_size = ImGui::GetContentRegionAvail();
|
||||
|
||||
const float off_x = canvas_pos.x;
|
||||
const float off_y = canvas_pos.y;
|
||||
const float scale = std::min(canvas_size.x, canvas_size.y) / DRS_TAPELED_COLS;
|
||||
|
||||
for (int x = 0; x < DRS_TAPELED_COLS; x++) {
|
||||
for (int y = 0; y < DRS_TAPELED_ROWS; y++) {
|
||||
auto &led = games::drs::DRS_TAPELED[x + y * DRS_TAPELED_COLS];
|
||||
|
||||
ImColor color(
|
||||
((uint8_t)led[0]) / ((float)DRS_TAPELED_MAX_VAL),
|
||||
((uint8_t)led[1]) / ((float)DRS_TAPELED_MAX_VAL),
|
||||
((uint8_t)led[2]) / ((float)DRS_TAPELED_MAX_VAL));
|
||||
|
||||
// if (x == 0 && y == 0) {
|
||||
// log_info("drs", "color: {} {} {}", (uint8_t)led[0], (uint8_t)led[1], (uint8_t)led[2]);
|
||||
// }
|
||||
|
||||
ImVec2 p1(x * scale + off_x, y * scale + off_y);
|
||||
ImVec2 p2((x + 1) * scale + off_x, (y + 1) * scale + off_y);
|
||||
draw_list->AddRectFilled(p1, p2, color, 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/drs/drs.h"
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class DRSDanceFloorDisplay : public Window {
|
||||
public:
|
||||
DRSDanceFloorDisplay(SpiceOverlay *overlay);
|
||||
void calculate_initial_window() override;
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
static void keep_aspect_ratio(ImGuiSizeCallbackData* data) {
|
||||
const float ratio = (float)DRS_TAPELED_ROWS / (float)DRS_TAPELED_COLS;
|
||||
data->DesiredSize.y = (data->DesiredSize.x * ratio) + ImGui::GetFrameHeight();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "exitprompt.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
ExitPrompt::ExitPrompt(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "spice2x";
|
||||
this->init_size = ImVec2(
|
||||
(ImGui::GetFontSize() * 14) + (ImGui::GetStyle().ItemSpacing.x * 2),
|
||||
120);
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||
10);
|
||||
|
||||
this->flags = ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_AlwaysAutoResize
|
||||
| ImGuiWindowFlags_NoDocking;
|
||||
}
|
||||
|
||||
void ExitPrompt::build_button(
|
||||
Window *window, std::string label, const ImVec2 &size, NextItem next, bool is_toggle) {
|
||||
|
||||
if (window == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::Button(label.c_str(), size)) {
|
||||
if (is_toggle) {
|
||||
window->toggle_active();
|
||||
} else {
|
||||
window->set_active(true);
|
||||
this->set_active(false);
|
||||
}
|
||||
}
|
||||
if (next == NextItem::NEW_LINE) {
|
||||
ImGui::Spacing();
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
void ExitPrompt::build_content() {
|
||||
const ImVec2 size(ImGui::GetFontSize() * 14, ImGui::GetFontSize() * 1.9f);
|
||||
const ImVec2 size_half(
|
||||
(size.x - ImGui::GetStyle().ItemSpacing.x) / 2,
|
||||
ImGui::GetFontSize() * 1.9f);
|
||||
const ImVec2 size_third(
|
||||
(size.x - (ImGui::GetStyle().ItemSpacing.x * 2)) / 3,
|
||||
ImGui::GetFontSize() * 2.5f);
|
||||
|
||||
if (ImGui::Button("Hide overlay", size)) {
|
||||
overlay::OVERLAY->set_active(false);
|
||||
}
|
||||
ImGui::Spacing();
|
||||
build_button(this->overlay->window_config, "Show Config", size, NextItem::NEW_LINE, false);
|
||||
build_button(this->overlay->window_sub, "Show Subscreen", size, NextItem::NEW_LINE, false);
|
||||
|
||||
ImGui::TextDisabled("Graphics");
|
||||
build_button(this->overlay->window_camera, "Camera control", size, NextItem::NEW_LINE);
|
||||
build_button(this->overlay->window_fps, "FPS", size_half, NextItem::SAME_LINE);
|
||||
build_button(this->overlay->window_resize, "Resize", size_half, NextItem::NEW_LINE);
|
||||
|
||||
ImGui::TextDisabled("I/O");
|
||||
build_button(this->overlay->window_cards, "Card Manager", size, NextItem::NEW_LINE);
|
||||
if (this->overlay->window_keypad2 == nullptr) {
|
||||
// 1p only
|
||||
build_button(this->overlay->window_keypad1, "Keypad", size_half, NextItem::SAME_LINE);
|
||||
build_button(this->overlay->window_iopanel, "I/O panel", size_half, NextItem::NEW_LINE);
|
||||
} else {
|
||||
// 1p and 2p
|
||||
build_button(this->overlay->window_keypad1, "Keypad\n P1", size_third, NextItem::SAME_LINE);
|
||||
build_button(this->overlay->window_iopanel, " I/O\npanel", size_third, NextItem::SAME_LINE);
|
||||
build_button(this->overlay->window_keypad2, "Keypad\n P2", size_third, NextItem::NEW_LINE);
|
||||
}
|
||||
|
||||
ImGui::TextDisabled("Debug");
|
||||
build_button(this->overlay->window_control, "Control", size_half, NextItem::SAME_LINE);
|
||||
build_button(this->overlay->window_log, "Log", size_half, NextItem::NEW_LINE);
|
||||
|
||||
ImGui::TextDisabled("Windows audio volume");
|
||||
if (ImGui::Button("-", size_third)) {
|
||||
INPUT keys = {};
|
||||
keys.type = INPUT_KEYBOARD;
|
||||
keys.ki.wVk = VK_VOLUME_DOWN;
|
||||
SendInput(1, &keys, sizeof(keys));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Mute", size_third)) {
|
||||
INPUT keys = {};
|
||||
keys.type = INPUT_KEYBOARD;
|
||||
keys.ki.wVk = VK_VOLUME_MUTE;
|
||||
SendInput(1, &keys, sizeof(keys));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("+", size_third)) {
|
||||
INPUT keys = {};
|
||||
keys.type = INPUT_KEYBOARD;
|
||||
keys.ki.wVk = VK_VOLUME_UP;
|
||||
SendInput(1, &keys, sizeof(keys));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// quit
|
||||
if (ImGui::Button("Exit Game", size)) {
|
||||
ImGui::OpenPopup("spice2x##quitdialog");
|
||||
}
|
||||
if (ImGui::BeginPopupModal(
|
||||
"spice2x##quitdialog",
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextUnformatted("Exit the game now?");
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Exit")) {
|
||||
log_info("exitprompt", "user chose to quit game...");
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
enum class NextItem {
|
||||
NEW_LINE,
|
||||
SAME_LINE
|
||||
};
|
||||
|
||||
class ExitPrompt : public Window {
|
||||
public:
|
||||
ExitPrompt(SpiceOverlay *overlay);
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
void build_button(Window *window, std::string label, const ImVec2 &size, NextItem next, bool is_toggle=true);
|
||||
};
|
||||
}
|
||||
@@ -20,7 +20,8 @@ namespace overlay::windows {
|
||||
|
||||
void FPS::calculate_initial_window() {
|
||||
// width is 114x82 px with window decoration, 98x47 for the content
|
||||
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x - 120, 8);
|
||||
int pos_x = overlay::FPS_SHOULD_FLIP ? 8 : ImGui::GetIO().DisplaySize.x - 122;
|
||||
this->init_pos = ImVec2(pos_x, 8);
|
||||
}
|
||||
|
||||
void FPS::build_content() {
|
||||
|
||||
@@ -15,5 +15,7 @@ namespace overlay::windows {
|
||||
|
||||
void calculate_initial_window() override;
|
||||
void build_content() override;
|
||||
|
||||
bool should_flip = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,10 +5,19 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "games/io.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
int GENERIC_SUB_WINDOW_X = 0;
|
||||
int GENERIC_SUB_WINDOW_Y = 0;
|
||||
int GENERIC_SUB_WINDOW_WIDTH = 0;
|
||||
int GENERIC_SUB_WINDOW_HEIGHT = 0;
|
||||
bool GENERIC_SUB_WINDOW_FULLSIZE = false;
|
||||
|
||||
// #define OVERLAYDBG 1
|
||||
|
||||
@@ -19,6 +28,7 @@ namespace overlay::windows {
|
||||
|
||||
GenericSubScreen::GenericSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
||||
this->remove_window_padding = true;
|
||||
// ImGuiWindowFlags_NoBackground is needed as the background is drawn on top of the subscreen image
|
||||
this->flags = ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoBackground |
|
||||
@@ -35,9 +45,34 @@ namespace overlay::windows {
|
||||
|
||||
overlay->set_subscreen_mouse_handler([this](LONG *x, LONG *y) -> bool {
|
||||
// convert to normalized form (relative window coordinates 0.f-1.f)
|
||||
ImVec2 xy(*x, *y);
|
||||
xy.x = (xy.x - overlay_content_top_left.x) / overlay_content_size.x;
|
||||
xy.y = (xy.y - overlay_content_top_left.y) / overlay_content_size.y;
|
||||
ImVec2 xy;
|
||||
|
||||
// log_misc("sub::overlay", "mouse handler {} {}", to_string(*x), to_string(*y));
|
||||
// log_misc("sub::overlay", "spicetouch coords {} {} {} {}", to_string(SPICETOUCH_TOUCH_X), to_string(SPICETOUCH_TOUCH_Y), to_string(SPICETOUCH_TOUCH_WIDTH), to_string(SPICETOUCH_TOUCH_HEIGHT));
|
||||
|
||||
float ratio_x, ratio_y;
|
||||
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
// input coords are relative to spicetouch wnd
|
||||
ratio_x = (float)*x / SPICETOUCH_TOUCH_WIDTH;
|
||||
ratio_y = (float)*y / SPICETOUCH_TOUCH_HEIGHT;
|
||||
} else {
|
||||
// inputs coords are relative to (0,0) for non-windowed mode
|
||||
ratio_x = (float)*x / ImGui::GetIO().DisplaySize.x;
|
||||
ratio_y = (float)*y / ImGui::GetIO().DisplaySize.y;
|
||||
}
|
||||
// log_misc("sub::overlay", "game coords {} {}", to_string(ratio_x), to_string(ratio_y));
|
||||
|
||||
// transform to subscreen overlay coords
|
||||
if (!GENERIC_SUB_WINDOW_FULLSIZE) {
|
||||
ratio_x = (ratio_x * ImGui::GetIO().DisplaySize.x - GENERIC_SUB_WINDOW_X) / GENERIC_SUB_WINDOW_WIDTH;
|
||||
ratio_y = (ratio_y * ImGui::GetIO().DisplaySize.y - GENERIC_SUB_WINDOW_Y) / GENERIC_SUB_WINDOW_HEIGHT;
|
||||
// log_misc("sub::overlay", "overlay coords {} {} {} {}", to_string(GENERIC_SUB_WINDOW_X), to_string(GENERIC_SUB_WINDOW_Y), to_string(GENERIC_SUB_WINDOW_WIDTH), to_string(GENERIC_SUB_WINDOW_HEIGHT));
|
||||
}
|
||||
|
||||
xy.x = ratio_x;
|
||||
xy.y = ratio_y;
|
||||
// log_misc("sub::overlay", "ratio {} {}", to_string(xy.x), to_string(xy.y));
|
||||
|
||||
// x/y can be outside of window
|
||||
if (xy.x < 0.f || 1.f < xy.x || xy.y < 0.f || 1.f < xy.y) {
|
||||
@@ -53,6 +88,11 @@ namespace overlay::windows {
|
||||
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||
|
||||
void GenericSubScreen::build_content() {
|
||||
if (this->disabled_message.has_value()) {
|
||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||
ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str());
|
||||
return;
|
||||
}
|
||||
this->draw_texture();
|
||||
|
||||
#if OVERLAYDBG
|
||||
@@ -111,6 +151,11 @@ namespace overlay::windows {
|
||||
overlay_content_size = ImGui::GetIO().DisplaySize;
|
||||
}
|
||||
|
||||
GENERIC_SUB_WINDOW_X = overlay_content_top_left.x;
|
||||
GENERIC_SUB_WINDOW_Y = overlay_content_top_left.y;
|
||||
GENERIC_SUB_WINDOW_WIDTH = overlay_content_size.x;
|
||||
GENERIC_SUB_WINDOW_HEIGHT = overlay_content_size.y;
|
||||
|
||||
if (this->draws_window &&
|
||||
this->texture &&
|
||||
((UINT)overlay_content_size.x != this->texture_width)) {
|
||||
@@ -161,7 +206,7 @@ namespace overlay::windows {
|
||||
bottom_right.x += overlay_content_top_left.x;
|
||||
bottom_right.y += overlay_content_top_left.y;
|
||||
ImGui::GetBackgroundDrawList()->AddImage(
|
||||
reinterpret_cast<void *>(this->texture),
|
||||
reinterpret_cast<ImTextureID>(this->texture),
|
||||
overlay_content_top_left,
|
||||
bottom_right);
|
||||
|
||||
|
||||
@@ -8,6 +8,19 @@
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
//=================================================================================================
|
||||
// Global variable to track the coords of Subscreen.
|
||||
// Values are in original coord-space (not scaled to Windowed mode size).
|
||||
//
|
||||
// For use with touch coords transformation.
|
||||
//=================================================================================================
|
||||
|
||||
extern int GENERIC_SUB_WINDOW_X;
|
||||
extern int GENERIC_SUB_WINDOW_Y;
|
||||
extern int GENERIC_SUB_WINDOW_WIDTH;
|
||||
extern int GENERIC_SUB_WINDOW_HEIGHT;
|
||||
extern bool GENERIC_SUB_WINDOW_FULLSIZE;
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class GenericSubScreen : public Window {
|
||||
@@ -20,6 +33,7 @@ namespace overlay::windows {
|
||||
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
||||
ImVec2 overlay_content_top_left;
|
||||
ImVec2 overlay_content_size;
|
||||
std::optional<std::string> disabled_message = std::nullopt;
|
||||
|
||||
private:
|
||||
static void keep_16_by_9(ImGuiSizeCallbackData* data) {
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
#undef CINTERFACE
|
||||
|
||||
#include "iidx_sub.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||
this->title = "IIDX Sub Screen";
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
this->disabled_message =
|
||||
"Close this overlay and use the second window.\n"
|
||||
"Or, turn on -iidxnosub to use the overlay instead.";
|
||||
this->draws_window = false;
|
||||
}
|
||||
|
||||
float size = 0.5f;
|
||||
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.has_value()) {
|
||||
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "large") {
|
||||
@@ -15,6 +25,7 @@ namespace overlay::windows {
|
||||
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "small") {
|
||||
size = 0.3f;
|
||||
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "fullscreen") {
|
||||
GENERIC_SUB_WINDOW_FULLSIZE = true;
|
||||
this->draws_window = false;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +48,14 @@ namespace overlay::windows {
|
||||
return;
|
||||
}
|
||||
|
||||
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
// Touch needs to be registered on global coords
|
||||
*x_out = SPICETOUCH_TOUCH_X + xy_in.x * SPICETOUCH_TOUCH_WIDTH;
|
||||
*y_out = SPICETOUCH_TOUCH_Y + xy_in.y * SPICETOUCH_TOUCH_HEIGHT;
|
||||
} else {
|
||||
// Fullscreen mode, scale to game coords
|
||||
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace overlay::windows {
|
||||
this->has_guitar_knobs = true;
|
||||
|
||||
// drummania can only have one player, no guitar knobs
|
||||
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')) {
|
||||
if (games::gitadora::is_drum()) {
|
||||
this->two_players = false;
|
||||
this->has_guitar_knobs = false;
|
||||
}
|
||||
@@ -39,6 +38,7 @@ namespace overlay::windows {
|
||||
|
||||
void GitadoraIOPanel::find_gfdm_buttons() {
|
||||
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||
const auto lights = games::get_lights(eamuse_get_game());
|
||||
|
||||
// device emulation treats drum controls to be the same as guitar 1p
|
||||
|
||||
@@ -49,12 +49,22 @@ namespace overlay::windows {
|
||||
this->left[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Left];
|
||||
this->right[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Right];
|
||||
|
||||
this->start_light[0] = &(*lights)[games::gitadora::Lights::P1MenuStart];
|
||||
this->help_light[0] = &(*lights)[games::gitadora::Lights::P1MenuHelp];
|
||||
this->updown_light[0] = &(*lights)[games::gitadora::Lights::P1MenuUpDown];
|
||||
this->leftright_light[0] = &(*lights)[games::gitadora::Lights::P1MenuLeftRight];
|
||||
|
||||
this->start[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Start];
|
||||
this->help[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Help];
|
||||
this->up[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Up];
|
||||
this->down[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Down];
|
||||
this->left[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Left];
|
||||
this->right[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Right];
|
||||
|
||||
this->start_light[1] = &(*lights)[games::gitadora::Lights::P2MenuStart];
|
||||
this->help_light[1] = &(*lights)[games::gitadora::Lights::P2MenuHelp];
|
||||
this->updown_light[1] = &(*lights)[games::gitadora::Lights::P2MenuUpDown];
|
||||
this->leftright_light[1] = &(*lights)[games::gitadora::Lights::P2MenuLeftRight];
|
||||
}
|
||||
|
||||
void GitadoraIOPanel::build_io_panel() {
|
||||
@@ -96,7 +106,7 @@ namespace overlay::windows {
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("<", leftright_size, this->left[p]);
|
||||
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -104,7 +114,7 @@ namespace overlay::windows {
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("^", updown_size, this->up[p]);
|
||||
this->build_button("^", updown_size, this->up[p], nullptr, this->updown_light[p]);
|
||||
const char *label;
|
||||
if (this->two_players) {
|
||||
if (p == 0) {
|
||||
@@ -115,8 +125,8 @@ namespace overlay::windows {
|
||||
} else {
|
||||
label = "Start";
|
||||
}
|
||||
this->build_button(label, start_button_size, this->start[p]);
|
||||
this->build_button("v", updown_size, this->down[p]);
|
||||
this->build_button(label, start_button_size, this->start[p], nullptr, this->start_light[p]);
|
||||
this->build_button("v", updown_size, this->down[p], nullptr, this->updown_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -124,8 +134,8 @@ namespace overlay::windows {
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("?", tiny_size, this->help[p]);
|
||||
this->build_button(">", leftright_size, this->right[p]);
|
||||
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
|
||||
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
@@ -27,5 +27,10 @@ namespace overlay::windows {
|
||||
Button *down[2];
|
||||
Button *left[2];
|
||||
Button *right[2];
|
||||
|
||||
Light *start_light[2];
|
||||
Light *help_light[2];
|
||||
Light *updown_light[2];
|
||||
Light *leftright_light[2];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <games/io.h>
|
||||
#include "keypad.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
@@ -37,6 +40,14 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void Keypad::build_content() {
|
||||
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
||||
build_tdj_keypad();
|
||||
} else {
|
||||
build_keypad();
|
||||
}
|
||||
}
|
||||
|
||||
void Keypad::build_keypad() {
|
||||
|
||||
// buttons
|
||||
static const struct {
|
||||
@@ -91,4 +102,26 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Keypad::build_tdj_keypad() {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("Keypad disabled in TDJ!\nUse subscreen overlay.");
|
||||
ImGui::SameLine();
|
||||
ImGui::WarnMarker(
|
||||
nullptr,
|
||||
"Lightning Model cabinets (TDJ) do not have any keypads; they use the subscreen.\n\n"
|
||||
"Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, "
|
||||
"then use your mouse to click. Page Up button is the default binding.\n\n"
|
||||
"Windowed mode: look for the second window in the taskbar.\n\n"
|
||||
"Windowed mode with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n"
|
||||
);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Insert Card")) {
|
||||
eamuse_set_keypad_overrides_overlay(this->unit, 1 << EAM_IO_INSERT);
|
||||
} else {
|
||||
eamuse_set_keypad_overrides_overlay(this->unit, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace overlay::windows {
|
||||
private:
|
||||
|
||||
size_t unit = 0;
|
||||
void build_keypad();
|
||||
void build_tdj_keypad();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
enum class HueFunc : int {
|
||||
Sine, Cosine, Absolute, Linear,
|
||||
Count
|
||||
};
|
||||
|
||||
class KFControl : public Window {
|
||||
public:
|
||||
|
||||
KFControl(SpiceOverlay *overlay);
|
||||
~KFControl() override;
|
||||
|
||||
void config_save();
|
||||
void config_load();
|
||||
ImVec4 hsv_transform(ImVec4 col,
|
||||
float hue = 0.f, float sat = 1.f, float val = 1.f);
|
||||
ImVec4 hue_shift(ImVec4 col, float amp, float per, uint64_t ms);
|
||||
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
std::string config_path;
|
||||
int config_profile = 0;
|
||||
|
||||
std::unique_ptr<std::thread> worker;
|
||||
bool worker_running = false;
|
||||
void worker_start();
|
||||
void worker_func();
|
||||
std::mutex worker_m;
|
||||
void worker_button_check(bool state_new, bool *state_old, int scan,
|
||||
int profile_switch = -1);
|
||||
void worker_button_set(int scan, bool state);
|
||||
void worker_mouse_click(bool state);
|
||||
void worker_mouse_move(int dx, int dy);
|
||||
|
||||
int poll_delay = 1;
|
||||
float vol_deadzone = 0.003f;
|
||||
uint64_t vol_timeout = 32;
|
||||
bool vol_mouse = false;
|
||||
float vol_mouse_sensitivity = 512;
|
||||
bool start_click = false;
|
||||
bool kp_profiles = false;
|
||||
|
||||
float vol_sound = 0.f;
|
||||
float vol_headphone = 0.f;
|
||||
float vol_external = 0.f;
|
||||
float vol_woofer = 0.f;
|
||||
bool vol_mute = false;
|
||||
|
||||
char icca_file[512] = "";
|
||||
uint64_t icca_timeout = 4000;
|
||||
uint64_t coin_timeout = 250;
|
||||
|
||||
ImVec4 light_wing_left_up {};
|
||||
ImVec4 light_wing_left_low {};
|
||||
ImVec4 light_wing_right_up {};
|
||||
ImVec4 light_wing_right_low {};
|
||||
ImVec4 light_woofer {};
|
||||
ImVec4 light_controller {};
|
||||
ImVec4 light_generator {};
|
||||
|
||||
bool light_buttons = true;
|
||||
bool light_hue_preview = false;
|
||||
bool light_hue_disable = false;
|
||||
HueFunc light_hue_func = HueFunc::Sine;
|
||||
float light_wing_left_up_hue_amp = 0.f;
|
||||
float light_wing_left_up_hue_per = 1.f;
|
||||
float light_wing_left_low_hue_amp = 0.f;
|
||||
float light_wing_left_low_hue_per = 1.f;
|
||||
float light_wing_right_up_hue_amp = 0.f;
|
||||
float light_wing_right_up_hue_per = 1.f;
|
||||
float light_wing_right_low_hue_amp = 0.f;
|
||||
float light_wing_right_low_hue_per = 1.f;
|
||||
float light_woofer_hue_amp = 0.f;
|
||||
float light_woofer_hue_per = 1.f;
|
||||
float light_controller_hue_amp = 0.f;
|
||||
float light_controller_hue_per = 1.f;
|
||||
float light_generator_hue_amp = 0.f;
|
||||
float light_generator_hue_per = 1.f;
|
||||
|
||||
/*
|
||||
* Keyboard Scancodes
|
||||
* Check: http://kbdlayout.info/kbdus/overview+scancodes
|
||||
*/
|
||||
|
||||
int scan_service = 3;
|
||||
int scan_test = 4;
|
||||
int scan_coin_mech = 5;
|
||||
int scan_bt_a = 32;
|
||||
int scan_bt_b = 33;
|
||||
int scan_bt_c = 36;
|
||||
int scan_bt_d = 37;
|
||||
int scan_fx_l = 46;
|
||||
int scan_fx_r = 50;
|
||||
int scan_start = 2;
|
||||
int scan_headphone = 0;
|
||||
int scan_vol_l_left = 17;
|
||||
int scan_vol_l_right = 18;
|
||||
int scan_vol_r_left = 24;
|
||||
int scan_vol_r_right = 25;
|
||||
int scan_icca = 6;
|
||||
int scan_coin = 7;
|
||||
int scan_kp_0 = 0;
|
||||
int scan_kp_1 = 0;
|
||||
int scan_kp_2 = 0;
|
||||
int scan_kp_3 = 0;
|
||||
int scan_kp_4 = 0;
|
||||
int scan_kp_5 = 0;
|
||||
int scan_kp_6 = 0;
|
||||
int scan_kp_7 = 0;
|
||||
int scan_kp_8 = 0;
|
||||
int scan_kp_9 = 0;
|
||||
int scan_kp_00 = 0;
|
||||
int scan_kp_decimal = 0;
|
||||
};
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace overlay::windows {
|
||||
name = "SYSTEM";
|
||||
break;
|
||||
}
|
||||
return fmt::format("{} (0x{:2X})", name, cmd);
|
||||
return fmt::format("{} (0x{:X})", name, cmd);
|
||||
}
|
||||
|
||||
MIDIWindow::MIDIWindow(SpiceOverlay *overlay) : Window(overlay) {
|
||||
@@ -90,7 +90,7 @@ namespace overlay::windows {
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", midi_cmd_str(data.cmd).c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("0x%02X - %i", data.ch, data.ch);
|
||||
ImGui::Text("0x%X - Ch%i", data.ch, data.ch + 1);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("0x%02X", data.b1);
|
||||
ImGui::NextColumn();
|
||||
|
||||
+2324
-422
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
@@ -8,6 +13,8 @@ namespace overlay::windows {
|
||||
Unknown,
|
||||
Memory,
|
||||
Signature,
|
||||
Union,
|
||||
Integer,
|
||||
};
|
||||
|
||||
enum class PatchStatus {
|
||||
@@ -16,6 +23,14 @@ namespace overlay::windows {
|
||||
Enabled,
|
||||
};
|
||||
|
||||
enum class PatchUrlStatus {
|
||||
Valid,
|
||||
Invalid,
|
||||
Unapplied,
|
||||
ValidButNoData,
|
||||
Partial,
|
||||
};
|
||||
|
||||
struct MemoryPatch {
|
||||
std::string dll_name = "";
|
||||
std::shared_ptr<uint8_t[]> data_disabled = nullptr;
|
||||
@@ -31,25 +46,59 @@ namespace overlay::windows {
|
||||
struct SignaturePatch {
|
||||
std::string dll_name = "";
|
||||
std::string signature = "", replacement = "";
|
||||
int64_t offset = 0, usage = 0;
|
||||
uint64_t offset = 0;
|
||||
int64_t usage = 0;
|
||||
|
||||
MemoryPatch to_memory(PatchData *patch);
|
||||
};
|
||||
|
||||
struct UnionPatch {
|
||||
std::string name = "";
|
||||
std::string dll_name = "";
|
||||
std::shared_ptr<uint8_t[]> data = nullptr;
|
||||
size_t data_len = 0;
|
||||
uint64_t offset = 0;
|
||||
uint8_t* data_offset_ptr = nullptr;
|
||||
bool fatal_error = false;
|
||||
};
|
||||
|
||||
struct NumberPatch {
|
||||
std::string dll_name = "";
|
||||
uint64_t data_offset = 0;
|
||||
uint8_t* data_offset_ptr = nullptr;
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
int32_t value;
|
||||
size_t size_in_bytes;
|
||||
bool fatal_error = false;
|
||||
};
|
||||
|
||||
struct PatchData {
|
||||
bool enabled;
|
||||
std::string game_code;
|
||||
int datecode_min, datecode_max;
|
||||
std::string name, description;
|
||||
int datecode_min = 0;
|
||||
int datecode_max = 0;
|
||||
std::string name, description, caution;
|
||||
std::string name_in_lower_case = "";
|
||||
PatchType type;
|
||||
bool preset;
|
||||
std::vector<MemoryPatch> patches_memory;
|
||||
std::vector<UnionPatch> patches_union;
|
||||
NumberPatch patch_number;
|
||||
PatchStatus last_status;
|
||||
bool saved;
|
||||
std::string hash;
|
||||
bool unverified = false;
|
||||
std::string peIdentifier;
|
||||
std::string error_reason = "";
|
||||
|
||||
// for union patch only
|
||||
std::string selected_union_name = "";
|
||||
};
|
||||
|
||||
extern std::optional<std::string> PATCH_MANAGER_CFG_PATH_OVERRIDE;
|
||||
|
||||
std::string get_game_identifier(const std::filesystem::path& dll_path);
|
||||
|
||||
class PatchManager : public Window {
|
||||
public:
|
||||
|
||||
@@ -57,27 +106,67 @@ namespace overlay::windows {
|
||||
~PatchManager() override;
|
||||
|
||||
void build_content() override;
|
||||
void reload_patches(bool apply_patches = false);
|
||||
void reload_local_patches(bool apply_patches = false);
|
||||
bool import_remote_patches_to_disk();
|
||||
bool load_from_patches_json(bool apply_patches);
|
||||
bool import_remote_patches_for_dll(const std::string& url, const std::string& dll_name);
|
||||
void hard_apply_patches();
|
||||
void load_embedded_patches(bool apply_patches);
|
||||
|
||||
private:
|
||||
|
||||
// configuration
|
||||
static std::string config_path;
|
||||
static std::filesystem::path config_path;
|
||||
static bool config_dirty;
|
||||
static bool setting_auto_apply;
|
||||
static std::vector<std::string> setting_auto_apply_list;
|
||||
static std::vector<std::string> setting_patches_enabled;
|
||||
static std::map<std::string, std::string> setting_union_patches_enabled;
|
||||
static std::map<std::string, int64_t> setting_int_patches_enabled;
|
||||
static std::string patch_url;
|
||||
static std::string patch_name_filter;
|
||||
|
||||
static std::filesystem::path LOCAL_PATCHES_PATH;
|
||||
static std::string ACTIVE_JSON_FILE;
|
||||
|
||||
// patches
|
||||
static std::vector<PatchData> patches;
|
||||
static bool patches_initialized;
|
||||
static bool local_patches_initialized;
|
||||
|
||||
void config_load();
|
||||
void config_save();
|
||||
|
||||
void append_patches(std::string &patches_json, bool apply_patches = false);
|
||||
void append_patches(
|
||||
std::string &patches_json,
|
||||
bool apply_patches = false,
|
||||
std::function<bool(const PatchData&)> filter = std::function<bool(const PatchData&)>(),
|
||||
std::string pe_identifier_for_patch = "");
|
||||
|
||||
void show_patch_tooltip(const PatchData& patch);
|
||||
};
|
||||
|
||||
PatchStatus is_patch_active(PatchData &patch);
|
||||
bool apply_patch(PatchData &patch, bool active);
|
||||
|
||||
int64_t parse_little_endian_int(uint8_t* bytes, size_t size);
|
||||
void int_to_little_endian_bytes(int64_t value, uint8_t* bytes, size_t size);
|
||||
|
||||
std::vector<uint8_t>* find_in_dll_map(
|
||||
const std::string& dll_name, size_t offset, size_t size);
|
||||
std::vector<uint8_t>* find_in_dll_map_org(
|
||||
const std::string& dll_name, size_t offset, size_t size);
|
||||
|
||||
bool restore_bytes_from_dll_map_org(
|
||||
uint8_t* destination, const std::string& dll_name, size_t offset, size_t size);
|
||||
|
||||
void create_dll_backup(
|
||||
std::vector<std::string>& written_list, const std::filesystem::path& dll_path);
|
||||
std::string fix_up_dll_name(const std::string& dll_name);
|
||||
uint8_t* get_dll_offset_for_patch_apply(
|
||||
const std::string& dll_name, const uint64_t data_offset, const size_t size_in_bytes);
|
||||
|
||||
uint64_t parse_json_data_offset(
|
||||
const std::string &patch_name, const rapidjson::Value &value);
|
||||
|
||||
void print_auto_apply_status(PatchData &patch);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace overlay::windows {
|
||||
this->init_pos = ImVec2(10, 10);
|
||||
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
||||
this->toggle_scene[0] = games::OverlayButtons::ScreenResizeScene1;
|
||||
this->toggle_scene[1] = games::OverlayButtons::ScreenResizeScene2;
|
||||
this->toggle_scene[2] = games::OverlayButtons::ScreenResizeScene3;
|
||||
this->toggle_scene[3] = games::OverlayButtons::ScreenResizeScene4;
|
||||
}
|
||||
|
||||
ScreenResize::~ScreenResize() {
|
||||
@@ -42,13 +46,16 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::reset_vars_to_default() {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
||||
cfg::SCREENRESIZE->centered = true;
|
||||
cfg::SCREENRESIZE->offset_x = 0;
|
||||
cfg::SCREENRESIZE->offset_y = 0;
|
||||
cfg::SCREENRESIZE->scale_x = 1.f;
|
||||
cfg::SCREENRESIZE->scale_y = 1.f;
|
||||
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
||||
scene.keep_aspect_ratio = true;
|
||||
scene.offset_x = 0;
|
||||
scene.offset_y = 0;
|
||||
scene.scale_x = 1.f;
|
||||
scene.scale_y = 1.f;
|
||||
}
|
||||
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||
@@ -63,11 +70,7 @@ namespace overlay::windows {
|
||||
void ScreenResize::build_content() {
|
||||
ImGui::Text("For: %s", eamuse_get_game().c_str());
|
||||
{
|
||||
int flags = 0;
|
||||
if (!GRAPHICS_WINDOWED || cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
||||
}
|
||||
if (ImGui::TreeNodeEx("Image Resize", flags)) {
|
||||
if (ImGui::TreeNodeEx("Image Resize", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
this->build_fullscreen_config();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
@@ -93,25 +96,57 @@ namespace overlay::windows {
|
||||
void ScreenResize::build_fullscreen_config() {
|
||||
// enable checkbox
|
||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle.");
|
||||
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
|
||||
if (ImGui::RadioButton("Scene 1", cfg::SCREENRESIZE->screen_resize_current_scene == 0)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 2", cfg::SCREENRESIZE->screen_resize_current_scene == 1)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 3", cfg::SCREENRESIZE->screen_resize_current_scene == 2)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 2;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 4", cfg::SCREENRESIZE->screen_resize_current_scene == 3)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 3;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Hint: bind a key to Screen Resize 1/2/3/4 for quick scene switching. "
|
||||
"Scene 1 is the default scene activated when starting the game.");
|
||||
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
|
||||
// general settings
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
||||
if (!cfg::SCREENRESIZE->centered) {
|
||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||
}
|
||||
ImGui::InputInt("X Offset", &scene.offset_x);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||
ImGui::InputInt("Y Offset", &scene.offset_y);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||
|
||||
// aspect ratio
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||
if (scene.keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.5f, 2.5f)) {
|
||||
scene.scale_y = scene.scale_x;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
} else {
|
||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.5f, 2.5f);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
@@ -141,6 +176,15 @@ namespace overlay::windows {
|
||||
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||
graphics_update_z_order(window);
|
||||
}
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
||||
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others.\n\n"
|
||||
"This can't be changed in-game; instead, set -windowscale option in spicecfg and restart.");
|
||||
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||
@@ -149,31 +193,35 @@ namespace overlay::windows {
|
||||
const uint32_t step = 1;
|
||||
const uint32_t step_fast = 10;
|
||||
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
changed |= ImGui::InputScalar(
|
||||
ImGui::InputScalar(
|
||||
"Width",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_width,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
ImGui::EndDisabled();
|
||||
changed |= ImGui::InputScalar(
|
||||
|
||||
ImGui::InputScalar(
|
||||
"Height",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_height,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
changed |= ImGui::InputScalar(
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"X Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_x,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
changed |= ImGui::InputScalar(
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"Y Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_y,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
if (changed) {
|
||||
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||
cfg::SCREENRESIZE->client_width =
|
||||
@@ -206,8 +254,10 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::update() {
|
||||
Window::update();
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
|
||||
// toggle
|
||||
if (this->toggle_screen_resize != ~0u) {
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
bool toggle_screen_resize_new = overlay_buttons
|
||||
&& this->overlay->hotkeys_triggered()
|
||||
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize));
|
||||
@@ -217,5 +267,36 @@ namespace overlay::windows {
|
||||
}
|
||||
this->toggle_screen_resize_state = toggle_screen_resize_new;
|
||||
}
|
||||
|
||||
// scene switch
|
||||
auto toggle_scene_state_new = ~0u;
|
||||
for (size_t i = 0; i < std::size(this->toggle_scene); i++) {
|
||||
if (this->toggle_scene[i] == ~0u) {
|
||||
continue;
|
||||
}
|
||||
bool scene_switched = overlay_buttons
|
||||
&& this->overlay->hotkeys_triggered()
|
||||
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_scene[i]));
|
||||
|
||||
if (scene_switched) {
|
||||
toggle_scene_state_new = (uint32_t)i;
|
||||
}
|
||||
|
||||
// only detect rising edges of key presses
|
||||
if (scene_switched && (this->toggle_scene_state != i)) {
|
||||
if (cfg::SCREENRESIZE->screen_resize_current_scene == (int8_t)i &&
|
||||
cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
// this scene is already active, turn scaling off
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
} else {
|
||||
// switch to scene
|
||||
cfg::SCREENRESIZE->enable_screen_resize = true;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// remember if a key was pressed (or nothing pressed) this frame
|
||||
this->toggle_scene_state = toggle_scene_state_new;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace overlay::windows {
|
||||
size_t toggle_screen_resize = ~0u;
|
||||
bool toggle_screen_resize_state = false;
|
||||
|
||||
size_t toggle_scene[4] = { ~0u, ~0u, ~0u, ~0u };
|
||||
uint32_t toggle_scene_state = ~0u;
|
||||
|
||||
void build_fullscreen_config();
|
||||
void build_windowed_config();
|
||||
void build_footer();
|
||||
|
||||
@@ -1,20 +1,57 @@
|
||||
#undef CINTERFACE
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "sdvx_sub.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||
this->title = "SDVX Sub Screen";
|
||||
|
||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||
if (!isValkyrieCabinetMode) {
|
||||
this->disabled_message = "Valkyrie Model mode is not enabled!";
|
||||
} else if (GRAPHICS_WINDOWED) {
|
||||
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
||||
} else {
|
||||
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead.";
|
||||
}
|
||||
}
|
||||
|
||||
const auto padding = ImGui::GetFrameHeight() / 2;
|
||||
|
||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
switch (games::sdvx::OVERLAY_POS) {
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
|
||||
this->init_size.x = (ImGui::GetIO().DisplaySize.x - (ImGui::GetIO().DisplaySize.y * 9 / 16)) / 2 - padding;
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM:
|
||||
case games::sdvx::SDVX_OVERLAY_MIDDLE:
|
||||
default:
|
||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
this->init_size.x /= 2;
|
||||
this->init_size.y /= 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2, 0);
|
||||
switch (games::sdvx::OVERLAY_POS) {
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
|
||||
this->init_pos.x = 0;
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
|
||||
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||
this->init_pos.y = padding;
|
||||
break;
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
#include "vr.h"
|
||||
#include "misc/vrutil.h"
|
||||
#include "util/logging.h"
|
||||
#include "games/io.h"
|
||||
#include "games/drs/drs.h"
|
||||
#include "avs/game.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
VRWindow::VRWindow(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "VR";
|
||||
this->flags = ImGuiWindowFlags_None;
|
||||
this->toggle_button = games::OverlayButtons::ToggleVRControl;
|
||||
this->init_size = ImVec2(500, 800);
|
||||
this->size_min = ImVec2(250, 200);
|
||||
}
|
||||
|
||||
VRWindow::~VRWindow() {
|
||||
}
|
||||
|
||||
void VRWindow::build_content() {
|
||||
ImGui::BeginTabBar("VRTabBar");
|
||||
if (ImGui::BeginTabItem("Info")) {
|
||||
build_info();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (avs::game::is_model("REC")) {
|
||||
if (ImGui::BeginTabItem("Dancefloor")) {
|
||||
build_dancefloor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VRWindow::build_info() {
|
||||
|
||||
// status
|
||||
auto status = vrutil::STATUS;
|
||||
switch (status) {
|
||||
case vrutil::VRStatus::Disabled:
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.4f, 0.4f, 0.4f, 1.f),
|
||||
"Disabled");
|
||||
if (ImGui::Button("Start")) {
|
||||
vrutil::init();
|
||||
if (avs::game::is_model("REC")) {
|
||||
games::drs::start_vr();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case vrutil::VRStatus::Error:
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.8f, 0.1f, 0.1f, 1.f),
|
||||
"Error");
|
||||
if (ImGui::Button("Restart")) {
|
||||
vrutil::shutdown();
|
||||
vrutil::init();
|
||||
}
|
||||
break;
|
||||
case vrutil::VRStatus::Running:
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.1f, 0.8f, 0.1f, 1.f),
|
||||
"Running");
|
||||
if (ImGui::Button("Stop")) {
|
||||
vrutil::shutdown();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// rescan
|
||||
if (ImGui::Button("Rescan Devices")) {
|
||||
vrutil::scan(true);
|
||||
}
|
||||
|
||||
// data table header
|
||||
ImGui::Columns(2);
|
||||
ImGui::Text("Device");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Position");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Separator();
|
||||
|
||||
// HMD/Left/Right data
|
||||
vr::TrackedDevicePose_t hmd_pose, left_pose, right_pose;
|
||||
vr::VRControllerState_t left_state, right_state;
|
||||
vrutil::get_hmd_pose(&hmd_pose);
|
||||
vrutil::get_con_pose(vrutil::INDEX_LEFT, &left_pose, &left_state);
|
||||
vrutil::get_con_pose(vrutil::INDEX_RIGHT, &right_pose, &right_state);
|
||||
auto hmd_pos = vrutil::get_translation(hmd_pose.mDeviceToAbsoluteTracking);
|
||||
auto left_pos = vrutil::get_translation(left_pose.mDeviceToAbsoluteTracking);
|
||||
auto right_pos = vrutil::get_translation(right_pose.mDeviceToAbsoluteTracking);
|
||||
ImGui::Text("HMD");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted(fmt::format(
|
||||
"X={:3f} Y={:3f} Z={:3f}",
|
||||
hmd_pos.x, hmd_pos.y, hmd_pos.z).c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Left");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted(fmt::format(
|
||||
"X={:3f} Y={:3f} Z={:3f}",
|
||||
left_pos.x, left_pos.y, left_pos.z).c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Right");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted(fmt::format(
|
||||
"X={:3f} Y={:3f} Z={:3f}",
|
||||
right_pos.x, right_pos.y, right_pos.z).c_str());
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
void VRWindow::build_dancefloor() {
|
||||
ImGui::Separator();
|
||||
|
||||
// settings
|
||||
ImGui::DragFloat3("Scale", &games::drs::VR_SCALE[0], 0.1f);
|
||||
ImGui::DragFloat3("Offset", &games::drs::VR_OFFSET[0], 0.1f);
|
||||
ImGui::DragFloat("Rotation", &games::drs::VR_ROTATION, 0.5f);
|
||||
for (int i = 0; i < (int) std::size(games::drs::VR_FOOTS); ++i) {
|
||||
auto &foot = games::drs::VR_FOOTS[i];
|
||||
ImGui::Separator();
|
||||
ImGui::PushID(&foot);
|
||||
ImGui::Text("%s Foot", i == 0 ? "Left" : "Right");
|
||||
ImGui::InputInt("Device Index", (int*) &foot.index, 1, 1);
|
||||
ImGui::DragFloat("Length", &foot.length,
|
||||
0.005f, 0.001f, 1000.f);
|
||||
ImGui::DragFloat("Size Base", &foot.size_base,
|
||||
0.005f, 0.001f, 1000.f);
|
||||
ImGui::DragFloat("Size Scale", &foot.size_scale,
|
||||
0.005f, 0.001f, 1000.f);
|
||||
ImGui::DragFloat4("Rotation Quat", &foot.rotation.x, 0.001f, -1, 1);
|
||||
if (ImGui::Button("Calibrate")) {
|
||||
vr::TrackedDevicePose_t pose;
|
||||
vr::VRControllerState_t state;
|
||||
vrutil::get_con_pose(foot.get_index(), &pose, &state);
|
||||
foot.length = foot.height + 0.02f;
|
||||
auto pose_rot = vrutil::get_rotation(pose.mDeviceToAbsoluteTracking.m);
|
||||
foot.rotation = linalg::qmul(linalg::qinv(pose_rot),
|
||||
vrutil::get_rotation((float) M_PI * -0.5f, 0, 0));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Place the controller to the lower part of your leg "
|
||||
"and press this button to auto calibrate angle and length");
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// prepare view
|
||||
auto draw_list = ImGui::GetWindowDrawList();
|
||||
auto canvas_pos = ImGui::GetCursorScreenPos();
|
||||
auto canvas_size = ImGui::GetContentRegionAvail();
|
||||
float offset_x = canvas_size.x * 0.5f;
|
||||
float offset_y = canvas_size.y * 0.1f;
|
||||
float off_x = offset_x + canvas_pos.x;
|
||||
float off_y = offset_y + canvas_pos.y;
|
||||
float scale = std::min(canvas_size.x, canvas_size.y) / 60;
|
||||
|
||||
// axis
|
||||
draw_list->AddLine(
|
||||
ImVec2(canvas_pos.x, off_y),
|
||||
ImVec2(canvas_pos.x + canvas_size.x, off_y),
|
||||
ImColor(255, 0, 0, 128));
|
||||
draw_list->AddLine(
|
||||
ImVec2(off_x, canvas_pos.y),
|
||||
ImVec2(off_x, canvas_pos.y + canvas_size.y),
|
||||
ImColor(0, 255, 0, 128));
|
||||
|
||||
// tiles
|
||||
for (int x = 0; x < 38; x++) {
|
||||
for (int y = 0; y < 49; y++) {
|
||||
auto &led = games::drs::DRS_TAPELED[x + y * 38];
|
||||
ImColor color((int) led[0], (int) led[1], (int) led[2]);
|
||||
ImVec2 p1((x - 19) * scale + off_x, (y + 0) * scale + off_y);
|
||||
ImVec2 p2((x - 18) * scale + off_x, (y + 1) * scale + off_y);
|
||||
draw_list->AddRectFilled(p1, p2, color, 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
// foots
|
||||
const float foot_box = 2.f * scale;
|
||||
for (auto &foot : games::drs::VR_FOOTS) {
|
||||
vr::TrackedDevicePose_t pose;
|
||||
vr::VRControllerState_t state;
|
||||
vrutil::get_con_pose(foot.get_index(), &pose, &state);
|
||||
if (pose.bPoseIsValid) {
|
||||
|
||||
// position
|
||||
auto pos = vrutil::get_translation(pose.mDeviceToAbsoluteTracking);
|
||||
pos = foot.to_world(pos);
|
||||
pos.x -= 19;
|
||||
pos *= scale;
|
||||
ImColor color(255, 0, 255);
|
||||
if (foot.event.type == games::drs::DRS_DOWN
|
||||
|| (foot.event.type == games::drs::DRS_MOVE)) {
|
||||
auto size_factor = foot.event.width / (foot.size_base + foot.size_scale);
|
||||
color = ImColor((int) (size_factor * 127) + 128, 0, 0);
|
||||
}
|
||||
ImVec2 p1(pos.x + off_x - foot_box * 0.5f,
|
||||
pos.y + off_y - foot_box * 0.5f);
|
||||
ImVec2 p2(pos.x + off_x + foot_box * 0.5f,
|
||||
pos.y + off_y + foot_box * 0.5f);
|
||||
draw_list->AddRectFilled(p1, p2, color, 0.f);
|
||||
|
||||
// direction
|
||||
auto direction = -linalg::qzdir(linalg::qmul(
|
||||
vrutil::get_rotation(pose.mDeviceToAbsoluteTracking.m),
|
||||
foot.rotation));
|
||||
direction = linalg::aliases::float3 {
|
||||
-direction.z, direction.x, direction.y
|
||||
};
|
||||
auto end = pos + direction * foot.length * scale;
|
||||
draw_list->AddLine(
|
||||
ImVec2(pos.x + off_x, pos.y + off_y),
|
||||
ImVec2(end.x + off_x, end.y + off_y),
|
||||
ImColor(0, 255, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class VRWindow : public Window {
|
||||
public:
|
||||
|
||||
VRWindow(SpiceOverlay *overlay);
|
||||
~VRWindow() override;
|
||||
|
||||
void build_content() override;
|
||||
void build_info();
|
||||
void build_dancefloor();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user