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

@@ -16,9 +16,10 @@ import (
type MenuType int
const (
TypeStatic MenuType = 0
TypeMenu MenuType = 1
TypeDragMenu MenuType = 2 // Only seen in Configure_Vehicle_{Chaos,Ultra}
TypeStatic MenuType = 0
TypeMenu MenuType = 1
TypeDragMenu MenuType = 2 // Only seen in Configure_Vehicle_{Chaos,Ultra}
TypeSimpleButton MenuType = 3
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?
@@ -85,7 +86,9 @@ type Record struct {
Parent *Record
Children []*Record
Id int
Id int
ObjectIdx int // Can be specified in MENUID, defaults to 0
Type MenuType
DrawType int
FontType int
@@ -109,11 +112,11 @@ type Menu struct {
ObjectFiles []string
FontNames []string
// These are properties set in the menu header. We don't know what they're
// all for.
BackgroundColor color.Color
HypertextColor color.Color
// FIXME: turn these into first-class data
Properties map[string]string
FontType int
// The actual menu records. There are multiple top-level items. Submenus are
// only ever nested one deep.
@@ -122,10 +125,9 @@ type Menu struct {
func LoadMenu(filename string) (*Menu, error) {
name := filepath.Base(filename)
name = strings.Replace(name, filepath.Ext(name), "", -1)
name = strings.TrimSuffix(name, filepath.Ext(name))
name = strings.ToLower(name)
// FIXME: this needs turning into a real parser sometime
scanner, err := asciiscan.New(filename)
if err != nil {
return nil, err
@@ -133,81 +135,135 @@ func LoadMenu(filename string) (*Menu, error) {
defer scanner.Close()
var str string
var record *Record
section := 0
isProp := false
out := &Menu{
Name: name,
Properties: map[string]string{},
Name: name,
}
if err := loadObjects(out, scanner); err != nil {
return nil, err
}
if err := loadProperties(out, scanner); err != nil {
return nil, err
}
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 {
str, err = scanner.ConsumeString()
ok, err := scanner.PeekProperty()
if err != nil {
return nil, err
return 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 == "~" {
if !ok {
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)
vInt, err := strconv.Atoi(v) // FIXME:
switch k {
case "BACKGROUND COLOR 0..255..-1 trans":
if err != nil {
return nil, err
}
k, v, err := scanner.ConsumeProperty()
if err != nil {
return err
}
out.BackgroundColor = data.ColorPalette[vInt]
case "HYPERTEXT COLOR 0..255":
if err != nil {
return nil, err
}
vInt, err := strconv.Atoi(v) // All properties have been int
if err != nil {
return err
}
out.HypertextColor = data.ColorPalette[vInt]
default:
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(out, nil)
case "SUBMENUID":
record = newRecord(out, record.Toplevel())
}
setProperty(record, k, v)
switch strings.ToUpper(k) {
case "BACKGROUND COLOR 0..255..-1 TRANS":
menu.BackgroundColor = data.ColorPalette[vInt]
case "HYPERTEXT COLOR 0..255":
menu.HypertextColor = data.ColorPalette[vInt]
case "FONT TYPE 0..5":
menu.FontType = vInt
default:
return fmt.Errorf("Unhandled menu property in %v: %q=%q", menu.Name, k, v)
}
}
log.Printf("Menu properties: %#+v", out.Properties)
return nil
}
return out, nil
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
}
menu.FontNames = strs
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
}
switch str {
case "*":
if record != nil {
menu.Records = append(menu.Records, record.Toplevel())
}
continue // NEXT RECORD
case "~":
return nil // THE END
}
k, v := asciiscan.ConsumeProperty(str)
switch k {
case "MENUID":
record = newRecord(menu, nil)
case "SUBMENUID":
record = newRecord(menu, record.Toplevel())
}
setProperty(record, k, v)
}
return nil
}
func LoadMenus(dir string) (map[string]*Menu, error) {
@@ -271,8 +327,25 @@ func setProperty(r *Record, k, v string) {
}
switch k {
case "MENUID", "SUBMENUID":
r.Id = vInt
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
}
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":
if strings.Contains(v, ",") {
r.Type = MenuType(vSplitInt[0]) // FIXME: what are the other values in this case?