Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions file/dirops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package file

import (
"fmt"
"io/ioutil"
"os"
"path"
)

// DirCreator abstracts folder creation
type DirCreator interface {
CreateDir(relpath string, suggestion string) (string, error)
}

type DirExplorer interface {
ListFilesAndFolders(relpath string) ([]string, []string, error)
}

// DirOps abstracts away folder creation and other future folder oprations
type DirOps struct {
base string
}

// NewDirOps allows for abstraction of creation of a directory operator
func NewDirOps(p string) *DirOps {
return &DirOps{
base: p,
}
}

// CreateDir allows objects to abstract creation of sub directories without knowning the root path of the machine
func (d *DirOps) CreateDir(relpath, suggestion string) (string, error) {
dirname := suggestion
err := os.Mkdir(path.Join(d.base, relpath, suggestion), 0644)
tries := 0
for err != nil && tries < 100 {
tries++
dirname = fmt.Sprintf("%s_%v", suggestion, tries)
err = os.Mkdir(path.Join(d.base, relpath, dirname), 0644)
}
if tries >= 100 {
return "", fmt.Errorf("could not find sutible name for sub directory based on suggestion %s; %v", suggestion, err)
}
return dirname, nil
}

// ListFilesAndFolders allows for file exploration. returns relateive file or folder names
func (d *DirOps) ListFilesAndFolders(relpath string) ([]string, []string, error) {
p := path.Join(d.base, relpath)
files, err := ioutil.ReadDir(p)
if err != nil {
return nil, nil, fmt.Errorf("ioutil.ReadDir(%s) : %v", p, err)
}
fnames := make([]string, 0)
folnames := make([]string, 0)
for _, file := range files {
if file.IsDir() {
folnames = append(folnames, path.Join(relpath, file.Name()))
continue
}
fnames = append(fnames, path.Join(relpath, file.Name()))
}
return fnames, folnames, nil
}
37 changes: 25 additions & 12 deletions file/luaops.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ const (

// LuaOps allows for arbitrary reads and writes of luascript
type LuaOps struct {
basepath string
readFileToBytes func(string) ([]byte, error)
basepaths []string
writeBasepath string
readFileToBytes func(string) ([]byte, error)
writeBytesToFile func(string, []byte) error
}

// LuaReader serves to describe all ways to read luascripts
Expand All @@ -29,8 +31,14 @@ type LuaWriter interface {

// NewLuaOps initializes our object on a directory
func NewLuaOps(base string) *LuaOps {
return NewLuaOpsMulti([]string{base}, base)
}

// NewLuaOpsMulti allows for luascript to be read from multiple directories
func NewLuaOpsMulti(readDirs []string, writeDir string) *LuaOps {
return &LuaOps{
basepath: base,
basepaths: readDirs,
writeBasepath: writeDir,
readFileToBytes: func(s string) ([]byte, error) {
sFile, err := os.Open(s)
if err != nil {
Expand All @@ -40,23 +48,28 @@ func NewLuaOps(base string) *LuaOps {

return ioutil.ReadAll(sFile)
},
writeBytesToFile: func(p string, b []byte) error {
return os.WriteFile(p, b, 0644)
},
}
}

// EncodeFromFile pulls a file from configs and encodes it as a string.
func (l *LuaOps) EncodeFromFile(filename string) (string, error) {
p := path.Join(l.basepath, filename)
b, err := l.readFileToBytes(p)
if err != nil {
return "", err
for _, base := range l.basepaths {
p := path.Join(base, filename)
b, err := l.readFileToBytes(p)
if err != nil {
continue
}
s := string(b)
return s, nil
}
s := string(b)

return s, nil
return "", fmt.Errorf("%s not found among any known paths", filename)
}

// EncodeToFile takes a single string and decodes escape characters; writes it.
func (l *LuaOps) EncodeToFile(script, file string) error {
p := path.Join(l.basepath, file)
return os.WriteFile(p, []byte(script), 0644)
p := path.Join(l.writeBasepath, file)
return l.writeBytesToFile(p, []byte(script))
}
95 changes: 95 additions & 0 deletions file/luaops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package file

import (
"fmt"

"testing"

"github.com/google/go-cmp/cmp"

)

type fakeFiles struct {
Expand All @@ -14,3 +19,93 @@ func (ff *fakeFiles) read(s string) ([]byte, error) {
}
return nil, fmt.Errorf("fake file %s not found", s)
}

func (ff *fakeFiles) write(path string, val []byte) error {
ff.fs[path] = val
return nil
}

func TestRead(t *testing.T) {
ff := &fakeFiles{
fs: map[string][]byte{"foo/bar": []byte("bar = 2")},
}
l := &LuaOps{
basepaths: []string{"foo"},
readFileToBytes: ff.read,
}

want := "bar = 2"
got, err := l.EncodeFromFile("bar")
if err != nil {
t.Errorf("EncodeFromFile(bar): %v", err)
}

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("want != got:\n%v\n", diff)
}
}

func TestReadMulti(t *testing.T) {
ff := &fakeFiles{
fs: map[string][]byte{
"foo/old": []byte("not here"),
"foo2/bar": []byte("bar = 2"),
},
}
l := &LuaOps{
basepaths: []string{"foo", "foo2"},
readFileToBytes: ff.read,
}

want := "bar = 2"
got, err := l.EncodeFromFile("bar")
if err != nil {
t.Fatalf("EncodeFromFile(bar): %v", err)
}

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("want != got:\n%v\n", diff)
}
}

func TestReadErr(t *testing.T) {
ff := &fakeFiles{
fs: map[string][]byte{
"foo/old": []byte("not here"),
"foo2/bar": []byte("bar = 2"),
},
}
l := &LuaOps{
basepaths: []string{"foo", "foo2"},
readFileToBytes: ff.read,
}

_, err := l.EncodeFromFile("notfound")
if err == nil {
t.Fatalf("Wanted error, got none")
}
}

func TestWrite(t *testing.T) {
ff := &fakeFiles{
fs: map[string][]byte{},
}
l := LuaOps{
writeBasepath: "foo",
writeBytesToFile: ff.write,
}

err := l.EncodeToFile("var bar = 2", "bar.lua")
if err != nil {
t.Fatalf("No error expected got %v", err)
}

want := []byte("var bar = 2")
got, ok := ff.fs["foo/bar.lua"]
if !ok {
t.Errorf("fake files not found")
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("want != got:\n%v\n", diff)
}
}
71 changes: 48 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ import (
)

var (
config = flag.String("moddir", "testdata/simple", "a directory containing tts mod configs")
moddir = flag.String("moddir", "testdata/simple", "a directory containing tts mod configs")
rev = flag.Bool("reverse", false, "Instead of building a json from file structure, build file structure from json.")
modfile = flag.String("modfile", "", "where to read from when reversing.")

expectedStr = []string{"SaveName", "Date", "VersionNumber", "GameMode", "GameType", "GameComplexity", "Table", "Sky", "Note", "LuaScript", "LuaScriptState", "XmlUI"}
expectedObj = []string{"TabStates", "MusicPlayer", "Grid", "Lighting", "Hands", "ComponentTags", "Turns"}
expectedObjArr = []string{"CameraStates", "DecalPallet", "CustomUIAssets", "SnapPoints"}
expectedObjArr = []string{"CameraStates", "DecalPallet", "CustomUIAssets", "SnapPoints", "Decals"}
expectedObjStates = "ObjectStates"
)

const (
textSubdir = "src"
jsonSubdir = "json"
objectsSubdir = "objects"
textSubdir = "src"
modsettingsDir = "modsettings"
objectsSubdir = "objects"
)

// Config is how users will specify their mod's configuration.
Expand All @@ -45,38 +45,64 @@ type ObjArray []map[string]interface{}
// module creation is done
type Mod struct {
Data Obj

lua file.LuaReader
modsettings file.JSONReader
objs file.JSONReader
objdirs file.DirExplorer
}

func main() {
flag.Parse()

lua := file.NewLuaOps(path.Join(*config, textSubdir))
j := file.NewJSONOps(path.Join(*config, jsonSubdir))
lua := file.NewLuaOpsMulti(
[]string{path.Join(*moddir, textSubdir), path.Join(*moddir, objectsSubdir)},
path.Join(*moddir, objectsSubdir),
)
ms := file.NewJSONOps(path.Join(*moddir, modsettingsDir))
objs := file.NewJSONOps(path.Join(*moddir, objectsSubdir))
objdir := file.NewDirOps(path.Join(*moddir, objectsSubdir))

if *rev {
raw, err := prepForReverse(*config, *modfile)
raw, err := prepForReverse(*moddir, *modfile)
if err != nil {
log.Fatalf("prepForReverse (%s) failed : %v", *modfile, err)
}
err = reverse.Write(raw, lua, j, *config, expectedStr, expectedObj, expectedObjArr)
r := reverse.Reverser{
ModSettingsWriter: ms,
LuaWriter: lua,
ObjWriter: objs,
ObjDirCreeator: objdir,
StringType: expectedStr,
ObjType: expectedObj,
ObjArrayType: expectedObjArr,
Root: *moddir,
}
err = r.Write(raw)
if err != nil {
log.Fatalf("reverse.Write(<%s>) failed : %v", *modfile, err)
}
return
}

c, err := readConfig(*config)
c, err := readConfig(*moddir)
if err != nil {
fmt.Printf("readConfig(%s) : %v\n", *config, err)
fmt.Printf("readConfig(%s) : %v\n", *moddir, err)
return
}

m, err := generateMod(*config, lua, j, c)
m := &Mod{
lua: lua,
modsettings: ms,
objs: objs,
objdirs: objdir,
}
err = m.generate(c)
if err != nil {
fmt.Printf("generateMod(<config>) : %v\n", err)
return
}
err = printMod(*config, m)
err = printMod(*moddir, m)
if err != nil {
log.Fatalf("printMod(...) : %v", err)
}
Expand Down Expand Up @@ -105,22 +131,21 @@ func readConfig(cPath string) (*Config, error) {
return &c, nil
}

func generateMod(p string, lua file.LuaReader, j file.JSONReader, c *Config) (*Mod, error) {
func (m *Mod) generate(c *Config) error {
if c == nil {
return nil, fmt.Errorf("nil config")
return fmt.Errorf("nil config")
}
var m Mod

m.Data = c.Raw

plainObj := func(s string) (interface{}, error) {
return j.ReadObj(s)
return m.modsettings.ReadObj(s)
}
objArray := func(s string) (interface{}, error) {
return j.ReadObjArray(s)
return m.modsettings.ReadObjArray(s)
}
luaGet := func(s string) (interface{}, error) {
return lua.EncodeFromFile(s)
return m.lua.EncodeFromFile(s)
}

ext := "_path"
Expand All @@ -136,12 +161,12 @@ func generateMod(p string, lua file.LuaReader, j file.JSONReader, c *Config) (*M
tryPut(&m.Data, objarraybased+ext, objarraybased, objArray)
}

allObjs, err := objects.ParseAllObjectStates(path.Join(p, objectsSubdir), lua)
allObjs, err := objects.ParseAllObjectStates(m.lua, m.objs, m.objdirs)
if err != nil {
return nil, fmt.Errorf("objects.ParseAllObjectStates(%s) : %v", path.Join(p, objectsSubdir), err)
return fmt.Errorf("objects.ParseAllObjectStates(%s) : %v", "", err)
}
m.Data[expectedObjStates] = allObjs
return &m, nil
return nil
}

func tryPut(d *Obj, from, to string, fun func(string) (interface{}, error)) {
Expand Down Expand Up @@ -183,7 +208,7 @@ func printMod(p string, m *Mod) error {

// prepForReverse creates the expected subdirectories in config path
func prepForReverse(cPath, modfile string) (Obj, error) {
subDirs := []string{textSubdir, jsonSubdir, objectsSubdir}
subDirs := []string{textSubdir, modsettingsDir, objectsSubdir}

for _, s := range subDirs {
p := path.Join(cPath, s)
Expand Down
Loading