1
0
Fork 0
mirror of https://github.com/Luzifer/sii.git synced 2024-12-21 00:21:15 +00:00
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2019-11-05 23:35:45 +01:00
parent 995cb1dafa
commit 5aa424fa3d
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
6 changed files with 299 additions and 10 deletions

52
block_player.go Normal file
View file

@ -0,0 +1,52 @@
package sii
func init() {
RegisterBlock(&Player{})
}
type Player struct {
HQCity string `sii:"hq_city"`
Trailers []Ptr `sii:"trailers"`
TrailerUtilizationLogs []Ptr `sii:"trailer_utilization_logs"`
TrailerDefs []Ptr `sii:"trailer_defs"`
AssignedTruck Ptr `sii:"assigned_truck"`
MyTruck Ptr `sii:"my_truck"`
MyTruckPlacement Placement `sii:"my_truck_placement"`
MyTruckPlacementValid bool `sii:"my_truck_placement_valid"`
MyTrailerPlacement Placement `sii:"my_trailer_placement"`
MySlaveTrailerPlacements []Placement `sii:"my_slave_trailer_placements"`
MyTrailerAttached bool `sii:"my_trailer_attached"`
MyTrailerUsed bool `sii:"my_trailer_used"`
AssignedTrailer Ptr `sii:"assigned_trailer"`
MyTrailer Ptr `sii:"my_trailer"`
AssignedTrailerConnected bool `sii:"assigned_trailer_connected"`
TruckPlacement Placement `sii:"truck_placement"`
TrailerPlacement Placement `sii:"trailer_placement"`
SlaveTrailerPlacements []Placement `sii:"slave_trailer_placements"`
ScheduleTransferToHQ bool `sii:"schedule_transfer_to_hq"`
Flags uint64 `sii:"flags"` // ????
GasPumpMoneyDebt int64 `sii:"gas_pump_money_debt"`
CurrentJob Ptr `sii:"current_job"`
CurrentBusJob Ptr `sii:"current_bus_job"`
SelectedJob Ptr `sii:"selected_job"`
DrivingTime int64 `sii:"driving_time"`
SleepingCount int `sii:"sleeping_count"`
FreeRoamDistance int64 `sii:"free_roam_distance"`
DiscoveryDistance float64 `sii:"discovary_distance"` // Typo is intended and copied from real save-game
DismissedDrivers int `sii:"dismissed_drivers"`
Trucks []Ptr `sii:"trucks"`
TruckProfitLogs []Ptr `sii:"truck_profit_logs"`
Drivers []Ptr `sii:"drivers"`
DriverReadinessTimer []int64 `sii:"driver_readiness_timer"`
DriverQuitWarned []bool `sii:"driver_quit_warned"`
blockName string
}
func (Player) Class() string { return "player" }
func (p *Player) Init(class, name string) {
p.blockName = name
}
func (p Player) Name() string { return p.blockName }

View file

@ -7,7 +7,7 @@ func init() {
type SaveContainer struct { type SaveContainer struct {
SaveName string `sii:"name"` SaveName string `sii:"name"`
Time int64 `sii:"time"` Time int64 `sii:"time"`
FileTime uint64 `sii:"file_time"` FileTime int64 `sii:"file_time"`
Version int `sii:"version"` Version int `sii:"version"`
Dependencies []string `sii:"dependencies"` Dependencies []string `sii:"dependencies"`

60
datatypes.go Normal file
View file

@ -0,0 +1,60 @@
package sii
import "strings"
// See https://modding.scssoft.com/wiki/Documentation/Engine/Units
// string => native type string
// float => native type float
// float2-4 => [2]float - [4]float
type Placement struct {
X, Y, Z float64
W, X2, Y2, Z2 float64
}
// TODO: Implement marshalling for Placement
// fixed => native type int
// fixed2-4 => [2]int - [4]int
// int2 => [2]int
// quaternion => [4]float
// s16, s32, s64 => int16, int32, int64
// u16, u32, u64 => uint16, uint32, uint64
// bool => native type bool
// token => native type string
type Ptr struct {
Target string
unit *Unit
}
func (p Ptr) CanResolve() bool { return strings.HasPrefix(p.Target, ".") }
func (p Ptr) MarshalSII() []byte { return []byte(p.Target) }
func (p Ptr) Resolve() Block {
if p.Target == "null" {
return nil
}
for _, b := range p.unit.Entries {
if b.Name() == p.Target {
return b
}
}
return nil
}
func (p *Ptr) UnmarshalSII(in []byte) error {
p.Target = string(in)
return nil
}
// resource_tie => native type string

182
genericMarshaller.go Normal file
View file

@ -0,0 +1,182 @@
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{}) 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{}):
// TODO: Implement
continue
case reflect.TypeOf(Placement{}):
// TODO: Implement
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.Float64:
v, err := strconv.ParseFloat(string(getSingleValue(in, attributeName)), 64)
if err != nil {
return errors.Wrapf(err, "Unable to parse float for attribute %q", attributeName)
}
valField.SetFloat(v)
case reflect.Int, reflect.Int64:
v, err := strconv.ParseInt(string(getSingleValue(in, attributeName)), 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.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().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.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] = grp[3]
}
}
return out, nil
}

View file

@ -89,6 +89,9 @@ func parseSIIPlainFile(r io.Reader) (*Unit, error) {
case strings.HasSuffix(line, `*/`): case strings.HasSuffix(line, `*/`):
inComment = false inComment = false
case strings.HasPrefix(line, `@include`):
return nil, errors.New("File uses includes (unsupported feature)")
default: default:
if inComment { if inComment {
// Inside multi-line-comment, just drop // Inside multi-line-comment, just drop
@ -125,7 +128,7 @@ func processBlock(unit *Unit, blockClass, blockName string, blockContent []byte)
if reflect.TypeOf(block).Implements(reflect.TypeOf((*Unmarshaler)(nil)).Elem()) { if reflect.TypeOf(block).Implements(reflect.TypeOf((*Unmarshaler)(nil)).Elem()) {
err = block.(Unmarshaler).UnmarshalSII(blockContent) err = block.(Unmarshaler).UnmarshalSII(blockContent)
} else { } else {
// TODO: Add generic unmarshal err = genericUnmarshal(blockContent, block)
} }
if err != nil { if err != nil {

View file

@ -7,16 +7,8 @@ import (
var testSii = `SiiNunit var testSii = `SiiNunit
{ {
/*
* Multi-line comment
*/
some_unit : .my_mod.unit some_unit : .my_mod.unit
{ {
// Single line comment
# Single line comment
/*
* In-Block multi-line string
*/
attribute_number: 40 attribute_number: 40
attribute_string: "TEST STRING" attribute_string: "TEST STRING"
attribute_unit: test.unit attribute_unit: test.unit