1
0
Protocol/structs/config_reader.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
}