RetroSharp.Tool
0.0.652
See the version list below for details.
dotnet tool install --global RetroSharp.Tool --version 0.0.652
dotnet new tool-manifest
dotnet tool install --local RetroSharp.Tool --version 0.0.652
#tool dotnet:?package=RetroSharp.Tool&version=0.0.652
nuke :add-package RetroSharp.Tool --version 0.0.652
RetroSharp
RetroSharp is a C#-inspired language, compiler, and zero-cost 2D framework for 8-bit game systems.
I'm making this in my free time to learn about compilers and old-school game hardware. It was supposed to make me happier; it has also managed to reach the shaved-head milestone.
See docs/ProjectOverview.md for the human-facing
overview of the language, framework, targets, and project objectives.
Project shape
RetroSharp has three layers that should stay separate:
- Language: target-neutral syntax, types, storage, constants, helpers, control flow, and zero-cost source ergonomics.
- Portable 2D SDK: capability-checked game APIs for frames, input, worlds, cameras, sprites, palettes, music, animation, and actors.
- Target intrinsics and lowering: Game Boy, NES, and future hardware-specific behavior.
The language should not know what a sprite, camera, tilemap, joypad, or palette register is. Those concepts belong in the SDK or in explicit target intrinsics.
Compilation paths
RetroSharp parses and prepares source through a shared target-neutral frontend,
then lowers it directly through one of two cartridge targets. The CLI requires
an explicit --target for standalone source files and manifests that do not
declare target or targets:
--target nes: emits an iNES mapper 0 ROM for static background/tile drawing plus tick-based input, logical sprites, horizontal camera streaming, four-screen 2-axis camera movement, TiledWorld.Load(...), runtime animation helpers, camera-relative runner collision, and VGM/VGZ-sourced 2A03 BGM playback. HUD and generic world-space collision are still not implemented.--target gb: emits a 32 KiB ROM-only Game Boy cartridge when the program fits, or an MBC1 banked ROM when large music assets need more space. It supports static background/map setup and a first runtime sprite loop subset with local byte-backed variables, assignment,if/else if/else,while,for, half-open rangefor,Video.WaitVBlank(), tick-based input polling,Scroll.Set(...), position-based camera X/Y scrolling with runtime row/column streaming,Sprite.Set(...), simple source-map tile queries for collision, joypad button queries, hUGETracker.ugeBGM playback,.gbapu/.gbapu.jsonAPU trace playback, and VGM/VGZ-sourced DMG playback through the same compact on-ROM trace repack. Bank selection for banked BGM data is emitted by the runtime; source code keeps usingMusic.Asset(...),Music.Play(...), andAudio.Update().
Both cartridge targets accept --runtime-abi-out <path> for a deterministic,
versioned JSON projection of their target-owned runtime layout and compiled
user-local symbols, bound to the emitted ROM content. It does not change ROM
bytes. tools/gameboy/runtime_abi.py and tools/nes/runtime_abi.py validate the
matching target contract, layout fingerprint, and ROM identity before
diagnostics consume an address. Game Boy runtime regions are fixed in
GameBoyRuntimeMemoryLayout, so its runtimeRegions collection is empty;
NES adds the ROM-specific regions allocated by its build.
Both cartridge targets accept --symbols-out <path> for a deterministic UTF-8
debugger symbol file. Each line has the MCP-compatible form AAAA name and
names may contain spaces. The export includes the target's runtime-memory
ranges and named addresses, compiled variables, and any ROM-specific runtime
regions reported by that target. It intentionally excludes ROM code labels,
fixed linker symbols, and banked segments in this first version. Because one
symbol file describes one target build, the option requires a single target
and cannot be combined with --world-budget-report.
For example, compile the runner and export both diagnostic sidecars:
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target gb \
--out /tmp/runner.gb \
--runtime-abi-out /tmp/runner.gb.runtime-abi.json \
--symbols-out /tmp/runner.gb.sym \
games/runner/runner.retrosharp.json
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target nes \
--out /tmp/runner.nes \
--runtime-abi-out /tmp/runner.nes.runtime-abi.json \
--symbols-out /tmp/runner.nes.sym \
games/runner/runner.retrosharp.json
In the matching Game Boy or NES debug MCP, call load_rom with the generated
ROM, load_symbols with its .sym file, then resolve_symbol or read_symbol
with a canonical name such as camera.XLow, camera.X,
packed camera.CommitCount, WorldPack.VisualSlot0, or player.x.
The actor framework acceptance slices live in samples/actor-framework and
samples/actor-contact-lifecycle. The former shows fixed pools, declarative
ActorPool enemies = Actors.Pool(...) runtime state,
EnemyDefinition Goomba = Enemies.Def(...) compile-time metadata with
qualified EnemyBehavior values, Tiled object-layer spawns, and runtime camera-window
activation. The latter proves transient top-contact capture, conditional defeat,
preserved behavior state, configured defeated lifetime, and slot release on Game
Boy and NES. Both lower without heap allocation or runtime dispatch. The
completed plan is docs/history/ActorFrameworkRoadmap.md; the current public
surface is in docs/Portable2DSdkV1.md.
The measured generated-code scaling work and CPU-budget plan live in
docs/history/GeneratedCodePerformanceRoadmap.md.
Language surface
Right now, RetroSharp can compile simple programs with:
- Basic arithmetic and logic operations
- Variables and assignments
- Decimal, hexadecimal (
0x2A), binary (0b1010_0000),_-separated, and width-suffixed (255u8,0x1234u16) integer literals - Type aliases normalized at compile time
- Compound assignment with
+=,-=,&=,|=, and^=, plus statement-only++and-- - Top-level and block-local constants with optional type annotations folded at compile time, including constant boolean and conditional expressions
sizeof(type)folded at compile time for primitive, reserved internal pointer-size marker, enum, and plain struct typesoffsetof(type, field)folded at compile time for direct fields of plain struct typescountof(array)folded at compile time for fixed-size local arrays- Top-level enums with qualified members folded at compile time
- Plain local structs with named and shorthand initializer lists plus field access in the current cartridge targets
- Restricted static classes with fixed-layout fields, instance methods lowered to receiver helpers, and static constants/methods lowered without heap allocation or dispatch
- Fixed-size local byte arrays with initializer lists, initializer-inferred lengths, constant index access, and byte-backed runtime index access in the current cartridge targets
- Function calls, including inline helper calls with parameters, named arguments, default parameter values, single-return expression helpers, and
=>expression-bodied helpers in the current cartridge targets - Control flow (
if/else if/else, no-fallthroughswitchwith multi-value and half-open range cases,while,do while,for, half-open rangefor,break,continue) - Half-open range membership expressions such as
tile in 1..4, lowered liketile >= 1 && tile < 4 - Short-circuit
&&/||and unary!conditions, plus byte-backed 0/1 logical value expressions, in the current cartridge targets - Conditional value expressions (
condition ? whenTrue : whenFalse) lowered to direct branches in the current cartridge targets - Byte-backed bitwise
&,|, and^, plus folded constant~masks, in the current cartridge targets - Explicit casts such as
(u8)expras validated zero-cost expression markers in the current cartridge targets - Canonical fixed-width types (
u8,i8,u16,i16,bool) plus source aliases where the current target accepts them - Immutable
letlocals with compile-time width inference:i16/u16fields, locals, helper results, and arithmetic keep two-byte storage; expressions proven byte-valued keep one byte - Public gameplay code stays at the SDK/resource level; raw buffers, hardware addresses, pointer member access, and
ptr<T>APIs remain reserved for internal SDK/backend addressability policy
let never narrows a statically word-valued expression. A width suffix on a
single literal remains authoritative, and an unsuffixed literal keeps the
existing u8 default. For non-literal expressions, an unambiguous i16 or
u16 operand makes the result use that same word type; otherwise a proven
byte-valued expression remains byte-backed. Mixing signed and unsigned word
values, or using an expression whose scalar type cannot be proven, is a
compile-time error; an explicit cast disambiguates the declaration. This rule
changes storage selection only and adds no runtime type metadata or dispatch.
Example program:
i16 Main()
{
return 2 * 3 * 4;
}
Framework surface
The portable 2D SDK gives game code a common vocabulary for tile-and-sprite
machines while keeping each target's limits explicit. Projects load
RetroSharp.Portable2D from manifest libraries and then use frame/input
calls, Tiled World.Load(...) with native tileset animation and a runtime background-animation toggle, collision flags, camera-relative AABB queries,
logical palettes, logical sprites, animation, music declarations, and
fixed-pool actor framework sugar. Standalone source files can still declare
import RetroSharp.Portable2D;; unknown imports are rejected. Generic compiler
and frontend APIs start with an empty SdkLibraryRegistry: a direct C# host
must compose every source library it wants to expose, including Portable2D.
The CLI is that host for normal command-line use: it supplies its bundled
Portable2D package alongside any --lib-path libraries. Pass
--no-default-libraries to build a source file or project without that bundle;
then use --lib-path and explicit imports for every library required.
A library package is a directory with retrosharp-library.json and one or more
RetroSharp source files:
{
"import": "Acme.Wait",
"rootNamespace": "Acme.Wait",
"sourceRoot": "src",
"namespaceMode": "physical",
"sources": [
"src/api.rs",
"src/timing/rules.rs"
],
"targets": [ "gb", "nes" ]
}
--lib-path may point at one package directory or at a directory containing
package subdirectories. Local paths remain the fastest editing workflow.
Versioned source libraries and SDKs
Projects can also restore source libraries and SDKs from one isolated NuGet feed.
Versions in packageDependencies are direct, exact versions; the CLI writes
the complete direct-and-transitive graph to the portable retrosharp.lock.json
and extracts packages under .retrosharp/packages. The same graph is prepared
once before a multi-target GB/NES build. A package must contain the ADR's
retrosharp-package.json with kind: "source-library" and its library
manifest at retrosharp/retrosharp-library.json.
An SDK package uses kind: "sdk-plugin" and includes that source manifest plus
its descriptor assembly under plugin/net10.0/. Declare it once to restore and
activate both parts:
{
"targets": ["gb", "nes"],
"sources": ["src/main.rs"],
"packageSources": ["./local-feed"],
"sdkDependencies": { "Acme.Probe": "1.0.0" }
}
packageDependencies remains for source-only packages. A transitively restored
SDK remains passive; declare it directly in sdkDependencies before importing
and using it. The host checks package identity/version and the source import
against the descriptor. An explicit RetroSharp.Portable2D package replaces
both bundled pieces. --no-default-libraries removes the bundle while retaining
explicit packages.
{
"targets": ["gb", "nes"],
"sources": ["src/main.rs"],
"packageSources": ["./local-feed"],
"packageDependencies": { "Acme.Timing": "1.2.3" }
}
The normal build only invokes restore if the local material is absent and never
rewrites an existing lock. Use --restore to create or intentionally update a
lock, --locked-mode to require it, and --no-restore or --offline to fail
without contacting the feed when material is absent. One feed needs no extra
mapping; multiple feeds must declare packageSourceMappings from each source
entry to its package-id patterns, so a failed feed cannot silently fall back to
another. Restore inspects archive paths and rejects build content and undeclared
executables. It never loads package DLLs, props, targets, tools, or scripts.
SDK entry points run only during explicitly activated builds.
For local development, use --lib-path and
--sdk-plugin-path <retrosharp-package.json-or-dll> together to replace one
product. External DLLs need package metadata; bare DLLs are invalid. A complete
local pair overrides a restored or bundled SDK; a partial override fails.
Sample and smoke builds that use the CLI bundle therefore pass neither path;
external SDK tests pass both paths with --no-default-libraries.
Source-only libraries cannot add new compiler semantics. When a feature needs a
new namespaced operation, resource, capability, validator, or per-target
lowering hook, the host registers a static in-process SDK plugin instead.
Pass an explicit trusted local plugin with repeatable
--sdk-plugin-path <dll-or-retrosharp-package.json>. External DLLs require
declarative package metadata; bare DLLs are rejected. --sdk-plugin <id> and
project plugin ids have no production registry. Plugin paths work with source and project builds,
resolve relative to the current working directory, and are intentionally not
stored in manifests. A loaded assembly exposes exactly one public
ISdkPluginEntryPoint with a public parameterless constructor. Unknown ids,
invalid assemblies, incompatible contracts, and duplicate ids are
rejected before compiling. Packaged plugins are checked through metadata and PE
references before loading; descriptor coherence is checked after construction.
Use --sdk-plugin-diagnostics to include technical exception details.
A registered plugin is
enforced per target: a target lowers a plugin operation only when it provides a
matching lowering hook and grants the operation's required capabilities,
otherwise the build fails before lowering with a diagnostic naming the plugin
feature and target id. The experimental reference plugin is
RetroSharp.Platformer2D (Platformer.GroundProbe()), which lowers on Game Boy
and is unsupported on NES. Path-loaded plugins execute arbitrary trusted code
inside the compiler process; there is no sandbox, implicit discovery, package
resolution, unloading, or stable cross-version binary compatibility promise.
See docs/SdkPluginBoundary.md.
Static plugins can also import declarative project resources at compile time. The project names the importer, asset identity, and path explicitly:
{
"plugins": ["RetroSharp.PushBox"],
"resources": [
{
"name": "levels",
"importer": "RetroSharp.PushBox.Levels",
"path": "assets/levels.sok"
}
]
}
Resource paths are resolved relative to the project manifest. The importer runs
inside the registered host plugin and contributes deterministic immutable byte
tables plus static u8(u8) lookup functions; Game Boy and NES retain ownership
of ROM placement and lookup emission. This is not a pre-build script or a
runtime parser.
When a library package opts into namespaceMode: "physical", folder names under
sourceRoot become compile-time namespaces just like project sources. Root
source files commonly act as the package's public facade after import, while
helper files under folders can reuse names without colliding with the game or
with other packages.
Portability is capability-based, not magic. A shared source file is portable
only when each selected target can support the requested scrolling mode, sprite
budget, palette slots, audio format, runtime tile writes, and asset shape. See
docs/Portable2DSdkV1.md for the current API
reference.
Project objectives
- Keep the language small, explicit, and independent from any one machine.
- Let the framework make small real 2D games practical without hiding hardware budgets.
- Keep raw machine access available through target intrinsics.
- Make Game Boy and NES the first compatibility pair for portable SDK work.
- Prefer zero-cost ergonomics over managed-runtime features: no heap, GC, RTTI, boxing, virtual dispatch, closures, delegates, or hidden object identity.
Which platforms does it compile for?
The active targets are Game Boy and NES, implemented under
src/RetroSharp.GameBoy and src/RetroSharp.NES. Runnable samples live under
samples/; only samples marked portable-sdk in samples/manifest.json are
cross-target SDK evidence.
The retired Z80 compiler path, its exact recovery commit, and the two external
forks it used are recorded in
docs/history/LegacyZ80Compiler.md.
Installation
RetroSharp is distributed as a .NET tool:
dotnet tool install --global RetroSharp
Then use it to compile your programs:
retrosharp --target gb --out myprogram.gb myprogram.rs
Build the Game Boy runner from source:
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target gb \
--out games/runner/bin/runner.gb \
games/runner/runner.retrosharp.json
For games split across several source files, use a RetroSharp project manifest. It is a small JSON file owned by RetroSharp, not an MSBuild project:
{
"targets": [ "gb", "nes" ],
"outputs": {
"gb": "bin/runner.gb",
"nes": "bin/runner.nes"
},
"rootNamespace": "Runner",
"sourceRoot": "src",
"namespaceMode": "physical",
"sources": [
"src/Program.rs",
"src/player/State.rs",
"src/camera/State.rs"
],
"libraryPaths": [
"lib"
],
"libraries": [
"RetroSharp.Portable2D"
]
}
Then build the project file directly:
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- retrosharp.json
Inspect a Tiled world's deterministic Game Boy or NES WorldPack and target
budgets without building or writing a ROM by opting into
--world-budget-report:
dotnet run --no-launch-profile --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target gb \
--world-budget-report \
samples/tiled-cross-target-2d-scroll/cross-target-2d-scroll.tmj
The option accepts one importer-ready .tmx or .tmj, requires --target gb or
--target nes, cannot be combined with --out, writes one deterministic
retrosharp.world-budget/v1 JSON object to stdout, and writes nothing to
stderr on success. Without this option, successful GB/NES stdout and stderr
remain unchanged. --no-launch-profile prevents the .NET launcher from
prepending its own status line to the CLI's JSON stdout.
The JSON reports source and hardware dimensions, metatile placements and
unique metatiles, chunk count, the exact visual/collision stored bytes from the
real WorldPack chunk directory, total serialized bytes, generated target
tiles, staging RAM, ROM/PRG bytes and required banks, and the bounded VBlank
tile/attribute commit shape. Diagnostics are always ordered as addressing,
rom-prg, chr-tile-count, staging-ram, and vblank; each carries measured
usage, the evaluated limit/profile, and an actionable remedy when it
overflows. selectedProfile is deliberately null: the report names current
and accepted-future profiles but never selects a banker or mapper.
stagingRam.usedBytes is the pack's actual fixed-slot shape;
stagingRam.limitBytes is the accepted WorldPack v1 maximum (554 bytes on GB,
594 on NES), while physicalRamCapacityBytes separately names the machine's
total RAM capacity. Addressing permits a 32,768-pixel extent because its last
coordinate is 32,767.
NES keeps nes-mapper-0-current separate from the accepted but unimplemented
nes-mmc3-tvrom-v1-accepted-future requirements. The report likewise keeps
currentPhysicalChrCapacityBytes,
acceptedFuturePhysicalChrCapacityBytes, the 8 KiB resident CHR limit, and the
256-entry tile-index budget as separate facts. Game Boy
cartridge.romPrgBytesUsed is measured content; allocatedRomBytes stays
null, so a padded output ROM length is never misreported as bytes used.
See how full an NES cartridge already is, and where the next change will land,
by adding --capacity-report to a normal NES build:
dotnet run --no-launch-profile --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target nes \
--capacity-report \
--out games/runner/bin/runner.nes \
games/runner/runner.retrosharp.json
The option requires --target nes, a single target, and cannot be combined with
--world-budget-report, which inspects a world without building. It writes one
retrosharp.nes-capacity/v1 JSON object to stdout and does not change a single
emitted byte: the ROM is identical with and without it.
The report leads with bindingConstraint, the scarce resource currently
closest to its own capacity. It also names the selected profile and PRG/CHR
size; the fixed PRG region's used bytes, remaining headroom and capacity; the
banked program's phase-to-bank map with per-bank occupancy and remaining R6
headroom; the bytes attributed to fixed veneers, pinned R7, boot R7 and
resident CHR; the top user-function duplication holders; and per-frame CPU work
for each named window. resources repeats fixed PRG, whole R6, the hot phase's
per-bank cap, and the CPU windows with the same vocabulary: name, unit, use,
capacity, headroom, binding status, next unit cost, and named remedy. Region
capacity is used plus headroom as the build measured it, not a re-derived board constant, and
bankedProgram is null for a program that is entirely fixed-resident. Shared
SDK subroutines and outlined user functions are counted with their call sites
rather than sized, because the build measures no byte total for either.
Two figures are deliberately labelled rather than trusted. duplication is a
lower bound: pure functions the compiler answered from a generated ROM table
never reach the user-function path, so it is not total recoverable headroom.
callsPerFrame is static: it is not path sensitive and does not multiply loop
iterations, so it is a lower bound where repeatsPerFrame is set and an upper
bound over mutually exclusive branches otherwise. cpuWork is an observation
per named window — frame is the whole NTSC frame, video-safe is the VBlank
window. The video-safe figure is the full cost the NES frame plan already
imposes, including NMI entry reserve, frame-boundary bookkeeping, packed column
commit, and retained-OAM publication. Other incomplete CPU windows still mean
uncalibrated work exists outside the known figure.
When any reported resource's headroom falls below five per cent, including the
hot phase's per-bank cap or a CPU window, the report adds a near-cliff warning
and the CLI repeats it on stderr. That is a warning, never a gate: spilling into
R6 and escalating the board are designed behaviours where allowed, so the build
still succeeds and nothing here can fail it.
Project paths are resolved relative to the JSON file. --target, --out, and
additional --lib-path options still work as command-line overrides. Use
project sources for code that belongs to the game itself; use
retrosharp-library.json packages when code should behave like an external,
imported dependency. libraryPaths tells the CLI where to discover local
packages; libraries names the package import paths that this project loads.
Source-level import still works as the explicit transitional form, but
C#-style project code normally declares loaded libraries in the manifest and
uses using only for namespace lookup inside source files.
When namespaceMode is physical, RetroSharp derives zero-cost source
namespaces from the path under sourceRoot: src/player/State.rs belongs to
Runner.Player, while src/camera/State.rs belongs to Runner.Camera. The
compiler lowers those names to unique internal symbols, so folders can contain
same-named helper classes such as Rules; source can refer to static constants
with path-qualified names like Player.Rules.Start.
Export a GBS subsong into a Game Boy APU trace when you want faithful playback of the original register writes:
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
gbs-to-gbapu \
--in path/to/theme.gbs \
--subsong 1 \
--seconds 120 \
--out path/to/theme.gbapu
This writes a compact binary .gbapu (use --emit-json for a JSON debug view, or gbapu-dump <file> to print the register writes). The loop is auto-detected and the capture is trimmed to one
loop body (--no-auto-loop/--loop-cycle override). Use the result directly with
Music.Asset(...). The source keeps cycle deltas for every supported APU register write; the ROM
compiler maps them onto DMG VBlank frames, removes redundant non-trigger writes, deduplicates
repeated frame groups into a pool, and stores full Wave RAM uploads as compact block commands for
Audio.Update() playback.
The helper requires gbsplay on PATH unless --gbsplay <path> is supplied. GBS files are not loaded directly by Music.Asset(...). See docs/GameBoyApuTraceFormat.md for the format shape, runtime use, limitations, and future alternatives.
Build the first cross-target camera sample for both cartridge targets:
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target gb \
--out /tmp/cross-camera.gb \
samples/cross-target-camera/camera.rs
dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
--target nes \
--out /tmp/cross-camera.nes \
samples/cross-target-camera/camera.rs
The sample sources have been migrated to the current language surface: symbolic const values and enums for static data, aliases where they clarify byte-backed values, let for immutable frame-local values, inline/pure helper contracts, SDK dot-calls, restricted static classes and receiver methods where they clarify ownership, switch expressions, pipelines, while (true) for infinite runtime loops, and compound mutation syntax where it maps directly to the old explicit assignments.
AI agent orientation
Repository-specific instructions live in AGENTS.md. For tasks that need
architecture, acceptance, or ownership context, docs/AgentContext.md routes
to the relevant owner document. Completed roadmaps are historical context, not
a startup bundle. llms.txt is the compact documentation index.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.1254 | 106 | 10/30/2026 |
| 0.0.1246 | 99 | 10/29/2026 |
| 0.0.1242 | 104 | 10/29/2026 |
| 0.0.1240 | 100 | 10/29/2026 |
| 0.0.1231 | 106 | 10/28/2026 |
| 0.0.902 | 121 | 10/13/2026 |
| 0.0.777 | 110 | 10/5/2026 |
| 0.0.769 | 111 | 10/5/2026 |
| 0.0.662 | 121 | 9/27/2026 |
| 0.0.661 | 119 | 9/24/2026 |
| 0.0.660 | 115 | 9/24/2026 |
| 0.0.659 | 116 | 9/24/2026 |
| 0.0.658 | 115 | 9/24/2026 |
| 0.0.657 | 112 | 9/24/2026 |
| 0.0.656 | 104 | 9/24/2026 |
| 0.0.655 | 122 | 9/24/2026 |
| 0.0.654 | 112 | 9/23/2026 |
| 0.0.653 | 119 | 9/23/2026 |
| 0.0.652 | 120 | 9/23/2026 |
| 0.0.651 | 118 | 9/23/2026 |
# Changelog 0.0.652
## Changes (initial history)
- Remove RetroSharp Azure validation pipeline (#969) (`36861561`)
- Unify RetroSharp release versions and package-backed game templates (#968) (`0b1f82a6`)
- Release: stage exact SDK package inventory locally (#967) (`40f76535`)
- SDK authoring: transactional dependencies, local overrides and templates (#966) (`736db8ae`)
- Plugins: isolate managed dependencies per SDK package (#965) (`482c0928`)
- SDK: compose restored source and plugin as one dependency (#964) (`e8dfdf55`)
- Separate runner-lift Mario assets for Game Boy and NES (#963) (`dee5be20`)
- Plugins: preflight declarative compatibility before loading (#962) (`3d67bb88`)
- CLI: restore locked NuGet source libraries (#961) (`efe3d5d0`)
- docs: decide SDK plugin distribution and ABI compatibility (#907) (#960) (`d66fd890`)
- Convert runner-lift theme to Game Boy DMG (#959) (`1f77b4b2`)
- Refactor Game Boy WorldPack runtime emitters (#953) (#958) (`6727e3f5`)
- Revert "Delete azure-pipelines.yml" (`c081316f`)
- Refactor NES WorldPack runtime emitters (#952) (#957) (`69d5e335`)
- Delete azure-pipelines.yml (`21273c71`)
- SDK: centralize generated source validation (#955) (`b44bef74`)
- Eliminar cinco residuos Tiled de runner y alinear documentación (#486) (#954) (`5ca2defd`)
- Move CPU-work calibrations to target owners (#942) (`4f7be674`)
- Preserve typed SDK storage identity through target resolution (#941) (`dd884bd9`)
- fix(gameboy): initialize camera queues for animation (#940) (`882c4485`)
- NES: relajar ramas fixed mediante el linker (#939) (`518d158c`)
- fix: repair GB cadence acceptance oracle (#938) (`fa7d6a8b`)
- Make games/update_roms scripts executable (#937) (`5972ea39`)
- Separate playable games into games/ and add update_roms script (#936) (`63693620`)
- fix: restore smooth NES runner-lift scrolling (#935) (`3849ea0c`)
- feat: support Tiled background colors on NES (#933) (`1b505e88`)
- docs: declutter agent context and prioritize fluid gameplay (#932) (`cb12e053`)
- perf: smooth GB/NES scrolling and gameplay (#930) (`23ca1305`)
- refactor: remove verified dead code and enforce full solution builds (#929) (`051dc42c`)
- Replace reflection in NesRuntimeMemoryLayout with static descriptors (#926) (#928) (`65befe75`)
- refactor(parser): rename SomeParser to RetroSharpParser (#927) (`a4d392be`)
- feat(portable2d): add automatic camera follow (`108852f9`)
- feat: add playable RetroSharp templates (`37955930`)
- Tune runner-lift thrown enemy momentum (`979dec76`)
- Fix runner-lift B carry and throw flow (`73a4c940`)
- fix(runner-lift): stabilize idle gameplay cadence (`bfa9d904`)
- fix(nes): support DPCM sound-effect priority (#912) (`b3887173`)
- feat(samples/runner-lift): integrate pull and throw sfx for NES and Game Boy (#911) (`a965565e`)
- feat(runner-lift): implement staged enemy lifting animation (#910) (`7b7157ab`)
- build: migrate CI/CD configuration to DotnetDeployer.Fleet (#906) (#909) (`1794afdd`)
- GitHub: remove PR/issue templates and PR workflow (`9e348e63`)
- SDK: complete portable defaults and final audit guards (#905) (`0461f830`)
- SDK: decouple Actor storage from compiler (#902) (`8721967f`)
- SDK: move Projectile pool defaults into PRODUCT (#901) (`4f64f448`)
- SDK: make Enemies.Def defaults and validation PRODUCT-owned (#900) (`bdb73157`)
- Integrate custom Mario sprite sheet with carrying and throwing animations (#899) (`4b6488db`)
- fix(tests): harden optimized test infrastructure (#898) (`e939fb0d`)
- feat(samples): add runner-lift sample with SMB2-style mechanics (#897) (`869d99db`)
- chore(samples): regenerate Runner and Snow Bros Jr ROMs and runtime ABIs (#893) (`9ac39625`)
- ARCH: audit and close Portable2D residual coupling and frozen-consumer invariants (#891) (`91592762`)