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 }