Skip to content

[Minor] Change overlays >255 to use NewINIFormat == 5 #1692

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ This page lists all the individual contributions to the project by their author.
- Allow to change the speed of gas particles
- Fix a jumpjet crash related to voxel shadow drawing
- Replace `BLOWFISH.DLL` using Red Alert source code
- Adjust the dehardcoding of the 255 `OverlayType` limit to a different format
- **CrimRecya**:
- Fix `LimboKill` not working reliably
- Allow using waypoints, area guard and attack move with aircraft
Expand Down
14 changes: 14 additions & 0 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ This page describes all AI scripting and mapping related additions and changes i
- If a pre-placed building has a `NaturalParticleSystem`, it used to always be created when the game starts. This has been removed.
- Superweapons used by AI for script actions `56 Chronoshift to Building`, `57 Chronoshift to a Target Type` and `10104 Chronoshift to Enemy Base` can now be explicitly set via `[General] -> AIChronoSphereSW` & `AIChronoWarpSW` respectively. If `AIChronoSphereSW` is set but `AIChronoWarpSW` is not, game will check former's `SW.PostDependent` for a second superweapon to use. Otherwise if not set, last superweapon listed in `[SuperWeaponTypes]` with `Type=ChronoSphere` or `Type=ChronoWarp` will be used, respectively.

### Increased Overlay Limit

- Maps can now contain OverlayTypes with indices up to 65535.

- To enable this, set `[Basic] -> NewINIFormat=5` in the scenario file.

```{note}
Maps using this feature cannot be loaded by the vanilla game.
```

```{warning}
Not all tools properly support this feature yet, and may crash or corrupt the map. We recommend using the [World-Altering Editor](https://github.com/CnCNet/WorldAlteringEditor) map editor when using this feature.
```

## Singleplayer Mission Maps

### Base node repairing
Expand Down
2 changes: 1 addition & 1 deletion docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ New:
- [Use `InsigniaType` to set the properties of insignia in a batch](Miscellanous.md#insignia-type) (by Ollerus)
- [Tiberium eater logic](New-or-Enhanced-Logics.md#tiberium-eater) (by NetsuNegi)
- [Customize the damage taken when falling from a bridge](Fixed-or-Improved-Logics.md#customize-bridge-falling-down-damage) (by FlyStar)
- Dehardcoded 255 limit of `OverlayType` (by secsome)
- Dehardcoded 255 limit of `OverlayType` (by secsome, ZivDero)
- [Customizable airstrike flare colors](Fixed-or-Improved-Logics.md#airstrike-flare-customizations) (by Starkku)
- Allowed player's self-healing effects to be benefited by allied or `PlayerControl=true` houses (by Ollerus)
- [Exclusive SuperWeapon Sidebar](User-Interface.md#superweapon-sidebar) (by NetsuNegi & CrimRecya)
Expand Down
77 changes: 46 additions & 31 deletions src/Misc/Hooks.Overlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct OverlayReader

bool IsAvailable() const { return uuLength > 0; }

unsigned char Get()
unsigned char GetByte()
{
if (IsAvailable())
{
Expand All @@ -44,38 +44,56 @@ struct OverlayReader
return 0;
}

unsigned short GetWord()
{
if (IsAvailable())
{
unsigned short ret;
ls.Get(&ret, sizeof(ret));
return ret;
}

return 0;
}

size_t uuLength;
void* pBuffer;
LCWStraw ls;
BufferStraw bs;
};

size_t Get()
int Get()
{
unsigned char ret[4];

ret[0] = ByteReaders[0].Get();
ret[1] = ByteReaders[1].Get();
ret[2] = ByteReaders[2].Get();
ret[3] = ByteReaders[3].Get();
if (ScenarioClass::NewINIFormat >= 5)
{
unsigned short shrt = ByteReader.GetWord();
if (shrt != static_cast<unsigned short>(-1))
return shrt;
}
else
{
unsigned char byte = ByteReader.GetByte();
if (byte != static_cast<unsigned char>(-1))
return byte;
}

return ret[0] == 0xFF ? 0xFFFFFFFF : (ret[0] | (ret[1] << 8) | (ret[2] << 16) | (ret[3] << 24));
return -1;
}

OverlayReader(CCINIClass* pINI)
:ByteReaders{ { pINI, "OverlayPack" }, { pINI, "OverlayPack2" }, { pINI, "OverlayPack3" }, { pINI, "OverlayPack4" }, }
: ByteReader{ pINI, "OverlayPack" }
{ }

private:
OverlayByteReader ByteReaders[4];
OverlayByteReader ByteReader;
};

struct OverlayWriter
{
struct OverlayByteWriter
{
OverlayByteWriter(const char* pSection, size_t nBufferLength)
: bp { nullptr,0 }, lp { FALSE,0x2000 }, uuLength { 0 }, lpSectionName { pSection }
: lpSectionName { pSection }, uuLength { 0 }, bp { nullptr,0 }, lp { FALSE,0x2000 }
{
this->Buffer = YRMemory::Allocate(nBufferLength);
bp.Buffer.Buffer = this->Buffer;
Expand All @@ -89,9 +107,14 @@ struct OverlayWriter
YRMemory::Deallocate(this->Buffer);
}

void Put(unsigned char data)
void PutByte(unsigned char data)
{
uuLength += lp.Put(&data, sizeof(data));
}

void PutWord(unsigned short data)
{
uuLength += lp.Put(&data, 1);
uuLength += lp.Put(&data, sizeof(data));
}

void PutBlock(CCINIClass* pINI)
Expand All @@ -108,32 +131,24 @@ struct OverlayWriter
};

OverlayWriter(size_t nLen)
: ByteWriters { { "OverlayPack", nLen}, { "OverlayPack2", nLen }, { "OverlayPack3", nLen }, { "OverlayPack4", nLen } }
: ByteWriter { "OverlayPack", nLen }
{ }

void Put(int nOverlay)
{
unsigned char bytes[4];
bytes[0] = (nOverlay & 0xFF);
bytes[1] = ((nOverlay >> 8) & 0xFF);
bytes[2] = ((nOverlay >> 16) & 0xFF);
bytes[3] = ((nOverlay >> 24) & 0xFF);
ByteWriters[0].Put(bytes[0]);
ByteWriters[1].Put(bytes[1]);
ByteWriters[2].Put(bytes[2]);
ByteWriters[3].Put(bytes[3]);
if (ScenarioClass::NewINIFormat >= 5)
ByteWriter.PutWord(static_cast<unsigned short>(nOverlay));
else
ByteWriter.PutByte(static_cast<unsigned char>(nOverlay));
}

void PutBlock(CCINIClass* pINI)
{
ByteWriters[0].PutBlock(pINI);
ByteWriters[1].PutBlock(pINI);
ByteWriters[2].PutBlock(pINI);
ByteWriters[3].PutBlock(pINI);
ByteWriter.PutBlock(pINI);
}

private:
OverlayByteWriter ByteWriters[4];
OverlayByteWriter ByteWriter;
};

DEFINE_HOOK(0x5FD2E0, OverlayClass_ReadINI, 0x7)
Expand All @@ -152,7 +167,7 @@ DEFINE_HOOK(0x5FD2E0, OverlayClass_ReadINI, 0x7)
for (short j = 0; j < 0x200; ++j)
{
CellStruct mapCoord{ j,i };
size_t nOvl = reader.Get();
int nOvl = reader.Get();

if (nOvl != 0xFFFFFFFF)
{
Expand Down Expand Up @@ -227,7 +242,7 @@ DEFINE_HOOK(0x5FD6A0, OverlayClass_WriteINI, 0x6)
CellStruct mapCoord { j,i };
auto const pCell = MapClass::Instance.GetCellAt(mapCoord);
writer.Put(pCell->OverlayTypeIndex);
dataWriter.Put(pCell->OverlayData);
dataWriter.PutByte(pCell->OverlayData);
}
}

Expand Down