177 lines
4.4 KiB
Go
177 lines
4.4 KiB
Go
package structs
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
// ConfigReader provides a compatible interface to the C++ ConfigReader
|
|
// It manages packet struct definitions and creates instances for specific versions
|
|
type ConfigReader struct {
|
|
parser *Parser
|
|
structs map[string][]*PacketVersion // Maps packet name to versions
|
|
mu sync.RWMutex
|
|
basePath string
|
|
}
|
|
|
|
// NewConfigReader creates a new config reader
|
|
func NewConfigReader(basePath string) *ConfigReader {
|
|
return &ConfigReader{
|
|
parser: NewParser(basePath),
|
|
structs: make(map[string][]*PacketVersion),
|
|
basePath: basePath,
|
|
}
|
|
}
|
|
|
|
// LoadStructs loads all packet structures from XML files
|
|
func (cr *ConfigReader) LoadStructs() error {
|
|
// Use the structs/xml subdirectory
|
|
xmlPath := filepath.Join(cr.basePath, "structs")
|
|
cr.parser = NewParser(xmlPath)
|
|
|
|
// Try to load all, but don't fail if directory doesn't exist
|
|
cr.parser.LoadAll()
|
|
|
|
return cr.buildVersionMap()
|
|
}
|
|
|
|
// LoadFiles loads specific XML files
|
|
func (cr *ConfigReader) LoadFiles(files []string) error {
|
|
for _, file := range files {
|
|
if err := cr.parser.LoadFile(file); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return cr.buildVersionMap()
|
|
}
|
|
|
|
// buildVersionMap builds the internal version map from loaded packets
|
|
func (cr *ConfigReader) buildVersionMap() error {
|
|
cr.mu.Lock()
|
|
defer cr.mu.Unlock()
|
|
|
|
// Build version map
|
|
cr.structs = make(map[string][]*PacketVersion)
|
|
|
|
for _, name := range cr.parser.ListPackets() {
|
|
def, _ := cr.parser.GetPacket(name)
|
|
versions := make([]*PacketVersion, len(def.Versions))
|
|
for i := range def.Versions {
|
|
versions[i] = &def.Versions[i]
|
|
}
|
|
cr.structs[name] = versions
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReloadStructs reloads all packet structures
|
|
func (cr *ConfigReader) ReloadStructs() error {
|
|
return cr.LoadStructs()
|
|
}
|
|
|
|
// GetStruct returns a packet struct for the best matching version
|
|
// This matches the C++ ConfigReader::getStruct behavior
|
|
func (cr *ConfigReader) GetStruct(name string, version uint16) (*PacketStruct, error) {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
versions, exists := cr.structs[name]
|
|
if !exists {
|
|
return nil, fmt.Errorf("struct %s not found", name)
|
|
}
|
|
|
|
// Find the best matching version (highest version <= requested)
|
|
var bestVersion *PacketVersion
|
|
for _, v := range versions {
|
|
if v.Version <= version {
|
|
if bestVersion == nil || v.Version > bestVersion.Version {
|
|
bestVersion = v
|
|
}
|
|
}
|
|
}
|
|
|
|
if bestVersion == nil {
|
|
return nil, fmt.Errorf("no suitable version found for struct %s version %d", name, version)
|
|
}
|
|
|
|
return NewPacketStruct(bestVersion), nil
|
|
}
|
|
|
|
// GetStructByVersion returns a packet struct for an exact version match
|
|
// This matches the C++ ConfigReader::getStructByVersion behavior
|
|
func (cr *ConfigReader) GetStructByVersion(name string, version uint16) (*PacketStruct, error) {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
versions, exists := cr.structs[name]
|
|
if !exists {
|
|
return nil, fmt.Errorf("struct %s not found", name)
|
|
}
|
|
|
|
// Find exact version match
|
|
for _, v := range versions {
|
|
if v.Version == version {
|
|
return NewPacketStruct(v), nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("struct %s with exact version %d not found", name, version)
|
|
}
|
|
|
|
// GetStructVersion returns the best matching version number for a struct
|
|
func (cr *ConfigReader) GetStructVersion(name string, version uint16) (uint16, error) {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
versions, exists := cr.structs[name]
|
|
if !exists {
|
|
return 0, fmt.Errorf("struct %s not found", name)
|
|
}
|
|
|
|
// Find the best matching version
|
|
var bestVersion uint16
|
|
for _, v := range versions {
|
|
if v.Version <= version && v.Version > bestVersion {
|
|
bestVersion = v.Version
|
|
}
|
|
}
|
|
|
|
if bestVersion == 0 {
|
|
return 0, fmt.Errorf("no suitable version found for struct %s version %d", name, version)
|
|
}
|
|
|
|
return bestVersion, nil
|
|
}
|
|
|
|
// ListStructs returns a list of all loaded struct names
|
|
func (cr *ConfigReader) ListStructs() []string {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
names := make([]string, 0, len(cr.structs))
|
|
for name := range cr.structs {
|
|
names = append(names, name)
|
|
}
|
|
return names
|
|
}
|
|
|
|
// GetVersions returns all available versions for a struct
|
|
func (cr *ConfigReader) GetVersions(name string) ([]uint16, error) {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
versions, exists := cr.structs[name]
|
|
if !exists {
|
|
return nil, fmt.Errorf("struct %s not found", name)
|
|
}
|
|
|
|
result := make([]uint16, len(versions))
|
|
for i, v := range versions {
|
|
result[i] = v.Version
|
|
}
|
|
|
|
return result, nil
|
|
} |