From c83496776e4dfa06f286e483d21fd269c331505c Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:25:34 -0700 Subject: [PATCH] cc: option for artificially delaying printer (#925) ## Link to GitHub Issue or related Pull Request, if one exists #0 ## Description of change Add an option to artificially delay the printing process (70 seconds or so, more for holo cards) so that the minigame is triggered on the screen. ## Testing --- src/spice2x/games/cardconnect/cardconnect.cpp | 5 +- src/spice2x/games/cardconnect/printer.cpp | 71 +++++++++++++++++-- src/spice2x/games/cardconnect/printer.h | 1 + src/spice2x/launcher/launcher.cpp | 3 + src/spice2x/launcher/options.cpp | 9 +++ src/spice2x/launcher/options.h | 1 + 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/spice2x/games/cardconnect/cardconnect.cpp b/src/spice2x/games/cardconnect/cardconnect.cpp index 520b3fae..80f32df0 100644 --- a/src/spice2x/games/cardconnect/cardconnect.cpp +++ b/src/spice2x/games/cardconnect/cardconnect.cpp @@ -20,9 +20,8 @@ namespace games::cardconnect { void CardConnectGame::attach() { Game::attach(); - // EXECEXE redirects Unity's raw-data writes under the game directory. - fileutils::dir_create_recursive_log("cardconnect", "game/dev/raw/log"); - if (!fileutils::bin_write("game/dev/raw/log/output_log.txt", nullptr, 0)) { + fileutils::dir_create_recursive_log("cardconnect", "dev/raw/log"); + if (!fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0)) { log_warning("cardconnect", "failed to create Unity output log"); } diff --git a/src/spice2x/games/cardconnect/printer.cpp b/src/spice2x/games/cardconnect/printer.cpp index 4c0a088e..1ece1709 100644 --- a/src/spice2x/games/cardconnect/printer.cpp +++ b/src/spice2x/games/cardconnect/printer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "games/shared/printer.h" @@ -20,6 +21,7 @@ namespace games::cardconnect { // Settings and native protocol values. bool PRINTER_DISABLED = false; + bool PRINTER_SIMULATE_DELAY = false; static constexpr uint16_t READY = 0; static constexpr uint16_t INVALID_PARAMETER = 1006; @@ -38,6 +40,21 @@ namespace games::cardconnect { INFO_IR_CORRECTION = 22, }; + enum PrinterMode : uint8_t { + MODE_YMC_MAT = 2, + MODE_YMC_LAM = 3, + MODE_YMC_MAT_HOLO = 5, + MODE_YMC_LAM_HOLO = 6, + MODE_IR_R_YMAC_MAT = 8, + MODE_IR_R_YMAC_LAM = 9, + MODE_R_YMAC_MAT = 10, + MODE_R_YMAC_LAM = 11, + MODE_YMC_GROSS = 18, + MODE_YMC_GROSS_HOLO = 21, + MODE_IR_R_YMAC_GROSS = 24, + MODE_R_YMAC_GROSS = 25, + }; + static constexpr uint16_t FORMAT_RGB = 3; static constexpr uint16_t FORMAT_BGR = 4; static constexpr uint16_t COLOR_COMPONENTS = 3; @@ -47,6 +64,25 @@ namespace games::cardconnect { static constexpr uint32_t RIBBON_REMAINING = 4096; static constexpr auto PRINT_TIME = std::chrono::seconds(5); + static constexpr std::optional get_mode_delay(uint8_t mode) { + // Printer::GetPrintTime adds 10s for holo and 8s per finished side. + if (mode == MODE_YMC_GROSS || mode == MODE_IR_R_YMAC_GROSS || mode == MODE_R_YMAC_GROSS) { + return std::chrono::seconds(0); + } + if (mode == MODE_YMC_MAT || mode == MODE_YMC_LAM || + mode == MODE_IR_R_YMAC_MAT || mode == MODE_IR_R_YMAC_LAM || + mode == MODE_R_YMAC_MAT || mode == MODE_R_YMAC_LAM) { + return std::chrono::seconds(8); + } + if (mode == MODE_YMC_GROSS_HOLO) { + return std::chrono::seconds(10); + } + if (mode == MODE_YMC_MAT_HOLO || mode == MODE_YMC_LAM_HOLO) { + return std::chrono::seconds(18); + } + return std::nullopt; + } + // Native reply layouts. #pragma pack(push, 1) struct FirmwareRecord { @@ -121,6 +157,8 @@ namespace games::cardconnect { bool receiving = false; // Submitted jobs stay busy while waiting for this baseline. bool awaiting_counter_baseline = false; + std::array mode_delays {}; + std::chrono::seconds print_time = PRINT_TIME; std::chrono::steady_clock::time_point next_copy; }; @@ -156,7 +194,7 @@ namespace games::cardconnect { while (job.remaining && now >= job.next_copy) { ++total_count; --job.remaining; - job.next_copy += PRINT_TIME; + job.next_copy += job.print_time; log_info("cardconnect::printer", "job {}: total count={}, remaining={}", job.id, total_count, job.remaining); } @@ -262,9 +300,10 @@ namespace games::cardconnect { // CoMain needs its post-cleanup baseline before any copy finishes. if (tag == INFO_PRINT_COUNT && job.awaiting_counter_baseline) { job.awaiting_counter_baseline = false; - job.next_copy = std::chrono::steady_clock::now() + PRINT_TIME; - log_info("cardconnect::printer", "job {}: counter baseline={}, starting print timer", - job.id, total_count); + job.next_copy = std::chrono::steady_clock::now() + job.print_time; + log_info("cardconnect::printer", + "job {}: counter baseline={}, starting print timer ({}s per copy)", + job.id, total_count, job.print_time.count()); } return TRUE; @@ -293,6 +332,25 @@ namespace games::cardconnect { return invalid_parameter(result, "setPrinterInfo"); } + if (PRINTER_SIMULATE_DELAY && (tag == INFO_FRONT_MODE || tag == INFO_BACK_MODE)) { + const auto mode = *static_cast(buffer); + const auto delay = get_mode_delay(mode); + if (!delay) { + log_warning("cardconnect::printer", "cannot estimate delay for print mode {}", mode); + return invalid_parameter(result, "setPrinterInfo print mode"); + } + if (!result) { + return set_result(result); + } + + std::lock_guard lock(printer_mutex); + if (tag == INFO_FRONT_MODE) { + job.mode_delays[0] = *delay; + } else { + job.mode_delays[1] = *delay; + } + } + return set_result(result); } @@ -346,6 +404,10 @@ namespace games::cardconnect { } job.id = next_page_id; + job.print_time = PRINT_TIME; + if (PRINTER_SIMULATE_DELAY) { + job.print_time = std::chrono::seconds(73) + job.mode_delays[0] + job.mode_delays[1]; + } job.error = READY; job.receiving = true; job.sides = {}; @@ -549,6 +611,7 @@ namespace games::cardconnect { namespace games::cardconnect { bool PRINTER_DISABLED = false; + bool PRINTER_SIMULATE_DELAY = false; } #endif diff --git a/src/spice2x/games/cardconnect/printer.h b/src/spice2x/games/cardconnect/printer.h index d61d3cee..1db0ef7f 100644 --- a/src/spice2x/games/cardconnect/printer.h +++ b/src/spice2x/games/cardconnect/printer.h @@ -2,5 +2,6 @@ namespace games::cardconnect { extern bool PRINTER_DISABLED; + extern bool PRINTER_SIMULATE_DELAY; void printer_attach(); } diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 6910549b..7c2d8b30 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1521,6 +1521,9 @@ int main_implementation(int argc, char *argv[]) { } games::cardconnect::PRINTER_DISABLED = options[launcher::Options::CardConnectNoPrinter].value_bool(); + if (options[launcher::Options::CardConnectPrinterDelay].value_bool()) { + games::cardconnect::PRINTER_SIMULATE_DELAY = true; + } if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) { rawinput::ENABLE_SMX_STAGE = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 0b8525d8..2768ab7e 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -2502,6 +2502,15 @@ static const std::vector OPTION_DEFINITIONS = std::invoke([]() .game_name = "Card Connect", .category = "Cab Peripherals", }; + c[launcher::Options::CardConnectPrinterDelay] = { + .title = "Card Connect Simulate Printer Delay", + .name = "cardconnectprinterdelay", + .desc = "Simulate real printing time: 73 seconds per card, plus hologram and coating delays " + "(up to 99 seconds). Off by default (5 seconds per card).", + .type = OptionType::Bool, + .game_name = "Card Connect", + .category = "Cab Peripherals", + }; c[launcher::Options::CardConnectPrinterOutputPath] = { .title = "Card Connect Printer Output Path", .name = "cardconnectprinterpath", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 743a380b..7f3e3a7d 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -256,6 +256,7 @@ namespace launcher { UDNNoIO, CardConnectArgs, CardConnectNoPrinter, + CardConnectPrinterDelay, CardConnectPrinterOutputPath, CardConnectPrinterOutputClear, CardConnectPrinterOutputOverwrite,