mirror of
https://github.com/Luzifer/sii.git
synced 2024-12-21 00:21:15 +00:00
WIP
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
995cb1dafa
commit
5aa424fa3d
6 changed files with 299 additions and 10 deletions
52
block_player.go
Normal file
52
block_player.go
Normal 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 }
|
|
@ -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
60
datatypes.go
Normal 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
182
genericMarshaller.go
Normal 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
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue