Skip to content

Commit 2ab4280

Browse files
authored
Merge pull request #45 from argonui/bundle
Allow Bundler to write the required files it found to the src/ directory
2 parents b329729 + 0bd7edf commit 2ab4280

File tree

13 files changed

+319
-159
lines changed

13 files changed

+319
-159
lines changed

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,50 @@ This golang library is intended to be able to convert source-controllable config
33
and luascript into a functioning json file that can be loaded into TTS as a
44
workshop mod.
55

6+
# Getting the binary
7+
8+
The binary is built by slsa-framework/slsa-github-generator and can be found attached to the latest release, for example: https://github.com/argonui/TTSModManager/releases/tag/v0.2.4/TTSModManager.exe for windows. In the examples i'll refer to the exe, but you can use the TTSModManager-Liunux with the same expected behavior.
9+
610
# Example Usage
711
## Generate json from a directory
812
$moddir = directory to read from
913

10-
$config/output.json = file to output to
1114
```
12-
go run main.go --moddir="C:\Users\USER\Documents\Projects\MyProject"
15+
TTSModManager.exe --moddir="C:\Users\USER\Documents\Projects\MyProject"
1316
```
1417

18+
The finished json file is found in $moddir/output.json by default. if you'd like
19+
to specify the output file you can use the `modfile` argument.
20+
1521
## Generate a directory from existing json file
16-
$config = directory to write to
22+
$moddir = directory to write to
23+
$modfile = existing tts mod file to read from
1724

18-
$ttsmodfile = existing tts mod file to read from
1925
```
20-
go run main.go --reverse --moddir="C:\Users\USER\Documents\Projects\MyProject" --modfile="C:\Users\USER\Documents\My Games\Tabletop Simulator\Mods\Workshop\existingMod.json"
26+
TTSModManager.exe --reverse --moddir="C:\Users\USER\Documents\Projects\MyProject" --modfile="C:\Users\USER\Documents\My Games\Tabletop Simulator\Mods\Workshop\existingMod.json"
2127
```
2228

29+
If you'd like the bundled lua requirements to be written to the `src/` folder, pass `--writesrc`.
30+
2331
## Testing a TTS mod conversion
2432
### reverse existing modfile into directory
2533
$ttsmodfile = existing tts mod file to read from
2634

2735
$moddir = directory to write to
2836
```
29-
go run main.go --reverse --moddir="C:\Users\USER\Documents\Projects\MyProject" --modfile="C:\Users\USER\Documents\My Games\Tabletop Simulator\Mods\Workshop\existingMod.json"
37+
TTSModManager.exe --reverse --moddir="C:\Users\USER\Documents\Projects\MyProject" --modfile="C:\Users\USER\Documents\My Games\Tabletop Simulator\Mods\Workshop\existingMod.json"
3038
```
3139

3240
### generate a modfile based on directory
3341
$moddir = directory to read from
3442
```
35-
go run main.go --moddir="C:\Users\USER\Documents\Projects\MyProject"
43+
TTSModManager.exe --moddir="C:\Users\USER\Documents\Projects\MyProject"
3644
```
3745

38-
### compare the original modfile with new generated modfile
39-
$ttsmodfile = original tts mod file
46+
## Running a local copy
47+
48+
If you are developing a feature and would like to run the tool, use this instead of `TTSModManager.exe`
4049

41-
$altmodfile = new generated modfile to compare to
4250
```
43-
go test . --modfile="C:\Users\USER\Documents\My Games\Tabletop Simulator\Mods\Workshop\existingMod.json" --altmodfile="C:\Users\USER\Documents\Projects\MyProject\output.json"
51+
go run main.go --moddir="..."
4452
```

bundler/bundler.go

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ end)(nil)`
5757
funcprefixReplace string = `SRC_LOCATION`
5858
funcprefix string = `__bundle_register("SRC_LOCATION", function(require, _LOADED, __bundle_register, __bundle_modules)`
5959
funcsuffix string = `end)`
60-
rootfuncname string = `__root`
60+
// Rootname is the bundle name for the batch of raw lua
61+
Rootname string = `__root`
6162
)
6263

6364
// IsBundled keeps regex bundling logic to this file
@@ -69,20 +70,62 @@ func IsBundled(rawlua string) bool {
6970
return false
7071
}
7172

72-
// Unbundle takes luacode and strips it down to the root sub function
73-
func Unbundle(rawlua string) (string, error) {
73+
// UnbundleAll takes luacode generates all bundlenames and bundles
74+
func UnbundleAll(rawlua string) (map[string]string, error) {
7475
if !IsBundled(rawlua) {
75-
return rawlua, nil
76+
return map[string]string{Rootname: rawlua}, nil
77+
}
78+
scripts := map[string]string{}
79+
r, err := findNextBundledScript(rawlua)
80+
for r.leftover != "" {
81+
if err != nil {
82+
return nil, fmt.Errorf("findNextBundledScript(%s): %v", rawlua, err)
83+
}
84+
scripts[r.name] = r.body
85+
r, err = findNextBundledScript(r.leftover)
7686
}
77-
// [return __bundle_require(\"__root\")|__bundle_register]
78-
root := regexp.MustCompile(`(?s)__bundle_register\("__root", function\(require, _LOADED, __bundle_register, __bundle_modules\)[\r\n\s]+(.*?)[\r\n ]+end\)[\n\r]+(return __bundle_require\(\"__root\"\)|__bundle_register)+`)
79-
matches := root.FindStringSubmatch(rawlua)
87+
if _, ok := scripts[Rootname]; !ok {
88+
return nil, fmt.Errorf("Failed to find root bundle")
89+
}
90+
91+
return scripts, nil
92+
}
8093

81-
if len(matches) <= 1 {
82-
return "", fmt.Errorf("could not find root bundle")
94+
type result struct {
95+
name, body, leftover string
96+
}
97+
98+
func findNextBundledScript(rawlua string) (result, error) {
99+
root := regexp.MustCompile(`(?s)__bundle_register\("(.*?)", function\(require, _LOADED, __bundle_register, __bundle_modules\)[\r\n\s]+(.*?)[\r\n ]+end\)[\n\r]+(return __bundle_require\(\"__root\"\)|__bundle_register)+`)
100+
m := root.FindStringSubmatchIndex(rawlua)
101+
if m == nil {
102+
return result{}, nil
103+
}
104+
if len(m) != 8 {
105+
return result{}, fmt.Errorf("Expected 8 indices, got %v", m)
83106
}
107+
// first 2 ints are indices of entire match
108+
// second 2 ints are indices of script name
109+
// third 2 are script body
110+
// fourth 2 are suffix needed to match
111+
return result{
112+
name: rawlua[m[2]:m[3]],
113+
body: rawlua[m[4]:m[5]],
114+
leftover: rawlua[m[6]:],
115+
}, nil
116+
}
84117

85-
return matches[1], nil
118+
// Unbundle extracts the root bundle per
119+
func Unbundle(rawlua string) (string, error) {
120+
srcmap, err := UnbundleAll(rawlua)
121+
if err != nil {
122+
return "", err
123+
}
124+
rt, ok := srcmap[Rootname]
125+
if !ok {
126+
return "", fmt.Errorf("Rootname not found in unbundled map")
127+
}
128+
return rt, nil
86129
}
87130

88131
// Bundle grabs all dependencies and creates a single luascript
@@ -91,9 +134,9 @@ func Bundle(rawlua string, l file.LuaReader) (string, error) {
91134
return rawlua, nil
92135
}
93136
reqs := map[string]string{
94-
rootfuncname: rawlua,
137+
Rootname: rawlua,
95138
}
96-
todo := []string{rootfuncname}
139+
todo := []string{Rootname}
97140
for len(todo) > 0 {
98141
fname := todo[0]
99142
todo = todo[1:] // pop first element off
@@ -115,6 +158,10 @@ func Bundle(rawlua string, l file.LuaReader) (string, error) {
115158
}
116159
todo = append(todo, reqsToLoad...)
117160
}
161+
if len(reqs) == 1 {
162+
// if there were no requires to load in, no need to bundle
163+
return rawlua, nil
164+
}
118165

119166
bundlestr := metaprefix + "\n"
120167

bundler/bundler_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package bundler
22

33
import (
44
"fmt"
5-
"strings"
65
"testing"
76

87
"github.com/google/go-cmp/cmp"
@@ -249,17 +248,23 @@ require("core/AgendaDeck")
249248
var a = '2'
250249
require("core/AgendaDeck")
251250
end)
251+
__bundle_register("core/AgendaDeck", function(require, _LOADED, __bundle_register, __bundle_modules)
252+
var b = '3'
253+
end)
252254
return __bundle_require("__root")
253255
`
254-
got, err := Unbundle(raw)
256+
got, err := UnbundleAll(raw)
255257
if err != nil {
256258
t.Fatalf("expected no err, got %v", err)
257259
}
258-
want := `require("core/AgendaDeck")
260+
want := map[string]string{
261+
Rootname: `require("core/AgendaDeck")
259262
var a = '2'
260-
require("core/AgendaDeck")`
261-
if want != got {
262-
t.Errorf("want <%s>, got <%s>\n", want, got)
263+
require("core/AgendaDeck")`,
264+
"core/AgendaDeck": "var b = '3'",
265+
}
266+
if diff := cmp.Diff(want, got); diff != "" {
267+
t.Errorf("want != got:\n%v\n", diff)
263268
}
264269
}
265270

@@ -425,20 +430,15 @@ func TestBundleNoRequires(t *testing.T) {
425430
if err != nil {
426431
t.Errorf("Expected no error, got %v", err)
427432
}
428-
want := strings.Trim(strings.Join(
429-
[]string{metaprefix,
430-
strings.Replace(funcprefix, funcprefixReplace, rootfuncname, 1),
431-
"var foo = 42",
432-
funcsuffix,
433-
metasuffix}, "\n"), "\n\n")
433+
want := `var foo = 42`
434434

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

440440
func TestFailedUnbundle(t *testing.T) {
441-
rawlua := ` __bundle_register("core/AgendaDeck", function(require, _LOADED, __bundle_register, __bundle_modules)
441+
rawlua := ` __bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
442442
MIN_VALUE = -99
443443
MAX_VALUE = 999
444444
`

handler/luahandler.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package handler
2+
3+
import (
4+
"ModCreator/bundler"
5+
"ModCreator/file"
6+
"fmt"
7+
)
8+
9+
// LuaHandler is needed because handling if a script should be written to src or to objects folder,
10+
// and if it it long enough to be written to separate file at all has become
11+
// burdensome. abstract into struct
12+
type LuaHandler struct {
13+
ObjWriter file.LuaWriter
14+
SrcWriter file.LuaWriter
15+
Reader file.LuaReader
16+
}
17+
18+
// HandleAction describes at the end of handling,
19+
// a single value should be written to a single key
20+
type HandleAction struct {
21+
Noop bool
22+
Key string
23+
Value string
24+
}
25+
26+
// WhileReadingFromFile consolidates expected behavior of both objects and root
27+
// json while reading lua from file.
28+
func (lh *LuaHandler) WhileReadingFromFile(rawj map[string]interface{}) (HandleAction, error) {
29+
rawscript := ""
30+
if spraw, ok := rawj["LuaScript_path"]; ok {
31+
sp, ok := spraw.(string)
32+
if !ok {
33+
return HandleAction{}, fmt.Errorf("Expected LuaScript_path to be type string, was %T", spraw)
34+
}
35+
encoded, err := lh.Reader.EncodeFromFile(sp)
36+
if err != nil {
37+
return HandleAction{}, fmt.Errorf("l.EncodeFromFile(%s) : %v", sp, err)
38+
}
39+
rawscript = encoded
40+
}
41+
if sraw, ok := rawj["LuaScript"]; ok {
42+
s, ok := sraw.(string)
43+
if !ok {
44+
return HandleAction{}, fmt.Errorf("Expected LuaScript to be type string, was %T", sraw)
45+
}
46+
rawscript = s
47+
}
48+
bundled, err := bundler.Bundle(rawscript, lh.Reader)
49+
if err != nil {
50+
return HandleAction{}, fmt.Errorf("Bundle(%s): %v", rawscript, err)
51+
}
52+
if bundled == "" {
53+
return HandleAction{
54+
Noop: true,
55+
}, nil
56+
}
57+
58+
return HandleAction{
59+
Key: "LuaScript",
60+
Value: bundled,
61+
Noop: false,
62+
}, nil
63+
}
64+
65+
// WhileWritingToFile consolidates the logic and flow of conditionally writing
66+
// lua to a file, and which file to write to
67+
func (lh *LuaHandler) WhileWritingToFile(rawj map[string]interface{}, possiblefname string) (HandleAction, error) {
68+
rawscript, ok := rawj["LuaScript"]
69+
if !ok {
70+
return HandleAction{Noop: true}, nil
71+
}
72+
script, ok := rawscript.(string)
73+
if !ok {
74+
return HandleAction{}, fmt.Errorf("Value of content at LuaScript expected to be string, was %v, type %T",
75+
rawscript, rawscript)
76+
}
77+
78+
allScripts, err := bundler.UnbundleAll(script)
79+
if err != nil {
80+
return HandleAction{}, fmt.Errorf("UnbundleAll(...): %v", err)
81+
}
82+
// root bundle is promised to exist
83+
rootscript, _ := allScripts[bundler.Rootname]
84+
returnAction := HandleAction{Noop: false}
85+
if len(rootscript) > 80 {
86+
err = lh.ObjWriter.EncodeToFile(rootscript, possiblefname)
87+
if err != nil {
88+
return HandleAction{}, fmt.Errorf("EncodeToFile(<root script>, %s): %v", possiblefname, err)
89+
}
90+
returnAction.Key = "LuaScript_path"
91+
returnAction.Value = possiblefname
92+
} else {
93+
returnAction.Key = "LuaScript"
94+
returnAction.Value = rootscript
95+
}
96+
delete(allScripts, bundler.Rootname)
97+
98+
for k, script := range allScripts {
99+
fname := fmt.Sprintf("%s.ttslua", k)
100+
if lh.SrcWriter == nil {
101+
break
102+
}
103+
err := lh.SrcWriter.EncodeToFile(script, fname)
104+
if err != nil {
105+
return HandleAction{}, fmt.Errorf("SrcWriter.EncodeToFile(<>, %s): %v", fname, err)
106+
}
107+
}
108+
109+
return returnAction, nil
110+
}

main.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import (
1414
)
1515

1616
var (
17-
moddir = flag.String("moddir", "testdata/simple", "a directory containing tts mod configs")
18-
rev = flag.Bool("reverse", false, "Instead of building a json from file structure, build file structure from json.")
19-
modfile = flag.String("modfile", "", "where to read from when reversing.")
17+
moddir = flag.String("moddir", "testdata/simple", "a directory containing tts mod configs")
18+
rev = flag.Bool("reverse", false, "Instead of building a json from file structure, build file structure from json.")
19+
writeToSrc = flag.Bool("writesrc", false, "When unbundling Lua, save the included 'require' files to the src/ directory.")
20+
modfile = flag.String("modfile", "", "where to read from when reversing.")
2021
)
2122

2223
const (
@@ -32,6 +33,7 @@ func main() {
3233
[]string{path.Join(*moddir, textSubdir), path.Join(*moddir, objectsSubdir)},
3334
path.Join(*moddir, objectsSubdir),
3435
)
36+
luaSrc := file.NewLuaOps(path.Join(*moddir, textSubdir))
3537
ms := file.NewJSONOps(path.Join(*moddir, modsettingsDir))
3638
objs := file.NewJSONOps(path.Join(*moddir, objectsSubdir))
3739
objdir := file.NewDirOps(path.Join(*moddir, objectsSubdir))
@@ -49,6 +51,9 @@ func main() {
4951
ObjDirCreeator: objdir,
5052
RootWrite: rootops,
5153
}
54+
if *writeToSrc {
55+
r.LuaSrcWriter = luaSrc
56+
}
5257
err = r.Write(raw)
5358
if err != nil {
5459
log.Fatalf("reverse.Write(<%s>) failed : %v", *modfile, err)

0 commit comments

Comments
 (0)