Heat: authentic 1e7-unit heat + the ambient radiator land (task #9)
- Emitters: heatPortion closed form = heatCostToFire x 1e7 x (charge/seekV)^2 (PPC 1.1e8 -> +632K on its own sink); the missing projectile/missile heat adds (raw -- resources author pre-scaled 1e7 units). - The bank's AMBIENT RADIATOR @4ae73c reconstructed (the system's ONLY heat exit; conductance x 0.1 x HeatSinkCount -- _DAT_004ae974 float80 = 0.1) + the link-attach guard corrected (skip = the 0xBBE bank, not Condenser; the inversion blocked condenser->bank links and closed the system). - Constant corrections (all byte-verified float80s): coolant epsilons 0.0025/0.003/1e-4 (was a single 1e-4 serving three sites); CoolantCapacityScale 0.05 (was 1.738). - Verified: heat flows + exits; max-rate autofire overheats weapons into the authentic heatLoad range-cutout (thermal spam unsustainable by design). - KNOWN REMAINING: linked-sink routing scrambled (heat pools in Condenser1; authored map says PPC->C4/C6) -- the heat-stream offset audit, filed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
fd055281a8
commit
4ed2bbc293
@@ -165,6 +165,27 @@ authentic path scoped.
|
||||
LODReuseHysteresis corrected 0.82→0.33 (a double); the MP beam-kill live-verify awaits a
|
||||
clear-sightline spawn (mechanism identical to solo; force-dmg cross-pod cycles verified).
|
||||
|
||||
- **HEAT (task #9, 2026-07-11): authentic 1e7-unit heat LIVE; conduction ROUTING defect
|
||||
remains [T2/T3].** Landed + byte-anchored: emitters emit `heatCostToFire x 1e7 x
|
||||
(charge/seekV)^2` (closed form; PPC = 1.1e8 -> +632 K on its 174000-mass sink); the missing
|
||||
projectile/missile heat adds (raw pre-scaled resources); the bank's AMBIENT RADIATOR @4ae73c
|
||||
(the system's only heat exit: relax toward 300 K, conductance x 0.1 x HeatSinkCount
|
||||
[_DAT_004ae974 float80 = 0.1 -- the audit's "1.7375" was a misread]); the link-attach guard
|
||||
corrected (skip = the 0xBBE bank, NOT Condenser -- the inversion had closed the system);
|
||||
three coolant epsilon corrections (0.0025/0.003/1e-4 for the old single 1e-4) +
|
||||
CoolantCapacityScale = 0.05 float80 (both prior readings wrong). Verified: heat flows,
|
||||
exits (~2/3 of a 1e10 spam run drained), weapons overheat under max-rate autofire exactly
|
||||
as the authored-constant simulation predicts (thermal spam is unsustainable BY DESIGN;
|
||||
heatLoad -> effectiveRange 0 = the authentic overheat cutout). **REMAINING DEFECT: the
|
||||
linked-sink ROUTING is scrambled** -- both PPCs' heat pools in Condenser1 (observed 6800 K)
|
||||
while their AUTHORED condensers (PPC_1->C4, PPC_2->C6) sit at 72 K; heat.hpp:186 already
|
||||
notes the heat-stream parse reading neighbouring fields for thermalMass/heatSinkIndex --
|
||||
audit the type-0x11/heat-family stream offsets + the "+2-biased" index resolve. Also
|
||||
[T4]: who reads FailureHeat to BLOCK firing; the HeatWatcher/AmmoBin cook-off watchedLink;
|
||||
the mech-level heatAlarm@0x450 producer. KB sweep pending: the "Heat MFD authentically
|
||||
NEAR-STATIC" claim (gauges-hud.md) was computed at the degenerate scale -- at authentic
|
||||
scale the MFD is dynamic.
|
||||
|
||||
## Locomotion / combat polish (non-gating)
|
||||
- Authentic per-mech TURN-RATE constant (currently a bring-up constant rate).
|
||||
- Body-callback gimp handlers (states 16-19, targets 0x70b2/0x7161 undecoded → fall back to stand);
|
||||
|
||||
@@ -201,11 +201,19 @@ void
|
||||
damagePortion = (damageFraction * energyTotal * kDamageScale)
|
||||
* chargeRatio * chargeRatio; // 0x44c
|
||||
}
|
||||
// Heat stays on the bring-up scale until the heat-calibration audit [T3]:
|
||||
// the authentic heatPortion is heatCostToFire x 1e7 energy units (the
|
||||
// missile path already feeds that scale raw); switching the emitters too
|
||||
// is the heat audit's call, not the damage economy's.
|
||||
heatPortion = energy - damageFraction * energy; // 0x450
|
||||
// THE AUTHENTIC HEAT (task #9; the calibration audit verified the whole
|
||||
// HeatSink chain is 1e7-unit-native and formula-exact): the closed form
|
||||
// mirroring the damage fix -- heatPortion = (1 - dF) x energyTotal x
|
||||
// (charge/seekV)^2 = heatCostToFire x 1e7 at full charge (PPC = 1.1e8 ->
|
||||
// +632 K on its own 174000-mass sink, conducting to the 1.39e6 bank;
|
||||
// authored thresholds 77 start / 1000 degradation / 2000 failure).
|
||||
{
|
||||
Scalar vRecH = seekVoltage[seekVoltageRecommendedIndex];
|
||||
Scalar chargeRatioH = (vRecH > 0.0f) ? (currentLevel / vRecH) : 1.0f;
|
||||
heatPortion = (1.0f - damageFraction) * energyTotal
|
||||
* chargeRatioH * chargeRatioH; // 0x450
|
||||
}
|
||||
(void)energy;
|
||||
|
||||
// dump the heat portion into our INHERITED thermal accumulator if heatable/online.
|
||||
// E5: pendingHeat (HeatSink @0x1C8) is the field the heat sim absorbs each frame
|
||||
|
||||
@@ -62,13 +62,21 @@
|
||||
// _DAT_004ad880 : 00 00 80 3f == 1.0f
|
||||
// _DAT_004ada90 : 00 00 80 3f == 1.0f (the "1 - exp(...)" unit term)
|
||||
// _DAT_004adbf4 : 17 b7 d1 38 == 1.0e-4f (heat-equalise threshold)
|
||||
// _DAT_004adcfc : 17 b7 d1 38 == 1.0e-4f (coolant-draw threshold)
|
||||
// _DAT_004adcfc : 17 b7 d1 38 == 1.0e-4f (DrawCoolant |amount| gate)
|
||||
// _DAT_0050e3d8 : 0a d7 23 3b == 0.0025f (draw-zero floor + OFF hysteresis)
|
||||
// _DAT_0050e3d4 : a6 9b 44 3b == 0.003f (coolantActive ON threshold)
|
||||
//
|
||||
static const Scalar HeatLoadScale = 0.002f; // _DAT_004ad870 (80-bit extended)
|
||||
static const Scalar HeatLoadMinimum = 0.0f; // _DAT_004ad87c
|
||||
static const Scalar HeatLoadMaximum = 1.0f; // _DAT_004ad880
|
||||
static const Scalar HeatEqualizeEpsilon = 1.0e-4f; // _DAT_004adbf4
|
||||
static const Scalar CoolantDrawEpsilon = 1.0e-4f; // _DAT_004adcfc / 0050e3d8 / 0050e3d4
|
||||
// task #9 correction: the old single CoolantDrawEpsilon (1e-4) served THREE
|
||||
// distinct byte-verified constants -- the draw floor / OFF hysteresis are
|
||||
// 0.0025 and the ON threshold 0.003 (the 1e-4 reading made the leak floor +
|
||||
// status-bit hysteresis fire ~25-30x too eagerly).
|
||||
static const Scalar CoolantDrawGate = 1.0e-4f; // _DAT_004adcfc (DrawCoolant |amount|)
|
||||
static const Scalar CoolantDrawFloor = 0.0025f; // _DAT_0050e3d8 (zero floor + OFF)
|
||||
static const Scalar CoolantActiveOn = 0.003f; // _DAT_0050e3d4 (ON threshold)
|
||||
|
||||
|
||||
//###########################################################################
|
||||
@@ -466,9 +474,15 @@ HeatSink::HeatSink(
|
||||
|
||||
//
|
||||
// Resolve and attach the linked heat sink referenced by heatSinkIndex.
|
||||
// (Skipped for "Condenser"-class segments -- see @0041a1a4 guard.)
|
||||
// task #9 GUARD CORRECTION: the binary's @0041a1a4 test is against GUID
|
||||
// 0x50e590 = the AGGREGATE bank (0xBBE "HeatSinkBank"), NOT Condenser --
|
||||
// only the BANK skips the link-attach (its authored SinkIdx=5 is dead;
|
||||
// it radiates to ambient instead). The old Condenser guard BLOCKED the
|
||||
// condenser->bank conduction links, which (with the ambient radiator
|
||||
// deferred) closed the system: heat could never leave -- the observed
|
||||
// monotonic runaway once emitters went authentic.
|
||||
//
|
||||
if (!IsDerivedFrom(*Condenser::GetClassDerivations())) // FUN_0041a1a4(*this[3], 0x50e590)
|
||||
if (!IsDerivedFrom("HeatSinkBank")) // FUN_0041a1a4(*this[3], 0x50e590)
|
||||
{
|
||||
// ⚠ ROOT-CAUSE FIX (the BGF-load heap corruption): heatSinkIndex indexes the
|
||||
// owner's SUBSYSTEM ROSTER, NOT the skeleton segment table. Raw @004adda0
|
||||
@@ -596,7 +610,7 @@ void
|
||||
&& (Entity *)owner == application->GetViewpointEntity() && pendingHeat > 0.0f)
|
||||
{
|
||||
s_heatLog = 0.0f;
|
||||
DEBUG_STREAM << "[heat] sink temp=" << currentTemperature
|
||||
DEBUG_STREAM << "[heat] " << GetName() << " temp=" << currentTemperature
|
||||
<< " heatEnergy=" << heatEnergy << " absorbed=" << pendingHeat
|
||||
<< " heatLoad=" << heatLoad << "\n" << std::flush;
|
||||
}
|
||||
@@ -781,7 +795,7 @@ void
|
||||
::DamageZone *ownZone = this->Subsystem::damageZone; // @0xE0 (word 0x38)
|
||||
Scalar zoneDamage = (ownZone != 0) ? ownZone->damageLevel : 0.0f; // +0x158
|
||||
coolantDraw = zoneDamage * heatLoad; // *(this[0x38]+0x158) * this[0x48]
|
||||
if (coolantDraw < CoolantDrawEpsilon)
|
||||
if (coolantDraw < CoolantDrawFloor) // _DAT_0050e3d8 = 0.0025
|
||||
{
|
||||
coolantDraw = 0.0f;
|
||||
}
|
||||
@@ -793,7 +807,7 @@ void
|
||||
}
|
||||
coolantLevel -= amount;
|
||||
|
||||
if (fabsf(amount) > CoolantDrawEpsilon)
|
||||
if (fabsf(amount) > CoolantDrawGate) // _DAT_004adcfc = 1e-4
|
||||
{
|
||||
coolantLevel += DrawCoolant(amount); // virtual: (**(*this+0x38))(this, amount)
|
||||
}
|
||||
@@ -801,11 +815,11 @@ void
|
||||
//
|
||||
// Draw state machine (this+0x138).
|
||||
//
|
||||
if (coolantActive == 0 && coolantDraw > CoolantDrawEpsilon)
|
||||
if (coolantActive == 0 && coolantDraw > CoolantActiveOn) // _DAT_0050e3d4 = 0.003
|
||||
{
|
||||
coolantActive = 1;
|
||||
}
|
||||
else if (coolantActive == 1 && coolantDraw < CoolantDrawEpsilon)
|
||||
else if (coolantActive == 1 && coolantDraw < CoolantDrawFloor) // _DAT_0050e3d8 = 0.0025
|
||||
{
|
||||
coolantActive = 0;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
// Recovered .data constants (best-effort; exact double bit-patterns folded to
|
||||
// decimal where known). TODO: confirm against section_dump.txt.
|
||||
//
|
||||
static const Scalar CoolantCapacityScale = 1.738f; // _DAT_004af518 (~ master-sink capacity scale)
|
||||
static const Scalar CoolantCapacityScale = 0.05f; // _DAT_004af518 (float80, byte-verified
|
||||
// task #9; both prior readings -- 1.738
|
||||
// and the audit's 1.675 -- were misreads)
|
||||
|
||||
// FUN_0049f788 -- defined near the foot of this file; forward-declared for MoveValve.
|
||||
void BTRecomputeCondenserValves(Entity *owner);
|
||||
@@ -720,10 +722,66 @@ AggregateHeatSink::AggregateHeatSink(
|
||||
Check_Pointer(subsystem_resource);
|
||||
|
||||
heatSinkCount = subsystem_resource->heatSinkCount; // this[0x74] @0x1D0 = res +0xFC
|
||||
ambientTemperature = 300.0f; // this[0x75] @0x1D4 (_DAT_004ae89c) FROZEN
|
||||
ambientTemperature = 300.0f; // this[0x75] @0x1D4 (_DAT_004ae89c)
|
||||
|
||||
// task #9 (the ambient radiator lands): the binary ctor @4ae8d0 scales the
|
||||
// bank's conductance by 0.1 x HeatSinkCount (_DAT_004ae974 float80 = 0.1
|
||||
// byte-verified; madcat count 14 -> x1.4) and installs the RADIATOR
|
||||
// Performance @4ae73c on masters -- the system's ONLY heat exit.
|
||||
thermalConductance = 0.1f * (Scalar)heatSinkCount * thermalConductance;
|
||||
if ((owner->simulationFlags & 0xC) == 0
|
||||
&& (owner->simulationFlags & 0x100) != 0)
|
||||
{
|
||||
SetPerformance((HeatSink::Performance)
|
||||
&AggregateHeatSink::RadiatorSimulation); // PTR @0050e5e8 = @4ae73c
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
// @4ae73c -- the bank's authentic Performance (task #9; was DEFERRED): its own
|
||||
// absorb/temperature/load step, then the AMBIENT RADIATOR -- relax toward the
|
||||
// 300 K setpoint (target = 300 - (300 - ambientTemperature) x 3.0; = 300 with
|
||||
// the frozen setpoint) with rate k = conductance x (1 - ownZoneDamage) x
|
||||
// (coolant/capacity) x flowScale / mass -- the only place heat LEAVES the
|
||||
// mech. Tail: top the bank's coolant up from the linked reservoir
|
||||
// (DrawCoolant, vtable +0x38) whenever below capacity. The old deferral note
|
||||
// ("raw self+0xE0 -> [+0x158] does not map") was wrong -- that read is the
|
||||
// engine-base DamageZone's damageLevel, the same named-member pattern
|
||||
// UpdateCoolant already uses. [T1 constants: 300 / 3.0 / 1.0 / 0.0 / 1e-4]
|
||||
//
|
||||
void
|
||||
AggregateHeatSink::RadiatorSimulation(Scalar time_slice)
|
||||
{
|
||||
if (HeatModelActive()) // FUN_004ad7d4
|
||||
{
|
||||
heatEnergy += pendingHeat; // [0x56] += [0x72]
|
||||
currentTemperature = heatEnergy / thermalMass; // [0x45] = [0x56]/[0x55]
|
||||
UpdateHeatLoad(); // FUN_004ad7f0
|
||||
pendingHeat = 0.0f;
|
||||
|
||||
Scalar target = 300.0f
|
||||
- (300.0f - ambientTemperature) * 3.0f; // _DAT_004ae89c/_DAT_004ae8a0
|
||||
::DamageZone *ownZone = this->Subsystem::damageZone; // @0xE0 (word 0x38)
|
||||
Scalar zoneDamage = (ownZone != 0) ? (Scalar)ownZone->damageLevel : 0.0f;
|
||||
double ex = (double)(-(time_slice * thermalConductance
|
||||
* (1.0f - zoneDamage) // _DAT_004ae8a4 - dmg
|
||||
* (coolantLevel / thermalCapacity) * coolantFlowScale)
|
||||
/ thermalMass);
|
||||
Scalar decay = 1.0f - (Scalar)exp(ex); // FUN_004dca38
|
||||
pendingHeat += -((currentTemperature * massScale - target)
|
||||
* thermalMass * decay);
|
||||
}
|
||||
|
||||
// coolant top-up from the reservoir (the binary tail: deficit > 0 and
|
||||
// past the 1e-4 gate -> DrawCoolant via vtable +0x38).
|
||||
Scalar deficit = thermalCapacity - coolantLevel; // [0x4a] - [0x4b]
|
||||
if (deficit > 0.0f && fabsf(deficit) > 1.0e-4f) // _DAT_004ae8a8/_DAT_004ae8ac
|
||||
{
|
||||
coolantLevel += DrawCoolant(deficit); // vcall +0x38
|
||||
}
|
||||
}
|
||||
|
||||
AggregateHeatSink::~AggregateHeatSink()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
@@ -347,6 +347,10 @@
|
||||
); // @4ae8d0
|
||||
~AggregateHeatSink();
|
||||
|
||||
// @4ae73c -- the authentic bank Performance (task #9): absorb + the
|
||||
// AMBIENT RADIATOR (the system's only heat exit) + coolant top-up.
|
||||
void RadiatorSimulation(Scalar time_slice);
|
||||
|
||||
static int CreateStreamedSubsystem( // @4ae9dc (offline content-build only)
|
||||
NotationFile *model_file,
|
||||
const char *model_name,
|
||||
|
||||
@@ -218,6 +218,14 @@ void MissileLauncher::FireWeapon()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
// THE FIRING HEAT (task #9): heatCostToFire RAW into the launcher's own
|
||||
// pendingHeat (pre-scaled 1e7-unit resources: SRM6 = 5e7, LRM15 = 6.5e7)
|
||||
// -- the binary's add @part_013.c:8744-8747, gated on the heatable check.
|
||||
if (HeatModelActive()) // FUN_004ad7d4 (heat.hpp; [T3] permissive)
|
||||
{
|
||||
pendingHeat += heatCostToFire; // HeatSink @0x1C8, raw
|
||||
}
|
||||
|
||||
// A salvo draws one ammo unit from the linked bin; a dry / not-ready bin
|
||||
// latches NoAmmo (ConsumeRound) and aborts the launch.
|
||||
if (!ConsumeRound()) // FUN_004bd4f4 (AmmoBin::FeedAmmo)
|
||||
|
||||
@@ -598,11 +598,19 @@ void
|
||||
{
|
||||
Check(this);
|
||||
|
||||
// THE FIRING HEAT (task #9; was "a separate pre-existing gap"): the
|
||||
// binary adds heatCostToFire RAW to the weapon's own pendingHeat --
|
||||
// projectile resources author PRE-scaled 1e7-unit heat (AFC100 = 6.5e7)
|
||||
// -- gated on the heatable check (part_013.c:8744-8747 idiom).
|
||||
if (HeatModelActive()) // FUN_004ad7d4 (heat.hpp; [T3] permissive)
|
||||
{
|
||||
pendingHeat += heatCostToFire; // HeatSink @0x1C8, raw
|
||||
}
|
||||
|
||||
// Binary @4bc136-4bc19c (task #56): the firing RECOIL kick, BEFORE the ammo
|
||||
// pull (no early-out precedes it in the binary). Gate: per-shot damage >
|
||||
// 3.0f (@0x4bc3f4) && the owner mech has a gyro. Direction (0, 0.6, -1.5)
|
||||
// (0x3f19999a/0xbfc00000), magnitude = damageAmount / 16 (@0x4bc3f8).
|
||||
// (The binary's preceding heat add is a separate pre-existing gap.)
|
||||
if (damageData.damageAmount > 3.0f && owner != 0)
|
||||
{
|
||||
extern void GyroApplyDamageImpulse(Subsystem *, Scalar, Scalar, Scalar, Scalar);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import struct
|
||||
data = open(r'C:/git/bt411/content/BTL4.RES','rb').read()
|
||||
n = len(data)
|
||||
|
||||
def cstr(off, maxlen=32):
|
||||
s = data[off:off+maxlen].split(b'\0')[0]
|
||||
try: return s.decode('ascii')
|
||||
except: return None
|
||||
|
||||
def f32(off): return struct.unpack_from('<f', data, off)[0]
|
||||
def i32(off): return struct.unpack_from('<i', data, off)[0]
|
||||
|
||||
# find ALL subsystem records (same heuristic as scan_weapsub.py)
|
||||
recs = []
|
||||
i = 0
|
||||
while i < n - 0x28:
|
||||
cid = struct.unpack_from('<I', data, i+0x20)[0]
|
||||
if 0xBB0 <= cid <= 0xBE5:
|
||||
sz = struct.unpack_from('<I', data, i+0x24)[0]
|
||||
if 0xE0 <= sz <= 0x300:
|
||||
name = cstr(i)
|
||||
if name and 2 <= len(name) <= 31 and all(32 < ord(c) < 127 for c in name):
|
||||
pad = data[i+len(name):i+0x20]
|
||||
if pad.count(0) >= len(pad) - 2:
|
||||
recs.append((i, name, cid, sz))
|
||||
i += 0x28
|
||||
continue
|
||||
i += 1
|
||||
|
||||
# group into chains (consecutive: next record at p+sz)
|
||||
chains = []
|
||||
cur = []
|
||||
for k,(p,name,cid,sz) in enumerate(recs):
|
||||
if cur and cur[-1][0] + cur[-1][3] == p:
|
||||
cur.append(recs[k])
|
||||
else:
|
||||
if cur: chains.append(cur)
|
||||
cur = [recs[k]]
|
||||
if cur: chains.append(cur)
|
||||
|
||||
mechnames = [b'madcat', b'bhk', b'vulture', b'thor', b'blackhawk', b'blh', b'owens', b'strider', b'sunder', b'cauldron', b'avatar', b'mdc']
|
||||
low = data.lower()
|
||||
namepos = []
|
||||
for m in mechnames:
|
||||
j = 0
|
||||
while True:
|
||||
j = low.find(m, j)
|
||||
if j < 0: break
|
||||
namepos.append((j, m.decode()))
|
||||
j += 1
|
||||
namepos.sort()
|
||||
|
||||
def nearest_label(p):
|
||||
best = None
|
||||
for pos, name in namepos:
|
||||
if pos < p: best = (pos, name)
|
||||
else: break
|
||||
return best
|
||||
|
||||
HEAT = {0xBBD:'Condenser', 0xBBE:'AggregateHeatSink', 0xBC0:'Reservoir', 0xBBB:'HeatSink?', 0xBBC:'?'}
|
||||
WEAP = {0xBC8:'Emitter', 0xBD4:'PPC', 0xBCD:'ProjWeapon', 0xBCE:'GaussRifle', 0xBD0:'MissileLauncher'}
|
||||
|
||||
for ci, ch in enumerate(chains):
|
||||
lbl = nearest_label(ch[0][0])
|
||||
names = [c[1] for c in ch]
|
||||
print(f'--- chain {ci} @0x{ch[0][0]:x} n={len(ch)} nearest-name={lbl}')
|
||||
for idx,(p,name,cid,sz) in enumerate(ch):
|
||||
tag = HEAT.get(cid) or WEAP.get(cid)
|
||||
if cid in HEAT:
|
||||
st, dg, ft = f32(p+0xE4), f32(p+0xE8), f32(p+0xEC)
|
||||
tc, tm = f32(p+0xF0), f32(p+0xF4)
|
||||
hsi = i32(p+0xF8)
|
||||
line = (f' [{idx:2d}] {name:20s} cid=0x{cid:X}({tag}) sz=0x{sz:X} '
|
||||
f'StartT={st:g} DegT={dg:g} FailT={ft:g} Conduct={tc:g} ThermMass={tm:g} HeatSinkIdx={hsi}')
|
||||
if cid == 0xBBD:
|
||||
line += f' RefrigFactor={f32(p+0xFC):g}'
|
||||
elif cid == 0xBBE:
|
||||
line += f' HeatSinkCount={i32(p+0xFC)}'
|
||||
elif cid == 0xBC0:
|
||||
line += f' CoolantCap={f32(p+0xFC):g} SquirtMass={f32(p+0x100):g}'
|
||||
print(line)
|
||||
elif cid in WEAP:
|
||||
heat = f32(p+0x1A4)
|
||||
print(f' [{idx:2d}] {name:20s} cid=0x{cid:X}({tag}) Heat={heat:g}')
|
||||
else:
|
||||
print(f' [{idx:2d}] {name:20s} cid=0x{cid:X} sz=0x{sz:X}')
|
||||
Reference in New Issue
Block a user