dorfylegends/analyze/df/structure.go

349 lines
6.5 KiB
Go
Raw Permalink Normal View History

2022-04-14 18:39:18 +03:00
package df
import (
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"os"
2022-04-14 20:06:52 +03:00
"path/filepath"
2022-04-14 18:39:18 +03:00
"strconv"
"strings"
2022-04-18 13:13:38 +03:00
"github.com/cheggaaa/pb/v3"
2022-04-14 18:39:18 +03:00
"github.com/iancoleman/strcase"
"github.com/robertjanetzko/LegendsBrowser2/backend/util"
)
2022-04-30 10:04:33 +03:00
func AnalyzeStructure(filex string, a *AnalyzeData) error {
2022-04-14 20:06:52 +03:00
fmt.Println("Search...", filex)
2022-04-15 16:27:51 +03:00
files, err := filepath.Glob(filex + "/*-legends.xml")
2022-04-14 20:06:52 +03:00
if err != nil {
return err
}
fmt.Println(files)
2022-04-30 10:04:33 +03:00
if a == nil {
a = NewAnalyzeData()
}
2022-04-14 20:06:52 +03:00
for _, file := range files {
analyze(file, a)
}
return a.Save()
}
2022-04-14 18:39:18 +03:00
type FieldData struct {
IsString bool
2022-04-16 21:34:19 +03:00
NoBool bool
2022-04-14 18:39:18 +03:00
Multiple bool
Base bool
Plus bool
2022-04-18 13:13:38 +03:00
Values map[string]bool
Enum bool
2022-04-14 18:39:18 +03:00
}
func NewFieldData() *FieldData {
2022-04-18 13:13:38 +03:00
return &FieldData{
Enum: true,
Values: make(map[string]bool),
}
2022-04-14 18:39:18 +03:00
}
type AnalyzeData struct {
2022-04-29 15:21:27 +03:00
Fields map[string]*FieldData
SubTypes map[string]*map[string]*Subtype
2022-04-30 10:04:33 +03:00
Overwrites *Overwrites `json:"-"`
2022-04-14 18:39:18 +03:00
}
func NewAnalyzeData() *AnalyzeData {
return &AnalyzeData{
2022-04-15 16:27:51 +03:00
Fields: make(map[string]*FieldData, 0),
SubTypes: make(map[string]*map[string]*Subtype),
2022-04-14 18:39:18 +03:00
}
}
func (a *AnalyzeData) Save() error {
file, err := json.MarshalIndent(a, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile("analyze.json", file, 0644)
}
func LoadAnalyzeData() (*AnalyzeData, error) {
data, err := ioutil.ReadFile("analyze.json")
if err != nil {
return nil, err
}
a := NewAnalyzeData()
json.Unmarshal(data, a)
2022-04-29 15:21:27 +03:00
data, err = ioutil.ReadFile("overwrites.json")
if err != nil {
return nil, err
}
overwrites := &Overwrites{}
json.Unmarshal(data, overwrites)
a.Overwrites = overwrites
2022-04-14 18:39:18 +03:00
return a, nil
}
func (a *AnalyzeData) GetField(s string) *FieldData {
if f, ok := a.Fields[s]; ok {
return f
} else {
2022-04-18 13:13:38 +03:00
f := NewFieldData()
2022-04-14 18:39:18 +03:00
a.Fields[s] = f
return f
}
}
2022-04-15 16:27:51 +03:00
func (a *AnalyzeData) GetSubType(s string, t string) *Subtype {
var (
st *map[string]*Subtype
su *Subtype
ok bool
)
if st, ok = a.SubTypes[s]; !ok {
x := make(map[string]*Subtype)
a.SubTypes[s] = &x
st = &x
}
if su, ok = (*st)[t]; !ok {
x := Subtype{Name: t}
(*st)[t] = &x
su = &x
}
return su
}
type AnalyzeContext struct {
2022-04-25 21:16:49 +03:00
file string
plus bool
subtypes map[string]map[int]string
overwrites *Overwrites
}
2022-04-29 15:21:27 +03:00
type AdditionalField struct {
Name string
Type string
}
2022-04-25 21:16:49 +03:00
type Overwrites struct {
2022-04-29 15:21:27 +03:00
ForceEnum map[string]bool
AdditionalFields map[string][]AdditionalField
2022-05-03 22:39:00 +03:00
Relations map[string]string
2022-04-15 16:27:51 +03:00
}
2022-04-14 18:39:18 +03:00
func analyze(file string, a *AnalyzeData) error {
2022-04-25 21:16:49 +03:00
data, err := ioutil.ReadFile("overwrites.json")
if err != nil {
return err
}
overwrites := &Overwrites{}
json.Unmarshal(data, overwrites)
2022-04-15 16:27:51 +03:00
ctx := AnalyzeContext{
2022-04-25 21:16:49 +03:00
file: file,
plus: false,
subtypes: make(map[string]map[int]string),
overwrites: overwrites,
2022-04-15 16:27:51 +03:00
}
// base file
2022-04-18 13:13:38 +03:00
fi, err := os.Stat(file)
if err != nil {
return err
}
size := fi.Size()
bar := pb.Full.Start64(size)
2022-04-14 18:39:18 +03:00
xmlFile, err := os.Open(file)
if err != nil {
2022-04-15 16:27:51 +03:00
return err
2022-04-14 18:39:18 +03:00
}
2022-04-18 13:13:38 +03:00
fmt.Println("\nAnalyzing", file)
2022-04-15 16:27:51 +03:00
defer xmlFile.Close()
2022-04-18 13:13:38 +03:00
converter := util.NewConvertReader(xmlFile)
barReader := bar.NewProxyReader(converter)
_, err = analyzeElement(xml.NewDecoder(barReader), a, make([]string, 0), &ctx)
2022-04-15 16:27:51 +03:00
if err != nil {
return err
}
2022-04-18 13:13:38 +03:00
bar.Finish()
2022-04-15 16:27:51 +03:00
// plus file
ctx.plus = true
file = strings.Replace(file, "-legends.xml", "-legends_plus.xml", 1)
2022-04-18 13:13:38 +03:00
fi, err = os.Stat(file)
if err != nil {
return err
}
size = fi.Size()
bar = pb.Full.Start64(size)
2022-04-15 16:27:51 +03:00
xmlFile, err = os.Open(file)
if err != nil {
return err
}
2022-04-14 18:39:18 +03:00
2022-04-18 13:13:38 +03:00
fmt.Println("\nAnalyzing", file)
2022-04-14 18:39:18 +03:00
defer xmlFile.Close()
2022-04-18 13:13:38 +03:00
converter = util.NewConvertReader(xmlFile)
barReader = bar.NewProxyReader(converter)
_, err = analyzeElement(xml.NewDecoder(barReader), a, make([]string, 0), &ctx)
bar.Finish()
2022-04-14 18:39:18 +03:00
2022-04-15 16:27:51 +03:00
return err
2022-04-14 18:39:18 +03:00
}
const PATH_SEPARATOR = "|"
2022-04-15 16:27:51 +03:00
type Value struct {
Name string
Value string
}
func analyzeElement(d *xml.Decoder, a *AnalyzeData, path []string, ctx *AnalyzeContext) (*Value, error) {
2022-04-14 18:39:18 +03:00
if len(path) > 1 {
s := strings.Join(path, PATH_SEPARATOR)
2022-04-14 20:06:52 +03:00
fd := a.GetField(s)
2022-04-15 16:27:51 +03:00
if ctx.plus {
2022-04-14 18:39:18 +03:00
fd.Plus = true
} else {
fd.Base = true
}
}
var (
2022-04-15 16:27:51 +03:00
data []byte
id int
idFound bool
subtype string
subtypeFound bool
2022-04-14 18:39:18 +03:00
)
value := true
fields := make(map[string]bool)
Loop:
for {
tok, err := d.Token()
if err == io.EOF {
break Loop
} else if err != nil {
2022-04-15 16:27:51 +03:00
return nil, err
2022-04-14 18:39:18 +03:00
}
switch t := tok.(type) {
case xml.StartElement:
value = false
newPath := append(path, t.Name.Local)
if _, ok := fields[t.Name.Local]; ok {
2022-04-14 20:06:52 +03:00
a.GetField(strings.Join(newPath, PATH_SEPARATOR)).Multiple = true
2022-04-14 18:39:18 +03:00
}
fields[t.Name.Local] = true
2022-04-15 16:27:51 +03:00
v, err := analyzeElement(d, a, newPath, ctx)
if err != nil {
return nil, err
}
if v != nil {
2022-04-30 11:25:50 +03:00
if v.Name == "id" || v.Name == "local_id" {
2022-04-15 16:27:51 +03:00
idFound = true
id, _ = strconv.Atoi(v.Value)
}
if v.Name == "type" {
subtypeFound = true
subtype = v.Value
if idFound && subtypeFound {
p := strings.Join(path, PATH_SEPARATOR)
if strings.Contains(p, "+") {
p = p[:strings.LastIndex(p, "+")]
}
typeMap, ok := ctx.subtypes[p]
if !ok {
typeMap = make(map[int]string)
ctx.subtypes[p] = typeMap
}
if !ctx.plus {
typeMap[id] = subtype
a.GetSubType(p, strcase.ToCamel(subtype)).BaseType = subtype
} else {
if typeMap[id] != subtype {
if typeMap[id] != "" {
a.GetSubType(p, strcase.ToCamel(typeMap[id])).PlusType = subtype
} else {
a.GetSubType(p, strcase.ToCamel(subtype)).PlusType = subtype
}
subtype = typeMap[id]
} else {
a.GetSubType(p, strcase.ToCamel(subtype)).PlusType = subtype
}
}
}
2022-04-18 13:13:38 +03:00
if allowedTyped[strings.Join(path, PATH_SEPARATOR)] {
path[len(path)-1] = path[len(path)-1] + "+" + strcase.ToCamel(subtype)
}
2022-04-15 16:27:51 +03:00
}
}
2022-04-14 18:39:18 +03:00
case xml.CharData:
data = append(data, t...)
case xml.EndElement:
if value {
2022-04-18 13:13:38 +03:00
s := strings.TrimSpace(string(data))
2022-04-15 16:27:51 +03:00
if _, err := strconv.Atoi(s); err != nil {
2022-04-18 13:13:38 +03:00
f := a.GetField(strings.Join(path, PATH_SEPARATOR))
2022-04-30 10:04:33 +03:00
force := ctx.overwrites.ForceEnum[strings.Join(path, PATH_SEPARATOR)]
if force {
f.Enum = true
}
2022-04-18 13:13:38 +03:00
f.IsString = true
if s != "" && f.Enum {
f.Values[s] = true
}
2022-04-30 10:04:33 +03:00
2022-04-25 21:16:49 +03:00
if len(f.Values) > 30 && !force {
2022-04-18 13:13:38 +03:00
f.Values = make(map[string]bool)
f.Enum = false
}
2022-04-14 18:39:18 +03:00
}
2022-04-16 21:34:19 +03:00
if len(s) > 0 {
a.GetField(strings.Join(path, PATH_SEPARATOR)).NoBool = true
}
2022-04-15 16:27:51 +03:00
return &Value{Name: t.Name.Local, Value: s}, nil
2022-04-14 18:39:18 +03:00
}
2022-04-15 16:27:51 +03:00
return nil, err
2022-04-14 18:39:18 +03:00
}
}
2022-04-15 16:27:51 +03:00
return nil, nil
2022-04-14 18:39:18 +03:00
}