4K: the exposure meter gets room for 4K tiles (the dark picture that comes and goes)

The game meters exposure on tiles of 128x72 pixels: the CPU (eboot 0x171610) sums one float per
tile from buffers made for 625 tiles (2500 bytes; four of them, set up at 0x171310 and 0x172320),
enough up to 1800p. At a 4K render (900 tiles) it summed whatever lay past them, so the exposure
jumped and the picture went dark, back to normal, and dark again (GitHub issue 15, tester logs
11-01 at 3840x2160 with and without the Pro-mode patch; 1080p and 1440p were fine).

The 'Bigger light list (needed for native 4K)' patch, on by default, now also makes the four
buffers 8192 bytes / 2048 tiles (checked on the 01.07 eboot: all 10 writes apply). With a render
size above 1800p and the patch off, the shim does the same (ApplyExposureCapacity). The light
list line no longer says 'not enlarged' when the patch already did it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014CsPEH9HsibG9QrEt8o9SE
This commit is contained in:
Claude committed 2026-11-04 10:12:28 -03:00
1 parent 86af332e2e
commit 48a3179cae
2 files changed
+55 -2

No files matched your search

+43 -1
View File
@@ -262,7 +262,11 @@ static void ApplyLightListCapacity(u32 width, u32 height) {
std::memcpy(&mov_imm, at(0x16be1f), 4);
if (std::memcmp(at(0x16be04), imul_head, 3) != 0 || std::memcmp(at(0x16be1d), mov_head, 2) != 0 ||
imul_imm != Original || mov_imm != Original) {
std::printf("Render size: light list not enlarged (the code differs in this eboot.bin)\n");
if (imul_imm == mov_imm && imul_imm >= entries) {
std::printf("Render size: light list already %u entries (patch)\n", imul_imm);
} else {
std::printf("Render size: light list not enlarged (the code differs in this eboot.bin)\n");
}
return;
}
std::memcpy(at(0x16be07), &entries, 4);
@@ -270,6 +274,43 @@ static void ApplyLightListCapacity(u32 width, u32 height) {
std::printf("Render size: light list %u entries for %ux%u\n", entries, width, height);
}
// issn 11-04: the exposure meter (CPU sum at eboot 0x171610) reads one float per 128x72-pixel tile
// from buffers made for 625 tiles (2500 bytes, eboot 0x171310 and 0x172320): enough up to 1800p.
// At 4K (900 tiles) it summed bytes past them, and the picture went dark and back (tester logs,
// GitHub issue 15). The patch "Bigger light list" enlarges them too; without it, this does.
static void ApplyExposureCapacity(u32 width, u32 height) {
const u64 tiles = u64((width + 127) / 128) * ((height + 71) / 72);
if (tiles <= 625) {
return;
}
static constexpr u64 sizes[4] = {0x17132b, 0x171375, 0x172341, 0x172388}; // mov esi, 0x9c4
static constexpr u64 counts[4] = {0x17134f, 0x171399, 0x172364, 0x1723ab}; // mov ecx, 0x271
const auto at = [](u64 offset) { return reinterpret_cast<u8*>(g_eboot_address + offset); };
bool original = true, patched = true;
for (int i = 0; i < 4; ++i) {
u32 size = 0, count = 0;
std::memcpy(&size, at(sizes[i]), 4);
std::memcpy(&count, at(counts[i]), 4);
original = original && at(sizes[i])[-1] == 0xbe && at(counts[i])[-1] == 0xb9 && size == 0x9c4 &&
count == 0x271;
patched = patched && size >= tiles * 4 && count >= tiles;
}
if (patched) {
std::printf("Exposure meter: room for %llu tiles (patch)\n", static_cast<unsigned long long>(tiles));
return;
}
if (!original) {
std::printf("Exposure meter: not enlarged (the code differs in this eboot.bin): the picture may go dark\n");
return;
}
const u32 count = 2048, size = count * 4;
for (int i = 0; i < 4; ++i) {
std::memcpy(at(sizes[i]), &size, 4);
std::memcpy(at(counts[i]), &count, 4);
}
std::printf("Exposure meter: %u tiles for %ux%u\n", count, width, height);
}
/// The render size at 16:9-basis height h0 in the shape `aspect` (axylpup's SizeAt): wide shapes
/// keep the height (up to 32:9), narrow ones keep the 16:9 width (down to 1:1).
static void SizeAt(u32 h0, double aspect, u32& width, u32& height) {
@@ -413,6 +454,7 @@ static void ApplyRenderSize() {
std::memcpy(reinterpret_cast<void*>(g_eboot_address + imm.offset), &value, 4);
}
ApplyLightListCapacity(width, height);
ApplyExposureCapacity(width, height);
if (narrow) {
const float two_over_k = float(2.0 / (base / aspect));
std::memcpy(reinterpret_cast<void*>(g_eboot_address + 0x581c84), &two_over_k, 4);
+12 -1
View File
@@ -29,10 +29,21 @@
<Line Type="bytes" Address="0x0072fa15" Value="E9A65C99006690" OriginalHash="3ee41d36233286e4"/>
</PatchList>
</Metadata>
<Metadata Title="inFAMOUS Second Son" Name="Bigger light list (needed for native 4K)" Note="The game sizes its light lists for 25 600 tiles of 16 pixels (enough up to 1800p); 4K needs 32 400. FR : liste des lumieres agrandie, necessaire pour la 4K native." Author="AYOUB1080p" PatchVer="1.0" AppVer="01.00" AppElf="eboot.bin" isEnabled="true">
<Metadata Title="inFAMOUS Second Son" Name="Bigger light list (needed for native 4K)" Note="The game sizes its light lists for 25 600 tiles of 16 pixels (enough up to 1800p); 4K needs 32 400. Also the exposure meter (625 tiles of 128x72 px; 4K needs 900, else the picture goes dark). FR : liste des lumieres agrandie, necessaire pour la 4K native." Author="AYOUB1080p" PatchVer="1.0" AppVer="01.00" AppElf="eboot.bin" isEnabled="true">
<PatchList>
<Line Type="bytes" Address="0x0056be07" Value="00004800" OriginalHash="4cf5e27f9da5a7b7"/>
<Line Type="bytes" Address="0x0056be1f" Value="00004800" OriginalHash="4cf5e27f9da5a7b7"/>
<!-- issn 11-04: the exposure meter's tile buffers (four, 2500 bytes / 625 tiles of 128x72 px:
enough up to 1800p; 4K needs 900, so the camera's exposure read garbage past them and
the picture went dark and back). 8192 bytes / 2048 tiles each. -->
<Line Type="bytes" Address="0x0057132b" Value="00200000" OriginalHash="05de299f0f9c851e"/>
<Line Type="bytes" Address="0x00571375" Value="00200000" OriginalHash="05de299f0f9c851e"/>
<Line Type="bytes" Address="0x00572341" Value="00200000" OriginalHash="05de299f0f9c851e"/>
<Line Type="bytes" Address="0x00572388" Value="00200000" OriginalHash="05de299f0f9c851e"/>
<Line Type="bytes" Address="0x0057134f" Value="00080000" OriginalHash="bd7e02077b7bafea"/>
<Line Type="bytes" Address="0x00571399" Value="00080000" OriginalHash="bd7e02077b7bafea"/>
<Line Type="bytes" Address="0x00572364" Value="00080000" OriginalHash="bd7e02077b7bafea"/>
<Line Type="bytes" Address="0x005723ab" Value="00080000" OriginalHash="bd7e02077b7bafea"/>
</PatchList>
</Metadata>
<Metadata Title="inFAMOUS Second Son" Name="Streaming: closest city blocks first" Note="Lower priority penalties of the world block streamer (3000/2000/2000 -&gt; 1000/700/700): the blocks nearest to the player are loaded and kept in high detail sooner. FR : streaming, les blocs de la ville les plus proches passent en priorite." Author="AYOUB1080p" PatchVer="1.0" AppVer="01.00" AppElf="eboot.bin" isEnabled="false">