// pvfdigestrestructure 重构 etc/storydigest/digest.etc 的段边界与 [ending], // 使 13 个城镇区域的过场能在各自开区等级所在的段播出。 // // 只改 digest.etc 的整数 token,不动 scene.etc、不新增共享池字符串。 package main import ( "encoding/binary" "flag" "fmt" "os" "path/filepath" platformpvf "longheng.io/server/internal/platform/pvf" ) // 实测的池引用(digest.etc) const ( refLevel = 0x003566E2 // "[level]" refEnding = 0x025138D6 // "[ending]" ) type seg struct { idx int lo, hi int ending int } var layout = []seg{ {1, 17, 23, 901}, {2, 24, 29, 902}, {3, 30, 34, 903}, {4, 35, 35, 904}, {5, 36, 39, 905}, {6, 40, 49, 906}, {7, 50, 62, 912}, {8, 63, 70, 908}, {9, 71, 80, 909}, {10, 81, 84, 900}, {11, 85, 86, 915}, {12, 87, 88, 916}, {13, 89, 89, 917}, {14, 90, 90, 913}, {15, 91, 91, 911}, {16, 92, 92, 910}, {17, 93, 999, 907}, } func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } func put32(b []byte, v uint32) { binary.LittleEndian.PutUint32(b, v) } func main() { input := flag.String("pvf", "", "input PVF") dir := flag.String("dir", "", "raw patch directory output") dry := flag.Bool("dry-run", false, "只打印,不写文件") flag.Parse() archive, err := platformpvf.Open(*input) if err != nil { panic(err) } raw, err := archive.ReadRaw("etc/storydigest/digest.etc") if err != nil { panic(err) } toks := len(raw) / 5 isSec := func(i int, ref uint32) bool { return i < toks && raw[i*5] == 3 && u32(raw[i*5+1:i*5+5]) == ref } isInt := func(i int) bool { return i < toks && raw[i*5] == 0 } fmt.Println("digest.etc 令牌:", toks) type slot struct{ loTok, hiTok, endTok int } var slots []slot for i := 0; i < toks; i++ { if !isSec(i, refLevel) { continue } if !isInt(i+1) || !isInt(i+2) { panic(fmt.Sprintf("[level]@%d 后不是两个整数", i)) } endTok := -1 for j := i + 3; j < toks; j++ { if isSec(j, refEnding) { if !isInt(j + 1) { panic(fmt.Sprintf("[ending]@%d 后不是整数", j)) } endTok = j + 1 break } if isSec(j, refLevel) { break } } if endTok < 0 { panic(fmt.Sprintf("第 %d 个 [level] 后找不到 [ending]", len(slots)+1)) } slots = append(slots, slot{i + 1, i + 2, endTok}) } fmt.Println("解析出段数:", len(slots)) if len(slots) != len(layout) { panic(fmt.Sprintf("段数 %d 与布局 %d 不一致", len(slots), len(layout))) } fmt.Println("--- 现状 ---") for i, s := range slots { fmt.Printf(" 段%-3d level %5d-%-5d ending %d\n", i+1, u32(raw[s.loTok*5+1:]), u32(raw[s.hiTok*5+1:]), u32(raw[s.endTok*5+1:])) } for i, s := range slots { w := layout[i] put32(raw[s.loTok*5+1:], uint32(w.lo)) put32(raw[s.hiTok*5+1:], uint32(w.hi)) put32(raw[s.endTok*5+1:], uint32(w.ending)) } fmt.Println("--- 应用后 ---") ok := true for i, s := range slots { lo := int(u32(raw[s.loTok*5+1:])) hi := int(u32(raw[s.hiTok*5+1:])) e := int(u32(raw[s.endTok*5+1:])) mark := "OK" if lo != layout[i].lo || hi != layout[i].hi || e != layout[i].ending { mark = "MISMATCH" ok = false } fmt.Printf(" 段%-3d level %5d-%-5d ending %-4d %s\n", i+1, lo, hi, e, mark) } if !ok { panic("应用结果与期望不符") } if *dry { fmt.Println("dry-run,未写文件") return } full := filepath.Join(*dir, "etc", "storydigest", "digest.etc") if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil { panic(err) } if err := os.WriteFile(full, raw, 0o644); err != nil { panic(err) } fmt.Println("已写出:", full, len(raw), "字节") }