diff --git a/src/factorio/factorio_data_types.go b/src/factorio/factorio_data_types.go new file mode 100644 index 00000000..78dc7ee7 --- /dev/null +++ b/src/factorio/factorio_data_types.go @@ -0,0 +1,433 @@ +package factorio + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "reflect" +) + +/////////////////// +// Reading //////// +/////////////////// +func readOptimUint(r io.Reader, v Version, bitSize int) (uint32, error) { + var b [4]byte + if !v.Less(Version{0, 14, 14, 0}) { + _, err := r.Read(b[:1]) + if err != nil { + return 0, err + } + if b[0] != 0xFF { + return uint32(b[0]), nil + } + } + + if bitSize < 0 || bitSize > 64 || (bitSize%8 != 0) { + panic("invalid bit size") + } + + _, err := r.Read(b[:bitSize/8]) + if err != nil { + return 0, err + } + + switch bitSize { + case 16: + return uint32(binary.LittleEndian.Uint16(b[:2])), nil + case 32: + return binary.LittleEndian.Uint32(b[:4]), nil + default: + panic("invalid bit size") + } +} + +func readString(r io.Reader, version Version, forceOptimized bool) (s string, err error) { + var n uint32 + + if !version.Less(Version{0, 16, 0, 0}) || forceOptimized { + n, err = readOptimUint(r, version, 32) + if err != nil { + return "", err + } + } else { + var b [4]byte + _, err := r.Read(b[:]) + if err != nil { + return "", fmt.Errorf("failed to read string length: %v", err) + } + n = uint32(binary.LittleEndian.Uint32(b[:])) + } + + // do not read the string, when it is empty + if n < 1 { + return "", nil + } + + d := make([]byte, n) + _, err = r.Read(d) + if err != nil { + return "", fmt.Errorf("failed to read string: %v", err) + } + + return string(d), nil +} + +func readStringSettings(file io.Reader, version Version) (string, error) { + // read "empty" flag + empty, err := readBool(file) + if err != nil { + log.Printf("error loading empty flag of string: %s", err) + return "", err + } + + if empty { + return "", nil + } + + key, err := readString(file, version, false) + if err != nil { + log.Printf("could not read key-string: %s", err) + return "", err + } + + return key, nil +} + +func readBool(file io.Reader) (bool, error) { + var _data byte + err := binary.Read(file, binary.LittleEndian, &_data) + if err != nil { + log.Printf("could not read boolean byte: %s", err) + return false, err + } + + return _data != 0, nil +} + +func readDouble(file io.Reader) (float64, error) { + var _data float64 + err := binary.Read(file, binary.LittleEndian, &_data) + if err != nil { + log.Printf("could not read double-value: %s", err) + return 0, err + } + + return _data, nil +} + +func readList(file io.Reader, version Version) ([]interface{}, error) { + var length uint32 + length, err := readOptimUint(file, version, 32) + if err != nil { + log.Printf("could not read list length") + return nil, err + } + + list := make([]interface{}, length) + for i := uint32(0); i < length; i++ { + list[i], err = readTree(file, version) + if err != nil { + log.Printf("could not read tree of list: %s", err) + return nil, err + } + } + + return list, nil +} + +func readDict(file io.Reader, version Version) (map[string]interface{}, error) { + var length uint32 + err := binary.Read(file, binary.LittleEndian, &length) + if err != nil { + log.Printf("could not read dict length: %s", err) + return nil, err + } + + dict := make(map[string]interface{}) + + for i := uint32(0); i < length; i++ { + key, err := readStringSettings(file, version) + + if err != nil { + log.Printf("error loading key: %s", err) + return dict, err + } + + dict[key], err = readTree(file, version) + if err != nil { + log.Printf("error loading readTree: %s", err) + return dict, err + } + } + + return dict, nil +} + +func readTree(file io.Reader, version Version) (interface{}, error) { + //type of embedded data + var _type byte + err := binary.Read(file, binary.LittleEndian, &_type) + if err != nil { + log.Printf("could not read first binary: %v", err) + return nil, err + } + + //anyType flag ... not useful + _, err = readBool(file) + if err != nil { + log.Printf("error loading anyType bool: %s", err) + return nil, err + } + + switch _type { + case BOOL: + return readBool(file) + case DOUBLE: + return readDouble(file) + case STRING: + return readStringSettings(file, version) + case LIST: + return readList(file, version) + case DICT: + return readDict(file, version) + default: + return nil, fmt.Errorf("Unknown type: %s ", err) + } +} + +/////////////////// +// Writing //////// +/////////////////// +func writeOptimUint(data uint32) []byte { + if data < 256 { + intBinary := []byte{byte(data)} + return intBinary[:] + } else { + var intBinary [4]byte + binary.LittleEndian.PutUint32(intBinary[:], data) + return append([]byte{0xff}, intBinary[:]...) + } +} + +func writeFloat64(data float64) ([]byte, error) { + var buf bytes.Buffer + + err := binary.Write(&buf, binary.LittleEndian, data) + if err != nil { + log.Printf("could not write data into buffer: %s", err) + return nil, err + } + + return buf.Bytes(), nil +} + +func writeBool(data bool) byte { + if data { + return 0x1 + } else { + return 0x0 + } +} + +func writeString(data string) []byte { + var output []byte + + length := uint32(len(data)) + // True if the string is empty ... not used by factorio, so set to false + //output = []byte{writeBool(length == 0)} + output = []byte{writeBool(false)} + + output = append(output, writeOptimUint(length)...) + + if length != 0 { + stringBytes := []byte(data) + output = append(output, stringBytes...) + } + return output +} + +func writeList(data []interface{}) ([]byte, error) { + var output []byte + + length := uint32(len(data)) + output = writeOptimUint(length) + + for i := uint32(0); i < length; i++ { + tree, err := writeTree(data[i]) + if err != nil { + log.Printf("error loading tree of list-element: %s", err) + return nil, err + } + output = append(output, tree...) + } + + return output, nil +} + +func writeDict(data map[string]interface{}) ([]byte, error) { + var output []byte + + length := uint32(len(data)) + + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], length) + output = append(output, buf[:]...) + + for key, value := range data { + output = append(output, writeString(key)...) + tree, err := writeTree(value) + if err != nil { + log.Printf("error loading tree of dict-element: %s", err) + return nil, err + } + output = append(output, tree...) + } + + return output, nil +} + +func writeTree(data interface{}) (output []byte, err error) { + // get type + _type := reflect.TypeOf(data).Kind() + + // write any-type flag -- Not used by factorio ... so set to false + //anyTypeFlag := writeBool(_type == reflect.String) + anyTypeFlag := writeBool(false) + + var typeByte byte + var marshalledBytes []byte + + switch _type { + case reflect.Bool: + typeByte = BOOL + marshalledBytes = []byte{writeBool(data.(bool))} + case reflect.Int: + floatValue, err := writeFloat64(float64(data.(int))) + if err != nil { + log.Printf("could not write int to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Int8: + floatValue, err := writeFloat64(float64(data.(int8))) + if err != nil { + log.Printf("could not write int8 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Int16: + floatValue, err := writeFloat64(float64(data.(int16))) + if err != nil { + log.Printf("could not write int16 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Int32: + floatValue, err := writeFloat64(float64(data.(int32))) + if err != nil { + log.Printf("could not write int32 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Int64: + floatValue, err := writeFloat64(float64(data.(int64))) + if err != nil { + log.Printf("could not write int64 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Uint: + floatValue, err := writeFloat64(float64(data.(uint))) + if err != nil { + log.Printf("could not write uint to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Uint8: + floatValue, err := writeFloat64(float64(data.(uint8))) + if err != nil { + log.Printf("could not write uint8 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Uint16: + floatValue, err := writeFloat64(float64(data.(uint16))) + if err != nil { + log.Printf("could not write uint16 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Uint32: + floatValue, err := writeFloat64(float64(data.(uint32))) + if err != nil { + log.Printf("could not write uint32 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Uint64: + floatValue, err := writeFloat64(float64(data.(uint64))) + if err != nil { + log.Printf("could not write uint64 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Float32: + floatValue, err := writeFloat64(float64(data.(float32))) + if err != nil { + log.Printf("could not write float32 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.Float64: + floatValue, err := writeFloat64(data.(float64)) + if err != nil { + log.Printf("could not write float64 to float64-value: %s", err) + return nil, err + } + typeByte = DOUBLE + marshalledBytes = floatValue + case reflect.String: + typeByte = STRING + marshalledBytes = writeString(data.(string)) + case reflect.Array: + // List + list, err := writeList(data.([]interface{})) + if err != nil { + log.Printf("could not read List: %s", err) + return nil, err + } + typeByte = LIST + marshalledBytes = list + case reflect.Map: + // Dict + _map, err := writeDict(data.(map[string]interface{})) + if err != nil { + log.Printf("could not read Dict: %s", err) + return nil, err + } + typeByte = DICT + marshalledBytes = _map + default: + log.Println("Unknown Datatype") + return output, fmt.Errorf("unknown datatype") + } + + output = append(output, typeByte) + output = append(output, anyTypeFlag) + output = append(output, marshalledBytes...) + + return output, nil +} diff --git a/src/factorio/mod_settings_dat.go b/src/factorio/mod_settings_dat.go new file mode 100644 index 00000000..22da2ba6 --- /dev/null +++ b/src/factorio/mod_settings_dat.go @@ -0,0 +1,82 @@ +package factorio + +import ( + "encoding/binary" + "fmt" + "io" + "log" +) + +const ( + NONE = 0 + BOOL = 1 + DOUBLE = 2 + STRING = 3 + LIST = 4 + DICT = 5 +) + +type FModData struct { + Version version64 + Data interface{} +} + +func (d *FModData) Decode(file io.Reader) error { + var version version64 + var versionB [8]byte + + err := binary.Read(file, binary.LittleEndian, versionB[:]) + if err != nil { + log.Printf("could not read version: %s", err) + } + + err = version.UnmarshalBinary(versionB[:]) + if err != nil { + log.Printf("Error loading Version: %s", err) + return err + } + + d.Version = version + + if Version(version).Greater(Version{0, 17, 0, 0}) { + //FIXME correct naming + var b [1]byte + _, err = file.Read(b[:]) + if err != nil { + return fmt.Errorf("read first random 0.17 byte: %v", err) + } + } + + d.Data, err = readTree(file, Version(d.Version)) + if err != nil { + log.Printf("error loading Data: %s", err) + return err + } + + return nil +} + +func (d *FModData) Encode() ([]byte, error) { + var output []byte + + _bytes, err := d.Version.MarshalBinary() + if err != nil { + log.Printf("couldn't create binary from version: %s", err) + return nil, err + } + + output = append(output, _bytes...) + + if Version(d.Version).Greater(Version{0, 17, 0, 0}) { + output = append(output, byte(0)) + } + + tree, err := writeTree(d.Data) + if err != nil { + log.Printf("error loading first tree: %s", err) + return nil, err + } + output = append(output, tree...) + + return output, nil +} diff --git a/src/factorio/mod_settings_dat_test.go b/src/factorio/mod_settings_dat_test.go new file mode 100644 index 00000000..b7d85d48 --- /dev/null +++ b/src/factorio/mod_settings_dat_test.go @@ -0,0 +1,218 @@ +package factorio + +import ( + "bytes" + "encoding/json" + "github.com/go-test/deep" + "io/ioutil" + "os" + "testing" +) + +func TestModSettings0_16(t *testing.T) { + // Read dat and compare to JSON + file, err := os.Open("../factorio_mod_settings_testfiles/mod_settings_0.16.dat") + if err != nil { + t.Fatalf("could not open mod-settings.dat: %s", err) + } + + var modData FModData + err = modData.Decode(file) + if err != nil { + t.Fatalf("could not decode FModData: %s", err) + } + + modDataJson, err := ioutil.ReadFile("../factorio_mod_settings_testfiles/mod_settings_0.16.json") + if err != nil { + t.Fatalf("could not read json-file: %s", err) + } + + var test interface{} + err = json.Unmarshal(modDataJson, &test) + if err != nil { + t.Fatalf("could not Unmarshal JSON: %s", err) + } + + diff := deep.Equal(modData.Data, test) + if len(diff) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff), diff) + } + + // Change some value + modData.Data.(map[string]interface{})["runtime-per-user"].(map[string]interface{})["folk-fill-fuel-stack-size"].(map[string]interface{})["value"] = 150 + test.(map[string]interface{})["runtime-per-user"].(map[string]interface{})["folk-fill-fuel-stack-size"].(map[string]interface{})["value"] = float64(150) + + // write new data + newBytes, err := modData.Encode() + newBytesReader := bytes.NewReader(newBytes) + if err != nil { + t.Fatalf("couldn't Encode modData: %s", err) + } + + var newData FModData + err = newData.Decode(newBytesReader) + if err != nil { + t.Fatalf("couldn't Decode newBytes: %s", err) + } + + diff2 := deep.Equal(newData.Data, test) + if len(diff2) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff2), diff2) + } +} + +func TestModSettings0_17(t *testing.T) { + // Read data and compare to JSON + file, err := os.Open("../factorio_mod_settings_testfiles/mod_settings_0.17.dat") + if err != nil { + t.Fatalf("could not open mod-settings.dat: %s", err) + } + + var modData FModData + err = modData.Decode(file) + if err != nil { + t.Fatalf("could not decode FModData: %s", err) + } + + modDataJson, err := ioutil.ReadFile("../factorio_mod_settings_testfiles/mod_settings_0.17.json") + if err != nil { + t.Fatalf("could not read json-file: %s", err) + } + + var test interface{} + err = json.Unmarshal(modDataJson, &test) + if err != nil { + t.Fatalf("could not Unmarshal JSON: %s", err) + } + + diff := deep.Equal(modData.Data, test) + if len(diff) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff), diff) + } + + // Change some value + modData.Data.(map[string]interface{})["runtime-per-user"].(map[string]interface{})["max-inventory-cleanup-drop-range"].(map[string]interface{})["value"] = 150 + test.(map[string]interface{})["runtime-per-user"].(map[string]interface{})["max-inventory-cleanup-drop-range"].(map[string]interface{})["value"] = float64(150) + + // write new data + newBytes, err := modData.Encode() + newBytesReader := bytes.NewReader(newBytes) + if err != nil { + t.Fatalf("couldn't Encode modData: %s", err) + } + + var newData FModData + err = newData.Decode(newBytesReader) + if err != nil { + t.Fatalf("couldn't Decode newBytes: %s", err) + } + + diff2 := deep.Equal(newData.Data, test) + if len(diff2) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff2), diff2) + } +} + +func TestModSettings1_0(t *testing.T) { + // Read data and compare to JSON + file, err := os.Open("../factorio_mod_settings_testfiles/mod_settings_1.0.dat") + if err != nil { + t.Fatalf("could not open mod-settings.dat: %s", err) + } + + var modData FModData + err = modData.Decode(file) + if err != nil { + t.Fatalf("could not decode FModData: %s", err) + } + + modDataJson, err := ioutil.ReadFile("../factorio_mod_settings_testfiles/mod_settings_1.0.json") + if err != nil { + t.Fatalf("could not read json-file: %s", err) + } + + var test interface{} + err = json.Unmarshal(modDataJson, &test) + if err != nil { + t.Fatalf("could not Unmarshal JSON: %s", err) + } + + diff := deep.Equal(modData.Data, test) + if len(diff) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff), diff) + } + + // Change some value + modData.Data.(map[string]interface{})["startup"].(map[string]interface{})["angels-pavement-stack-size"].(map[string]interface{})["value"] = 200 + test.(map[string]interface{})["startup"].(map[string]interface{})["angels-pavement-stack-size"].(map[string]interface{})["value"] = float64(200) + + // write new data + newBytes, err := modData.Encode() + newBytesReader := bytes.NewReader(newBytes) + if err != nil { + t.Fatalf("couldn't Encode modData: %s", err) + } + + var newData FModData + err = newData.Decode(newBytesReader) + if err != nil { + t.Fatalf("couldn't Decode newBytes: %s", err) + } + + diff2 := deep.Equal(newData.Data, test) + if len(diff2) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff2), diff2) + } +} + +func TestModSettings1_1(t *testing.T) { + // Read data and compare to JSON + file, err := os.Open("../factorio_mod_settings_testfiles/mod_settings_1.1.dat") + if err != nil { + t.Fatalf("could not open mod-settings.dat: %s", err) + } + + var modData FModData + err = modData.Decode(file) + if err != nil { + t.Fatalf("could not decode FModData: %s", err) + } + + modDataJson, err := ioutil.ReadFile("../factorio_mod_settings_testfiles/mod_settings_1.1.json") + if err != nil { + t.Fatalf("could not read json-file: %s", err) + } + + var test interface{} + err = json.Unmarshal(modDataJson, &test) + if err != nil { + t.Fatalf("could not Unmarshal JSON: %s", err) + } + + diff := deep.Equal(modData.Data, test) + if len(diff) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff), diff) + } + + // Change some value + modData.Data.(map[string]interface{})["startup"].(map[string]interface{})["angels-pavement-stack-size"].(map[string]interface{})["value"] = 200 + test.(map[string]interface{})["startup"].(map[string]interface{})["angels-pavement-stack-size"].(map[string]interface{})["value"] = float64(200) + + // write new data + newBytes, err := modData.Encode() + newBytesReader := bytes.NewReader(newBytes) + if err != nil { + t.Fatalf("couldn't Encode modData: %s", err) + } + + var newData FModData + err = newData.Decode(newBytesReader) + if err != nil { + t.Fatalf("couldn't Decode newBytes: %s", err) + } + + diff2 := deep.Equal(newData.Data, test) + if len(diff2) > 0 { + t.Fatalf("Data has %d differences: %s", len(diff2), diff2) + } +} diff --git a/src/factorio/save.go b/src/factorio/save.go index a97cd6e3..16a4331b 100644 --- a/src/factorio/save.go +++ b/src/factorio/save.go @@ -235,64 +235,6 @@ func (h *SaveHeader) ReadFrom(r io.Reader) (err error) { return nil } -func readOptimUint(r io.Reader, v Version, bitSize int) (uint32, error) { - var b [4]byte - if !v.Less(Version{0, 14, 14, 0}) { - _, err := r.Read(b[:1]) - if err != nil { - return 0, err - } - if b[0] != 0xFF { - return uint32(b[0]), nil - } - } - - if bitSize < 0 || bitSize > 64 || (bitSize%8 != 0) { - panic("invalid bit size") - } - - _, err := r.Read(b[:bitSize/8]) - if err != nil { - return 0, err - } - - switch bitSize { - case 16: - return uint32(binary.LittleEndian.Uint16(b[:2])), nil - case 32: - return binary.LittleEndian.Uint32(b[:4]), nil - default: - panic("invalid bit size") - } -} - -func readString(r io.Reader, game Version, forceOptimized bool) (s string, err error) { - var n uint32 - - // since 0.16 read optimized uint - if !game.Less(Version{0, 16, 0, 0}) || forceOptimized { - n, err = readOptimUint(r, game, 32) - if err != nil { - return "", err - } - } else { - var b [4]byte - _, err := r.Read(b[:]) - if err != nil { - return "", fmt.Errorf("failed to read string length: %v", err) - } - n = uint32(binary.LittleEndian.Uint32(b[:])) - } - - d := make([]byte, n) - _, err = r.Read(d) - if err != nil { - return "", fmt.Errorf("failed to read string: %v", err) - } - - return string(d), nil -} - func (h SaveHeader) readStats(r io.Reader) (stats map[byte][]map[uint16]uint32, err error) { var scratch [4]byte stats = make(map[byte][]map[uint16]uint32) diff --git a/src/factorio_mod_settings_testfiles/mod_settings_0.16.dat b/src/factorio_mod_settings_testfiles/mod_settings_0.16.dat new file mode 100644 index 00000000..ee77eef4 Binary files /dev/null and b/src/factorio_mod_settings_testfiles/mod_settings_0.16.dat differ diff --git a/src/factorio_mod_settings_testfiles/mod_settings_0.16.json b/src/factorio_mod_settings_testfiles/mod_settings_0.16.json new file mode 100644 index 00000000..d00dc906 --- /dev/null +++ b/src/factorio_mod_settings_testfiles/mod_settings_0.16.json @@ -0,0 +1,485 @@ +{ + "startup": { + "charxpmod_xpinilevel": { + "value": 600.0 + }, + "charxpmod_xpmult": { + "value": 1.6 + }, + "charxpmod_xp_multiplier_bonus": { + "value": 1.0 + }, + "crash-sequence": { + "value": true + }, + "5d-change-inventory": { + "value": 60.0 + }, + "5d-change-stack": { + "value": 1.0 + }, + "5d-long-reach": { + "value": 6.0 + }, + "5d-long-mine": { + "value": 6.0 + }, + "5d-mining-speed": { + "value": 0.01 + }, + "5d-light-radius": { + "value": 25.0 + }, + "5d-ores": { + "value": false + }, + "5d-yield": { + "value": false + }, + "5d-tree": { + "value": false + }, + "5d-item-start": { + "value": "Nothing" + }, + "5d-equip-start": { + "value": "Nothing" + }, + "5d-train-tech": { + "value": false + }, + "5d-robot-tech": { + "value": false + }, + "5d-hp": { + "value": 250.0 + }, + "5d-healing": { + "value": 0.15 + }, + "5d-bicho-spawner-hp": { + "value": 1.0 + }, + "5d-bicho-hp": { + "value": 1.0 + }, + "5d-bicho-healing": { + "value": 1.0 + }, + "5d-suicide": { + "value": true + }, + "5d-exp": { + "value": true + }, + "5d-swimmer": { + "value": true + }, + "5d-climber": { + "value": true + }, + "5d-worm": { + "value": true + }, + "5d-trash": { + "value": false + }, + "5d-logistics": { + "value": false + }, + "5d-storage": { + "value": true + }, + "5d-stack": { + "value": true + }, + "5d-speed": { + "value": true + }, + "5d-speed-gun-turret": { + "value": true + }, + "5d-braking": { + "value": true + }, + "5d-grid": { + "value": true + }, + "5d-grid-override": { + "value": false + }, + "start-with-random-color": { + "value": true + }, + "start-with-unit-remote-control": { + "value": true + }, + "start-with-vehicle-miner": { + "value": true + }, + "SpaceX-research": { + "value": 1.0 + }, + "SpaceX-production": { + "value": 1.0 + }, + "SpaceX-launch-profile": { + "value": "Classic" + }, + "SpaceX-ignore-tech-multiplier": { + "value": false + }, + "SpaceX-no-space-sci": { + "value": false + }, + "SpaceX-cheaper-fusion-reactor": { + "value": false + }, + "SpaceX-no-bob": { + "value": false + }, + "bobmods-assembly-chemicalplants": { + "value": true + }, + "bobmods-assembly-electrolysers": { + "value": true + }, + "bobmods-assembly-electronicmachines": { + "value": true + }, + "bobmods-assembly-furnaces": { + "value": true + }, + "bobmods-assembly-multipurposefurnaces": { + "value": true + }, + "bobmods-assembly-oilrefineries": { + "value": true + }, + "bobmods-enemies-aliensdropartifacts": { + "value": true + }, + "bobmods-enemies-enableartifacts": { + "value": false + }, + "bobmods-enemies-enablenewartifacts": { + "value": false + }, + "bobmods-enemies-enablesmallartifacts": { + "value": false + }, + "bobmods-inserters-long2": { + "value": true + }, + "bobmods-inserters-more2": { + "value": true + }, + "bobmods-logistics-beltoverhaul": { + "value": false + }, + "bobmods-logistics-beltoverhaulspeed": { + "value": true + }, + "bobmods-logistics-beltperlevel": { + "value": 4.0 + }, + "bobmods-logistics-beltrequireprevious": { + "value": true + }, + "bobmods-logistics-beltstarting": { + "value": 3.0 + }, + "bobmods-logistics-disableroboports": { + "value": false + }, + "bobmods-logistics-drainlessinserters": { + "value": false + }, + "bobmods-logistics-flyingrobotframes": { + "value": true + }, + "bobmods-logistics-inserteroverhaul": { + "value": false + }, + "bobmods-logistics-inserterrequireprevious": { + "value": true + }, + "bobmods-logistics-pipeperlevel": { + "value": 4.0 + }, + "bobmods-logistics-pipestarting": { + "value": 11.0 + }, + "bobmods-logistics-roboportrecipeupdate": { + "value": true + }, + "bobmods-logistics-robotparts": { + "value": true + }, + "bobmods-logistics-robotrequireprevious": { + "value": true + }, + "bobmods-logistics-ugdistanceoverhaul": { + "value": false + }, + "bobmods-mining-areadrills": { + "value": true + }, + "bobmods-mining-miningaxes": { + "value": true + }, + "bobmods-mining-miningdrills": { + "value": true + }, + "bobmods-mining-pumpjacks": { + "value": true + }, + "bobmods-mining-waterminers": { + "value": true + }, + "replace-electronics": { + "value": true + }, + "replace-logistics": { + "value": true + }, + "replace-warfare": { + "value": true + }, + "bobmods-modules-enablegodmodules": { + "value": false + }, + "bobmods-modules-enablegreenmodules": { + "value": true + }, + "bobmods-modules-enableproductivitylimitation": { + "value": true + }, + "bobmods-modules-enablerawproductivitymodules": { + "value": true + }, + "bobmods-modules-enablerawspeedmodules": { + "value": true + }, + "bobmods-modules-perlevel-bonus-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-bonus-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-bonus-pollutioncreate": { + "value": 0.5 + }, + "bobmods-modules-perlevel-bonus-productivity": { + "value": 0.05 + }, + "bobmods-modules-perlevel-bonus-speed": { + "value": 0.2 + }, + "bobmods-modules-perlevel-penalty-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-penalty-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-penalty-speed": { + "value": 0.05 + }, + "bobmods-modules-productivityhasspeed": { + "value": false + }, + "bobmods-modules-start-bonus-consumption": { + "value": 0.0 + }, + "bobmods-modules-start-bonus-pollution": { + "value": 0.0 + }, + "bobmods-modules-start-bonus-pollutioncreate": { + "value": 0.0 + }, + "bobmods-modules-start-bonus-productivity": { + "value": 0.0 + }, + "bobmods-modules-start-bonus-speed": { + "value": 0.0 + }, + "bobmods-modules-start-penalty-consumption": { + "value": 0.0 + }, + "bobmods-modules-start-penalty-pollution": { + "value": 0.0 + }, + "bobmods-modules-start-penalty-speed": { + "value": 0.2 + }, + "bobmods-modules-transmitproductivity": { + "value": false + }, + "bobmods-gems-amethystratio": { + "value": 0.5 + }, + "bobmods-gems-diamondratio": { + "value": 0.2 + }, + "bobmods-gems-emeraldratio": { + "value": 0.6 + }, + "bobmods-gems-rubyratio": { + "value": 1.0 + }, + "bobmods-gems-sapphireratio": { + "value": 0.8 + }, + "bobmods-gems-topazratio": { + "value": 0.4 + }, + "bobmods-ores-enablebauxite": { + "value": false + }, + "bobmods-ores-enablecobaltore": { + "value": false + }, + "bobmods-ores-enablegemsore": { + "value": false + }, + "bobmods-ores-enablegoldore": { + "value": false + }, + "bobmods-ores-enableleadore": { + "value": false + }, + "bobmods-ores-enablenickelore": { + "value": false + }, + "bobmods-ores-enablequartz": { + "value": false + }, + "bobmods-ores-enablerutile": { + "value": false + }, + "bobmods-ores-enablesilverore": { + "value": false + }, + "bobmods-ores-enablesulfur": { + "value": false + }, + "bobmods-ores-enablethoriumore": { + "value": false + }, + "bobmods-ores-enabletinore": { + "value": false + }, + "bobmods-ores-enabletungstenore": { + "value": false + }, + "bobmods-ores-enablewaterores": { + "value": false + }, + "bobmods-ores-enablezincore": { + "value": false + }, + "bobmods-ores-enhancestone": { + "value": false + }, + "bobmods-ores-gemprobability": { + "value": 0.05 + }, + "bobmods-ores-gemsfromotherores": { + "value": false + }, + "bobmods-ores-infiniteore": { + "value": false + }, + "bobmods-ores-leadgivesnickel": { + "value": false + }, + "bobmods-ores-leadnickelratio": { + "value": 0.6 + }, + "bobmods-ores-nickelcobaltratio": { + "value": 0.4 + }, + "bobmods-ores-nickelgivescobalt": { + "value": false + }, + "bobmods-ores-unsortedgemore": { + "value": false + }, + "bobmods-plates-batteryupdate": { + "value": true + }, + "bobmods-plates-cheapersteel": { + "value": true + }, + "bobmods-plates-groundwater": { + "value": false + }, + "bobmods-plates-inventorysize": { + "value": 60.0 + }, + "bobmods-plates-oreoverride": { + "value": true + }, + "bobmods-plates-purewater": { + "value": true + }, + "bobmods-plates-vanillabarrelling": { + "value": false + }, + "bobmods-tech-colorupdate": { + "value": false + }, + "bobmods-vehicleequipment-enablevehiclegrids": { + "value": true + }, + "bobmods-warfare-drainlesslaserturrets": { + "value": false + }, + "bobmods-warfare-robotupdate": { + "value": true + } + }, + "runtime-global": { + "extra behaviors": { + "value": true + }, + "infinite evolution": { + "value": true + }, + "evolution scaling factor": { + "value": 1.0 + }, + "charxpmod_afk": { + "value": 15.0 + }, + "charxpmod_death_penal": { + "value": 10.0 + }, + "charxpmod_time_ratio_xp": { + "value": true + }, + "aai-max-structs-per-tick": { + "value": 0.0 + }, + "path-visualisation": { + "value": true + }, + "vehicle-mining-multiplier": { + "value": 100.0 + } + }, + "runtime-per-user": { + "charxpmod_print_xp_user": { + "value": false + }, + "folk-fill-ammo-stack-size": { + "value": 10.0 + }, + "folk-fill-fuel-stack-size": { + "value": 100.0 + }, + "hand-deploy-vehicle-ai-default": { + "value": "Auto" + } + } +} \ No newline at end of file diff --git a/src/factorio_mod_settings_testfiles/mod_settings_0.17.dat b/src/factorio_mod_settings_testfiles/mod_settings_0.17.dat new file mode 100644 index 00000000..9f252358 Binary files /dev/null and b/src/factorio_mod_settings_testfiles/mod_settings_0.17.dat differ diff --git a/src/factorio_mod_settings_testfiles/mod_settings_0.17.json b/src/factorio_mod_settings_testfiles/mod_settings_0.17.json new file mode 100644 index 00000000..cb5c3c4a --- /dev/null +++ b/src/factorio_mod_settings_testfiles/mod_settings_0.17.json @@ -0,0 +1,368 @@ +{ + "runtime-global": { + "loader-snapping": { + "value": true + }, + "loader-use-trains": { + "value": "disabled" + } + }, + "runtime-per-user": { + "cleanup-logistic-request-overflow": { + "value": true + }, + "distribution-delay": { + "value": 0.9 + }, + "drop-trash-to-chests": { + "value": true + }, + "enable-ed": { + "value": true + }, + "ignored-entities": { + "value": "" + }, + "immediately-start-crafting": { + "value": true + }, + "inventory-cleanup-custom-trash": { + "value": "iron-plate:800 copper-plate:600 steel-plate:600 stone-brick:400 artillery-shell:0" + }, + "max-inventory-cleanup-drop-range": { + "value": 30 + }, + "take-from-car": { + "value": true + } + }, + "startup": { + "Warehousing-copy-logistic-system": { + "value": false + }, + "Warehousing-icon-scaling": { + "value": true + }, + "Warehousing-sixteen-mode": { + "value": false + }, + "bobmods-assembly-chemicalplants": { + "value": true + }, + "bobmods-assembly-distilleries": { + "value": true + }, + "bobmods-assembly-electrolysers": { + "value": true + }, + "bobmods-assembly-electronicmachines": { + "value": true + }, + "bobmods-assembly-furnaces": { + "value": true + }, + "bobmods-assembly-multipurposefurnaces": { + "value": true + }, + "bobmods-assembly-oilrefineries": { + "value": true + }, + "bobmods-colorupdate": { + "value": true + }, + "bobmods-enemies-aliensdropartifacts": { + "value": true + }, + "bobmods-enemies-enableartifacts": { + "value": true + }, + "bobmods-enemies-enablenewartifacts": { + "value": true + }, + "bobmods-enemies-enablesmallartifacts": { + "value": true + }, + "bobmods-gems-amethystratio": { + "value": 0.5 + }, + "bobmods-gems-diamondratio": { + "value": 0.2 + }, + "bobmods-gems-emeraldratio": { + "value": 0.6 + }, + "bobmods-gems-rubyratio": { + "value": 1 + }, + "bobmods-gems-sapphireratio": { + "value": 0.8 + }, + "bobmods-gems-topazratio": { + "value": 0.4 + }, + "bobmods-inserters-long2": { + "value": true + }, + "bobmods-inserters-more2": { + "value": true + }, + "bobmods-logistics-beltoverhaul": { + "value": true + }, + "bobmods-logistics-beltoverhaulspeed": { + "value": true + }, + "bobmods-logistics-beltperlevel": { + "value": 4 + }, + "bobmods-logistics-beltrequireprevious": { + "value": true + }, + "bobmods-logistics-beltspeedperlevel": { + "value": 12.5 + }, + "bobmods-logistics-beltstarting": { + "value": 3 + }, + "bobmods-logistics-disableroboports": { + "value": false + }, + "bobmods-logistics-drainlessinserters": { + "value": false + }, + "bobmods-logistics-flyingrobotframes": { + "value": true + }, + "bobmods-logistics-inserteroverhaul": { + "value": true + }, + "bobmods-logistics-inserterrequireprevious": { + "value": true + }, + "bobmods-logistics-pipeperlevel": { + "value": 4 + }, + "bobmods-logistics-pipestarting": { + "value": 11 + }, + "bobmods-logistics-roboportrecipeupdate": { + "value": true + }, + "bobmods-logistics-robotparts": { + "value": true + }, + "bobmods-logistics-robotrequireprevious": { + "value": true + }, + "bobmods-logistics-ugdistanceoverhaul": { + "value": true + }, + "bobmods-mining-areadrills": { + "value": true + }, + "bobmods-mining-miningaxes": { + "value": true + }, + "bobmods-mining-miningdrills": { + "value": true + }, + "bobmods-mining-pumpjacks": { + "value": true + }, + "bobmods-mining-waterminers": { + "value": true + }, + "bobmods-modules-enablegodmodules": { + "value": false + }, + "bobmods-modules-enablegreenmodules": { + "value": true + }, + "bobmods-modules-enableproductivitylimitation": { + "value": true + }, + "bobmods-modules-enablerawproductivitymodules": { + "value": true + }, + "bobmods-modules-enablerawspeedmodules": { + "value": true + }, + "bobmods-modules-perlevel-bonus-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-bonus-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-bonus-pollutioncreate": { + "value": 0.5 + }, + "bobmods-modules-perlevel-bonus-productivity": { + "value": 0.05 + }, + "bobmods-modules-perlevel-bonus-speed": { + "value": 0.2 + }, + "bobmods-modules-perlevel-penalty-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-penalty-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-penalty-speed": { + "value": 0.05 + }, + "bobmods-modules-productivityhasspeed": { + "value": false + }, + "bobmods-modules-start-bonus-consumption": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollution": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollutioncreate": { + "value": 0 + }, + "bobmods-modules-start-bonus-productivity": { + "value": 0 + }, + "bobmods-modules-start-bonus-speed": { + "value": 0 + }, + "bobmods-modules-start-penalty-consumption": { + "value": 0 + }, + "bobmods-modules-start-penalty-pollution": { + "value": 0 + }, + "bobmods-modules-start-penalty-speed": { + "value": 0.2 + }, + "bobmods-modules-transmitproductivity": { + "value": false + }, + "bobmods-ores-enablebauxite": { + "value": false + }, + "bobmods-ores-enablecobaltore": { + "value": false + }, + "bobmods-ores-enablegemsore": { + "value": false + }, + "bobmods-ores-enablegoldore": { + "value": false + }, + "bobmods-ores-enableleadore": { + "value": false + }, + "bobmods-ores-enablenickelore": { + "value": false + }, + "bobmods-ores-enablequartz": { + "value": false + }, + "bobmods-ores-enablerutile": { + "value": false + }, + "bobmods-ores-enablesilverore": { + "value": false + }, + "bobmods-ores-enablesulfur": { + "value": false + }, + "bobmods-ores-enablethoriumore": { + "value": false + }, + "bobmods-ores-enabletinore": { + "value": false + }, + "bobmods-ores-enabletungstenore": { + "value": false + }, + "bobmods-ores-enablewaterores": { + "value": false + }, + "bobmods-ores-enablezincore": { + "value": false + }, + "bobmods-ores-enhancestone": { + "value": false + }, + "bobmods-ores-gemprobability": { + "value": 0.05 + }, + "bobmods-ores-gemsfromotherores": { + "value": false + }, + "bobmods-ores-infiniteore": { + "value": false + }, + "bobmods-ores-leadgivesnickel": { + "value": false + }, + "bobmods-ores-leadnickelratio": { + "value": 0.6 + }, + "bobmods-ores-nickelcobaltratio": { + "value": 0.4 + }, + "bobmods-ores-nickelgivescobalt": { + "value": false + }, + "bobmods-ores-unsortedgemore": { + "value": false + }, + "bobmods-plates-batteryupdate": { + "value": true + }, + "bobmods-plates-cheapersteel": { + "value": true + }, + "bobmods-plates-groundwater": { + "value": false + }, + "bobmods-plates-inventorysize": { + "value": 80 + }, + "bobmods-plates-oreoverride": { + "value": true + }, + "bobmods-plates-purewater": { + "value": true + }, + "bobmods-plates-vanillabarrelling": { + "value": false + }, + "bobmods-power-accumulators": { + "value": true + }, + "bobmods-power-fluidgenerator": { + "value": true + }, + "bobmods-power-poles": { + "value": true + }, + "bobmods-power-solar": { + "value": true + }, + "bobmods-power-steam": { + "value": true + }, + "bobmods-tech-colorupdate": { + "value": true + }, + "bobmods-vehicleequipment-enablevehiclegrids": { + "value": true + }, + "bobmods-warfare-drainlesslaserturrets": { + "value": false + }, + "bobmods-warfare-robotupdate": { + "value": true + }, + "early-autotrash-research": { + "value": true + } + } +} \ No newline at end of file diff --git a/src/factorio_mod_settings_testfiles/mod_settings_1.0.dat b/src/factorio_mod_settings_testfiles/mod_settings_1.0.dat new file mode 100644 index 00000000..a8cc6273 Binary files /dev/null and b/src/factorio_mod_settings_testfiles/mod_settings_1.0.dat differ diff --git a/src/factorio_mod_settings_testfiles/mod_settings_1.0.json b/src/factorio_mod_settings_testfiles/mod_settings_1.0.json new file mode 100644 index 00000000..637904c8 --- /dev/null +++ b/src/factorio_mod_settings_testfiles/mod_settings_1.0.json @@ -0,0 +1,554 @@ +{ + "runtime-global": { + "bobmods-classes-class-select": { + "value": false + }, + "bobmods-classes-class-select-user": { + "value": false + }, + "bobmods-classes-editor-mode": { + "value": false + }, + "bobmods-classes-god-mode": { + "value": false + }, + "flib-translations-per-tick": { + "value": 50 + }, + "loader-snapping": { + "value": true + }, + "loader-use-trains": { + "value": "disabled" + } + }, + "runtime-per-user": { + "bobmods-inserters-show-window": { + "value": "off" + } + }, + "startup": { + "angels-cab-energy-transfer-rate-mk1": { + "value": 500000 + }, + "angels-components-stack-size": { + "value": 1000 + }, + "angels-crawlertrain-tier-amount": { + "value": 3 + }, + "angels-disable-bobs-chemical-plants": { + "value": true + }, + "angels-disable-bobs-distilleries": { + "value": true + }, + "angels-disable-bobs-electrolysers": { + "value": true + }, + "angels-disable-vanilla-chemical-plants": { + "value": true + }, + "angels-enable-acids": { + "value": true + }, + "angels-enable-auto-barreling": { + "value": false + }, + "angels-enable-components": { + "value": false + }, + "angels-enable-converter": { + "value": true + }, + "angels-enable-hide-void": { + "value": true + }, + "angels-enable-industries": { + "value": true + }, + "angels-enable-inline-tank": { + "value": true + }, + "angels-enable-pressure-tank": { + "value": true + }, + "angels-enable-silos": { + "value": true + }, + "angels-enable-storage-icon-scaling": { + "value": false + }, + "angels-enable-tech": { + "value": false + }, + "angels-enable-warehouses": { + "value": true + }, + "angels-enablefluidreq": { + "value": true + }, + "angels-hide-converter": { + "value": true + }, + "angels-hq-graphics": { + "value": true + }, + "angels-infinite-yield": { + "value": 20 + }, + "angels-keepuranacid": { + "value": true + }, + "angels-lower-infinite-yield": { + "value": 1 + }, + "angels-marathon-buildingmulti": { + "value": 2 + }, + "angels-marathon-buildingtime": { + "value": 2 + }, + "angels-marathon-intermediatemulti": { + "value": 1 + }, + "angels-marathon-rawmulti": { + "value": 1 + }, + "angels-pavement-stack-size": { + "value": 200 + }, + "angels-petrotrain-tier-amount": { + "value": 3 + }, + "angels-return-ingredients": { + "value": true + }, + "angels-show-chemical-formula": { + "value": true + }, + "angels-smeltingtrain-tier-amount": { + "value": 3 + }, + "angels-starting-resource-base": { + "value": 40000 + }, + "angels-starting-resource-ore1": { + "value": true + }, + "angels-starting-resource-ore2": { + "value": false + }, + "angels-starting-resource-ore3": { + "value": true + }, + "angels-starting-resource-ore4": { + "value": false + }, + "angels-starting-resource-ore5": { + "value": true + }, + "angels-starting-resource-ore6": { + "value": true + }, + "angels-tryptophobia-friendly-stiratite": { + "value": false + }, + "bobmods-assembly-burner": { + "value": true + }, + "bobmods-assembly-centrifuge": { + "value": true + }, + "bobmods-assembly-chemicalplants": { + "value": true + }, + "bobmods-assembly-distilleries": { + "value": true + }, + "bobmods-assembly-electrolysers": { + "value": true + }, + "bobmods-assembly-electronicmachines": { + "value": true + }, + "bobmods-assembly-furnaces": { + "value": true + }, + "bobmods-assembly-limits": { + "value": false + }, + "bobmods-assembly-multipurposefurnaces": { + "value": true + }, + "bobmods-assembly-oilfurnaces": { + "value": true + }, + "bobmods-assembly-oilrefineries": { + "value": true + }, + "bobmods-burnerphase": { + "value": false + }, + "bobmods-colorupdate": { + "value": true + }, + "bobmods-enemies-aliensdropartifacts": { + "value": true + }, + "bobmods-enemies-biggersooner": { + "value": false + }, + "bobmods-enemies-enableartifacts": { + "value": true + }, + "bobmods-enemies-enablenewartifacts": { + "value": true + }, + "bobmods-enemies-enablesmallartifacts": { + "value": true + }, + "bobmods-enemies-healthincrease": { + "value": false + }, + "bobmods-enemies-leviathanfrequency": { + "value": 0.05 + }, + "bobmods-enemies-superspawner": { + "value": false + }, + "bobmods-gems-amethystratio": { + "value": 0.5 + }, + "bobmods-gems-diamondratio": { + "value": 0.2 + }, + "bobmods-gems-emeraldratio": { + "value": 0.6 + }, + "bobmods-gems-rubyratio": { + "value": 1 + }, + "bobmods-gems-sapphireratio": { + "value": 0.8 + }, + "bobmods-gems-topazratio": { + "value": 0.4 + }, + "bobmods-inserters-long2": { + "value": true + }, + "bobmods-inserters-more2": { + "value": true + }, + "bobmods-logistics-beltoverhaul": { + "value": true + }, + "bobmods-logistics-beltoverhaulspeed": { + "value": false + }, + "bobmods-logistics-beltperlevel": { + "value": 4 + }, + "bobmods-logistics-beltrequireprevious": { + "value": true + }, + "bobmods-logistics-beltspeedperlevel": { + "value": 15 + }, + "bobmods-logistics-beltstarting": { + "value": 3 + }, + "bobmods-logistics-disableroboports": { + "value": false + }, + "bobmods-logistics-drainlessinserters": { + "value": false + }, + "bobmods-logistics-fluidwagonbase": { + "value": 25 + }, + "bobmods-logistics-flyingrobotframes": { + "value": true + }, + "bobmods-logistics-inserteroverhaul": { + "value": true + }, + "bobmods-logistics-inserterrequireprevious": { + "value": true + }, + "bobmods-logistics-pipeperlevel": { + "value": 4 + }, + "bobmods-logistics-pipestarting": { + "value": 11 + }, + "bobmods-logistics-roboportrecipeupdate": { + "value": true + }, + "bobmods-logistics-robotparts": { + "value": true + }, + "bobmods-logistics-robotrequireprevious": { + "value": true + }, + "bobmods-logistics-storagetankbase": { + "value": 25 + }, + "bobmods-logistics-trains": { + "value": true + }, + "bobmods-logistics-ugdistanceoverhaul": { + "value": true + }, + "bobmods-mining-areadrills": { + "value": true + }, + "bobmods-mining-miningaxes": { + "value": true + }, + "bobmods-mining-miningdrills": { + "value": true + }, + "bobmods-mining-pumpjacks": { + "value": true + }, + "bobmods-mining-steamminingdrills": { + "value": true + }, + "bobmods-mining-waterminers": { + "value": true + }, + "bobmods-modules-enablegodmodules": { + "value": false + }, + "bobmods-modules-enablegreenmodules": { + "value": true + }, + "bobmods-modules-enableproductivitylimitation": { + "value": true + }, + "bobmods-modules-enablerawproductivitymodules": { + "value": true + }, + "bobmods-modules-enablerawspeedmodules": { + "value": true + }, + "bobmods-modules-perlevel-bonus-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-bonus-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-bonus-pollutioncreate": { + "value": 0.5 + }, + "bobmods-modules-perlevel-bonus-productivity": { + "value": 0.05 + }, + "bobmods-modules-perlevel-bonus-speed": { + "value": 0.2 + }, + "bobmods-modules-perlevel-penalty-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-penalty-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-penalty-speed": { + "value": 0.05 + }, + "bobmods-modules-productivityhasspeed": { + "value": false + }, + "bobmods-modules-start-bonus-consumption": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollution": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollutioncreate": { + "value": 0 + }, + "bobmods-modules-start-bonus-productivity": { + "value": 0 + }, + "bobmods-modules-start-bonus-speed": { + "value": 0 + }, + "bobmods-modules-start-penalty-consumption": { + "value": 0 + }, + "bobmods-modules-start-penalty-pollution": { + "value": 0 + }, + "bobmods-modules-start-penalty-speed": { + "value": 0.2 + }, + "bobmods-modules-transmitproductivity": { + "value": false + }, + "bobmods-ores-enablebauxite": { + "value": false + }, + "bobmods-ores-enablecobaltore": { + "value": false + }, + "bobmods-ores-enablegemsore": { + "value": false + }, + "bobmods-ores-enablegoldore": { + "value": false + }, + "bobmods-ores-enableleadore": { + "value": false + }, + "bobmods-ores-enablenickelore": { + "value": false + }, + "bobmods-ores-enablequartz": { + "value": false + }, + "bobmods-ores-enablerutile": { + "value": false + }, + "bobmods-ores-enablesilverore": { + "value": false + }, + "bobmods-ores-enablesulfur": { + "value": false + }, + "bobmods-ores-enablethoriumore": { + "value": false + }, + "bobmods-ores-enabletinore": { + "value": false + }, + "bobmods-ores-enabletungstenore": { + "value": false + }, + "bobmods-ores-enablewaterores": { + "value": false + }, + "bobmods-ores-enablezincore": { + "value": false + }, + "bobmods-ores-gemprobability": { + "value": 0.05 + }, + "bobmods-ores-gemsfromotherores": { + "value": false + }, + "bobmods-ores-infiniteore": { + "value": false + }, + "bobmods-ores-leadgivesnickel": { + "value": false + }, + "bobmods-ores-leadnickelratio": { + "value": 0.6 + }, + "bobmods-ores-nickelcobaltratio": { + "value": 0.4 + }, + "bobmods-ores-nickelgivescobalt": { + "value": false + }, + "bobmods-ores-unsortedgemore": { + "value": false + }, + "bobmods-plates-batteryupdate": { + "value": true + }, + "bobmods-plates-bluedeuterium": { + "value": false + }, + "bobmods-plates-cheapersteel": { + "value": true + }, + "bobmods-plates-convert-recipes": { + "value": true + }, + "bobmods-plates-groundwater": { + "value": false + }, + "bobmods-plates-inventorysize": { + "value": 80 + }, + "bobmods-plates-newsteel": { + "value": true + }, + "bobmods-plates-nuclearupdate": { + "value": true + }, + "bobmods-plates-oreoverride": { + "value": true + }, + "bobmods-plates-purewater": { + "value": true + }, + "bobmods-plates-vanillabarrelling": { + "value": false + }, + "bobmods-power-accumulators": { + "value": true + }, + "bobmods-power-burnergenerator": { + "value": true + }, + "bobmods-power-fluidgenerator": { + "value": true + }, + "bobmods-power-heatsources": { + "value": true + }, + "bobmods-power-nuclear": { + "value": true + }, + "bobmods-power-poles": { + "value": true + }, + "bobmods-power-solar": { + "value": true + }, + "bobmods-power-steam": { + "value": true + }, + "bobmods-revamp-hardmode": { + "value": true + }, + "bobmods-revamp-nuclear": { + "value": true + }, + "bobmods-revamp-oil": { + "value": true + }, + "bobmods-revamp-old-oil": { + "value": true + }, + "bobmods-revamp-rtg": { + "value": true + }, + "bobmods-tech-colorupdate": { + "value": true + }, + "bobmods-vehicleequipment-enablevehiclegrids": { + "value": true + }, + "bobmods-warfare-drainlesslaserturrets": { + "value": false + }, + "bobmods-warfare-robotupdate": { + "value": true + }, + "replace-electronics": { + "value": true + }, + "replace-research-icons": { + "value": true + }, + "replace-warfare": { + "value": true + } + } +} \ No newline at end of file diff --git a/src/factorio_mod_settings_testfiles/mod_settings_1.1.dat b/src/factorio_mod_settings_testfiles/mod_settings_1.1.dat new file mode 100644 index 00000000..363c0cde Binary files /dev/null and b/src/factorio_mod_settings_testfiles/mod_settings_1.1.dat differ diff --git a/src/factorio_mod_settings_testfiles/mod_settings_1.1.json b/src/factorio_mod_settings_testfiles/mod_settings_1.1.json new file mode 100644 index 00000000..2d9cd962 --- /dev/null +++ b/src/factorio_mod_settings_testfiles/mod_settings_1.1.json @@ -0,0 +1,557 @@ +{ + "runtime-global": { + "flib-translations-per-tick": { + "value": 50 + }, + "loader-snapping": { + "value": true + }, + "loader-use-trains": { + "value": "disabled" + } + }, + "runtime-per-user": { + "bobmods-inserters-gui-position": { + "value": "right" + }, + "bobmods-inserters-show-window": { + "value": "off" + } + }, + "startup": { + "angels-cab-energy-transfer-rate-mk1": { + "value": 500000 + }, + "angels-components-stack-size": { + "value": 1000 + }, + "angels-crawlertrain-tier-amount": { + "value": 3 + }, + "angels-disable-bobs-chemical-plants": { + "value": true + }, + "angels-disable-bobs-distilleries": { + "value": true + }, + "angels-disable-bobs-electrolysers": { + "value": true + }, + "angels-disable-vanilla-chemical-plants": { + "value": true + }, + "angels-enable-acids": { + "value": true + }, + "angels-enable-auto-barreling": { + "value": "Disabled" + }, + "angels-enable-components": { + "value": false + }, + "angels-enable-converter": { + "value": true + }, + "angels-enable-hide-void": { + "value": true + }, + "angels-enable-industries": { + "value": true + }, + "angels-enable-inline-tank": { + "value": true + }, + "angels-enable-oresilos": { + "value": true + }, + "angels-enable-pressure-tank": { + "value": true + }, + "angels-enable-silos": { + "value": true + }, + "angels-enable-storage-icon-scaling": { + "value": false + }, + "angels-enable-tech": { + "value": false + }, + "angels-enable-warehouses": { + "value": true + }, + "angels-enablefluidreq": { + "value": true + }, + "angels-hide-converter": { + "value": true + }, + "angels-hq-graphics": { + "value": true + }, + "angels-infinite-yield": { + "value": 20 + }, + "angels-keepuranacid": { + "value": true + }, + "angels-lower-infinite-yield": { + "value": 1 + }, + "angels-marathon-buildingmulti": { + "value": 2 + }, + "angels-marathon-buildingtime": { + "value": 2 + }, + "angels-marathon-intermediatemulti": { + "value": 1 + }, + "angels-marathon-rawmulti": { + "value": 1 + }, + "angels-pavement-stack-size": { + "value": 200 + }, + "angels-petrotrain-tier-amount": { + "value": 3 + }, + "angels-return-ingredients": { + "value": true + }, + "angels-show-chemical-formula": { + "value": true + }, + "angels-smeltingtrain-tier-amount": { + "value": 3 + }, + "angels-starting-resource-base": { + "value": 40000 + }, + "angels-starting-resource-ore1": { + "value": true + }, + "angels-starting-resource-ore2": { + "value": false + }, + "angels-starting-resource-ore3": { + "value": true + }, + "angels-starting-resource-ore4": { + "value": false + }, + "angels-starting-resource-ore5": { + "value": true + }, + "angels-starting-resource-ore6": { + "value": true + }, + "angels-tryptophobia-friendly-stiratite": { + "value": false + }, + "bobmods-assembly-burner": { + "value": true + }, + "bobmods-assembly-centrifuge": { + "value": true + }, + "bobmods-assembly-chemicalplants": { + "value": true + }, + "bobmods-assembly-distilleries": { + "value": true + }, + "bobmods-assembly-electrolysers": { + "value": true + }, + "bobmods-assembly-electronicmachines": { + "value": true + }, + "bobmods-assembly-furnaces": { + "value": true + }, + "bobmods-assembly-limits": { + "value": false + }, + "bobmods-assembly-multipurposefurnaces": { + "value": true + }, + "bobmods-assembly-oilfurnaces": { + "value": true + }, + "bobmods-assembly-oilrefineries": { + "value": true + }, + "bobmods-burnerphase": { + "value": false + }, + "bobmods-colorupdate": { + "value": true + }, + "bobmods-enemies-aliensdropartifacts": { + "value": true + }, + "bobmods-enemies-biggersooner": { + "value": false + }, + "bobmods-enemies-enableartifacts": { + "value": true + }, + "bobmods-enemies-enablenewartifacts": { + "value": true + }, + "bobmods-enemies-enablesmallartifacts": { + "value": true + }, + "bobmods-enemies-healthincrease": { + "value": false + }, + "bobmods-enemies-leviathanfrequency": { + "value": 0.05 + }, + "bobmods-enemies-superspawner": { + "value": false + }, + "bobmods-gems-amethystratio": { + "value": 0.5 + }, + "bobmods-gems-diamondratio": { + "value": 0.2 + }, + "bobmods-gems-emeraldratio": { + "value": 0.6 + }, + "bobmods-gems-rubyratio": { + "value": 1 + }, + "bobmods-gems-sapphireratio": { + "value": 0.8 + }, + "bobmods-gems-topazratio": { + "value": 0.4 + }, + "bobmods-inserters-long2": { + "value": true + }, + "bobmods-inserters-more2": { + "value": true + }, + "bobmods-library-recipe-cleanup": { + "value": true + }, + "bobmods-library-technology-cleanup": { + "value": true + }, + "bobmods-logistics-beltoverhaul": { + "value": true + }, + "bobmods-logistics-beltoverhaulspeed": { + "value": false + }, + "bobmods-logistics-beltperlevel": { + "value": 4 + }, + "bobmods-logistics-beltrequireprevious": { + "value": true + }, + "bobmods-logistics-beltspeedperlevel": { + "value": 15 + }, + "bobmods-logistics-beltstarting": { + "value": 3 + }, + "bobmods-logistics-disableroboports": { + "value": false + }, + "bobmods-logistics-drainlessinserters": { + "value": false + }, + "bobmods-logistics-fluidwagonbase": { + "value": 25 + }, + "bobmods-logistics-flyingrobotframes": { + "value": true + }, + "bobmods-logistics-inserteroverhaul": { + "value": true + }, + "bobmods-logistics-inserterrequireprevious": { + "value": true + }, + "bobmods-logistics-pipeperlevel": { + "value": 4 + }, + "bobmods-logistics-pipestarting": { + "value": 11 + }, + "bobmods-logistics-roboportrecipeupdate": { + "value": true + }, + "bobmods-logistics-robotparts": { + "value": true + }, + "bobmods-logistics-robotrequireprevious": { + "value": true + }, + "bobmods-logistics-storagetankbase": { + "value": 25 + }, + "bobmods-logistics-trains": { + "value": true + }, + "bobmods-logistics-ugdistanceoverhaul": { + "value": true + }, + "bobmods-mining-areadrills": { + "value": true + }, + "bobmods-mining-miningaxes": { + "value": true + }, + "bobmods-mining-miningdrills": { + "value": true + }, + "bobmods-mining-pumpjacks": { + "value": true + }, + "bobmods-mining-steamminingdrills": { + "value": true + }, + "bobmods-mining-waterminers": { + "value": true + }, + "bobmods-modules-enablegodmodules": { + "value": false + }, + "bobmods-modules-enablegreenmodules": { + "value": true + }, + "bobmods-modules-enableproductivitylimitation": { + "value": true + }, + "bobmods-modules-enablerawproductivitymodules": { + "value": true + }, + "bobmods-modules-enablerawspeedmodules": { + "value": true + }, + "bobmods-modules-perlevel-bonus-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-bonus-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-bonus-pollutioncreate": { + "value": 0.5 + }, + "bobmods-modules-perlevel-bonus-productivity": { + "value": 0.05 + }, + "bobmods-modules-perlevel-bonus-speed": { + "value": 0.2 + }, + "bobmods-modules-perlevel-penalty-consumption": { + "value": 0.1 + }, + "bobmods-modules-perlevel-penalty-pollution": { + "value": 0.15 + }, + "bobmods-modules-perlevel-penalty-speed": { + "value": 0.05 + }, + "bobmods-modules-productivityhasspeed": { + "value": false + }, + "bobmods-modules-start-bonus-consumption": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollution": { + "value": 0 + }, + "bobmods-modules-start-bonus-pollutioncreate": { + "value": 0 + }, + "bobmods-modules-start-bonus-productivity": { + "value": 0 + }, + "bobmods-modules-start-bonus-speed": { + "value": 0 + }, + "bobmods-modules-start-penalty-consumption": { + "value": 0 + }, + "bobmods-modules-start-penalty-pollution": { + "value": 0 + }, + "bobmods-modules-start-penalty-speed": { + "value": 0.2 + }, + "bobmods-modules-transmitproductivity": { + "value": false + }, + "bobmods-ores-enablebauxite": { + "value": false + }, + "bobmods-ores-enablecobaltore": { + "value": false + }, + "bobmods-ores-enablegemsore": { + "value": false + }, + "bobmods-ores-enablegoldore": { + "value": false + }, + "bobmods-ores-enableleadore": { + "value": false + }, + "bobmods-ores-enablenickelore": { + "value": false + }, + "bobmods-ores-enablequartz": { + "value": false + }, + "bobmods-ores-enablerutile": { + "value": false + }, + "bobmods-ores-enablesilverore": { + "value": false + }, + "bobmods-ores-enablesulfur": { + "value": false + }, + "bobmods-ores-enablethoriumore": { + "value": false + }, + "bobmods-ores-enabletinore": { + "value": false + }, + "bobmods-ores-enabletungstenore": { + "value": false + }, + "bobmods-ores-enablewaterores": { + "value": false + }, + "bobmods-ores-enablezincore": { + "value": false + }, + "bobmods-ores-gemprobability": { + "value": 0.05 + }, + "bobmods-ores-gemsfromotherores": { + "value": false + }, + "bobmods-ores-infiniteore": { + "value": false + }, + "bobmods-ores-leadgivesnickel": { + "value": false + }, + "bobmods-ores-leadnickelratio": { + "value": 0.6 + }, + "bobmods-ores-nickelcobaltratio": { + "value": 0.4 + }, + "bobmods-ores-nickelgivescobalt": { + "value": false + }, + "bobmods-ores-unsortedgemore": { + "value": false + }, + "bobmods-plates-batteryupdate": { + "value": true + }, + "bobmods-plates-bluedeuterium": { + "value": false + }, + "bobmods-plates-cheapersteel": { + "value": true + }, + "bobmods-plates-convert-recipes": { + "value": false + }, + "bobmods-plates-expensive-electrolysis": { + "value": false + }, + "bobmods-plates-groundwater": { + "value": false + }, + "bobmods-plates-inventorysize": { + "value": 80 + }, + "bobmods-plates-newsteel": { + "value": true + }, + "bobmods-plates-nuclearupdate": { + "value": true + }, + "bobmods-plates-oreoverride": { + "value": true + }, + "bobmods-plates-purewater": { + "value": true + }, + "bobmods-plates-vanillabarrelling": { + "value": false + }, + "bobmods-power-accumulators": { + "value": true + }, + "bobmods-power-burnergenerator": { + "value": true + }, + "bobmods-power-fluidgenerator": { + "value": true + }, + "bobmods-power-heatsources": { + "value": true + }, + "bobmods-power-nuclear": { + "value": true + }, + "bobmods-power-poles": { + "value": true + }, + "bobmods-power-solar": { + "value": true + }, + "bobmods-power-steam": { + "value": true + }, + "bobmods-revamp-hardmode": { + "value": true + }, + "bobmods-revamp-nuclear": { + "value": true + }, + "bobmods-revamp-oil": { + "value": true + }, + "bobmods-revamp-old-oil": { + "value": true + }, + "bobmods-revamp-rtg": { + "value": true + }, + "bobmods-tech-colorupdate": { + "value": true + }, + "bobmods-vehicleequipment-enablevehiclegrids": { + "value": true + }, + "bobmods-warfare-drainlesslaserturrets": { + "value": false + }, + "bobmods-warfare-robotupdate": { + "value": true + }, + "bobmods-warfare-spidertron-needsfuel": { + "value": "default" + }, + "bobmods-warfare-spidertron-overhaul": { + "value": true + }, + "bobmods-warfare-vehicleflamethrowerstartsfires": { + "value": false + } + } +} \ No newline at end of file diff --git a/src/go.mod b/src/go.mod index 6cefaf56..523c9ea7 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/OpenFactorioServerManager/rcon v0.0.0-20120923215419-8fbb8268b60a github.com/go-ini/ini v1.49.0 + github.com/go-test/deep v1.0.7 github.com/golang/protobuf v1.3.1 // indirect github.com/gorilla/mux v1.7.3 github.com/gorilla/securecookie v1.1.1 diff --git a/src/go.sum b/src/go.sum index 75460a0e..d85a6364 100644 --- a/src/go.sum +++ b/src/go.sum @@ -6,7 +6,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-ini/ini v1.49.0 h1:ymWFBUkwN3JFPjvjcJJ5TSTwh84M66QrH+8vOytLgRY= github.com/go-ini/ini v1.49.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -57,17 +58,14 @@ golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIo golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/ui/App/components/ModConfigContent.jsx b/ui/App/components/ModConfigContent.jsx new file mode 100644 index 00000000..b5e1458b --- /dev/null +++ b/ui/App/components/ModConfigContent.jsx @@ -0,0 +1,221 @@ +import React from "react"; +import {Link} from "react-router-dom"; +import FontAwesomeIcon from "./FontAwesomeIcon"; +import {ReactSwalDanger} from "../../js/customSwal"; + +export default class ModConfigContent extends React.Component { + constructor(props) { + super(props); + + this.updateConfig = this.updateConfig.bind(this); + + this.state = { + config: null + }; + } + + componentDidMount() { + this.loadConfig(); + } + + loadConfig() { + $.ajax({ + url: "/api/mods/settings", + type: "GET", + dataType: "json", + success: (data) => { + if(data.success) { + this.setState({ + config: data.data + }); + } else { + ReactSwalDanger.fire({ + title: "Loading mod-settings failed", + text: data.data, + type: "error" + }); + } + }, + error: (xhr, status, err) => { + console.log('api/server/status', status, err.toString()); + let json_data = JSON.parse(jqXHR.responseJSON.data); + + ReactSwalNormal.fire({ + title: json_data.detail, + type: "error" + }); + } + }); + } + + updateConfig(e) { + let config = this.state.config; + let configElem = $('.mod-config'); + + $.each(configElem, (_, v) => { + let configName = v.id; + let configValue = v.value; + let category = v.getAttribute("data-cat"); + let type = v.getAttribute("data-type"); + + switch (type) { + case "boolean": + configValue = configValue == "true" ? true : false; + break; + case "number": + configValue = parseFloat(configValue); + break; + } + + if (!config[category]) { + config[category] = []; + } + if (!config[category][configName]) { + config[category][configName] = {}; + } + config[category][configName].value = configValue; + }); + + let configString = JSON.stringify(config); + $.ajax({ + url: "/api/mods/settings", + type: "POST", + data: { + data: configString + }, + dataType: "json", + success: (data) => { + if(data.success) { + this.setState({ + config: config + }); + } else { + ReactSwalDanger.fire({ + title: "Loading mod-settings failed", + text: data.data, + type: "error" + }); + } + }, + error: (xhr, status, err) => { + console.log('api/server/status', status, err.toString()); + let json_data = JSON.parse(jqXHR.responseJSON.data); + + ReactSwalNormal.fire({ + title: json_data.detail, + type: "error" + }); + } + }); + } + + render() { + let categories = []; + if(this.state.config) { + for(let confCat in this.state.config) { + let singles = []; + + for(let confSingle in this.state.config[confCat]) { + let singleType = typeof this.state.config[confCat][confSingle].value; + let input; + switch (singleType) { + case "boolean": + input = + break; + case "number": + input = + break; + case "string": + input = + break; + default: + input = + break; + } + + singles.push(