diff --git a/.gitignore b/.gitignore index d2651b2..dff58de 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,7 @@ subprojects/tomlc99 subprojects/SDL2-2.26.5 subprojects/xxHash-0.8.2 subprojects/safetyhook +subprojects/zxing-cpp-2.2.1 +subprojects/stb dist.7z .vscode \ No newline at end of file diff --git a/dist/config.toml b/dist/config.toml index cb23b76..2353a0a 100644 --- a/dist/config.toml +++ b/dist/config.toml @@ -22,6 +22,7 @@ unlock_songs = true [qr] data = { serial = "", type = 0, song_no = [] } +image_path = "" [drum] wait_period = 4 diff --git a/dist/keyconfig.toml b/dist/keyconfig.toml index 2952a72..410edfc 100644 --- a/dist/keyconfig.toml +++ b/dist/keyconfig.toml @@ -10,6 +10,7 @@ COIN_ADD = ["ENTER", "SDL_START"] CARD_INSERT_1 = ["P"] CARD_INSERT_2 = [] QR_DATA_READ = ["Q"] +QR_IMAGE_READ = ["W"] P1_LEFT_BLUE = ["D", "SDL_LTRIGGER"] P1_LEFT_RED = ["F", "SDL_LSTICK_PRESS"] diff --git a/meson.build b/meson.build index 9c0f8c0..0c1f4a1 100644 --- a/meson.build +++ b/meson.build @@ -31,6 +31,8 @@ tomlc99 = subproject('tomlc99') sdl2 = subproject('sdl2', default_options: ['default_library=static', 'test=false', 'use_render=disabled']) xxhash = subproject('xxhash', default_options: ['default_library=static', 'cli=false']) safetyhook = subproject('safetyhook') +stb = subproject('stb') +zxing_dep = dependency('ZXing') library( 'bnusio', @@ -49,6 +51,9 @@ library( xxhash.get_variable('inc'), safetyhook.get_variable('safetyhook_inc'), ], + dependencies: [stb.get_variable('stb_dep'), + zxing_dep, + ], sources : [ 'src/dllmain.cpp', 'src/helpers.cpp', diff --git a/src/bnusio.cpp b/src/bnusio.cpp index 2f1a197..268ea55 100644 --- a/src/bnusio.cpp +++ b/src/bnusio.cpp @@ -30,6 +30,7 @@ Keybindings COIN_ADD = {.keycodes = {VK_RETURN}, .buttons = {SDL_CONTROLLER Keybindings CARD_INSERT_1 = {.keycodes = {'P'}}; Keybindings CARD_INSERT_2 = {}; Keybindings QR_DATA_READ = {.keycodes = {'Q'}}; +Keybindings QR_IMAGE_READ = {.keycodes = {'W'}}; Keybindings P1_LEFT_BLUE = {.keycodes = {'D'}, .axis = {SDL_AXIS_LEFT_DOWN}}; Keybindings P1_LEFT_RED = {.keycodes = {'F'}, .axis = {SDL_AXIS_LEFT_RIGHT}}; Keybindings P1_RIGHT_RED = {.keycodes = {'J'}, .axis = {SDL_AXIS_RIGHT_RIGHT}}; @@ -348,6 +349,7 @@ Init () { SetConfigValue (keyconfig, "CARD_INSERT_1", &CARD_INSERT_1); SetConfigValue (keyconfig, "CARD_INSERT_2", &CARD_INSERT_2); SetConfigValue (keyconfig, "QR_DATA_READ", &QR_DATA_READ); + SetConfigValue (keyconfig, "QR_IMAGE_READ", &QR_IMAGE_READ); SetConfigValue (keyconfig, "P1_LEFT_BLUE", &P1_LEFT_BLUE); SetConfigValue (keyconfig, "P1_LEFT_RED", &P1_LEFT_RED); diff --git a/src/patches/qr.cpp b/src/patches/qr.cpp index 6916ca6..dc6c5f0 100644 --- a/src/patches/qr.cpp +++ b/src/patches/qr.cpp @@ -1,23 +1,27 @@ #include "constants.h" #include "helpers.h" #include "poll.h" +#include #include #include #include #include #include +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" extern GameVersion gameVersion; extern Keybindings CARD_INSERT_1; extern Keybindings CARD_INSERT_2; extern Keybindings QR_DATA_READ; +extern Keybindings QR_IMAGE_READ; extern char accessCode1[21]; extern char accessCode2[21]; namespace patches::Qr { enum class State { Ready, CopyWait }; -enum class Mode { Card, Data }; +enum class Mode { Card, Data, Image }; State gState = State::Ready; Mode gMode = Mode::Card; std::string accessCode; @@ -48,16 +52,15 @@ HOOK_DYNAMIC (i64, __fastcall, copy_data, i64, void *dest, int length) { if (gState == State::CopyWait) { std::cout << "Copy data, length: " << length << std::endl; - auto configPath = std::filesystem::current_path () / "config.toml"; - toml_table_t *config = openConfig (configPath); + auto configPath = std::filesystem::current_path () / "config.toml"; + std::unique_ptr config_ptr (openConfig (configPath), toml_free); + toml_table_t *config = config_ptr.get (); if (gMode == Mode::Card) { - if (config) toml_free (config); - memcpy (dest, accessCode.c_str (), accessCode.size () + 1); gState = State::Ready; return accessCode.size () + 1; - } else { + } else if (gMode == Mode::Data) { std::string serial = ""; u16 type = 0; std::vector songNoes; @@ -72,7 +75,6 @@ HOOK_DYNAMIC (i64, __fastcall, copy_data, i64, void *dest, int length) { songNoes = readConfigIntArray (data, "song_no", songNoes); } } - toml_free (config); } BYTE serial_length = (BYTE)serial.size (); @@ -104,6 +106,47 @@ HOOK_DYNAMIC (i64, __fastcall, copy_data, i64, void *dest, int length) { memcpy (dest, byteBuffer.data (), byteBuffer.size ()); gState = State::Ready; return byteBuffer.size (); + } else { + const char *imagePath = ""; + + if (config) { + auto qr = openConfigSection (config, "qr"); + if (qr) imagePath = readConfigString (qr, "image_path", imagePath); + } + + if (!std::filesystem::is_regular_file (imagePath)) { + std::cerr << "Failed to open image: " << imagePath << " (file not found)" + << "\n"; + gState = State::Ready; + return 0; + } + + int width, height, channels; + std::unique_ptr buffer (stbi_load (imagePath, &width, &height, &channels, 3), stbi_image_free); + if (!buffer) { + std::cerr << "Failed to read image: " << imagePath << " (" << stbi_failure_reason () << ")" + << "\n"; + gState = State::Ready; + return 0; + } + + ZXing::ImageView image{buffer.get (), width, height, ZXing::ImageFormat::RGB}; + auto result = ReadBarcode (image); + if (!result.isValid ()) { + std::cerr << "Failed to read qr: " << imagePath << " (" << ToString (result.error ()) << ")" + << "\n"; + gState = State::Ready; + return 0; + } + + std::cout << "Valid" << std::endl; + auto byteData = result.bytes (); + std::cout << ZXing::ToHex (byteData) << std::endl; + auto dataSize = byteData.size (); + + memcpy (dest, byteData.data (), dataSize); + gState = State::Ready; + return dataSize; } } return 0; @@ -132,6 +175,10 @@ Update () { std::cout << "Insert" << std::endl; gState = State::CopyWait; gMode = Mode::Data; + } else if (IsButtonTapped (QR_IMAGE_READ)) { + std::cout << "Insert" << std::endl; + gState = State::CopyWait; + gMode = Mode::Image; } } } diff --git a/subprojects/packagefiles/stb/meson.build b/subprojects/packagefiles/stb/meson.build new file mode 100644 index 0000000..990277b --- /dev/null +++ b/subprojects/packagefiles/stb/meson.build @@ -0,0 +1,6 @@ +project('stb', 'cpp') + +depinc = include_directories('.') +stb_dep = declare_dependency( + include_directories: depinc, +) diff --git a/subprojects/stb.wrap b/subprojects/stb.wrap new file mode 100644 index 0000000..8071b88 --- /dev/null +++ b/subprojects/stb.wrap @@ -0,0 +1,8 @@ +[wrap-git] +directory=stb +url=https://github.com/nothings/stb.git +revision=head +patch_directory = stb + +[provide] +stb = stb_dep diff --git a/subprojects/zxing.wrap b/subprojects/zxing.wrap new file mode 100644 index 0000000..35976cd --- /dev/null +++ b/subprojects/zxing.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory=zxing-cpp-2.2.1 + +source_url=https://github.com/zxing-cpp/zxing-cpp/archive/refs/tags/v2.2.1.zip +source_filename=zxing-cpp-2.2.1.zip +source_hash=71d9288f0637d321ee6823d8c27e684e9a00d4ffb92f18aff95fb5342cb6521d +method = cmake + +[provide] +ZXing=ZXing_dep +