Make include directives work in .mnu files
This commit is contained in:
@@ -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?
|
||||
|
Reference in New Issue
Block a user