feat(nfc): NFC tags from the player's own dumps, with an NFC tab in the Insert menu #280
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!280
Loading…
Reference in a new issue
No description provided.
Delete branch "airdamien/Longshot:feat/nfc-tags"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Where the game asks for a figure's NFC tag, nothing happens in Longshot: the console's NFC library (
nn::nfp, 23 functions the game imports) is not there. This gives the game that library on tags of the player's own: dump files in a folder, chosen and "tapped" from a new NFC tab in the Insert menu, and real tags on a Switch 2 Pro Controller's reader.What changed
host/nn_nfp.c(new):nn::nfpon a pretended reader. The tags are files in the player's NFC folder (hos_nfc_dir:%LOCALAPPDATA%\Longshot\nfc,$XDG_DATA_HOME/longshot/nfc, orLONGSHOT_NFC_DIR): an NTAG215's 540 bytes (.bin) or a Flipper Zero NFC file (.nfc), checked inload_tag. Only the tag's ID and the figure's model are read from one (both plain on a tag): no key is needed or used. What the game writes to a tag (its application area) is kept in a file beside the saves (area_load,area_save), and no tag is written. The device's states follow what the game polls (GetDeviceState: searching, found, mounted).reader_main,reader_read,scan_run, Windows, over the controller pull request'shos_switch2_command): while the game waits for a tag and none of the files is held to the reader, a real tag on the controller is read and, when it is a figure's, given to the game; a scan copies one into the folder. The reader's commands are in the file's comment as the controller answered them.host/nn_os_sync.chos_event_init(an event of the OS layer's own making);host/rx_backend.candgpu/vk/vk_api.h:MkwVkHostgetsnfc_count,nfc_name,nfc_tap,nfc_state,nfc_dir,nfc_scan,nfc_scan_name(MKWVK_API_VERSION23: the controller pull request takes 22).gpu/vk/vk_menu.cpp: an NFC tab: the NFC tag row (the folder's files by name, listed again each time the menu opens:refresh_nfc), Tap tag, Remove tag, Scan tag on controller (nfc_press,nfc_scan_says).docs/nfc.md(new), README (the tab's rows and a paragraph),docs/development.md(three switches).add_file): it stopped at 256 while the folder was still being read and sorted after, so past 256 files it held whichever the folder gave first. Asked for in the review of the launcher's page (MagicBeanDev/LongshotLauncher#49 (comment)).tests/test_readme_menu_rows.pycovers the NFC tab's rows;tests/test_menu_keys.pygets a stub forrefresh_nfc.tests/test_nfc_list_cap.py(new):tag_ending,by_nameandadd_fileas they are in the source, fed 300 names in three orders; fails on the code before.Flags and settings
nfc_tag), Tap tag, Remove tag, Scan tag on controllerLONGSHOT_NFC_DIR=<folder>HOS_NFP_TRACE=1HOS_NFP_SEARCH=1Measurements
main:
fbd42024(2026-11-13)this PR: feat/nfc-tags @ 1dd5ec73 (the measured commit; b99145e0 after it added only the row in docs/tested-configurations.md)
Hardware: NVIDIA GeForce RTX 5080 (Vulkan driver 0x93cec000), Windows 11; a Switch 2 Pro Controller (firmware 1.1.5) plugged in over USB
Scene: the save's load point, Link standing still; 3840x2160 output, DLSS, frame generation 3x, the game's frame rate cap 60
Method: 150 s runs, the first 60 s left out, two interleaved runs each (main, this PR, ...). The numbers are
RX_VK_FRAME_LOG's frame times, a line per present (made frames included); the game's own rate islongshot_log.txt'svi:line.How to reproduce: the builds from the same dump with
nx2/make_game.py, the samelongshot_options.iniand save,HOS_INPUT_SCRIPT=12:A,15:A,...,42:A HOS_INPUT_SCRIPT_BACKGROUND=1 RX_VK_FRAME_LOG=<csv>.fbd42024The measured build was made before the branch was put on the controller pull request's last commit (a docs row); its code is the same.
Rebased since onto main @
514591a3(the branch's head is nowea2cbd4b) and not measured again: main's ten commits sincefbd42024are the website, docs, the forge script, a Damage taken row, an HDR route for FSR 4 that is opt-in, occlusion query numbering, quick pipelines and two input fixes; none is in this change's path, and the rebase of this pull request's own commit had no conflict. The rebased branch was built whole (nx2/make_game.py) and its checks run. Rebased again onto main @cf605c85(the head is nowf15350db) and not measured again: main's 34 commits since514591a3are renderer, host and docs work none of which is in this change's path (sparse VRAM mirrors, late readbacks, the recorder thread, Intel fixes, hold-to-toggle, a Mono choice, the caches' and logs' folders); this pull request's own commit conflicted only with the controller pull request's newrumble_test(the NFC functions follow it inMkwVkHost,MKWVK_API_VERSION23), and the measured run is now a file underdocs/tested/.Testing
Tested:
python -I -B tests/run_all.pyon Windows 11 (llvm-mingw): 478 passed, 7 failed, at the head (85d27e5e). The same 7 fail onmain@cf605c85on this PC (test_bake_portability,test_fpx_sticky,test_module_ids,test_oot_state_tap,test_pad_layout,test_spawn_emit,test_tested_configurations): local tools and packages, line endings in a pipe, and a fixture of main's that does not compile here, not this change.RX_VK_ONLY=1 nx2/build.py --standalonecompiles and linkslongshot_vk.so;host/nn_nfp.c,host/nn_os_sync.c,host/rx_backend.ccompile (-fsyntax-onlywith the build's includes).nfp: the game uses NFC tags: ...); the NFC tab lists the folder's files; Tap tag holds one to the reader (nfp: the NFC tag "..." held to the reader (model ...); the game is not asking for one now), Remove tag takes it away.HOS_NFP_SEARCH=1) and from the Scan row (nfp: scan: the tag (UID ...) was read, but it is not a figure's tag: not saved).nfp: the game waits for an NFC tagsix times, each followed bynfp: the game reads the NFC tag "..."andnfp: the game stopped waiting for an NFC tag; oncenfp: a tag read on the controller (model 0105000003580902): given to the game, a figure's real tag on the Switch 2 Pro Controller; tags tapped both ahead (... the game is not asking for one now: it stays there until it does) and while it waited (... the game was waiting for one). Nonn::nfpimport was left unimplemented and nothing crashed.nfp: scan: waiting ten seconds for a tag on the controller, thennfp: the tag on the controller was scanned into the folder as "Scanned <ID>" (model 0105000003580902); the 540-byte file is in the folder.Not tested:
Needs other testers:
Screenshots
The NFC tab, this PR @ 1dd5ec73, on pretended tags (
mainhas no such tab):Risk and rollback
nn::nfp::InitializeandAttachAvailabilityChangeEventat start before (logged as unimplemented imports, returning as they were); now they do something: a reader that is always there. A game path that waited on the reader's event now goes on.MKWVK_API_VERSIONgoes to 23: the exe and the renderer must be of one build, as at every bump.nfc_tag=<its place in the list>, as any row's; it means nothing to another build.Reviewer notes
#dev-workbeforehand.Issues
None.
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run (now a file underdocs/tested/:2026-11-13-nfc-tags-airdamien-run01.md).MKWVK_API_VERSIONbumped (gpu/vk/vk_api.h'sMkwVkHostorMkwVkRendererchanged).tests/that reads changed source updated in this PR, not weakened.🤖 Generated with Claude Code
Tested in play by the author: at the game's NFC screen the game read six tags in one session, from the Insert menu's NFC tab and, once, a figure's real tag on the Switch 2 Pro Controller's reader (the log's lines are in the description). The two requests that asked for that (T-3UQZ, T-LHBX) are withdrawn; one for Linux is open (T-KZYU). Marking it ready.
WIP: feat(nfc): NFC tags from the player's own dumps, with an NFC tab in the Insert menuto feat(nfc): NFC tags from the player's own dumps, with an NFC tab in the Insert menub99145e0baea2cbd4bb9Rebased with the controller pull request onto main @
514591a3(headea2cbd4b), no conflict in this one's own commit. Built whole and the checks run. Also tested since, by the author: Scan tag on controller saved a figure's real tag into the folder (nfp: the tag on the controller was scanned into the folder as "Scanned <ID>").ea2cbd4bb9f15350db31Rebased with the controller pull request onto main @
cf605c85(headf15350db). The NFC functions now follow that pull request'srumble_testinMkwVkHost:MKWVK_API_VERSION23. The measured run is a file underdocs/tested/. 477 passed; the 7 that fail also fail on main here.The review of the launcher's NFC page (MagicBeanDev/LongshotLauncher#49 (comment)) found that the game did not list "the first 256 by name":
add_file()stopped atNFC_MAXwhile the folder was still being read andlist_files()sorted after, so past 256 files the menu had whichever the folder gave first. Fixed here as asked (sort, then cap, in effect):85d27e5e. Once the list is full, a file takes the place of the last by name when it comes before it, so the list is the first 256 by name whatever order the folder gives.key_files and files that are no tag's by their ending are left out before that, as they were.tests/test_nfc_list_cap.pyruns the three functions as they are in the source on 300 names as sorted, reversed and shuffled, and fails on the code before (place 0 "Tag 0044", not "Tag 0000").tests/run_all.py: 478 passed; the 7 that fail also fail on main here. Not built whole or run in the game since this commit: the change is those few lines, compiled by the check with-Wall -Werror.85d27e5ef880cfd9c512View command line instructions
Manual merge helper
Use this merge commit message when completing the merge manually.
Checkout
From your project repository, check out a new branch and test the changes.