Extract scanning ASCII files into a util package
This commit is contained in:
@@ -3,6 +3,8 @@ package data
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"ur.gs/chaos-gate/internal/util/asciiscan"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Comments are `//`, start of line only, for accounting
|
// Comments are `//`, start of line only, for accounting
|
||||||
@@ -13,11 +15,14 @@ var (
|
|||||||
type Accounting map[string]string
|
type Accounting map[string]string
|
||||||
|
|
||||||
func LoadAccounting(filename string) (Accounting, error) {
|
func LoadAccounting(filename string) (Accounting, error) {
|
||||||
scanLines, err := fileToScanner(filename)
|
s, err := asciiscan.New(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer s.Close()
|
||||||
|
scanLines := s.Bufio()
|
||||||
|
|
||||||
out := make(Accounting)
|
out := make(Accounting)
|
||||||
|
|
||||||
for scanLines.Scan() {
|
for scanLines.Scan() {
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"errors"
|
"errors"
|
||||||
// "fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"ur.gs/chaos-gate/internal/util/asciiscan"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cell struct {
|
type Cell struct {
|
||||||
@@ -35,13 +35,15 @@ type AnimatedObject struct {
|
|||||||
func LoadAnimatedObjectDefinitions(filename string) ([]AnimatedObject, error) {
|
func LoadAnimatedObjectDefinitions(filename string) ([]AnimatedObject, error) {
|
||||||
var out []AnimatedObject
|
var out []AnimatedObject
|
||||||
|
|
||||||
scanLines, err := fileToScanner(filename)
|
s, err := asciiscan.New(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
obj, err := consumeAnimatedObjectDefinition(scanLines)
|
obj, err := consumeAnimatedObjectDefinition(s)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return out, nil
|
return out, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -52,7 +54,7 @@ func LoadAnimatedObjectDefinitions(filename string) ([]AnimatedObject, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, error) {
|
func consumeAnimatedObjectDefinition(scanner *asciiscan.Scanner) (AnimatedObject, error) {
|
||||||
var out AnimatedObject
|
var out AnimatedObject
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -61,8 +63,7 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
|
|||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := consumeIntPtrs(
|
if err := scanner.ConsumeIntPtrs(
|
||||||
scanner,
|
|
||||||
&out.Direction, &out.Type, &out.AnimationID, &out.AnimationGroup, &out.AnimatedType,
|
&out.Direction, &out.Type, &out.AnimationID, &out.AnimationGroup, &out.AnimatedType,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
@@ -89,8 +90,7 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
|
|||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := consumeIntPtrs(
|
if err := scanner.ConsumeIntPtrs(
|
||||||
scanner,
|
|
||||||
&out.Visibility, &out.Protection, &out.MinDelay, &out.DelayRange,
|
&out.Visibility, &out.Protection, &out.MinDelay, &out.DelayRange,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
@@ -99,9 +99,9 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func consumeCell(scanner *bufio.Scanner) (Cell, error) {
|
func consumeCell(scanner *asciiscan.Scanner) (Cell, error) {
|
||||||
out := Cell{}
|
out := Cell{}
|
||||||
str, err := consumeString(scanner)
|
str, err := scanner.ConsumeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,9 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"ur.gs/chaos-gate/internal/util/asciiscan"
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var hashComment = []byte("#")
|
|
||||||
|
|
||||||
type CompassPoints struct {
|
type CompassPoints struct {
|
||||||
N int
|
N int
|
||||||
NE int
|
NE int
|
||||||
@@ -26,80 +20,17 @@ type Range struct {
|
|||||||
End int
|
End int
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileToScanner(filename string) (*bufio.Scanner, error) {
|
func consumeRange(scanner *asciiscan.Scanner) (Range, error) {
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufio.NewScanner(bytes.NewReader(data)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func consumeString(scanner *bufio.Scanner) (string, error) {
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Bytes()
|
|
||||||
|
|
||||||
if len(line) == 0 || line[0] == hashComment[0] { // Most .dat files use # for comments
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
comment := bytes.Index(line, hashComment)
|
|
||||||
if comment > 0 {
|
|
||||||
line = line[0:comment]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes.TrimRight(line, "\r\n\t ")), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := scanner.Err()
|
|
||||||
if err == nil {
|
|
||||||
return "", io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
func consumeInt(scanner *bufio.Scanner) (int, error) {
|
|
||||||
str, err := consumeString(scanner)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return strconv.Atoi(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
func consumeIntPtr(to *int, scanner *bufio.Scanner) error {
|
|
||||||
val, err := consumeInt(scanner)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*to = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func consumeIntPtrs(scanner *bufio.Scanner, ptrs ...*int) error {
|
|
||||||
for _, ptr := range ptrs {
|
|
||||||
if err := consumeIntPtr(ptr, scanner); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func consumeRange(scanner *bufio.Scanner) (Range, error) {
|
|
||||||
var out Range
|
var out Range
|
||||||
|
|
||||||
err := consumeIntPtrs(scanner, &out.Start, &out.End)
|
err := scanner.ConsumeIntPtrs(&out.Start, &out.End)
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func consumeCompassPoints(scanner *bufio.Scanner) (CompassPoints, error) {
|
func consumeCompassPoints(scanner *asciiscan.Scanner) (CompassPoints, error) {
|
||||||
var out CompassPoints
|
var out CompassPoints
|
||||||
|
|
||||||
err := consumeIntPtrs(
|
err := scanner.ConsumeIntPtrs(
|
||||||
scanner,
|
|
||||||
&out.N, &out.NE, &out.E, &out.SE, &out.S, &out.SW, &out.W, &out.NW,
|
&out.N, &out.NE, &out.E, &out.SE, &out.S, &out.SW, &out.W, &out.NW,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ur.gs/chaos-gate/internal/util/asciiscan"
|
||||||
|
)
|
||||||
|
|
||||||
type ActionPointEnum int
|
type ActionPointEnum int
|
||||||
type SettingEnum int
|
type SettingEnum int
|
||||||
type TransitionFrameEnum int
|
type TransitionFrameEnum int
|
||||||
@@ -205,10 +209,11 @@ type Generic struct {
|
|||||||
|
|
||||||
// TODO: consume these values from the file
|
// TODO: consume these values from the file
|
||||||
func LoadGeneric(filename string) (*Generic, error) {
|
func LoadGeneric(filename string) (*Generic, error) {
|
||||||
scanLines, err := fileToScanner(filename)
|
s, err := asciiscan.New(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
out := &Generic{
|
out := &Generic{
|
||||||
ActionPoints: make(map[ActionPointEnum]int),
|
ActionPoints: make(map[ActionPointEnum]int),
|
||||||
@@ -224,7 +229,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
|
|
||||||
// Various action point values, first to last
|
// Various action point values, first to last
|
||||||
for ap := ActionPointEnum(0); ap < APMax; ap++ {
|
for ap := ActionPointEnum(0); ap < APMax; ap++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -234,7 +239,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
|
|
||||||
// Other miscellaneous data fields, first to last
|
// Other miscellaneous data fields, first to last
|
||||||
for setting := SettingEnum(0); setting < SettingMax; setting++ {
|
for setting := SettingEnum(0); setting < SettingMax; setting++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -244,7 +249,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
|
|
||||||
// Vehicle walk transition frames. Whatever they are.
|
// Vehicle walk transition frames. Whatever they are.
|
||||||
for tf := TransitionFrameEnum(0); tf < TFMax; tf++ {
|
for tf := TransitionFrameEnum(0); tf < TFMax; tf++ {
|
||||||
val, err := consumeCompassPoints(scanLines)
|
val, err := consumeCompassPoints(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -253,7 +258,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for st := SquadTypeEnum(0); st < SquadMax; st++ {
|
for st := SquadTypeEnum(0); st < SquadMax; st++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -262,7 +267,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for vt := VehicleTypeEnum(0); vt < VehicleMax; vt++ {
|
for vt := VehicleTypeEnum(0); vt < VehicleMax; vt++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -271,7 +276,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for op := OptionEnum(0); op < OptionMax; op++ {
|
for op := OptionEnum(0); op < OptionMax; op++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -280,7 +285,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < NumCampaignScenarios; i++ {
|
for i := 0; i < NumCampaignScenarios; i++ {
|
||||||
val, err := consumeString(scanLines)
|
val, err := s.ConsumeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -289,7 +294,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < NumCampaignScenarios; i++ {
|
for i := 0; i < NumCampaignScenarios; i++ {
|
||||||
val, err := consumeString(scanLines)
|
val, err := s.ConsumeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -298,13 +303,13 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
out.CampaignEndMissionWavs = append(out.CampaignEndMissionWavs, val)
|
out.CampaignEndMissionWavs = append(out.CampaignEndMissionWavs, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
out.AICoherentForce, err = consumeInt(scanLines)
|
out.AICoherentForce, err = s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for sp := SpellEnum(0); sp < SpellMax; sp++ {
|
for sp := SpellEnum(0); sp < SpellMax; sp++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -313,7 +318,7 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ai := AIPriorityEnum(0); ai < AIPriorityMax; ai++ {
|
for ai := AIPriorityEnum(0); ai < AIPriorityMax; ai++ {
|
||||||
val, err := consumeInt(scanLines)
|
val, err := s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -321,13 +326,13 @@ func LoadGeneric(filename string) (*Generic, error) {
|
|||||||
out.AIPriority[ai] = val
|
out.AIPriority[ai] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
out.AICommandInfiniteLoopCounterWatermark, err = consumeInt(scanLines)
|
out.AICommandInfiniteLoopCounterWatermark, err = s.ConsumeInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for rg := RngRangeEnum(0); rg < RngRangeMax; rg++ {
|
for rg := RngRangeEnum(0); rg < RngRangeMax; rg++ {
|
||||||
val, err := consumeRange(scanLines)
|
val, err := consumeRange(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
90
internal/util/asciiscan/asciiscan.go
Normal file
90
internal/util/asciiscan/asciiscan.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// package asciiscan is used to parse plaintext files into structured data
|
||||||
|
package asciiscan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hashComment = []byte("#")
|
||||||
|
|
||||||
|
type Scanner struct {
|
||||||
|
bufio *bufio.Scanner
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(filename string) (*Scanner, error) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Scanner{
|
||||||
|
bufio: bufio.NewScanner(f),
|
||||||
|
closer: f,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) Bufio() *bufio.Scanner {
|
||||||
|
return s.bufio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) Close() error {
|
||||||
|
return s.closer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) ConsumeString() (string, error) {
|
||||||
|
for s.bufio.Scan() {
|
||||||
|
line := s.bufio.Bytes()
|
||||||
|
|
||||||
|
if len(line) == 0 || line[0] == hashComment[0] { // Most .dat files use # for comments
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
comment := bytes.Index(line, hashComment)
|
||||||
|
if comment > 0 {
|
||||||
|
line = line[0:comment]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes.TrimRight(line, "\r\n\t ")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.bufio.Err()
|
||||||
|
if err == nil {
|
||||||
|
return "", io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) ConsumeInt() (int, error) {
|
||||||
|
str, err := s.ConsumeString()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Atoi(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) ConsumeIntPtr(to *int) error {
|
||||||
|
val, err := s.ConsumeInt()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*to = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) ConsumeIntPtrs(ptrs ...*int) error {
|
||||||
|
for _, ptr := range ptrs {
|
||||||
|
if err := s.ConsumeIntPtr(ptr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Reference in New Issue
Block a user