Expose a non-blocking TLS socket API to mods (WSS clients) without bundling
any game-specific multiworld content.
Co-authored-by: Cursor <cursoragent@cursor.com>
subprocess.run(..., capture_output=True, text=True) with no explicit
encoding falls back to locale.getpreferredencoding(False) -- the OS
default codepage. On Windows that's a legacy single-byte codepage
(e.g. cp1252), never UTF-8. When the LuaJIT dump contains a byte with
no mapping in that codepage, subprocess's internal _readerthread
crashes with an uncaught UnicodeDecodeError in a background thread; the
thread dies silently and the caller gets back stdout=None instead of a
string, crashing one line later with
AttributeError: 'NoneType' object has no attribute 'splitlines'.
Concretely, the Yellow-side imported dataset contains:
"_ColosseumHeightText" -> "...6’8” tall!"
The right double quotation mark (U+201D) encodes in UTF-8 as E2 80 9D;
0x9D has no defined character in cp1252, so decoding as cp1252 fails
outright. Verified against the real imported dataset: the Red/Blue-only
dump has zero bytes outside cp1252's defined range; the Yellow dump has
exactly one, at this row.
UTF-8 is the actual encoding these dumps are produced in -- the driver
Lua sources are read/written as UTF-8 throughout this file, and LuaJIT
writes those source strings' bytes back out verbatim -- so passing
encoding="utf-8" explicitly at the three affected call sites
(run_loader, check_data_dump, dump_dataset) is a no-op on platforms
whose default codepage is already UTF-8 (Linux/macOS) and a correctness
fix on Windows.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Yellow's field.oakSpeech manifest carried only shrink frames, so
OakSpeech.lua's `oakGfx.demoSpecies or "NIDORINO"` fallback fired and
the opening speech showed Nidorino's sprite and cry instead of the
player's Pikachu.
- Stamp demoSpecies "PIKACHU" in the Yellow import manifest (source of
truth for fresh ROM imports and developer builds).
- Stamp it in make_yellow_manifest.py so regeneration keeps the value.
- Repair stale Yellow caches in Data:applyVersionedFieldData() with a
fill-if-absent block, matching the #617 oldManBattle RATTATA pattern.
- Add parity test (manifest carries PIKACHU; stale cache filled;
pre-stamped value left alone).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The mon inspector now has a NICKNAME section: a text field with Set / Clear, commit-on-Enter and discard-on-Escape.
Input is gated at the field instead of refused at commit. Only glyphs the game
font can actually draw AND that round-trip through a .sav are accepted, capped
at the naming screen's 10 glyphs, so a bad keystroke or paste never appears and
Set always succeeds. Chars like "@" (the Gen1 string terminator), "#" and the
dakuten kana have codec entries but no font tile so they are blocked rather than silently mangled.
This fixes the nil value returned when running validate or pack on Fedora 43. Since Love isn't running, luajit calls on an empty table. Providing a stub table resolves the nil error.
The DEX grid now defaults to Pokedex-number order with an A-Z view, set
from two chips in the header. Sorting is view-only: it never dirties the
save, resets the scroll, and no-ops on a re-click. Ops.dexList builds the
order deterministically and sorts partial mod records last. Tests pin the
orderings against the real generated data.
The launcher spent ~9ms per frame building and drawing, and the Find Mods
tab could hang the window for minutes. Both had the same root cause: a
retained UI tree rebuilt every frame, and blocking curl calls made from the
draw path.
Replace the vendored FlexLove engine (28.5k lines) with src/ui/kit/ (Kit,
Theme, Layout, Loader). The kit caches Text objects and all measurement,
allocates nothing in the steady state, and draws flat. Build+draw is now
under 1ms at every window size and on every tab (POKEPORT_LAUNCHER_PROF).
Move every network call off the render thread onto a love.thread pool
(src/net/Fetch.lua): mod index fetches, per-mod release checks, find-tab
stats, thumbnails and mod installs. Mod indexes prewarm at boot so the
Find Mods tab is populated before it is opened.
Paginate every list -- mods, find, save slots, settings, release notes,
versions -- with the page size derived from the real viewport height, so a
500-mod index costs what a 10-mod one does. Scrolling is gone.
Anything that waits now raises a non-dismissable loader; per-row background
work shows an inline spinner instead. The in-app updater moves to the top
right beside the settings gear and pulses when an update is waiting.
Theme is black with white outlines, no gradients or glows, and solid
colour-coded embossed buttons with bold labels. The game tabs keep their
cartridge colours. Everything is 1.3x larger. The save editor shares the
theme, and adding an item there is now a searchable pop-up like adding a
Pokemon.
Also:
- Reset rebinds, in Settings and under Touch Controls. Rebinds are additive
(Input:applyBindings layers them over the defaults), so there was no
in-game way to undo one.
- Launch options: --game red [--slot N] / POKEPORT_GAME boots straight into
a game for shortcuts and frontends, falling back to that game's tab when
its ROM is not imported.
Fixes found while porting:
- Ellipsis and letterspacing truncated bytes, not codepoints, so a
multi-byte mod name crashed the first frame on a Japanese index.
Measurement no longer throws on malformed input either.
- The new font set missed UiFont's kana fallback, rendering translated
builds as tofu.
- Fetch workers idle in Channel:demand() and LOVE waits for live threads at
exit, so the process outlived the window; quitting mid-download also
waited on curl's 300s ceiling. Shut the pool down in love.quit and bound
its transfer timeouts.
- In one column the save-slot card drew below the fold, over the footer,
with no scrollbar left to reach it.
The two FlexLove engine tests guarded a scroll manager and an auto-height
propagation bug that no longer exist; replace them with a kit suite covering
page bounds, viewport sizing and UTF-8 truncation, and retarget the NX test
to assert the dependency is gone rather than that its perf guards are set.
Opening Touch Controls dropped the launcher virtual cursor and swallowed
gamepad input while touch still worked. Share PadCursor with the save
editor, forward pad events, and centralize overlay handoff/resume so both
hosts park and re-arm the pointer cleanly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bring FlexLove launcher UI ("big ui moment"), iOS picker dismiss, and Metal updates into the Switch NX branch. Keep pack_love (with libs/), NX inbox/rescan paths, SwitchDiagnostics, and Save Editor PadInput; port Scan again labels into LauncherView.
Co-authored-by: Cursor <cursoragent@cursor.com>
Route gamepad/touch into the editor instead of dropping them in editorMode,
and hit-test clicks at event coords so a finger tap is not lost under the
Joy-Con virtual cursor.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bring feat/switch-nx up to date with origin/dev (72 commits). Resolve
Input/RomImporter conflicts by keeping GamepadMap (NX face remap + dual-path
gate) while adopting upstream joyBindings rebinds (#632) and Enable-all mods
(#647). Gate shoulder GAME SPEED hotkeys when Select is held so Select+L
display chords still work.
Co-authored-by: Cursor <cursoragent@cursor.com>
Yellow's IsSurfingPikachuInParty swaps the player's overworld sheet to
a Pikachu-on-a-surfboard when the party mon that knows SURF is a
Pikachu. The recomp was missing both halves of this: the sheet was
never extracted, and the engine had no seam for the swap.
Extraction: SurfingPikachuSprite (gfx/sprites/surfing_pikachu.2bpp)
loads outside SpriteSheetPointerTable via
LoadSurfingPlayerSpriteGraphics2, the same bypass RedBikeSprite uses.
Added the symbol to the Yellow manifest and a parallel extract in
RomExtractor / build_rom_data / extract/sprites, minting
SPRITE_SURFING_PIKACHU. PaletteFX colors it (player OBP palette, same
as the bike).
Engine: new field.playerSprites.surfPikachu (default
SPRITE_SURFING_PIKACHU, guarded so before extraction the ride keeps
the Seel). Player.new caches surfPikachuSprite; pose() picks it when
surfing and the SURF-mon is a Pikachu. New
OverworldState:syncSurfingPikachu derives the flag from partyKnows at
every surf-state toggle (mount, dismount, fly, teleport, blackout,
forced-surf tile, boot-restore). Runtime-only, re-derived at load so a
party change between save and load is honored.
Lane B: RFC at docs/rfcs/0001-surfing-pikachu-sprite.md.
Backward-compatible — existing mods see no change (surf still defaults
to SPRITE_SEEL; surfPikachu only resolves on a Yellow import after
regeneration). Parity tests in tests/parity_surfing_pikachu_sprite.lua
(12/12) and tests/mod_world_tests.lua (19229/19229 with the new
boot-seed checks). tests/parity_cinnabar_east_surf.lua (24/24)
unchanged.
Regeneration required: re-run make_yellow_manifest.py against a
pret/pokeyellow checkout, then re-import the Yellow ROM.
Clarify that Mac+OpenMTP is contributor tooling (not the final product
contract), record Dusklight method references, and surface how/what we
tested so reviewers and the community can help finish the Switch port.
Co-authored-by: Cursor <cursoragent@cursor.com>
On compact Android viewports the item picker can end up with no list
space. Passing its negative height to setScissor crashes LÖVE.
Use an empty clip region instead.