Make include directives work in .mnu files

This commit is contained in:
2020-04-13 21:03:54 +01:00
parent 76bf8438b0
commit dc131939f4
11 changed files with 298 additions and 119 deletions

View File

@@ -134,6 +134,16 @@ $GenLoad.mni
It looks like we just interpolate the named file into the text when we come It looks like we just interpolate the named file into the text when we come
across one of these lines. across one of these lines.
The `MENUID` in `GenDialog` and `GenLoad` is a 2-element list, like `1000,1`
or `2000,2`. The second number corresponds to the offset in the list of object
files.
## `MENUTYPE`
Here's the full list of
## `SUBMENUTYPE`
## (Sub)menu types ## (Sub)menu types
The types seem to refer to different types of UI widget. Here's a list of unique The types seem to refer to different types of UI widget. Here's a list of unique

View File

@@ -9,7 +9,7 @@ import (
type Menu struct { type Menu struct {
assets *AssetStore assets *AssetStore
fonts []*Font // TODO: place the fonts directly into the relevant records fonts []*Font // TODO: place the fonts directly into the relevant records
obj *Object // TODO: handle multiple objects in the menu objects []*Object // TODO: place the objects directly into the relevant records
raw *menus.Menu // TODO: remove raw raw *menus.Menu // TODO: remove raw
Name string Name string
@@ -25,10 +25,10 @@ func (m *Menu) Font(idx int) *Font {
return m.fonts[idx] return m.fonts[idx]
} }
func (m *Menu) Images(start, count int) ([]*ebiten.Image, error) { func (m *Menu) Images(objIdx, start, count int) ([]*ebiten.Image, error) {
out := make([]*ebiten.Image, count) out := make([]*ebiten.Image, count)
sprites, err := m.Sprites(start, count) sprites, err := m.Sprites(objIdx, start, count)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,11 +40,11 @@ func (m *Menu) Images(start, count int) ([]*ebiten.Image, error) {
return out, nil return out, nil
} }
func (m *Menu) Sprites(start, count int) ([]*Sprite, error) { func (m *Menu) Sprites(objIdx, start, count int) ([]*Sprite, error) {
out := make([]*Sprite, count) out := make([]*Sprite, count)
for i := start; i < start+count; i++ { for i := start; i < start+count; i++ {
sprite, err := m.Sprite(i) sprite, err := m.Sprite(objIdx, i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -55,8 +55,8 @@ func (m *Menu) Sprites(start, count int) ([]*Sprite, error) {
return out, nil return out, nil
} }
func (m *Menu) Sprite(idx int) (*Sprite, error) { func (m *Menu) Sprite(objIdx, idx int) (*Sprite, error) {
return m.obj.Sprite(idx) return m.objects[objIdx].Sprite(idx)
} }
func (a *AssetStore) Menu(name string) (*Menu, error) { func (a *AssetStore) Menu(name string) (*Menu, error) {
@@ -93,7 +93,9 @@ func (a *AssetStore) Menu(name string) (*Menu, error) {
raw.Internationalize(i18n) raw.Internationalize(i18n)
obj, err := a.loadMenuObject(raw) // TODO: multiple objects // FIXME: we should parse the menu into a list of elements like "ListBox",
// "Dialogue", etc, and present those with objects already selected
objects, err := a.loadMenuObjects(raw)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -101,7 +103,7 @@ func (a *AssetStore) Menu(name string) (*Menu, error) {
menu := &Menu{ menu := &Menu{
assets: a, assets: a,
fonts: fonts, fonts: fonts,
obj: obj, objects: objects,
raw: raw, raw: raw,
Name: name, Name: name,
} }
@@ -110,12 +112,21 @@ func (a *AssetStore) Menu(name string) (*Menu, error) {
return menu, nil return menu, nil
} }
func (a *AssetStore) loadMenuObject(menu *menus.Menu) (*Object, error) { func (a *AssetStore) loadMenuObjects(menu *menus.Menu) ([]*Object, error) {
filename := menu.ObjectFiles[0] out := make([]*Object, len(menu.ObjectFiles))
filename, err := a.lookup(filename, "", "Menu") // Extension already present for i, name := range menu.ObjectFiles {
filename, err := a.lookup(name, "", "Menu") // Extension already present
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.ObjectByPath(filename) obj, err := a.ObjectByPath(filename)
if err != nil {
return nil, err
}
out[i] = obj
}
return out, nil
} }

View File

@@ -19,6 +19,7 @@ const (
TypeStatic MenuType = 0 TypeStatic MenuType = 0
TypeMenu MenuType = 1 TypeMenu MenuType = 1
TypeDragMenu MenuType = 2 // Only seen in Configure_Vehicle_{Chaos,Ultra} TypeDragMenu MenuType = 2 // Only seen in Configure_Vehicle_{Chaos,Ultra}
TypeSimpleButton MenuType = 3 TypeSimpleButton MenuType = 3
TypeDoorHotspot MenuType = 30 // Like a button I guess? "FONTTYPE is animation speed" TypeDoorHotspot MenuType = 30 // Like a button I guess? "FONTTYPE is animation speed"
TypeDoorHotspot2 MenuType = 31 // Seems like a duplicate of the above? What's different? TypeDoorHotspot2 MenuType = 31 // Seems like a duplicate of the above? What's different?
@@ -86,6 +87,8 @@ type Record struct {
Children []*Record Children []*Record
Id int Id int
ObjectIdx int // Can be specified in MENUID, defaults to 0
Type MenuType Type MenuType
DrawType int DrawType int
FontType int FontType int
@@ -109,11 +112,11 @@ type Menu struct {
ObjectFiles []string ObjectFiles []string
FontNames []string FontNames []string
// These are properties set in the menu header. We don't know what they're
// all for.
BackgroundColor color.Color BackgroundColor color.Color
HypertextColor color.Color HypertextColor color.Color
FontType int
// FIXME: turn these into first-class data
Properties map[string]string
// The actual menu records. There are multiple top-level items. Submenus are // The actual menu records. There are multiple top-level items. Submenus are
// only ever nested one deep. // only ever nested one deep.
@@ -122,10 +125,9 @@ type Menu struct {
func LoadMenu(filename string) (*Menu, error) { func LoadMenu(filename string) (*Menu, error) {
name := filepath.Base(filename) name := filepath.Base(filename)
name = strings.Replace(name, filepath.Ext(name), "", -1) name = strings.TrimSuffix(name, filepath.Ext(name))
name = strings.ToLower(name) name = strings.ToLower(name)
// FIXME: this needs turning into a real parser sometime
scanner, err := asciiscan.New(filename) scanner, err := asciiscan.New(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -133,81 +135,135 @@ func LoadMenu(filename string) (*Menu, error) {
defer scanner.Close() defer scanner.Close()
var str string
var record *Record
section := 0
isProp := false
out := &Menu{ out := &Menu{
Name: name, Name: name,
Properties: map[string]string{},
} }
for { if err := loadObjects(out, scanner); err != nil {
str, err = scanner.ConsumeString()
if err != nil {
return nil, err return nil, err
} }
// Whether the lines are properties or not alternate with each section, if err := loadProperties(out, scanner); err != nil {
// except the records use `*` as a separator return nil, err
if section < 3 && isProp != asciiscan.IsProperty(str) {
section += 1
isProp = !isProp
} }
if str == "~" { if err := loadFonts(out, scanner); err != nil {
return nil, err
}
if err := loadRecords(filepath.Dir(filename), out, scanner); err != nil {
return nil, err
}
return out, nil
}
func loadObjects(menu *Menu, scanner *asciiscan.Scanner) error {
strs, err := scanner.ConsumeStringList()
if err != nil {
return err
}
menu.ObjectFiles = strs
return nil
}
func loadProperties(menu *Menu, scanner *asciiscan.Scanner) error {
for {
ok, err := scanner.PeekProperty()
if err != nil {
return err
}
if !ok {
break break
} }
switch section { k, v, err := scanner.ConsumeProperty()
case 0: // List of object files
out.ObjectFiles = append(out.ObjectFiles, str)
case 1: // List of properties
k, v := asciiscan.ConsumeProperty(str)
vInt, err := strconv.Atoi(v) // FIXME:
switch k {
case "BACKGROUND COLOR 0..255..-1 trans":
if err != nil { if err != nil {
return nil, err return err
} }
out.BackgroundColor = data.ColorPalette[vInt] vInt, err := strconv.Atoi(v) // All properties have been int
if err != nil {
return err
}
switch strings.ToUpper(k) {
case "BACKGROUND COLOR 0..255..-1 TRANS":
menu.BackgroundColor = data.ColorPalette[vInt]
case "HYPERTEXT COLOR 0..255": case "HYPERTEXT COLOR 0..255":
if err != nil { menu.HypertextColor = data.ColorPalette[vInt]
return nil, err case "FONT TYPE 0..5":
menu.FontType = vInt
default:
return fmt.Errorf("Unhandled menu property in %v: %q=%q", menu.Name, k, v)
}
} }
out.HypertextColor = data.ColorPalette[vInt] return nil
default: }
out.Properties[k] = v
func loadFonts(menu *Menu, scanner *asciiscan.Scanner) error {
// FIXME: Can we just ignore NULL, or does the index matter?
strs, err := scanner.ConsumeStringList("NULL")
if err != nil {
return err
} }
case 2: // list of fonts
// FIXME: do we need to do something cleverer here? menu.FontNames = strs
if str == "NULL" {
return nil
}
func loadRecords(baseDir string, menu *Menu, scanner *asciiscan.Scanner) error {
var record *Record // We build records here and add them when complete
for {
str, err := scanner.ConsumeString()
if err != nil {
return err
}
if strings.HasPrefix(str, "$") {
subScanner, err := asciiscan.New(filepath.Join(baseDir, str[1:]))
if err != nil {
return err
}
err = loadRecords(baseDir, menu, subScanner)
subScanner.Close() // Don't keep this around for all of loadRecords
if err != nil {
return fmt.Errorf("Processing child %q: %v", str, err)
}
continue continue
} }
out.FontNames = append(out.FontNames, str)
case 3: // Menu records switch str {
if str == "*" { // NEXT RECORD case "*":
out.Records = append(out.Records, record.Toplevel()) if record != nil {
continue menu.Records = append(menu.Records, record.Toplevel())
}
continue // NEXT RECORD
case "~":
return nil // THE END
} }
k, v := asciiscan.ConsumeProperty(str) k, v := asciiscan.ConsumeProperty(str)
switch k { switch k {
case "MENUID": case "MENUID":
record = newRecord(out, nil) record = newRecord(menu, nil)
case "SUBMENUID": case "SUBMENUID":
record = newRecord(out, record.Toplevel()) record = newRecord(menu, record.Toplevel())
} }
setProperty(record, k, v) setProperty(record, k, v)
} }
}
log.Printf("Menu properties: %#+v", out.Properties) return nil
return out, nil
} }
func LoadMenus(dir string) (map[string]*Menu, error) { func LoadMenus(dir string) (map[string]*Menu, error) {
@@ -271,8 +327,25 @@ func setProperty(r *Record, k, v string) {
} }
switch k { switch k {
case "MENUID", "SUBMENUID": case "MENUID":
// ObjectIdx can be specified in the MENUID. Only seen for .mni files
if strings.Contains(v, ",") && len(vSplitInt) >= 2 {
r.Id = vSplitInt[0]
r.ObjectIdx = vSplitInt[1]
} else {
r.Id = vInt r.Id = vInt
}
case "SUBMENUID":
if strings.Contains(v, ",") {
log.Printf("%v has an object index in SUBMENUID - surprising", r.Locator())
r.Id = vSplitInt[0]
r.ObjectIdx = vSplitInt[1]
} else {
r.Id = vInt
if r.Parent != nil { // Children seem to inherit from parents?
r.ObjectIdx = r.Parent.ObjectIdx
}
}
case "MENUTYPE", "SUBMENUTYPE": case "MENUTYPE", "SUBMENUTYPE":
if strings.Contains(v, ",") { if strings.Contains(v, ",") {
r.Type = MenuType(vSplitInt[0]) // FIXME: what are the other values in this case? r.Type = MenuType(vSplitInt[0]) // FIXME: what are the other values in this case?

View File

@@ -51,12 +51,12 @@ func registerInvokeButton(d *Driver, r *menus.Record) error {
} }
func registerMainButton(d *Driver, r *menus.Record) error { func registerMainButton(d *Driver, r *menus.Record) error {
sprites, err := d.menu.Sprites(r.Share, 3) // base, pressed, disabled sprites, err := d.menu.Sprites(r.ObjectIdx, r.Share, 3) // base, pressed, disabled
if err != nil { if err != nil {
return err return err
} }
hovers, err := d.menu.Images(r.SpriteId[0], r.DrawType) hovers, err := d.menu.Images(r.ObjectIdx, r.SpriteId[0], r.DrawType)
if err != nil { if err != nil {
return err return err
} }
@@ -81,7 +81,7 @@ func registerMainButton(d *Driver, r *menus.Record) error {
} }
func registerDoorHotspot(d *Driver, r *menus.Record) error { func registerDoorHotspot(d *Driver, r *menus.Record) error {
sprites, err := d.menu.Sprites(r.Share, 2) // base, pressed sprites, err := d.menu.Sprites(r.ObjectIdx, r.Share, 2) // base, pressed
if err != nil { if err != nil {
return err return err
} }
@@ -104,7 +104,7 @@ func registerDoorHotspot(d *Driver, r *menus.Record) error {
} }
func registerButton(d *Driver, r *menus.Record, spriteId int) (*button, error) { func registerButton(d *Driver, r *menus.Record, spriteId int) (*button, error) {
sprites, err := d.menu.Sprites(spriteId, 3) // base, pressed, disabled sprites, err := d.menu.Sprites(r.ObjectIdx, spriteId, 3) // base, pressed, disabled
if err != nil { if err != nil {
return nil, err return nil, err
} }

20
internal/ui/dialogue.go Normal file
View File

@@ -0,0 +1,20 @@
package ui
import (
"code.ur.gs/lupine/ordoor/internal/menus"
)
func init() {
// Needed for Keyboard.mnu (main -> options -> keyboard).
// Dialogues can be active(?) or not. If they're not, then they are hidden.
// Dialogues seem to be modal in all cases?
registerBuilder(menus.TypeDialogue, registerDebug("WIP Dialogue", registerDialogue))
}
func registerDialogue(d *Driver, r *menus.Record) ([]*menus.Record, error) {
// The dialogue itself has a sprite
_, err := registerNoninteractive(d, r)
// TODO: we need to group these. Z levels seem overkill?
return r.Children, err
}

View File

@@ -17,11 +17,6 @@ import (
func init() { func init() {
// FIXME: these need implementing // FIXME: these need implementing
// Needed for Keyboard.mnu (main -> options -> keyboard)
registerBuilder(menus.TypeDialogue, registerDebug("Unimplemented Dialogue", nil))
// Needed for ChaEquip.mnu
// Needed for MainGameChaos.mnu // Needed for MainGameChaos.mnu
registerBuilder(menus.TypeStatusBar, registerDebug("Unimplemented StatusBar", nil)) registerBuilder(menus.TypeStatusBar, registerDebug("Unimplemented StatusBar", nil))
@@ -51,7 +46,7 @@ type builderFunc func(d *Driver, r *menus.Record) (children []*menus.Record, err
func registerDebug(reason string, onward builderFunc) builderFunc { func registerDebug(reason string, onward builderFunc) builderFunc {
return func(d *Driver, r *menus.Record) ([]*menus.Record, error) { return func(d *Driver, r *menus.Record) ([]*menus.Record, error) {
log.Printf("%v: %#+v", reason, r) log.Printf("%v: %v: %#+v", reason, r.Locator(), r)
if onward == nil { if onward == nil {
return r.Children, nil return r.Children, nil
} }
@@ -327,7 +322,7 @@ func (d *Driver) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) {
} }
func (d *Driver) addRecord(record *menus.Record) error { func (d *Driver) addRecord(record *menus.Record) error {
log.Printf("Adding record %v: %#+v", record.Locator(), record) //log.Printf("Adding record %v: %#+v", record.Locator(), record)
children := record.Children children := record.Children
handler, ok := widgetBuilders[record.Type] handler, ok := widgetBuilders[record.Type]

View File

@@ -21,7 +21,7 @@ type inventorySelect struct {
// Called from the menu, which fills "others" for us // Called from the menu, which fills "others" for us
func registerInventorySelect(d *Driver, r *menus.Record) (*inventorySelect, error) { func registerInventorySelect(d *Driver, r *menus.Record) (*inventorySelect, error) {
sprites, err := d.menu.Sprites(r.Share, 3) // unchecked, checked, disabled sprites, err := d.menu.Sprites(r.ObjectIdx, r.Share, 3) // unchecked, checked, disabled
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -101,7 +101,7 @@ func registerListBox(d *Driver, menu *menus.Record) ([]*menus.Record, error) {
return nil, err return nil, err
} }
thumbBaseSpr, err := d.menu.Sprite(thumb.Share) thumbBaseSpr, err := d.menu.Sprite(menu.ObjectIdx, thumb.Share)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -111,7 +111,7 @@ func registerListBox(d *Driver, menu *menus.Record) ([]*menus.Record, error) {
thumbSprId = thumb.Share thumbSprId = thumb.Share
} }
thumbImgSpr, err := d.menu.Sprite(thumbSprId) thumbImgSpr, err := d.menu.Sprite(menu.ObjectIdx, thumbSprId)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -70,7 +70,7 @@ func registerNoninteractive(d *Driver, r *menus.Record) (*noninteractive, error)
spriteId = r.SpriteId[0] spriteId = r.SpriteId[0]
} }
sprite, err := d.menu.Sprite(spriteId) sprite, err := d.menu.Sprite(r.ObjectIdx, spriteId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -89,7 +89,7 @@ func registerNoninteractive(d *Driver, r *menus.Record) (*noninteractive, error)
} }
func registerHypertext(d *Driver, r *menus.Record) error { func registerHypertext(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.Share) sprite, err := d.menu.Sprite(r.ObjectIdx, r.Share)
if err != nil { if err != nil {
return err return err
} }
@@ -108,7 +108,7 @@ func registerHypertext(d *Driver, r *menus.Record) error {
// An overlay is a static image + some text that needs to be rendered // An overlay is a static image + some text that needs to be rendered
func registerOverlay(d *Driver, r *menus.Record) error { func registerOverlay(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.Share) sprite, err := d.menu.Sprite(r.ObjectIdx, r.Share)
if err != nil { if err != nil {
return err return err
} }
@@ -139,12 +139,12 @@ func registerOverlay(d *Driver, r *menus.Record) error {
// An animation is a non-interactive element that displays something in a loop // An animation is a non-interactive element that displays something in a loop
func registerAnimation(d *Driver, r *menus.Record) error { func registerAnimation(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.SpriteId[0]) sprite, err := d.menu.Sprite(r.ObjectIdx, r.SpriteId[0])
if err != nil { if err != nil {
return err return err
} }
frames, err := d.menu.Images(r.SpriteId[0], r.DrawType) frames, err := d.menu.Images(r.ObjectIdx, r.SpriteId[0], r.DrawType)
if err != nil { if err != nil {
return err return err
} }
@@ -163,17 +163,17 @@ func registerAnimation(d *Driver, r *menus.Record) error {
} }
func registerAnimationHover(d *Driver, r *menus.Record) error { func registerAnimationHover(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.SpriteId[0]) sprite, err := d.menu.Sprite(r.ObjectIdx, r.SpriteId[0])
if err != nil { if err != nil {
return err return err
} }
enterFrames, err := d.menu.Images(r.SpriteId[0], r.DrawType) enterFrames, err := d.menu.Images(r.ObjectIdx, r.SpriteId[0], r.DrawType)
if err != nil { if err != nil {
return err return err
} }
exitFrames, err := d.menu.Images(r.SpriteId[0]+r.DrawType, r.DrawType) exitFrames, err := d.menu.Images(r.ObjectIdx, r.SpriteId[0]+r.DrawType, r.DrawType)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -39,7 +39,7 @@ type slider struct {
// A checkbox has 3 sprites, and 3 states: unchecked, checked, disabled. // A checkbox has 3 sprites, and 3 states: unchecked, checked, disabled.
func registerCheckbox(d *Driver, r *menus.Record) error { func registerCheckbox(d *Driver, r *menus.Record) error {
sprites, err := d.menu.Sprites(r.Share, 3) // unchecked, disabled, checked sprites, err := d.menu.Sprites(r.ObjectIdx, r.Share, 3) // unchecked, disabled, checked
if err != nil { if err != nil {
return err return err
} }
@@ -65,7 +65,7 @@ func registerCheckbox(d *Driver, r *menus.Record) error {
} }
func registerSlider(d *Driver, r *menus.Record) error { func registerSlider(d *Driver, r *menus.Record) error {
sprites, err := d.menu.Sprites(r.Share, 3) // base, clicked, slider element sprites, err := d.menu.Sprites(r.ObjectIdx, r.Share, 3) // base, clicked, slider element
if err != nil { if err != nil {
return err return err
} }

View File

@@ -4,6 +4,7 @@ package asciiscan
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
"strconv" "strconv"
@@ -15,6 +16,9 @@ var hashComment = []byte("#")
type Scanner struct { type Scanner struct {
bufio *bufio.Scanner bufio *bufio.Scanner
closer io.Closer closer io.Closer
// If we've peeked, there will be items here
buffered []string
} }
func New(filename string) (*Scanner, error) { func New(filename string) (*Scanner, error) {
@@ -38,6 +42,13 @@ func (s *Scanner) Close() error {
} }
func (s *Scanner) ConsumeString() (string, error) { func (s *Scanner) ConsumeString() (string, error) {
if len(s.buffered) > 0 {
out, buffered := s.buffered[0], s.buffered[1:]
s.buffered = buffered
return out, nil
}
for s.bufio.Scan() { for s.bufio.Scan() {
line := s.bufio.Bytes() line := s.bufio.Bytes()
@@ -68,15 +79,41 @@ func ConsumeProperty(s string) (string, string) {
} }
parts := strings.SplitN(s, ":", 2) parts := strings.SplitN(s, ":", 2)
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
} }
// Peek ahead in the input stream to see if the next line might be a property // Check to see if the line looks like a property (contains a colon character).
// (contain a colon character).
func IsProperty(s string) bool { func IsProperty(s string) bool {
return strings.Contains(s, ":") return strings.Contains(s, ":")
} }
// Checks if the next line might be a property, without reading it
func (s *Scanner) PeekProperty() (bool, error) {
str, err := s.ConsumeString()
if err != nil {
return false, err
}
s.buffered = append(s.buffered, str)
return IsProperty(str), nil
}
func (s *Scanner) ConsumeProperty() (string, string, error) {
str, err := s.ConsumeString()
if err != nil {
return "", "", err
}
if !IsProperty(str) {
return "", "", fmt.Errorf("Not a property: %q", str)
}
k, v := ConsumeProperty(str)
return k, v, nil
}
func (s *Scanner) ConsumeInt() (int, error) { func (s *Scanner) ConsumeInt() (int, error) {
str, err := s.ConsumeString() str, err := s.ConsumeString()
if err != nil { if err != nil {
@@ -86,6 +123,39 @@ func (s *Scanner) ConsumeInt() (int, error) {
return strconv.Atoi(str) return strconv.Atoi(str)
} }
// Reads a list of non-property lines, skipping any that match the given strings
func (s *Scanner) ConsumeStringList(skip ...string) ([]string, error) {
skipper := make(map[string]bool, len(skip))
for _, str := range skip {
skipper[str] = true
}
var out []string
for {
isProp, err := s.PeekProperty()
if err != nil {
return nil, err
}
// The object list is terminated by the first property
if isProp {
break
}
str, err := s.ConsumeString()
if err != nil {
return nil, err
}
if !skipper[str] {
out = append(out, str)
}
}
return out, nil
}
func (s *Scanner) ConsumeIntPtr(to *int) error { func (s *Scanner) ConsumeIntPtr(to *int) error {
val, err := s.ConsumeInt() val, err := s.ConsumeInt()
if err != nil { if err != nil {