package jdr
|
|
|
|
import (
|
|
//"html/template"
|
|
"encoding/xml"
|
|
|
|
"errors"
|
|
"os"
|
|
)
|
|
|
|
type Page struct {
|
|
Items []SheetItem
|
|
}
|
|
|
|
type SheetItem interface{
|
|
}
|
|
|
|
type Block struct {
|
|
Name string
|
|
Items []SheetItem
|
|
}
|
|
|
|
type Variable struct {
|
|
Type VariableType
|
|
Name string
|
|
}
|
|
|
|
type CharacterSheet struct {
|
|
types map[string]VariableType
|
|
Pages []*Page
|
|
}
|
|
|
|
func (self *CharacterSheet) AddType(t VariableType) {
|
|
if self.types == nil {
|
|
self.types = make(map[string]VariableType)
|
|
}
|
|
|
|
name := t.Name()
|
|
self.types[name] = t
|
|
}
|
|
|
|
func (self *CharacterSheet) AddDefaultTypes() {
|
|
self.AddType(&VariableType_bool{})
|
|
self.AddType(&VariableType_int{})
|
|
self.AddType(&VariableType_float{})
|
|
self.AddType(&VariableType_string{})
|
|
self.AddType(&VariableType_d{})
|
|
}
|
|
|
|
func (self *CharacterSheet) getType(typename string) (VariableType, error) {
|
|
t, ok := self.types[typename]
|
|
if !ok {
|
|
return &VariableType_badType{}, errors.New("unknown type \"" + typename + "\"")
|
|
}
|
|
|
|
return t, nil
|
|
}
|
|
|
|
type xmlNode struct {
|
|
XMLName xml.Name
|
|
Attrs []xml.Attr `xml:"-"`
|
|
Content []byte `xml:",innerxml"`
|
|
Nodes []xmlNode `xml:",any"`
|
|
}
|
|
|
|
func (n *xmlNode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
n.Attrs = start.Attr
|
|
type node xmlNode
|
|
|
|
return d.DecodeElement((*node)(n), &start)
|
|
}
|
|
|
|
func parseVariable(root xmlNode, sheet *CharacterSheet, defaults *Variable) (*Variable, error) {
|
|
ret := &Variable{}
|
|
|
|
typeOk := false
|
|
nameOk := false
|
|
|
|
if defaults != nil {
|
|
switch defaults.Type.(type) {
|
|
case *VariableType_badType:
|
|
typeOk = false
|
|
default:
|
|
typeOk = true
|
|
ret.Type = defaults.Type
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(root.Attrs); i++ {
|
|
attrName := root.Attrs[i].Name.Local
|
|
attrVal := root.Attrs[i].Value
|
|
|
|
switch attrName {
|
|
case "type":
|
|
t, err := sheet.getType(attrVal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret.Type = t
|
|
typeOk = true
|
|
case "name":
|
|
ret.Name = attrVal
|
|
nameOk = true
|
|
}
|
|
}
|
|
|
|
if !nameOk {
|
|
return nil, errors.New("Unnamed variable")
|
|
}
|
|
if !typeOk {
|
|
return nil, errors.New("No type provided for variable \"" + ret.Name + "\"")
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func parseBlock(root xmlNode, sheet *CharacterSheet) (*Block, error) {
|
|
ret := &Block{}
|
|
|
|
for i := 0; i < len(root.Attrs); i++ {
|
|
attrName := root.Attrs[i].Name.Local
|
|
attrVal := root.Attrs[i].Value
|
|
|
|
switch attrName {
|
|
case "name":
|
|
ret.Name = attrVal
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(root.Nodes); i++ {
|
|
item, err := parseItem(root.Nodes[i], sheet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ret.Items = append(ret.Items, item)
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func parseVariableGroup(root xmlNode, sheet *CharacterSheet, defaults *Variable) ([]*Variable, error) {
|
|
ret := []*Variable{}
|
|
|
|
typeOk := false
|
|
if defaults == nil {
|
|
defaults = &Variable{}
|
|
} else {
|
|
typeOk = true
|
|
}
|
|
|
|
for i := 0; i < len(root.Attrs); i++ {
|
|
attrName := root.Attrs[i].Name.Local
|
|
attrVal := root.Attrs[i].Value
|
|
|
|
switch attrName {
|
|
case "default-type":
|
|
t, err := sheet.getType(attrVal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defaults.Type = t
|
|
typeOk = true
|
|
}
|
|
}
|
|
|
|
if !typeOk {
|
|
defaults.Type = &VariableType_badType{}
|
|
}
|
|
|
|
for i := 0; i < len(root.Nodes); i++ {
|
|
node := root.Nodes[i]
|
|
|
|
switch node.XMLName.Local {
|
|
case "variable":
|
|
item, err := parseVariable(node, sheet, defaults)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ret = append(ret, item)
|
|
case "variable-group":
|
|
items, err := parseVariableGroup(node, sheet, defaults)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for j := 0; j < len(items); j++ {
|
|
ret = append(ret, items[j])
|
|
}
|
|
|
|
default:
|
|
return nil, errors.New("Unknown variable-group item: \"" + node.XMLName.Local + "\"")
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func parseItem(root xmlNode, sheet *CharacterSheet) ([]SheetItem, error) {
|
|
switch root.XMLName.Local {
|
|
case "block":
|
|
block, err := parseBlock(root, sheet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []SheetItem{block}, nil
|
|
case "variable":
|
|
variable, err := parseVariable(root, sheet, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []SheetItem{variable}, err
|
|
case "variable-group":
|
|
variables, err := parseVariableGroup(root, sheet, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make([]SheetItem, len(variables))
|
|
for i, v := range variables {
|
|
items[i] = SheetItem(v)
|
|
}
|
|
return items, nil
|
|
default:
|
|
return nil, errors.New("Unknown item: " + root.XMLName.Local)
|
|
}
|
|
}
|
|
|
|
func parsePage(root xmlNode, sheet *CharacterSheet) (*Page, error) {
|
|
ret := &Page{}
|
|
|
|
for i := 0; i < len(root.Nodes); i++ {
|
|
item, err := parseItem(root.Nodes[i], sheet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ret.Items = append(ret.Items, item)
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func parseSheet(root xmlNode) (*CharacterSheet, error) {
|
|
if root.XMLName.Local != "jdr-desc" {
|
|
return nil, errors.New("Wrong root node")
|
|
}
|
|
|
|
sheet := &CharacterSheet{}
|
|
sheet.AddDefaultTypes()
|
|
for i := 0; i < len(root.Nodes); i++ {
|
|
node := root.Nodes[i]
|
|
|
|
switch node.XMLName.Local {
|
|
case "custom-types":
|
|
types, err := parseCustomTypes(node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for j := 0; j < len(types); j++ {
|
|
sheet.AddType(types[j])
|
|
}
|
|
|
|
case "page":
|
|
page, err := parsePage(node, sheet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sheet.Pages = append(sheet.Pages, page)
|
|
|
|
default:
|
|
return nil, errors.New("Wrong format")
|
|
}
|
|
}
|
|
|
|
return sheet, nil
|
|
}
|
|
|
|
func ReadCharacterSheet(filename string) (*CharacterSheet, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
decoder := xml.NewDecoder(file)
|
|
var n xmlNode
|
|
err = decoder.Decode(&n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sheet, err := parseSheet(n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sheet, nil
|
|
}
|