Extract scanning ASCII files into a util package
This commit is contained in:
@@ -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() {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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,
|
||||
)
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
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