122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
"zombiezen.com/go/sqlite"
|
|
"zombiezen.com/go/sqlite/sqlitex"
|
|
|
|
"git.sunturtle.xyz/zephyr/horse/mdb"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
mdbf string
|
|
out string
|
|
region string
|
|
)
|
|
flag.StringVar(&mdbf, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb")
|
|
flag.StringVar(&out, "o", `.`, "`dir`ectory for output files")
|
|
flag.StringVar(®ion, "region", "global", "region the database is for (global, jp)")
|
|
flag.Parse()
|
|
|
|
slog.Info("open", slog.String("mdb", mdbf))
|
|
db, err := sqlitex.NewPool(mdbf, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
|
|
if err != nil {
|
|
slog.Error("opening mdb", slog.String("mdb", mdbf), slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
go func() {
|
|
<-ctx.Done()
|
|
stop()
|
|
}()
|
|
|
|
loadgroup, ctx1 := errgroup.WithContext(ctx)
|
|
charas := load(ctx1, loadgroup, db, "characters", mdb.Characters)
|
|
aff := load(ctx1, loadgroup, db, "pair affinity", mdb.AffinitySummary)
|
|
umas := load(ctx1, loadgroup, db, "umas", mdb.Umas)
|
|
sg := load(ctx1, loadgroup, db, "skill groups", mdb.SkillGroups)
|
|
skills := load(ctx1, loadgroup, db, "skills", mdb.Skills)
|
|
races := load(ctx1, loadgroup, db, "races", mdb.Races)
|
|
saddles := load(ctx1, loadgroup, db, "saddles", mdb.Saddles)
|
|
scenarios := load(ctx1, loadgroup, db, "scenarios", mdb.Scenarios)
|
|
sparks := load(ctx1, loadgroup, db, "sparks", mdb.Sparks)
|
|
convos := load(ctx1, loadgroup, db, "lobby conversations", mdb.Conversations)
|
|
|
|
if err := os.MkdirAll(filepath.Join(out, region), 0775); err != nil {
|
|
slog.Error("create output dir", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
writegroup, ctx2 := errgroup.WithContext(ctx)
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "character.json", charas) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "affinity.json", aff) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "uma.json", umas) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "skill-group.json", sg) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "skill.json", skills) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "race.json", races) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "saddle.json", saddles) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "scenario.json", scenarios) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "spark.json", sparks) })
|
|
writegroup.Go(func() error { return write(ctx2, out, region, "conversation.json", convos) })
|
|
if err := writegroup.Wait(); err != nil {
|
|
slog.ErrorContext(ctx, "write", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
slog.InfoContext(ctx, "done")
|
|
}
|
|
|
|
func load[T any](ctx context.Context, group *errgroup.Group, db *sqlitex.Pool, kind string, get func(context.Context, *sqlitex.Pool) ([]T, error)) func() ([]T, error) {
|
|
slog.InfoContext(ctx, "load", slog.String("kind", kind))
|
|
var r []T
|
|
group.Go(func() error {
|
|
got, err := get(ctx, db)
|
|
r = got
|
|
return err
|
|
})
|
|
return func() ([]T, error) {
|
|
err := group.Wait()
|
|
if err == context.Canceled {
|
|
// After the first wait, all future ones return context.Canceled.
|
|
// We want to be able to wait any number of times, so hide it.
|
|
err = nil
|
|
}
|
|
return r, err
|
|
}
|
|
}
|
|
|
|
func write[T any](ctx context.Context, out, region, name string, v func() (T, error)) error {
|
|
p := filepath.Join(out, region, name)
|
|
r, err := v()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
slog.InfoContext(ctx, "write", slog.String("path", p))
|
|
f, err := os.Create(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
w := bufio.NewWriter(f)
|
|
enc := json.NewEncoder(w)
|
|
enc.SetEscapeHTML(false)
|
|
enc.SetIndent("", "\t")
|
|
err = enc.Encode(r)
|
|
err = errors.Join(err, w.Flush())
|
|
slog.InfoContext(ctx, "marshaled", slog.String("path", p))
|
|
return err
|
|
}
|