Skip to content

fix(hlod): Keep the aggregate and proxy arrays of HLod definitions - #3553

Open
xezon wants to merge 7 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-hlod-save
Open

xezon wants to merge 7 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-hlod-save

Conversation

@xezon

@xezon xezon commented Oct 18, 2026

Copy link
Copy Markdown

This change fixes missing save data in HLodDefClass::Save.

It is not a practical issue for the game and existing tools, only for future W3D writers that would go through this code.

The new code also matches what the max2w3d plugin does, so Claude claims.

AI summary

HLodDefClass::Load_W3D reads the aggregate and proxy arrays of a W3D_CHUNK_HLOD, but
Save wrote neither, so W3DView's Export LOD dropped them. Save_Aggregate_Array was never
called, and if it had been, SubObjectArrayClass::Save_W3D would have nested the
aggregates in a W3D_CHUNK_HLOD_LOD_ARRAY where Load_W3D cannot read them. There was no
writer for proxies at all. Initialize, which W3DView uses after Record Screen Area and
Include NULL LOD, copied only the LOD arrays.

SubObjectArrayClass::Save_W3D now writes only the array contents, as its Load_W3D reads
them and as max2w3d writes them. Save_Lod_Array wraps each LOD array in its chunk, and
Save writes the aggregate array and the new proxy array after the LOD arrays, in
max2w3d's order. Initialize copies the additional models as aggregates and shares the
proxy array, and Free now also releases the aggregates. No HLod in the retail archives
of either game has an aggregate or proxy array.

A Google Test round-trips an HLod chunk with both arrays and one with only a LOD array,
and saves a definition built from an HLod with an additional model.

AI Use

This bug was found and fixed by Claude Opus 5.5. Tweaked a bit by hand.

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Tools Affects Tools only Fix Is fixing something, but is not user facing labels Oct 18, 2026
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: beb41ae5-4c4d-407b-9d40-226cf0da42de
📥 Commits

Reviewing files that changed from the base of the PR and between 8643495 and 835bf71.

📒 Files selected for processing: 2
  • Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
  • GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Walkthrough

HLodDefClass now copies additional models and retains proxy references from an HLodClass. Saving includes LOD, aggregate, and proxy arrays. Tests compare saved HLOD data with serialized fixtures.

Changes

HLOD Definition Serialization

Layer / File(s) Summary
Capture HLOD data
Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.*, GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.*
HLodDefClass::Free resets aggregate data. Initialization copies additional model names and bone indices, and retains the proxy array.
Serialize HLOD arrays
Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.*, GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.*, Tests/Google/Core/CMakeLists.txt, Tests/Google/Core/Libraries/WWVegas/WW3D2/hlod_test.cpp
Saving includes LOD, aggregate, and proxy arrays. Save_Lod_Array supplies the LOD chunk wrapper, while Save_W3D writes the array header and model entries. Tests compare saved output with three HLOD fixtures.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: lfeenanea

Merge Risk: ⚪ Minimal · up to 835bf

No actionable merge-blocking issue is established; this change is ready for normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The description clearly explains the missing aggregate and proxy save data, the serialization changes, initialization behavior, and added tests.
Title check Passed The title clearly and concisely identifies the main change: preserving aggregate and proxy arrays in HLOD definitions.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp Outdated
Comment thread Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issue was found.

Summary

The PR keeps aggregate and proxy data when saving or rebuilding an HLodDefClass.

  • HLOD saves now keep their LOD, aggregate, and proxy arrays.

No new actionable findings were accepted. The supplied previous threads are unnumbered and receive no entries in previousFindings.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A["HLodDefClass::Save"] --> B["HLOD header"]
  B --> C["LOD arrays"]
  C --> D["Aggregate array, if present"]
  D --> E["Proxy array, if present"]
Loading

Reviews (4) · Last reviewed commit: "return error in HLodDefClass::Save_Aggre..." · Reviewed by Greptile

Comment thread Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (2)

🟡 Minor · Propagate aggregate chunk-finalization failures. · hlod.cpp:553-560

Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp:553-560
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Propagate aggregate chunk-finalization failures.

When Aggregates.ModelCount > 0, End_Chunk() can report that the aggregate header rewrite failed. Save_Aggregate_Array ignores that result and returns WW3D_ERROR_OK, so HLodDefClass::Save can report success for an invalid aggregate chunk. Check all three operations, and call End_Chunk() after every attempted outer chunk, including a failed Begin_Chunk().

Suggested fix
 WW3DErrorType HLodDefClass::Save_Aggregate_Array(ChunkSaveClass & csave)
 {
 	if (Aggregates.ModelCount > 0) {
-		csave.Begin_Chunk(W3D_CHUNK_HLOD_AGGREGATE_ARRAY);
-		Aggregates.Save_W3D(csave);
-		csave.End_Chunk();
+		bool success = csave.Begin_Chunk(W3D_CHUNK_HLOD_AGGREGATE_ARRAY) == TRUE;
+		if (success) {
+			success = Aggregates.Save_W3D(csave);
+		}
+		success = (csave.End_Chunk() == TRUE) && success;
+		if (!success) {
+			return WW3D_ERROR_SAVE_FAILED;
+		}
 	}
 	return WW3D_ERROR_OK;
 }
🟡 Minor · Propagate aggregate write failures. · hlod.cpp:553-560

GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp:553-560
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Propagate aggregate write failures.

When Aggregates.ModelCount > 0, Save_Aggregate_Array ignores Begin_Chunk and Aggregates.Save_W3D. A failed write can therefore produce incomplete aggregate output while HLodDefClass::Save reports WW3D_ERROR_OK. Preserve the unconditional End_Chunk() call and return WW3D_ERROR_SAVE_FAILED for any failed operation.

Suggested fix
 WW3DErrorType HLodDefClass::Save_Aggregate_Array(ChunkSaveClass & csave)
 {
 	if (Aggregates.ModelCount > 0) {
-		csave.Begin_Chunk(W3D_CHUNK_HLOD_AGGREGATE_ARRAY);
-		Aggregates.Save_W3D(csave);
-		csave.End_Chunk();
+		bool success = csave.Begin_Chunk(W3D_CHUNK_HLOD_AGGREGATE_ARRAY) == TRUE;
+		if (success) {
+			success = Aggregates.Save_W3D(csave);
+		}
+		success &= csave.End_Chunk();
+		if (!success) {
+			return WW3D_ERROR_SAVE_FAILED;
+		}
 	}
 	return WW3D_ERROR_OK;
 }

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: da9c77c1-abbc-4826-98d5-9dcb71b52f32
📥 Commits

Reviewing files that changed from the base of the PR and between e572d87 and ef7e384.

📒 Files selected for processing: 6
  • Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
  • Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.h
  • GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
  • GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.h
  • Tests/Google/Core/CMakeLists.txt
  • Tests/Google/Core/Libraries/WWVegas/WW3D2/hlod_test.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment thread Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
Comment thread GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp
Comment thread Generals/Code/Libraries/Source/WWVegas/WW3D2/hlod.cpp Outdated
xezon added 7 commits October 18, 2026 20:30
HLodDefClass::Load_W3D reads the aggregate and proxy arrays of a W3D_CHUNK_HLOD, but
Save wrote neither, so W3DView's Export LOD dropped them. Save_Aggregate_Array was never
called, and if it had been, SubObjectArrayClass::Save_W3D would have nested the
aggregates in a W3D_CHUNK_HLOD_LOD_ARRAY where Load_W3D cannot read them. There was no
writer for proxies at all. Initialize, which W3DView uses after Record Screen Area and
Include NULL LOD, copied only the LOD arrays.

SubObjectArrayClass::Save_W3D now writes only the array contents, as its Load_W3D reads
them and as max2w3d writes them. Save_Lod_Array wraps each LOD array in its chunk, and
Save writes the aggregate array and the new proxy array after the LOD arrays, in
max2w3d's order. Initialize copies the additional models as aggregates and shares the
proxy array, and Free now also releases the aggregates. No HLod in the retail archives
of either game has an aggregate or proxy array.

A Google Test round-trips an HLod chunk with both arrays and one with only a LOD array,
and saves a definition built from an HLod with an additional model.
@xezon
xezon force-pushed the xezon/fix-hlod-save branch from 835bf71 to 45cf94b Compare October 18, 2026 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Minor Severity: Minor < Major < Critical < Blocker Tools Affects Tools only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant