1+ /* *
2+ * =============================================================================
3+ * DumpSource2
4+ * Copyright (C) 2024 ValveResourceFormat Contributors
5+ * =============================================================================
6+ *
7+ * This program is free software; you can redistribute it and/or modify it under
8+ * the terms of the GNU General Public License, version 3.0, as published by the
9+ * Free Software Foundation.
10+ *
11+ * This program is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+ * details.
15+ *
16+ * You should have received a copy of the GNU General Public License along with
17+ * this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+ #include " filesystem_exporter.h"
21+ #include " globalvariables.h"
22+ #include " interfaces.h"
23+ #include < filesystem>
24+ #include < fstream>
25+ #include < map>
26+ #include < unordered_set>
27+ #include < algorithm>
28+ #include < optional>
29+ #include < spdlog/spdlog.h>
30+
31+ namespace Dumpers ::Schemas::FilesystemExporter
32+ {
33+
34+ std::string CommentBlock (std::string str)
35+ {
36+ size_t pos = 0 ;
37+ while ((pos = str.find (' \n ' , pos)) != std::string::npos) {
38+ str.replace (pos, 1 , " \n //" );
39+ pos += 3 ;
40+ }
41+
42+ return str;
43+ }
44+
45+
46+ void OutputMetadataEntry (const IntermediateMetadata& entry, std::ofstream& output, bool tabulate)
47+ {
48+ output << (tabulate ? " \t " : " " ) << " // " << entry.name ;
49+
50+ if (entry.hasValue ) {
51+ if (entry.stringValue ) {
52+ output << " = " << CommentBlock (*entry.stringValue );
53+ }
54+ else
55+ output << " (UNKNOWN FOR PARSER)" ;
56+ }
57+
58+ output << " \n " ;
59+ }
60+
61+ void DumpClasses (const std::vector<IntermediateSchemaClass>& classes, std::filesystem::path schemaPath, std::map<std::string, std::unordered_set<std::string>>& foundFiles)
62+ {
63+ for (const auto & intermediateClass : classes)
64+ {
65+ if (!std::filesystem::is_directory (schemaPath / intermediateClass.module ))
66+ if (!std::filesystem::create_directory (schemaPath / intermediateClass.module ))
67+ return ;
68+
69+ // Some classes have :: in them which we can't save.
70+ auto sanitizedFileName = intermediateClass.name ;
71+ std::replace (sanitizedFileName.begin (), sanitizedFileName.end (), ' :' , ' _' );
72+
73+ // We save the file in a map so that we know which files are outdated and should be removed
74+ foundFiles[intermediateClass.module ].insert (sanitizedFileName);
75+
76+ std::ofstream output ((schemaPath / intermediateClass.module / sanitizedFileName).replace_extension (" .h" ));
77+
78+ // Output metadata entries as comments before the class definition
79+ spdlog::trace (" Dumping class: '{}'" , intermediateClass.name );
80+ for (const auto & metadata : intermediateClass.metadata )
81+ {
82+ OutputMetadataEntry (metadata, output, false );
83+ }
84+
85+ output << " class " << intermediateClass.name ;
86+ Globals::stringsIgnoreStream << intermediateClass.name << " \n " ;
87+
88+ bool wroteBase = false ;
89+ for (const auto & parent : intermediateClass.parents )
90+ {
91+ if (!wroteBase)
92+ {
93+ output << " : public " << parent.name ;
94+ wroteBase = true ;
95+ }
96+ else
97+ {
98+ output << " , public " << parent.name ;
99+ }
100+ }
101+
102+ output << " \n {\n " ;
103+
104+ for (const auto & field : intermediateClass.fields )
105+ {
106+ spdlog::trace (" Dumping field: '{}' for class: '{}'" , field.name , intermediateClass.name );
107+ // Output metadata entires as comments before the field definition
108+ for (const auto & metadata : field.metadata )
109+ {
110+ OutputMetadataEntry (metadata, output, true );
111+ }
112+
113+ output << " \t " << field.type ->m_sTypeName .String () << " " << field.name << " ;\n " ;
114+ Globals::stringsIgnoreStream << field.name << " \n " ;
115+ }
116+
117+ output << " };\n " ;
118+ }
119+ }
120+
121+ void DumpEnums (const std::vector<IntermediateSchemaEnum>& enums, std::filesystem::path schemaPath, std::map<std::string, std::unordered_set<std::string>>& foundFiles)
122+ {
123+ for (const auto & intermediateEnum : enums)
124+ {
125+ if (!std::filesystem::is_directory (schemaPath / intermediateEnum.module ))
126+ if (!std::filesystem::create_directory (schemaPath / intermediateEnum.module ))
127+ return ;
128+
129+ // Some classes have :: in them which we can't save.
130+ auto sanitizedFileName = intermediateEnum.name ;
131+ std::replace (sanitizedFileName.begin (), sanitizedFileName.end (), ' :' , ' _' );
132+
133+ // We save the file in a map so that we know which files are outdated and should be removed
134+ foundFiles[intermediateEnum.module ].insert (sanitizedFileName);
135+
136+ std::ofstream output ((schemaPath / intermediateEnum.module / sanitizedFileName).replace_extension (" .h" ));
137+
138+ for (const auto & metadata : intermediateEnum.metadata )
139+ {
140+ OutputMetadataEntry (metadata, output, false );
141+ }
142+
143+ output << " enum " << intermediateEnum.name << " : " << (intermediateEnum.stringAlignment .has_value () ? *intermediateEnum.stringAlignment : " unknown alignment type" ) << " \n {\n " ;
144+ Globals::stringsIgnoreStream << intermediateEnum.name << " \n " ;
145+
146+ for (const auto & member : intermediateEnum.members )
147+ {
148+ // Output metadata entires as comments before the field definition
149+ for (const auto & metadata: member.metadata )
150+ {
151+ OutputMetadataEntry (metadata, output, true );
152+ }
153+
154+ output << " \t " << member.name << " = " << member.value << " ,\n " ;
155+ Globals::stringsIgnoreStream << member.name << " \n " ;
156+ }
157+
158+ output << " };\n " ;
159+ }
160+ }
161+
162+ void Dump (const std::vector<IntermediateSchemaEnum>& enums, const std::vector<IntermediateSchemaClass>& classes)
163+ {
164+ spdlog::info (" Dumping schemas to filesystem" );
165+ const auto schemaPath = Globals::outputPath / " schemas" ;
166+
167+ if (!std::filesystem::is_directory (schemaPath))
168+ if (!std::filesystem::create_directory (schemaPath))
169+ return ;
170+
171+ std::map<std::string, std::unordered_set<std::string>> foundFiles;
172+
173+ DumpClasses (classes, schemaPath, foundFiles);
174+ DumpEnums (enums, schemaPath, foundFiles);
175+
176+ for (const auto & entry : std::filesystem::directory_iterator (schemaPath))
177+ {
178+ auto projectName = entry.path ().filename ().string ();
179+ bool isInMap = foundFiles.find (projectName) != foundFiles.end ();
180+
181+ if (entry.is_directory () && !isInMap)
182+ {
183+ spdlog::info (" Removing orphan schema folder {}" , entry.path ().generic_string ());
184+ std::filesystem::remove_all (entry.path ());
185+ }
186+ else if (isInMap)
187+ {
188+
189+ for (const auto & typeScopePath : std::filesystem::directory_iterator (entry.path ()))
190+ {
191+ auto & filesSet = foundFiles[projectName];
192+
193+ if (filesSet.find (typeScopePath.path ().stem ().string ()) == filesSet.end ())
194+ {
195+ spdlog::info (" Removing orphan schema file {}" , typeScopePath.path ().generic_string ());
196+ std::filesystem::remove (typeScopePath.path ());
197+ }
198+ }
199+ }
200+ }
201+ }
202+
203+ } // namespace Dumpers::Schemas::FilesystemExporter
0 commit comments