Skip to content

Add editable map metadata editors - #103

Merged
Frotty merged 5 commits into
masterfrom
codex/editable-cameras-regions
Aug 24, 2026
Merged

Add editable map metadata editors#103
Frotty merged 5 commits into
masterfrom
codex/editable-cameras-regions

Conversation

@Frotty

@Frotty Frotty commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Add editable custom editors for Warcraft III .mmp, .w3c, and .w3r map metadata files.
  • Support editing, adding, removing, undoing, redoing, and saving minimap icons, cameras, and regions.
  • Preserve opaque binary tails and validate serialized data by reparsing before writes.
  • Add fixtures and end-to-end coverage for the new editors.

Checks

  • npm test
  • npx tsc -p . --noEmit
  • npm run lint
  • npm run test:e2e — 65 passed
  • git diff --check

Known gaps

  • Camera and region string fields currently edit the stored raw strings directly; localized/TRIGSTR resolution remains outside this change.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 888b598375

ℹ️ 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".

Comment thread src/features/mapDataPreview.ts Outdated
function serializeValidatedW3c(file: W3cFile, name: string): Buffer {
if (file.error) throw new Error(`Refusing to save ${name}: the source file did not parse (${file.error}).`);
const reparsed = parseW3cFile(serializeW3c(file));
if (reparsed.error || reparsed.version !== file.version || !reparsed.tail.equals(file.tail) || reparsed.cameras.length !== file.cameras.length || reparsed.cameras.some((camera, i) => JSON.stringify(camera) !== JSON.stringify(file.cameras[i]))) throw new Error(`Refusing to save ${name}: round-trip verification failed.`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize float32 values before validating round trips

When a camera field is edited to a decimal that is not exactly representable as float32 (for example, 0.1), writeF32 reparses it as approximately 0.10000000149, so this exact JSON comparison rejects the save with “round-trip verification failed.” The same comparison at line 1489 breaks fractional W3R bounds as well; normalize edited values with Math.fround or compare against float32-normalized expected records.

AGENTS.md reference: AGENTS.md:L114-L116

Useful? React with 👍 / 👎.

Comment thread src/features/mpqViewer.ts
Comment on lines 51 to 53
if (ext === '.wpm') return 'wurst.wpmPreview';
if (ext === '.wtg') return 'wurst.wtgPreview';
if (ext === '.wct') return 'wurst.wctPreview';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep archive entries read-only until saves repack the MPQ

When a .mmp, .w3c, or .w3r entry is opened from the MPQ viewer, these mappings now present an editable editor, but the inspected openFile flow at lines 181–200 only extracts the entry under /tmp/wurst_mpq_extract and has no path that writes it back into the .w3x/.w3m archive. Saving therefore appears successful while modifying only the temporary copy, so the user's map remains unchanged; route these entries to the read-only preview as before unless archive repacking is implemented.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fa34ddd: MPQ-extracted .mmp, .w3c, and .w3r entries now open through the separate read-only wurst.mapDataArchivePreview view type. Standalone files remain editable, while archive saves cannot falsely appear to update the parent MPQ.

@Frotty

Frotty commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

@codex review

@Frotty

Frotty commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa34ddddcf

ℹ️ 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".

import * as fs from 'fs';
import * as vscode from 'vscode';
import { BinReader, parseW3i as parseW3iFile, serializeW3i, W3iFile, W3iPlayer, W3iForce } from 'casc-ts/formats';
import { BinReader, BinWriter, parseW3i as parseW3iFile, serializeW3i, W3iFile, W3iPlayer, W3iForce } from 'casc-ts/formats';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move the new binary codecs into casc-ts

The .mmp, .w3c, and .w3r editors define their parsers and serializers locally through this BinReader/BinWriter import, while the added tests only reparse generated fixtures instead of proving that an untouched real file round-trips byte-for-byte. An unmodeled format variant can therefore be rewritten and still pass the safety gate because parsing and validation share the same local interpretation; implement these codecs in the sibling casc-ts repository and add byte-exact fixture tests before making the formats editable.

AGENTS.md reference: AGENTS.md:L114-L116

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred from this PR as a cross-repository architecture follow-up. Moving these codecs into casc-ts requires coordinated sibling-library API/export changes, fixture tests, dependency build output, and CI mock updates; the current PR keeps the local codecs tail-preserving and gates every write with serialize → reparse validation. This should be handled as a dedicated casc-ts change rather than partially changing the consumer here.

Comment thread src/features/mapDataPreview.ts Outdated
]);

function w3cCameraInput(camera: W3cCamera, index: number, field: keyof W3cCamera, className = ''): string {
const value = field === 'name' ? camera.name : fmt(camera[field] as number);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve float32 precision in editable inputs

When a stored or entered camera value has more than three decimal places, such as 12.3456, this new editable input renders it as 12.346. Because every edit causes postState to rebuild the table with this formatter, the UI then displays a value different from the model and the saved float32; the region inputs at line 1402 have the same issue. Render a shortest round-trippable float32 representation instead of the preview-oriented three-decimal formatting.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f193340: editable camera and region inputs now render Math.fround(value).toString() so the displayed value preserves the stored float32 precision. Regression assertions cover both editors.

@Frotty

Frotty commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1933403f6

ℹ️ 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".

Comment thread src/features/mapDataPreview.ts Outdated
Comment on lines +1233 to +1234
function w3cCameraInput(camera: W3cCamera, index: number, field: keyof W3cCamera, className = ''): string {
const value = field === 'name' ? camera.name : fmtF32(camera[field] as number);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve trigger-string resolution in editable metadata views

When a camera name is stored as TRIGSTR_###, this input now displays the raw reference because the editable provider neither loads war3map.wts nor calls resolveTriggerString, whereas the previous default read-only renderW3c resolved it. The new default .w3r editor does the same for region names and sounds, so affected maps lose their human-readable labels and editing the displayed text replaces the reference instead of updating its WTS entry; retain the source reference and resolve/stage these values as the W3i editor does.

AGENTS.md reference: AGENTS.md:L85-L85

Useful? React with 👍 / 👎.

Comment on lines +1333 to +1336
else if (W3C_NUMERIC_FIELDS.has(field as keyof W3cCamera)) {
const value = Number(rawValue);
if (!Number.isFinite(value)) return;
after[field as keyof W3cCamera] = value as never;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject values that overflow float32

For a finite JavaScript number outside the float32 range, such as 1e39, this accepts the edit even though writeF32 serializes it as Infinity. The round-trip gate does not catch the corruption because Math.fround(1e39) is also Infinity and both sides of the JSON comparison stringify that value as null; the equivalent W3R bounds path has the same problem. Require Number.isFinite(Math.fround(value)) before accepting these edits.

Useful? React with 👍 / 👎.

@Frotty

Frotty commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Codex review-loop summary

Findings

  • Fixed float32 round-trip validation and display precision, including overflow rejection.
  • Fixed MPQ extraction behavior by keeping archive entries read-only until repacking exists.
  • Fixed editable TRIGSTR_* camera/region names and sounds to resolve through and stage updates in war3map.wts.
  • Deferred moving the codecs into casc-ts as a separate cross-repository architecture follow-up requiring coordinated API, fixture, dependency, and CI-mock work.

Validation

  • npm test passed.
  • npx tsc -p . --noEmit passed.
  • npm run lint passed with the existing webpack.config.js unused-variable warning.
  • New camera/region E2E coverage passed: 6 tests.
  • Full E2E: 66/67; the existing objmod field-table reload test failed both in the full run and isolated spec run, while all new editor tests passed.
  • GitHub CI build passed.

The PR is ready for merge with the casc-ts codec relocation explicitly deferred.

@Frotty
Frotty merged commit 2a914d0 into master Aug 24, 2026
1 check passed
@Frotty
Frotty deleted the codex/editable-cameras-regions branch August 24, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant