Files
GaCy-GO/internal/common/currency_wars_role_compatibility.go
T
2026-10-28 18:54:43 +07:00

96 lines
3.6 KiB
Go

package common
import (
"encoding/hex"
"encoding/json"
"fmt"
)
// The base capture remains identified by its original assembly. A separately
// proven overlay supplies replacements removed from the current Client. It is
// not a claim that every other CW table has been recaptured for that assembly.
func (c *CurrencyWarsCatalog) applyRoleCompatibility() error {
raw, err := ReadResourceFile("resources/CurrencyWarsRoleCompatibility.json")
if err != nil {
return err
}
var overlay struct {
Format uint32 `json:"format_version"`
Source string `json:"source_assembly_sha256"`
Target string `json:"target_assembly_sha256"`
Capture string `json:"capture_sha256"`
Entries []struct {
Previous uint32 `json:"previous_role_id"`
Current uint32 `json:"current_role_id"`
Variants []CurrencyWarsRoleVariant `json:"variants"`
} `json:"entries"`
}
if err = json.Unmarshal(raw, &overlay); err != nil {
return err
}
if overlay.Format != 1 || overlay.Source != c.Provenance.GameAssemblySHA256 {
return fmt.Errorf("CW role compatibility source differs")
}
for _, hash := range []string{overlay.Target, overlay.Capture} {
b, e := hex.DecodeString(hash)
if e != nil || len(b) != 32 {
return fmt.Errorf("CW role compatibility evidence missing")
}
}
c.roleReplacements = map[uint32]uint32{}
for _, entry := range overlay.Entries {
if _, duplicate := c.roleReplacements[entry.Previous]; duplicate {
return fmt.Errorf("duplicate CW role replacement source")
}
previous, ok := c.roleByID[entry.Previous]
if !ok || entry.Current == 0 || entry.Previous == entry.Current || len(entry.Variants) == 0 {
return fmt.Errorf("invalid CW role replacement")
}
if _, duplicate := c.roleByID[entry.Current]; duplicate {
return fmt.Errorf("CW role replacement already exists")
}
first := entry.Variants[0]
if first.Config.AvatarID != previous.Config.AvatarID || first.Config.SeasonID != previous.Config.SeasonID || first.Star != previous.DefaultStar || uint32(len(entry.Variants)) != first.MaxStar {
return fmt.Errorf("CW replacement family or star coverage differs")
}
current := CurrencyWarsCapturedRole{Config: first.Config, Price: first.Price, MaxStar: first.MaxStar, DefaultStar: first.Star}
c.roleByID[entry.Current] = current
sale, e := c.BaseSalePrice(entry.Current, first.Star)
if e != nil {
return e
}
current.DefaultSellPrice = sale
c.roleByID[entry.Current] = current
stars := map[uint32]bool{}
for _, v := range entry.Variants {
var star struct{ ID, Star uint32 }
if json.Unmarshal(v.StarConfig, &star) != nil || v.Config.ID != entry.Current || star.ID != entry.Current || star.Star != v.Star || v.Star == 0 || v.Star > first.MaxStar || stars[v.Star] || v.Config.AvatarID != previous.Config.AvatarID || v.Config.SeasonID != previous.Config.SeasonID {
return fmt.Errorf("CW replacement star identity differs")
}
stars[v.Star] = true
c.variantByKey[[2]uint32{entry.Current, v.Star}] = v
}
for i := range c.CapturedRoles {
if c.CapturedRoles[i].Config.ID == entry.Previous {
c.CapturedRoles[i] = current
}
}
kept := make([]CurrencyWarsRoleVariant, 0, len(c.CapturedVariants)+len(entry.Variants))
for _, v := range c.CapturedVariants {
if v.Config.ID != entry.Previous {
kept = append(kept, v)
}
}
c.CapturedVariants = append(kept, entry.Variants...)
c.roleReplacements[entry.Previous] = entry.Current
}
return nil
}
func (c *CurrencyWarsCatalog) CurrentRoleID(id uint32) uint32 {
if current := c.roleReplacements[id]; current != 0 {
return current
}
return id
}