]> git.tdb.fi Git - libs/game.git/blob - tools/setupgen/setupgen.cpp
Add a tool for generating setup structs and loaders for them
[libs/game.git] / tools / setupgen / setupgen.cpp
1 #include "setupgen.h"
2 #include <msp/core/getopt.h>
3 #include <msp/core/maputils.h>
4 #include <msp/fs/utils.h>
5 #include <msp/io/console.h>
6 #include <msp/io/print.h>
7 #include <msp/strings/utils.h>
8
9 using namespace std;
10 using namespace Msp;
11
12 SetupGen::SetupGen(int argc, char **argv)
13 {
14         GetOpt getopt;
15         getopt.add_option('o', "output", out_fn, GetOpt::REQUIRED_ARG);
16         getopt.add_argument("input_file", in_fn, GetOpt::REQUIRED_ARG);
17         getopt(argc, argv);
18 }
19
20 int SetupGen::main()
21 {
22         create_standard_types();
23         load();
24
25         collect_headers();
26
27         if(out_fn.empty())
28         {
29                 generate_header(IO::cout);
30                 generate_code(IO::cout);
31         }
32         else
33         {
34                 string out_base = FS::basename(out_fn);
35                 string out_ext = FS::extpart(out_base);
36                 if(out_ext==".cpp" || out_ext==".h")
37                 {
38                         FS::Path out_dir = FS::dirname(out_fn);
39                         out_base = FS::basepart(out_base);
40                         IO::BufferedFile header_out((out_dir/(out_base+".h")).str(), IO::M_WRITE);
41                         generate_header(header_out);
42                         IO::BufferedFile code_out((out_dir/(out_base+".cpp")).str(), IO::M_WRITE);
43                         IO::print(code_out, "#include \"%s.h\"\n", out_base);
44                         if(!enums.empty())
45                                 IO::print(code_out, "#include <msp/strings/format.h>\n");
46                         generate_code(code_out);
47                 }
48                 else
49                 {
50                         IO::BufferedFile out(out_fn, IO::M_WRITE);
51                         generate_header(out);
52                         out.put('\n');
53                         generate_code(out);
54                 }
55         }
56
57         return 0;
58 }
59
60 void SetupGen::create_standard_types()
61 {
62         add_type("bool", Type::VALUE);
63         add_type("int", Type::VALUE);
64         add_type("uint", Type::VALUE).set_cpp_type("unsigned");
65         const Type &float_type = add_type("float", Type::VALUE);
66         add_type("string", Type::VALUE).set_cpp_type("std::string", "string");
67         add_type("angle", Type::VALUE).set_cpp_type("Msp::Geometry::Angle<float>", "msp/geometry/angle.h")
68                 .set_load("float", "Msp::Geometry::Angle<float>::from_degrees");
69         add_type("vector2", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 2>", "msp/linal/vector.h")
70                 .set_elements(float_type, 2);
71         add_type("vector3", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 3>", "msp/linal/vector.h")
72                 .set_elements(float_type, 3);
73 }
74
75 Type &SetupGen::add_type(const string &n, Type::Kind k)
76 {
77         auto result = types.try_emplace(n, n, k);
78         if(!result.second)
79                 throw key_error(n);
80         return result.first->second;
81 }
82
83 const Type &SetupGen::get_type(const string &n) const
84 {
85         return get_item(types, n);
86 }
87
88 void SetupGen::load()
89 {
90         IO::BufferedFile in_file(in_fn);
91         DataFile::Parser parser(in_file, in_fn);
92         Loader ldr(*this);
93         ldr.load(parser);
94 }
95
96 void SetupGen::collect_headers()
97 {
98         headers.insert("msp/datafile/objectloader.h");
99         headers.insert("msp/strings/lexicalcast.h");
100         for(const unique_ptr<Struct> &s: structs)
101                 for(const Struct::Field &f: s->get_fields())
102                         if(const string &h = f.type->get_header(); !h.empty())
103                                 headers.insert(h);
104 }
105
106 void SetupGen::generate_header(IO::Base &out) const
107 {
108         string guard = toupper(FS::basepart(FS::basename(out_fn.empty() ? in_fn : out_fn)))+"_H_";
109         if(!name_space.empty())
110         {
111                 vector<string> parts = split(name_space, "::");
112                 parts.emplace_back(move(guard));
113                 guard = join(parts.begin(), parts.end(), "_");
114         }
115         IO::print(out, "#ifndef %s\n", guard);
116         IO::print(out, "#define %s\n", guard);
117
118         if(!headers.empty())
119         {
120                 out.put('\n');
121                 for(const string &h: headers)
122                         IO::print(out, "#include <%s>\n", h);
123         }
124
125         if(!name_space.empty())
126                 IO::print(out, "\nnamespace %s {\n", name_space);
127
128         for(const unique_ptr<Enum> &e: enums)
129         {
130                 out.put('\n');
131                 e->define_type(out);
132         }
133
134         for(const unique_ptr<Struct> &s: structs)
135         {
136                 out.put('\n');
137                 s->define_type(out);
138         }
139
140         for(const unique_ptr<Struct> &s: structs)
141         {
142                 out.put('\n');
143                 s->define_loader(out);
144         }
145
146         for(const unique_ptr<Enum> &e: enums)
147         {
148                 out.put('\n');
149                 e->declare_conversions(out);
150         }
151
152         if(!name_space.empty())
153                 IO::print(out, "\n}  // namespace %s\n", name_space);
154
155         IO::print(out, "\n#endif\n");
156 }
157
158 void SetupGen::generate_code(IO::Base &out) const
159 {
160         if(!name_space.empty())
161                 IO::print(out, "\nnamespace %s {\n", name_space);
162
163         for(const unique_ptr<Struct> &s: structs)
164         {
165                 out.put('\n');
166                 s->define_functions(out);
167         }
168
169         for(const unique_ptr<Enum> &e: enums)
170         {
171                 out.put('\n');
172                 e->define_conversions(out);
173         }
174
175         if(!name_space.empty())
176                 IO::print(out, "\n}  // namespace %s\n", name_space);
177 }
178
179
180 SetupGen::Loader::Loader(SetupGen &s):
181         ObjectLoader<SetupGen>(s)
182 {
183         static ActionMap shared_actions;
184         set_actions(shared_actions);
185 }
186
187 void SetupGen::Loader::init_actions()
188 {
189         add("component", &Loader::struct_def, Struct::COMPONENT);
190         add("entity", &Loader::struct_def, Struct::ENTITY);
191         add("enum", &Loader::enum_def);
192         add("namespace", &Loader::name_space);
193 }
194
195 void SetupGen::Loader::enum_def(const DataFile::Symbol &n)
196 {
197         Enum en(n.name);
198         load_sub(en);
199         Type &type = obj.add_type(n.name, Type::ENUM);
200         type.set_enum(*obj.enums.emplace_back(make_unique<Enum>(move(en))));
201 }
202
203 void SetupGen::Loader::name_space(const string &ns)
204 {
205         obj.name_space = ns;
206 }
207
208 void SetupGen::Loader::struct_def(Struct::Kind kind, const DataFile::Symbol &n)
209 {
210         Struct sct(n.name+"Setup", kind);
211         load_sub(sct, obj);
212         Type &type = obj.add_type(n.name, Type::STRUCT);
213         type.set_struct(*obj.structs.emplace_back(make_unique<Struct>(move(sct))));
214 }