First pass at displaying Menu files
This commit is contained in:
178
internal/menus/menus.go
Normal file
178
internal/menus/menus.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package menus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ur.gs/ordoor/internal/util/asciiscan"
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
Parent *Record
|
||||
Children []*Record
|
||||
|
||||
Id int
|
||||
Type int
|
||||
Active bool
|
||||
SpriteId int
|
||||
X int
|
||||
Y int
|
||||
|
||||
// FIXME: turn these into first-class data
|
||||
properties map[string]string
|
||||
}
|
||||
|
||||
type Menu struct {
|
||||
Name string
|
||||
// TODO: load these
|
||||
ObjectFiles []string
|
||||
FontNames []string
|
||||
|
||||
// FIXME: turn these into first-class data
|
||||
Properties map[string]string
|
||||
|
||||
// The actual menu records. There are multiple top-level items. Submenus are
|
||||
// only ever nested one deep.
|
||||
Records []*Record
|
||||
}
|
||||
|
||||
func LoadMenu(filename string) (*Menu, error) {
|
||||
name := filepath.Base(filename)
|
||||
|
||||
// FIXME: this needs turning into a real parser sometime
|
||||
scanner, err := asciiscan.New(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var str string
|
||||
var record *Record
|
||||
|
||||
section := 0
|
||||
isProp := false
|
||||
out := &Menu{
|
||||
Name: name,
|
||||
Properties: map[string]string{},
|
||||
}
|
||||
|
||||
for {
|
||||
str, err = scanner.ConsumeString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Whether the lines are properties or not alternate with each section,
|
||||
// except the records use `*` as a separator
|
||||
if section < 3 && isProp != asciiscan.IsProperty(str) {
|
||||
section += 1
|
||||
isProp = !isProp
|
||||
}
|
||||
|
||||
if str == "~" {
|
||||
break
|
||||
}
|
||||
|
||||
switch section {
|
||||
case 0: // List of object files
|
||||
out.ObjectFiles = append(out.ObjectFiles, str)
|
||||
case 1: // List of properties
|
||||
k, v := asciiscan.ConsumeProperty(str)
|
||||
out.Properties[k] = v
|
||||
case 2: // list of fonts
|
||||
// FIXME: do we need to do something cleverer here?
|
||||
if str == "NULL" {
|
||||
continue
|
||||
}
|
||||
out.FontNames = append(out.FontNames, str)
|
||||
case 3: // Menu records
|
||||
if str == "*" { // NEXT RECORD
|
||||
out.Records = append(out.Records, record.Toplevel())
|
||||
continue
|
||||
}
|
||||
|
||||
k, v := asciiscan.ConsumeProperty(str)
|
||||
switch k {
|
||||
case "MENUID":
|
||||
record = newRecord(nil)
|
||||
case "SUBMENUID":
|
||||
record = newRecord(record.Toplevel())
|
||||
}
|
||||
setProperty(record, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func LoadMenus(dir string) (map[string]*Menu, error) {
|
||||
fis, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]*Menu, len(fis))
|
||||
|
||||
for _, fi := range fis {
|
||||
relname := fi.Name()
|
||||
basename := filepath.Base(relname)
|
||||
extname := filepath.Ext(relname)
|
||||
|
||||
// Skip anything that isn't a .mnu file
|
||||
if !strings.EqualFold(extname, ".mnu") {
|
||||
continue
|
||||
}
|
||||
|
||||
built, err := LoadMenu(filepath.Join(dir, relname))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", filepath.Join(dir, relname), err)
|
||||
}
|
||||
|
||||
out[basename] = built
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func newRecord(parent *Record) *Record {
|
||||
out := &Record{
|
||||
Parent: parent,
|
||||
properties: map[string]string{},
|
||||
}
|
||||
|
||||
if parent != nil {
|
||||
parent.Children = append(parent.Children, out)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Record) Toplevel() *Record {
|
||||
if r.Parent != nil {
|
||||
return r.Parent.Toplevel()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func setProperty(r *Record, k, v string) {
|
||||
vInt, _ := strconv.Atoi(v)
|
||||
switch k {
|
||||
case "MENUID", "SUBMENUID":
|
||||
r.Id = vInt
|
||||
case "MENUTYPE", "SUBMENUTYPE":
|
||||
r.Type = vInt
|
||||
case "ACTIVE":
|
||||
r.Active = (vInt != 0)
|
||||
case "SPRITEID":
|
||||
r.SpriteId = vInt
|
||||
case "X-CORD":
|
||||
r.X = vInt
|
||||
case "Y-CORD":
|
||||
r.Y = vInt
|
||||
default:
|
||||
r.properties[k] = v
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user