Texture1D<float4> IniParams : register(t120);
Texture2DArray<float4> tex : register(t100);

struct vs2ps {
    float4 pos : SV_Position0;
    float2 uv  : TEXCOORD1;
};

cbuffer cb2 : register(b2)
{
    float4 cb2[20];
}

float3 SRGBToLinear(float3 c)
{
    c = max(c, 0.0);
    float3 lo = c / 12.92;
    float3 hi = pow((c + 0.055) / 1.055, 2.4);
    return lerp(lo, hi, step(0.04045, c));
}

float3 LinearToSRGB(float3 c)
{
    c = max(c, 0.0);
    float3 lo = c * 12.92;
    float3 hi = 1.055 * pow(c, 1.0 / 2.4) - 0.055;
    return lerp(lo, hi, step(0.0031308, c));
}

// -----------------------------------------------------------------------------
// CONSTANTS
// -----------------------------------------------------------------------------
static const float UI_REF_W = 2560.0;
static const float UI_REF_H = 1440.0;
static const float UI_REF_ASPECT = UI_REF_W / UI_REF_H;

// Baseline (16:9 calibrated)
static const float SLOT_CENTER_X_BIAS_BASE = -0.0048;
static const float SLOT_CENTER_Y_BASE      = 0.5128;

// Narrow aspect correction (your original working model)
static const float SLOT_CENTER_Y_SLOPE = 0.005625;

// Ultrawide tuning (derived from your findings)
static const float ULTRAWIDE_X_SCALE = 0.0024;
static const float ULTRAWIDE_Y_SCALE = 0.002;

// -----------------------------------------------------------------------------
// HELPERS
// -----------------------------------------------------------------------------
float GetUiScale(float2 r)
{
    return min(r.x / UI_REF_W, r.y / UI_REF_H);
}

float GetAspect(float2 r)
{
    return r.x / r.y;
}

float GetWideDelta(float aspect)
{
    return max(aspect / UI_REF_ASPECT - 1.0, 0.0);
}

float GetNarrowDelta(float aspect)
{
    return max(UI_REF_ASPECT - aspect, 0.0);
}

float GetCenterXBias(float aspect)
{
    float w = GetWideDelta(aspect);
    return SLOT_CENTER_X_BIAS_BASE + (ULTRAWIDE_X_SCALE * w);
}

float GetCenterY(float aspect, float dragDeltaY)
{
    float narrow = GetNarrowDelta(aspect);
    float wide   = GetWideDelta(aspect);

    float y = SLOT_CENTER_Y_BASE;

    // Narrow correction (4:3, 16:10 etc.)
    y += narrow * SLOT_CENTER_Y_SLOPE;

    // Ultrawide correction
    y -= wide * ULTRAWIDE_Y_SCALE;

    return y + dragDeltaY;
}

float2 GetSlotSize(float2 slotSizeUI, float2 renderSize)
{
    float uiScale = GetUiScale(renderSize);

    float2 size;
    size.x = (slotSizeUI.x * uiScale) / renderSize.x;
    size.y = (slotSizeUI.y * uiScale) / renderSize.y;

    return size;
}

float2 GetCoverFit(float2 slotSize, float texW, float texH, float2 renderSize)
{
    float wFromH = slotSize.y * (texW / texH) * (renderSize.y / renderSize.x);
    float hFromW = slotSize.x * (texH / texW) * (renderSize.x / renderSize.y);

    float2 outSize;
    if (wFromH < slotSize.x)
    {
        outSize.x = slotSize.x;
        outSize.y = hFromW;
    }
    else
    {
        outSize.x = wFromH;
        outSize.y = slotSize.y;
    }

    return outSize;
}

#ifdef VERTEX_SHADER

#define SLOT_SIZE_UI      IniParams[87].xy
#define RENDER_SIZE       IniParams[91].xy
#define MANUAL_OFFSET_N   IniParams[92].xy
#define MANUAL_SCALE      IniParams[92].zw

void main(out vs2ps o, uint vertex : SV_VertexID)
{
    float texW, texH, texL;
    tex.GetDimensions(texW, texH, texL);

    texW = max(texW, 1.0);
    texH = max(texH, 1.0);

    float uiScale = GetUiScale(RENDER_SIZE);
    float aspect  = GetAspect(RENDER_SIZE);

    float2 slotSize = GetSlotSize(SLOT_SIZE_UI, RENDER_SIZE);

    float2 imgSize = GetCoverFit(slotSize, texW, texH, RENDER_SIZE);
    imgSize *= MANUAL_SCALE;

    float dragDeltaY = (113.0 - cb2[3].z) / UI_REF_H;

    float centerX = 0.5 + GetCenterXBias(aspect) + (cb2[3].x * uiScale) / RENDER_SIZE.x;
    float centerY = GetCenterY(aspect, dragDeltaY);

    float2 center = float2(centerX, centerY);
    float2 topLeft = center - imgSize * 0.5;

    // image-only offset
    topLeft += MANUAL_OFFSET_N;

    float2 offset;
    offset.x = topLeft.x * 2.0 - 1.0;
    offset.y = (1.0 - topLeft.y) * 2.0 - 1.0;

    float2 base;
    base.x = 2.0 * imgSize.x;
    base.y = -2.0 * imgSize.y;

    switch (vertex)
    {
        case 0: o.pos.xy = float2(base.x + offset.x, base.y + offset.y); o.uv = float2(1,0); break;
        case 1: o.pos.xy = float2(base.x + offset.x, offset.y);          o.uv = float2(1,1); break;
        case 2: o.pos.xy = float2(offset.x, base.y + offset.y);          o.uv = float2(0,0); break;
        case 3: o.pos.xy = float2(offset.x, offset.y);                   o.uv = float2(0,1); break;
        default: o.pos.xy = 0; o.uv = 0; break;
    }

    o.pos.zw = float2(0,1);
}

#endif

#ifdef PIXEL_SHADER

#define SLOT_SIZE_UI    IniParams[87].xy
#define RENDER_SIZE     IniParams[91].xy
#define frameIndex      IniParams[85].x
#define COLORSPACE_MODE ((uint)(IniParams[96].x + 0.5))

void main(vs2ps i, out float4 o : SV_Target0)
{
    float texW, texH, texL;
    tex.GetDimensions(texW, texH, texL);

    if (texW <= 0.0 || texH <= 0.0 || texL <= 0.0)
    {
        o = float4(1,0,1,1);
        return;
    }

    float uiScale = GetUiScale(RENDER_SIZE);
    float aspect  = GetAspect(RENDER_SIZE);

    float2 slotSize = GetSlotSize(SLOT_SIZE_UI, RENDER_SIZE);

    float dragDeltaY = (113.0 - cb2[3].z) / UI_REF_H;

    float centerX = 0.5 + GetCenterXBias(aspect) + (cb2[3].x * uiScale) / RENDER_SIZE.x;
    float centerY = GetCenterY(aspect, dragDeltaY);

    float2 center = float2(centerX, centerY);

    float2 minP = center - slotSize * 0.5;
    float2 maxP = center + slotSize * 0.5;

    float2 p;
    p.x = i.pos.x / RENDER_SIZE.x;
    p.y = i.pos.y / RENDER_SIZE.y;

    if (p.x < minP.x || p.x > maxP.x || p.y < minP.y || p.y > maxP.y)
        discard;

    i.uv.y = 1 - i.uv.y;

    uint slice = min((uint)(frameIndex + 0.5), (uint)texL - 1u);
    int2 px = int2(i.uv * float2(texW - 1, texH - 1));

    o = tex.Load(int4(px, slice, 0));

    if (COLORSPACE_MODE == 1) o.rgb = SRGBToLinear(o.rgb);
    else if (COLORSPACE_MODE == 2) o.rgb = LinearToSRGB(o.rgb);
}

#endif