diff --git a/block_company.go b/block_company.go index a08a4a4..effa430 100644 --- a/block_company.go +++ b/block_company.go @@ -11,7 +11,7 @@ type Company struct { JobOffer []Ptr `sii:"job_offer"` CargoOfferSeeds []int64 `sii:"cargo_offer_seeds"` Discovered bool `sii:"discovered"` - ReservedTrailerSlot int64 `sii:"reserved_trailer_slot"` // TODO: Maybe wrong type, haven't seen other than "nil" + ReservedTrailerSlot Ptr `sii:"reserved_trailer_slot"` // TODO: Maybe wrong type, haven't seen other than "nil" blockName string } diff --git a/block_job_offer_data.go b/block_job_offer_data.go index 34953bb..9aed841 100644 --- a/block_job_offer_data.go +++ b/block_job_offer_data.go @@ -5,19 +5,19 @@ func init() { } type JobOfferData struct { - Target string `sii:"target"` - ExpirationTime int64 `sii:"expiration_time"` - Urgency int64 `sii:"urgency"` - ShortestDistanceKM int64 `sii:"shortest_distance_km"` - FerryTime int64 `sii:"ferry_time"` - FerryPrice int64 `sii:"ferry_price"` - Cargo Ptr `sii:"cargo"` // External pointer - CompanyTruck Ptr `sii:"company_truck"` // Partial external pointer? - TrailerVariant Ptr `sii:"trailer_variant"` // External pointer - TrailerDefinition Ptr `sii:"trailer_definition"` // External pointer - UnitsCount int64 `sii:"units_count"` - FillRatio float32 `sii:"fill_ratio"` - TrailerPlace int64 `sii:"trailer_place"` // TODO: What's this? Seems to be "0" in all jobs + Target string `sii:"target"` + ExpirationTime *int64 `sii:"expiration_time"` + Urgency *int64 `sii:"urgency"` + ShortestDistanceKM int64 `sii:"shortest_distance_km"` + FerryTime int64 `sii:"ferry_time"` + FerryPrice int64 `sii:"ferry_price"` + Cargo Ptr `sii:"cargo"` // External pointer + CompanyTruck Ptr `sii:"company_truck"` // Partial external pointer? + TrailerVariant Ptr `sii:"trailer_variant"` // External pointer + TrailerDefinition Ptr `sii:"trailer_definition"` // External pointer + UnitsCount int64 `sii:"units_count"` + FillRatio float32 `sii:"fill_ratio"` + TrailerPlace []Placement `sii:"trailer_place"` blockName string } diff --git a/block_player.go b/block_player.go index 69092d6..1e5b674 100644 --- a/block_player.go +++ b/block_player.go @@ -5,7 +5,7 @@ func init() { } type Player struct { - HQCity string `sii:"hq_city"` + HQCity Ptr `sii:"hq_city"` Trailers []Ptr `sii:"trailers"` TrailerUtilizationLogs []Ptr `sii:"trailer_utilization_logs"` TrailerDefs []Ptr `sii:"trailer_defs"` diff --git a/datatypes.go b/datatypes.go index d6fc7a9..2df3f60 100644 --- a/datatypes.go +++ b/datatypes.go @@ -21,24 +21,24 @@ var placementRegexp = regexp.MustCompile(`^\(([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0- type Placement [7]float32 func (p Placement) MarshalSII() ([]byte, error) { - var siiFloats = [][]byte{} + var siiFloats = make([][]byte, 7) - for _, f := range p { + for i, f := range p { b, err := float2sii(f) if err != nil { return nil, errors.Wrap(err, "Unable to encode float") } - siiFloats = append(siiFloats, b) + siiFloats[i] = b } var buf = new(bytes.Buffer) buf.Write([]byte("(")) - bytes.Join(siiFloats[0:3], []byte(", ")) + buf.Write(bytes.Join(siiFloats[0:3], []byte(", "))) buf.Write([]byte(") (")) buf.Write(siiFloats[3]) buf.Write([]byte("; ")) - bytes.Join(siiFloats[4:7], []byte(", ")) + buf.Write(bytes.Join(siiFloats[4:7], []byte(", "))) buf.Write([]byte(")")) return buf.Bytes(), nil diff --git a/generator.go b/generator.go new file mode 100644 index 0000000..e8d4138 --- /dev/null +++ b/generator.go @@ -0,0 +1,59 @@ +package sii + +import ( + "bytes" + "fmt" + "io" + "reflect" + + "github.com/pkg/errors" +) + +func writeSIIPlainFile(unit *Unit, w io.Writer) error { + var err error + + // Write file header + if _, err = fmt.Fprintln(w, "SiiNunit\n{"); err != nil { + return errors.Wrap(err, "Unable to write header") + } + + for _, block := range unit.Entries { + // Write block header + if _, err = fmt.Fprintf(w, "%s : %s {\n", block.Class(), block.Name()); err != nil { + return errors.Wrap(err, "Unable to write block header") + } + + // Obtain and write block content + var raw []byte + + if reflect.TypeOf(block).Elem().Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) { + raw, err = block.(Marshaler).MarshalSII() + } else { + raw, err = genericMarshal(block) + } + + if err != nil { + return errors.Wrap(err, "Unable to marshal block") + } + + if len(raw) > 0 { + raw = append(bytes.TrimRight(raw, "\n"), '\n') + } + + if _, err = w.Write(raw); err != nil { + return errors.Wrap(err, "Unable to write block data") + } + + // Close block + if _, err = fmt.Fprintf(w, "}\n\n"); err != nil { + return errors.Wrap(err, "Unable to close block") + } + } + + // Write file footer + if _, err = fmt.Fprintln(w, "}"); err != nil { + return errors.Wrap(err, "Unable to write footer") + } + + return nil +} diff --git a/genericMarshaller.go b/genericMarshaller.go index f109b8d..995ae3f 100644 --- a/genericMarshaller.go +++ b/genericMarshaller.go @@ -1,36 +1,26 @@ package sii import ( - "bufio" "bytes" "fmt" "reflect" - "regexp" "strconv" - "strings" "github.com/pkg/errors" ) -var ( - singleLineValue = `^\s*%s\s?:\s?(.+)$` - arrayLineValue = `^\s*%s(\[([0-9]*)\])?\s?:\s?(.+)$` -) - func genericMarshal(in interface{}) ([]byte, error) { - return nil, errors.New("Not implemented") -} - -func genericUnmarshal(in []byte, out interface{}, unit *Unit) error { - if reflect.TypeOf(out).Kind() != reflect.Ptr { - return errors.New("Calling parser with non-pointer") + if reflect.TypeOf(in).Kind() == reflect.Ptr { + in = reflect.ValueOf(in).Elem().Interface() } - if reflect.ValueOf(out).Elem().Kind() != reflect.Struct { - return errors.New("Calling parser with pointer to non-struct") + if reflect.TypeOf(in).Kind() != reflect.Struct { + return nil, errors.New("Calling marshaller with non-struct") } - st := reflect.ValueOf(out).Elem() + var buf = new(bytes.Buffer) + + st := reflect.ValueOf(in) for i := 0; i < st.NumField(); i++ { valField := st.Field(i) typeField := st.Type().Field(i) @@ -44,208 +34,146 @@ func genericUnmarshal(in []byte, out interface{}, unit *Unit) error { switch typeField.Type { case reflect.TypeOf(Ptr{}): - data := getSingleValue(in, attributeName) - v := Ptr{unit: unit} - if err := v.UnmarshalSII(data); err != nil { - return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName) - } - valField.Set(reflect.ValueOf(v)) + v := valField.Interface().(Ptr).MarshalSII() + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v)) continue case reflect.TypeOf(Placement{}): - data := getSingleValue(in, attributeName) - v := Placement{} - if err := v.UnmarshalSII(data); err != nil { - return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName) + v, err := valField.Interface().(Placement).MarshalSII() + if err != nil { + return nil, errors.Wrap(err, "Unable to encode Placement") } - valField.Set(reflect.ValueOf(v)) + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v)) continue } switch typeField.Type.Kind() { + case reflect.Bool: - v, err := strconv.ParseBool(string(getSingleValue(in, attributeName))) - if err != nil { - return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName) - } - valField.SetBool(v) + v := valField.Bool() + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, strconv.FormatBool(v))) case reflect.Float32: - v, err := sii2float(getSingleValue(in, attributeName)) + v, err := float2sii(float32(valField.Float())) if err != nil { - return errors.Wrapf(err, "Unable to parse float for attribute %q", attributeName) + return nil, errors.Wrap(err, "Unable to encode float32") } - valField.Set(reflect.ValueOf(v)) + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v)) case reflect.Int, reflect.Int64: - bv := getSingleValue(in, attributeName) - if isNilValue(bv) { - continue - } - v, err := strconv.ParseInt(string(bv), 10, 64) - if err != nil { - return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) - } - valField.SetInt(v) + buf.WriteString(fmt.Sprintf(" %s: %d\n", attributeName, valField.Int())) case reflect.String: - v := strings.Trim(string(getSingleValue(in, attributeName)), `"`) - valField.SetString(v) + buf.WriteString(fmt.Sprintf(" %s: %q\n", attributeName, valField.String())) case reflect.Uint64: - v, err := strconv.ParseUint(string(getSingleValue(in, attributeName)), 10, 64) - if err != nil { - return errors.Wrapf(err, "Unable to parse uint for attribute %q", attributeName) + v := strconv.FormatUint(valField.Uint(), 10) + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v)) + + case reflect.Ptr: + + switch typeField.Type.Elem().Kind() { + + case reflect.Int64: + var v string + if valField.IsNil() { + v = "nil" + } else { + v = strconv.FormatInt(valField.Elem().Int(), 10) + } + buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v)) + + default: + return nil, errors.Errorf("Unsupported type: *%s", typeField.Type.Elem().Kind()) + } - valField.SetUint(v) case reflect.Slice: - ba, err := getArrayValues(in, attributeName) - if err != nil { - return errors.Wrapf(err, "Unable to fetch array values for attribute %q", attributeName) - } + var values []string switch typeField.Type.Elem() { case reflect.TypeOf(Ptr{}): - var v []Ptr - for _, bv := range ba { - e := Ptr{unit: unit} - if err := e.UnmarshalSII(bv); err != nil { - return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName) - } - v = append(v, e) + for _, val := range valField.Interface().([]Ptr) { + values = append(values, string(val.MarshalSII())) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) continue case reflect.TypeOf(Placement{}): - var v []Placement - for _, bv := range ba { - e := Placement{} - if err := e.UnmarshalSII(bv); err != nil { - return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName) + for _, val := range valField.Interface().([]Placement) { + ev, err := val.MarshalSII() + if err != nil { + return nil, errors.Wrap(err, "Unable to encode Placement") } - v = append(v, e) + values = append(values, string(ev)) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) continue case reflect.TypeOf(RawValue{}): - var v []RawValue - for _, bv := range ba { - e := RawValue{} - if err := e.UnmarshalSII(bv); err != nil { - return errors.Wrapf(err, "Unable to parse RawValue for attribute %q", attributeName) + for _, val := range valField.Interface().([]RawValue) { + ev, err := val.MarshalSII() + if err != nil { + return nil, errors.Wrap(err, "Unable to encode RawValue") } - v = append(v, e) + values = append(values, string(ev)) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) continue } switch typeField.Type.Elem().Kind() { + case reflect.Bool: - var v []bool - for _, bv := range ba { - pbv, err := strconv.ParseBool(string(bv)) - if err != nil { - return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName) - } - v = append(v, pbv) + for _, val := range valField.Interface().([]bool) { + values = append(values, strconv.FormatBool(val)) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) case reflect.Int: - var v []int - for _, bv := range ba { - pbv, err := strconv.Atoi(string(bv)) - if err != nil { - return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) - } - v = append(v, pbv) + for _, val := range valField.Interface().([]int) { + values = append(values, strconv.FormatInt(int64(val), 10)) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) case reflect.Int64: - var v []int64 - for _, bv := range ba { - pbv, err := strconv.ParseInt(string(bv), 10, 64) - if err != nil { - return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) - } - v = append(v, pbv) + for _, val := range valField.Interface().([]int64) { + values = append(values, strconv.FormatInt(val, 10)) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) case reflect.String: - var v []string - for _, bv := range ba { - v = append(v, strings.Trim(string(bv), `"`)) + for _, val := range valField.Interface().([]string) { + values = append(values, val) } - valField.Set(reflect.ValueOf(v)) + buf.Write(encodeSliceValue(attributeName, values)) default: - return errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind()) + return nil, errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind()) + } default: - return errors.Errorf("Unsupported type: %s", typeField.Type.Kind()) + return nil, errors.Errorf("Unsupported type: %s", typeField.Type.Kind()) + } + } - return nil + return buf.Bytes(), nil } -func getSingleValue(in []byte, name string) []byte { - rex := regexp.MustCompile(fmt.Sprintf(singleLineValue, name)) +func encodeSliceValue(attributeName string, values []string) []byte { + var buf = new(bytes.Buffer) - var scanner = bufio.NewScanner(bytes.NewReader(in)) - for scanner.Scan() { - if rex.Match(scanner.Bytes()) { - grp := rex.FindSubmatch(scanner.Bytes()) - return grp[1] - } - } - return nil -} + fmt.Fprintf(buf, " %s: %d\n", attributeName, len(values)) -func getArrayValues(in []byte, name string) ([][]byte, error) { - rex := regexp.MustCompile(fmt.Sprintf(arrayLineValue, name)) - var out [][]byte - - var scanner = bufio.NewScanner(bytes.NewReader(in)) - for scanner.Scan() { - if rex.Match(scanner.Bytes()) { - grp := rex.FindSubmatch(scanner.Bytes()) - if len(grp[1]) == 0 { - arrayLen, err := strconv.Atoi(string(grp[3])) - if err != nil { - return nil, errors.Wrap(err, "Unable to parse array capacity") - } - out = make([][]byte, arrayLen) - continue - } - - if len(grp[2]) == 0 { - out = append(out, grp[3]) - continue - } - - idx, err := strconv.Atoi(string(grp[2])) - if err != nil { - return nil, errors.Wrap(err, "Unable to parse array index") - } - - out[idx] = grp[3] - } + for i, v := range values { + fmt.Fprintf(buf, " %s[%d]: %s\n", attributeName, i, v) } - return out, nil -} - -func isNilValue(in []byte) bool { - return reflect.DeepEqual(in, []byte("nil")) + return buf.Bytes() } diff --git a/genericUnmarshaller.go b/genericUnmarshaller.go new file mode 100644 index 0000000..14d5302 --- /dev/null +++ b/genericUnmarshaller.go @@ -0,0 +1,270 @@ +package sii + +import ( + "bufio" + "bytes" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +var ( + singleLineValue = `^\s*%s\s?:\s?(.+)$` + arrayLineValue = `^\s*%s(\[([0-9]*)\])?\s?:\s?(.+)$` +) + +func genericUnmarshal(in []byte, out interface{}, unit *Unit) error { + if reflect.TypeOf(out).Kind() != reflect.Ptr { + return errors.New("Calling parser with non-pointer") + } + + if reflect.ValueOf(out).Elem().Kind() != reflect.Struct { + return errors.New("Calling parser with pointer to non-struct") + } + + st := reflect.ValueOf(out).Elem() + for i := 0; i < st.NumField(); i++ { + valField := st.Field(i) + typeField := st.Type().Field(i) + + attributeName := typeField.Tag.Get("sii") + if attributeName == "" { + // Names must be explicitly defined, library does not support name guessing + continue + } + + switch typeField.Type { + + case reflect.TypeOf(Ptr{}): + data := getSingleValue(in, attributeName) + v := Ptr{unit: unit} + if err := v.UnmarshalSII(data); err != nil { + return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName) + } + valField.Set(reflect.ValueOf(v)) + continue + + case reflect.TypeOf(Placement{}): + data := getSingleValue(in, attributeName) + v := Placement{} + if err := v.UnmarshalSII(data); err != nil { + return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName) + } + valField.Set(reflect.ValueOf(v)) + continue + + } + + switch typeField.Type.Kind() { + case reflect.Bool: + v, err := strconv.ParseBool(string(getSingleValue(in, attributeName))) + if err != nil { + return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName) + } + valField.SetBool(v) + + case reflect.Float32: + v, err := sii2float(getSingleValue(in, attributeName)) + if err != nil { + return errors.Wrapf(err, "Unable to parse float for attribute %q", attributeName) + } + valField.Set(reflect.ValueOf(v)) + + case reflect.Int, reflect.Int64: + bv := getSingleValue(in, attributeName) + if isNilValue(bv) { + continue + } + v, err := strconv.ParseInt(string(bv), 10, 64) + if err != nil { + return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) + } + valField.SetInt(v) + + case reflect.String: + v := strings.Trim(string(getSingleValue(in, attributeName)), `"`) + valField.SetString(v) + + case reflect.Uint64: + v, err := strconv.ParseUint(string(getSingleValue(in, attributeName)), 10, 64) + if err != nil { + return errors.Wrapf(err, "Unable to parse uint for attribute %q", attributeName) + } + valField.SetUint(v) + + case reflect.Ptr: + + switch typeField.Type.Elem().Kind() { + + case reflect.Int64: + bv := getSingleValue(in, attributeName) + if !isNilValue(bv) { + v, err := strconv.ParseInt(string(bv), 10, 64) + if err != nil { + return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) + } + valField.Set(reflect.ValueOf(&v)) + } + + default: + return errors.Errorf("Unsupported type: *%s", typeField.Type.Elem().Kind()) + + } + + case reflect.Slice: + ba, err := getArrayValues(in, attributeName) + if err != nil { + return errors.Wrapf(err, "Unable to fetch array values for attribute %q", attributeName) + } + + switch typeField.Type.Elem() { + + case reflect.TypeOf(Ptr{}): + var v []Ptr + for _, bv := range ba { + e := Ptr{unit: unit} + if err := e.UnmarshalSII(bv); err != nil { + return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName) + } + v = append(v, e) + } + valField.Set(reflect.ValueOf(v)) + continue + + case reflect.TypeOf(Placement{}): + var v []Placement + for _, bv := range ba { + e := Placement{} + if err := e.UnmarshalSII(bv); err != nil { + return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName) + } + v = append(v, e) + } + valField.Set(reflect.ValueOf(v)) + continue + + case reflect.TypeOf(RawValue{}): + var v []RawValue + for _, bv := range ba { + e := RawValue{} + if err := e.UnmarshalSII(bv); err != nil { + return errors.Wrapf(err, "Unable to parse RawValue for attribute %q", attributeName) + } + v = append(v, e) + } + valField.Set(reflect.ValueOf(v)) + continue + + } + + switch typeField.Type.Elem().Kind() { + case reflect.Bool: + var v []bool + for _, bv := range ba { + pbv, err := strconv.ParseBool(string(bv)) + if err != nil { + return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName) + } + v = append(v, pbv) + } + valField.Set(reflect.ValueOf(v)) + + case reflect.Int: + var v []int + for _, bv := range ba { + pbv, err := strconv.Atoi(string(bv)) + if err != nil { + return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) + } + v = append(v, pbv) + } + valField.Set(reflect.ValueOf(v)) + + case reflect.Int64: + var v []int64 + for _, bv := range ba { + pbv, err := strconv.ParseInt(string(bv), 10, 64) + if err != nil { + return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName) + } + v = append(v, pbv) + } + valField.Set(reflect.ValueOf(v)) + + case reflect.String: + var v []string + for _, bv := range ba { + v = append(v, strings.Trim(string(bv), `"`)) + } + valField.Set(reflect.ValueOf(v)) + + default: + return errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind()) + } + + default: + return errors.Errorf("Unsupported type: %s", typeField.Type.Kind()) + } + } + + return nil +} + +func getSingleValue(in []byte, name string) []byte { + rex := regexp.MustCompile(fmt.Sprintf(singleLineValue, name)) + + var scanner = bufio.NewScanner(bytes.NewReader(in)) + for scanner.Scan() { + if rex.Match(scanner.Bytes()) { + grp := rex.FindSubmatch(scanner.Bytes()) + return grp[1] + } + } + return nil +} + +func getArrayValues(in []byte, name string) ([][]byte, error) { + rex := regexp.MustCompile(fmt.Sprintf(arrayLineValue, name)) + var out [][]byte + + var scanner = bufio.NewScanner(bytes.NewReader(in)) + for scanner.Scan() { + if rex.Match(scanner.Bytes()) { + grp := rex.FindSubmatch(scanner.Bytes()) + + if len(grp[1]) == 0 { + arrayLen, err := strconv.Atoi(string(grp[3])) + if err != nil { + return nil, errors.Wrap(err, "Unable to parse array capacity") + } + out = make([][]byte, arrayLen) + continue + } + + if len(grp[2]) == 0 { + out = append(out, grp[3]) + continue + } + + idx, err := strconv.Atoi(string(grp[2])) + if err != nil { + return nil, errors.Wrap(err, "Unable to parse array index") + } + + out[idx] = make([]byte, len(grp[3])) + for i, b := range grp[3] { + out[idx][i] = b + } + } + } + + return out, errors.Wrap(scanner.Err(), "Unable to parse array lines") +} + +func isNilValue(in []byte) bool { + return reflect.DeepEqual(in, []byte("nil")) +} diff --git a/helpers.go b/helpers.go index aacf5c6..70f5616 100644 --- a/helpers.go +++ b/helpers.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "fmt" + "math" "strconv" "github.com/pkg/errors" @@ -15,6 +17,10 @@ func float2sii(f float32) ([]byte, error) { err error ) + if math.Floor(float64(f)) == float64(f) { + return []byte(fmt.Sprintf("%.0f", f)), nil + } + err = binary.Write(buf, binary.BigEndian, f) if err != nil { return nil, errors.Wrap(err, "Unable to encode float") diff --git a/parser.go b/parser.go index d996fcf..8622828 100644 --- a/parser.go +++ b/parser.go @@ -49,7 +49,7 @@ func parseSIIPlainFile(r io.Reader) (*Unit, error) { case line == "}": if inBlock { - if err := processBlock(unit, blockClass, blockName, blockContent); err != nil { + if err := processBlock(unit, blockClass, blockName, bytes.Trim(blockContent, "\n")); err != nil { return nil, errors.Wrap(err, "Unable to process block") } diff --git a/sii.go b/sii.go index 6aa4b41..c178fb9 100644 --- a/sii.go +++ b/sii.go @@ -117,8 +117,23 @@ func WriteUnitFile(filename string, encrypt bool, data *Unit) error { return ErrNoEncryptionKeySet } - // FIXME: Implement this - return errors.New("Not implemented") + var buf = new(bytes.Buffer) + + if err := writeSIIPlainFile(data, buf); err != nil { + return errors.Wrap(err, "Unable to create SII file content") + } + + // FIXME: Implement encryption + + // Create output file + f, err := os.Create(filename) + if err != nil { + return errors.Wrap(err, "Unable to open output file") + } + defer f.Close() + + _, err = buf.WriteTo(f) + return errors.Wrap(err, "Unable to write buffer") } func readFTHeader(f io.ReaderAt) ([]byte, error) { diff --git a/unit.go b/unit.go index d5cbfca..3230594 100644 --- a/unit.go +++ b/unit.go @@ -25,9 +25,9 @@ func (r *RawBlock) Init(class, name string) { r.blockClass = class r.blockName = name } -func (r RawBlock) MarshalSII() []byte { return r.Data } -func (r RawBlock) Name() string { return r.blockName } -func (r RawBlock) Class() string { return r.blockClass } +func (r RawBlock) MarshalSII() ([]byte, error) { return r.Data, nil } +func (r RawBlock) Name() string { return r.blockName } +func (r RawBlock) Class() string { return r.blockClass } func (r *RawBlock) UnmarshalSII(in []byte) error { r.Data = in return nil