From f4120deb47ec37ee3e45e9d35585ab9274d7f821 Mon Sep 17 00:00:00 2001 From: theAstrogoth Date: Sun, 12 Apr 2026 19:03:24 -0500 Subject: [PATCH 1/3] trainer card detection and tid reading --- .../Menus/PokemonFRLG_TrainerCardDetector.cpp | 65 +++++++ .../Menus/PokemonFRLG_TrainerCardDetector.h | 52 ++++++ .../Inference/PokemonFRLG_TrainerIdReader.cpp | 159 ++++++++++++++++++ .../Inference/PokemonFRLG_TrainerIdReader.h | 47 ++++++ .../Source/PokemonFRLG/PokemonFRLG_Panels.cpp | 2 + .../PokemonFRLG_ReadTrainerId.cpp | 70 ++++++++ .../TestPrograms/PokemonFRLG_ReadTrainerId.h | 42 +++++ SerialPrograms/cmake/SourceFiles.cmake | 6 + 8 files changed, 443 insertions(+) create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp new file mode 100644 index 0000000000..493da5e996 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp @@ -0,0 +1,65 @@ +/* Trainer Card Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "PokemonFRLG/PokemonFRLG_Settings.h" +#include "PokemonFRLG_TrainerCardDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +TrainerCardDetector::TrainerCardDetector(Color color) + : m_box_tid_top(0.581250, 0.108413, 0.332692, 0.007212) // light blue (227, 255, 255) + , m_box_tid_right(0.914904, 0.117067, 0.005769, 0.064904) + , m_box_stripe_top(0.038942, 0.090384, 0.918269, 0.002885) // blue (94, 177, 255) + , m_box_stripe_right(0.954808, 0.095432, 0.002885, 0.109615) + , m_box_border_top(0.051442, 0.040625, 0.895192, 0.003606) // gray (100, 100, 120) + , m_box_border_right(0.968750, 0.075961, 0.002885, 0.848077) +{} +void TrainerCardDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_tid_top)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_tid_right)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_stripe_top)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_stripe_right)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_border_top)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_border_right)); +} +bool TrainerCardDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 image_tid_top = extract_box_reference(game_screen, m_box_tid_top); + ImageViewRGB32 image_tid_right = extract_box_reference(game_screen, m_box_tid_right); + ImageViewRGB32 image_stripe_top = extract_box_reference(game_screen, m_box_stripe_top); + ImageViewRGB32 image_stripe_right = extract_box_reference(game_screen, m_box_stripe_right); + ImageViewRGB32 image_border_top = extract_box_reference(game_screen, m_box_border_top); + ImageViewRGB32 image_border_right = extract_box_reference(game_screen, m_box_border_right); + + if ( is_solid(image_tid_top, { 0.308, 0.346, 0.346 }) + && is_solid(image_tid_right, { 0.308, 0.346, 0.346 }) + && is_solid(image_stripe_top, { 0.179, 0.337, 0.485 }) + && is_solid(image_stripe_right, { 0.179, 0.337, 0.485 }) + && is_solid(image_border_top, { 0.313, 0.313, 0.375 }) + && is_solid(image_border_right, { 0.313, 0.313, 0.375 }) + ){ + return true; + } + return false; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h new file mode 100644 index 0000000000..d1ecc27a58 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h @@ -0,0 +1,52 @@ +/* Trainer Card Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_MapDetector_H +#define PokemonAutomation_PokemonFRLG_MapDetector_H + +#include +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonTools/VisualDetector.h" +#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +// Detect the map screen for Kanto +class TrainerCardDetector : public StaticScreenDetector{ +public: + TrainerCardDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_box_tid_top; + ImageFloatBox m_box_tid_right; + ImageFloatBox m_box_stripe_top; + ImageFloatBox m_box_stripe_right; + ImageFloatBox m_box_border_top; + ImageFloatBox m_box_border_right; +}; +class TrainerCardWatcher : public DetectorToFinder{ +public: + TrainerCardWatcher(Color color) + : DetectorToFinder("TrainerCardWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + +} +} +} + +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp new file mode 100644 index 0000000000..1458a9fdec --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp @@ -0,0 +1,159 @@ +/* Trainer ID Reader + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "PokemonFRLG_TrainerIdReader.h" +#include "Common/Cpp/Color.h" +#include "Common/Cpp/Exceptions.h" +#include "CommonFramework/GlobalSettingsPanel.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/Tools/GlobalThreadPools.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonTools/Images/ImageManip.h" +#include "CommonTools/OCR/OCR_NumberReader.h" +#include "CommonTools/OCR/OCR_Routines.h" +#include "PokemonFRLG/PokemonFRLG_Settings.h" +#include "PokemonFRLG_DigitReader.h" +#include + +namespace PokemonAutomation { +namespace NintendoSwitch { +namespace PokemonFRLG { + +// Debug counter for unique filenames +static int debug_counter = 0; + +// Full OCR preprocessing pipeline for GBA pixel fonts. +// +// GBA fonts are seven-segment-like with 1-pixel gaps between segments. +// Pipeline: blur at native -> smooth upscale -> BW -> smooth BW -> re-BW -> pad +// +// The native blur connects gaps. Post-BW padding provides margins. +static ImageRGB32 preprocess_for_ocr( + const ImageViewRGB32 &image, + int blur_kernel_size, int blur_passes, + bool in_range_black, uint32_t bw_min, + uint32_t bw_max +){ + const bool save_debug_images = GlobalSettings::instance().SAVE_DEBUG_IMAGES; + int id = debug_counter++; + std::string prefix = "DebugDumps/ocr_tid_" + std::to_string(id); + + // Save raw input + if (save_debug_images){ + image.save(prefix + "_0_raw.png"); + } + + cv::Mat src = image.to_opencv_Mat(); + + // Step 1: Gaussian blur at NATIVE resolution with 5x5 kernel. + cv::Mat blurred_native; + src.copyTo(blurred_native); + if (blur_kernel_size > 0 && blur_passes > 0){ + for (int i = 0; i < blur_passes; i++){ + cv::GaussianBlur( + blurred_native, blurred_native, + cv::Size(blur_kernel_size, blur_kernel_size), 1.5 + ); + } + } + + // Save blurred at native res + ImageRGB32 blurred_native_img(blurred_native.cols, blurred_native.rows); + blurred_native.copyTo(blurred_native_img.to_opencv_Mat()); + if (save_debug_images){ + blurred_native_img.save(prefix + "_1_blurred_native.png"); + } + + // Step 2: Smooth upscale 4x with bilinear interpolation. + int scale_factor = 4; + int new_w = static_cast(image.width()) * scale_factor; + int new_h = static_cast(image.height()) * scale_factor; + cv::Mat resized; + cv::resize( + blurred_native, resized, cv::Size(new_w, new_h), 0, 0, + cv::INTER_LINEAR + ); + + // Save upscaled + ImageRGB32 resized_img(resized.cols, resized.rows); + resized.copyTo(resized_img.to_opencv_Mat()); + if (save_debug_images){ + resized_img.save(prefix + "_2_upscaled.png"); + } + + // Step 3: BW threshold on the smooth upscaled image. + ImageRGB32 bw = + to_blackwhite_rgb32_range(resized_img, in_range_black, bw_min, bw_max); + if (save_debug_images){ + bw.save(prefix + "_3_bw.png"); + } + + // Step 4: Post-BW smoothing -> re-threshold. + cv::Mat bw_mat = bw.to_opencv_Mat(); + cv::Mat smoothed; + cv::GaussianBlur(bw_mat, smoothed, cv::Size(7, 7), 2.0); + + // Re-threshold: convert smoothed back to ImageRGB32 and BW threshold. + ImageRGB32 smoothed_img(smoothed.cols, smoothed.rows); + smoothed.copyTo(smoothed_img.to_opencv_Mat()); + ImageRGB32 smooth_bw = to_blackwhite_rgb32_range( + smoothed_img, true, combine_rgb(0, 0, 0), combine_rgb(128, 128, 128)); + if (save_debug_images){ + smooth_bw.save(prefix + "_4_smooth_bw.png"); + } + + // Step 5: Pad with white border (Tesseract needs margins). + ImageRGB32 padded = pad_image(smooth_bw, smooth_bw.height() / 2, 0xffffffff); + if (save_debug_images){ + padded.save(prefix + "_5_padded.png"); + } + + return padded; +} + +TrainerIdReader::TrainerIdReader(Color color) + : m_color(color) + , m_box_tid(0.742683, 0.117314, 0.129734, 0.076006) +{} + +void TrainerIdReader::make_overlays(VideoOverlaySet &items) const { + const BoxOption &GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(m_color, GAME_BOX.inner_to_outer(m_box_tid)); +} + +uint16_t TrainerIdReader::read_tid( + Logger &logger, const ImageViewRGB32 &frame +){ + ImageViewRGB32 game_screen = + extract_box_reference(frame, GameSettings::instance().GAME_BOX); + + + ImageViewRGB32 tid_region = extract_box_reference(game_screen, m_box_tid); + + if (!GlobalSettings::instance().USE_PADDLE_OCR){ + // Tesseract-free path: waterfill segmentation + template matching + // against the PokemonFRLG/Digits/0-9.png templates. + return uint16_t(read_digits_waterfill_template(logger, tid_region)); + } + + // PaddleOCR path (original): preprocess then per-digit waterfill OCR. + ImageRGB32 ocr_ready = preprocess_for_ocr( + tid_region, 7, 2, true, + combine_rgb(0, 0, 0), combine_rgb(190, 190, 190) + ); + + // Waterfill isolates each digit -> per-char SINGLE_CHAR OCR. + return uint16_t(OCR::read_number_waterfill( + logger, ocr_ready, 0xff000000, + 0xff808080 + )); +} + +} // namespace PokemonFRLG +} // namespace NintendoSwitch +} // namespace PokemonAutomation + diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h new file mode 100644 index 0000000000..e8b00cf8cc --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h @@ -0,0 +1,47 @@ +/* Trainer ID Reader + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_TrainerIdReader_H +#define PokemonAutomation_PokemonFRLG_TrainerIdReader_H + +#include +#include +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/Language.h" + + +namespace PokemonAutomation{ + +class Logger; +class ImageViewRGB32; +class VideoOverlaySet; + +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class TrainerIdReader { +public: + TrainerIdReader(Color color = COLOR_RED); + + void make_overlays(VideoOverlaySet &items) const; + + // Reads the Trainer ID on the Trainer Card + uint16_t read_tid( + Logger &logger, const ImageViewRGB32 &frame + ); + +private: + Color m_color; + ImageFloatBox m_box_tid; + +}; + +} // namespace PokemonFRLG +} // namespace NintendoSwitch +} // namespace PokemonAutomation +#endif + diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index b6686e6b30..606ed485df 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -20,6 +20,7 @@ #include "Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" #include "Programs/TestPrograms/PokemonFRLG_ReadStats.h" +#include "Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h" #include "Programs/TestPrograms/PokemonFRLG_ReadEncounter.h" namespace PokemonAutomation{ @@ -61,6 +62,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- Developer Tools ----"); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp new file mode 100644 index 0000000000..04d7ec2ae9 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp @@ -0,0 +1,70 @@ +/* Read Trainer ID + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include +#include +#include "Common/Cpp/Color.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "Pokemon/Inference/Pokemon_NameReader.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h" +#include "PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h" +#include "PokemonFRLG_ReadTrainerId.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +using namespace std::chrono_literals; + + +ReadTrainerId_Descriptor::ReadTrainerId_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:ReadTrainerId", + Pokemon::STRING_POKEMON + " FRLG", + "Read TID", "", + "Read TID from the trainer card.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS +){} + +ReadTrainerId::ReadTrainerId() +{} + +void ReadTrainerId::program( + SingleSwitchProgramEnvironment &env, + ProControllerContext &context +){ + env.log( + "Starting Read Trainer ID program... Please ensure you are on the Trainer Card." + ); + + TrainerCardDetector detector(COLOR_RED); + TrainerIdReader reader; + VideoOverlaySet overlays(env.console.overlay()); + reader.make_overlays(overlays); + + VideoSnapshot screen = env.console.video().snapshot(); + bool trainercard = detector.detect(screen); + if (trainercard){ + env.log("Trainer Card detected."); + env.log("Reading TID..."); + uint16_t tid = reader.read_tid(env.logger(), screen); + env.log("TID: " + tid); + }else{ + env.log("Trainer Card not detected!"); + } + context.wait_for_all_requests(); +} + +} // namespace PokemonFRLG +} // namespace NintendoSwitch +} // namespace PokemonAutomation + diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h new file mode 100644 index 0000000000..5a0e533f03 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h @@ -0,0 +1,42 @@ +/* Read Trainer Id + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_ReadTrainerId_H +#define PokemonAutomation_PokemonFRLG_ReadTrainerId_H + +#include "CommonFramework/Tools/VideoStream.h" +#include "CommonTools/Options/LanguageOCROption.h" +#include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class ReadTrainerId_Descriptor : public SingleSwitchProgramDescriptor{ +public: + ReadTrainerId_Descriptor(); +}; + +class ReadTrainerId : public SingleSwitchProgramInstance{ +public: + ReadTrainerId(); + virtual void program( + SingleSwitchProgramEnvironment &env, + ProControllerContext &context + ) override; + + virtual void start_program_border_check( + VideoStream &stream, FeedbackType feedback_type + ) override{} + +}; + +} // namespace PokemonFRLG +} // namespace NintendoSwitch +} // namespace PokemonAutomation +#endif + diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 2479d90f97..5896270303 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1456,6 +1456,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Inference/Menus/PokemonFRLG_LoadMenuDetector.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h Source/PokemonFRLG/Inference/PokemonFRLG_SelectionArrowDetector.cpp @@ -1466,6 +1468,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Inference/PokemonFRLG_DigitReader.h Source/PokemonFRLG/Inference/PokemonFRLG_StatsReader.cpp Source/PokemonFRLG/Inference/PokemonFRLG_StatsReader.h + Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp + Source/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h Source/PokemonFRLG/Inference/PokemonFRLG_BattlePokemonDetector.cpp Source/PokemonFRLG/Inference/PokemonFRLG_BattlePokemonDetector.h Source/PokemonFRLG/Inference/PokemonFRLG_WildEncounterReader.cpp @@ -1498,6 +1502,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadStats.cpp Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadStats.h + Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp + Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadEncounter.cpp Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadEncounter.h Source/PokemonHome/Inference/PokemonHome_BallReader.cpp From 189e63c6db50e4f9bc2a650bf8efb9046d7c07d2 Mon Sep 17 00:00:00 2001 From: theAstrogoth Date: Mon, 13 Apr 2026 00:18:45 -0500 Subject: [PATCH 2/3] fix small typo --- .../Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h index 5a0e533f03..fff3c2c268 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.h @@ -1,4 +1,4 @@ -/* Read Trainer Id +/* Read Trainer ID * * From: https://github.com/PokemonAutomation/ * From c079530add83eb604b44cea482d97d757b93621c Mon Sep 17 00:00:00 2001 From: theAstrogoth Date: Mon, 13 Apr 2026 00:38:24 -0500 Subject: [PATCH 3/3] fix string addition --- .../Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp index 04d7ec2ae9..a2d365f1c9 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_ReadTrainerId.cpp @@ -57,7 +57,7 @@ void ReadTrainerId::program( env.log("Trainer Card detected."); env.log("Reading TID..."); uint16_t tid = reader.read_tid(env.logger(), screen); - env.log("TID: " + tid); + env.log("TID: " + std::to_string(tid)); }else{ env.log("Trainer Card not detected!"); }