import os import re import sys sys.stdout.reconfigure(encoding='utf-8') src_dir = r"C:\Users\Phitchayut\Downloads\Ananta_PS_FullBuild\Ananta_PS_FullBuild\Drmk.Server\Drmk.Core\ClientData\Client4229938" dst_dir = r"C:\Users\Phitchayut\Desktop\ngp-Ananta-DEV5\ngp-Ananta-DEV5\internal\catalogs" os.makedirs(dst_dir, exist_ok=True) files = [f for f in os.listdir(src_dir) if f.endswith(".cs")] print(f"Converting {len(files)} catalog files to Go with multi-line parser...") TYPE_MAP = { 'uint': 'uint32', 'int': 'int32', 'ulong': 'uint64', 'long': 'int64', 'ushort': 'uint16', 'short': 'int16', 'byte': 'uint8', 'sbyte': 'int8', 'float': 'float32', 'double': 'float64', 'bool': 'bool', 'string': 'string', 'Vector3': 'protocol.Vec3', 'UXVector3': 'protocol.Vec3', } def map_type(t): t = t.strip() if t.endswith('[]'): elem = map_type(t[:-2]) return f"[]{elem}" if t.endswith('?'): return f"*{map_type(t[:-1])}" if t.startswith('List<') or t.startswith('IList<') or t.startswith('IReadOnlyList<'): inner = t[t.find('<')+1 : t.rfind('>')] return f"[]{map_type(inner)}" if t.startswith('Dictionary<') or t.startswith('IReadOnlyDictionary<'): parts = t[t.find('<')+1 : t.rfind('>')].split(',') if len(parts) == 2: return f"map[{map_type(parts[0])}]{map_type(parts[1])}" if t.startswith('HashSet<') or t.startswith('IReadOnlySet<'): inner = t[t.find('<')+1 : t.rfind('>')] return f"map[{map_type(inner)}]struct{{}}" return TYPE_MAP.get(t, t) def clean_val(val, ftype): v = val.strip().rstrip(';') if ftype in ('float32', 'float64'): v = re.sub(r'([0-9]+(?:\.[0-9]+)?)[fFdD]', r'\1', v) if '.' not in v: v += ".0" elif ftype in ('uint32', 'uint64', 'int32', 'int64'): v = re.sub(r'([0-9]+)[uUlL]', r'\1', v) return v def to_go_filename(cs_name): base = cs_name[:-3] if base.endswith("4229938"): base = base[:-7] s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', base) s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() return f"{s2}.go" converted_count = 0 for f in files: cs_path = os.path.join(src_dir, f) with open(cs_path, "r", encoding="utf-8", errors="ignore") as fp: raw_code = fp.read() go_name = to_go_filename(f) go_path = os.path.join(dst_dir, go_name) records = [] constants = [] comments = [] config_file = "" lines = raw_code.splitlines() for line in lines: sline = line.strip() if sline.startswith("///"): comments.append("// " + sline[3:].strip()) elif sline.startswith("//"): comments.append(sline) cfg_m = re.search(r'["\']([A-Za-z0-9_]+\.json)["\']', line) if cfg_m and not config_file: config_file = cfg_m.group(1) # Look for const const_m = re.search(r'const\s+([A-Za-z0-9_]+)\s+([A-Za-z0-9_]+)\s*=\s*([^;]+);', line) if const_m: ctype = map_type(const_m.group(1)) cname = const_m.group(2) cval = clean_val(const_m.group(3), ctype) constants.append((cname, ctype, cval)) # Match all multi-line and single-line records: record struct Name(...) rec_matches = re.finditer(r'record\s+(?:struct\s+)?([A-Za-z0-9_]+)\s*\((.*?)\);', raw_code, re.DOTALL) for rm in rec_matches: rname = rm.group(1) raw_params = rm.group(2) # Parse params fields = [] for p in re.split(r',\s*(?![^<]*>)', raw_params): p = p.strip() if not p: continue parts = p.split() if len(parts) >= 2: ftype = map_type(" ".join(parts[:-1])) fname = parts[-1] jtag = fname[0].lower() + fname[1:] fields.append(f"\t{fname} {ftype} `json:\"{jtag}\"`") records.append((rname, fields)) base_name = f[:-3] if base_name.endswith("4229938"): base_name = base_name[:-7] if base_name.endswith("Catalog"): catalog_struct_name = base_name else: catalog_struct_name = f"{base_name}Catalog" content = [ "package catalogs", "", "import (", "\t\"encoding/json\"", "\t\"os\"", "\t\"path/filepath\"", "\t\"sync\"", "\t\"ananta-ps/pkg/protocol\"", ")", "", "// Suppress unused imports", "var _ = func() bool {", "\t_ = json.Marshal", "\t_ = os.Stat", "\t_ = filepath.Join", "\t_ = protocol.MethodCheckVersion", "\treturn true", "}()", "", ] if comments: content.extend(comments[:15]) content.append("") if constants: content.append("const (") for cname, ctype, cval in constants: content.append(f"\t{cname} {ctype} = {cval}") content.append(")") content.append("") item_struct_name = f"{base_name}Item" if records: for rname, fields in records: content.append(f"type {rname} struct {{") content.extend(fields) content.append("}") content.append("") item_struct_name = records[0][0] else: content.append(f"type {item_struct_name} struct {{") content.append("\tID uint32 `json:\"id\"`") content.append("\tName string `json:\"name,omitempty\"`") content.append("\tRaw map[string]interface{} `json:\"-\"`") content.append("}") content.append("") content.append(f"type {catalog_struct_name} struct {{") content.append("\tmu sync.RWMutex") content.append(f"\titems []{item_struct_name}") content.append(f"\tbyId map[uint32]{item_struct_name}") content.append("\tloaded bool") content.append("}") content.append("") content.append(f"var Global{catalog_struct_name} = &{catalog_struct_name}{{") content.append(f"\tbyId: make(map[uint32]{item_struct_name}),") content.append("}") content.append("") content.append(f"func (c *{catalog_struct_name}) Load(baseDir string) error {{") content.append("\tc.mu.Lock()") content.append("\tdefer c.mu.Unlock()") content.append("\tif c.loaded { return nil }") if config_file: content.append(f"\tpath := filepath.Join(baseDir, \"data\", \"client_data\", \"Configs\", \"{config_file}\")") content.append("\tif _, err := os.Stat(path); os.IsNotExist(err) {") content.append(f"\t\tpath = filepath.Join(baseDir, \"data\", \"client_data\", \"World\", \"{config_file}\")") content.append("\t}") content.append("\tdata, err := os.ReadFile(path)") content.append("\tif err != nil { return err }") content.append("\tvar records struct { Records []json.RawMessage `json:\"records\"` }") content.append("\tif err := json.Unmarshal(data, &records); err == nil && len(records.Records) > 0 {") content.append(f"\t\tfor _, raw := range records.Records {{") content.append(f"\t\t\tvar item {item_struct_name}") content.append("\t\t\tif err := json.Unmarshal(raw, &item); err == nil {") content.append("\t\t\t\tc.items = append(c.items, item)") content.append("\t\t\t}") content.append("\t\t}") content.append("\t}") content.append("\tc.loaded = true") content.append("\treturn nil") content.append("}") content.append("") content.append(f"func (c *{catalog_struct_name}) All() []{item_struct_name} {{") content.append("\tc.mu.RLock()") content.append("\tdefer c.mu.RUnlock()") content.append("\treturn c.items") content.append("}") content.append("") with open(go_path, "w", encoding="utf-8") as out_fp: out_fp.write("\n".join(content) + "\n") converted_count += 1 print(f"Successfully converted {converted_count} catalog files to {dst_dir}!")