More d/lyt (#52)

* More Lyt (maybe some wrong type associations)

* more meter

* Rename

* More splits and renames

* Fix SizedString conversion operators

* Renames

* dLytBattery_c almost OK

* d_lyt_battery OK

* diff clean

* Continue doing splits while I'm at it

* two more splits
This commit is contained in:
robojumper
2024-10-06 22:34:31 +02:00
committed by GitHub
parent 25d80826c7
commit f8e61c7de8
43 changed files with 1987 additions and 350 deletions
+2 -2
View File
@@ -47,8 +47,8 @@ public:
return mRefCount != 0;
}
inline const char *name() const {
return &mArcName;
inline const SizedString<32> &name() const {
return mArcName;
}
inline bool isNotLoaded() {
+81 -50
View File
@@ -1,10 +1,15 @@
#ifndef D2D_H
#define D2D_H
#include <libms/msgfile.h>
#include <m/m2d.h>
#include <d/lyt/meter/d_lyt_meter_base.h>
#include <nw4r/lyt/lyt_pane.h>
#include <nw4r/lyt/lyt_picture.h>
class dTextBox_c;
class dWindow_c;
namespace d2d {
class ResAccIf_c : public m2d::ResAccIf_c {
@@ -50,6 +55,10 @@ public:
nw4r::lyt::Bounding *findBounding(const char *name) const;
void unbindAnims();
const Layout_c *getLayout() const {
return &mLayout;
}
Layout_c *getLayout() {
return &mLayout;
}
@@ -73,13 +82,38 @@ public:
mLayout.Draw(mDrawInfo);
};
virtual bool build(const char *name, m2d::ResAccIf_c *acc) override;
dTextBox_c *getTextBox(const char *name);
dTextBox_c *getSizeBoxInWindow(const char *windowName);
dWindow_c *getWindow(const char *name);
nw4r::lyt::Group *findGroupByName(const char *name);
bool fn_800AB940(const char *name, int arg);
bool fn_800AB9A0(dTextBox_c *textbox, int arg);
bool fn_800ABE50(dTextBox_c *textbox, int arg, void *unk);
static void linkMeters(nw4r::lyt::Group *group, LytMeterGroup *meterGroup);
private:
void *mpMsbtInfo;
void setPropertiesRecursive(nw4r::lyt::Pane *pane, f32, f32, f32, f32, f32);
void setProperties(nw4r::lyt::Pane *pane, f32, f32, f32, f32, f32);
dTextBox_c *getTextBoxViaUserData(nw4r::lyt::Pane *pane, const char *name);
bool fn_800ABB80(dTextBox_c *textbox1, dTextBox_c *textbox2, int arg);
bool fn_800ABCE0(const nw4r::lyt::res::ExtUserData *userDatum, dTextBox_c *textbox1, dTextBox_c *textbox2, int arg);
bool fn_800AC040(dTextBox_c *textbox1, dTextBox_c *textbox2, int arg, void *unk);
bool fn_800AC1AC(const nw4r::lyt::res::ExtUserData *userDatum, dTextBox_c *textbox1, dTextBox_c *textbox2, int arg,
void *unk);
MsbtInfo *getMsbtInfo() const;
bool fn_800AB930(dTextBox_c *box);
/* 0x8C */ MsbtInfo *mpMsbtInfo;
};
struct AnmGroup_c {
AnmGroup_c() {}
~AnmGroup_c() {}
struct AnmGroupBase_c {
AnmGroupBase_c(m2d::FrameCtrl_c *frameCtrl): field_0x04(nullptr), mFlags(0), mpFrameCtrl(frameCtrl) {}
virtual ~AnmGroupBase_c() {}
bool init(const char *fileName, m2d::ResAccIf_c *acc, d2d::Layout_c *layout, const char *animName);
bool init(nw4r::lyt::AnimTransform *transform, const char *fileName, m2d::ResAccIf_c *acc, nw4r::lyt::Group *group);
@@ -87,15 +121,15 @@ struct AnmGroup_c {
bool fn_800AC6D0(bool);
bool fn_800AC7D0();
bool fn_800AC860();
void fn_800AC870(bool);
void setAnimEnable(bool);
void setAnmFrame(f32);
void syncAnmFrame();
void setForward();
void setBackward();
inline void setFrame(f32 frame) {
inline void setFrameAndControlThings(f32 frame) {
fn_800AC6D0(false);
fn_800AC870(true);
setAnimEnable(true);
mpFrameCtrl->setFrame(frame);
syncAnmFrame();
}
@@ -105,6 +139,15 @@ struct AnmGroup_c {
syncAnmFrame();
}
inline void setFrame(f32 frame) {
mpFrameCtrl->setFrame(frame);
syncAnmFrame();
}
inline f32 getFrame() const {
return mpFrameCtrl->getFrame();
}
inline void setToStart() {
mpFrameCtrl->setToStart();
syncAnmFrame();
@@ -115,62 +158,50 @@ struct AnmGroup_c {
syncAnmFrame();
}
u8 field_0x00[0x08 - 0x00];
inline bool isEndReached() const {
return mpFrameCtrl->isEndReached();
}
inline void setRate(f32 rate) {
mpFrameCtrl->setRate(rate);
setAnimEnable(true);
}
inline bool isFlag2() const {
return (mFlags & 2) != 0;
}
private:
/* 0x04 */ void *field_0x04;
/* 0x08 */ m2d::FrameCtrl_c *mpFrameCtrl;
/* 0x0C */ u8 mFlags;
/* 0x10 */ nw4r::lyt::AnimResource mAnmResource;
/* 0x20 */ nw4r::lyt::Group *mpGroup;
/* 0x24 */ nw4r::lyt::AnimTransform *mpAnmTransform;
/* 0x28 */ u8 field_0x20[0x40 - 0x28];
};
struct dLytStructB {
struct AnmGroup_c : public AnmGroupBase_c {
AnmGroup_c(): AnmGroupBase_c(&mFrameCtrl) {}
virtual ~AnmGroup_c() {}
/* 0x28 */ m2d::FrameCtrl_c mFrameCtrl;
};
// Probably pause menu specific
struct dLytStructB: public dLytMeterBase {
dLytStructB();
~dLytStructB();
virtual bool build(d2d::ResAccIf_c *resAcc) override;
virtual bool LytMeter0x10() override;
virtual bool LytMeter0x14() override;
virtual nw4r::lyt::Pane *getPane() override;
virtual void *LytMeter0x1C() override;
virtual const char *getName() const override;
void init(void *, u8);
u8 field_0x00[0x808 - 0x00];
};
struct dLytStructC {
dLytStructC();
~dLytStructC();
u8 field_0x00[0x10 - 0x00];
};
struct dLytStructD_Base {
dLytStructD_Base()
: field_0x04(0), field_0x08(0), field_0x0C(0), field_0x10(0), field_0x14(0), field_0x015(0), field_0x016(0) {}
virtual ~dLytStructD_Base();
u32 field_0x04;
u32 field_0x08;
u32 field_0x0C;
u32 field_0x10;
u16 field_0x14;
u8 field_0x015;
u8 field_0x016;
};
struct dLytStructD : dLytStructD_Base {
dLytStructD() : field_0x18(0), field_0x1C(0), field_0x20(0), field_0x22(0), field_0x23(0), field_0x24(0) {}
virtual ~dLytStructD();
void init(nw4r::lyt::Pane *, u16, u8, u8);
void append(dLytStructD *other);
void detach(dLytStructD *other);
void fn_80065E70(nw4r::lyt::Pane *, s32, s32, s32);
void fn_80065F70();
u32 field_0x18;
u32 field_0x1C;
u16 field_0x20;
u8 field_0x22;
u8 field_0x23;
u32 field_0x24;
u8 field_0x00[0x808 - 0x08];
};
} // namespace d2d
+41
View File
@@ -0,0 +1,41 @@
#ifndef D_LYT_BATTERY_H
#define D_LYT_BATTERY_H
#include <d/lyt/d2d.h>
#include <d/lyt/d_lyt_sub.h>
#include <d/lyt/meter/d_lyt_meter_base.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
class dLytBattery_c {
public:
static bool create();
static bool draw();
bool dismissManually();
private:
dLytBattery_c() : mStateMgr(*this, sStateID::null) {
sInstance = this;
}
bool doDraw();
bool init();
bool hasEnoughBatteryCharge();
STATE_FUNC_DECLARE(dLytBattery_c, Off);
STATE_FUNC_DECLARE(dLytBattery_c, In);
STATE_FUNC_DECLARE(dLytBattery_c, On);
STATE_FUNC_DECLARE(dLytBattery_c, None);
STATE_FUNC_DECLARE(dLytBattery_c, Out);
/* 0x000 */ UI_STATE_MGR_DECLARE(dLytBattery_c);
/* 0x040 */ d2d::ResAccIf_c mResAcc;
/* 0x0EB */ dLytSub mLyt;
/* 0x110 */ d2d::AnmGroup_c mAnmGroups[4];
/* 0x540 */ bool mDismissManually;
static dLytBattery_c *sInstance;
};
#endif
@@ -2,6 +2,7 @@
#define D_LYT_COMMON_ARROW_H
#include <d/lyt/d2d.h>
#include <d/lyt/d_structd.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
@@ -32,7 +33,7 @@ public:
void fn_80168880();
private:
STATE_MGR_DECLARE(dLytCommonArrow_c);
UI_STATE_MGR_DECLARE(dLytCommonArrow_c);
/* 0x40 */ d2d::LytBase_c mLytBase;
d2d::ResAccIf_c mResAcc;
d2d::AnmGroup_c field_0x440[0x9];
+40
View File
@@ -0,0 +1,40 @@
#ifndef D_LYT_DO_BUTTON_H
#define D_LYT_DO_BUTTON_H
#include <d/lyt/d2d.h>
#include <d/lyt/d_lyt_sub.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
class dLytDobutton_c {
public:
dLytDobutton_c() : mStateMgr(*this, sStateID::null) {
sInstance = this;
}
bool init(m2d::ResAccIf_c *resAcc);
dLytDobutton_c *sInstance;
private:
STATE_FUNC_DECLARE(dLytDobutton_c, InvisibleWait);
STATE_FUNC_DECLARE(dLytDobutton_c, InvisibleTimeCnt);
STATE_FUNC_DECLARE(dLytDobutton_c, In);
STATE_FUNC_DECLARE(dLytDobutton_c, Wait);
STATE_FUNC_DECLARE(dLytDobutton_c, Out);
UI_STATE_MGR_DECLARE(dLytDobutton_c);
/* 0x040 */ dLytSub mLyt;
/* 0x0D4 */ d2d::AnmGroup_c mAnmGroups[12];
/* 0x3D4 */ u8 field_0x3D4[0x470 - 0x3D4];
/* 0x470 */ s32 mDoActionToShow;
/* 0x474 */ s32 field_0x474;
/* 0x478 */ s32 field_0x478;
/* 0x47C */ s32 field_0x47C;
/* 0x480 */ s32 field_0x480;
/* 0x484 */ s32 mNextDoActionToShow;
/* 0x488 */ s32 field_0x488;
/* 0x48C */ s32 field_0x48C;
};
#endif
@@ -2,13 +2,14 @@
#define LYT_PAUSE_DISP_00_H
#include <d/lyt/d2d.h>
#include <d/lyt/d_structd.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
class dLytPauseDisp00_c {
public:
dLytPauseDisp00_c();
virtual ~dLytPauseDisp00_c();
virtual ~dLytPauseDisp00_c() {}
void init();
@@ -27,8 +28,8 @@ private:
d2d::LytBase_c mLytBase;
d2d::AnmGroup_c field_0x00D0[0x7E];
d2d::dLytStructB field_0x2050[0x18];
/* 0xE110 */ nw4r::ut::LinkList<d2d::dLytStructB, 0> mList;
d2d::dLytStructC field_0xE11C[0x18];
/* 0xE110 */ LytMeterGroup mLytMeterGroup;
LytMeterListNode field_0xE11C[0x18];
d2d::dLytStructD field_0xE29C;
};
+23
View File
@@ -0,0 +1,23 @@
#ifndef D_LYT_SUB_H
#define D_LYT_SUB_H
#include <d/lyt/d2d.h>
class dLytSub : public d2d::LytBase_c {
public:
dLytSub() {}
virtual bool build(const char *name, m2d::ResAccIf_c *acc) override {
mpName = name;
d2d::LytBase_c::build(name, acc);
}
const char *getName() const {
return mpName;
}
private:
/// This name is stored by `build` and usually accessed by dLytMeters' getName functions
const char *mpName;
};
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef D_LYT_STRUCTD_H
#define D_LYT_STRUCTD_H
#include <egg/core/eggHeap.h>
#include <nw4r/ut/ut_LinkList.h>
#include <toBeSorted/tlist.h>
namespace d2d {
struct dLytStructD_Base {
dLytStructD_Base()
: field_0x0C(0), field_0x10(0), field_0x14(0), field_0x015(0), field_0x016(0) {}
virtual ~dLytStructD_Base();
TListNode mNode;
u32 field_0x0C;
u32 field_0x10;
u16 field_0x14;
u8 field_0x015;
u8 field_0x016;
};
struct dLytStructD : dLytStructD_Base {
dLytStructD() : field_0x18(0), field_0x1C(0), field_0x20(0), field_0x22(0), field_0x23(0), field_0x24(0) {}
virtual ~dLytStructD();
void init(nw4r::lyt::Pane *, u16, u8, u8);
void fn_80065E70(nw4r::lyt::Pane *, s32, s32, s32);
void fn_80065F70();
u32 field_0x18;
u32 field_0x1C;
u16 field_0x20;
u8 field_0x22;
u8 field_0x23;
u32 field_0x24;
};
class dLytStructDList {
public:
dLytStructDList();
static dLytStructDList *create(EGG::Heap *heap);
static dLytStructDList *sInstance;
void appendToList1(dLytStructD *other);
void removeFromList1(dLytStructD *other);
void appendToList2(dLytStructD *other);
void removeFromList2(dLytStructD *other);
private:
TList<dLytStructD, 4> mList1;
TList<dLytStructD, 4> mList2;
bool field_0x18;
};
} // namespace d2d
#endif
+38 -1
View File
@@ -1,8 +1,12 @@
#ifndef D_LYT_TEXTBOX_H
#define D_LYT_TEXTBOX_H
#include <d/lyt/d2d.h>
#include <nw4r/lyt/lyt_textBox.h>
extern "C" void *lbl_805753B0;
class dTextBox_c : public nw4r::lyt::TextBox {
friend class dWindow_c;
@@ -11,8 +15,41 @@ public:
f32 GetLineWidth(f32 *pOutSpacing);
void setLytBase(d2d::LytBase_c *lytBase) {
mpLytBase = lytBase;
}
void SetScale(float scale) {
nw4r::math::VEC2 value = GetScale();
value.x = GetScale().x * scale;
value.y = GetScale().y * scale;
mScale = scale;
MySetScale(value);
nw4r::lyt::TextBox::SetScale(value);
}
static inline f32 GetTranslateX1() {
if (lbl_805753B0 != nullptr) {
return GetTranslateX1_();
} else {
return 0.0f;
}
}
static f32 GetTranslateX1_();
// @bug: This does not implement UT's RTTI, so casts to dTextBox_c will
// succeed even if all you have is a lyt::TextBox
private:
u8 field_0x104[0x204 - 0x104];
void MySetScale(const nw4r::math::VEC2 &value);
d2d::LytBase_c *mpLytBase;
u8 field_0x108[0x118 - 0x108];
/* 0x118 */ nw4r::math::VEC2 mTextScale;
/* 0x120 */ f32 mScale;
u8 field_0x124[0x204 - 0x124];
};
#endif
+3
View File
@@ -11,6 +11,9 @@ public:
virtual ~dWindow_c() {}
void UpdateSize(dTextBox_c *textBox, f32 f);
// @bug: This does not implement UT's RTTI, so casts to dWindow_c will
// succeed even if all you have is a lyt::Window
private:
UNKWORD field_0x108;
};
+23
View File
@@ -0,0 +1,23 @@
#ifndef D_LYT_METER_H
#define D_LYT_METER_H
#include <d/lyt/d2d.h>
#include <d/lyt/meter/d_lyt_meter.h>
#include <d/lyt/meter/d_lyt_meter_remocon_bg.h>
class dLytMeter_c {
public:
dLytMeter_c();
virtual ~dLytMeter_c() {}
d2d::ResAccIf_c mResAcc;
d2d::LytBase_c mLyt;
d2d::AnmGroup_c mAnmGroups[34];
// Meters...
dLytMeterRemoconBg_c mRemoCon;
LytMeterGroup mMeters;
};
#endif
+46
View File
@@ -0,0 +1,46 @@
#ifndef D_LYT_METER_BASE_H
#define D_LYT_METER_BASE_H
#include <nw4r/lyt/lyt_pane.h>
namespace d2d {
class ResAccIf_c;
}
class dLytMeterBase {
public:
dLytMeterBase() : field_0x04(false), field_0x05(0) {}
virtual ~dLytMeterBase() {}
virtual bool build(d2d::ResAccIf_c *resAcc) = 0;
virtual bool LytMeter0x10() = 0;
virtual bool LytMeter0x14() = 0;
virtual nw4r::lyt::Pane *getPane() = 0;
virtual void *LytMeter0x1C() = 0;
virtual const char *getName() const = 0;
virtual bool LytMeter0x24() const {
return field_0x04;
}
virtual void LytMeter0x28(bool arg) {
field_0x04 = arg;
}
virtual u8 LytMeter0x2C() const {
return field_0x05;
}
virtual void LytMeter0x30(u8 arg) {
field_0x05 = arg;
}
bool field_0x04;
u8 field_0x05;
};
struct LytMeterListNode {
nw4r::ut::LinkListNode mNode;
dLytMeterBase *mpMeter;
nw4r::lyt::Pane *mpPane;
};
typedef nw4r::ut::LinkList<LytMeterListNode, 0> LytMeterGroup;
#endif
@@ -0,0 +1,37 @@
#ifndef D_LYT_METER_REMOCON_BH_H
#define D_LYT_METER_REMOCON_BH_H
#include <d/lyt/d2d.h>
#include <d/lyt/d_lyt_sub.h>
#include <d/lyt/meter/d_lyt_meter_base.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
class dLytMeterRemoconBg_c : public dLytMeterBase {
public:
dLytMeterRemoconBg_c();
virtual bool build(d2d::ResAccIf_c *resAcc) override;
virtual bool LytMeter0x10() override;
virtual bool LytMeter0x14() override;
virtual nw4r::lyt::Pane *getPane() override;
virtual void *LytMeter0x1C() override;
virtual const char *getName() const override;
virtual ~dLytMeterRemoconBg_c();
private:
STATE_FUNC_DECLARE(dLytMeterRemoconBg_c, Wait);
STATE_FUNC_DECLARE(dLytMeterRemoconBg_c, On);
STATE_FUNC_DECLARE(dLytMeterRemoconBg_c, Active);
STATE_FUNC_DECLARE(dLytMeterRemoconBg_c, Off);
UI_STATE_MGR_DECLARE(dLytMeterRemoconBg_c);
dLytSub mLyt;
/* 0xD8 */ nw4r::lyt::Pane *mpPane;
/* 0xDC */ u32 field_0xDC;
/* 0xE0 */ u32 field_0xE0;
/* 0xE4 */ u32 field_0xE4;
/* 0xE8 */ u32 field_0xE8;
/* 0xEC */ bool field_0xEC;
};
#endif
+2 -7
View File
@@ -1,17 +1,12 @@
#ifndef D_T_ROCK_BOAT
#define D_T_ROCK_BOAT
#include <d/a/d_a_base.h>
#include <d/tg/d_tg.h>
#include <f/f_list_nd.h>
#include <s/s_State.hpp>
#include <s/s_StateMgr.hpp>
class dTgAbs_c : public dAcBase_c {
public:
virtual ~dTgAbs_c() {}
};
class dTgRockBoat_c : public dTgAbs_c {
class dTgRockBoat_c : public dTg_c {
public:
dTgRockBoat_c() : mStateMgr(*this, sStateID::null) {}
virtual ~dTgRockBoat_c() {}
+1
View File
@@ -46,6 +46,7 @@ protected:
class FrameCtrl_c {
public:
FrameCtrl_c() : mEndFrame(0.0f), mCurrFrame(0.0f), mPrevFrame(0.0f), mRate(1.0f) {}
virtual ~FrameCtrl_c();
void play();
+4 -2
View File
@@ -15,6 +15,8 @@ struct PaneLink {
};
} // namespace detail
typedef ut::LinkList<detail::PaneLink, 0> PaneList;
class Group {
public:
Group(const res::Group *pResGroup, Pane *pRootPane);
@@ -23,7 +25,7 @@ public:
void AppendPane(Pane *pPane);
void Init();
ut::LinkList<detail::PaneLink, 0> *GetPaneList() {
PaneList *GetPaneList() {
return &mPaneListLink;
}
bool IsUserAllocated() const {
@@ -35,7 +37,7 @@ public:
private:
ut::LinkListNode mLink; // at 0x04
ut::LinkList<detail::PaneLink, 0> mPaneListLink; // at 0x0C
PaneList mPaneListLink; // at 0x0C
char mName[NW4R_RES_NAME_SIZE + 1]; // at 0x18
bool mbUserAllocated; // at 0x29
u8 mPadding[2]; // at 0x2A
+28
View File
@@ -143,7 +143,35 @@ struct Group {
u16 paneNum; // at 0x18
u16 padding; // at 0x1A
};
enum ExtUserDataType {
TYPE_STRING,
TYPE_INT,
TYPE_FLOAT,
};
struct ExtUserData {
inline const char *GetString() const {
return detail::ConvertOffsToPtr<const char>(this, datOffs);
}
inline float GetFloat() const {
return *detail::ConvertOffsToPtr<float>(this, datOffs);
}
inline int GetInt() const {
return *detail::ConvertOffsToPtr<int>(this, datOffs);
}
inline const char *GetName() const {
return nameOffs != 0 ? detail::ConvertOffsToPtr<const char>(this, nameOffs) : nullptr;
}
inline ExtUserDataType GetType() const {
return (ExtUserDataType)type;
}
private:
u32 nameOffs; // at 0x00
u32 datOffs; // at 0x04
u16 numEntries; // at 0x08
+19
View File
@@ -53,6 +53,25 @@ public:
void SetFontSize(const Size &fontSize) {
mFontSize = fontSize;
}
f32 GetCharSpace() const {
return mCharSpace;
}
void SetCharSpace(f32 charSpace) {
mCharSpace = charSpace;
}
f32 GetLineSpace() const {
return mLineSpace;
}
void SetLineSpace(f32 lineSpace) {
mLineSpace = lineSpace;
}
ut::TagProcessorBase<wchar_t> *GetTagProcessor() const {
return mpTagProcessor;
}
+3 -3
View File
@@ -34,18 +34,18 @@ inline void SetBit(T *bits, int pos, bool val) {
}
template <typename T>
T GetBits(T bits, int pos, int len) {
inline T GetBits(T bits, int pos, int len) {
u32 mask = T(1 << pos);
return bits & mask;
}
template <typename T>
T *ConvertOffsToPtr(void *baseAddress, unsigned int offset) {
inline T *ConvertOffsToPtr(void *baseAddress, unsigned int offset) {
return (T *)((u32)baseAddress + offset);
}
template <typename T>
const T *ConvertOffsToPtr(const void *baseAddress, unsigned int offset) {
inline const T *ConvertOffsToPtr(const void *baseAddress, unsigned int offset) {
return (const T *)((u32)baseAddress + offset);
}
+3
View File
@@ -22,4 +22,7 @@
void dummy_GetStateID() { \
mStateMgr.getStateID(); \
}
#define UI_STATE_MGR_DECLARE(class_name) \
sFStateMgr_c<class_name, sStateMethodUsr_FI_c> mStateMgr;
#endif
+8 -2
View File
@@ -3,6 +3,8 @@
#include <MSL_C/string.h>
extern "C" bool strequals(const char *a, const char *b);
/**
* A statically sized string buffer used for resource
* identification where strings are guaranteed to be short.
@@ -23,11 +25,11 @@ struct SizedString {
char mChars[Size];
char *operator&() {
operator char *() {
return mChars;
}
const char *operator&() const {
operator const char *() const {
return mChars;
}
@@ -57,6 +59,10 @@ struct SizedString {
}
}
bool operator==(const char* other) const {
return strequals(mChars, other);
}
int sprintf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);