1+ /* *
2+ * =============================================================================
3+ * DumpSource2
4+ * Copyright (C) 2026 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 " json_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+ #include < nlohmann/json.hpp>
31+
32+ using json = nlohmann::json;
33+
34+ namespace Dumpers ::Schemas::JsonExporter
35+ {
36+
37+ json SerializeMetadataArray (const std::vector<IntermediateMetadata>& metadataVector)
38+ {
39+ json arr = json::array ();
40+ for (const auto & metadata : metadataVector)
41+ {
42+ json j;
43+ j[" name" ] = metadata.name ;
44+ if (metadata.hasValue && metadata.stringValue .has_value ())
45+ {
46+ j[" value" ] = *metadata.stringValue ;
47+ }
48+
49+ arr.push_back (j);
50+ }
51+ return arr;
52+ }
53+
54+ json SerializeType (CSchemaType* type)
55+ {
56+ if (!type)
57+ return nullptr ;
58+
59+ json j;
60+
61+ switch (type->m_eTypeCategory )
62+ {
63+ case SCHEMA_TYPE_BUILTIN:
64+ j[" category" ] = " builtin" ;
65+ j[" name" ] = type->m_sTypeName .String ();
66+ break ;
67+ case SCHEMA_TYPE_POINTER:
68+ j[" category" ] = " ptr" ;
69+ j[" inner" ] = SerializeType (static_cast <CSchemaType_Ptr*>(type)->m_pObjectType );
70+ break ;
71+ case SCHEMA_TYPE_FIXED_ARRAY:
72+ {
73+ auto * arr = static_cast <CSchemaType_FixedArray*>(type);
74+ j[" category" ] = " fixed_array" ;
75+ j[" count" ] = arr->m_nElementCount ;
76+ j[" inner" ] = SerializeType (arr->m_pElementType );
77+ break ;
78+ }
79+ case SCHEMA_TYPE_ATOMIC:
80+ {
81+ j[" category" ] = " atomic" ;
82+
83+ // Extract outer name from m_sTypeName before '<'
84+ std::string typeName = type->m_sTypeName .String ();
85+ auto pos = typeName.find (' <' );
86+ if (pos != std::string::npos)
87+ {
88+ auto name = typeName.substr (0 , pos);
89+ // Trim trailing whitespace
90+ while (!name.empty () && name.back () == ' ' )
91+ name.pop_back ();
92+ j[" name" ] = name;
93+ }
94+ else
95+ {
96+ j[" name" ] = typeName;
97+ }
98+
99+ if (type->m_eAtomicCategory == SCHEMA_ATOMIC_T ||
100+ type->m_eAtomicCategory == SCHEMA_ATOMIC_COLLECTION_OF_T)
101+ {
102+ j[" inner" ] = SerializeType (static_cast <CSchemaType_Atomic_T*>(type)->m_pTemplateType );
103+ }
104+ else if (type->m_eAtomicCategory == SCHEMA_ATOMIC_TT)
105+ {
106+ auto * tt = static_cast <CSchemaType_Atomic_TT*>(type);
107+ j[" inner" ] = SerializeType (tt->m_pTemplateType );
108+ j[" inner2" ] = SerializeType (tt->m_pTemplateType2 );
109+ }
110+ break ;
111+ }
112+ case SCHEMA_TYPE_DECLARED_CLASS:
113+ {
114+ j[" category" ] = " declared_class" ;
115+ auto * classType = static_cast <CSchemaType_DeclaredClass*>(type);
116+ if (classType->m_pClassInfo && classType->m_pClassInfo ->m_pszName )
117+ {
118+ j[" name" ] = classType->m_pClassInfo ->m_pszName ;
119+ j[" module" ] = classType->m_pClassInfo ->m_pszProjectName ;
120+ }
121+ else
122+ j[" name" ] = type->m_sTypeName .String ();
123+ break ;
124+ }
125+ case SCHEMA_TYPE_DECLARED_ENUM:
126+ {
127+ j[" category" ] = " declared_enum" ;
128+ auto * enumType = static_cast <CSchemaType_DeclaredEnum*>(type);
129+ if (enumType->m_pEnumInfo && enumType->m_pEnumInfo ->m_pszName )
130+ {
131+ j[" name" ] = enumType->m_pEnumInfo ->m_pszName ;
132+ j[" module" ] = enumType->m_pEnumInfo ->m_pszProjectName ;
133+ }
134+ else
135+ j[" name" ] = type->m_sTypeName .String ();
136+ break ;
137+ }
138+ case SCHEMA_TYPE_BITFIELD:
139+ j[" category" ] = " bitfield" ;
140+ j[" count" ] = static_cast <CSchemaType_Bitfield*>(type)->m_nBitfieldCount ;
141+ break ;
142+ default :
143+ j[" category" ] = " builtin" ;
144+ j[" name" ] = type->m_sTypeName .String ();
145+ break ;
146+ }
147+
148+ return j;
149+ }
150+
151+ void DumpClasses (const std::vector<IntermediateSchemaClass>& classes, json& classesArray)
152+ {
153+ for (const auto & intermediateClass : classes)
154+ {
155+ spdlog::trace (" Dumping class for json: '{}'" , intermediateClass.name );
156+
157+ json classObj;
158+ classObj[" name" ] = intermediateClass.name ;
159+ classObj[" module" ] = intermediateClass.module ;
160+ classObj[" metadata" ] = SerializeMetadataArray (intermediateClass.metadata );
161+
162+ json parents = json::array ();
163+ for (const auto & parent : intermediateClass.parents )
164+ {
165+ json parentObj;
166+ parentObj[" name" ] = parent.name ;
167+ parentObj[" module" ] = parent.module ;
168+ parents.push_back (std::move (parentObj));
169+ }
170+
171+ classObj[" parents" ] = parents;
172+
173+ json fields = json::array ();
174+ for (const auto & field : intermediateClass.fields )
175+ {
176+ spdlog::trace (" Dumping field: '{}' for class: '{}'" , field.name , intermediateClass.name );
177+ json fieldObj;
178+ fieldObj[" name" ] = field.name ;
179+ fieldObj[" offset" ] = field.offset ;
180+ fieldObj[" type" ] = SerializeType (field.type );
181+ fieldObj[" metadata" ] = SerializeMetadataArray (field.metadata );
182+ fields.push_back (fieldObj);
183+ }
184+ classObj[" fields" ] = fields;
185+
186+ classesArray.push_back (std::move (classObj));
187+ }
188+ }
189+
190+ void DumpEnums (const std::vector<IntermediateSchemaEnum>& enums, json& enumsArray)
191+ {
192+ for (const auto & intermediateEnum : enums)
193+ {
194+ json enumObj;
195+ enumObj[" name" ] = intermediateEnum.name ;
196+ enumObj[" module" ] = intermediateEnum.module ;
197+ enumObj[" alignment" ] = intermediateEnum.stringAlignment ;
198+
199+ enumObj[" metadata" ] = SerializeMetadataArray (intermediateEnum.metadata );
200+
201+ json members = json::array ();
202+ for (const auto & member : intermediateEnum.members )
203+ {
204+ json memberObj;
205+ memberObj[" name" ] = member.name ;
206+ memberObj[" value" ] = member.value ;
207+
208+ memberObj[" metadata" ] = SerializeMetadataArray (member.metadata );
209+ members.push_back (std::move (memberObj));
210+ }
211+ enumObj[" members" ] = std::move (members);
212+ enumsArray.push_back (std::move (enumObj));
213+ }
214+ }
215+
216+ void Dump (const std::vector<IntermediateSchemaEnum>& enums, const std::vector<IntermediateSchemaClass>& classes)
217+ {
218+ spdlog::info (" Dumping schemas to json" );
219+
220+ json root;
221+ json classesArray = json::array ();
222+ json enumsArray = json::array ();
223+
224+ DumpClasses (classes, classesArray);
225+ DumpEnums (enums, enumsArray);
226+
227+ root[" classes" ] = classesArray;
228+ root[" enums" ] = enumsArray;
229+
230+ std::ofstream output (Globals::outputPath / " schemas.json" );
231+ output << root.dump (1 );
232+
233+ spdlog::info (" Wrote schemas.json ({} classes, {} enums)" , classesArray.size (), enumsArray.size ());
234+ }
235+
236+ } // namespace Dumpers::Schemas::JsonExporter
0 commit comments