feat: ID 映射支持叠加单独导出的数据包,并纳入 event 表
D:/A031OptDump/Root 是比 G:\Package 更新的一层增补(曲目到 12024、宴曲
111537/111765/112002),原来的 --package 只认整合包内的 A??? 目录,套不到它。
- 新增可重复的 --data-root,把一个目录直接当数据根,按传参顺序追加、后者覆盖
同 ID 行;--package 与 --data-root 可混用。
- 新增 events.json(eventId → 活动名 / alwaysOpen / infoType / disableArea)。
本地 event 表没有日期窗口,开没开由服务器 GetGameEventApi 的
GameEvent{startDate,endDate} 决定,README 里写明了这一点,免得误用它判状态。
- 合并两层后:music 1367→1378、map 168→169、title 3000→3001、event 824。
11820 在两层数据包里都不存在,结论不变。
- lockType 分布按合并后数据更正为 Unlock 1347 / Lock 18 / Challenge 5 /
Transmission 2 / KaleidScope 6,顺带说明大多数歌 lockType==Unlock、
本来就可玩,不查 itemKind 5。
This commit is contained in:
1 parent
264b1d0aa7
commit
2c7f9c1de7
7 files changed
+6066
-32
No files matched your search
@@ -168,7 +168,11 @@ data/id_mapping/ # 由 tools/gen_id_mapping.dart 从机台数据包抽
|
|||||||
「上传成功但没生效」。为此把机台数据包里的表抽成可查的映射:
|
「上传成功但没生效」。为此把机台数据包里的表抽成可查的映射:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# 整合包(自动找 <pkg>/Sinmai_Data/StreamingAssets/A0xx/)
|
||||||
dart run tools/gen_id_mapping.dart --package G:/Package
|
dart run tools/gen_id_mapping.dart --package G:/Package
|
||||||
|
|
||||||
|
# 再叠一层单独导出的数据包,后面的覆盖前面的同 ID 行
|
||||||
|
dart run tools/gen_id_mapping.dart --package G:/Package --data-root D:/A031OptDump/Root
|
||||||
```
|
```
|
||||||
|
|
||||||
产物落在 `data/id_mapping/`:
|
产物落在 `data/id_mapping/`:
|
||||||
@@ -179,22 +183,32 @@ dart run tools/gen_id_mapping.dart --package G:/Package
|
|||||||
| `items.json` | 按 `ItemKind` 分组(1 姓名框 2 称号 3 头像 9 角色 10 搭档 11 背景板 12 功能票)的 id → 名称 |
|
| `items.json` | 按 `ItemKind` 分组(1 姓名框 2 称号 3 头像 9 角色 10 搭档 11 背景板 12 功能票)的 id → 名称 |
|
||||||
| `maps.json` | mapId → 区域名 / `IslandId` / `OpenEventId` / `ColorId` / `BonusMusicId` / `IsInfinity` / `IsCollabo` |
|
| `maps.json` | mapId → 区域名 / `IslandId` / `OpenEventId` / `ColorId` / `BonusMusicId` / `IsInfinity` / `IsCollabo` |
|
||||||
| `gates.json` | 万花筒三张表:`gateList`(gateId → 门名 / `gateType` / `eventName`)、`courseList`(courseId ↔ gateId ↔ keyId)、`keyList` |
|
| `gates.json` | 万花筒三张表:`gateList`(gateId → 门名 / `gateType` / `eventName`)、`courseList`(courseId ↔ gateId ↔ keyId)、`keyList` |
|
||||||
|
| `events.json` | eventId → 活动名 / `alwaysOpen` / `infoType` / `disableArea` |
|
||||||
|
|
||||||
要点:
|
要点:
|
||||||
|
|
||||||
- 真正的 ID 在 XML 的 `<name><id>` 里,**目录名只是 `dataName`,两者不相等**
|
- 真正的 ID 在 XML 的 `<name><id>` 里,**目录名只是 `dataName`,两者不相等**
|
||||||
(`icon/icon550201/Icon.xml` 的 `name.id` 实际是 1)。
|
(`icon/icon550201/Icon.xml` 的 `name.id` 实际是 1;`music/music111537` 的
|
||||||
|
`name.id` 是 111537,属宴曲段)。
|
||||||
- 数据包按 `Sinmai_Data/StreamingAssets/A0xx/` 分基础包与增补包,抽取时按目录名
|
- 数据包按 `Sinmai_Data/StreamingAssets/A0xx/` 分基础包与增补包,抽取时按目录名
|
||||||
升序合并、同 ID 后者覆盖前者,与客户端 `DataManager.LoadData` 的语义一致。
|
升序合并、同 ID 后者覆盖前者,与客户端 `DataManager.LoadData` 的语义一致。
|
||||||
|
- **本地 event 表没有日期窗口**:开关状态是服务器 `GetGameEventApi` 的
|
||||||
|
`GameEvent{startDate,endDate}` 决定的。`events.json` 只能告诉你某个
|
||||||
|
`eventName.id` 是哪个活动、是否 `alwaysOpen`,判断「现在开没开」得问服务器。
|
||||||
- 放在 `data/` 而不是 `assets/`:目前还没有代码在运行时读它,进 `assets/` 会被
|
- 放在 `data/` 而不是 `assets/`:目前还没有代码在运行时读它,进 `assets/` 会被
|
||||||
原样打进每个安装包(约 1.2MB)。真要接进 App 时再连同 `pubspec.yaml` 一起挪。
|
原样打进每个安装包(约 1.2MB)。真要接进 App 时再连同 `pubspec.yaml` 一起挪。
|
||||||
|
|
||||||
顺手用这份表核出来的几个事实,都能解释此前的「不显示」:
|
顺手用这份表核出来的几个事实,都能解释此前的「不显示」:
|
||||||
|
|
||||||
- **musicId 11820 在曲表里根本不存在**(11819、11821 也不在;11814 之后直接跳到
|
- **musicId 11820 在任何一层数据包里都不存在**(11819、11821 也不在;11814 之后
|
||||||
11822)。它不是万花筒锁——`lockType = 4 (KaleidScope)` 的歌整张表只有 6 首
|
直接跳到 11822,G:\Package 与 D:/A031OptDump 两层都查过)。它不是万花筒锁——
|
||||||
(11740 / 11745 / 11749 / 11753 / 11809 / 11814)。11820 属于「机台没有这首歌」,
|
`lockType = 4 (KaleidScope)` 的歌整张表只有 6 首(11740 / 11745 / 11749 /
|
||||||
任何 `UpsertUserAll` 都变不出来。
|
11753 / 11809 / 11814)。11820 属于「机台没这首歌」,任何 `UpsertUserAll`
|
||||||
|
都变不出来。
|
||||||
|
- `lockType` 的分布是 `Unlock=1347 / Lock=18 / Challenge=5 / Transmission=2 /
|
||||||
|
KaleidScope=6`(合并两层数据包后共 1378 首),所以绝大多数歌根本不查
|
||||||
|
`itemKind 5`(`lockType == Unlock` 时 `IsPlayable` 直接为 true)——这也解释了
|
||||||
|
为什么有些「解歌」包发下去看不出效果:那首歌本来就可玩。
|
||||||
- **角色(旅行伙伴)1001 不存在**,合法 ID 从 101 起,整表 983 条、上界到 605905。
|
- **角色(旅行伙伴)1001 不存在**,合法 ID 从 101 起,整表 983 条、上界到 605905。
|
||||||
以前页面里「例如 1001」的提示正是这个坑。
|
以前页面里「例如 1001」的提示正是这个坑。
|
||||||
- **万花筒只有 gate 1~6**(蓝/白/紫/黑/黄/红,course 与 key 同为 1~6 一一对应)。
|
- **万花筒只有 gate 1~6**(蓝/白/紫/黑/黄/红,course 与 key 同为 1~6 一一对应)。
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -13593,6 +13593,10 @@
|
|||||||
{
|
{
|
||||||
"id": 609000,
|
"id": 609000,
|
||||||
"name": "WEC2026 以光之明"
|
"name": "WEC2026 以光之明"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 708106,
|
||||||
|
"name": "OV3RCLOCK"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"3": [
|
"3": [
|
||||||
|
|||||||
@@ -2350,5 +2350,19 @@
|
|||||||
"BonusMusicIdName": "Paradigm: Rebootちほーボーナス曲",
|
"BonusMusicIdName": "Paradigm: Rebootちほーボーナス曲",
|
||||||
"IsCollabo": true,
|
"IsCollabo": true,
|
||||||
"IsInfinity": false
|
"IsInfinity": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 650056,
|
||||||
|
"name": "ヒメヒナちほー2",
|
||||||
|
"IslandId": 60,
|
||||||
|
"IslandIdName": "maimaiDX CiRCLE",
|
||||||
|
"OpenEventId": 261002041,
|
||||||
|
"OpenEventIdName": "261002_04_1:ヒメヒナちほー2 解禁",
|
||||||
|
"ColorId": 650056,
|
||||||
|
"ColorIdName": "ヒメヒナ2",
|
||||||
|
"BonusMusicId": 650056,
|
||||||
|
"BonusMusicIdName": "ヒメヒナちほー2ボーナス曲",
|
||||||
|
"IsCollabo": true,
|
||||||
|
"IsInfinity": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
"generatedFrom": "G:/Package",
|
|
||||||
"dataDirs": [
|
"dataDirs": [
|
||||||
"A000",
|
"A000",
|
||||||
"A005",
|
"A005",
|
||||||
"A011",
|
"A011",
|
||||||
"A015"
|
"A015",
|
||||||
|
"Root"
|
||||||
],
|
],
|
||||||
"counts": {
|
"counts": {
|
||||||
"music": 1367,
|
"music": 1378,
|
||||||
"map": 168,
|
"map": 169,
|
||||||
"gate": 6,
|
"gate": 6,
|
||||||
"course": 6,
|
"course": 6,
|
||||||
"key": 6,
|
"key": 6,
|
||||||
|
"event": 824,
|
||||||
"itemKind1": 398,
|
"itemKind1": 398,
|
||||||
"itemKind2": 3000,
|
"itemKind2": 3001,
|
||||||
"itemKind3": 1046,
|
"itemKind3": 1046,
|
||||||
"itemKind9": 983,
|
"itemKind9": 983,
|
||||||
"itemKind10": 25,
|
"itemKind10": 25,
|
||||||
|
|||||||
+190
-3
@@ -18963,13 +18963,13 @@
|
|||||||
"genreNameName": "POPSアニメ",
|
"genreNameName": "POPSアニメ",
|
||||||
"AddVersion": 21,
|
"AddVersion": 21,
|
||||||
"AddVersionName": "BUDDiES",
|
"AddVersionName": "BUDDiES",
|
||||||
"eventName": 1,
|
"eventName": 0,
|
||||||
"eventNameName": "無期限常時解放",
|
"eventNameName": "解放なし",
|
||||||
"disable": false,
|
"disable": false,
|
||||||
"lockType": 0,
|
"lockType": 0,
|
||||||
"subLockType": 1,
|
"subLockType": 1,
|
||||||
"longMusic": 0,
|
"longMusic": 0,
|
||||||
"version": 24006
|
"version": 24003
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11640,
|
"id": 11640,
|
||||||
@@ -22252,6 +22252,142 @@
|
|||||||
"longMusic": 0,
|
"longMusic": 0,
|
||||||
"version": 25007
|
"version": 25007
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 11997,
|
||||||
|
"name": "キスキツネ",
|
||||||
|
"artistName": 781,
|
||||||
|
"artistNameName": "HIMEHINA",
|
||||||
|
"genreName": 101,
|
||||||
|
"genreNameName": "POPSアニメ",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261002021,
|
||||||
|
"eventNameName": "261002_02_1:ヒメヒナちほー2 3曲追加《キスキツネ、WWW、うたかたよいかないで》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26506
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11998,
|
||||||
|
"name": "WWW",
|
||||||
|
"artistName": 781,
|
||||||
|
"artistNameName": "HIMEHINA",
|
||||||
|
"genreName": 101,
|
||||||
|
"genreNameName": "POPSアニメ",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261002021,
|
||||||
|
"eventNameName": "261002_02_1:ヒメヒナちほー2 3曲追加《キスキツネ、WWW、うたかたよいかないで》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26506
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11999,
|
||||||
|
"name": "うたかたよいかないで",
|
||||||
|
"artistName": 781,
|
||||||
|
"artistNameName": "HIMEHINA",
|
||||||
|
"genreName": 101,
|
||||||
|
"genreNameName": "POPSアニメ",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261002021,
|
||||||
|
"eventNameName": "261002_02_1:ヒメヒナちほー2 3曲追加《キスキツネ、WWW、うたかたよいかないで》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26506
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12000,
|
||||||
|
"name": "Intergalactic Bound",
|
||||||
|
"artistName": 1127,
|
||||||
|
"artistNameName": "Yunosuke / CircusP",
|
||||||
|
"genreName": 102,
|
||||||
|
"genreNameName": "niconicoボーカロイド",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016021,
|
||||||
|
"eventNameName": "261016_02_1:niconico2曲、バラエティ1曲追加《Intergalactic Bound、LEMON MELON COOKIE、3rd Avenue》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26507
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12001,
|
||||||
|
"name": "LEMON MELON COOKIE",
|
||||||
|
"artistName": 1128,
|
||||||
|
"artistNameName": "TAK",
|
||||||
|
"genreName": 102,
|
||||||
|
"genreNameName": "niconicoボーカロイド",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016021,
|
||||||
|
"eventNameName": "261016_02_1:niconico2曲、バラエティ1曲追加《Intergalactic Bound、LEMON MELON COOKIE、3rd Avenue》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 0,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26507
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12002,
|
||||||
|
"name": "3rd Avenue",
|
||||||
|
"artistName": 1138,
|
||||||
|
"artistNameName": "Sound Souler",
|
||||||
|
"genreName": 104,
|
||||||
|
"genreNameName": "ゲームバラエティ",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016021,
|
||||||
|
"eventNameName": "261016_02_1:niconico2曲、バラエティ1曲追加《Intergalactic Bound、LEMON MELON COOKIE、3rd Avenue》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26507
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12019,
|
||||||
|
"name": "Cryogenic",
|
||||||
|
"artistName": 1148,
|
||||||
|
"artistNameName": "Camellia ft. Petra Gurin「In Falsus」",
|
||||||
|
"genreName": 104,
|
||||||
|
"genreNameName": "ゲームバラエティ",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016022,
|
||||||
|
"eventNameName": "261016_02_2:バラエティ1曲追加《Cryogenic》",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26507
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12024,
|
||||||
|
"name": "OV3RCLOCK",
|
||||||
|
"artistName": 1152,
|
||||||
|
"artistNameName": "Alicemetix",
|
||||||
|
"genreName": 105,
|
||||||
|
"genreNameName": "maimai",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261002024,
|
||||||
|
"eventNameName": "261002_02_4:伝導楽曲 1曲(Lock)",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 3,
|
||||||
|
"subLockType": 1,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26506
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": 100018,
|
"id": 100018,
|
||||||
"name": "[協]Love You",
|
"name": "[協]Love You",
|
||||||
@@ -22949,6 +23085,23 @@
|
|||||||
"longMusic": 0,
|
"longMusic": 0,
|
||||||
"version": 24015
|
"version": 24015
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 111537,
|
||||||
|
"name": "[即]Straight into the lights",
|
||||||
|
"artistName": 890,
|
||||||
|
"artistNameName": "Cosmograph",
|
||||||
|
"genreName": 107,
|
||||||
|
"genreNameName": "宴会場",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016023,
|
||||||
|
"eventNameName": "261016_02_3:宴譜面追加3",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 0,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26508
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": 111557,
|
"id": 111557,
|
||||||
"name": "[蛸]不機嫌なスリーカード",
|
"name": "[蛸]不機嫌なスリーカード",
|
||||||
@@ -23051,6 +23204,23 @@
|
|||||||
"longMusic": 0,
|
"longMusic": 0,
|
||||||
"version": 25007
|
"version": 25007
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 111765,
|
||||||
|
"name": "[音]愛包ダンスホール",
|
||||||
|
"artistName": 781,
|
||||||
|
"artistNameName": "HIMEHINA",
|
||||||
|
"genreName": 107,
|
||||||
|
"genreNameName": "宴会場",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261002022,
|
||||||
|
"eventNameName": "261002_02_2:宴譜面追加4",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 0,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26507
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": 111772,
|
"id": 111772,
|
||||||
"name": "[宴]人マニア",
|
"name": "[宴]人マニア",
|
||||||
@@ -23102,6 +23272,23 @@
|
|||||||
"longMusic": 0,
|
"longMusic": 0,
|
||||||
"version": 25502
|
"version": 25502
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 112002,
|
||||||
|
"name": "[覚]3rd Avenue",
|
||||||
|
"artistName": 1138,
|
||||||
|
"artistNameName": "Sound Souler",
|
||||||
|
"genreName": 107,
|
||||||
|
"genreNameName": "宴会場",
|
||||||
|
"AddVersion": 26,
|
||||||
|
"AddVersionName": "CiRCLEPLUS",
|
||||||
|
"eventName": 261016023,
|
||||||
|
"eventNameName": "261016_02_3:宴譜面追加3",
|
||||||
|
"disable": false,
|
||||||
|
"lockType": 0,
|
||||||
|
"subLockType": 0,
|
||||||
|
"longMusic": 0,
|
||||||
|
"version": 26508
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": 120227,
|
"id": 120227,
|
||||||
"name": "[r]Garakuta Doll Play",
|
"name": "[r]Garakuta Doll Play",
|
||||||
|
|||||||
+63
-19
@@ -4,16 +4,20 @@
|
|||||||
// 产物放在 data/ 而不是 assets/:目前没有代码在运行时读它,放 assets/ 会被
|
// 产物放在 data/ 而不是 assets/:目前没有代码在运行时读它,放 assets/ 会被
|
||||||
// 原样打进每个安装包(约 1.2MB)。要接进 App 时再一起挪进 pubspec 的 assets。
|
// 原样打进每个安装包(约 1.2MB)。要接进 App 时再一起挪进 pubspec 的 assets。
|
||||||
//
|
//
|
||||||
// 用法:
|
// 用法(两种根可以混用,后面的覆盖前面的同 ID 行):
|
||||||
// dart run tools/gen_id_mapping.dart --package G:/Package
|
// dart run tools/gen_id_mapping.dart --package G:/Package
|
||||||
|
// dart run tools/gen_id_mapping.dart --package G:/Package --data-root D:/A031OptDump/Root
|
||||||
//
|
//
|
||||||
// 数据布局:`<pkg>/Sinmai_Data/StreamingAssets/A0xx/<表名>/<前缀><dataName>/<表名>.xml`,
|
// `--package` 会自动去找 `<pkg>/Sinmai_Data/StreamingAssets/A0xx/`;
|
||||||
// 一个实体一个目录。真正的 ID 在 XML 的 `<name><id>` 里,目录名只是 dataName,
|
// `--data-root` 直接把给定目录当一个数据根(像 A031OptDump/Root 那种单独导出的包)。
|
||||||
// 二者不相等(例如 `icon/icon550201/Icon.xml` 的 name.id 其实是 1)。
|
|
||||||
//
|
//
|
||||||
// 多个 A0xx 目录要按名字升序合并、后者覆盖同 ID 的行——客户端 DataManager.LoadData
|
// 数据布局:`<root>/<表名>/<前缀><dataName>/<表名>.xml`,一个实体一个目录。
|
||||||
|
// 真正的 ID 在 XML 的 `<name><id>` 里,目录名只是 dataName(`icon/icon550201/Icon.xml`
|
||||||
|
// 的 name.id 其实是 1;`music/music111537` 的 name.id 是 111537,属宴曲段)。
|
||||||
|
//
|
||||||
|
// 多根合并按名字升序、后者覆盖同 ID 的行——客户端 DataManager.LoadData
|
||||||
// 就是这个语义(按 dirs 顺序遍历,`sortedDictionary[key] =` 直接覆写)。
|
// 就是这个语义(按 dirs 顺序遍历,`sortedDictionary[key] =` 直接覆写)。
|
||||||
// 只认形如 `A???` 的目录,其余(Table/、RomConfig.xml 等)跳过。
|
// `--package` 下只认形如 `A???` 的目录,其余(Table/、RomConfig.xml 等)跳过。
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
@@ -90,33 +94,59 @@ const _keySpec = _TableSpec(
|
|||||||
stringIds: ['name', 'gateName'],
|
stringIds: ['name', 'gateName'],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// 本地事件表只有名字与 `alwaysOpen`;**开关的日期窗口不在这里**,
|
||||||
|
/// 是服务器 `GetGameEventApi` 下发的 `GameEvent{startDate,endDate}`。
|
||||||
|
/// 所以这张表能用来看某个 eventName.id 属于什么活动,但判断「现在开没开」
|
||||||
|
/// 得去问服务器。
|
||||||
|
const _eventSpec = _TableSpec(
|
||||||
|
dirName: 'event',
|
||||||
|
xmlFileName: 'Event.xml',
|
||||||
|
scalars: ['infoType', 'alwaysOpen', 'disableArea'],
|
||||||
|
stringIds: ['name'],
|
||||||
|
);
|
||||||
|
|
||||||
void main(List<String> args) {
|
void main(List<String> args) {
|
||||||
final packageRoot = _argValue(args, '--package') ?? 'G:/Package';
|
|
||||||
final outDir = Directory(
|
final outDir = Directory(
|
||||||
_argValue(args, '--out') ?? 'data/id_mapping',
|
_argValue(args, '--out') ?? 'data/id_mapping',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final dataDirs = <String>[];
|
||||||
|
|
||||||
|
// 整合包:先按 A??? 升序铺进来,让高编号的增补包覆盖基础包。
|
||||||
|
final packageRoot = _argValue(args, '--package');
|
||||||
|
if (packageRoot != null) {
|
||||||
final assetsRoot = Directory(
|
final assetsRoot = Directory(
|
||||||
'$packageRoot/Sinmai_Data/StreamingAssets',
|
'${_norm(packageRoot)}/Sinmai_Data/StreamingAssets',
|
||||||
);
|
);
|
||||||
if (!assetsRoot.existsSync()) {
|
if (!assetsRoot.existsSync()) {
|
||||||
stderr.writeln('找不到 $assetsRoot');
|
stderr.writeln('找不到 ${assetsRoot.path}');
|
||||||
exitCode = 1;
|
exitCode = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dataDirs.addAll(
|
||||||
// 升序,让高编号的增补包覆盖基础包,和客户端一致。
|
|
||||||
final dataDirs =
|
|
||||||
assetsRoot
|
assetsRoot
|
||||||
.listSync()
|
.listSync()
|
||||||
.whereType<Directory>()
|
.whereType<Directory>()
|
||||||
.where((d) => RegExp(r'^A\d{3}$').hasMatch(_baseName(d.path)))
|
.map((d) => _norm(d.path))
|
||||||
.map((d) => d.path)
|
.where((p) => RegExp(r'^A\d{3}$').hasMatch(_baseName(p)))
|
||||||
.toList()
|
.toList()
|
||||||
..sort();
|
..sort(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单独导出的数据根(如 D:/A031OptDump/Root),按传参顺序追加,后面的赢。
|
||||||
|
for (final extra in _argValues(args, '--data-root')) {
|
||||||
|
final dir = Directory(_norm(extra));
|
||||||
|
if (!dir.existsSync()) {
|
||||||
|
stderr.writeln('找不到数据根 ${dir.path}');
|
||||||
|
exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dataDirs.add(_norm(extra));
|
||||||
|
}
|
||||||
|
|
||||||
if (dataDirs.isEmpty) {
|
if (dataDirs.isEmpty) {
|
||||||
stderr.writeln('$assetsRoot 下没有 A??? 数据目录');
|
stderr.writeln('没有可用数据根,给 --package 或 --data-root');
|
||||||
exitCode = 1;
|
exitCode = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -128,6 +158,7 @@ void main(List<String> args) {
|
|||||||
final gates = _mergeById(dataDirs, _gateSpec);
|
final gates = _mergeById(dataDirs, _gateSpec);
|
||||||
final courses = _mergeById(dataDirs, _courseSpec);
|
final courses = _mergeById(dataDirs, _courseSpec);
|
||||||
final keys = _mergeById(dataDirs, _keySpec);
|
final keys = _mergeById(dataDirs, _keySpec);
|
||||||
|
final events = _mergeById(dataDirs, _eventSpec);
|
||||||
|
|
||||||
final items = <String, List<Map<String, dynamic>>>{};
|
final items = <String, List<Map<String, dynamic>>>{};
|
||||||
for (final (kind, spec) in _itemTables) {
|
for (final (kind, spec) in _itemTables) {
|
||||||
@@ -143,15 +174,16 @@ void main(List<String> args) {
|
|||||||
'keyList': keys,
|
'keyList': keys,
|
||||||
},
|
},
|
||||||
'items.json': items,
|
'items.json': items,
|
||||||
|
'events.json': events,
|
||||||
'meta.json': {
|
'meta.json': {
|
||||||
'generatedFrom': packageRoot,
|
'dataDirs': dataDirs.map((p) => _baseName(p)).toList(),
|
||||||
'dataDirs': dataDirs.map(_baseName).toList(),
|
|
||||||
'counts': {
|
'counts': {
|
||||||
'music': music.length,
|
'music': music.length,
|
||||||
'map': maps.length,
|
'map': maps.length,
|
||||||
'gate': gates.length,
|
'gate': gates.length,
|
||||||
'course': courses.length,
|
'course': courses.length,
|
||||||
'key': keys.length,
|
'key': keys.length,
|
||||||
|
'event': events.length,
|
||||||
for (final (kind, _) in _itemTables)
|
for (final (kind, _) in _itemTables)
|
||||||
'itemKind$kind': items['$kind']!.length,
|
'itemKind$kind': items['$kind']!.length,
|
||||||
},
|
},
|
||||||
@@ -310,4 +342,16 @@ String? _argValue(List<String> args, String name) {
|
|||||||
return args[i + 1];
|
return args[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
String _baseName(String path) => path.replaceAll(r'\', '/').split('/').last;
|
/// 可重复参数(`--data-root A --data-root B`)。
|
||||||
|
List<String> _argValues(List<String> args, String name) {
|
||||||
|
final out = <String>[];
|
||||||
|
for (var i = 0; i < args.length - 1; i++) {
|
||||||
|
if (args[i] == name) out.add(args[i + 1]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Windows 传参常见反斜杠,统一成 slash 再拼路径。
|
||||||
|
String _norm(String path) => path.replaceAll(r'\', '/');
|
||||||
|
|
||||||
|
String _baseName(String path) => _norm(path).split('/').last;
|
||||||
Reference in new issue
Block a user