namespace AcDream.Core.CharGen;
///
/// Pure port of retail's shade→palette-index resolution
/// (PalSet::GetPaletteID @ 0x005AC570, invoked from
/// gmCG3DView::Update @ 0x004EE9D0 for the skin/hair subpalette
/// build and from ClothingTable::BuildObjDesc @ 0x005A7900 for every
/// clothing-slot dye choice). The decompiled body is genuinely FPU-elided —
/// the _ftol2() truncating-cast operand is lost to the decompiler,
/// and can only be read as "some product of -ish and
/// -ish operands" from the surrounding x87 stack
/// traffic — but the decomp's own control-flow SHAPE is still verifiable
/// independent of that lost operand: a two-sided FPU compare at
/// 0x005AC5A0 gating on >= 0.0, consistent with a
/// [0,1] shade bounds check before the cast. What resolves the
/// elided operand is ACE's ACE.DatLoader.FileTypes.PaletteSet.GetPaletteID,
/// which carries the explicit comment "Taken from acclient.c
/// (PalSet::GetPaletteID)" against the exact formula below. That is TWO
/// sources (decomp control flow + ACE's cited port), not three: the
/// PaletteSet.cs file present in the vendored ACViewer checkout is
/// ACE's own file, not an independent reimplementation, and ACViewer's
/// ClothingTableList.xaml.cs:97 UI slider computes a DIFFERENT
/// expression for a DIFFERENT problem (mapping a shade back to a slider tick
/// position against Shades.Maximum, i.e. count-1, not
/// count) — neither corroborates this formula and both are dropped
/// from the evidence chain here.
///
public static class ChargenPalSetMath
{
///
/// Resolves a shade fraction to an index into a palette-id list of the
/// given . Returns -1 (retail's
/// INVALID_DID outcome) when is
/// non-positive or falls outside
/// [0.0, 1.0] — including retail's own -1.0 "unset"
/// sentinel (CharGenState::Reset @ 0x005C68A0), which is
/// deliberately out of range so an untouched shade resolves to
/// "nothing," matching retail. Callers should treat -1 as "skip this
/// subpalette contribution" rather than emit a placeholder id.
///
public static int GetPaletteIndex(int count, double shade)
{
if (count <= 0 || shade < 0.0 || shade > 1.0)
return -1;
// Truncating cast, exactly as ACE's cited port and the decomp's
// _ftol2() (which truncates toward zero on x86, matching a plain
// C-style (int) cast here since count > 0 and 0 <= shade <= 1 keep
// the product non-negative).
int index = (int)((count - 0.000001) * shade);
if (index < 0)
index = 0;
if (index > count - 1)
index = count - 1;
return index;
}
}