Fix null-terminated string handling in DAT files#7
Merged
redtoad merged 1 commit intosupport-all-filesfrom Jan 30, 2026
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes parsing/round-tripping of null-terminated fixed-size strings in X-COM DAT files by removing a broken custom unpacker and switching affected DAT structs to a NullString representation that trims at the first NUL on access.
Changes:
- Replace
NullString.UnpackwithNullString.String()and migrate name fields toNullString(soldiers, bases, save metadata). - Fix SAVEINFO.DAT round-tripping by making the missdat flag field exported and preserving the full 40-byte record.
- Adjust BASE.DAT record layout by widening the
Activefield storage to cover the full 4-byte value.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| savegame/soldiers.go | Use NullString.String() for soldier names and empty checks. |
| savegame/metadata.go | Return save title via NullString.String(). |
| savegame/bases.go | Use NullString.String() for base names and presence checks. |
| internal/types.go | Remove broken unpacker and add NullString.String() truncation behavior. |
| internal/types_test.go | Update and expand tests for NullString.String() and multi-field round-trip. |
| internal/geoscape/soldier_dat.go | Store soldier name as internal.NullString in SOLDIER.DAT struct. |
| internal/geoscape/soldier_dat_test.go | Adapt tests for NullString name handling. |
| internal/geoscape/saveinfo_dat.go | Store save name as NullString and export missdat flag field. |
| internal/geoscape/saveinfo_dat_test.go | Add tests for SAVEINFO.DAT name parsing and round-trip. |
| internal/geoscape/base_dat.go | Store base name as NullString and widen Active storage to 4 bytes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
0790996 to
1c3d284
Compare
Replace the broken NullString.Unpack method (which consumed the entire remaining buffer, breaking multi-field structs) with a String() method that strips at the first null byte on access. Use NullString type for Name fields in SaveinfoFile, SoldierData, and BaseData. Also fix two pre-existing round-trip issues: rename the unnamed field in SaveinfoFile so its value is preserved, and widen BaseData.Active from int8 to int32 to cover the full 292-byte record. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1c3d284 to
5d836fb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
NullString.Unpackmethod with aString()method that strips at the first null byte on access. The oldUnpackconsumed theentire remaining buffer, breaking parsing of any struct with fields after the name.
NullStringtype forNamefields inSaveinfoFile,SoldierData, andBaseDataso null-terminated strings are handled correctly throughout._field inSaveinfoFileso its value is preserved on pack, and widenBaseData.Activefrom
int8toint32to cover the full 292-byte record.Test plan
NullString.String()strips at null byte and handles missing nullNullStringworks in multi-field structs (new test)SaveinfoFilename parsing returns clean strings ("Test", not"Test\x00...")SaveinfoFileround-trip produces identical binary outputSoldierFileround-trip produces identical binary output (250 records, 17 KB)🤖 Generated with Claude Code