package common import ( "encoding/hex" "encoding/json" "fmt" ) type ComicDessertCycle struct { ID uint32 ProgressAmount uint32 LevelIDList []uint32 } type ComicDessertLevel struct { ID uint32 LevelConfigPath string RequiredCoin uint32 } type ComicDessertCatalog struct { Cycles []ComicDessertCycle Levels []ComicDessertLevel } // Read the two captured flat tables. Never initialize gameplay by inventing a // cycle/level ID, and never write user progress from this catalog reader. func LoadComicDessertCatalog() (*ComicDessertCatalog, error) { raw, err := ReadResourceFile("resources/ComicDessertCatalog.json") if err != nil { return nil, err } var resource struct { Format uint32 `json:"format_version"` Assembly string `json:"client_assembly_sha256"` Evidence string `json:"evidence_id"` ComicDessertCatalog } if err = json.Unmarshal(raw, &resource); err != nil { return nil, err } digest, err := hex.DecodeString(resource.Assembly) if err != nil || len(digest) != 32 || resource.Format != 1 || resource.Evidence == "" { return nil, fmt.Errorf("ComicDessert catalog provenance invalid") } raw, err = ReadResourceFile("resources/PreviewGameplayCatalog.json") if err != nil { return nil, err } var client struct { Assembly string `json:"client_assembly_sha256"` } if err = json.Unmarshal(raw, &client); err != nil || client.Assembly != resource.Assembly { return nil, fmt.Errorf("ComicDessert/client catalog identity mismatch") } levels := make(map[uint32]bool, len(resource.Levels)) for _, level := range resource.Levels { if level.ID == 0 || level.LevelConfigPath == "" || levels[level.ID] { return nil, fmt.Errorf("invalid ComicDessert level catalog") } levels[level.ID] = true } cycles := make(map[uint32]bool, len(resource.Cycles)) for _, cycle := range resource.Cycles { if cycle.ID == 0 || cycles[cycle.ID] || len(cycle.LevelIDList) == 0 { return nil, fmt.Errorf("invalid ComicDessert cycle catalog") } cycles[cycle.ID] = true for _, id := range cycle.LevelIDList { if !levels[id] { return nil, fmt.Errorf("ComicDessert cycle references absent level %d", id) } } } if len(cycles) == 0 || len(levels) == 0 { return nil, fmt.Errorf("ComicDessert catalog is empty") } return &resource.ComicDessertCatalog, nil } func (c *ComicDessertCatalog) InitialCycle() ComicDessertCycle { var first ComicDessertCycle for _, cycle := range c.Cycles { if first.ID == 0 || cycle.ProgressAmount < first.ProgressAmount || (cycle.ProgressAmount == first.ProgressAmount && cycle.ID < first.ID) { first = cycle } } return first }