77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
// 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 <persistent-backup>")
|
|
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))
|
|
}
|
|
}
|