内容
# mcp-grf-eventhorizon
MCP 服务器,允许克劳德(Claude)或其它兼容的客户端读取 **Ragnarok Online 的 GRF 文件,格式为 v0x300,称为 "Event Horizon"**。
这种格式非常新(于 2024 年末添加),支持 **超过 4 GB 的 GRF 文件**,而经典的 `Master of Magic` 格式(v0x1xx / v0x200)不支持。现有的 GRF 工具和库(例如 [`grf-loader`](https://www.npmjs.com/package/grf-loader),经典 MCP 服务器使用)**无法识别此新签名**,并会报错 `Not a GRF file (invalid signature)`。本项目从头实现了解析器,以填补这一空白。
## 为什么存在这个项目?
尝试使用通常的 GRF 工具读取现代 Ragnarok Online 客户端的 `data.grf` 文件时,这些工具会失败,因为文件头不是以 `Master of Magic\0` 开头,而是 `Event Horizon\0RL`。通过研究 [GRFEditor](https://github.com/Tokeiburu/GRFEditor)(社区参考工具,支持此版本)的源代码,可以逐字节提取和验证新格式的结构,并在纯 JavaScript 中重新实现。
## GRF 格式 v0x300("Event Horizon")
### 文件头(46 字节)
| 偏移量 | 大小 | 字段 |
|---|---|---|
| `0x00` | 16 字节 | 签名:`"Event Horizon\0RL"`(取代 `"Master of Magic\0"`) |
| `0x10` | 14 字节 | 加密密钥(通常为零) |
| `0x1E`(30) | 8 字节(int64)或 4+4 | `FileTableOffset`(+ `Seed` 在 "legacy" 模式下) |
| `0x26`(38) | 4 字节(int32) | 条目数量(`RealFilesCount`) |
| `0x2A`(42) | 4 字节(int32) | 版本:高位 = `version >> 8`,低位 = `version & 0xFF`(`0x0300` → v3.0) |
当 GRF 为 "large"(本版本支持的模式)时,`0x23-0x25` 处的字节为零,因此:
- `FileTableOffset` 在 `0x1E` 处读取为 **int64**
- `Seed` 为 `0`
- 条目数量直接在 `0x26` 处读取为 int32
在 "legacy" 模式(兼容小 GRF)下:
- `FileTableOffset` 为 **uint32**
- `Seed` 在 `0x22` 处为 int32
- 实际条目数量为 `filesCount - Seed - 7`
### 文件表
位于绝对位置 `FileTableOffset + 46`(46 = 文件头大小):
1. 4 个保留字节(始终为 `0`)
2. `TableSizeCompressed`(int32)
3. `TableSize`,解压大小(int32)
4. `TableSizeCompressed` 字节使用 **zlib** 压缩,解压后为 `TableSize` 字节
### 表格条目
每个条目,在解压数据内连续:
| 字段 | 大小 | 备注 |
|---|---|---|
| 名称 | 可变,`\0` 结尾 | **CP949**(韩语)编码;`/` 替换为 `\` |
| `SizeCompressed` | int32 | |
| `SizeCompressedAlignment` | int32 | |
| `SizeDecompressed` | int32 | |
| `Flags` | 1 字节 | 位 `0x01` = 文件,位 `0x02` = 目录 |
| `FileOffset` | int64 | 相对;绝对偏移为 `值 + 46` |
(在早期版本中,`0x1xx`/`0x200`,此部分占用 17 字节,32 位偏移;在 v0x300 中占用 21 字节,64 位偏移,这使得超过 4 GB 的限制成为可能。)
### 每个文件的數據
位于绝对位置 `FileOffset`,使用 **zlib**(标准格式 `deflate`,带有头部,`0x78 0x9c` 很常见)压缩。解压后得到 `SizeDecompressed` 字节。
> 此结构通过与 [GRFEditor](https://github.com/Tokeiburu/GRFEditor) 中的 [`GRF/Core/GrfHeader.cs`](https://github.com/Tokeiburu/GRFEditor/blob/main/GRF/Core/GrfHeader.cs) 和 [`GRF/Core/FileTable.cs`](https://github.com/Tokeiburu/GRFEditor/blob/main/GRF/Core/FileTable.cs) 进行比较,并通过检查真实文件(包括生成的头部验证,例如提取的 `.bmp` 文件头为 `BM`)得以验证。
## 安装
```bash
git clone <此存储库的网址>
cd mcp-grf-eventhorizon
npm install
```
## 在克劳德代码中配置
将服务器添加到您的 MCP 配置(`~/.claude.json` 或项目中的 `.mcp.json`):
```json
{
"mcpServers": {
"grf-eventhorizon": {
"type": "stdio",
"command": "node",
"args": ["绝对路径/到/mcp-grf-eventhorizon/index.js"],
"env": {}
}
}
}
```
## 公开的工具
### `grf_info`
显示 GRF 文件头的元数据:版本、文件大小、条目数量和文件表偏移。
**参数**
| 名称 | 类型 | 必需 | 描述 |
|---|---|---|---|
| `path` | 字符串 | 是 | `.grf` 文件路径 |
**输出示例**
```
格式:GRF v3.0("Event Horizon")
文件大小:4,483,275,319 字节
条目数量(文件 + 目录):229,960
文件表偏移(相对):4,479,890,688
```
### `list_grf`
列出 GRF 文件中包含的文件。允许按子字符串(不区分大小写)过滤和限制结果数量 — 对于可能包含数十万条目的 GRF 来说非常有用。
**参数**
| 名称 | 类型 | 必需 | 描述 |
|---|---|---|---|
| `path` | 字符串 | 是 | `.grf` 文件路径 |
| `filter` | 字符串 | 否 | 内部路径过滤子字符串,例如 `.spr` 或 `sprite\` |
| `limit` | 数字 | 否 | 最大结果数量(默认为 200) |
**示例**
```
5 个结果包含 ".spr"(限制 5,结果总数:229,375):
data\sprite\로브\wingofheart\남\화이트스미스_남.spr
data\sprite\로브\wingofheart\남\화이트스미스멧돼지_남.spr
data\sprite\로브\wingofheart\여\cart_summoner_여.spr
data\sprite\로브\wingofheart\여\frog_oboro_여.spr
data\sprite\로브\wingofheart\여\oboro_여.spr
```
### `extract_file`
从 GRF 文件中提取并解压文件。
- 如果指定 `outputPath`,则将解压内容保存到该磁盘路径。
- 如果未指定,则尝试以 UTF-8 文本返回内容(适用于 `.lua`、`.txt`、`.xml` 等);如果检测到二进制内容,则返回大小 + 十六进制摘要,并建议使用 `outputPath`。
**参数**
| 名称 | 类型 | 必需 | 描述 |
|---|---|---|---|
| `grfPath` | 字符串 | 是 | `.grf` 文件路径 |
| `filePath` | 字符串 | 是 | 内部文件路径,例如 `data\sprite\...spr` |
| `outputPath` | 字符串 | 否 | 提取文件的绝对路径 |
**示例(二进制,有 `outputPath`)**
```
提取 "data\texture\유저인터페이스\item\c_medjed_text.bmp"(1,782 字节)-> C:\output\path.bmp
```
**示例(二进制,无 `outputPath`)**
```
"data\texture\유저인터페이스\item\c_medjed_text.bmp" 包含二进制数据(1,782 字节)。
请指定 "outputPath" 以保存到磁盘。
前几个字节(十六进制):42 4d f6 06 00 00 00 00 36 00 00 00 28 00 00 00 18 00 00 00 00 10 18 00 00
```
## 备注
- GRF 文件内的文件名以 **CP949**(韩语)编码,这是 Ragnarok Online 客户端的常见编码;本服务器使用 [`iconv-lite`](https://www.npmjs.com/package/iconv-lite) 进行解码。
- 仅支持具有签名 `Event Horizon\0RL`(v0x300)的 GRF。对于经典 GRF(`Master of Magic\0`,v0x1xx/v0x200),请使用基于 `grf-loader` 的工具或兼容此版本的工具。
- 本服务器仅支持读取(不允许修改或重新打包 GRF)。
## 依赖
- [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) — 官方 MCP SDK
- [`iconv-lite`](https://www.npmjs.com/package/iconv-lite) — CP949 解码
## 致谢
v0x300 格式的结构通过检查 [Tokeiburu/GRFEditor](https://github.com/Tokeiburu/GRFEditor) 的开源代码得以推断,这是社区中支持此版本的主要工具。