Repository navigation
Conversation
- Rebuild the original build tree: ZAR resources become text .zrd files in the folders their compiler recorded (84 directories, including subfolders), interp.zbd becomes the gamegen *.gs and support\*.gw scripts with their original times, and sound banks become data\common\sounds WAVs with stored lower-quality variants. - Keep record order, residue bytes and timestamps in .zstudio layouts tied to source hashes. Accept an output only after it packs back byte-identically; other families (GameZ, anim, texture packs, image.zbd) are carried verbatim for now. All 58 files of the 1999 corpus, and the 1998 corpus, reconstruct and pack back exactly. - Define a lossless zReader text syntax (bit-exact floats, Latin-1 strings, dangling keys) and engine-exact gamegen script tokenization, with line-numbered parse errors. - Pack into a separate folder with staged, re-parsed, all-or-nothing writes, or verify without writing; report identical, changed and failed outputs. - Open text .zrd sources in the ZRD editor and save them as text, compile them on archive import, and show scripts as token text. - Add Tools menu commands and the source_reconstruct, source_pack and source_status MCP tools (85 tools), with documentation and tests.
Version 0.7.2 collects the source-project work and further features before release; the changelog entry stays unreleased until then.
- Publish packs with rollback: replaced and stale outputs move aside and are restored if any later step fails. - Refuse links on every path inside a pack folder, and pack folders created by another project (each project now has an identity). - Read a frozen project state: a file that changes during a pack fails it before anything is written. - Cancel a pack when the workspace root is replaced and never report its result into the new workspace. - Remove partial output after a canceled or failed reconstruction, and require RECOIL evidence in the source folder. - Bound text sources to 16 MiB before decoding, probe archive structure before source-file names, and bound every manifest field when a project loads.
…files Source projects are now separate from direct ZBD editing: the data/gamegen tree is authoritative and game files change only on export. Exports must work in the game rather than reproduce shipped bytes, so the project keeps no zStudio metadata (manifest, layouts, stored variants, passthrough copies and pack markers are removed) and outputs are derived from its folders. - Reconstruction writes text .zrd resources in their recorded folders, gamegen scripts with their index times, and each sound's best-quality WAV. Families not reconstructed yet are listed, not copied. - SourceBuilder plans outputs from the tree and checks or exports all or selected game files: archives compiled from zrdr folders, interp.zbd from gamegen, and the three sound banks converted to the sounds.zrd formats. Declared formats are per-component ceilings, as in every retail bank. Duplicate member or sound names are refused (the engine's lookups are linear case-insensitive scans). - Exports keep the frozen snapshot, staged re-parse and rollback publication, and replace existing game files only with explicit consent. - Tools menu: Export all ZBD files, Export ZBD file (one output) and Check source project. MCP: source_export (outputs, overwrite) replaces source_pack; source_status lists buildable outputs and inputs. - Corpus test: reconstruct, export and reconstruct again gives the same tree for zbd_1999 and zbd_1998, with equivalent members, scripts and sounds.
…tion Version 0.8.0 collects source-project reconstruction and export of textures, worlds and animations. Tools > Reconstruct source project starts with the open ZBD folder selected as the source.
Texture packs and image.zbd become PNG sources in their original folders, each from the best-quality copy across all packs and missions. Mission textures are placed by the sorted runs of the pack records (effects, common, mission with bft\, or the multiplayer vehicle folder), which matches the earlier corpus analysis for all 2,657 files. Interface images are placed by recorded IMAGE_PATH folders, objective and font/HUD references and their sorted runs; duplicated names keep one file per folder. A new pack builder writes hardware rtexture<N> packs in RGB565, software texture<N>/texturemax packs with shared palette pages (direct colour kept for vehicle skins and the WriteTextureSetMap damage masks), and image.zbd at authored size. It enforces the engine's power-of-two, 8-1024 and aspect rules, fits budgets by even halving, chooses opaque/keyed/alpha-plane storage and writes the Direct3D clamp word. Export adds rtexture8/16 and texturemax, which the engine supports but the game did not ship. Mission pack membership is interim (every PNG on the mission's texture search path) until world assembly supplies the referenced set. PngDecoder now reads every PNG colour type, depth, tRNS and Adam7 interlacing.
GameZWorld models a version-15 world: texture directory, material and display-instance pools, and the node table with class data and references. GameZWriter serializes it like CZZbd::WriteZBDFile (retail 0x454A50): the doubly linked material lists, model data at the absolute offsets the crater reload seeks, every node slot with class data, lists and free chains, and indices for all references. WorldUpdate recomputes what gwNodeUpdateAll derives before writing: model boxes and approximate spheres (with the engine's extended-precision intermediates), node model/child/cached bounds, the virtual area partition grid, cell membership with inclusion tolerance, overflow children and area height ranges. Every retail world of both corpora converts to the model and rewrites byte-identically except stale runtime pointers; recomputing its derived values reproduces the stored flags, bounds, spheres, cells and areas (21 child boxes differ in the last float bit).
GltfDocument reads and writes glTF 2.0 (JSON with external buffers, data URIs and GLB; TRS or matrix transforms; strips and fans; morph targets; extras). WorldGltf is the RECOIL profile: glTF nodes are object3d or lod nodes, shared meshes are shared display instances, materials carry the engine material and the polygon draw attributes (priority, back face, zone tag, flat shading), and engine-only values sit in extras.recoil when they differ from what the original loader produced (carried node flags, zones only where they change, LOD fields, model mode, lens-flare points). Duplicate names survive Blender through extras; Blender's .NNN suffixes are removed. ModelBuilder adds polygons like zDi::AddPolygonEx: degenerate corners are dropped (never the first), non-planar polygons fan, large ones split, vertices merge within 0.001 up to 921, normals within 0.0001, morph deltas stay beside their vertices, and textured UVs are shifted, extrapolated when not affine, and quantized to 1/256. Importing joins fan triangles back into convex coplanar polygons with affine UVs. Round-tripping every M1 and M6 subtree keeps all nodes and 94-97% of polygons exact; the rest are merged coplanar neighbours or split concave polygons that draw the same.
…scripts Reconstruction replays each mission's build scripts against its shipped world, undoes the scripts' edits, and writes every loaded model, external reference and mission database as glTF once, in the folder the scripts search. Export runs the scripts again (mN/gamez.zbd) and gives each mission's texture packs every texture its world uses, including ones a model brings from another mission's folders, with the edge modes from the glTF samplers. All retail worlds rebuild with no semantic differences, and exporting then reconstructing reproduces the source tree exactly. To get there, models keep polygons as the original build did (repeated corners, non-planar polygons, unextrapolated UVs), glTF records polygon groupings fan merging cannot recover, shared nodes survive as instances, and nodes keep gwNodeNew's name residue so an empty name never looks like a free slot.
Export builds mN/anim.zbd from the mission's animation definitions (data/mN/zrdr/anim.zrd and the files it lists) and .zan keyframe scripts, bound to the world the same export assembles: wildcard roots expand to matching nodes, definitions whose root the world lacks are left out, and names the game could not resolve are reported. No source stamps are written, since the game rejects a file whose stamped sources changed. Reconstruction writes each script event's keyframes as an object track in its .zan, where the shipped stamps place it. A few shipped definitions changed after anim.zbd was compiled; those are rebuilt from the compiled entries by a decompiler, so every shipped animation entry recompiles unchanged. The launch-direction rule, frame timing, rates and reference tables follow the shipped data exactly. The Launch child animation name field is 32 bytes, not 20.
When a source project is open, Tools > Open mission world builds a mission privately, as the export builds it, and shows it read-only in Whole world. Add model loads any project model into it, unplaced for resources such as aiv.zrd to place or placed at a position and heading, and lists the animation definition files other missions list for its name. Each edit, undo and redo rebuilds the world and keeps the camera; Save writes only gamegen/mN.gs and data/mN/zrdr/anim.zrd, refusing files changed on disk. Adds the source_world_open, source_world_models, source_world_definitions and source_world_add_model MCP tools; undo_redo, save_document and reload_document cover source worlds.
The preview and audio preload read CALL_ANIMATION names as 20 bytes, so launches of shipped animations with longer names (m6 reset_the_transporters, the plunger prerequisite triggers; 229 events in 1999, 224 in 1998) found no child, or a different animation whose name is the truncated prefix. The engine compares the whole field with the entry name (retail 0x45BC60), as the catalog now does.
- Effect templates: LoadZbd rejects the whole anim.zbd when an effect name is not in effects.zrd (retail 0x45F899, exact comparison at 0x462280). The export now warns for effects no effects.zrd of the project defines, as it already did for missing nodes. - Values that do not fit their stored field (EXECUTION_PRIORITY, MINIMUM_TO_SATISFY, LOOP_COUNT above 16 bits, more than 255 records in a table) are InvalidDataException errors naming the file. Checked casts used to throw OverflowException, which escaped the per-output failure handling and aborted a whole check, export or mission-world preview. - At most 32,767 entries: LoadZbd sign-extends the 16-bit count (retail 0x45F18C); 32,768 would allocate a negative size. - Bounded work: compiled output is capped at the 512 MiB document limit while it is built, each keyframe script is read and parsed once per compilation, scripts obey the 16 MiB text source limit, and definition file listings are limited to 10,000 reads. - Keyframe scripts: two keys at the same frame are a cut (zero rate) instead of a division by zero that failed with "Edited numbers must be finite"; zero quaternions are refused at their line. - Reconstruction notes a track whose object name a script cannot hold instead of aborting, and writes scripts as Latin-1, as they are read.
A placed model is attached by AddChild, which finds the newest node with its name; a model with a node of its own named like the addition (vtol.gltf under its default name vtol, for example) put that node in the world and left the placed root out. The preview build now checks that the script ran each addition's lines where it writes the world and that each placed root is a child of the world, and the assembler warns about shadowed roots. While a world rebuilds, other edits, undo/redo, save and reload of it are refused (busy) and their GUI commands disabled: a save during the rebuild wrote an edit the world was never built with, and an overlapping edit whose rebuild failed left the earlier edit pending but never shown. An edit whose rebuild fails in any way or is canceled (operation cancel, disconnection) is reverted; cancellation and malformed glTF sources used to leave it in the history. Save checks both managed files before writing either and restores the script if replacing the animation list fails; a refused save used to write the script already. Stale detection of a world's ~2,000 build inputs (about 100 ms per 3 s timer tick for retail m1) runs off the UI thread. The Add model dialog no longer sets a result after being closed during its definition lookup, and document state bounds each addition's definition files.
Document state's sourceWorld reports rebuilding, the state in which its other edits, undo/redo, save and reload return busy; the desktop check covers the flag and the disabled Save, Undo and Add model commands.
…ions Decoding PNGs while other threads forced compacting collections failed with AccessViolationException inside the per-pixel local function in most runs (pure managed code; every JIT mode, regions and segments GC, with or without zlib in the loop). The same conversion as plain static methods over a row did not fail in 14 runs and produces identical pixels.
…unded input Blender round trips of reconstructed models lost engine data: LOD records and scroll values mixing whole and fractional numbers come back as floats and failed to import, morph targets written as sparse accessors lost their deltas, flat surfaces gained normals, and point-only models (lens flares) were dropped because their meshes had no primitive. Import now accepts whole numbers in either form, applies sparse accessors, keeps flat materials flat unless they record normals, and point-only models travel in node extras. Names ending like a Blender copy keep their suffix. Malformed glTF files and extras now fail as InvalidDataException naming the file instead of escaping the build's per-output error handling; strides, offsets and decoded sizes are validated; hierarchies are depth-limited across external references and bounded to what a world can hold; embedded images are reported. Models never store more than 921 normals (the game transforms them into a 1,024-entry buffer), and non-finite geometry is discarded. Worlds read for reconstruction are refused when their links loop or run too deep, and partitions are limited to the cells the reader accepts.
…and lighting World builds and reconstruction traces now follow CZInterp::HandleBuiltinCommand: a condition holds only when its macro is exactly TRUE (IsMacroTrue), conditions combine with || and && (EvalConditionExpr), the skip depth is one counter across sourced files and nested conditions are ignored while skipping, and built-ins and macro names match case-sensitively. Macros set after the world is written still expand. Switches follow ParseBoolToken (only on/true; a missing argument or 1 is off), numbers follow atof, and NodeSetLighting applies to the whole subtree (AssignInt32ToDiRecursive). Ancestor checks and FindSubNode visit each node once and keep their own stack, so shared nodes cannot make them exponential, and a script-built hierarchy deeper than the update passes can follow is refused instead of exhausting the stack.
…indices Each primitive's material was compared with every material of the world, so a file with many surfaces took quadratic time; materials and textures are now found through per-load indexes that keep the first identical entry, as before. Texture names must be Latin-1 without path separators, since the world stores them in a Latin-1 field and the packs are built from files of that name. Worlds read for reconstruction are refused when a polygon names a missing vertex or normal, which the reader only warns about and the model export would index.
… model loads A macro that repeats itself doubled on every line and could grow without limit; expansion now stops at the retail interpreter's 1,024-byte buffer (ExpandMacroRefs), with one implementation shared by the build and the reconstruction trace. Scripts cannot create more nodes than a world holds, which keeps name lookups bounded, and model loads observe cancellation between nodes and batches of polygons.
NewNode, NewSEQ, FreeNode, NodeSetActive, Object3DAddChild, Object3DSetShowBackFace and Object3DSetMorphVertex change nodes or models in the game's interpreter; a build script that uses them before the world is written now gets a warning instead of a silently different world.
…erlap checks consistent - A source that fails in an unexpected way (a glTF with invalid JSON, for example) now fails its own output and the world's dependants instead of aborting the whole check or export with an unattributed exception. - Mission packs store a texture the world takes from outside the mission folders under the name the world uses, so a model whose image file has another name no longer leaves the world pointing at a missing texture. - Exporting refuses while an open mission world has unsaved additions, as for any unsaved project file. - Publication never replaces a game file that appeared during the export unless overwrite was given. - Destinations are compared with their input through drive roots and resolved paths (short names, SUBST, links above the input). - Reconstruction bounds a resource's text form while it is built instead of after. - An export whose workspace changed after it wrote its files says that the files were written.
…erting them to negative formats
…record the review The mission-world preview build treats any build exception of a non-world output as that output's failure, as exports do. Animation cleanup matches tracked nodes by their whole 36-byte name. The intermittent access violation seen in long runs was traced to one faulty CPU core of the development machine, not to the decoder; its comment no longer says otherwise.
Document RECOIL's original build work tree as interp.zbd and the other recorded evidence give it (docs/recoil-original-worktree.md) and make reconstruction follow it: a mission's own loads and every external reference stay in that mission's folders, a copy per mission as the textures are, only the pickups a shared script loads go to data/common/models, and every folder the scripts search is created.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3231a22164
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81a0ff2d42
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d4cd70589
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2806752a2f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92ff5017d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9dd2f84adb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee5a65a65a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e56e08825
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1add92cd7d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Report unimplemented world mutations, validate active world references, and keep source planning and inference bounded and cancellable. Add allocation, malformed-input, and GUI/MCP warning regressions. Refs #18.
Propagate terrain reference refusals, cap retained script history and diagnostic work, and share validated glTF/GLB reference discovery. Address adversarial findings in recovery journals, animation compilation and lookup, database ranking, numeric transforms, and Desktop ownership guards. Validate with independent full-base challenges, 2,128 passing solution tests with corpus data, 1998 reconstruction/export, and packaged live MCP checks. Retain the documented atomic-publication race limitations.
Store per-map zone profiles and logical model bindings in data/mN/meta, share zone-neutral geometry, and add interactive GUI/MCP zone drafts. Preserve assignments through supported terrain and structural edits and refuse ambiguous external topology changes. Include accumulated PR #18 review fixes for bounded parsing, lookup and preview work, source transactions, recovery, and GUI/MCP state handling. Validation: Release build with no warnings/errors; 2,838 Core, 188 Desktop, and 76 PR-watch tests passed; separate 1998 reconstruction/export passed. Portable packaging and live MCP validation passed with 107 tools. Checkpoint pushed at the user's explicit request before further independent review. Full adversarial review is incomplete; logical aliases in Add Model and an intermittent retired-journal cleanup failure remain recorded work.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Problem and resulting behavior
RECOIL's ZBD files are build outputs. The studio built them from a source tree (
data\and thegamegenscripts) that was never shipped. This branch reconstructs that tree, edits it as a world editor, and exports game files that work in the game. It also brings the zStudio features built on top of it intomainbefore further work continues. No related issue; this is the world-editor milestone (docs/world-editor-plan.md,docs/source-project.md).Source projects
.zrdfiles in their recorded folders.interp.zbdbecomes the original build scripts..zadfiles.docs/engine-evidence.md.World editor
Every capability has MCP parity. The catalog is regenerated from the registry, and
docs/mcp.md/docs/mcp-capabilities.jsondescribe the new tools and result fields. SeeCHANGELOG.md(0.8.0, unreleased; no version change or release with this PR).Verification
Local checks on the final head
ZSTUDIO_CORPUS,ZSTUDIO_MW3_CORPUS): 1,173/1,173 passed.SourceProjectCorpusTestsand the terrain-conversion corpus tests with the 1998 corpus: 8/8 passed.diff -rq, 5,748 and 5,003 files).Not covered
Adversarial review
Scope: all work on the branch since 2fa5074, reviewed in 14 cycles following
docs/code-review.md. The reviewers were fresh agents split by risk area, plus executable sweeps on fresh reconstructions of both releases.Sweeps, by evidence
Findings fixed: every P1/P2 the reviews found, each with a regression test that fails when the fix is removed. Every P3 open at the end of cycle 13 was then fixed in a dedicated round. Two items were kept by user decision or as protections: Initialize only before a root is opened, and projects below links refused. A final review cycle of those fixes found one P2 (script positions snapped like angles) and P3s, all fixed.
Final challenge pass: The last fix round had two more challenge passes. The first found one P2 (re-editing a transform that zStudio had set for one mission was refused), fixed with a regression. The second found no P1/P2; its P3s were fixed with mutation-checked regressions. Ledger rows kept as conservative: an edit is still refused for another mission's rotate/scale when the node keeps a transform of its own, and "may set" refusals for a FindNode by name.
Remaining limitations: