Implemented LUT codegen, untested.
This commit is contained in:
179
reference/record_codegen.py
Normal file
179
reference/record_codegen.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import json
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
INDENT = 8
|
||||
WIDTH = 80
|
||||
LAST = "};"
|
||||
SV = "std::string_view"
|
||||
|
||||
PREAMBLE = """#include "esx_reader.hpp"
|
||||
|
||||
using namespace esxr;
|
||||
|
||||
namespace esxr_lut {"""
|
||||
POSTAMBLE = "}\n"
|
||||
|
||||
|
||||
def transpose(seq):
|
||||
return list(zip(*seq))
|
||||
|
||||
def clean(seq):
|
||||
return [x for x in seq if x is not None]
|
||||
|
||||
def indented_block(items):
|
||||
lines = []
|
||||
line = ""
|
||||
for item in items:
|
||||
if len(line) + len(item) + 1 > WIDTH:
|
||||
lines += [line[:-1]]
|
||||
line = ""
|
||||
if not line:
|
||||
line = " " * INDENT
|
||||
line += item + ", "
|
||||
if line.strip():
|
||||
lines += [line]
|
||||
return "\n".join(lines)
|
||||
|
||||
def indented_list(items):
|
||||
return ",\n".join([" " * INDENT + x for x in items])
|
||||
|
||||
def max_length(list):
|
||||
return max(map(lambda x: len(x), list))
|
||||
|
||||
def wrap_bracket(list):
|
||||
return ["{" + string + "}" for string in list]
|
||||
|
||||
def padded_join(lists):
|
||||
maximums = [max_length(x) for x in lists]
|
||||
padded = []
|
||||
for max, l in zip(maximums, lists):
|
||||
padded += [[x + " " * (max - len(x)) for x in l]]
|
||||
padded_transposed = transpose(padded)
|
||||
return ["".join(x) for x in padded_transposed]
|
||||
|
||||
@dataclass
|
||||
class Map:
|
||||
first_type: str
|
||||
second_type: str
|
||||
first_name: str
|
||||
second_name: str
|
||||
formats: list[str]
|
||||
|
||||
F_DECL = "static constexpr std::pair<{0}, {1}> {2}_builtin[] {{"
|
||||
F_SOA = """static constexpr auto {0}_std = utility::array_builtin_to_std({0}_builtin);
|
||||
static constexpr auto {0} = utility::map_to_soa({0}_std);"""
|
||||
F_FUNC_DECL = "[[nodiscard]] constexpr std::optional<{0}> {1}_to_{2}({3} {1}) noexcept"
|
||||
F_FUNC_BODY_FORWARD = " return utility::soa_first_to_second({0}, {1});"
|
||||
F_FUNC_BODY_REVERSE = " return utility::soa_second_to_first({0}, {1});"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.first_name + "_" + self.second_name + "_map"
|
||||
|
||||
@property
|
||||
def decl(self):
|
||||
return Map.F_DECL.format(self.first_type, self.second_type, self.name)
|
||||
|
||||
@property
|
||||
def to_soa(self):
|
||||
return Map.F_SOA.format(self.name)
|
||||
|
||||
@property
|
||||
def forward_decl(self):
|
||||
return Map.F_FUNC_DECL.format(self.second_type, self.first_name, self.second_name, self.first_type)
|
||||
|
||||
@property
|
||||
def reverse_decl(self):
|
||||
return Map.F_FUNC_DECL.format(self.first_type, self.second_name, self.first_name, self.second_type)
|
||||
|
||||
@property
|
||||
def forward_func(self):
|
||||
return self.forward_decl + "\n{\n" + Map.F_FUNC_BODY_FORWARD.format(self.name, self.first_name) + "\n}"
|
||||
|
||||
@property
|
||||
def reverse_func(self):
|
||||
return self.reverse_decl + "\n{\n" + Map.F_FUNC_BODY_REVERSE.format(self.name, self.second_name) + "\n}"
|
||||
|
||||
def data_to_list(self, data):
|
||||
parts = []
|
||||
for format in self.formats:
|
||||
parts += [[format.format(*x) for x in data]]
|
||||
return indented_block(wrap_bracket(padded_join(parts)))
|
||||
|
||||
def code(self, data, reverse = False):
|
||||
parts = [self.decl, self.data_to_list(data), LAST, self.to_soa, self.forward_func]
|
||||
decls = [self.forward_decl]
|
||||
if reverse:
|
||||
parts += [self.reverse_func]
|
||||
decls += [self.reverse_decl]
|
||||
return ["\n".join(parts), "\n".join([x + ';' for x in decls])]
|
||||
|
||||
|
||||
def add_extra(data):
|
||||
grup = {"fourcc": "GRUP", "name": "Group", "flags": []}
|
||||
note = {"fourcc": "NOTE", "name": "Note", "flags": []}
|
||||
data += [grup, note]
|
||||
data.sort(key=lambda x: x["fourcc"])
|
||||
|
||||
def gen_enum(data):
|
||||
# generate sorted list of signatures
|
||||
sigs = [r["fourcc"] for r in data]
|
||||
string = "enum class RecordType {\n" + indented_block(sigs) + LAST
|
||||
return [None, string]
|
||||
|
||||
def gen_group_type(data):
|
||||
map = Map("GroupType", SV, "group_type", "name", ["GroupType::{0}", ", \"{1}\""])
|
||||
data = [(x['type'], x['name']) for x in data]
|
||||
return map.code(data)
|
||||
|
||||
def gen_names(data):
|
||||
map = Map("RecordType", SV, "record_type", "name", ["RecordType::{0}", ", \"{1}\""])
|
||||
data = [(x['fourcc'], x['name']) for x in data]
|
||||
return map.code(data)
|
||||
|
||||
def gen_fourcc(data):
|
||||
map = Map("RecordType", "FourCC", "record_type", "fourcc", ["RecordType::{0}", ", FourCC(\"{0}\")"])
|
||||
data = [(x['fourcc'], ) for x in data]
|
||||
return map.code(data, reverse = True)
|
||||
|
||||
def gen_flags(data):
|
||||
map = Map("Flag", SV, "flag", "description", ["{{RecordType::{0}, {1:>2}}}", ", \"{2}\""])
|
||||
data = [(x['fourcc'], y['bit'], y['description']) for x in data for y in x['flags']]
|
||||
return map.code(data)
|
||||
|
||||
def gen_refr_flags(data):
|
||||
map = Map("RefrFlag", SV, "refr_flag", "description", ["{{RecordType::{0}, {1:>2}}}", ", \"{2}\""])
|
||||
data = [(x['fourcc'], y['bit'], y['description']) for x in data for y in x['flags']]
|
||||
return map.code(data)
|
||||
|
||||
def main():
|
||||
fdir = os.path.dirname(__file__)
|
||||
with open(os.path.join(fdir, "records.json"), "r") as f:
|
||||
data = json.load(f)
|
||||
with open(os.path.join(fdir, "refr_flags.json"), "r") as f:
|
||||
refr_data = json.load(f)
|
||||
with open(os.path.join(fdir, "group_type.json"), "r") as f:
|
||||
gt_data = json.load(f)
|
||||
add_extra(data)
|
||||
code = []
|
||||
code += [gen_enum(data)]
|
||||
code += [gen_group_type(gt_data)]
|
||||
code += [gen_fourcc(data)]
|
||||
code += [gen_names(data)]
|
||||
code += [gen_flags(data)]
|
||||
code += [gen_refr_flags(refr_data)]
|
||||
code = transpose(code)
|
||||
lut = [PREAMBLE] + clean(code[0]) + [POSTAMBLE]
|
||||
lut = "\n\n".join(lut)
|
||||
header = clean(code[1])
|
||||
header = "\n".join(header)
|
||||
with open(os.path.join(fdir, "esx_reader_lut.cpp"), "w") as f:
|
||||
f.write(lut)
|
||||
with open(os.path.join(fdir, "esx_reader.hpp.fragment"), "w") as f:
|
||||
f.write(header)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user