mirror of
https://github.com/Luzifer/sii.git
synced 2024-12-20 16:11:17 +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 {
|
||||
SaveName string `sii:"name"`
|
||||
Time int64 `sii:"time"`
|
||||
FileTime uint64 `sii:"file_time"`
|
||||
FileTime int64 `sii:"file_time"`
|
||||
Version int `sii:"version"`
|
||||
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, `*/`):
|
||||
inComment = false
|
||||
|
||||
case strings.HasPrefix(line, `@include`):
|
||||
return nil, errors.New("File uses includes (unsupported feature)")
|
||||
|
||||
default:
|
||||
if inComment {
|
||||
// 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()) {
|
||||
err = block.(Unmarshaler).UnmarshalSII(blockContent)
|
||||
} else {
|
||||
// TODO: Add generic unmarshal
|
||||
err = genericUnmarshal(blockContent, block)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -7,16 +7,8 @@ import (
|
|||
|
||||
var testSii = `SiiNunit
|
||||
{
|
||||
/*
|
||||
* Multi-line comment
|
||||
*/
|
||||
some_unit : .my_mod.unit
|
||||
{
|
||||
// Single line comment
|
||||
# Single line comment
|
||||
/*
|
||||
* In-Block multi-line string
|
||||
*/
|
||||
attribute_number: 40
|
||||
attribute_string: "TEST STRING"
|
||||
attribute_unit: test.unit
|
||||
|
|
Loading…
Reference in a new issue