diff --git a/cmd/sym_dump/main.go b/cmd/sym_dump/main.go index 9d70160..16adbd0 100644 --- a/cmd/sym_dump/main.go +++ b/cmd/sym_dump/main.go @@ -77,6 +77,7 @@ func main() { } p.ParseTypes(f.Syms) p.ParseDecls(f.Syms) + p.RemoveDuplicateTypes() p.MakeNamesUnique() // Output once for each files if not in merge mode. if !merge { @@ -148,7 +149,6 @@ func pruneDuplicates(ps []*csym.Parser, skipAddrDiff, skipLineDiff bool, opts *s } // Sort types by tag. - natsort.Strings(dst.EnumTags) less := func(i, j int) bool { ti := dst.Typedefs[i].(*c.VarDecl) tj := dst.Typedefs[j].(*c.VarDecl) @@ -301,8 +301,9 @@ func addUniqueUnions(dst *csym.Parser, p *csym.Parser, pnum int, fakeCount *int, // addUniqueEnums adds unique Enums to destination parser. func addUniqueEnums(dst *csym.Parser, p *csym.Parser, pnum int, fakeCount *int, isPresent *map[string]bool, opts *sym.Options) { - for _, tag := range p.EnumTags { - t := p.Enums[tag] + for tag, enums := range p.EnumTags { + for i := 0; i < len(enums); i++ { + t := enums[i] fake := strings.Contains(t.Tag, "fake") if fake { t.Tag = placeholder @@ -315,16 +316,17 @@ func addUniqueEnums(dst *csym.Parser, p *csym.Parser, pnum int, fakeCount *int, } if !(*isPresent)[s] { if !fake { - if _, ok := dst.Enums[tag]; ok { + if _, ok := dst.EnumTags[tag]; ok { tag = fmt.Sprintf("%s_dup_%d", tag, pnum) t.Tag = tag } } - dst.Enums[tag] = t - dst.EnumTags = append(dst.EnumTags, tag) + dst.EnumTags[tag] = append(dst.EnumTags[tag], t) + dst.Enums = append(dst.Enums, t) } (*isPresent)[s] = true } + } } // dump dumps the declarations of the parser to the given output directory, in diff --git a/cmd/sym_dump/output.go b/cmd/sym_dump/output.go index e972885..e490ca0 100644 --- a/cmd/sym_dump/output.go +++ b/cmd/sym_dump/output.go @@ -37,8 +37,7 @@ func dumpTypes(p *csym.Parser, outputDir string) error { } } // Print enums. - for _, tag := range p.EnumTags { - t := p.Enums[tag] + for _, t := range p.Enums { if _, err := fmt.Fprintf(f, "%s;\n\n", t.Def()); err != nil { return errors.WithStack(err) } diff --git a/csym/parse.go b/csym/parse.go index a3fbada..e53d5bf 100644 --- a/csym/parse.go +++ b/csym/parse.go @@ -12,18 +12,18 @@ type Parser struct { // Struct maps from struct tag to struct types. StructTags map[string][]*c.StructType - // unions maps from union tag to union type. + // Unions maps from union tag to union types. UnionTags map[string][]*c.UnionType - // enums maps from enum tag to enum type. - Enums map[string]*c.EnumType + // Enums maps from enum tag to enum types. + EnumTags map[string][]*c.EnumType // types maps from type name to underlying type definition. Types map[string]c.Type // Structs in order of occurrence in SYM file. Structs []*c.StructType // Unions in order of occurrence in SYM file. Unions []*c.UnionType - // Enum tags in order of occurrence in SYM file. - EnumTags []string + // Enums in order of occurrence in SYM file. + Enums []*c.EnumType // Type definitions in order of occurrence in SYM file. Typedefs []c.Type // Tracks unique enum member names. @@ -53,7 +53,7 @@ func NewParser(opts *sym.Options) *Parser { return &Parser{ StructTags: make(map[string][]*c.StructType), UnionTags: make(map[string][]*c.UnionType), - Enums: make(map[string]*c.EnumType), + EnumTags: make(map[string][]*c.EnumType), Types: make(map[string]c.Type), enumMembers: make(map[string]bool), Overlay: overlay, diff --git a/csym/parse_fixups.go b/csym/parse_fixups.go index 90ee70b..4a491d6 100644 --- a/csym/parse_fixups.go +++ b/csym/parse_fixups.go @@ -2,14 +2,41 @@ package csym import ( "fmt" + "reflect" "github.com/mefistotelis/psx_mnd_sym/csym/c" ) -//TODO make unique names of types -//TODO remove duplicate items +// RemoveDuplicateTypes goes through parsed types and marks exact duplicates. +func (p *Parser) RemoveDuplicateTypes() { + if p.opts.Verbose { fmt.Printf("Remove duplicate types...\n") } + p.removeStructsDuplicates() +} + +// removeStructsDuplicates goes through parsed symbols and marks exact duplicates. +func (p *Parser) removeStructsDuplicates() { + n := 0 + for tag, structs := range p.StructTags { + for i := 0; i < len(structs); i++ { + t1 := structs[i] + for k := i+1; k < len(structs); k++ { + t2 := structs[k] + if !reflect.DeepEqual(t2, t1) { continue } + structs = structsRemoveIndex(structs, k) + k-- + n++ + } + } + p.StructTags[tag] = structs; + } + if p.opts.Verbose { fmt.Printf("Removed structs: %d\n", n) } +} -// MakeNamesUnique goes through parsed symbols and renames duplicate ones. +func structsRemoveIndex(s []*c.StructType, index int) []*c.StructType { + return append(s[:index], s[index+1:]...) +} + +// MakeNamesUnique goes through parsed symbols and renames duplicate names. func (p *Parser) MakeNamesUnique() { if p.opts.Verbose { fmt.Printf("Making names unique...\n") } p.makeStructsUnique() @@ -60,7 +87,6 @@ func (p *Parser) makeFuncNamesUniqueInOverlay(overlay *Overlay) { // makeStructsUnique goes through parsed symbols and renames duplicate ones. func (p *Parser) makeStructsUnique() { for _, structs := range p.StructTags { - // Do not rename extern declarations real_len := len(structs) if real_len < 2 { continue } for i := 0; i < len(structs); i++ { @@ -72,10 +98,26 @@ func (p *Parser) makeStructsUnique() { // makeUnionsUnique goes through parsed symbols and renames duplicate ones. func (p *Parser) makeUnionsUnique() { + for _, unions := range p.UnionTags { + real_len := len(unions) + if real_len < 2 { continue } + for i := 0; i < len(unions); i++ { + t := unions[i] + t.Tag = UniqueUnionTag(p.UnionTags, t) + } + } } // makeEnumsUnique goes through parsed symbols and renames duplicate ones. func (p *Parser) makeEnumsUnique() { + for _, enums := range p.EnumTags { + real_len := len(enums) + if real_len < 2 { continue } + for i := 0; i < len(enums); i++ { + t := enums[i] + t.Tag = UniqueEnumTag(p.EnumTags, t) + } + } } // UniqueName returns a unique name based on the given name and address. @@ -141,3 +183,18 @@ func UniqueUnionTag(unionTags map[string][]*c.UnionType, t *c.UnionType) string } return newTag } + +// UniqueEnumTag returns a unique enum tag based on the given enum +// and set of present enums mapped by tags. +func UniqueEnumTag(EnumTags map[string][]*c.EnumType, t *c.EnumType) string { + newTag := t.Tag + for { + enums, ok := EnumTags[newTag] + if !ok { break } // the tag is unique - done + k := SliceIndex(len(enums), func(i int) bool { return enums[i] == t }) + if k < 0 { k = len(enums) } + newTag = UniqueTag(newTag, k) + } + return newTag +} + diff --git a/csym/parse_types.go b/csym/parse_types.go index a1c68a6..9cc613c 100644 --- a/csym/parse_types.go +++ b/csym/parse_types.go @@ -72,8 +72,8 @@ func (p *Parser) emptyEnum(tag string) *c.EnumType { t := &c.EnumType{ Tag: tag, } - p.Enums[tag] = t - p.EnumTags = append(p.EnumTags, tag) + p.EnumTags[tag] = append(p.EnumTags[tag], t) + p.Enums = append(p.Enums, t) return t } @@ -92,9 +92,6 @@ func (p *Parser) initTaggedTypes(syms []*sym.Symbol) { // Add scaffolding types for structs, unions and enums, so they may be // referrenced before defined. p.emptyStruct("__vtbl_ptr_type", 0) - var ( - enumTags = make(map[string]bool) - ) for _, s := range syms { switch body := s.Body.(type) { case *sym.Def: @@ -105,7 +102,6 @@ func (p *Parser) initTaggedTypes(syms []*sym.Symbol) { case sym.ClassUNTAG: p.emptyUnion(tag, body.Size) case sym.ClassENTAG: - tag = uniqueTag(tag, enumTags) p.emptyEnum(tag) } } @@ -384,10 +380,21 @@ func findEmptyUnion(p *Parser, tag string, size uint32) *c.UnionType { // findEnum returns the enumeration with the given tag. func (p *Parser) findEnum(tag string) *c.EnumType { - t, ok := p.Enums[tag] - // Ignore size - we currently support only one type with specific tag - if !ok { + var t *c.EnumType = nil + nameExists := false + enums, ok := p.EnumTags[tag] + if ok { + nameExists = len(enums) > 0 + for i := 0; i < len(enums); i++ { + tt := enums[i] + t = tt + } + } + if t == nil { t = p.emptyEnum(tag) + if nameExists { + t.Tag = UniqueEnumTag(p.EnumTags, t) + } log.Printf("unable to locate enum %q, created empty", tag) } return t @@ -397,17 +404,19 @@ func (p *Parser) findEnum(tag string) *c.EnumType { // It selects the enum which has no members defined yet, and // asserts that the type exists. func findEmptyEnum(p *Parser, tag string) *c.EnumType { - newTag := tag - for i := 0; ; i++ { - t, ok := p.Enums[newTag] - if !ok { - panic(fmt.Errorf("unable to locate enum %q", tag)) - } - if len(t.Members) == 0 { - return t + var t *c.EnumType = nil + enums, ok := p.EnumTags[tag] + if ok { + for i := 0; i < len(enums); i++ { + tt := enums[i] + if len(tt.Members) != 0 { continue } + t = tt } - newTag = fmt.Sprintf(duplicateTagFormat, tag, i) } + if t == nil { + panic(fmt.Errorf("unable to locate enum %q", tag)) + } + return t } // parseType parses the SYM type into the equivalent C type.