package common import ( "encoding/hex" "encoding/json" "fmt" "strconv" ) type StellaJumpCase struct { ID uint32 `json:"GameBoyCaseID"` Challenges []uint32 `json:"GameBoyChallengeIDList"` CheatQuestID uint32 FDGroupID, FDHardInstanceID, FDHardEntityID, FDCheatInstanceID, FDCheatEntityID uint32 } func LoadStellaJumpCases() (map[uint32]StellaJumpCase, error) { raw, err := ReadResourceFile("resources/StellaJumpConfig.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"` Cases []StellaJumpCase `json:"cases"` } 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 == "" || len(resource.Cases) == 0 { return nil, fmt.Errorf("Stella Jump catalog provenance invalid") } cases := make(map[uint32]StellaJumpCase, len(resource.Cases)) for _, row := range resource.Cases { if _, exists := cases[row.ID]; row.ID == 0 || exists || row.FDGroupID == 0 || row.FDHardInstanceID == 0 || row.FDCheatInstanceID == 0 { return nil, fmt.Errorf("Stella Jump case invalid") } seen := map[uint32]bool{} for _, id := range row.Challenges { if id == 0 || seen[id] { return nil, fmt.Errorf("Stella Jump challenge invalid") } seen[id] = true } cases[row.ID] = row } return cases, nil } type StellaJumpRecord struct { BestTime uint64 `json:"best_time"` FinishedChallenges []uint32 `json:"finished_challenges"` CheatInput bool `json:"cheat_input"` CheatMode bool `json:"cheat_mode"` } func SaveStellaJumpCheatSetting(id uint32, input bool, enabled bool) error { cases, err := LoadStellaJumpCases() if err != nil { return err } if _, ok := cases[id]; !ok { return fmt.Errorf("unknown Stella Jump case %d", id) } persistentWriteMu.Lock() defer persistentWriteMu.Unlock() unlock, err := lockPersistentFile() if err != nil { return err } defer unlock() root, err := readCurrencyWarsPersistentRoot() if err != nil { return err } records := map[uint32]StellaJumpRecord{} if raw, exists := root["stella_jump"]; exists { if err = json.Unmarshal(raw, &records); err != nil { return err } if records == nil { records = map[uint32]StellaJumpRecord{} } } record := records[id] if input { record.CheatInput = enabled } else { record.CheatMode = enabled } records[id] = record root["stella_jump"], err = json.Marshal(records) if err != nil { return err } return writeJSONAtomic("persistent", root) } func ReadStellaJumpRecord(id uint32) (StellaJumpRecord, error) { root, err := readCurrencyWarsPersistentRoot() if err != nil { return StellaJumpRecord{}, err } var records map[uint32]StellaJumpRecord if raw, exists := root["stella_jump"]; exists { if err = json.Unmarshal(raw, &records); err != nil { return StellaJumpRecord{}, err } } return records[id], nil } // Merge under the shared process/file locks. An old game session cannot replace // another case's score, CW snapshot, or a switch changed through the SDK. func SaveStellaJumpResult(id uint32, elapsed uint64, challenges []uint32) (StellaJumpRecord, error) { cases, err := LoadStellaJumpCases() if err != nil { return StellaJumpRecord{}, err } row, ok := cases[id] if !ok { return StellaJumpRecord{}, fmt.Errorf("unknown Stella Jump case %d", id) } allowed := map[uint32]bool{} for _, v := range row.Challenges { allowed[v] = true } for _, v := range challenges { if !allowed[v] { return StellaJumpRecord{}, fmt.Errorf("challenge %d not in case %d", v, id) } } persistentWriteMu.Lock() defer persistentWriteMu.Unlock() unlock, err := lockPersistentFile() if err != nil { return StellaJumpRecord{}, err } defer unlock() root, err := readCurrencyWarsPersistentRoot() if err != nil { return StellaJumpRecord{}, err } records := map[uint32]StellaJumpRecord{} if raw, exists := root["stella_jump"]; exists { if err = json.Unmarshal(raw, &records); err != nil { return StellaJumpRecord{}, err } if records == nil { records = map[uint32]StellaJumpRecord{} } } record := records[id] if elapsed != 0 && (record.BestTime == 0 || elapsed < record.BestTime) { record.BestTime = elapsed } seen := map[uint32]bool{} for _, v := range record.FinishedChallenges { seen[v] = true } for _, v := range challenges { if !seen[v] { record.FinishedChallenges = append(record.FinishedChallenges, v) seen[v] = true } } records[id] = record encoded, err := json.Marshal(records) if err != nil { return StellaJumpRecord{}, err } root["stella_jump"] = encoded if err = writeJSONAtomic("persistent", root); err != nil { return StellaJumpRecord{}, fmt.Errorf("save Stella Jump case %s: %w", strconv.FormatUint(uint64(id), 10), err) } return record, nil }