nooikko/serpents-eyes
GitHub: nooikko/serpents-eyes
「Serpent's Gaze」游戏的非官方只读收藏品图鉴浏览器,通过解析本地存档以卡片形式展示玩家的解锁进度与未解锁提示。
Stars: 0 | Forks: 0
# Serpent's Eyes
**Serpent's Gaze** 的收藏品浏览器 —— 查看所有你已解锁和未解锁的内容。
[](https://github.com/nooikko/serpents-eyes/actions/workflows/ci.yml)
[](https://github.com/nooikko/serpents-eyes/releases/latest)
[](https://github.com/nooikko/serpents-eyes/releases)
[](LICENSE)
[](https://github.com/nooikko/serpents-eyes/releases)
."
value = counter (1 = unlocked/done once, N = count, 0 = reached but not done)
TRAILER (current-run snapshot) — absent entirely in some saves
int32 pending tag count observed 0 or 1
count × fstring progression tags; see below
fstring map name "Majin_HolyCity", or "None" between runs
double X, Y, Z player world position
float unknown observed 73.0 (possibly health)
pairs × { fstring slot type ("Item"); fstring id ("Class_Stronk", …) }
until a zero/implausible length is read
zero padding, then FF FF FF FF
```
有两个细节很容易弄错,而且这里也曾存在过相关的 bug:
- **末尾结构中的第一个 `int32` 是一个计数值,而不是一个未知字段。** 在任何于两次 run 之间保存的存档文件中(这也占了绝大多数情况),它的值都是 `0`,因此将其视为不透明字段看起来能正常工作。然而,当其值不为零时,其后方的每一个字段都会整体偏移一个字符串的位置,导致解析出的地图名称、位置和 Loadout 全部出错。它所计数的正是进度标签 —— 例如,在刚解锁 `Progression.Item.BasicCrit` 后保存的文件中,就会确切地列出该标签。
- **`"None"` 是 Unreal 的空 `FName`,而不是地图名。** 在两次 run 之间,游戏会写入该字面量字符串而不是清空字段,因此,仅凭一个非空的地图名称,并不能证明当前正处于某次 run 进行中。
## 重新生成游戏数据
`src/SerpentsEyes.Core/GameData/TagDatabase.g.cs` 以及 `src/SerpentsEyes.App/Assets/` 下的图标都是自动生成的。**请勿手动编辑它们。** 这通常只在游戏更新后才需要执行。
游戏将其资源打包在 UE5 IoStore 容器中(`NinjaGarden-Windows.utoc` / `.ucas`,体积约 6.8 GB),并且采用了 Oodle 压缩。提取器无法直接读取这些文件,因此第一步是使用第三方工具(例如 [retoc](https://github.com/trumank/retoc))对其进行解包。
```
# 1. 解包游戏(第三方工具),生成 .../NinjaGarden/Content
# 2. 重新生成 tag 数据库和 icon manifest
dotnet run --project tools/SerpentsEyes.Extractor --
# 3. 重新导出 icons
dotnet run --project tools/SerpentsEyes.Extractor -- --icons
```
内容根目录也可以通过 `SERPENTS_GAZE_CONTENT` 环境变量来指定。报告和图标清单默认会输出到 `artifacts/` 目录;你可以使用 `--out ` 进行覆盖。使用 `--help` 运行可以查看所有指令,包括用于导出已知资源中原始字符串的 `--probe`。
提取器会解析游戏的 18 个 StringTable 资源以及约 140 个物品定义资源,并生成相应的 C# 代码和一份 JSON 报告。`--icons` 步骤会直接解码已打包的 UI 贴图 —— 包括内联的 `PF_B8G8R8A8` 以及带有 `.ubulk` 的 DXT1/5 格式 —— 因此不需要 `.usmap` 文件,接着会将它们缩小至 384 像素,并编码为 WebP 格式。这依然是它们在界面上所能绘制出的最大尺寸的两倍多,并且能将图标集的体积从 42.8 MB 骤减至 2.4 MB。
## 开发说明
```
dotnet build
dotnet test
```
要求使用 .NET 10(版本已在 `global.json` 中锁定)。在所有项目中,警告(Warnings)都会被视为错误(Errors)。
测试固件是真实存档的冻结副本,因此即使游戏不断向实时存档目录写入数据,测试依然能保持通过。`profile_pending_tags.sav` 专门用于覆盖非空的待处理标签列表场景,这是其他测试固件所不具备的。
欢迎参与贡献 —— 详见 [CONTRIBUTING.md](.github/CONTRIBUTING.md)。
## 免责声明
Serpent's Eyes 是一个非官方的、粉丝制作的工具。它不隶属于、未经过授权、未获得认可,也未以任何官方形式与 Serpent's Gaze 的开发者或发行商建立联系。“Serpent's Gaze”以及所有相关的名称、标识、美术素材和游戏内容均归其各自所有者所有。
本工具仅读取已经存在于你个人电脑上的存档文件。它不会修改、规避(破解)或重新分发游戏本体。
如果你是版权方且对本仓库中的任何内容有异议,请[提交一个 issue](https://github.com/nooikko/serpents-eyes/issues),我会妥善解决。如果情况紧急,请在标题中注明。
## 项目状态
就我个人而言,项目已基本完工 —— 它实现了我最初设想的全部功能,我也没在积极添加新特性。
不过,它并未被抛弃。我真诚地欢迎 Bug 报告和 Pull Request,而且我也会认真阅读它们。我大约每周查看一次 GitHub,因此请耐心等待几天以获取回复。
## 许可证
源代码基于 [MIT 许可证](LICENSE) 授权。
该许可仅涵盖代码部分。它不包含以下内容,具体细节已在 [NOTICE](NOTICE) 文件中完整列出:
- **游戏内容。** 游戏内的名称、描述、背景故事以及位于 `src/SerpentsEyes.App/Assets/Icons` 下的图标均为 Serpent's Gaze 版权所有者的财产,此处出于互操作性的目的进行再现。它们并未在 MIT 许可证下授权。
- **衍生自 Wiki 的文本。** 经过精心整理的解锁提示改编自 [Serpent's Gaze 社区 Wiki](https://serpents-gaze.fandom.com/),并基于 [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) 协议使用,由于该协议具有“相同方式共享”的传染性,因此与 MIT 协议不兼容。
## 鸣谢
- [Serpent's Gaze 社区 Wiki](https://serpents-gaze.fandom.com/),提供了那些无法从游戏文件中直接恢复的解锁条件。
- [Avalonia](https://avaloniaui.net/),提供的 UI 框架。
- [retoc](https://github.com/trumank/retoc),让提取游戏资源成为可能。
标签:存档读取, 收藏浏览器, 游戏工具, 进度追踪