200 lines
7.6 KiB
C++
200 lines
7.6 KiB
C++
// levelpath.cpp — transcription of FUN_180563440 (LUT curve + band combine)
|
|
// and FUN_18056e3e0 (twin-mask factory)
|
|
//
|
|
// Extracted from: handoff/nls_dasm/f_563440.dis (222 lines)
|
|
// Constants from: soothe_mem.bin at ImageBase 0x180000000
|
|
//
|
|
// Key addresses:
|
|
// 0x540000+0x2198 = r13+0x2198 = output accumulator (1024 doubles, stride 0x2000)
|
|
// 0x540000+0x188 = band config struct (A, B, C, flag, callback)
|
|
// 0x5408b0 = LUT coefficient table (PRNG state)
|
|
// 0x540868 = band count (max 6)
|
|
// 0x540870 = level weight (float)
|
|
// 0x540874 = level-dependent weight (float)
|
|
// 0x54087c = band weight (float)
|
|
// 0x54088c = sharpness weight (float)
|
|
// 0x540658 = window table (2048 floats, live-captured)
|
|
// 0x540698 = freq-axis (2049 floats, live-captured)
|
|
// 0x5406a8 = warp table (2049 floats)
|
|
// 0x5406b8 = warp exponent (float, = A_FIT)
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
|
|
// Constants extracted from binary
|
|
static constexpr float SCALE = 0.0009775171056389809f; // 1/1024 (DAT_1824c3c54)
|
|
static constexpr float ONE = 1.0f; // DAT_1824c3ea4
|
|
static constexpr float TWO = 2.0f; // DAT_1824c41e0
|
|
static constexpr float NEG1 = -1.0f; // DAT_1824c4680
|
|
static constexpr float HALF = 0.5f; // DAT_1824c3d8c
|
|
static constexpr float ZERO = 0.0f; // DAT_1824c4140
|
|
static constexpr float DEPTH_SCALE = 4.0f; // DAT_1824c4334
|
|
static constexpr float DB_CONV = 8.68588924407959f; // 20/ln(10) (DAT_1824c43e0)
|
|
static constexpr float FLOOR_DB = -6.907755374908447f; // ln(0.001) (DAT_1824c4704)
|
|
static constexpr float FLOOR LIN = 0.001f; // exp(FLOOR_DB)
|
|
|
|
// PRNG state offsets from param_1
|
|
static constexpr int PRNG_STATE = 0x2404e0;
|
|
static constexpr int PRNG_LUT = 0x5408b0;
|
|
|
|
// Band config struct layout (offsets from band_base = param_1 + 0x188)
|
|
struct BandConfig {
|
|
float A; // +0x00: start value
|
|
float B; // +0x04: end value
|
|
float _pad[2];
|
|
float threshold; // +0x0c: threshold (compared to 1.0)
|
|
uint8_t flag; // +0x10: 0=linear, 1=power-law
|
|
uint8_t _pad2[3];
|
|
float _pad3[15];
|
|
void* callback; // +0x50: vtable callback (if non-null, use callback)
|
|
};
|
|
|
|
// LUT evaluation for a single bin
|
|
// x is in [0, 1] range
|
|
static float eval_lut_bin(float x, const BandConfig* band) {
|
|
// Path 1: callback exists → use vtable
|
|
if (band->callback != nullptr) {
|
|
// TODO: transcribe callback vtable call
|
|
return x;
|
|
}
|
|
|
|
// Path 2: power-law (flag != 0 and threshold != 1.0)
|
|
if (band->flag != 0 && band->threshold != ONE) {
|
|
float C = band->threshold;
|
|
// x = 2*x - 1 (center at zero: [-1, 1])
|
|
float centered = TWO * x - ONE;
|
|
if (C == ONE || centered == ZERO) {
|
|
// fall through to linear
|
|
} else {
|
|
// sign(x) * 10^(log10(|x|) / C)
|
|
float sign = (centered < ZERO) ? NEG1 : ONE;
|
|
// absolute value: |x|
|
|
float abs_x = fabsf(centered);
|
|
// if abs_x > 0: result = sign * exp(log(|x|) * (1/C))
|
|
if (abs_x > ZERO) {
|
|
float log_val = log10f(abs_x);
|
|
float result = powf(10.0f, log_val / C);
|
|
centered = sign * result;
|
|
}
|
|
// fall through to linear with transformed x
|
|
x = centered * HALF + HALF; // remap back to [0,1]
|
|
}
|
|
}
|
|
|
|
// Path 3: linear interpolation (always applied after transform)
|
|
float slope = band->B - band->A;
|
|
return slope * x + band->A;
|
|
}
|
|
|
|
// FUN_180563440: LUT curve evaluation for 1024 bins
|
|
// r13 = context pointer (param_1)
|
|
// Reads: band config at r13+0x188 (one per band)
|
|
// Writes: output at r13+0x198 (1024 doubles, stride 8)
|
|
void lut_curve_eval(void* ctx, int bin_start, int bin_end) {
|
|
auto* base = static_cast<uint8_t*>(ctx);
|
|
int band_count = *reinterpret_cast<int*>(base + 0x540868);
|
|
if (band_count <= 0) {
|
|
// Initialize with default 0x800 bins
|
|
band_count = 0x800; // 2048? or 1024?
|
|
}
|
|
|
|
// Output pointer: r13+0x198
|
|
double* output = reinterpret_cast<double*>(base + 0x198);
|
|
|
|
// Evaluate LUT curve for each bin (0x400 = 1024 iterations)
|
|
for (int bin = 0; bin < 0x400; bin++) {
|
|
float x = static_cast<float>(bin) * SCALE;
|
|
x = fminf(fmaxf(x, ZERO), ONE); // clamp to [0, 1]
|
|
|
|
BandConfig* band = reinterpret_cast<BandConfig*>(base + 0x188);
|
|
float result = eval_lut_bin(x, band);
|
|
|
|
// Store as double-precision (line 196: cvtss2sd + movsd [rsi])
|
|
output[bin] = static_cast<double>(result);
|
|
}
|
|
}
|
|
|
|
// FUN_18056e3e0: twin-mask factory
|
|
// Creates per-band mask by applying twin resonance to the LUT curve
|
|
// band_count = number of bands (max 6)
|
|
// N = 1024 (FFT size for LUT evaluation)
|
|
// Output stride: 0x2000 (8192 bytes = 1024 doubles)
|
|
void twin_mask_factory(void* ctx, int band_idx, int n_bins) {
|
|
auto* base = static_cast<uint8_t*>(ctx);
|
|
// Calls twin evaluation for each bin
|
|
// TODO: transcribe the full loop from disassembly
|
|
// The factory applies the band's resonance shape to the LUT curve
|
|
}
|
|
|
|
// FUN_180563a60: band combine
|
|
// Combines 6 band masks into final per-bin gain
|
|
// Stereo: max 2 channels, output stride per band = 0x2000
|
|
// Pattern: gain = 1.0 - sum(band_masks)
|
|
void band_combine(void* ctx, int n_channels, int n_bins) {
|
|
auto* base = static_cast<uint8_t*>(ctx);
|
|
int band_count = *reinterpret_cast<int*>(base + 0x540868);
|
|
if (band_count > 6) band_count = 6;
|
|
|
|
// Output accumulator at r13+0x2198
|
|
// Each band's mask is at r13+0x2198 + band_idx * 0x2000
|
|
|
|
for (int ch = 0; ch < n_channels; ch++) {
|
|
// For each bin: sum all band contributions
|
|
// Then invert: gain = 1.0 - sum
|
|
double* acc = reinterpret_cast<double*>(base + 0x2198 + ch * 0x2000);
|
|
for (int bin = 0; bin < n_bins; bin++) {
|
|
acc[bin] = ONE - acc[bin];
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUN_180529fe0: coefficient setup (from decomp_funs.txt)
|
|
// Generates per-band coefficients via PRNG, applies depth scaling
|
|
// This is the vtable method for Soothe2Module
|
|
void coefficient_setup(void* ctx, int band_idx, int param3, int param4) {
|
|
auto* base = static_cast<uint8_t*>(ctx);
|
|
|
|
// Lock (atomic flag at 0x2404dc)
|
|
uint32_t* lock = reinterpret_cast<uint32_t*>(base + 0x2404dc);
|
|
// LOCK(); *lock |= 1; UNLOCK(); // simplified
|
|
|
|
// PRNG state update (LCG)
|
|
int32_t state = *reinterpret_cast<int32_t*>(base + PRNG_STATE);
|
|
state = (state + 0x3cdca) & 0x7fffffff;
|
|
*reinterpret_cast<int32_t*>(base + PRNG_STATE) = state;
|
|
|
|
// Load LUT coefficients
|
|
float* lut_table = reinterpret_cast<float*>(base + PRNG_LUT);
|
|
float coeff0 = lut_table[state];
|
|
float coeff1 = lut_table[state + 1];
|
|
|
|
// Generate 6 coefficient pairs
|
|
// Each pair: (coeff_i * scale + offset) * global_scale
|
|
float acc = ZERO;
|
|
for (int i = 0; i < 3; i++) {
|
|
state = (state + 0x140236 + i * 0x10d56) & 0x7fffffff;
|
|
float a = lut_table[state];
|
|
float b = lut_table[state + 1];
|
|
acc += a * b;
|
|
}
|
|
|
|
// Normalize
|
|
float normalized = acc / static_cast<float>(param3);
|
|
|
|
// Apply depth scaling: powf(normalized, depth)
|
|
float depth = *reinterpret_cast<float*>(base + 0x2c);
|
|
float depthScaled = powf(normalized, depth);
|
|
|
|
// Store result
|
|
*reinterpret_cast<float*>(base + 0x54088c) = depthScaled;
|
|
|
|
// Apply sharpness weight
|
|
float sharpness = *reinterpret_cast<float*>(base + 0x540870);
|
|
depthScaled *= sharpness;
|
|
|
|
// Invert: gain = 1 - mask
|
|
*reinterpret_cast<float*>(base + 0x54088c) = ONE - depthScaled;
|
|
}
|