//go:build ignore package main import ( "encoding/xml" "fmt" "io" "log" "os" "path/filepath" "sort" "strconv" "strings" "text/template" ) // XMLDefinition represents the root element containing structs type XMLDefinition struct { XMLName xml.Name `xml:"structs"` Structs []XMLStruct `xml:"struct"` } // XMLStruct represents a packet structure definition type XMLStruct struct { Name string `xml:"name,attr"` ClientVersion string `xml:"clientVersion,attr"` OpcodeName string `xml:"opcodeName,attr"` } // Registration represents a packet registration entry type Registration struct { StructName string TypeName string Version uint16 OpcodeName string } // RegistrationData holds all registration data for template type RegistrationData struct { PackageName string Registrations []Registration } const registrationTemplate = `// Code generated by go generate; DO NOT EDIT. // This file was generated from XML packet definitions package {{.PackageName}} import ( "reflect" "git.sharkk.net/EQ2/Protocol/defs/generated" ) // initializePacketMappings loads all packet structure definitions func (pm *PacketManager) initializePacketMappings() { {{range .Registrations}} pm.registerStruct("{{.StructName}}", {{.Version}}, "{{.OpcodeName}}", reflect.TypeOf(generated.{{.TypeName}}{})) {{end}}} ` func main() { // Process all XML files xmlDir := filepath.Join(".", "xml") outputFile := "registrations_generated.go" registrations := []Registration{} // Process common.xml commonFile := filepath.Join(xmlDir, "common.xml") if regs, err := processXMLFile(commonFile); err == nil { registrations = append(registrations, regs...) } else { log.Printf("Warning: Could not process common.xml: %v", err) } // Process login.xml loginFile := filepath.Join(xmlDir, "login.xml") if regs, err := processXMLFile(loginFile); err == nil { registrations = append(registrations, regs...) } else { log.Printf("Warning: Could not process login.xml: %v", err) } // Sort registrations by struct name, then version sort.Slice(registrations, func(i, j int) bool { if registrations[i].StructName != registrations[j].StructName { return registrations[i].StructName < registrations[j].StructName } return registrations[i].Version < registrations[j].Version }) // Generate the registration code if err := generateRegistrationCode(registrations, outputFile); err != nil { log.Fatalf("Failed to generate registration code: %v", err) } log.Printf("Generated %s with %d registrations", outputFile, len(registrations)) } func processXMLFile(filename string) ([]Registration, error) { file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() data, err := io.ReadAll(file) if err != nil { return nil, err } // Wrap in root element if needed xmlContent := string(data) if !strings.Contains(xmlContent, "") { xmlContent = "\n" + xmlContent + "\n" } var def XMLDefinition if err := xml.Unmarshal([]byte(xmlContent), &def); err != nil { return nil, fmt.Errorf("failed to parse XML: %v", err) } registrations := []Registration{} for _, s := range def.Structs { if s.OpcodeName == "" { continue // Skip structs without opcodes } version, err := strconv.ParseUint(s.ClientVersion, 10, 16) if err != nil { log.Printf("Warning: Invalid version %s for %s", s.ClientVersion, s.Name) continue } // Determine the Go type name (remove underscores from struct names) typeName := strings.ReplaceAll(s.Name, "_", "") if version > 1 { typeName = fmt.Sprintf("%sV%d", typeName, version) } registrations = append(registrations, Registration{ StructName: s.Name, TypeName: typeName, Version: uint16(version), OpcodeName: s.OpcodeName, }) } return registrations, nil } func generateRegistrationCode(registrations []Registration, outputFile string) error { tmpl, err := template.New("registration").Parse(registrationTemplate) if err != nil { return fmt.Errorf("failed to parse template: %v", err) } out, err := os.Create(outputFile) if err != nil { return fmt.Errorf("failed to create output file: %v", err) } defer out.Close() data := RegistrationData{ PackageName: "defs", Registrations: registrations, } if err := tmpl.Execute(out, data); err != nil { return fmt.Errorf("failed to execute template: %v", err) } return nil }