215 lines
7.8 KiB
Python
Vendored
215 lines
7.8 KiB
Python
Vendored
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}!")
|