@etothepii/satisfactory-file-parser

中文文档
4.1.2发布于 2026/07/26MITTypeScriptCommonJS近 30 天下载 1.1万

满意文件的文件解析器 。 包括保存文件和蓝图文件 .

中文使用文档
文档目录4 节
  1. 关于解析方法的其他选项
  2. 示例打印枢纽终端位置
  3. 修改玩家位置的示例
  4. 在存储容器中覆盖项目堆叠的示例

满意的文件解析器

Run Tests

这是类型脚本 github 项目 以解析满意保存/蓝印。 满意是一款由Coffee Stain Studios发行的游戏. 重写完全用 TypeScript 写,并捆绑在 国家预防机制。 。 。 . 这里列举的例子有Node.js. 也应该在浏览器里工作.

此解析器可以读取,修改并写入 :

  • 保存文件 .sav
  • 蓝图文件 .sbp, (中文(简体) ), .sbpcfg

解析器是用于深度保存编辑,因为它只是解析到JSON和后. 拥有可编辑结构,游戏逻辑的目的不详. 建议您查看解析后保存/ 蓝印, 以获取您想要编辑的内容 .

支持的版本

游戏更新版的版本支持如下所示. 解析器与更古老的保存和蓝图都相当相容(例如来自U8). 如果你还碰到错误,告诉我或者提出一个问题. U5及以下的游戏版本文件不支持.

游戏版本支助
U5型导弹无法兼容
U6 级qq 基本兼容
U7型导弹qq 基本兼容
铀8兼容
铀1.0兼容
铀1.1兼容
铀1.2兼容

版本迁移

由于兼容性原因,解析器不会从一个游戏版本到另一个版本的MIGRATE. 如果您分析一个U8保存/蓝印,解析器也会将它序列化回一个U8保存/蓝印.

安装

无

npm install @etothepii/satisfactory-file-parser

线条

yarn add @etothepii/satisfactory-file-parser

错误报告或反馈

你总是可以提出一个问题 在链接 github 项目 或以不和之相击我 etothepii。 。 。 . 我所知道的每一个错误 都会让解析器更坚固 没有很多人的帮助,它就不会在现在的位置!

模块支持 ++ 1.1 +++

通过默认,大多数Mods只是再利用了基础游戏的属性和Structs. 如果一个Mod不能工作, 或只是有对象 有很多跟踪数据 无法解析.

一些经过明确测试的模型包括: Ficit-Cam 结构解决方案 线性运动 集装箱屏幕 接通器墙洞 X3-Signs, X3-Roads

例外: Ficsit Networks 更复杂了 还没把一切都画出来 数据保存为原始字节数组. 如果你想的话 我可以看看你的救赎.

正在读取保存

在记忆中读取保存 .

import * as fs from 'fs';
import { Parser } from '@etothepii/satisfactory-file-parser';

const file = new Uint8Array(fs.readFileSync('./MySave.sav')).buffer;
const save = Parser.ParseSave('MySave', file);

// write to json file
fs.writeFileSync('MySave.json', JSON.stringify(save));

也可以通过流来读取“保存”,以保存RAM. 整个保存的二进制数据仍然会存在内存中,但转换后的JSON可以被流出. (你当然可以继续读取溪流的记忆). 回来的 stream 是一个可读的类型字符串的WhatWG流并代表一个 SatisfactorySave 对象。此对象可以再次被序列化. WhatWG被浏览器默认使用. 节点js可以使用它们 Writable.toWeb() 财务报告和审定财务报表 Writable.fromWeb() 举个例子.

import * as fs from 'fs';
import { Writable } from 'stream';
import { WritableStream } from 'stream/web';
import { ReadableStreamParser } from '@etothepii/satisfactory-file-parser';

const file = fs.readFileSync('./MySave.sav');
const jsonFileStream = fs.createWriteStream('./MySave.json', { highWaterMark: 1024 * 1024 * 200 }); // your outgoing JSON stream. In this case directly to file.  
const whatwgWriteStream = Writable.toWeb(jsonFileStream) as WritableStream<string>;                  // convert the file stream to WHATWG-compliant stream

const { stream, startStreaming } = ReadableStreamParser.CreateReadableStreamFromSaveToJson('MySave', file);

stream.pipeTo(whatwgWriteStream);
jsonFileStream.on('close', () => {
    // write stream finished
});

startStreaming();

写入保存

因此,写一个解析后保存的文件同样容易. SaveParser在进程的不同场合同步时有回调帮助. 例如,写出标题或写出保存正文块时. 将二进制数据分割成单个块, 就可以更容易地将二进制数据流到其他地方.

import * as fs from 'fs';
import { Parser } from "@etothepii/satisfactory-file-parser";

let fileHeader: Uint8Array;
const bodyChunks: Uint8Array[] = [];
Parser.WriteSave(save, header => {
    console.log('on save header.');
    fileHeader = header;
}, chunk => {
    console.log('on save body chunk.');
    bodyChunks.push(chunk);
});

// write complete sav file back to disk
fs.writeFileSync('./MyModifiedSave.sav', new Uint8Array(Buffer.concat([fileHeader!, ...bodyChunks]));

阅读蓝图

蓝图由2个文件组成. 这个 .sbp 主文件和配置文件 .sbpcfg。 。 。 .

import * as fs from 'fs';
import { Parser } from "@etothepii/satisfactory-file-parser";

const file = new Uint8Array(fs.readFileSync('./MyBlueprint.sbp')).buffer;
const configFile = new Uint8Array(fs.readFileSync('./MyBlueprint.sbpcfg')).buffer;
const blueprint = Parser.ParseBlueprintFiles('Myblueprint', mainFile, configFile);

// write to json file
fs.writeFileSync('Myblueprint.json', JSON.stringify(blueprint));

写入蓝图

因此,将蓝图写入二进制数据,与以保存解析的相同风格获得回调相同.

import * as fs from 'fs';
import { Parser } from "@etothepii/satisfactory-file-parser";

let mainFileHeader: Uint8Array;
const mainFileBodyChunks: Uint8Array[] = [];
const summary = Parser.WriteBlueprintFiles(blueprint,
    header => {
        console.log('on main file header.');
        mainFileHeader = header;
    },
    chunk => {
        console.log('on main file body chunk.');
        mainFileBodyChunks.push(chunk);
    }
);

// write complete .sbp file back to disk
fs.writeFileSync('./MyBlueprint.sbp', new Uint8Array(Buffer.concat([mainFileHeader!, ...mainFileBodyChunks])));

// write .sbpcfg file back to disk, we get that data from the result of WriteBlueprintFiles
fs.writeFileSync('./MyBlueprint.sbpcfg', new Uint8Array(summary.configFileBinary));

关于解析方法的其他选项

对于每个解析器呼叫,您可以通过可选的回调来接收额外的信息. 就像在被解压的救生机上回叫 解析保存为报告进度提供了回调 [0] 和不定期的信息. 若为 throwErrors ,解析器将在无法解析的数据上立即中止.

const save = Parser.ParseSave('MySave', file.buffer, {
    onDecompressedSaveBody: (body) => console.log('on decompressed body', body.byteLength),
    onProgressCallback: (progress, msg) => console.log(progress, msg),
    throwErrors: false
});
const { stream, startStreaming } = ReadableStreamParser.CreateReadableStreamFromSaveToJson(savename, file, {
    onProgress: (progress, msg) => console.log(`progress`, progress, msg);
});
const blueprint = Parser.ParseBlueprintFiles('Myblueprint', file, configFile, {
    onDecompressedBlueprintBody: (body) => console.log('on decompressed body', body.byteLength),
    throwErrors: false
});

保存编辑实例(JS/TS)

import { SaveComponent, SaveEntity, StructArrayProperty, Int32Property, ObjectProperty, StrProperty, StructProperty, InventoryItemStructPropertyValue, DynamicStructPropertyValue } from '@etothepii/satisfactory-file-parser';

// method to overwrite save objects
// currently quite inefficient to loop through everything, so theres room to improve in a future version. Feel free to raise an issue.
const modifyObjects = (...modifiedObjects: (SaveEntity | SaveComponent)[]) => {
    for (const modifiedObject of modifiedObjects) {
        for (const level of Object.values(save.levels)) {
            for (let i = 0; i < level.objects.length; i++) {
                if (level.objects[i].instanceName === modifiedObject.instanceName) {
                    level.objects[i] = modifiedObject;
                }
            }
        }
    }
}

const objects = Object.values(save.levels).flatMap(level => level.objects);
const collectables = Object.values(save.levels).flatMap(level => level.collectables);

示例打印枢纽终端位置

// get hub terminals. Beware that filter returns a COPIED array and not the original objects.
const hubTerminals = objects.filter(obj => obj.typePath === '/Game/FactoryGame/Buildable/Factory/HubTerminal/Build_HubTerminal.Build_HubTerminal_C') as SaveEntity[];
const firstHubPosition = hubTerminals[0].transform.translation;
console.log(`Hub terminal is located at ${firstHubPosition.x}, ${firstHubPosition.y}, ${firstHubPosition.z}`);

修改玩家位置的示例

const players = objects.filter(obj => obj.typePath === '/Game/FactoryGame/Character/Player/Char_Player.Char_Player_C') as SaveEntity[];
for (const player of players) {
    const name = (player.properties.mCachedPlayerName as StrProperty).value;
    player.transform.translation = {
        x: player.transform.translation.x + 5000,
        y: player.transform.translation.y + 5000,
        z: player.transform.translation.z,
    }
    console.log(`Player ${name} is now located at ${player.transform.translation.x}, ${player.transform.translation.y}, ${player.transform.translation.z}`);
}

// modify original save objects
modifyObjects(...players);

在存储容器中覆盖项目堆叠的示例

// get the first storage container, either mk1 or mk2.
const storageContainers = objects.filter(obj =>
    obj.typePath === '/Game/FactoryGame/Buildable/Factory/StorageContainerMk1/Build_StorageContainerMk1.Build_StorageContainerMk1_C'
    || obj.typePath === '/Game/FactoryGame/Buildable/Factory/StorageContainerMk2/Build_StorageContainerMk2.Build_StorageContainerMk2_C'
);
const firstContainer = storageContainers[0];

// the container has a reference name to an inventory component.
const inventoryReference = firstContainer.properties.mStorageInventory as ObjectProperty;
const inventory = objects.find(obj => obj.instanceName === inventoryReference.value.pathName) as SaveComponent;
const inventoryStacks = inventory.properties.mInventoryStacks as StructArrayProperty;
const firstStack = inventoryStacks.values[0];

// Items within ItemStacks are quite nested. And StructProperties can basically be anything.
// overwrite first item stack with 5 Rotors.
((firstStack as DynamicStructPropertyValue).properties.Item as InventoryItemStructPropertyValue).itemReference = {
    levelName: '',
    pathName: '/Game/FactoryGame/Resource/Parts/Rotor/Desc_Rotor.Desc_Rotor_C'
};
((firstStack as DynamicStructPropertyValue).properties.NumItems as IntProperty).value = 5;

// modify original save object
modifyObjects(inventory);

错误处理( 从 3. 3. 0 起)

从版本 3.3.0,解析器将不再默认丢出每个错误. 只有您明确指定 throwErrors: true 当调用保存/蓝印上的解析器时,当遇到无法解析的数据时,解析器会立即中止. 否则, throwErrors: false 默认值.

何时 throwErrors 实值 false 行为如下:

  • 对于未知的属性,它们的数据土地位于 rawBytes 区域.
  • 如果错误更为严重,则影响整体属性结构: trailingData 输入数据的字段.
  • 否则,整个尺度的物体可能腐败,将完全从所解析的JSON中被排除在外.
  • 更广泛的保存结构(头顺序、身体顺序、身体结构)的错误,无论怎样,仍然被扔出。 那你的救赎是无法解释的.

自动生成类型文件参考。 。 。 .

索引桶文件也使用桶比自动生成. npx barrelsby --directory src --delete --recursive --verbose --exclude src/test

基本指南。 。 。 .

更详细地解释一些基本的东西 在剖析器.

更改日志

许可证

版本与发布记录

@etothepii/satisfactory-file-parser 共发布 105 个版本,首次发布于 2023/03/26;过去 12 个月发布了 10 个版本。

版本发布日期距今
4.1.2latest2026/07/263 个月前
4.1.12026/04/266 个月前
4.1.02026/04/266 个月前
4.0.22026/04/246 个月前
4.0.12026/04/186 个月前
4.0.02026/04/186 个月前

关于 @etothepii/satisfactory-file-parser 的常见问题

@etothepii/satisfactory-file-parser 是什么?

满意文件的文件解析器 。 包括保存文件和蓝图文件 .

如何安装 @etothepii/satisfactory-file-parser?

在项目目录运行 npm install @etothepii/satisfactory-file-parser,或使用 pnpm add / yarn add / bun add。

@etothepii/satisfactory-file-parser 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@etothepii/satisfactory-file-parser 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@etothepii/satisfactory-file-parser 可以商用吗?

@etothepii/satisfactory-file-parser 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@etothepii/satisfactory-file-parser 的下载量有多少?

根据 npm 官方下载统计,@etothepii/satisfactory-file-parser 在 2026/10/14 – 2026/11/12 这 30 天内被下载 11,135 次,日均约 371 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@etothepii/satisfactory-file-parser 还在维护吗?

最新版本 4.1.2 发布于 2026/07/26(3 个月前);至今共发布 105 个版本,其中过去 12 个月发布了 10 个;首次发布于 2023/03/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。