@@ -158,180 +158,6 @@ namespace RC::UVTD
158158 }
159159
160160
161- // Information about a versioned bitfield for adaptive getter generation
162- struct VersionedBitfieldInfo
163- {
164- File::StringType storage_type;
165- uint8_t bit_position;
166- uint8_t bit_length;
167- int32_t major_version;
168- int32_t minor_version;
169- };
170-
171- // Helper to format mask as hex string
172- static auto format_hex_mask (uint64_t mask) -> File::StringType
173- {
174- return std::format (STR (" 0x{:X}" ), mask);
175- }
176-
177- // Helper to generate a version-adaptive bitfield getter body
178- // This handles cases where bit position or storage type changes between versions
179- static auto generate_versioned_bitfield_getter_body (
180- Output::Targets<Output::NewFileDevice>& dumper,
181- const File::StringType& class_name,
182- const File::StringType& variable_name,
183- const std::vector<VersionedBitfieldInfo>& versions) -> void
184- {
185- dumper.send (STR (" {{\n " ));
186- dumper.send (STR (" static const int32_t offset = []() -> int32_t {{\n " ));
187- dumper.send (STR (" auto offset_it = MemberOffsets.find(STR(\" {}\" ));\n " ), variable_name);
188- dumper.send (STR (
189- " if (offset_it == MemberOffsets.end()) {{ throw std::runtime_error{{\" Tried getting member variable '{}::{}' "
190- " that doesn't exist in this engine version.\" }}; }}\n " ),
191- class_name,
192- variable_name);
193- dumper.send (STR (" return offset_it->second;\n " ));
194- dumper.send (STR (" }}();\n\n " ));
195-
196- // Generate version checks from newest to oldest
197- bool first = true ;
198- for (auto it = versions.rbegin (); it != versions.rend (); ++it)
199- {
200- const auto & ver = *it;
201- uint64_t mask = (1ULL << ver.bit_length ) - 1 ;
202- File::StringType mask_hex = format_hex_mask (mask);
203-
204- if (first)
205- {
206- // For the newest version (checked first at runtime)
207- auto next_it = it;
208- ++next_it;
209- if (next_it != versions.rend ())
210- {
211- dumper.send (STR (" if (Version::IsAtLeast({}, {})) {{\n " ), ver.major_version , ver.minor_version );
212- dumper.send (STR (" {} storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
213- if (ver.bit_length == 1 )
214- {
215- dumper.send (STR (" return (storage >> {}) & 1;\n " ), ver.bit_position );
216- }
217- else
218- {
219- dumper.send (STR (" return (storage >> {}) & {};\n " ), ver.bit_position , mask_hex);
220- }
221- dumper.send (STR (" }}\n " ));
222- }
223- else
224- {
225- // Only one version - no need for version check
226- dumper.send (STR (" {} storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
227- if (ver.bit_length == 1 )
228- {
229- dumper.send (STR (" return (storage >> {}) & 1;\n " ), ver.bit_position );
230- }
231- else
232- {
233- dumper.send (STR (" return (storage >> {}) & {};\n " ), ver.bit_position , mask_hex);
234- }
235- }
236- first = false ;
237- }
238- else
239- {
240- // Check if this is the last (oldest) version
241- auto next_it = it;
242- ++next_it;
243- if (next_it == versions.rend ())
244- {
245- // Last version - use else
246- dumper.send (STR (" else {{\n " ));
247- }
248- else
249- {
250- dumper.send (STR (" else if (Version::IsAtLeast({}, {})) {{\n " ), ver.major_version , ver.minor_version );
251- }
252- dumper.send (STR (" {} storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
253- if (ver.bit_length == 1 )
254- {
255- dumper.send (STR (" return (storage >> {}) & 1;\n " ), ver.bit_position );
256- }
257- else
258- {
259- dumper.send (STR (" return (storage >> {}) & {};\n " ), ver.bit_position , mask_hex);
260- }
261- dumper.send (STR (" }}\n " ));
262- }
263- }
264- dumper.send (STR (" }}\n " ));
265- }
266-
267- // Helper to generate a version-adaptive bitfield setter body
268- static auto generate_versioned_bitfield_setter_body (
269- Output::Targets<Output::NewFileDevice>& dumper,
270- const File::StringType& class_name,
271- const File::StringType& variable_name,
272- const std::vector<VersionedBitfieldInfo>& versions) -> void
273- {
274- dumper.send (STR (" {{\n " ));
275- dumper.send (STR (" static const int32_t offset = []() -> int32_t {{\n " ));
276- dumper.send (STR (" auto offset_it = MemberOffsets.find(STR(\" {}\" ));\n " ), variable_name);
277- dumper.send (STR (
278- " if (offset_it == MemberOffsets.end()) {{ throw std::runtime_error{{\" Tried setting member variable '{}::{}' "
279- " that doesn't exist in this engine version.\" }}; }}\n " ),
280- class_name,
281- variable_name);
282- dumper.send (STR (" return offset_it->second;\n " ));
283- dumper.send (STR (" }}();\n\n " ));
284-
285- // Generate version checks from newest to oldest
286- bool first = true ;
287- for (auto it = versions.rbegin (); it != versions.rend (); ++it)
288- {
289- const auto & ver = *it;
290- uint64_t mask = (1ULL << ver.bit_length ) - 1 ;
291- File::StringType mask_hex = format_hex_mask (mask);
292-
293- if (first)
294- {
295- auto next_it = it;
296- ++next_it;
297- if (next_it != versions.rend ())
298- {
299- dumper.send (STR (" if (Version::IsAtLeast({}, {})) {{\n " ), ver.major_version , ver.minor_version );
300- dumper.send (STR (" {}& storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
301- dumper.send (STR (" storage = (storage & ~(static_cast<{}>({}) << {})) | ((static_cast<{}>(value) & {}) << {});\n " ),
302- ver.storage_type , mask_hex, ver.bit_position , ver.storage_type , mask_hex, ver.bit_position );
303- dumper.send (STR (" }}\n " ));
304- }
305- else
306- {
307- // Only one version
308- dumper.send (STR (" {}& storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
309- dumper.send (STR (" storage = (storage & ~(static_cast<{}>({}) << {})) | ((static_cast<{}>(value) & {}) << {});\n " ),
310- ver.storage_type , mask_hex, ver.bit_position , ver.storage_type , mask_hex, ver.bit_position );
311- }
312- first = false ;
313- }
314- else
315- {
316- auto next_it = it;
317- ++next_it;
318- if (next_it == versions.rend ())
319- {
320- dumper.send (STR (" else {{\n " ));
321- }
322- else
323- {
324- dumper.send (STR (" else if (Version::IsAtLeast({}, {})) {{\n " ), ver.major_version , ver.minor_version );
325- }
326- dumper.send (STR (" {}& storage = *Helper::Casting::ptr_cast<{}*>(this, offset);\n " ), ver.storage_type , ver.storage_type );
327- dumper.send (STR (" storage = (storage & ~(static_cast<{}>({}) << {})) | ((static_cast<{}>(value) & {}) << {});\n " ),
328- ver.storage_type , mask_hex, ver.bit_position , ver.storage_type , mask_hex, ver.bit_position );
329- dumper.send (STR (" }}\n " ));
330- }
331- }
332- dumper.send (STR (" }}\n\n " ));
333- }
334-
335161 // Information about a versioned getter that needs a public wrapper
336162 struct VersionedGetterInfo
337163 {
@@ -501,7 +327,9 @@ namespace RC::UVTD
501327 // Check if this variable has version-specific type changes
502328 // If so, we generate versioned getters instead of a single getter
503329 // Versioned getters are always private - public wrappers will be added later
504- bool has_versioned_getters = variable.has_type_changes () && !inheritance_info.has_value ();
330+ // Versioned bitfields are handled by BitfieldProxy at runtime (storage_size, bit_pos, bit_len from INI),
331+ // so they don't need versioned getters
332+ bool has_versioned_getters = variable.has_type_changes () && !inheritance_info.has_value () && !variable.is_bitfield ;
505333
506334 // Count how many unique getters we'll actually generate after normalization
507335 // Types that normalize to the same thing (e.g., FDefaultAllocator vs TSizedDefaultAllocator<32>,
@@ -510,30 +338,8 @@ namespace RC::UVTD
510338 std::set<File::StringType> seen_normalized_types;
511339 File::StringType best_type_for_single_getter; // The "best" type to use if we only need one getter
512340
513- // Check if all versioned types are bitfields - if so, we can generate an adaptive getter
514- bool all_versions_are_bitfields = false ;
515- std::vector<VersionedBitfieldInfo> versioned_bitfield_infos;
516-
517341 if (has_versioned_getters)
518342 {
519- all_versions_are_bitfields = true ;
520- for (const auto & [version_key, versioned_type] : variable.types_by_version )
521- {
522- if (!versioned_type.is_bitfield )
523- {
524- all_versions_are_bitfields = false ;
525- break ;
526- }
527-
528- VersionedBitfieldInfo bf_info;
529- bf_info.storage_type = versioned_type.type ;
530- bf_info.bit_position = versioned_type.bit_position ;
531- bf_info.bit_length = versioned_type.bit_length ;
532- bf_info.major_version = versioned_type.major_version ;
533- bf_info.minor_version = versioned_type.minor_version ;
534- versioned_bitfield_infos.push_back (bf_info);
535- }
536-
537343 for (const auto & [version_key, versioned_type] : variable.types_by_version )
538344 {
539345 File::StringType version_type_name = versioned_type.type ;
@@ -564,8 +370,7 @@ namespace RC::UVTD
564370
565371 // Only treat as versioned if we have multiple unique types after normalization
566372 // If only one unique type remains, treat as a normal getter with the best type
567- // EXCEPT: if all versions are bitfields, we can generate a single adaptive getter
568- bool effectively_has_versioned_getters = has_versioned_getters && valid_getter_count > 1 && !all_versions_are_bitfields;
373+ bool effectively_has_versioned_getters = has_versioned_getters && valid_getter_count > 1 ;
569374
570375 if (is_private || effectively_has_versioned_getters)
571376 {
@@ -578,34 +383,9 @@ namespace RC::UVTD
578383
579384 if (has_versioned_getters)
580385 {
581- // If all versions are bitfields, generate a single adaptive getter that handles
582- // the different storage types and bit positions at runtime
583- if (all_versions_are_bitfields && versioned_bitfield_infos.size () > 1 )
584- {
585- Output::send (STR (" Generating adaptive bitfield getter for {}::{} ({} versions)\n " ),
586- final_class_name, final_variable_name, versioned_bitfield_infos.size ());
587-
588- // For bitfields, return bool for single-bit fields, otherwise use the largest storage type
589- // to ensure we can hold any version's value
590- bool all_single_bit = std::all_of (versioned_bitfield_infos.begin (), versioned_bitfield_infos.end (),
591- [](const VersionedBitfieldInfo& info) { return info.bit_length == 1 ; });
592-
593- File::StringType return_type = all_single_bit ? STR (" bool" ) : STR (" uint32_t" );
594-
595- // For versioned bitfields, we can't provide a reference getter since storage type may change
596- // So we only provide the Value getter/setter methods
597- header_wrapper_dumper.send (STR (" {} Get{}Value() const;\n " ), return_type, final_variable_name);
598- header_wrapper_dumper.send (STR (" void Set{}Value({} value);\n\n " ), final_variable_name, return_type);
599-
600- wrapper_src_dumper.send (STR (" {} {}::Get{}Value() const\n " ), return_type, final_class_name, final_variable_name);
601- generate_versioned_bitfield_getter_body (wrapper_src_dumper, final_class_name, final_variable_name, versioned_bitfield_infos);
602-
603- wrapper_src_dumper.send (STR (" void {}::Set{}Value({} value)\n " ), final_class_name, final_variable_name, return_type);
604- generate_versioned_bitfield_setter_body (wrapper_src_dumper, final_class_name, final_variable_name, versioned_bitfield_infos);
605- }
606386 // If after normalization we only have one unique type, generate a single getter
607387 // using the "best" type (the most recent version, which has TObjectPtr/TSizedDefaultAllocator)
608- else if (valid_getter_count == 1 )
388+ if (valid_getter_count == 1 )
609389 {
610390 Output::send (STR (" Unified versioned getters for {}::{} to single type: {}\n " ),
611391 final_class_name, final_variable_name, best_type_for_single_getter);
0 commit comments