Skip to content

Commit c30dc0b

Browse files
committed
fix: C2sParser.ParseNote ALD/ASD Cell/Width 错误赋值
重构为各分支独立设置字段,ALD/ASD 不再从 p[3]/p[4] 误设 Cell/Width
1 parent 7f19976 commit c30dc0b

2 files changed

Lines changed: 76 additions & 6 deletions

File tree

parser/chu/C2sParser.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,25 @@ private static void ParseTiming(string[] p, C2sChart chart)
8686
private static void ParseNote(string[] p, C2sChart chart, List<Alert> alerts, int lineNum)
8787
{
8888
var tag = p[0].ToUpperInvariant();
89-
var note = new ChuNote { Type = tag, Measure = Int(p, 1), Offset = Int(p, 2), Cell = Int(p, 3), Width = Math.Max(1, Int(p, 4, 1)) };
89+
var note = new ChuNote { Type = tag, Measure = Int(p, 1), Offset = Int(p, 2) };
9090

9191
switch (tag)
9292
{
93-
case "TAP": case "MNE": break;
94-
case "CHR": note.Extra = Str(p, 5); break;
95-
case "HLD": case "HXD": note.HoldDuration = Int(p, 5); break;
93+
case "TAP": case "MNE":
94+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1)); break;
95+
case "CHR":
96+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1)); note.Extra = Str(p, 5); break;
97+
case "HLD": case "HXD":
98+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1)); note.HoldDuration = Int(p, 5); break;
9699
case "SLD": case "SLC": case "SXD": case "SXC":
100+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1));
97101
note.SlideDuration = Int(p, 5); note.EndCell = Int(p, 6); note.EndWidth = Math.Max(1, Int(p, 7, 1)); break;
98-
case "FLK": note.Extra = Str(p, 5); break;
102+
case "FLK":
103+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1)); note.Extra = Str(p, 5); break;
99104
case "AIR": case "AUR": case "AUL": case "ADW": case "ADR": case "ADL":
100-
note.TargetNote = Str(p, 5); break;
105+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1)); note.TargetNote = Str(p, 5); break;
101106
case "AHD":
107+
note.Cell = Int(p, 3); note.Width = Math.Max(1, Int(p, 4, 1));
102108
note.TargetNote = Str(p, 5); note.AirHoldDuration = Int(p, 6); break;
103109
case "ALD": case "ASD":
104110
note.StartHeight = Int(p, 3); note.SlideDuration = Int(p, 4);

tests/chu/README-chu.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# MuConvert.chu — CHUNITHM 谱面格式支持
2+
3+
## 支持的格式
4+
5+
| 格式 | 类型 | tick 单位 | 说明 |
6+
|------|------|-----------|------|
7+
| C2S | 官方 | 384/小节 | 游戏引擎原生格式,tab 分隔纯谱面数据 |
8+
| UGC | 社区 | 480/拍 | UMIGURI 编辑器格式,含完整元数据 |
9+
| SUS | 社区 | 480/拍 | SUSPlayer/Seaurchin 格式,十六进制编码 |
10+
11+
## 架构
12+
13+
```
14+
Parser → IR → Generator
15+
16+
C2sParser → C2sChart ─┬─→ C2sGenerator
17+
UgcParser → UgcChart ─┼─→ UgcGenerator
18+
SusParser → SusChart ─┴─→ SusGenerator
19+
20+
IChuChart (统一接口)
21+
```
22+
23+
## 用法
24+
25+
```csharp
26+
// C2S → UGC
27+
var c2s = new C2sParser().Parse(File.ReadAllText("0003_00.c2s"));
28+
var ml = MusicXmlParser.Parse("Music.xml");
29+
var ugc = new C2sToUgcConverter().Convert(c2s, ml);
30+
File.WriteAllText("output.ugc", UgcSerializer.Serialize(ugc));
31+
32+
// UGC → C2S
33+
var ugc = new UgcParser().Parse(File.ReadAllText("basic.ugc"));
34+
var c2s = new UgcToC2sConverter().Convert(ugc);
35+
File.WriteAllText("output.c2s", C2sSerializer.Serialize(c2s));
36+
37+
// C2S → SUS
38+
var sus = new C2sToSusConverter().Convert(c2s, ml.Title, ml.Artist);
39+
File.WriteAllText("output.sus", SusSerializer.Serialize(sus));
40+
```
41+
42+
## 格式差异
43+
44+
| 差异项 | C2S | UGC | SUS |
45+
|--------|-----|-----|-----|
46+
| 分隔符 | tab | tab | 无(紧凑) |
47+
| 音符编码 | `TAP 5 0 8 4` | `#5'0:t84` | `#05000:010808` |
48+
| cell/width | 十进制 | 十六进制 | 十六进制 |
49+
| 元数据 | Music.xml | 内嵌 @HEADER | #HEADER |
50+
| EX 音符 | HXD/SXD/SXC | 映射为基础类型 | 映射为基础类型 |
51+
| 速度控制 | SFL | @TIL | 不支持 |
52+
53+
## 往返精度
54+
55+
- C2S ↔ UGC:227/227 一致,仅同位置音符排序不同
56+
- 时长缩放:`c2sTick = ugcTick × 96 / 480`
57+
58+
## 测试
59+
60+
```bash
61+
dotnet test --filter "FullyQualifiedName~ChuTests"
62+
```
63+
64+
5 项测试覆盖 C2S/UGC 解析、往返、跨格式转换,用官谱和社区谱为样本。

0 commit comments

Comments
 (0)