Tormented Souls 2
The same D3D12 guards on the colour-management library the game imports.
GPL-3.0-or-laterThis fix in MacGameFix
What you see
Fatal error! Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0xfffffffffffffff8 before the first frame
Why it happens
The game keeps only 16:9 resolutions and has no fallback when none exist
What the fix changes
Put 16:9 modes in the list the game reads
The code
Puts 16:9 modes into the display mode list the game reads, since it keeps only 16:9 ones and has no fallback when there are none.
fixes/d3d12-guards.c · my_modelist, lines 422–507c
static HRESULT WINAPI my_modelist(void *self, UINT fmt, UINT flags, UINT *count, void *modes)
{
HRESULT hr;
UINT w = 0, h = 0, hz = 0, i, have, room, added = 0, big_w = 0, big_h = 0;
if (!desktop_size(&w, &h, &hz)) return real_modelist(self, fmt, flags, count, modes);
if (!modes) /* the counting call */
{
hr = real_modelist(self, fmt, flags, count, NULL);
if (SUCCEEDED(hr) && count) *count += ladder_fits(w, h); /* room for ours */
return hr;
}
/* The filling call. Hold back the slots we may need, fill the rest normally. */
room = ladder_fits(w, h);
have = *count;
/* Never write past what the caller allocated. It asked for a count first
* and this said "that many plus room", but a caller is free to hand back a
* smaller buffer than it was told about, and appending into one would be a
* far worse bug than the crash being repaired. */
if (room > have) room = have;
*count = have - room;
hr = real_modelist(self, fmt, flags, count, modes);
if (FAILED(hr)) { *count = have; return hr; }
{
/* Say what is actually on offer, with the right stride. A mode list
* read at the wrong stride reports resolutions nobody offered, and a
* conclusion drawn from it is worse than no conclusion. */
static LONG told;
UINT tallest = 0, widest = 0;
for (i = 0; i < *count; ++i)
{
const unsigned char *m = (const unsigned char *)modes + (size_t)i * MODE_DESC_SIZE;
UINT mw = *(const UINT *)(m + 0), mh = *(const UINT *)(m + 4);
if (mh > tallest) tallest = mh;
if (mw > widest) widest = mw;
}
if (InterlockedIncrement(&told) <= 4)
logf_("mode list for format %u: %u modes, largest %ux%u; the screen is %ux%u",
fmt, *count, widest, tallest, w, h);
}
for (i = 0; i < *count; ++i)
{
const unsigned char *m = (const unsigned char *)modes + (size_t)i * MODE_DESC_SIZE;
UINT mw = *(const UINT *)(m + 0), mh = *(const UINT *)(m + 4);
double aspect;
if (!mh) continue;
aspect = (double)mw / (double)mh;
if (aspect > ASPECT_LOW && aspect < ASPECT_HIGH)
{
static LONG said;
if (InterlockedIncrement(&said) <= 4)
logf_(" a 16:9 mode is already on offer (%ux%u), adding nothing",
mw, mh);
return hr;
}
}
/* Nothing on offer is 16:9 and this game keeps nothing else, so the array
* it is about to build would come out empty. Give it the whole ladder that
* fits, not just enough to survive: the player gets a resolution menu, not
* a single forced entry. */
for (i = 0; i < LADDER_N && added < room; ++i)
{
unsigned char *m;
if ((ladder[i].w > w || ladder[i].h > h) && !(added == 0 && i + 1 == LADDER_N))
continue;
m = (unsigned char *)modes + (size_t)(*count) * MODE_DESC_SIZE;
if (*count) memcpy(m, modes, MODE_DESC_SIZE); /* inherit format and scaling */
else memset(m, 0, MODE_DESC_SIZE);
*(UINT *)(m + 0) = ladder[i].w;
*(UINT *)(m + 4) = ladder[i].h;
*(UINT *)(m + 8) = hz;
*(UINT *)(m + 12) = 1;
*(UINT *)(m + 16) = fmt;
*count += 1;
if (!added) { big_w = ladder[i].w; big_h = ladder[i].h; } /* largest first */
++added;
}
if (InterlockedIncrement(&modes_added) <= 8)
logf_("not one of the %u modes on offer is 16:9, and this game keeps "
"nothing else -- added %u of them, largest %ux%u @ %u Hz",
*count - added, added, big_w, big_h, hz);
return hr;
}Details
- Needs
- D3DMetal, D3D12 (and D3D11 — it fails identically either way)
- Installer
- install-tormented-fix.sh
- Executable
- TormentedSouls2.exe
- Built from
- d3d12-guards.c
- Verified on
- Stable 26.3 with Game Porting Toolkit 4.0b2 (2026-08-31). The measurements below were made on
crossover-preview-arm64-20260821, which is no longer a supported engine