Skip to content

refactor(ini): Make the INI code cleaner and more consistent#2596

Open
Caball009 wants to merge 7 commits intoTheSuperHackers:mainfrom
Caball009:ini_small_refactor
Open

refactor(ini): Make the INI code cleaner and more consistent#2596
Caball009 wants to merge 7 commits intoTheSuperHackers:mainfrom
Caball009:ini_small_refactor

Conversation

@Caball009
Copy link
Copy Markdown

A handful of miscellaneous changes to the INI code. Should have zero change in behavior.

Generals and Zero Hour code are not separated, but the commit diffs are pretty clean.

@Caball009 Caball009 added Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing labels Apr 12, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 12, 2026

Greptile Summary

This PR refactors the INI class by replacing five separator/end-token member variables (m_seps, m_sepsPercent, m_sepsColon, m_sepsQuote, m_blockEndToken) with static methods, and promoting getNextToken, getNextTokenOrNull, and getNextSubToken to static. Across 23 files it also standardises loop style (whilefor, token != nullptrtoken) and replaces getNextTokenOrNull + explicit null-check + throw with the simpler getNextToken (which throws internally). All changes are consistent with the stated "zero behavioral change" goal — the separator strings and stricmp-based end-token comparison are identical, no callers pass nullptr explicitly to the token functions, and the %% in getSepsPercent was already present in the old initialiser.

Confidence Score: 5/5

Safe to merge; all remaining open concerns from prior threads are being addressed in separate PRs, and no new defects were introduced.

The diff is a clean mechanical refactoring with zero behavioral change confirmed: separator strings are identical, the strtok calling convention is unchanged, no callers pass explicit nullptr to the token functions (verified by search), and the getEndToken() case change is guarded by stricmp. The only previously flagged structural issue (missing = delete) is tracked in a prior thread. All remaining findings across the 23 files are P2 or below.

Core/GameEngine/Include/Common/INI.h — the missing = delete for copy/assignment is the one outstanding item, already discussed in a prior thread.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/INI.h Central class interface change: 5 separator/end-token member variables removed and replaced with static methods; getNextToken/getNextTokenOrNull/getNextSubToken promoted to static; destructor declaration removed; copy-protection declarations removed without = delete replacement (flagged separately).
Core/GameEngine/Source/Common/INI/INI.cpp Implementation updated to match header: member variable initializations removed, empty destructor removed, m_seps/m_blockEndToken references replaced with static calls, while→for loop conversions, token != nullptr → token simplifications, and constexpr added to local SEPS constant.
Generals/Code/GameEngine/Source/GameLogic/Object/Update/AutoDepositUpdate.cpp getNextTokenOrNull + manual null-check + throw replaced with getNextToken (which throws internally); semantically equivalent and cleaner.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/OCLUpdate.cpp Same getNextTokenOrNull + null-check + throw → getNextToken simplification as AutoDepositUpdate.cpp; only present in GeneralsMD version.
Generals/Code/GameEngine/Source/GameLogic/Object/ObjectCreationList.cpp Loop variable renamed debrisName→token; double-advance pattern preserved unchanged (pre-existing behavior, separate PR planned per prior thread).

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class INI_Before {
        -const char* m_seps
        -const char* m_sepsPercent
        -const char* m_sepsColon
        -const char* m_sepsQuote
        -const char* m_blockEndToken
        +getSeps() const char*
        +getSepsPercent() const char*
        +getSepsColon() const char*
        +getSepsQuote() const char*
        +getNextToken(seps=nullptr) const char*
        +getNextTokenOrNull(seps=nullptr) const char*
        +getNextSubToken(expected) const char*
        ~INI()
    }
    class INI_After {
        +getSeps()$ const char*
        +getSepsPercent()$ const char*
        +getSepsColon()$ const char*
        +getSepsQuote()$ const char*
        +getEndToken()$ const char*
        +getNextToken(seps=getSeps())$ const char*
        +getNextTokenOrNull(seps=getSeps())$ const char*
        +getNextSubToken(expected)$ const char*
    }
    note for INI_After "$ = static\nMember vars removed\nDestructor is compiler-generated"
Loading

Reviews (3): Last reviewed commit: "Fixed forgotten change." | Re-trigger Greptile

INI(const INI&);
INI& operator=(const INI&);

public:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Missing = delete for copy/assignment after removing private declarations

The PR removes the private INI(const INI&) / INI& operator=(const INI&) declarations without replacing them with = delete. m_readBuffer is heap-allocated (file->readEntireAndClose()) and released via delete[] m_readBuffer in unPrepFile(). With no copy protection, the compiler will generate a shallow-copy constructor and assignment operator, meaning two INI instances could share the same m_readBuffer pointer. When either calls unPrepFile(), the other is left with a dangling pointer — use-after-free or double-delete follows.

// Suggested fix — add in the public or class body:
INI(const INI&) = delete;
INI& operator=(const INI&) = delete;
Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Include/Common/INI.h
Line: 163

Comment:
**Missing `= delete` for copy/assignment after removing private declarations**

The PR removes the private `INI(const INI&)` / `INI& operator=(const INI&)` declarations without replacing them with `= delete`. `m_readBuffer` is heap-allocated (`file->readEntireAndClose()`) and released via `delete[] m_readBuffer` in `unPrepFile()`. With no copy protection, the compiler will generate a shallow-copy constructor and assignment operator, meaning two INI instances could share the same `m_readBuffer` pointer. When either calls `unPrepFile()`, the other is left with a dangling pointer — use-after-free or double-delete follows.

```cpp
// Suggested fix — add in the public or class body:
INI(const INI&) = delete;
INI& operator=(const INI&) = delete;
```

How can I resolve this? If you propose a fix, please make it concise.


this will *never* return null; if there are no more tokens, an exception will be thrown.
*/
const char* getNextToken(const char* seps = nullptr);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I wonder if we should only have one token retrieving function and let external code handle an invalid return depending on where the check is being performed, i don't like the idea of it just throwing an error due to a malformed ini, it should more gracefully handle it and warn the user.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah, maybe, but would require a larger change.

I'm working on a large overhaul of the INI code, but it's a significant endeavor so I'm not sure it'll work out.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No worries, it's just nasty that the original token retrieval code throws in the first place.

class INI
{
INI(const INI&);
INI& operator=(const INI&);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

prob best to keep the copy and copy move constructors private.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Why is that?

Copy link
Copy Markdown

@Mauller Mauller Apr 13, 2026

Choose a reason for hiding this comment

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

the INI class is not a simple class, it has internal data handled on the heap.

The default copy constructor will only make a shallow copy of the INI, so if the original is deleted the pointed at memory in m_framebuffer will dangle to invalid memory.

the manual destructor tbh should remain but also call unprep to clear the frame buffer too.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

greptile also warned about that.


//-------------------------------------------------------------------------------------------------
/*static*/ const char* INI::getNextToken(const char* seps)
/*static*/ const char* INI::getNextToken(const char* seps /*= getSeps()*/)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No need to block comment the default argument.

{
SlowDeathBehaviorModuleData* self = (SlowDeathBehaviorModuleData*)instance;
SlowDeathPhaseType sdphase = (SlowDeathPhaseType)INI::scanIndexList(ini->getNextToken(), TheSlowDeathPhaseNames);
for (const char* token = ini->getNextToken(); token != nullptr; token = ini->getNextTokenOrNull())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can also leave the != nullptr.

const char* SEPS = " \t,=";
const char *c = ini->getNextTokenOrNull(SEPS);
while ( c )
constexpr const char* SEPS = " \t,=";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Or Seps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants