RAG itself is annoying to properly implement, plucking sentences with no context is useless. Also it should not be a part of main package, same for goes for tui. The number of global vars is absurd.
128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
package pngmeta
|
|
|
|
import (
|
|
"bytes"
|
|
"elefant/models"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
embType = "tEXt"
|
|
)
|
|
|
|
type PngEmbed struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
func (c PngEmbed) GetDecodedValue() (*models.CharCardSpec, error) {
|
|
data, err := base64.StdEncoding.DecodeString(c.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
card := &models.CharCardSpec{}
|
|
if err := json.Unmarshal(data, &card); err != nil {
|
|
return nil, err
|
|
}
|
|
return card, nil
|
|
}
|
|
|
|
func extractChar(fname string) (*PngEmbed, error) {
|
|
data, err := os.ReadFile(fname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reader := bytes.NewReader(data)
|
|
pr, err := NewPNGStepReader(reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for {
|
|
step, err := pr.Next()
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
break
|
|
}
|
|
}
|
|
if step.Type() != embType {
|
|
if _, err := io.Copy(io.Discard, step); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
buf, err := io.ReadAll(step)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dataInstep := string(buf)
|
|
values := strings.Split(dataInstep, "\x00")
|
|
if len(values) == 2 {
|
|
return &PngEmbed{Key: values[0], Value: values[1]}, nil
|
|
}
|
|
}
|
|
if err := step.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return nil, errors.New("failed to find embedded char in png: " + fname)
|
|
}
|
|
|
|
func ReadCard(fname, uname string) (*models.CharCard, error) {
|
|
pe, err := extractChar(fname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
charSpec, err := pe.GetDecodedValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return charSpec.Simplify(uname, fname), nil
|
|
}
|
|
|
|
func readCardJson(fname string) (*models.CharCard, error) {
|
|
data, err := os.ReadFile(fname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
card := models.CharCard{}
|
|
if err := json.Unmarshal(data, &card); err != nil {
|
|
return nil, err
|
|
}
|
|
return &card, nil
|
|
}
|
|
|
|
func ReadDirCards(dirname, uname string) ([]*models.CharCard, error) {
|
|
files, err := os.ReadDir(dirname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := []*models.CharCard{}
|
|
for _, f := range files {
|
|
if f.IsDir() {
|
|
continue
|
|
}
|
|
if strings.HasSuffix(f.Name(), ".png") {
|
|
fpath := path.Join(dirname, f.Name())
|
|
cc, err := ReadCard(fpath, uname)
|
|
if err != nil {
|
|
return nil, err // better to log and continue
|
|
}
|
|
resp = append(resp, cc)
|
|
}
|
|
if strings.HasSuffix(f.Name(), ".json") {
|
|
fpath := path.Join(dirname, f.Name())
|
|
cc, err := readCardJson(fpath)
|
|
if err != nil {
|
|
return nil, err // better to log and continue
|
|
}
|
|
resp = append(resp, cc)
|
|
}
|
|
}
|
|
return resp, nil
|
|
}
|