168 lines
4.1 KiB
Go
168 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"cmp"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
type DumpCard struct {
|
|
ID int `json:"card_id"`
|
|
Level int `json:"talent_level"`
|
|
Rarity int `json:"rarity"`
|
|
CreateTime int `json:"create_time"`
|
|
}
|
|
|
|
type DumpSupport struct {
|
|
ID int `json:"supportCardId"`
|
|
Level int `json:"level"`
|
|
LB int `json:"limitBreakCount"`
|
|
MaxLevel int `json:"maxLevel"`
|
|
CreateTime int `json:"createTime"`
|
|
Exp int `json:"exp"`
|
|
Stock int `json:"stock"`
|
|
Favorite bool `json:"isFavoriteLock"`
|
|
BestTraining int `json:"bestTraining"`
|
|
}
|
|
|
|
type Collection struct {
|
|
App string `json:"app"` // "gametora"
|
|
Game string `json:"game"` // "umamusume"
|
|
Type string `json:"type"` // "collection"
|
|
Version int `json:"version"` // 4
|
|
Timestamp string `json:"timestamp"` // time.Now().String()
|
|
Servers map[string]CollectionServer `json:"servers"`
|
|
}
|
|
|
|
type CollectionServer struct {
|
|
CharCards map[string]int `json:"charCards"`
|
|
Supports map[string]int `json:"supports"`
|
|
Veterans struct{} `json:"-"` // `json:"veterans"`
|
|
}
|
|
|
|
type Localize struct {
|
|
CardID int `json:"card_id"` // character card, or trainee
|
|
SupportID int `json:"id"` // support card
|
|
TID string `json:"tid"`
|
|
// many other fields we don't need
|
|
}
|
|
|
|
func decode[T any](r io.Reader) ([]T, error) {
|
|
var l []T
|
|
d := json.NewDecoder(r)
|
|
err := d.Decode(&l)
|
|
return l, err
|
|
}
|
|
|
|
func tidMap(ctx context.Context, url string) (map[int]string, error) {
|
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := decode[Localize](resp.Body)
|
|
m := make(map[int]string, len(l))
|
|
for _, v := range l {
|
|
m[cmp.Or(v.CardID, v.SupportID)] = v.TID
|
|
}
|
|
return m, err
|
|
}
|
|
|
|
func filejson[T any](path string) ([]T, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return decode[T](f)
|
|
}
|
|
|
|
func main() {
|
|
ts := time.Now().UTC().Format(time.DateOnly) // can't use RFC3339 because :s in filename
|
|
var (
|
|
dumpDir string
|
|
out string
|
|
)
|
|
flag.StringVar(&dumpDir, "dump", ".", "`dir`ectory containing umadump output files")
|
|
flag.StringVar(&out, "o", fmt.Sprintf("uma-col-all-%s.json", ts), "output `file` (stdout if empty)")
|
|
flag.Parse()
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
go func() {
|
|
<-ctx.Done()
|
|
stop()
|
|
}()
|
|
|
|
var err error
|
|
w := os.Stdout
|
|
if out != "" {
|
|
w, err = os.Create(out)
|
|
if err != nil {
|
|
fatal("output", slog.Any("err", err))
|
|
}
|
|
}
|
|
|
|
charas, err := filejson[DumpCard](filepath.Join(dumpDir, "card_data.json"))
|
|
if err != nil {
|
|
fatal("character dump", slog.Any("err", err))
|
|
}
|
|
supports, err := filejson[DumpSupport](filepath.Join(dumpDir, "support_card_data.json"))
|
|
if err != nil {
|
|
fatal("support card dump", slog.Any("err", err))
|
|
}
|
|
charaTID, err := tidMap(ctx, "https://gametora.com/loc/umamusume/character_cards.json")
|
|
if err != nil {
|
|
fatal("character localization file", slog.Any("err", err))
|
|
}
|
|
supportTID, err := tidMap(ctx, "https://gametora.com/loc/umamusume/support_cards.json")
|
|
if err != nil {
|
|
fatal("support card localization file", slog.Any("err", err))
|
|
}
|
|
|
|
charaLevels := make(map[string]int, len(charas))
|
|
for _, v := range charas {
|
|
charaLevels[charaTID[v.ID]] = v.Rarity
|
|
}
|
|
supportLevels := make(map[string]int, len(supports))
|
|
for _, v := range supports {
|
|
supportLevels[supportTID[v.ID]] = v.LB
|
|
}
|
|
|
|
collection := Collection{
|
|
App: "gametora",
|
|
Game: "umamusume",
|
|
Type: "collection",
|
|
Version: 4,
|
|
Timestamp: ts,
|
|
Servers: map[string]CollectionServer{
|
|
"en": {
|
|
CharCards: charaLevels,
|
|
Supports: supportLevels,
|
|
},
|
|
},
|
|
}
|
|
b, err := json.Marshal(collection)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if _, err := w.Write(b); err != nil {
|
|
fatal("writing output", slog.Any("err", err))
|
|
}
|
|
}
|
|
|
|
func fatal(msg string, args ...any) {
|
|
slog.Error(msg, args...)
|
|
os.Exit(1)
|
|
}
|