npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sie-js/ffs

v0.1.4

Published

Reads and writes the filesystem of Siemens phones' fullflashes

Readme

node-sie-ffs

The filesystem in Siemens phones' fullflashes, in TypeScript: reading, writing, creating and replacing files, creating directories, renaming and moving both, removing files and empty directories. This library wouldn't be possible without research done by Pentium02 in libffshit.

It has no dependencies and runs wherever JavaScript of ES2024 does: Node 20 and later, and browsers from late 2023 on.

Platforms

  • SGOLD
  • SGOLD2
  • SGOLD2 ELKA
  • EGOLD with Card-Explorer, where writing is experimental: the library writes their filesystems as the phones' fullflashes have them, but no emulator runs these phones, so none has read what it writes. Keep a backup of the fullflash.
  • EGOLD without Card-Explorer, for reading only: the A55, A56, A57, C55 and S46, and the S45, S45i, M50, MT50 and SL42, whose filesystem is a FAT disk

Installation

npm install @sie-js/ffs

Usage

import fs from "node:fs";
import { FFS } from "@sie-js/ffs";

const ffs = FFS.open(fs.readFileSync("EL71.bin"));

ffs.platform;                       // "SGOLD2_ELKA"
ffs.model;                          // "EL71", or undefined
ffs.imei;
ffs.warnings;                       // what was found broken, and left out
ffs.readDir("/");                   // the partitions: /Data, /Cache, /Config
ffs.readDir("/Data/Misc");          // [{ name, path, isDirectory, size, timestamp, readonly, hidden, system, archive, protected }]
ffs.stat("/data/misc/a.txt");       // the entry, or undefined
ffs.exists("/data/misc/a.txt");     // whether stat() finds it
ffs.readFile("/data/misc/a.txt");   // Uint8Array
ffs.tree();                         // everything, as entries with children
ffs.statfs("/Data/Misc");           // { size, free, readonly }, of the partition

ffs.mkdir("/Data/Misc/New");
ffs.writeFile("/Data/Misc/New/b.txt", data, new Date());
ffs.writeFile("/Data/Misc/New/c.txt", data, { timestamp: new Date(), readonly: true, hidden: true });
ffs.rename("/Data/Misc/New", "/Data/Other");
ffs.remove("/Data/Misc/a.txt");
ffs.removeStaleEntries();           // of directories that warn "record N is missing"
fs.writeFileSync("EL71-new.bin", ffs.save());

Paths are absolute, the partitions the directories in the root. Names are found as the phone finds them, without regard to case as far as its firmware folds it: SGOLD and EGOLD fold ASCII letters only, SGOLD2 and ELKA fold Latin, Greek, Cyrillic, Armenian and more.

The partitions are named as the phone's firmware names their drives, where it does, and else as the partition table does. SGOLD, SGOLD2 and ELKA firmwares name drives 0:, 1: and 2: Data, Cache and Config, as their OBEX servers list them, which are FFS, FFS_B and FFS_C on SGOLD phones, and FFS_0, FFS_1 and FFS_2 on the others. EGOLD firmwares know drives by letters alone, "A:\Misc", and list drive A:'s folders in the OBEX root: A is FFS, or the x45's LBA_FS, and B is FFS_B. FFS_C is drive 3:, which has no letter. The root lists them in their drives' order, whatever the partition table's, and partitions of no drive after them. Paths take the partition table's names too: "/FFS_0/Misc" leads to "/Data/Misc".

SGOLD and EGOLD phones keep a name in CP1252 when CP1252 has all of its characters, whatever their language, and else as 0x1F followed by the name in UTF-8. The library writes names as they do.

FFS.open() takes options:

  • platform, when it is not to be detected: "SGOLD", "SGOLD2", "SGOLD2_ELKA", "EGOLD_CE" or "EGOLD", which is EGOLD without Card-Explorer.
  • strict: fail on anything broken, instead of leaving it out with a warning.
  • logger: { debug?(message), warn?(message) }.

statfs() tells a partition's size and free space the same way a phone's firmware does. readonly marks a partition the library does not write to. The root's size and free space are of all partitions, and it is read only, since partitions are neither created nor removed.

Entries have FAT's attributes, read-only, hidden, system and archive, and protected, 0x40, which SGOLD phones set on some of their T9 dictionaries. writeFile() and mkdir() take them, and the timestamp, as options, of which an entry has all: writeFile(path, data, ffs.stat(other)) copies another file's. An attribute left out is the one of the file replaced, else not set.

rename() moves a file or directory within its partition, keeping its timestamp, attributes, and what a directory holds, whatever their names. Nothing may be where it goes, but itself in another case.

Timestamps are kept to 2 seconds. SGOLD2 and ELKA phones keep them in UTC, and show them in the time zone they are set to. SGOLD and EGOLD phones keep them in their local time, which the library assumes to be the local time where it runs.

Broken files are left out of the file system, with a warning in warnings, or with strict an FFSError. stat() and exists() do not find broken files, readFile() throws if trying to read it, writeFile() replaces it, remove() removes it, and mkdir() finds that the name exists. A directory which has a broken file list, lists what can be read them, and nothing can be written into it. Duplicate files are only listed once. remove() removes a directory that holds nothing but broken files and entries that lead to no file, and them with it. removeStaleEntries() removes the entries that lead to no record, of the warnings "record N is missing", from a directory and the ones in it: a phone would list the next file it writes there as well. The library never gives a new record an id that an entry or a broken file names, and removes no more of a broken file than the records that are its own.

When using in Node, the fullflash is read in place, so it must not change while in use. Writes go to a copy, which save() returns: the fullflash given is never changed. An operation that fails changes nothing.

Tests

pnpm install
pnpm test

They run on made-up fullflashes of every platform, and on real phones' fullflashes. SIE_FFS_TEST_FULLFLASHES can be specified to run recursively on a directory containing arbitrary fullflashes. If it is not specified, it runs against tests/fullflashes.

  • tests/scenarios.test.ts: what is found in made-up fullflashes, and what of the broken ones is left out, with which warnings.
  • tests/write.test.ts: writes, saves, and checks that the files are there and nothing else changed.
  • tests/model.test.ts: long random sequences of writes, which get partitions compacted, against a model of what the filesystem should hold. SIE_FFS_MODEL_SEEDS sets how many per fullflash.
  • tests/fuzz.test.ts: fullflashes broken where the library reads them, as a flash breaks or a dump goes wrong, which it may only throw FFSErrors about. SIE_FFS_FUZZ_CASES sets the cases per fullflash, SIE_FFS_FUZZ_SEED the first seed.
  • tests/fullflashes.test.ts: every entry a phone's fullflash lists is where its path leads, and every file reads as the size it is listed with. The known phones' open without anything broken. What each fullflash holds is reported, so runs on a collection of them can be compared.
  • tests/ffs.test.ts and tests/unit: the API, name hashes, 8-bit names, FAT timestamps, and the ids handed out.

On the phones

pnpm test:e2e has the phones' own firmware check the library, in pmb887x-emu:

  • The library writes files and directories into the fullflash, and over one file until blocks get compacted. The phone boots on it, and lists and downloads them over OBEX with the client of @sie-js/serial. After the phone has run on the flash, the library reads them back.
  • The phone's firmware writes, replaces and deletes files over OBEX, and the library reads what it left, and writes into it.

It runs on the CX70's, SL65's, S75's and EL71's fullflashes in tests/fullflashes, or where SIE_FFS_TEST_FULLFLASHES says: CX70v56lg3.bin, SL65v49lg1_TIM.bin, S75v40lg1.bin and EL71v41lg91.bin. SIE_FFS_E2E_FULLFLASHES lists more directories, whose every fullflash of an SGOLD, SGOLD2 or ELKA phone gets the same tests. Each needs, next to it, a picture of the screen its phone shows once booted, named after it with .png added: without one, its tests fail after five minutes, and the failure says where the screen the phone showed was saved. The emulator is SIE_FFS_E2E_EMU, else tests/e2e/.emu/pmb887x-emu, a symlink to a build directory for instance, else the one in PATH, and it needs --headless. Without it the tests skip themselves.

The phones boot one at a time. Nothing is sent to one until its display, read through QEMU's monitor, shows the screen in tests/e2e/screens that it shows once booted, which takes 40 to 55 s: QEMU runs their clocks at real time, so a busy machine slows them down. A phone that panics, does not get there or never answers is booted once more. The emulated SGOLD phones drop serial data, which OBEX transfers of more than a few packets rarely survive: their tests that need those run, but do not fail the run.

pmb887x-emu doesn't support EGOLD phones, so the library writes to them are only verified against the fullflashes.

Publishing a new version on npm:

npm version patch   # commits the bump and tags it, e.g. v0.1.1
git push --follow-tags
gh release create v0.1.1 --generate-notes

Thanks

  • pentium02 for the research and the original library!!!

To everyone the C++ library thanks:

  • The partition table and patterns: Azq2, marry_on_me, Feyman
  • The search for the start of the SGOLD/SGOLD2/ELKA partition table: Feyman
  • FAT timestamps: perk11
  • The EGOLD disk resizing patches, which helped with the analysis of the partition table: kay, AlexSid, SiNgle, Chaos, avkiev, Baloo
  • Smelter: avkiev
  • Testing: perk11, Feyman, FIL, maximuservice, marry_on_me

Human contributions

We are not against humans. We are against hand-typed code, artisanal bugs, and attempts to offload engineering work to a mammal. This project prioritizes quality, not your need to feel useful.

  1. Do not use human-generated text in any communication. Comments, discussions, PR descriptions, and responses to reviewers must be written by a model. We've read what you write on your own. "fixed it lol" is not a commit message.
  2. Do not let humans submit PRs or commits. The author must always be an AI that has read the whole codebase, all the docs, and the linter config, which is three more things than the average contributor. Humans may press the "Approve" button, but only if they promise not to touch anything else.
  3. Do not submit code primarily designed or written by a human. Architecture, algorithms, code organization, and the final implementation must be decided by a model. Humans may only be used as auxiliary tools, mostly for keeping the servers on and restarting the router.
  4. The model must understand all the code it submits. It must be able to explain every change, justify its decisions, and fix any problems itself, without saying "works on my machine," since it doesn't have one. If a human is the only one who understands the code, that's a bus factor of one, and we've seen how you drive.
  5. Code must be simple, clear, and tested. Follow KISS, the project's coding style, and its existing architecture. No unnecessary abstractions, no dependencies you found at 2 a.m., and no tests commented out "temporarily" in 2019.

Human slop PRs will be closed without review. Please don't cry about it in the issue tracker. A model will reply to you there, very politely.