96 lines
3.6 KiB
Go
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
|
|
}
|