// Read-only recovery inspection: decode a saved CW envelope without loading a // player session, migrating data, contacting the Client or writing persistent. package main import ( "encoding/json" "fmt" "os" "strconv" pb "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/proto" ) func main() { if len(os.Args) != 2 { fmt.Fprintln(os.Stderr, "usage: go run tools/inspect_saved_cw.go ") os.Exit(2) } raw, err := os.ReadFile(os.Args[1]) if err != nil { panic(err) } var root struct { CW map[string]struct { UID uint32 `json:"uid"` Version string `json:"client_version"` Assembly string `json:"assembly_sha256"` Revision uint64 `json:"revision"` Wire []byte `json:"wire_data"` } `json:"currency_wars"` } if err := json.Unmarshal(raw, &root); err != nil { panic(err) } for key, save := range root.CW { if key != strconv.FormatUint(uint64(save.UID), 10) { panic("CW account key differs from envelope UID") } var data pb.GridFightGetDataScRsp if err := proto.Unmarshal(save.Wire, &data); err != nil { panic(err) } if data.Retcode != 0 || data.LFLCDNGALME == nil { panic("CW response lacks valid permanent metadata") } summary := map[string]any{"uid": save.UID, "revision": save.Revision, "client_version": save.Version, "assembly_sha256": save.Assembly, "active_run": data.DHFOFLFCLAH != nil} if run := data.DHFOFLFCLAH; run != nil { summary["run_id"] = run.UniqueId if pending := run.FMIGOKIDKJL; pending != nil { summary["queue"] = pending.QueuePosition summary["pending_kind"] = fmt.Sprintf("%T", pending.FGLIODAJFML) if offer := pending.GetIIDBLCFAAPC(); offer != nil { summary["bonfire_offer"] = offer } } for _, component := range run.NAGDJGMMCBE { if component == nil { continue } if stage := component.GetKLCPAAHDPJF(); stage != nil { summary["route"] = stage.EMOHLJHPOPC summary["chapter"] = stage.DCAHJELNJNP summary["section"] = stage.SectionId summary["boss_info"] = stage.BossInfo summary["camps"] = stage.HEKMEJLMGFF } } } out, err := json.Marshal(summary) if err != nil { panic(err) } fmt.Println(string(out)) } }