Extract scanning ASCII files into a util package

This commit is contained in:
2018-03-18 05:04:46 +00:00
parent 4176ccd94f
commit 10e22d8428
5 changed files with 131 additions and 100 deletions

View File

@@ -3,6 +3,8 @@ package data
import (
"bytes"
"errors"
"ur.gs/chaos-gate/internal/util/asciiscan"
)
// Comments are `//`, start of line only, for accounting
@@ -13,11 +15,14 @@ var (
type Accounting map[string]string
func LoadAccounting(filename string) (Accounting, error) {
scanLines, err := fileToScanner(filename)
s, err := asciiscan.New(filename)
if err != nil {
return nil, err
}
defer s.Close()
scanLines := s.Bufio()
out := make(Accounting)
for scanLines.Scan() {

View File

@@ -1,12 +1,12 @@
package data
import (
"bufio"
"errors"
// "fmt"
"io"
"strconv"
"strings"
"ur.gs/chaos-gate/internal/util/asciiscan"
)
type Cell struct {
@@ -35,13 +35,15 @@ type AnimatedObject struct {
func LoadAnimatedObjectDefinitions(filename string) ([]AnimatedObject, error) {
var out []AnimatedObject
scanLines, err := fileToScanner(filename)
s, err := asciiscan.New(filename)
if err != nil {
return nil, err
}
defer s.Close()
for {
obj, err := consumeAnimatedObjectDefinition(scanLines)
obj, err := consumeAnimatedObjectDefinition(s)
if err == io.EOF {
return out, 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 err error
@@ -61,8 +63,7 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
return out, err
}
if err := consumeIntPtrs(
scanner,
if err := scanner.ConsumeIntPtrs(
&out.Direction, &out.Type, &out.AnimationID, &out.AnimationGroup, &out.AnimatedType,
); err != nil {
return out, err
@@ -89,8 +90,7 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
return out, err
}
if err := consumeIntPtrs(
scanner,
if err := scanner.ConsumeIntPtrs(
&out.Visibility, &out.Protection, &out.MinDelay, &out.DelayRange,
); err != nil {
return out, err
@@ -99,9 +99,9 @@ func consumeAnimatedObjectDefinition(scanner *bufio.Scanner) (AnimatedObject, er
return out, nil
}
func consumeCell(scanner *bufio.Scanner) (Cell, error) {
func consumeCell(scanner *asciiscan.Scanner) (Cell, error) {
out := Cell{}
str, err := consumeString(scanner)
str, err := scanner.ConsumeString()
if err != nil {
return out, err
}

View File

@@ -1,15 +1,9 @@
package data
import (
"bufio"
"bytes"
"io"
"io/ioutil"
"strconv"
"ur.gs/chaos-gate/internal/util/asciiscan"
)
var hashComment = []byte("#")
type CompassPoints struct {
N int
NE int
@@ -26,80 +20,17 @@ type Range struct {
End int
}
func fileToScanner(filename string) (*bufio.Scanner, 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) {
func consumeRange(scanner *asciiscan.Scanner) (Range, error) {
var out Range
err := consumeIntPtrs(scanner, &out.Start, &out.End)
err := scanner.ConsumeIntPtrs(&out.Start, &out.End)
return out, err
}
func consumeCompassPoints(scanner *bufio.Scanner) (CompassPoints, error) {
func consumeCompassPoints(scanner *asciiscan.Scanner) (CompassPoints, error) {
var out CompassPoints
err := consumeIntPtrs(
scanner,
err := scanner.ConsumeIntPtrs(
&out.N, &out.NE, &out.E, &out.SE, &out.S, &out.SW, &out.W, &out.NW,
)

View File

@@ -1,5 +1,9 @@
package data
import (
"ur.gs/chaos-gate/internal/util/asciiscan"
)
type ActionPointEnum int
type SettingEnum int
type TransitionFrameEnum int
@@ -205,10 +209,11 @@ type Generic struct {
// TODO: consume these values from the file
func LoadGeneric(filename string) (*Generic, error) {
scanLines, err := fileToScanner(filename)
s, err := asciiscan.New(filename)
if err != nil {
return nil, err
}
defer s.Close()
out := &Generic{
ActionPoints: make(map[ActionPointEnum]int),
@@ -224,7 +229,7 @@ func LoadGeneric(filename string) (*Generic, error) {
// Various action point values, first to last
for ap := ActionPointEnum(0); ap < APMax; ap++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -234,7 +239,7 @@ func LoadGeneric(filename string) (*Generic, error) {
// Other miscellaneous data fields, first to last
for setting := SettingEnum(0); setting < SettingMax; setting++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -244,7 +249,7 @@ func LoadGeneric(filename string) (*Generic, error) {
// Vehicle walk transition frames. Whatever they are.
for tf := TransitionFrameEnum(0); tf < TFMax; tf++ {
val, err := consumeCompassPoints(scanLines)
val, err := consumeCompassPoints(s)
if err != nil {
return nil, err
}
@@ -253,7 +258,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for st := SquadTypeEnum(0); st < SquadMax; st++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -262,7 +267,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for vt := VehicleTypeEnum(0); vt < VehicleMax; vt++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -271,7 +276,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for op := OptionEnum(0); op < OptionMax; op++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -280,7 +285,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for i := 0; i < NumCampaignScenarios; i++ {
val, err := consumeString(scanLines)
val, err := s.ConsumeString()
if err != nil {
return nil, err
}
@@ -289,7 +294,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for i := 0; i < NumCampaignScenarios; i++ {
val, err := consumeString(scanLines)
val, err := s.ConsumeString()
if err != nil {
return nil, err
}
@@ -298,13 +303,13 @@ func LoadGeneric(filename string) (*Generic, error) {
out.CampaignEndMissionWavs = append(out.CampaignEndMissionWavs, val)
}
out.AICoherentForce, err = consumeInt(scanLines)
out.AICoherentForce, err = s.ConsumeInt()
if err != nil {
return nil, err
}
for sp := SpellEnum(0); sp < SpellMax; sp++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -313,7 +318,7 @@ func LoadGeneric(filename string) (*Generic, error) {
}
for ai := AIPriorityEnum(0); ai < AIPriorityMax; ai++ {
val, err := consumeInt(scanLines)
val, err := s.ConsumeInt()
if err != nil {
return nil, err
}
@@ -321,13 +326,13 @@ func LoadGeneric(filename string) (*Generic, error) {
out.AIPriority[ai] = val
}
out.AICommandInfiniteLoopCounterWatermark, err = consumeInt(scanLines)
out.AICommandInfiniteLoopCounterWatermark, err = s.ConsumeInt()
if err != nil {
return nil, err
}
for rg := RngRangeEnum(0); rg < RngRangeMax; rg++ {
val, err := consumeRange(scanLines)
val, err := consumeRange(s)
if err != nil {
return nil, err
}

View 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
}