| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Interop tools for Electronics Workbench / Multisim / Ultiboard binary formats. The goal: get designs and part databases out of EW into mainstream representations (and back, where the format permits).
| Key | Extension(s) | Kind | Decode output |
|---|---|---|---|
| ewprj | .ewprj | compressed-xml | <file>.xml |
| multisim | .ms10 … .ms19 | compressed-xml | <file>.xml |
| mdb | .prj, .usr | mdb | <file>.json |
PKWare DCL Implode (ASCII literal mode, large dictionary) wrapped in a small container: a magic-string header, a 64-bit LE total decompressed length, then a sequence of (decompressed_length: u32, compressed_length: u32, pkware_implode_block) sections. Multisim chunks big files into 900 000-decompressed-byte sections; ewe mirrors that.
Decode and encode are both supported. A decode/encode/decode round-trip on every sample tested produces byte-identical XML, and the resulting files are accepted by ewd.
NI ships part libraries and project component databases as Microsoft Access Jet 3 / Jet 4 files (despite the .prj / .usr extensions). ewd reads them via mdb-reader and emits a single JSON document with every table:
{
"format": "mdb",
"source": "Stocked.prj",
"tables": {
"SYS_COMPONENT": {
"columns": [{ "name": "Component_ID", "type": "long" }, ...],
"rows": [{ "Component_ID": 17, "Component_Name": "BD9763FVM", ... }, ...]
},
...
}
}Dates become ISO 8601 strings; binary blob fields (ole columns) become { "_bytes": "base64", "value": "..." } envelopes so the data survives a JSON round-trip.
Encode is a deliberately narrow byte-patch path, not a real Jet writer. Reimplementing Access's storage engine (page splits, B-tree index maintenance, free-space tracking, MSysObjects invariants) is out of scope for this project — ewd/ewe exist to move data out of Electronics Workbench, not to clone Microsoft's database engine.
What ewe does on an mdb input today:
You decode with ewd to produce <file>.prj.json.
You edit the JSON in your text editor (or with a script).
You run ewe edited.json. It diffs the edited JSON against the original .prj, then for each changed cell where:
it overwrites the bytes in place. Anything that violates those constraints is skipped, and --verbose prints the reason per cell.
This is enough for the typical vendor / price / MPN editing workflow because those values are usually unique and you usually substitute one same-length identifier for another. It is not a general-purpose editor and we don't intend to grow it into one.
Not planned in this repo. The clean way to add it is a separate, optional, Windows-only tool that drives Microsoft's own Access engine:
That path requires Windows + a driver install and so deliberately lives outside this cross-platform repo's CI. The current byte-patch encode covers the vendor/price editing case; for anything harder, route through Access (or the ACE driver, or DAO/ADO) instead of expecting ewe to grow into a full Jet writer.
ewd identifies the format by reading the magic bytes at the start of the file, so the filename extension can be anything (renamed files, no extension, .dat, etc.). It then dispatches to the right decoder.
ewe infers the format from the output filename's extension; pass --format <key> to override for non-standard extensions. Out-of-range Multisim versions (e.g. .ms9 or future .ms20+) need an explicit --format multisim.
npm i -g electronics-workbench-decoder # puts `ewd` and `ewe` on your PATH
# …or run without installing:
npx -p electronics-workbench-decoder ewd MyDesign.ms14The published package is a self-contained bundle that runs on Node ≥ 18 — you don't need bun to use it. Installed this way, invoke the tools directly as ewd / ewe (the bun run ewd form below is for working from source).
Requires bun (≥ 1.0).
bun installbun run ewd --verbose ./samples/Temp.ewprj ./samples/Design1.ms14
bun run ewd --verbose ./samples/Stocked.prj # writes Stocked.prj.jsonFor each input, writes <filename>.xml (compressed-xml) or <filename>.json (mdb) next to it.
Options:
bun run ewe --verbose ./samples/Temp.ewprj.xml
# or with explicit output:
bun run ewe --output ./out.ewprj ./samples/Temp.ewprj.xml
# or force a format on a non-standard extension:
bun run ewe --format multisim --output ./out.dat ./samples/Design1.ms14.xmlBy default, strips a trailing .xml from each input to derive the output path. Format is inferred from the output extension (see the table above).
# Decode first to produce the editable JSON.
bun run ewd ./samples/Stocked.prj
# Edit ./samples/Stocked.prj.json in your editor / script.
# Then re-encode: ewe diffs vs the original and writes a patched copy.
bun run ewe --verbose ./samples/Stocked.prj.json
# -> samples/Stocked.prj.patched.prjewe recognizes .json inputs and routes them through the in-place edit path. The original .prj location is taken from the JSON's source field (or --source <path> to override). The patched copy goes to <source>.patched.prj (or --output <path> to override).
Each --verbose run prints N applied, M skipped and lists every skipped change with its reason. Skipped changes leave the file untouched at that cell.
ewe uses node-pkware's implode for compression. The repetition search isn't quite as sophisticated as the original PKWare DCL implementation Multisim itself uses — re-encoded files are roughly 2× the size of the originals (e.g. 9 KB .ewprj round-trips to 20 KB). The output is still valid PKWare DCL Implode: round-trips are byte-identical on every sample tested, and the resulting files are accepted by ewd.
This is a substantial improvement over the previous baseline (7-10× bloat) thanks to fixes in the v5-fix branch of the cinderblock/node-pkware fork — see plans/jet-in-place-edit.md context and the fork's v5-fix branch for the specific bugs that were patched.
The package also ships a library API (ESM + CJS, with TypeScript types) — the same functions the CLIs are built on.
import {
decodeFile,
decodeBuffer,
encodeBuffer,
decodeMdbBuffer,
formatByKey,
} from 'electronics-workbench-decoder';
// Filename → decoded JS values (format detected from the file's magic bytes)
const result = await decodeFile('Design1.ms14');
if (result.kind === 'compressed-xml') {
console.log(result.xml.toString()); // the decoded XML
} else {
console.log(result.data.tables.SYS_COMPONENT.rows); // Jet DB as a JS object
}
// Buffer in, buffer out — no file I/O
const { xml } = decodeBuffer(ewprjBytes);
const ewprj = encodeBuffer(xml, formatByKey('ewprj')!);
const db = decodeMdbBuffer(prjBytes); // → { format, source, tables: {...} }Exposed: decodeFile, decodeBuffer, encodeBuffer, decodeMdbBuffer, the format registry (FORMATS, formatByKey, formatForExtension, detectFormatByHeader, detectFileFormat), and the supporting types (EwbFormat, MdbJson, DecodeResult, DecodedXml, …).
Decoding covers every supported format. Encoding via the API covers the compressed-xml containers (encodeBuffer); .prj in-place editing remains CLI-only (ewe).
Everything operates in memory — a file is read whole, decoded/encoded whole, then written. Peak RAM is on the order of the input plus the decoded output. This is fine for the file sizes Electronics Workbench produces (KB to a few MB). Streaming wouldn't help the .prj/.usr databases anyway (Jet is a random-access format that mdb-reader must load fully), and for the compressed-xml containers it would only cap RAM at one ~900 KB section — a marginal win not worth the added complexity at these sizes.
bun testCovers the format registry (extension matching, header detection including the Jet magic prefix with leading NUL bytes, on-disk detection), encoder filename helpers, mdb value normalization (Date → ISO, OLE blobs → base64), the public library API (buffer round-trips, decodeFile against a temp file), and compressed-xml round-trip integration (tiny .ewprj, tiny .ms14, multi-block payload that spans more than one PKWare section, and an empty payload).
bun run dev:ewd --verbose ./samples/Temp.ewprj
bun run dev:ewe --verbose ./samples/Temp.ewprj.xmlBoth use bun --watch for fast reload on source changes.
bun run check # biome lint + format check (no changes)
bun run check:fix # apply all safe fixes
bun run format # format only
bun run typecheck # tsc --noEmitCI on push/PR runs check, typecheck, and test on Ubuntu and Windows.
Publishing to npm is automated by GitHub Actions (.github/workflows/publish.yml) using npm Trusted Publishing (OIDC): no stored NPM_TOKEN, and each release gets a signed provenance attestation.
To cut a release:
npm version patch # or `minor` / `major`; bumps package.json, commits, tags vX.Y.Z
git push --follow-tags # pushing the tag triggers the publish workflowThe vX.Y.Z tag triggers the workflow, which verifies the tag matches package.json, runs the full gate (check + typecheck + test + license collection + build), then npm publish --provenance.
Notes for maintainers:
| Back | FazBrowse Home | New Git URL |