From da315234b7d988a21992ae32a7a7938c75bcacd7 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Thu, 20 Jun 2024 17:01:56 +0200
Subject: [PATCH 01/33] A first pass over m3d

---
 config/SOUE01/splits.txt               |  39 +++
 config/SOUE01/symbols.txt              | 350 ++++++++++++-------------
 configure.py                           |  10 +
 include/egg/core/eggFrmHeap.h          |   2 +-
 include/m/m3d/m3d.h                    |  41 +++
 include/m/m3d/m_anmchr.h               |  23 ++
 include/m/m3d/m_anmvis.h               |  22 ++
 include/m/m3d/m_banm.h                 |  38 +++
 include/m/m3d/m_bmdl.h                 |  34 +++
 include/m/m3d/m_calc_ratio.h           |  33 +++
 include/m/m3d/m_fanm.h                 |  40 +++
 include/m/m3d/m_mdl.h                  |  62 +++++
 include/m/m3d/m_scnleaf.h              |  41 +++
 include/m/m3d/m_smdl.h                 |  22 ++
 include/nw4r/g3d/g3d_3dsmax.h          |   3 +-
 include/nw4r/g3d/g3d_anmcamera.h       |   2 +-
 include/nw4r/g3d/g3d_anmchr.h          |   2 +-
 include/nw4r/g3d/g3d_anmobj.h          |   6 +-
 include/nw4r/g3d/g3d_anmscn.h          |   8 +-
 include/nw4r/g3d/g3d_anmtexpat.h       |   4 +-
 include/nw4r/g3d/g3d_anmtexsrt.h       |   4 +-
 include/nw4r/g3d/g3d_anmvis.h          |   4 +-
 include/nw4r/g3d/g3d_basic.h           |   6 +-
 include/nw4r/g3d/g3d_calcview.h        |   1 +
 include/nw4r/g3d/g3d_calcworld.h       |   1 +
 include/nw4r/g3d/g3d_camera.h          |   4 +-
 include/nw4r/g3d/g3d_dcc.h             |   3 +-
 include/nw4r/g3d/g3d_draw.h            |   2 +-
 include/nw4r/g3d/g3d_draw1mat1shp.h    |   4 +-
 include/nw4r/g3d/g3d_fog.h             |   6 +-
 include/nw4r/g3d/g3d_gpu.h             |   4 +-
 include/nw4r/g3d/g3d_init.h            |   6 -
 include/nw4r/g3d/g3d_light.h           |   3 +-
 include/nw4r/g3d/g3d_maya.h            |   3 +-
 include/nw4r/g3d/g3d_obj.h             |   1 +
 include/nw4r/g3d/g3d_resanmcamera.h    |   4 +-
 include/nw4r/g3d/g3d_resanmchr.h       |   2 +-
 include/nw4r/g3d/g3d_resanmclr.h       |   7 +-
 include/nw4r/g3d/g3d_resanmfog.h       |   4 +-
 include/nw4r/g3d/g3d_resanmscn.h       |   6 +-
 include/nw4r/g3d/g3d_resanmshp.h       |   2 +-
 include/nw4r/g3d/g3d_resanmtexpat.h    |   7 +-
 include/nw4r/g3d/g3d_resanmtexsrt.h    |   7 +-
 include/nw4r/g3d/g3d_resanmvis.h       |  14 +-
 include/nw4r/g3d/g3d_resdict.h         |   2 +-
 include/nw4r/g3d/g3d_resfile.h         |   4 +-
 include/nw4r/g3d/g3d_resmat.h          |   4 +-
 include/nw4r/g3d/g3d_resmdl.h          |   6 +-
 include/nw4r/g3d/g3d_resnode.h         |   6 +-
 include/nw4r/g3d/g3d_resshp.h          |   6 +-
 include/nw4r/g3d/g3d_restev.h          |   6 +-
 include/nw4r/g3d/g3d_restex.h          |   2 +-
 include/nw4r/g3d/g3d_resvtx.h          |   4 +-
 include/nw4r/g3d/g3d_scnmdl.h          |  11 +-
 include/nw4r/g3d/g3d_scnmdl1mat1shp.h  |   6 +-
 include/nw4r/g3d/g3d_scnmdlsmpl.h      |  26 +-
 include/nw4r/g3d/g3d_scnobj.h          |  30 ++-
 include/nw4r/g3d/g3d_scnproc.h         |   2 +-
 include/nw4r/g3d/g3d_scnrfl.h          |   2 +-
 include/nw4r/g3d/g3d_scnroot.h         |  17 +-
 include/nw4r/g3d/g3d_state.h           |   1 +
 include/nw4r/g3d/g3d_xsi.h             |   3 +-
 include/nw4r/snd/snd_Sound3DActor.h    |   4 +-
 include/nw4r/snd/snd_Sound3DListener.h |   2 +-
 include/nw4r/snd/snd_Sound3DManager.h  |   4 +-
 src/m/m3d/m3d.cpp                      |  99 +++++++
 src/m/m3d/m_anmchr.cpp                 |  30 +++
 src/m/m3d/m_anmvis.cpp                 |  58 ++++
 src/m/m3d/m_banm.cpp                   |  62 +++++
 src/m/m3d/m_bmdl.cpp                   |  62 +++++
 src/m/m3d/m_calc_ratio.cpp             |  75 ++++++
 src/m/m3d/m_fanm.cpp                   |  83 ++++++
 src/m/m3d/m_mdl.cpp                    | 127 +++++++++
 src/m/m3d/m_scnLeaf.cpp                |  58 ++++
 src/m/m3d/m_smdl.cpp                   |  36 +++
 75 files changed, 1439 insertions(+), 286 deletions(-)
 create mode 100644 include/m/m3d/m3d.h
 create mode 100644 include/m/m3d/m_anmchr.h
 create mode 100644 include/m/m3d/m_anmvis.h
 create mode 100644 include/m/m3d/m_banm.h
 create mode 100644 include/m/m3d/m_bmdl.h
 create mode 100644 include/m/m3d/m_calc_ratio.h
 create mode 100644 include/m/m3d/m_fanm.h
 create mode 100644 include/m/m3d/m_mdl.h
 create mode 100644 include/m/m3d/m_scnleaf.h
 create mode 100644 include/m/m3d/m_smdl.h
 create mode 100644 src/m/m3d/m3d.cpp
 create mode 100644 src/m/m3d/m_anmchr.cpp
 create mode 100644 src/m/m3d/m_anmvis.cpp
 create mode 100644 src/m/m3d/m_banm.cpp
 create mode 100644 src/m/m3d/m_bmdl.cpp
 create mode 100644 src/m/m3d/m_calc_ratio.cpp
 create mode 100644 src/m/m3d/m_fanm.cpp
 create mode 100644 src/m/m3d/m_mdl.cpp
 create mode 100644 src/m/m3d/m_scnLeaf.cpp
 create mode 100644 src/m/m3d/m_smdl.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index fe294296..b2e71a12 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -271,6 +271,45 @@ f/f_manager.cpp:
 	.sbss       start:0x80575BB8 end:0x80575BC0
 	.bss        start:0x805B84D8 end:0x805B8588
 
+m/m3d/m3d.cpp:
+	.text       start:0x802E3E50 end:0x802E4714
+	.sbss       start:0x80575BD0 end:0x80575BE8
+
+m/m3d/m_anmchr.cpp:
+	.text       start:0x802E48E0 end:0x802E4D98
+
+m/m3d/m_anmvis.cpp:
+	.text       start:0x802E7A60 end:0x802E7D48
+	.data       start:0x805425C8 end:0x805425E0
+	.sdata2     start:0x8057CD18 end:0x8057CD28
+
+m/m3d/m_banm.cpp:
+	.text       start:0x802E7D50 end:0x802E7F94
+	.data       start:0x805425E0 end:0x80542628
+
+m/m3d/m_bmdl.cpp:
+	.text       start:0x802E9270 end:0x802EA6EC
+
+m/m3d/m_calc_ratio.cpp:
+	.text       start:0x802EA6F0 end:0x802EA898
+	.data       start:0x80542698 end:0x805426A8
+	.sdata2     start:0x8057CD48 end:0x8057CD50
+
+m/m3d/m_fanm.cpp:
+	.text       start:0x802EA8A0 end:0x802EAF10
+
+m/m3d/m_mdl.cpp:
+	.text       start:0x802EAF80 end:0x802EBB5C
+	.data       start:0x805426C0 end:0x80542708
+
+m/m3d/m_scnLeaf.cpp:
+	.text       start:0x802EBBD0 end:0x802EBFF8
+	.data       start:0x80542708 end:0x8054272C
+
+m/m3d/m_smdl.cpp:
+	.text       start:0x802EDF30 end:0x802EE094
+	.data       start:0x805427F0 end:0x80542820
+
 m/m_allocator.cpp:
 	.text       start:0x802EE0E0 end:0x802EE5EC
 	.data       start:0x80542820 end:0x80542848
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 99db08ad..7f71ec83 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17409,43 +17409,43 @@ fn_802E3B90 = .text:0x802E3B90; // type:function size:0x170
 fn_802E3D00 = .text:0x802E3D00; // type:function size:0x108
 fn_802E3E10 = .text:0x802E3E10; // type:function size:0x20
 fn_802E3E30 = .text:0x802E3E30; // type:function size:0x20
-fn_802E3E50 = .text:0x802E3E50; // type:function size:0xEC
-fn_802E3F40 = .text:0x802E3F40; // type:function size:0x17C
-fn_802E40C0 = .text:0x802E40C0; // type:function size:0x134
-fn_802E4200 = .text:0x802E4200; // type:function size:0xC8
-fn_802E42D0 = .text:0x802E42D0; // type:function size:0x8
-fn_802E42E0 = .text:0x802E42E0; // type:function size:0xC
-fn_802E42F0 = .text:0x802E42F0; // type:function size:0x8
-fn_802E4300 = .text:0x802E4300; // type:function size:0xC
-fn_802E4310 = .text:0x802E4310; // type:function size:0xC
-fn_802E4320 = .text:0x802E4320; // type:function size:0xC
-fn_802E4330 = .text:0x802E4330; // type:function size:0x10
-fn_802E4340 = .text:0x802E4340; // type:function size:0x10
-fn_802E4350 = .text:0x802E4350; // type:function size:0x7C
-fn_802E43D0 = .text:0x802E43D0; // type:function size:0x38
-fn_802E4410 = .text:0x802E4410; // type:function size:0x80
-fn_802E4490 = .text:0x802E4490; // type:function size:0x8
-fn_802E44A0 = .text:0x802E44A0; // type:function size:0xC4
-fn_802E4570 = .text:0x802E4570; // type:function size:0x8
-fn_802E4580 = .text:0x802E4580; // type:function size:0x8
-fn_802E4590 = .text:0x802E4590; // type:function size:0x20
-fn_802E45B0 = .text:0x802E45B0; // type:function size:0x50
-fn_802E4600 = .text:0x802E4600; // type:function size:0x3C
+create__3m3dFPQ23EGG4HeapUlUlUlUl = .text:0x802E3E50; // type:function size:0xEC
+create__3m3dFPQ23EGG4Heap11_GXPixelFmt8_GXColorUlUlUlUlUlUl = .text:0x802E3F40; // type:function size:0x17C
+createLightMgr__3m3dFPQ23EGG4HeapUsUsUcbi = .text:0x802E40C0; // type:function size:0x134
+createFogMgr__3m3dFPQ23EGG4Heapii = .text:0x802E4200; // type:function size:0xC8
+getScnRoot__3m3dFv = .text:0x802E42D0; // type:function size:0x8
+getCamera__3m3dFi = .text:0x802E42E0; // type:function size:0xC
+getCurrentCamera__3m3dFv = .text:0x802E42F0; // type:function size:0x8
+getCurrentCameraID__3m3dFv = .text:0x802E4300; // type:function size:0xC
+setCurrentCamera__3m3dFi = .text:0x802E4310; // type:function size:0xC
+getLightSettingP__3m3dFv = .text:0x802E4320; // type:function size:0xC
+getLightMgr__3m3dFi = .text:0x802E4330; // type:function size:0x10
+getFogMgr__3m3dFi = .text:0x802E4340; // type:function size:0x10
+drawDone__3m3dFi = .text:0x802E4350; // type:function size:0x7C
+drawLightMapTexture__3m3dFi = .text:0x802E43D0; // type:function size:0x38
+calcWorld__3m3dFi = .text:0x802E4410; // type:function size:0x80
+calcMaterial__3m3dFv = .text:0x802E4490; // type:function size:0x8
+calcView__3m3dFi = .text:0x802E44A0; // type:function size:0xC4
+drawOpa__3m3dFv = .text:0x802E4570; // type:function size:0x8
+drawXlu__3m3dFv = .text:0x802E4580; // type:function size:0x8
+pushBack__3m3dFPQ34nw4r3g3d6ScnObj = .text:0x802E4590; // type:function size:0x20
+clear__3m3dFv = .text:0x802E45B0; // type:function size:0x50
+reset__3m3dFv = .text:0x802E4600; // type:function size:0x3C
 fn_802E4640 = .text:0x802E4640; // type:function size:0x40
-fn_802E4680 = .text:0x802E4680; // type:function size:0x4C
-fn_802E46D0 = .text:0x802E46D0; // type:function size:0x44
-fn_802E4720 = .text:0x802E4720; // type:function size:0x2C
+getNodeID__3m3dFQ34nw4r3g3d6ResMdlPCc = .text:0x802E4680; // type:function size:0x4C
+resetMaterial__3m3dFv = .text:0x802E46D0; // type:function size:0x44
+proc_c_drawProc__3m3dFPQ34nw4r3g3d7ScnProcb = .text:0x802E4720; // type:function size:0x2C
 fn_802E4750 = .text:0x802E4750; // type:function size:0x58
 fn_802E47B0 = .text:0x802E47B0; // type:function size:0x8
 fn_802E47C0 = .text:0x802E47C0; // type:function size:0x4
 fn_802E47D0 = .text:0x802E47D0; // type:function size:0x4
-fn_802E47E0 = .text:0x802E47E0; // type:function size:0xF4
-fn_802E48E0 = .text:0x802E48E0; // type:function size:0x58
-fn_802E4940 = .text:0x802E4940; // type:function size:0x8
-fn_802E4950 = .text:0x802E4950; // type:function size:0x120
-fn_802E4A70 = .text:0x802E4A70; // type:function size:0x84
-fn_802E4B00 = .text:0x802E4B00; // type:function size:0x22C
-fn_802E4D30 = .text:0x802E4D30; // type:function size:0x68
+create__Q23m3d6proc_cFP12mAllocator_cPUl = .text:0x802E47E0; // type:function size:0xF4
+__dt__Q23m3d8anmChr_cFv = .text:0x802E48E0; // type:function size:0x58
+getType__Q23m3d8anmChr_cFv = .text:0x802E4940; // type:function size:0x8
+create__Q23m3d8anmChr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmChrP12mAllocator_cPUl = .text:0x802E4950; // type:function size:0x120
+setAnm__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4A70; // type:function size:0x84
+setAnmAfter__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4B00; // type:function size:0x22C
+setFrmCtrlDefault__Q23m3d8anmChr_cFRQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4D30; // type:function size:0x68
 fn_802E4DA0 = .text:0x802E4DA0; // type:function size:0x58
 fn_802E4E00 = .text:0x802E4E00; // type:function size:0x8
 fn_802E4E10 = .text:0x802E4E10; // type:function size:0xB8
@@ -17494,64 +17494,64 @@ fn_802E6140 = .text:0x802E6140; // type:function size:0x120
 fn_802E6260 = .text:0x802E6260; // type:function size:0xEC
 fn_802E6350 = .text:0x802E6350; // type:function size:0x68
 fn_802E63C0 = .text:0x802E63C0; // type:function size:0x8
-fn_802E63D0 = .text:0x802E63D0; // type:function size:0x58
-fn_802E6430 = .text:0x802E6430; // type:function size:0x7C
-fn_802E64B0 = .text:0x802E64B0; // type:function size:0x11C
+__dt__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E63D0; // type:function size:0x58
+heapCost__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatb = .text:0x802E6430; // type:function size:0x7C
+create__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatP12mAllocator_cPUl = .text:0x802E64B0; // type:function size:0x11C
 fn_802E65D0 = .text:0x802E65D0; // type:function size:0xB4
-fn_802E6690 = .text:0x802E6690; // type:function size:0x5C
-fn_802E66F0 = .text:0x802E66F0; // type:function size:0x68
-fn_802E6760 = .text:0x802E6760; // type:function size:0xE0
-fn_802E6840 = .text:0x802E6840; // type:function size:0x23C
-fn_802E6A80 = .text:0x802E6A80; // type:function size:0x6C
+releaseAnm__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E6690; // type:function size:0x5C
+setFrmCtrlDefault__Q33m3d11anmTexPat_c7child_cFRQ34nw4r3g3d12ResAnmTexPatQ23m3d10playMode_e = .text:0x802E66F0; // type:function size:0x68
+heapCost__Q23m3d11anmTexPat_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatlb = .text:0x802E6760; // type:function size:0xE0
+create__Q23m3d11anmTexPat_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatP12mAllocator_cPUll = .text:0x802E6840; // type:function size:0x23C
+__dt__Q23m3d11anmTexPat_cFv = .text:0x802E6A80; // type:function size:0x6C
 fn_802E6AF0 = .text:0x802E6AF0; // type:function size:0x8
-fn_802E6B00 = .text:0x802E6B00; // type:function size:0xEC
-fn_802E6BF0 = .text:0x802E6BF0; // type:function size:0x140
-fn_802E6D30 = .text:0x802E6D30; // type:function size:0xAC
-fn_802E6DE0 = .text:0x802E6DE0; // type:function size:0x60
-fn_802E6E40 = .text:0x802E6E40; // type:function size:0x10
-fn_802E6E50 = .text:0x802E6E50; // type:function size:0x10
-fn_802E6E60 = .text:0x802E6E60; // type:function size:0x10
-fn_802E6E70 = .text:0x802E6E70; // type:function size:0x10
-fn_802E6E80 = .text:0x802E6E80; // type:function size:0x14
+remove__Q23m3d11anmTexPat_cFv = .text:0x802E6B00; // type:function size:0xEC
+setAnm__Q23m3d11anmTexPat_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexPatlQ23m3d10playMode_e = .text:0x802E6BF0; // type:function size:0x140
+play__Q23m3d11anmTexPat_cFv = .text:0x802E6D30; // type:function size:0xAC
+play__Q23m3d11anmTexPat_cFl = .text:0x802E6DE0; // type:function size:0x60
+getFrame__Q23m3d11anmTexPat_cCFl = .text:0x802E6E40; // type:function size:0x10
+setFrame__Q23m3d11anmTexPat_cFfl = .text:0x802E6E50; // type:function size:0x10
+setRate__Q23m3d11anmTexPat_cFfl = .text:0x802E6E60; // type:function size:0x10
+isStop__Q23m3d11anmTexPat_cCFl = .text:0x802E6E70; // type:function size:0x10
+getFrameMax__Q23m3d11anmTexPat_cCFl = .text:0x802E6E80; // type:function size:0x14
 fn_802E6EA0 = .text:0x802E6EA0; // type:function size:0x14
 fn_802E6EC0 = .text:0x802E6EC0; // type:function size:0x8
-fn_802E6ED0 = .text:0x802E6ED0; // type:function size:0x58
-fn_802E6F30 = .text:0x802E6F30; // type:function size:0x7C
-fn_802E6FB0 = .text:0x802E6FB0; // type:function size:0x12C
-fn_802E70E0 = .text:0x802E70E0; // type:function size:0xB4
-fn_802E71A0 = .text:0x802E71A0; // type:function size:0x5C
-fn_802E7200 = .text:0x802E7200; // type:function size:0x68
-fn_802E7270 = .text:0x802E7270; // type:function size:0xE0
-fn_802E7350 = .text:0x802E7350; // type:function size:0x27C
-fn_802E75D0 = .text:0x802E75D0; // type:function size:0x6C
+__dt__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E6ED0; // type:function size:0x58
+heapCost__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtb = .text:0x802E6F30; // type:function size:0x7C
+create__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUl = .text:0x802E6FB0; // type:function size:0x12C
+setAnm__Q33m3d11anmTexSrt_c7child_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexSrtQ23m3d10playMode_e = .text:0x802E70E0; // type:function size:0xB4
+releaseAnm__Q33m3d11anmTexSrt_c7child_cFv = .text:0x802E71A0; // type:function size:0x5C
+setFrmCtrlDefault__Q33m3d11anmTexSrt_c7child_cFRQ34nw4r3g3d12ResAnmTexSrtQ23m3d10playMode_e = .text:0x802E7200; // type:function size:0x68
+heapCost__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtlb = .text:0x802E7270; // type:function size:0xE0
+create__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUll = .text:0x802E7350; // type:function size:0x27C
+__dt__Q23m3d11anmTexSrt_cFv = .text:0x802E75D0; // type:function size:0x6C
 fn_802E7640 = .text:0x802E7640; // type:function size:0x8
-fn_802E7650 = .text:0x802E7650; // type:function size:0xEC
-fn_802E7740 = .text:0x802E7740; // type:function size:0x140
-fn_802E7880 = .text:0x802E7880; // type:function size:0xAC
-fn_802E7930 = .text:0x802E7930; // type:function size:0x60
-fn_802E7990 = .text:0x802E7990; // type:function size:0x10
-fn_802E79A0 = .text:0x802E79A0; // type:function size:0x10
-fn_802E79B0 = .text:0x802E79B0; // type:function size:0x10
-fn_802E79C0 = .text:0x802E79C0; // type:function size:0x10
+remove__Q23m3d11anmTexSrt_cFv = .text:0x802E7650; // type:function size:0xEC
+setAnm__Q23m3d11anmTexSrt_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexSrtlQ23m3d10playMode_e = .text:0x802E7740; // type:function size:0x140
+play__Q23m3d11anmTexSrt_cFv = .text:0x802E7880; // type:function size:0xAC
+play__Q23m3d11anmTexSrt_cFl = .text:0x802E7930; // type:function size:0x60
+getFrame__Q23m3d11anmTexSrt_cCFl = .text:0x802E7990; // type:function size:0x10
+setFrame__Q23m3d11anmTexSrt_cFfl = .text:0x802E79A0; // type:function size:0x10
+setRate__Q23m3d11anmTexSrt_cFfl = .text:0x802E79B0; // type:function size:0x10
+isStop__Q23m3d11anmTexSrt_cCFl = .text:0x802E79C0; // type:function size:0x10
 fn_802E79D0 = .text:0x802E79D0; // type:function size:0x10
-fn_802E79E0 = .text:0x802E79E0; // type:function size:0x14
-fn_802E7A00 = .text:0x802E7A00; // type:function size:0x14
-fn_802E7A20 = .text:0x802E7A20; // type:function size:0x14
+setPlayMode__Q23m3d11anmTexSrt_cFQ23m3d10playMode_el = .text:0x802E79E0; // type:function size:0x14
+getFrameMax__Q23m3d11anmTexSrt_cCFl = .text:0x802E7A00; // type:function size:0x14
+setFrameStart__Q23m3d11anmTexSrt_cFfl = .text:0x802E7A20; // type:function size:0x14
 fn_802E7A40 = .text:0x802E7A40; // type:function size:0x14
-fn_802E7A60 = .text:0x802E7A60; // type:function size:0x58
-fn_802E7AC0 = .text:0x802E7AC0; // type:function size:0x8
-fn_802E7AD0 = .text:0x802E7AD0; // type:function size:0x118
-fn_802E7BF0 = .text:0x802E7BF0; // type:function size:0xE8
-fn_802E7CE0 = .text:0x802E7CE0; // type:function size:0x68
-fn_802E7D50 = .text:0x802E7D50; // type:function size:0x6C
-fn_802E7DC0 = .text:0x802E7DC0; // type:function size:0x64
-fn_802E7E30 = .text:0x802E7E30; // type:function size:0xBC
-fn_802E7EF0 = .text:0x802E7EF0; // type:function size:0x20
-fn_802E7F10 = .text:0x802E7F10; // type:function size:0x4
-fn_802E7F20 = .text:0x802E7F20; // type:function size:0x14
-fn_802E7F40 = .text:0x802E7F40; // type:function size:0x14
-fn_802E7F60 = .text:0x802E7F60; // type:function size:0x14
-fn_802E7F80 = .text:0x802E7F80; // type:function size:0x14
+__dt__Q23m3d8anmVis_cFv = .text:0x802E7A60; // type:function size:0x58
+getType__Q23m3d8anmVis_cFv = .text:0x802E7AC0; // type:function size:0x8
+create__Q23m3d8anmVis_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmVisP12mAllocator_cPUl = .text:0x802E7AD0; // type:function size:0x118
+setAnm__Q23m3d8anmVis_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmVisQ23m3d10playMode_e = .text:0x802E7BF0; // type:function size:0xE8
+setFrmCtrlDefault__Q23m3d8anmVis_cFRQ34nw4r3g3d9ResAnmVisQ23m3d10playMode_e = .text:0x802E7CE0; // type:function size:0x68
+__dt__Q23m3d6banm_cFv = .text:0x802E7D50; // type:function size:0x6C
+remove__Q23m3d6banm_cFv = .text:0x802E7DC0; // type:function size:0x64
+createAllocator__Q23m3d6banm_cFP12mAllocator_cPUl = .text:0x802E7E30; // type:function size:0xBC
+IsBound__Q23m3d6banm_cCFv = .text:0x802E7EF0; // type:function size:0x20
+play__Q23m3d6banm_cFv = .text:0x802E7F10; // type:function size:0x4
+getFrame__Q23m3d6banm_cCFv = .text:0x802E7F20; // type:function size:0x14
+setFrameOnly__Q23m3d6banm_cFf = .text:0x802E7F40; // type:function size:0x14
+getRate__Q23m3d6banm_cCFv = .text:0x802E7F60; // type:function size:0x14
+setRate__Q23m3d6banm_cFf = .text:0x802E7F80; // type:function size:0x14
 fn_802E7FA0 = .text:0x802E7FA0; // type:function size:0x88
 fn_802E8030 = .text:0x802E8030; // type:function size:0x148
 fn_802E8180 = .text:0x802E8180; // type:function size:0x30
@@ -17569,18 +17569,18 @@ fn_802E8A20 = .text:0x802E8A20; // type:function size:0x4
 fn_802E8A30 = .text:0x802E8A30; // type:function size:0xA4
 fn_802E8AE0 = .text:0x802E8AE0; // type:function size:0x66C
 fn_802E9150 = .text:0x802E9150; // type:function size:0x11C
-fn_802E9270 = .text:0x802E9270; // type:function size:0x7C
-fn_802E92F0 = .text:0x802E92F0; // type:function size:0x8
+__dt__Q23m3d6bmdl_cFv = .text:0x802E9270; // type:function size:0x7C
+getType__Q23m3d6bmdl_cCFv = .text:0x802E92F0; // type:function size:0x8
 fn_802E9300 = .text:0x802E9300; // type:function size:0x3C
 fn_802E9340 = .text:0x802E9340; // type:function size:0x3C
-fn_802E9380 = .text:0x802E9380; // type:function size:0xA8
+getNodeWorldMtx__Q23m3d6bmdl_cCFUlPQ34nw4r4math5MTX34 = .text:0x802E9380; // type:function size:0xA8
 fn_802E9430 = .text:0x802E9430; // type:function size:0x5C
-fn_802E9490 = .text:0x802E9490; // type:function size:0x60
-fn_802E94F0 = .text:0x802E94F0; // type:function size:0x15C
-fn_802E9650 = .text:0x802E9650; // type:function size:0x20
-fn_802E9670 = .text:0x802E9670; // type:function size:0x80
-fn_802E96F0 = .text:0x802E96F0; // type:function size:0x9C
-fn_802E9790 = .text:0x802E9790; // type:function size:0xBC
+getNodeWorldMtxMultVecZero__Q23m3d6bmdl_cCFUlRQ34nw4r4math4VEC3 = .text:0x802E9490; // type:function size:0x60
+setAnm__Q23m3d6bmdl_cFRQ23m3d6banm_c = .text:0x802E94F0; // type:function size:0x15C
+play__Q23m3d6bmdl_cFv = .text:0x802E9650; // type:function size:0x20
+getResMdl__Q23m3d6bmdl_cCFv = .text:0x802E9670; // type:function size:0x80
+getResMat__Q23m3d6bmdl_cCFUl = .text:0x802E96F0; // type:function size:0x9C
+removeAnm__Q23m3d6bmdl_cFQ44nw4r3g3d12ScnMdlSimple10AnmObjType = .text:0x802E9790; // type:function size:0xBC
 fn_802E9850 = .text:0x802E9850; // type:function size:0xA0
 fn_802E98F0 = .text:0x802E98F0; // type:function size:0x130
 fn_802E9A20 = .text:0x802E9A20; // type:function size:0x16C
@@ -17592,66 +17592,66 @@ fn_802EA090 = .text:0x802EA090; // type:function size:0x120
 fn_802EA1B0 = .text:0x802EA1B0; // type:function size:0xE4
 fn_802EA2A0 = .text:0x802EA2A0; // type:function size:0x2F0
 fn_802EA590 = .text:0x802EA590; // type:function size:0x150
-fn_802EA6E0 = .text:0x802EA6E0; // type:function size:0xC
-fn_802EA6F0 = .text:0x802EA6F0; // type:function size:0x38
-fn_802EA730 = .text:0x802EA730; // type:function size:0x40
-fn_802EA770 = .text:0x802EA770; // type:function size:0xC
-fn_802EA780 = .text:0x802EA780; // type:function size:0x1C
-fn_802EA7A0 = .text:0x802EA7A0; // type:function size:0x14
-fn_802EA7C0 = .text:0x802EA7C0; // type:function size:0x38
-fn_802EA800 = .text:0x802EA800; // type:function size:0x7C
-fn_802EA880 = .text:0x802EA880; // type:function size:0x18
-fn_802EA8A0 = .text:0x802EA8A0; // type:function size:0x70
-fn_802EA910 = .text:0x802EA910; // type:function size:0x58
-fn_802EA970 = .text:0x802EA970; // type:function size:0x130
-fn_802EAAA0 = .text:0x802EAAA0; // type:function size:0xC4
-fn_802EAB70 = .text:0x802EAB70; // type:function size:0xC0
-fn_802EAC30 = .text:0x802EAC30; // type:function size:0x3C
-fn_802EAC70 = .text:0x802EAC70; // type:function size:0xC0
-fn_802EAD30 = .text:0x802EAD30; // type:function size:0x138
+remove__Q23m3d6bmdl_cFv = .text:0x802EA6E0; // type:function size:0xC
+__ct__Q23m3d11calcRatio_cFv = .text:0x802EA6F0; // type:function size:0x38
+__dt__Q23m3d11calcRatio_cFv = .text:0x802EA730; // type:function size:0x40
+remove__Q23m3d11calcRatio_cFv = .text:0x802EA770; // type:function size:0xC
+reset__Q23m3d11calcRatio_cFv = .text:0x802EA780; // type:function size:0x1C
+offUpdate__Q23m3d11calcRatio_cFv = .text:0x802EA7A0; // type:function size:0x14
+set__Q23m3d11calcRatio_cFf = .text:0x802EA7C0; // type:function size:0x38
+calc__Q23m3d11calcRatio_cFv = .text:0x802EA800; // type:function size:0x7C
+isEnd__Q23m3d11calcRatio_cCFv = .text:0x802EA880; // type:function size:0x18
+__ct__Q23m3d6fanm_cFv = .text:0x802EA8A0; // type:function size:0x70
+__dt__Q23m3d6fanm_cFv = .text:0x802EA910; // type:function size:0x58
+play__Q23m3d6fanm_cFv = .text:0x802EA970; // type:function size:0x130
+set__Q23m3d6fanm_cFfQ23m3d10playMode_eff = .text:0x802EAAA0; // type:function size:0xC4
+setFrame__Q23m3d6fanm_cFf = .text:0x802EAB70; // type:function size:0xC0
+setFrameOnly__Q23m3d6fanm_cFf = .text:0x802EAC30; // type:function size:0x3C
+isStop__Q23m3d6fanm_cCFv = .text:0x802EAC70; // type:function size:0xC0
+checkFrame__Q23m3d6fanm_cCFf = .text:0x802EAD30; // type:function size:0x138
 fn_802EAE70 = .text:0x802EAE70; // type:function size:0xA0
 fn_802EAF10 = .text:0x802EAF10; // type:function size:0x70
-fn_802EAF80 = .text:0x802EAF80; // type:function size:0x54
-fn_802EAFE0 = .text:0x802EAFE0; // type:function size:0x5C
-fn_802EB040 = .text:0x802EB040; // type:function size:0x418
-fn_802EB460 = .text:0x802EB460; // type:function size:0x90
-fn_802EB4F0 = .text:0x802EB4F0; // type:function size:0x5C
-fn_802EB550 = .text:0x802EB550; // type:function size:0xF4
-fn_802EB650 = .text:0x802EB650; // type:function size:0x50
-fn_802EB6A0 = .text:0x802EB6A0; // type:function size:0x8
-fn_802EB6B0 = .text:0x802EB6B0; // type:function size:0x40
-fn_802EB6F0 = .text:0x802EB6F0; // type:function size:0x50
-fn_802EB740 = .text:0x802EB740; // type:function size:0x7C
-fn_802EB7C0 = .text:0x802EB7C0; // type:function size:0x48
-fn_802EB810 = .text:0x802EB810; // type:function size:0x1D4
-fn_802EB9F0 = .text:0x802EB9F0; // type:function size:0x8C
-fn_802EBA80 = .text:0x802EBA80; // type:function size:0x8
-fn_802EBA90 = .text:0x802EBA90; // type:function size:0x34
-fn_802EBAD0 = .text:0x802EBAD0; // type:function size:0x74
-fn_802EBB50 = .text:0x802EBB50; // type:function size:0xC
+__ct__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EAF80; // type:function size:0x54
+__dt__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EAFE0; // type:function size:0x5C
+ExecCallbackA__Q33m3d5mdl_c13mdlCallback_cFPQ34nw4r3g3d12ChrAnmResultQ34nw4r3g3d6ResMdlPQ34nw4r3g3d16FuncObjCalcWorld = .text:0x802EB040; // type:function size:0x418
+ExecCallbackB__Q33m3d5mdl_c13mdlCallback_cFPQ34nw4r3g3d13WorldMtxManipQ34nw4r3g3d6ResMdlPQ34nw4r3g3d16FuncObjCalcWorld = .text:0x802EB460; // type:function size:0x90
+ExecCallbackC__Q33m3d5mdl_c13mdlCallback_cFPQ34nw4r4math5MTX34Q34nw4r3g3d6ResMdlPQ34nw4r3g3d16FuncObjCalcWorld = .text:0x802EB4F0; // type:function size:0x5C
+create__Q33m3d5mdl_c13mdlCallback_cFQ34nw4r3g3d6ResMdlP12mAllocator_cPUl = .text:0x802EB550; // type:function size:0xF4
+remove__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EB650; // type:function size:0x50
+setBlendFrame__Q33m3d5mdl_c13mdlCallback_cFf = .text:0x802EB6A0; // type:function size:0x8
+calcBlend__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EB6B0; // type:function size:0x40
+__ct__Q23m3d5mdl_cFv = .text:0x802EB6F0; // type:function size:0x50
+__dt__Q23m3d5mdl_cFv = .text:0x802EB740; // type:function size:0x7C
+create__Q23m3d5mdl_cFQ34nw4r3g3d6ResMdlP12mAllocator_cUliPUl = .text:0x802EB7C0; // type:function size:0x48
+create__Q23m3d5mdl_cFQ34nw4r3g3d6ResMdlPQ33m3d5mdl_c13mdlCallback_cP12mAllocator_cUliPUl = .text:0x802EB810; // type:function size:0x1D4
+remove__Q23m3d5mdl_cFv = .text:0x802EB9F0; // type:function size:0x8C
+setAnm__Q23m3d5mdl_cFRQ23m3d6banm_c = .text:0x802EBA80; // type:function size:0x8
+play__Q23m3d5mdl_cFv = .text:0x802EBA90; // type:function size:0x34
+setAnm__Q23m3d5mdl_cFRQ23m3d6banm_cf = .text:0x802EBAD0; // type:function size:0x74
+setCallback__Q23m3d5mdl_cFPQ33m3d5mdl_c10callback_c = .text:0x802EBB50; // type:function size:0xC
 fn_802EBB60 = .text:0x802EBB60; // type:function size:0x40
 fn_802EBBA0 = .text:0x802EBBA0; // type:function size:0x4
 fn_802EBBB0 = .text:0x802EBBB0; // type:function size:0x4
 fn_802EBBC0 = .text:0x802EBBC0; // type:function size:0x4
-fn_802EBBD0 = .text:0x802EBBD0; // type:function size:0x4C
-fn_802EBC20 = .text:0x802EBC20; // type:function size:0x74
-fn_802EBCA0 = .text:0x802EBCA0; // type:function size:0x44
-fn_802EBCF0 = .text:0x802EBCF0; // type:function size:0x8
-fn_802EBD00 = .text:0x802EBD00; // type:function size:0x14
-fn_802EBD20 = .text:0x802EBD20; // type:function size:0x14
-fn_802EBD40 = .text:0x802EBD40; // type:function size:0x20
+__ct__Q23m3d9scnLeaf_cFv = .text:0x802EBBD0; // type:function size:0x4C
+__dt__Q23m3d9scnLeaf_cFv = .text:0x802EBC20; // type:function size:0x74
+remove__Q23m3d9scnLeaf_cFv = .text:0x802EBCA0; // type:function size:0x44
+entry__Q23m3d9scnLeaf_cFv = .text:0x802EBCF0; // type:function size:0x8
+setOption__Q23m3d9scnLeaf_cFUlUl = .text:0x802EBD00; // type:function size:0x14
+setScale__Q23m3d9scnLeaf_cFfff = .text:0x802EBD20; // type:function size:0x14
+setScale__Q23m3d9scnLeaf_cFRCQ34nw4r4math4VEC3 = .text:0x802EBD40; // type:function size:0x20
 fn_802EBD60 = .text:0x802EBD60; // type:function size:0x28
-fn_802EBD90 = .text:0x802EBD90; // type:function size:0x10
-fn_802EBDA0 = .text:0x802EBDA0; // type:function size:0x10
+setLocalMtx__Q23m3d9scnLeaf_cFPCQ34nw4r4math5MTX34 = .text:0x802EBD90; // type:function size:0x10
+getLocalMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDA0; // type:function size:0x10
 fn_802EBDB0 = .text:0x802EBDB0; // type:function size:0xC
 fn_802EBDC0 = .text:0x802EBDC0; // type:function size:0x10
-fn_802EBDD0 = .text:0x802EBDD0; // type:function size:0x10
+getViewMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDD0; // type:function size:0x10
 fn_802EBDE0 = .text:0x802EBDE0; // type:function size:0xC
 fn_802EBDF0 = .text:0x802EBDF0; // type:function size:0x10
 fn_802EBE00 = .text:0x802EBE00; // type:function size:0x8
 fn_802EBE10 = .text:0x802EBE10; // type:function size:0x8
-fn_802EBE20 = .text:0x802EBE20; // type:function size:0x78
-fn_802EBEA0 = .text:0x802EBEA0; // type:function size:0x78
+calc__Q23m3d9scnLeaf_cFb = .text:0x802EBE20; // type:function size:0x78
+calcVtx__Q23m3d9scnLeaf_cFb = .text:0x802EBEA0; // type:function size:0x78
 fn_802EBF20 = .text:0x802EBF20; // type:function size:0x84
 fn_802EBFB0 = .text:0x802EBFB0; // type:function size:0x48
 fn_802EC000 = .text:0x802EC000; // type:function size:0x88
@@ -17688,9 +17688,9 @@ fn_802EDC80 = .text:0x802EDC80; // type:function size:0x8
 fn_802EDC90 = .text:0x802EDC90; // type:function size:0x17C
 fn_802EDE10 = .text:0x802EDE10; // type:function size:0x10C
 fn_802EDF20 = .text:0x802EDF20; // type:function size:0xC
-fn_802EDF30 = .text:0x802EDF30; // type:function size:0x4C
-fn_802EDF80 = .text:0x802EDF80; // type:function size:0x58
-fn_802EDFE0 = .text:0x802EDFE0; // type:function size:0xB4
+__ct__Q23m3d6smdl_cFv = .text:0x802EDF30; // type:function size:0x4C
+__dt__Q23m3d6smdl_cFv = .text:0x802EDF80; // type:function size:0x58
+create__Q23m3d6smdl_cFQ34nw4r3g3d6ResMdlP12mAllocator_cUliPUl = .text:0x802EDFE0; // type:function size:0xB4
 fn_802EE0A0 = .text:0x802EE0A0; // type:function size:0x4
 fn_802EE0B0 = .text:0x802EE0B0; // type:function size:0x4
 fn_802EE0C0 = .text:0x802EE0C0; // type:function size:0x4
@@ -24438,7 +24438,7 @@ fn_80442E40 = .text:0x80442E40; // type:function size:0x8
 fn_80442E50 = .text:0x80442E50; // type:function size:0x8
 fn_80442E60 = .text:0x80442E60; // type:function size:0x40
 fn_80442EA0 = .text:0x80442EA0; // type:function size:0x40
-fn_80442EE0 = .text:0x80442EE0; // type:function size:0x150
+Construct__Q34nw4r3g3d12AnmObjVisResFP12MEMAllocatorPUlQ34nw4r3g3d9ResAnmVisQ34nw4r3g3d6ResMdl = .text:0x80442EE0; // type:function size:0x150
 fn_80443030 = .text:0x80443030; // type:function size:0x64
 fn_804430A0 = .text:0x804430A0; // type:function size:0x8
 fn_804430B0 = .text:0x804430B0; // type:function size:0x8
@@ -24664,14 +24664,14 @@ fn_8044A380 = .text:0x8044A380; // type:function size:0x30
 fn_8044A3B0 = .text:0x8044A3B0; // type:function size:0xC
 fn_8044A3C0 = .text:0x8044A3C0; // type:function size:0x328
 fn_8044A6F0 = .text:0x8044A6F0; // type:function size:0x4C
-fn_8044A740 = .text:0x8044A740; // type:function size:0x78
-fn_8044A7C0 = .text:0x8044A7C0; // type:function size:0x58
+Destroy__Q34nw4r3g3d6G3dObjFv = .text:0x8044A740; // type:function size:0x78
+DetachFromParent__Q34nw4r3g3d6G3dObjFv = .text:0x8044A7C0; // type:function size:0x58
 fn_8044A820 = .text:0x8044A820; // type:function size:0x30
 fn_8044A850 = .text:0x8044A850; // type:function size:0xC
 fn_8044A860 = .text:0x8044A860; // type:function size:0x8
 fn_8044A870 = .text:0x8044A870; // type:function size:0x70
 fn_8044A8E0 = .text:0x8044A8E0; // type:function size:0x28
-fn_8044A910 = .text:0x8044A910; // type:function size:0x18
+TestAnmFlag__Q34nw4r3g3d6AnmObjCFQ44nw4r3g3d6AnmObj7AnmFlag = .text:0x8044A910; // type:function size:0x18
 fn_8044A930 = .text:0x8044A930; // type:function size:0x80
 fn_8044A9B0 = .text:0x8044A9B0; // type:function size:0x30
 fn_8044A9E0 = .text:0x8044A9E0; // type:function size:0x70
@@ -24723,7 +24723,7 @@ fn_8044E120 = .text:0x8044E120; // type:function size:0x5C
 fn_8044E180 = .text:0x8044E180; // type:function size:0xA4
 fn_8044E230 = .text:0x8044E230; // type:function size:0x1C
 fn_8044E250 = .text:0x8044E250; // type:function size:0x68
-fn_8044E2C0 = .text:0x8044E2C0; // type:function size:0xD4
+SetRenderModeObj__Q34nw4r3g3d8G3DStateFRC16_GXRenderModeObj = .text:0x8044E2C0; // type:function size:0xD4
 fn_8044E3A0 = .text:0x8044E3A0; // type:function size:0xC
 fn_8044E3B0 = .text:0x8044E3B0; // type:function size:0x16C
 fn_8044E520 = .text:0x8044E520; // type:function size:0x15C
@@ -24847,13 +24847,13 @@ fn_80457EA0 = .text:0x80457EA0; // type:function size:0xEC
 fn_80457F90 = .text:0x80457F90; // type:function size:0x80
 fn_80458010 = .text:0x80458010; // type:function size:0x198
 fn_804581B0 = .text:0x804581B0; // type:function size:0xD0
-fn_80458280 = .text:0x80458280; // type:function size:0x8C
+SetMtx__Q34nw4r3g3d6ScnObjFQ44nw4r3g3d6ScnObj13ScnObjMtxTypePCQ34nw4r4math5MTX34 = .text:0x80458280; // type:function size:0x8C
 fn_80458310 = .text:0x80458310; // type:function size:0x5C
-fn_80458370 = .text:0x80458370; // type:function size:0x4C
+GetMtx__Q34nw4r3g3d6ScnObjCFQ44nw4r3g3d6ScnObj13ScnObjMtxTypePQ34nw4r4math5MTX34 = .text:0x80458370; // type:function size:0x4C
 fn_804583C0 = .text:0x804583C0; // type:function size:0xC
 fn_804583D0 = .text:0x804583D0; // type:function size:0x8
-fn_804583E0 = .text:0x804583E0; // type:function size:0x24
-fn_80458410 = .text:0x80458410; // type:function size:0x24
+SetPriorityDrawOpa__Q34nw4r3g3d6ScnObjFi = .text:0x804583E0; // type:function size:0x24
+SetPriorityDrawXlu__Q34nw4r3g3d6ScnObjFi = .text:0x80458410; // type:function size:0x24
 fn_80458440 = .text:0x80458440; // type:function size:0x40
 fn_80458480 = .text:0x80458480; // type:function size:0x40
 fn_804584C0 = .text:0x804584C0; // type:function size:0x14
@@ -24885,21 +24885,21 @@ fn_80459730 = .text:0x80459730; // type:function size:0xC
 fn_80459740 = .text:0x80459740; // type:function size:0x84
 fn_804597D0 = .text:0x804597D0; // type:function size:0x30
 fn_80459800 = .text:0x80459800; // type:function size:0xC
-fn_80459810 = .text:0x80459810; // type:function size:0x138
+Construct__Q34nw4r3g3d7ScnRootFP12MEMAllocatorPUlUlUlUlUl = .text:0x80459810; // type:function size:0x138
 fn_80459950 = .text:0x80459950; // type:function size:0xA8
-fn_80459A00 = .text:0x80459A00; // type:function size:0x5C
-fn_80459A60 = .text:0x80459A60; // type:function size:0x3C
-fn_80459AA0 = .text:0x80459AA0; // type:function size:0x8
+GetCamera__Q34nw4r3g3d7ScnRootFi = .text:0x80459A00; // type:function size:0x5C
+GetCurrentCamera__Q34nw4r3g3d7ScnRootFv = .text:0x80459A60; // type:function size:0x3C
+SetCurrentCamera__Q34nw4r3g3d7ScnRootFi = .text:0x80459AA0; // type:function size:0x8
 fn_80459AB0 = .text:0x80459AB0; // type:function size:0x5C
 fn_80459B10 = .text:0x80459B10; // type:function size:0x234
 fn_80459D50 = .text:0x80459D50; // type:function size:0x188
 fn_80459EE0 = .text:0x80459EE0; // type:function size:0x4C
-fn_80459F30 = .text:0x80459F30; // type:function size:0x1C
+CalcMaterial__Q34nw4r3g3d7ScnRootFv = .text:0x80459F30; // type:function size:0x1C
 fn_80459F50 = .text:0x80459F50; // type:function size:0x78
 fn_80459FD0 = .text:0x80459FD0; // type:function size:0xEC
 fn_8045A0C0 = .text:0x8045A0C0; // type:function size:0x60
-fn_8045A120 = .text:0x8045A120; // type:function size:0x74
-fn_8045A1A0 = .text:0x8045A1A0; // type:function size:0x74
+DrawOpa__Q34nw4r3g3d7ScnRootFv = .text:0x8045A120; // type:function size:0x74
+DrawXlu__Q34nw4r3g3d7ScnRootFv = .text:0x8045A1A0; // type:function size:0x74
 fn_8045A220 = .text:0x8045A220; // type:function size:0xFC
 fn_8045A320 = .text:0x8045A320; // type:function size:0x7C
 fn_8045A3A0 = .text:0x8045A3A0; // type:function size:0x120
@@ -24921,14 +24921,14 @@ fn_8045B0D0 = .text:0x8045B0D0; // type:function size:0x40
 fn_8045B110 = .text:0x8045B110; // type:function size:0x68
 fn_8045B180 = .text:0x8045B180; // type:function size:0x30
 fn_8045B1B0 = .text:0x8045B1B0; // type:function size:0xC
-fn_8045B1C0 = .text:0x8045B1C0; // type:function size:0x194
+Construct__Q34nw4r3g3d12ScnMdlSimpleFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdli = .text:0x8045B1C0; // type:function size:0x194
 fn_8045B360 = .text:0x8045B360; // type:function size:0x258
 fn_8045B5C0 = .text:0x8045B5C0; // type:function size:0x34
 fn_8045B600 = .text:0x8045B600; // type:function size:0x34C
 fn_8045B950 = .text:0x8045B950; // type:function size:0x890
 fn_8045C1E0 = .text:0x8045C1E0; // type:function size:0x40
 fn_8045C220 = .text:0x8045C220; // type:function size:0x38
-fn_8045C260 = .text:0x8045C260; // type:function size:0xE0
+GetScnMtxPos__Q34nw4r3g3d12ScnMdlSimpleCFPQ34nw4r4math5MTX34Q44nw4r3g3d6ScnObj13ScnObjMtxTypeUl = .text:0x8045C260; // type:function size:0xE0
 fn_8045C340 = .text:0x8045C340; // type:function size:0x610
 fn_8045C950 = .text:0x8045C950; // type:function size:0x178
 fn_8045CAD0 = .text:0x8045CAD0; // type:function size:0xE0
@@ -24960,7 +24960,7 @@ fn_8045D850 = .text:0x8045D850; // type:function size:0xD0
 fn_8045D920 = .text:0x8045D920; // type:function size:0xBC
 fn_8045D9E0 = .text:0x8045D9E0; // type:function size:0xBC
 fn_8045DAA0 = .text:0x8045DAA0; // type:function size:0xBC
-fn_8045DB60 = .text:0x8045DB60; // type:function size:0xB88
+Construct__Q34nw4r3g3d6ScnMdlFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdlUli = .text:0x8045DB60; // type:function size:0xB88
 fn_8045E6F0 = .text:0x8045E6F0; // type:function size:0x57C
 fn_8045EC70 = .text:0x8045EC70; // type:function size:0x4B0
 fn_8045F120 = .text:0x8045F120; // type:function size:0x40
@@ -24977,7 +24977,7 @@ fn_8045FCF0 = .text:0x8045FCF0; // type:function size:0x80
 fn_8045FD70 = .text:0x8045FD70; // type:function size:0x30
 fn_8045FDA0 = .text:0x8045FDA0; // type:function size:0xC
 fn_8045FDB0 = .text:0x8045FDB0; // type:function size:0x234
-fn_8045FFF0 = .text:0x8045FFF0; // type:function size:0xF0
+G3dInit__Q24nw4r3g3dFb = .text:0x8045FFF0; // type:function size:0xF0
 fn_804600E0 = .text:0x804600E0; // type:function size:0x8
 fn_804600F0 = .text:0x804600F0; // type:function size:0x110
 fn_80460200 = .text:0x80460200; // type:function size:0x100
@@ -29124,8 +29124,8 @@ lbl_804F7AF0 = .rodata:0x804F7AF0; // type:object size:0x10
 lbl_804F7B00 = .rodata:0x804F7B00; // type:object size:0x10
 lbl_804F7B10 = .rodata:0x804F7B10; // type:object size:0x10
 lbl_804F7B20 = .rodata:0x804F7B20; // type:object size:0x10
-lbl_804F7B30 = .rodata:0x804F7B30; // type:object size:0x18
-lbl_804F7B48 = .rodata:0x804F7B48; // type:object size:0x10
+TYPE_NAME__Q34nw4r3g3d12ScnMdlSimple = .rodata:0x804F7B30; // type:object size:0x18
+TYPE_NAME__Q34nw4r3g3d6ScnMdl = .rodata:0x804F7B48; // type:object size:0x10
 lbl_804F7B58 = .rodata:0x804F7B58; // type:object size:0x18
 lbl_804F7B70 = .rodata:0x804F7B70; // type:object size:0x10
 lbl_804F7B80 = .rodata:0x804F7B80; // type:object size:0x460
@@ -35695,17 +35695,17 @@ lbl_80542568 = .data:0x80542568; // type:object size:0x18
 lbl_80542580 = .data:0x80542580; // type:object size:0x18
 lbl_80542598 = .data:0x80542598; // type:object size:0x18
 lbl_805425B0 = .data:0x805425B0; // type:object size:0x18
-lbl_805425C8 = .data:0x805425C8; // type:object size:0x18
+__vt__Q23m3d8anmVis_c = .data:0x805425C8; // type:object size:0x18
 lbl_805425E0 = .data:0x805425E0; // type:object size:0x30
-lbl_80542610 = .data:0x80542610; // type:object size:0x18
+__vt__Q23m3d6banm_c = .data:0x80542610; // type:object size:0x18
 lbl_80542628 = .data:0x80542628; // type:object size:0x30
 lbl_80542658 = .data:0x80542658; // type:object size:0x10
-lbl_80542668 = .data:0x80542668; // type:object size:0x30
-lbl_80542698 = .data:0x80542698; // type:object size:0x10
-lbl_805426A8 = .data:0x805426A8; // type:object size:0x18
-lbl_805426C0 = .data:0x805426C0; // type:object size:0x30
-lbl_805426F0 = .data:0x805426F0; // type:object size:0x18
-lbl_80542708 = .data:0x80542708; // type:object size:0x28
+__vt__Q23m3d6bmdl_c = .data:0x80542668; // type:object size:0x30
+__vt__Q23m3d11calcRatio_c = .data:0x80542698; // type:object size:0x10
+__vt__Q23m3d6fanm_c = .data:0x805426A8; // type:object size:0x18
+__vt__Q23m3d5mdl_c = .data:0x805426C0; // type:object size:0x2C
+__vt__Q33m3d5mdl_c13mdlCallback_c = .data:0x805426F0; // type:object size:0x18
+__vt__Q23m3d9scnLeaf_c = .data:0x80542708; // type:object size:0x24
 lbl_80542730 = .data:0x80542730; // type:object size:0x10
 lbl_80542740 = .data:0x80542740; // type:object size:0x20
 lbl_80542760 = .data:0x80542760; // type:object size:0x10
@@ -35714,7 +35714,7 @@ lbl_80542780 = .data:0x80542780; // type:object size:0x28
 lbl_805427A8 = .data:0x805427A8; // type:object size:0x2C
 lbl_805427D4 = .data:0x805427D4; // type:object size:0xC
 lbl_805427E0 = .data:0x805427E0; // type:object size:0x10
-lbl_805427F0 = .data:0x805427F0; // type:object size:0x30
+__vt__Q23m3d6smdl_c = .data:0x805427F0; // type:object size:0x2C
 __vt__16mHeapAllocator_c = .data:0x80542820; // type:object size:0x14
 __vt__12mAllocator_c = .data:0x80542834; // type:object size:0x14
 __vt__13mColorFader_c = .data:0x80542848; // type:object size:0x24
@@ -40295,8 +40295,8 @@ m_StopProcInf__10fManager_c = .sbss:0x80575BB8; // type:object size:0x4 data:4by
 sProfileList__8fProfile = .sbss:0x80575BC0; // type:object size:0x8 data:4byte
 lbl_80575BC8 = .sbss:0x80575BC8; // type:object size:0x4 data:4byte
 lbl_80575BCC = .sbss:0x80575BCC; // type:object size:0x1 data:byte
-lbl_80575BD0 = .sbss:0x80575BD0; // type:object size:0x4 data:4byte
-lbl_80575BD4 = .sbss:0x80575BD4; // type:object size:0x4 data:4byte
+l_allocator_p__Q23m3d8internal = .sbss:0x80575BD0; // type:object size:0x4 data:4byte
+l_scnRoot_p__Q23m3d8internal = .sbss:0x80575BD4; // type:object size:0x4 data:4byte
 lbl_80575BD8 = .sbss:0x80575BD8; // type:object size:0x4 data:4byte
 lbl_80575BDC = .sbss:0x80575BDC; // type:object size:0x4 data:4byte
 lbl_80575BE0 = .sbss:0x80575BE0; // type:object size:0x4 data:4byte
diff --git a/configure.py b/configure.py
index 8c85ecd1..fa38ddf7 100644
--- a/configure.py
+++ b/configure.py
@@ -335,6 +335,16 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(NonMatching, "f/f_base.cpp"),
             Object(Matching, "f/f_list.cpp"),
             Object(Matching, "f/f_manager.cpp"),
+            Object(NonMatching, "m/m3d/m3d.cpp"),
+            Object(NonMatching, "m/m3d/m_anmchr.cpp"),
+            Object(Matching, "m/m3d/m_anmvis.cpp"),
+            Object(Matching, "m/m3d/m_banm.cpp"),
+            Object(NonMatching, "m/m3d/m_bmdl.cpp"),
+            Object(NonMatching, "m/m3d/m_calc_ratio.cpp"),
+            Object(NonMatching, "m/m3d/m_fanm.cpp"),
+            Object(NonMatching, "m/m3d/m_mdl.cpp"),
+            Object(NonMatching, "m/m3d/m_scnLeaf.cpp"),
+            Object(Matching, "m/m3d/m_smdl.cpp"),
             Object(Matching, "m/m_allocator.cpp"),
             Object(Matching, "m/m_angle.cpp"),
             Object(Matching, "m/m_color_fader.cpp"),
diff --git a/include/egg/core/eggFrmHeap.h b/include/egg/core/eggFrmHeap.h
index f68e8e4f..10757d7c 100644
--- a/include/egg/core/eggFrmHeap.h
+++ b/include/egg/core/eggFrmHeap.h
@@ -22,7 +22,7 @@ class FrmHeap : public Heap {
 public:
     /* 80495fa0 */ FrmHeap(MEMiHeapHead *heapHead);
     /* 80496060 */ static FrmHeap *create(void *block, size_t size, u16 attr);
-    /* 804962a0 */ void free(u32 flags);
+    /* 804962a0 */ void free(s32 flags);
     /* 80496370 */ void recordState(u32 id); // non official for now
     /* 80496380 */ void freeState(u32 id);   // non official for now
 };
diff --git a/include/m/m3d/m3d.h b/include/m/m3d/m3d.h
new file mode 100644
index 00000000..835ff57a
--- /dev/null
+++ b/include/m/m3d/m3d.h
@@ -0,0 +1,41 @@
+#ifndef M3D_H
+#define M3D_H
+
+#include <egg/core/eggHeap.h>
+#include <m/m_allocator.h>
+#include <nw4r/g3d/g3d_scnroot.h>
+
+namespace m3d {
+
+namespace internal {
+
+extern mAllocator_c *l_allocator_p;
+extern nw4r::g3d::ScnRoot *l_scnRoot_p;
+
+}
+
+bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4);
+
+nw4r::g3d::ScnRoot *getScnRoot();
+nw4r::g3d::Camera getCamera(int id);
+nw4r::g3d::Camera getCurrentCamera();
+int getCurrentCameraID();
+
+// TODO EGG
+
+void calcWorld(int idx);
+void calcMaterial();
+
+void drawOpa();
+void drawXlu();
+
+bool pushBack(nw4r::g3d::ScnObj *obj);
+void clear();
+
+void reset();
+
+void resetMaterial();
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_anmchr.h b/include/m/m3d/m_anmchr.h
new file mode 100644
index 00000000..775d9fab
--- /dev/null
+++ b/include/m/m3d/m_anmchr.h
@@ -0,0 +1,23 @@
+#ifndef M_ANMCHR_H
+#define M_ANMCHR_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+#include <nw4r/g3d/g3d_resanmchr.h>
+
+
+namespace m3d {
+
+class anmChr_c : public fanm_c {
+    virtual ~anmChr_c();
+
+    virtual int getType() override;
+
+    void setAnm(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
+    void setAnmAfter(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
+    void setFrmCtrlDefault(nw4r::g3d::ResAnmChr &, m3d::playMode_e);
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_anmvis.h b/include/m/m3d/m_anmvis.h
new file mode 100644
index 00000000..800ea00b
--- /dev/null
+++ b/include/m/m3d/m_anmvis.h
@@ -0,0 +1,22 @@
+#ifndef M_ANMVIS_H
+#define M_ANMVIS_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+
+
+namespace m3d {
+
+class anmVis_c : public fanm_c {
+    virtual ~anmVis_c();
+
+    virtual int getType() override;
+
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmVis, mAllocator_c *, u32 *);
+    void setAnm(m3d::bmdl_c &, nw4r::g3d::ResAnmVis, m3d::playMode_e);
+    void setFrmCtrlDefault(nw4r::g3d::ResAnmVis &, m3d::playMode_e);
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_banm.h b/include/m/m3d/m_banm.h
new file mode 100644
index 00000000..d5647a71
--- /dev/null
+++ b/include/m/m3d/m_banm.h
@@ -0,0 +1,38 @@
+#ifndef M3D_M_BANM_H
+#define M3D_M_BANM_H
+
+#include <egg/core/eggFrmHeap.h>
+#include <m/m_allocator.h>
+#include <nw4r/g3d/g3d_anmobj.h>
+
+namespace m3d {
+
+class banm_c {
+public:
+    banm_c() : mpAnmObj(nullptr), mpFrameHeap(nullptr) {}
+    virtual ~banm_c();
+
+    virtual int getType() = 0;
+    virtual void remove();
+    virtual void play();
+
+    bool createAllocator(mAllocator_c *alloc, u32 *pSize);
+    bool IsBound() const;
+    f32 getFrame() const;
+    void setFrameOnly(f32);
+    f32 getRate() const;
+    void setRate(f32);
+
+    inline nw4r::g3d::AnmObj *getAnimObj() const {
+        return mpAnmObj;
+    }
+
+protected:
+    nw4r::g3d::AnmObj *mpAnmObj;
+    EGG::FrmHeap *mpFrameHeap;
+    mAllocator_c mAllocator;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_bmdl.h b/include/m/m3d/m_bmdl.h
new file mode 100644
index 00000000..fd1a24ed
--- /dev/null
+++ b/include/m/m3d/m_bmdl.h
@@ -0,0 +1,34 @@
+#ifndef M3D_M_BMDL_H
+#define M3D_M_BMDL_H
+
+#include <m/m3d/m_banm.h>
+#include <m/m3d/m_scnleaf.h>
+#include <nw4r/g3d/g3d_scnmdlsmpl.h>
+
+
+namespace m3d {
+
+class bmdl_c : public scnLeaf_c {
+public:
+    bmdl_c() : mpCurrentAnm(nullptr) {}
+    virtual ~bmdl_c();
+
+    virtual int getType() const override;
+    virtual void remove() override;
+    virtual void setAnm(banm_c &anm);
+    virtual void play();
+
+    void getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const;
+
+    nw4r::g3d::ResMdl getResMdl() const;
+    nw4r::g3d::ResMat getResMat(u32 index) const;
+
+    void removeAnm(nw4r::g3d::ScnMdlSimple::AnmObjType);
+
+private:
+    banm_c *mpCurrentAnm;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_calc_ratio.h b/include/m/m3d/m_calc_ratio.h
new file mode 100644
index 00000000..3b9140a8
--- /dev/null
+++ b/include/m/m3d/m_calc_ratio.h
@@ -0,0 +1,33 @@
+#ifndef M3D_M_CALC_RATIO_H
+#define M3D_M_CALC_RATIO_H
+
+#include <egg/core/eggFrmHeap.h>
+#include <m/m_allocator.h>
+
+namespace m3d {
+
+class calcRatio_c {
+public:
+    calcRatio_c();
+    virtual ~calcRatio_c();
+
+    void remove();
+    void reset();
+    void offUpdate();
+    void set(f32);
+    void calc();
+    bool isEnd() const;
+
+private:
+    f32 mf1;
+    f32 mf2;
+    f32 mf3;
+    f32 mf4;
+    f32 mf5;
+    u8 mb1;
+    u8 mb2;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
new file mode 100644
index 00000000..e6435c7a
--- /dev/null
+++ b/include/m/m3d/m_fanm.h
@@ -0,0 +1,40 @@
+#ifndef M3D_M_FANM_H
+#define M3D_M_FANM_H
+
+#include <m/m3d/m_banm.h>
+
+namespace m3d {
+
+enum playMode_e {
+    PLAY_MODE_0,
+    PLAY_MODE_1,
+    PLAY_MODE_2,
+    PLAY_MODE_3,
+    PLAY_MODE_4,
+};
+
+class fanm_c : public banm_c {
+public:
+    fanm_c();
+    virtual ~fanm_c();
+
+    virtual int getType() = 0;
+    virtual void play();
+
+    void set(f32, playMode_e, f32, f32);
+    void setFrame(f32);
+    void setFrameOnly(f32);
+    bool isStop() const;
+    bool checkFrame(f32) const;
+    bool unk_802EAE70() const;
+
+private:
+    f32 mEndFrame;
+    f32 mStartFrame;
+    f32 mCurrentFrame;
+    u8 mPlayState;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
new file mode 100644
index 00000000..f3f6c4af
--- /dev/null
+++ b/include/m/m3d/m_mdl.h
@@ -0,0 +1,62 @@
+#ifndef M3D_M_MDL_H
+#define M3D_M_MDL_H
+
+#include <m/m3d/m_banm.h>
+#include <m/m3d/m_calc_ratio.h>
+#include <m/m3d/m_smdl.h>
+
+class UnkClass3 {};
+
+namespace m3d {
+
+class mdl_c : smdl_c {
+    class mdlCallback_c : public nw4r::g3d::IScnObjCallback {
+    public:
+        mdlCallback_c();
+        virtual ~mdlCallback_c();
+
+        bool create(nw4r::g3d::ResMdl, mAllocator_c *, u32 *);
+
+        // TODO g3d headers
+        virtual void ExecCallbackA();
+        virtual void ExecCallbackB();
+        virtual void ExecCallbackC();
+
+        void remove();
+        void setBlendFrame(f32);
+        void calcBlend();
+
+        mAllocator_c *getAllocator() {
+            return mpAlloc;
+        }
+
+    private:
+        calcRatio_c mCalcRatio;
+        u32 mNumNode;
+        void *mpNodes;
+        u32 mCallbackNum;
+        mAllocator_c *mpAlloc;
+    };
+
+public:
+    mdl_c();
+    virtual ~mdl_c();
+
+    bool create(nw4r::g3d::ResMdl, mAllocator_c *, u32, int, u32 *);
+    bool create(nw4r::g3d::ResMdl, mdlCallback_c *cb, mAllocator_c *, u32, int, u32 *);
+
+    virtual void remove();
+
+    void setCallback(mdlCallback_c *cb);
+    void play();
+    void setAnm(banm_c &);
+    void setAnm(banm_c &, f32);
+
+private:
+    mdlCallback_c *mpOwnedCallback;
+    mdlCallback_c *mpCallback;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_scnleaf.h b/include/m/m3d/m_scnleaf.h
new file mode 100644
index 00000000..32f63565
--- /dev/null
+++ b/include/m/m3d/m_scnleaf.h
@@ -0,0 +1,41 @@
+#ifndef M3D_M_SCNLEAF_H
+#define M3D_M_SCNLEAF_H
+
+#include <egg/core/eggDisposer.h>
+#include <m/m_allocator.h>
+#include <nw4r/g3d/g3d_scnobj.h>
+
+namespace m3d {
+
+class UnkClass {
+public:
+    UnkClass() {}
+};
+
+class scnLeaf_c : UnkClass, EGG::Disposer {
+public:
+    scnLeaf_c();
+    virtual ~scnLeaf_c();
+    virtual int getType() const = 0;
+    virtual void remove();
+    virtual int entry();
+
+    void setOption(unsigned long flag, unsigned long set);
+    void setScale(f32, f32, f32);
+    void setScale(const nw4r::math::VEC3 &);
+
+    void setLocalMtx(const nw4r::math::MTX34 *);
+    void getLocalMtx(nw4r::math::MTX34 *) const;
+
+    void getViewMtx(nw4r::math::MTX34 *) const;
+
+    void calc(bool);
+    void calcVtx(bool);
+
+protected:
+    nw4r::g3d::ScnLeaf *mpScnLeaf;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_smdl.h b/include/m/m3d/m_smdl.h
new file mode 100644
index 00000000..7089d07a
--- /dev/null
+++ b/include/m/m3d/m_smdl.h
@@ -0,0 +1,22 @@
+#ifndef M3D_M_SMDL_H
+#define M3D_M_SMDL_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_scnleaf.h>
+#include <nw4r/g3d/g3d_resmdl.h>
+
+class UnkClass2 {};
+
+namespace m3d {
+
+class smdl_c : public bmdl_c, UnkClass2 {
+public:
+    smdl_c();
+    virtual ~smdl_c();
+
+    bool create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize);
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/nw4r/g3d/g3d_3dsmax.h b/include/nw4r/g3d/g3d_3dsmax.h
index 56c56453..220e4d09 100644
--- a/include/nw4r/g3d/g3d_3dsmax.h
+++ b/include/nw4r/g3d/g3d_3dsmax.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_3DSMAX_H
 #define NW4R_G3D_3DSMAX_H
 #include "common.h"
-#include "g3d_anmtexsrt.h"
+#include "nw4r/g3d/g3d_anmtexsrt.h"
+#include "nw4r/math.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_anmcamera.h b/include/nw4r/g3d/g3d_anmcamera.h
index 972ed03e..7a33f1dd 100644
--- a/include/nw4r/g3d/g3d_anmcamera.h
+++ b/include/nw4r/g3d/g3d_anmcamera.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_ANMCAMERA_H
 #define NW4R_G3D_ANMCAMERA_H
 #include "common.h"
-#include "math_types.h"
+#include "nw4r/math/math_types.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_anmchr.h b/include/nw4r/g3d/g3d_anmchr.h
index 577bd8ff..deed7bdb 100644
--- a/include/nw4r/g3d/g3d_anmchr.h
+++ b/include/nw4r/g3d/g3d_anmchr.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_ANMCHR_H
 #define NW4R_G3D_ANMCHR_H
 #include "common.h"
-#include "math_types.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_anmobj.h b/include/nw4r/g3d/g3d_anmobj.h
index 421223a0..7fb62f1e 100644
--- a/include/nw4r/g3d/g3d_anmobj.h
+++ b/include/nw4r/g3d/g3d_anmobj.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_ANMOBJ_H
 #define NW4R_G3D_ANMOBJ_H
 #include "common.h"
-#include "g3d_obj.h"
-#include "g3d_rescommon.h"
-#include "g3d_resmdl.h"
+#include "nw4r/g3d/g3d_obj.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/g3d/g3d_resmdl.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_anmscn.h b/include/nw4r/g3d/g3d_anmscn.h
index f32f99f4..050c64eb 100644
--- a/include/nw4r/g3d/g3d_anmscn.h
+++ b/include/nw4r/g3d/g3d_anmscn.h
@@ -1,10 +1,10 @@
 #ifndef NW4R_G3D_ANMSCN_H
 #define NW4R_G3D_ANMSCN_H
 #include "common.h"
-#include "g3d_camera.h"
-#include "g3d_fog.h"
-#include "g3d_light.h"
-#include "g3d_obj.h"
+#include "nw4r/g3d/g3d_camera.h"
+#include "nw4r/g3d/g3d_fog.h"
+#include "nw4r/g3d/g3d_light.h"
+#include "nw4r/g3d/g3d_obj.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_anmtexpat.h b/include/nw4r/g3d/g3d_anmtexpat.h
index 434a10f7..031c3264 100644
--- a/include/nw4r/g3d/g3d_anmtexpat.h
+++ b/include/nw4r/g3d/g3d_anmtexpat.h
@@ -1,6 +1,6 @@
 #ifndef NW4R_G3D_ANMTEXPAT_H
 #define NW4R_G3D_ANMTEXPAT_H
-#include "g3d_restex.h"
+#include "nw4r/g3d/g3d_restex.h"
 
 namespace nw4r
 {
@@ -21,4 +21,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_anmtexsrt.h b/include/nw4r/g3d/g3d_anmtexsrt.h
index 55c286aa..5e5e99fc 100644
--- a/include/nw4r/g3d/g3d_anmtexsrt.h
+++ b/include/nw4r/g3d/g3d_anmtexsrt.h
@@ -1,6 +1,6 @@
 #ifndef NW4R_G3D_ANMTEXSRT_H
 #define NW4R_G3D_ANMTEXSRT_H
-#include "g3d_restex.h"
+#include "nw4r/g3d/g3d_restex.h"
 
 namespace nw4r
 {
@@ -44,4 +44,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_anmvis.h b/include/nw4r/g3d/g3d_anmvis.h
index 26bffd4d..54d54e34 100644
--- a/include/nw4r/g3d/g3d_anmvis.h
+++ b/include/nw4r/g3d/g3d_anmvis.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_ANMVIS_H
 #define NW4R_G3D_ANMVIS_H
 #include "common.h"
-#include "g3d_anmobj.h"
-#include "g3d_resanmvis.h"
+#include "nw4r/g3d/g3d_anmobj.h"
+#include "nw4r/g3d/g3d_resanmvis.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_basic.h b/include/nw4r/g3d/g3d_basic.h
index e80b3ed3..672796d9 100644
--- a/include/nw4r/g3d/g3d_basic.h
+++ b/include/nw4r/g3d/g3d_basic.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_BASIC_H
 #define NW4R_G3D_BASIC_H
-#include "math_types.h"
-#include "g3d_anmchr.h"
+#include "nw4r/math/math_types.h"
+#include "nw4r/g3d/g3d_anmchr.h"
 
 namespace nw4r
 {
@@ -45,4 +45,4 @@ namespace nw4r
     }
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_calcview.h b/include/nw4r/g3d/g3d_calcview.h
index 8cb3a7c5..3c9a43cf 100644
--- a/include/nw4r/g3d/g3d_calcview.h
+++ b/include/nw4r/g3d/g3d_calcview.h
@@ -1,6 +1,7 @@
 #ifndef NW4R_G3D_CALC_VIEW_H
 #define NW4R_G3D_CALC_VIEW_H
 #include "common.h"
+#include "nw4r/math.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_calcworld.h b/include/nw4r/g3d/g3d_calcworld.h
index f4968c2f..ba2825df 100644
--- a/include/nw4r/g3d/g3d_calcworld.h
+++ b/include/nw4r/g3d/g3d_calcworld.h
@@ -1,6 +1,7 @@
 #ifndef NW4R_G3D_CALC_WORLD_H
 #define NW4R_G3D_CALC_WORLD_H
 #include "common.h"
+#include "nw4r/math.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_camera.h b/include/nw4r/g3d/g3d_camera.h
index e5c58271..9ac3ebb2 100644
--- a/include/nw4r/g3d/g3d_camera.h
+++ b/include/nw4r/g3d/g3d_camera.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_CAMERA_H
 #define NW4R_G3D_CAMERA_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "math_types.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math/math_types.h"
 #include <rvl/MTX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_dcc.h b/include/nw4r/g3d/g3d_dcc.h
index 56a009d8..0c83c1f0 100644
--- a/include/nw4r/g3d/g3d_dcc.h
+++ b/include/nw4r/g3d/g3d_dcc.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_DCC_H
 #define NW4R_G3D_DCC_H
 #include "common.h"
-#include "g3d_anmtexsrt.h"
+#include "nw4r/math.h"
+#include "nw4r/g3d/g3d_anmtexsrt.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_draw.h b/include/nw4r/g3d/g3d_draw.h
index 0d0956d7..3501607e 100644
--- a/include/nw4r/g3d/g3d_draw.h
+++ b/include/nw4r/g3d/g3d_draw.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_DRAW_H
 #define NW4R_G3D_DRAW_H
 #include "common.h"
-#include "g3d_resmdl.h"
+#include "nw4r/g3d/g3d_resmdl.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_draw1mat1shp.h b/include/nw4r/g3d/g3d_draw1mat1shp.h
index b442c9d1..694b8974 100644
--- a/include/nw4r/g3d/g3d_draw1mat1shp.h
+++ b/include/nw4r/g3d/g3d_draw1mat1shp.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_DRAW1_MAT1_SHP_H
 #define NW4R_G3D_DRAW1_MAT1_SHP_H
 #include "common.h"
-#include "g3d_resmat.h"
-#include "g3d_resshp.h"
+#include "nw4r/g3d/g3d_resmat.h"
+#include "nw4r/g3d/g3d_resshp.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_fog.h b/include/nw4r/g3d/g3d_fog.h
index 0c1932a7..c5e953aa 100644
--- a/include/nw4r/g3d/g3d_fog.h
+++ b/include/nw4r/g3d/g3d_fog.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_FOG_H
 #define NW4R_G3D_FOG_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "math_types.h"
-#include "ut_Color.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math/math_types.h"
+#include "nw4r/ut/ut_Color.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_gpu.h b/include/nw4r/g3d/g3d_gpu.h
index 693af719..2db74bd5 100644
--- a/include/nw4r/g3d/g3d_gpu.h
+++ b/include/nw4r/g3d/g3d_gpu.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_GPU_H
 #define NW4R_G3D_GPU_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "math_types.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math/math_types.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_init.h b/include/nw4r/g3d/g3d_init.h
index d6e9718a..9f52d5f1 100644
--- a/include/nw4r/g3d/g3d_init.h
+++ b/include/nw4r/g3d/g3d_init.h
@@ -5,12 +5,6 @@
 
 namespace nw4r {
 namespace g3d {
-inline void InitFastCast() {
-    OSInitFastCast();
-    OSSetGQR6(7, 5);
-    OSSetGQR7(7, 8);
-}
-
 void G3dInit(bool);
 void G3dReset();
 } // namespace g3d
diff --git a/include/nw4r/g3d/g3d_light.h b/include/nw4r/g3d/g3d_light.h
index a989dcf6..051688f6 100644
--- a/include/nw4r/g3d/g3d_light.h
+++ b/include/nw4r/g3d/g3d_light.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_LIGHT_H
 #define NW4R_G3D_LIGHT_H
 #include "common.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_maya.h b/include/nw4r/g3d/g3d_maya.h
index ab10b53f..4e344bb9 100644
--- a/include/nw4r/g3d/g3d_maya.h
+++ b/include/nw4r/g3d/g3d_maya.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_MAYA_H
 #define NW4R_G3D_MAYA_H
 #include "common.h"
-#include "g3d_anmtexsrt.h"
+#include "nw4r/g3d/g3d_anmtexsrt.h"
+#include "nw4r/math.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_obj.h b/include/nw4r/g3d/g3d_obj.h
index 6f596a11..978546ef 100644
--- a/include/nw4r/g3d/g3d_obj.h
+++ b/include/nw4r/g3d/g3d_obj.h
@@ -90,6 +90,7 @@ class G3dObj {
     }
 
     void Destroy();
+    void DetachFromParent();
 
     G3dObj(MEMAllocator *pAllocator, G3dObj *pParent) : mAllocator(pAllocator), mParent(pParent) {}
 
diff --git a/include/nw4r/g3d/g3d_resanmcamera.h b/include/nw4r/g3d/g3d_resanmcamera.h
index 0b1eb409..3dc8a33b 100644
--- a/include/nw4r/g3d/g3d_resanmcamera.h
+++ b/include/nw4r/g3d/g3d_resanmcamera.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESANMCAMERA_H
 #define NW4R_G3D_RESANMCAMERA_H
 #include "common.h"
-#include "g3d_anmcamera.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_anmcamera.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resanmchr.h b/include/nw4r/g3d/g3d_resanmchr.h
index fd6696f9..68b69e98 100644
--- a/include/nw4r/g3d/g3d_resanmchr.h
+++ b/include/nw4r/g3d/g3d_resanmchr.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_RESANMCHR_H
 #define NW4R_G3D_RESANMCHR_H
 #include "common.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resanmclr.h b/include/nw4r/g3d/g3d_resanmclr.h
index a92ad053..85c5327c 100644
--- a/include/nw4r/g3d/g3d_resanmclr.h
+++ b/include/nw4r/g3d/g3d_resanmclr.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_RESANMCLR_H
 #define NW4R_G3D_RESANMCLR_H
-#include "g3d_resdict.h"
-#include "g3d_resanm.h"
+#include "nw4r/g3d/g3d_resdict.h"
+#include "nw4r/g3d/g3d_resanm.h"
+#include "nw4r/g3d/g3d_anmclr.h"
 
 namespace nw4r
 {
@@ -65,4 +66,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_resanmfog.h b/include/nw4r/g3d/g3d_resanmfog.h
index 4cc933cc..77443159 100644
--- a/include/nw4r/g3d/g3d_resanmfog.h
+++ b/include/nw4r/g3d/g3d_resanmfog.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESANMFOG_H
 #define NW4R_G3D_RESANMFOG_H
 #include "common.h"
-#include "g3d_anmfog.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_anmfog.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resanmscn.h b/include/nw4r/g3d/g3d_resanmscn.h
index 5983b256..3f7af493 100644
--- a/include/nw4r/g3d/g3d_resanmscn.h
+++ b/include/nw4r/g3d/g3d_resanmscn.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_RESANMSCN_H
 #define NW4R_G3D_RESANMSCN_H
 #include "common.h"
-#include "g3d_resanmcamera.h"
-#include "g3d_resanmfog.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_resanmcamera.h"
+#include "nw4r/g3d/g3d_resanmfog.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resanmshp.h b/include/nw4r/g3d/g3d_resanmshp.h
index 58c3452b..c9a08db2 100644
--- a/include/nw4r/g3d/g3d_resanmshp.h
+++ b/include/nw4r/g3d/g3d_resanmshp.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_RESANMSHP_H
 #define NW4R_G3D_RESANMSHP_H
 #include "common.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resanmtexpat.h b/include/nw4r/g3d/g3d_resanmtexpat.h
index e06dfe9c..09b362de 100644
--- a/include/nw4r/g3d/g3d_resanmtexpat.h
+++ b/include/nw4r/g3d/g3d_resanmtexpat.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_RESANMTEXPAT_H
 #define NW4R_G3D_RESANMTEXPAT_H
-#include "g3d_resdict.h"
-#include "g3d_resanm.h"
+#include "nw4r/g3d/g3d_resdict.h"
+#include "nw4r/g3d/g3d_resfile.h"
+#include "nw4r/g3d/g3d_resanm.h"
 
 namespace nw4r
 {
@@ -87,4 +88,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_resanmtexsrt.h b/include/nw4r/g3d/g3d_resanmtexsrt.h
index 563a0e00..94e2966a 100644
--- a/include/nw4r/g3d/g3d_resanmtexsrt.h
+++ b/include/nw4r/g3d/g3d_resanmtexsrt.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_RESANMTEXSRT_H
 #define NW4R_G3D_RESANMTEXSRT_H
-#include "g3d_resanm.h"
-#include "g3d_resdict.h"
+#include "nw4r/g3d/g3d_resanm.h"
+#include "nw4r/g3d/g3d_resdict.h"
+#include "nw4r/g3d/g3d_anmtexsrt.h"
 
 namespace nw4r
 {
@@ -60,4 +61,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_resanmvis.h b/include/nw4r/g3d/g3d_resanmvis.h
index 82c28ea1..89bbbd44 100644
--- a/include/nw4r/g3d/g3d_resanmvis.h
+++ b/include/nw4r/g3d/g3d_resanmvis.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESANMVIS_H
 #define NW4R_G3D_RESANMVIS_H
-#include "g3d_resdict.h"
-#include "g3d_resanm.h"
-#include "g3d_anmobj.h"
+#include "nw4r/g3d/g3d_resdict.h"
+#include "nw4r/g3d/g3d_resanm.h"
+#include "nw4r/g3d/g3d_anmobj.h"
 
 namespace nw4r
 {
@@ -10,10 +10,10 @@ namespace nw4r
 	{
 		struct ResAnmVisInfoData
 		{
-			char UNK_0x0[0x8];
-			u16 mNumFrames; // at 0x1C
+			char UNK_0x0[0xC];
+			u16 mNumFrames; // at 0x20
 			u16 mNumNodes;
-			AnmPolicy mAnmPolicy; // at 0x20
+			AnmPolicy mAnmPolicy; // at 0x24
 		};
 		
 		struct ResAnmVisNodeData
@@ -79,4 +79,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_resdict.h b/include/nw4r/g3d/g3d_resdict.h
index 07ecc2e7..237ad095 100644
--- a/include/nw4r/g3d/g3d_resdict.h
+++ b/include/nw4r/g3d/g3d_resdict.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_RESDICT_H
 #define NW4R_G3D_RESDICT_H
 #include "common.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 #include <string.h>
 
 namespace nw4r {
diff --git a/include/nw4r/g3d/g3d_resfile.h b/include/nw4r/g3d/g3d_resfile.h
index 597fec6c..37f9d0c8 100644
--- a/include/nw4r/g3d/g3d_resfile.h
+++ b/include/nw4r/g3d/g3d_resfile.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESFILE_H
 #define NW4R_G3D_RESFILE_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "g3d_resdict.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/g3d/g3d_resdict.h"
 #include "ut_binaryFileFormat.h"
 
 namespace nw4r {
diff --git a/include/nw4r/g3d/g3d_resmat.h b/include/nw4r/g3d/g3d_resmat.h
index 1c5a6358..6c495685 100644
--- a/include/nw4r/g3d/g3d_resmat.h
+++ b/include/nw4r/g3d/g3d_resmat.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESMAT_H
 #define NW4R_G3D_RESMAT_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "math_types.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math/math_types.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_resmdl.h b/include/nw4r/g3d/g3d_resmdl.h
index bf865abf..6a7b4c4f 100644
--- a/include/nw4r/g3d/g3d_resmdl.h
+++ b/include/nw4r/g3d/g3d_resmdl.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_RESMDL_H
 #define NW4R_G3D_RESMDL_H
 #include "common.h"
-#include "g3d_rescommon.h"
-#include "g3d_resdict.h"
-#include "g3d_resnode.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/g3d/g3d_resdict.h"
+#include "nw4r/g3d/g3d_resnode.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resnode.h b/include/nw4r/g3d/g3d_resnode.h
index 4e080c9d..9c9fb445 100644
--- a/include/nw4r/g3d/g3d_resnode.h
+++ b/include/nw4r/g3d/g3d_resnode.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_RESNODE_H
 #define NW4R_G3D_RESNODE_H
 #include "common.h"
-#include "g3d_anmchr.h"
-#include "g3d_rescommon.h"
-#include "math_types.h"
+#include "nw4r/g3d/g3d_anmchr.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_resshp.h b/include/nw4r/g3d/g3d_resshp.h
index 55cd6f29..eadb88f6 100644
--- a/include/nw4r/g3d/g3d_resshp.h
+++ b/include/nw4r/g3d/g3d_resshp.h
@@ -1,8 +1,8 @@
 #ifndef NW4R_G3D_RESSHP_H
 #define NW4R_G3D_RESSHP_H
-#include "g3d_rescommon.h"
-#include "g3d_resmdl.h"
-#include "g3d_resvtx.h"
+#include "nw4r/g3d/g3d_rescommon.h"
+#include "nw4r/g3d/g3d_resmdl.h"
+#include "nw4r/g3d/g3d_resvtx.h"
 #include <GXAttr.h>#include "common.h"
 
 
diff --git a/include/nw4r/g3d/g3d_restev.h b/include/nw4r/g3d/g3d_restev.h
index 14c158ad..9146c1fd 100644
--- a/include/nw4r/g3d/g3d_restev.h
+++ b/include/nw4r/g3d/g3d_restev.h
@@ -2,8 +2,8 @@
 #define NW4R_G3D_RESTEV_H
 #include <GXTev.h>
 #include <GXTexture.h>
-#include "g3d_cpu.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_cpu.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r
 {
@@ -58,4 +58,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_restex.h b/include/nw4r/g3d/g3d_restex.h
index 9d85d355..1f9b6596 100644
--- a/include/nw4r/g3d/g3d_restex.h
+++ b/include/nw4r/g3d/g3d_restex.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_RESTEX_H
 #define NW4R_G3D_RESTEX_H
 #include "common.h"
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 #include <rvl/GX.h>
 
 
diff --git a/include/nw4r/g3d/g3d_resvtx.h b/include/nw4r/g3d/g3d_resvtx.h
index 717aa0e4..448eaedd 100644
--- a/include/nw4r/g3d/g3d_resvtx.h
+++ b/include/nw4r/g3d/g3d_resvtx.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_RESVTX_H
 #define NW4R_G3D_RESVTX_H
 #include <GXAttr.h>
-#include "g3d_rescommon.h"
+#include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r
 {
@@ -155,4 +155,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/g3d/g3d_scnmdl.h b/include/nw4r/g3d/g3d_scnmdl.h
index 5f1cb9e9..778d0d96 100644
--- a/include/nw4r/g3d/g3d_scnmdl.h
+++ b/include/nw4r/g3d/g3d_scnmdl.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_SCN_MDL_H
 #define NW4R_G3D_SCN_MDL_H
 #include "common.h"
-#include "g3d_draw.h"
-#include "g3d_resmat.h"
-#include "g3d_scnmdlsmpl.h"
+#include "nw4r/g3d/g3d_draw.h"
+#include "nw4r/g3d/g3d_resmat.h"
+#include "nw4r/g3d/g3d_scnmdlsmpl.h"
 
 namespace nw4r {
 namespace g3d {
@@ -19,6 +19,11 @@ class ScnMdl : public ScnMdlSimple {
     };
 
 public:
+
+    virtual bool SetAnmObj(AnmObj* p, AnmObjType type) override;
+
+    static ScnMdl *Construct(MEMAllocator*, unsigned long*, nw4r::g3d::ResMdl, u32 bufferOption, int);
+
     static const G3dObj::TypeObj GetTypeObjStatic() {
         return TypeObj(TYPE_NAME);
     }
diff --git a/include/nw4r/g3d/g3d_scnmdl1mat1shp.h b/include/nw4r/g3d/g3d_scnmdl1mat1shp.h
index 5e3d3dc9..ddbf561e 100644
--- a/include/nw4r/g3d/g3d_scnmdl1mat1shp.h
+++ b/include/nw4r/g3d/g3d_scnmdl1mat1shp.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_SCN_MDL1_MAT1_SHP_H
 #define NW4R_G3D_SCN_MDL1_MAT1_SHP_H
 #include "common.h"
-#include "g3d_resmat.h"
-#include "g3d_resshp.h"
-#include "g3d_scnobj.h"
+#include "nw4r/g3d/g3d_resmat.h"
+#include "nw4r/g3d/g3d_resshp.h"
+#include "nw4r/g3d/g3d_scnobj.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_scnmdlsmpl.h b/include/nw4r/g3d/g3d_scnmdlsmpl.h
index 00ab12a0..f788a0e9 100644
--- a/include/nw4r/g3d/g3d_scnmdlsmpl.h
+++ b/include/nw4r/g3d/g3d_scnmdlsmpl.h
@@ -1,8 +1,9 @@
 #ifndef NW4R_G3D_SCN_MDL_SIMPLE_H
 #define NW4R_G3D_SCN_MDL_SIMPLE_H
 #include "common.h"
-#include "g3d_resmdl.h"
-#include "g3d_scnobj.h"
+#include "nw4r/math/math_types.h"
+#include "nw4r/g3d/g3d_resmdl.h"
+#include "nw4r/g3d/g3d_scnobj.h"
 
 namespace nw4r {
 namespace g3d {
@@ -15,13 +16,30 @@ class ScnMdlSimple : public ScnLeaf {
         BYTE_CODE_DRAW_XLU,
     };
 
+    enum AnmObjType {
+        ANMOBJ_0,
+        ANMOBJ_1,
+        ANMOBJ_2,
+        ANMOBJ_3,
+        ANMOBJ_4,
+        ANMOBJ_5,
+        ANMOBJ_6,
+    };
+
 public:
     ScnMdlSimple(MEMAllocator *, ResMdl, math::MTX34 *, u32 *, math::MTX34 *, math::MTX33 *, math::MTX34 *, int, int);
 
+    static ScnMdlSimple *Construct(MEMAllocator*, unsigned long*, nw4r::g3d::ResMdl, int);
+
+    bool GetScnMtxPos(math::MTX34* pOut, ScnObjMtxType tp, u32 nodeID) const;
+
     virtual bool IsDerivedFrom(TypeObj other) const // at 0x8
     {
         return (other == GetTypeObjStatic()) ? true : ScnLeaf::IsDerivedFrom(other);
     }
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
     virtual void G3dProc(u32, u32, void *);  // at 0xC
     virtual ~ScnMdlSimple();                 // at 0x10
     virtual const TypeObj GetTypeObj() const // at 0x14
@@ -33,6 +51,10 @@ class ScnMdlSimple : public ScnLeaf {
         return GetTypeObj().GetTypeName();
     }
 
+    virtual bool SetAnmObj(AnmObj* p, AnmObjType type);
+    virtual bool RemoveAnmObj(AnmObj *p);
+    virtual bool RemoveAnmObj(AnmObjType type);
+
     const u8 *GetByteCode(ByteCodeType) const;
 
     const ResMdl GetResMdl() const {
diff --git a/include/nw4r/g3d/g3d_scnobj.h b/include/nw4r/g3d/g3d_scnobj.h
index f10a037e..73456000 100644
--- a/include/nw4r/g3d/g3d_scnobj.h
+++ b/include/nw4r/g3d/g3d_scnobj.h
@@ -1,9 +1,9 @@
 #ifndef NW4R_G3D_SCNOBJ_H
 #define NW4R_G3D_SCNOBJ_H
 #include "common.h"
-#include "g3d_obj.h"
-#include "math_geometry.h"
-#include "math_types.h"
+#include "nw4r/g3d/g3d_obj.h"
+#include "nw4r/math/math_geometry.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace g3d {
@@ -39,9 +39,9 @@ class ScnObj : public G3dObj {
         FLAG_60 = FLAG_40 | FLAG_20
     };
 
-    enum ScnObjMtxType { MTX_TYPE_0, MTX_TYPE_WORLD, MTX_TYPE_VIEW, MTX_TYPE_MAX };
+    enum ScnObjMtxType { MTX_TYPE_LOCAL, MTX_TYPE_WORLD, MTX_TYPE_VIEW, MTX_TYPE_MAX };
 
-    enum Timing { TIMING_1 = 0x1, TIMING_2 = 0x2, TIMING_4 = 0x4 };
+    enum Timing { TIMING_1 = 0x1, TIMING_2 = 0x2, TIMING_4 = 0x4, TIMING_ALL = 0x7 };
 
     enum ExecOp { EXEC_OP_1 = 0x1, EXEC_OP_2 = 0x2, EXEC_OP_4 = 0x4 };
 
@@ -88,6 +88,9 @@ class ScnObj : public G3dObj {
     void EnableScnObjCallbackExecOp(ExecOp);
     bool SetBoundingVolume(ScnObjBoundingVolumeType, const math::AABB *);
     bool GetBoundingVolume(ScnObjBoundingVolumeType, math::AABB *) const;
+    void SetCallback(IScnObjCallback *cb) {
+        mCallback = cb;
+    }
 
     const math::MTX34 *GetMtxPtr(ScnObjMtxType type) const {
         return &mMatrices[type];
@@ -202,6 +205,17 @@ class ScnLeaf : public ScnObj {
     ScaleProperty GetScaleProperty() const;
     void DefG3dProcScnLeaf(u32, u32, void *);
 
+    inline void SetScale(f32 x, f32 y, f32 z) {
+        mScale.x = x;
+        mScale.y = y;
+        mScale.z = z;
+    }
+
+    inline void SetScale(const math::VEC3& scale)
+    {
+        mScale = scale;
+    }
+
 private:
     math::VEC3 mScale;
 
@@ -235,6 +249,10 @@ class ScnGroup : public ScnObj {
         return TypeObj(TYPE_NAME);
     }
 
+    bool PushBack(ScnObj *obj) {
+        return Insert(Size(), obj);
+    }
+
     bool Empty() const {
         return mSize == 0;
     }
@@ -263,8 +281,6 @@ class ScnGroup : public ScnObj {
     void ScnGroup_G3DPROC_CALC_VIEW(u32, const math::MTX34 *);
     void DefG3dProcScnGroup(u32, u32, void *);
 
-    bool PushBack(ScnObj *);
-
     ScnObj **mObjects; // at 0xDC
     u32 mCapacity;     // at 0xE0
     u32 mSize;         // at 0xE4
diff --git a/include/nw4r/g3d/g3d_scnproc.h b/include/nw4r/g3d/g3d_scnproc.h
index 45aa86b6..c9d75a1d 100644
--- a/include/nw4r/g3d/g3d_scnproc.h
+++ b/include/nw4r/g3d/g3d_scnproc.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_SCNPROC_H
 #define NW4R_G3D_SCNPROC_H
 #include "common.h"
-#include "g3d_scnobj.h"
+#include "nw4r/g3d/g3d_scnobj.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/g3d/g3d_scnrfl.h b/include/nw4r/g3d/g3d_scnrfl.h
index cca91279..a5e30e44 100644
--- a/include/nw4r/g3d/g3d_scnrfl.h
+++ b/include/nw4r/g3d/g3d_scnrfl.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_G3D_SCNRFL_H
 #define NW4R_G3D_SCNRFL_H
 #include "common.h"
-#include "g3d_scnleaf.h"
+#include "nw4r/g3d/g3d_scnobj.h"
 #include <RFL/RFL_MiddleDatabase.h>
 #include <RFL/RFL_Model.h>
 
diff --git a/include/nw4r/g3d/g3d_scnroot.h b/include/nw4r/g3d/g3d_scnroot.h
index bfd9a9b3..0de89925 100644
--- a/include/nw4r/g3d/g3d_scnroot.h
+++ b/include/nw4r/g3d/g3d_scnroot.h
@@ -1,9 +1,10 @@
 #ifndef NW4R_G3D_SCNROOT_H
 #define NW4R_G3D_SCNROOT_H
 #include "common.h"
-#include "g3d_camera.h"
-#include "g3d_fog.h"
-#include "g3d_scnobj.h"
+#include "nw4r/g3d/g3d_camera.h"
+#include "nw4r/g3d/g3d_fog.h"
+#include "nw4r/g3d/g3d_scnobj.h"
+#include "nw4r/g3d/g3d_light.h"
 
 namespace nw4r {
 namespace g3d {
@@ -14,6 +15,8 @@ class ScnRoot : public ScnGroup {
     void SetCurrentCamera(int);
     Fog GetFog(int);
 
+    static ScnRoot *Construct(MEMAllocator *param_1,u32 *param_2,u32 param_3,u32 param_4, u32 param_5,u32 param_6);
+
     void UpdateFrame();
     void CalcWorld();
     void CalcMaterial();
@@ -28,11 +31,17 @@ class ScnRoot : public ScnGroup {
         return mCameraId;
     }
 
+    inline LightSetting *getLightSetting() {
+        return &mLightSetting;
+    }
+
 private:
     UNKWORD WORD_0xE8;
     UNKWORD WORD_0xEC;
     UNKWORD WORD_0xF0;
-    u8 mCameraId;
+    /* 0x00F4 */ u8 mCameraId;
+    u8 unk1[0x2878 - 0x00F8];
+    /* 0x2878 */ LightSetting mLightSetting;
 };
 } // namespace g3d
 } // namespace nw4r
diff --git a/include/nw4r/g3d/g3d_state.h b/include/nw4r/g3d/g3d_state.h
index 0f16769f..f3af0bde 100644
--- a/include/nw4r/g3d/g3d_state.h
+++ b/include/nw4r/g3d/g3d_state.h
@@ -1,6 +1,7 @@
 #ifndef NW4R_G3D_STATE_H
 #define NW4R_G3D_STATE_H
 #include "common.h"
+#include "nw4r/math.h"
 #include <rvl/GX.h>
 
 namespace nw4r {
diff --git a/include/nw4r/g3d/g3d_xsi.h b/include/nw4r/g3d/g3d_xsi.h
index fb812e49..a993df96 100644
--- a/include/nw4r/g3d/g3d_xsi.h
+++ b/include/nw4r/g3d/g3d_xsi.h
@@ -1,7 +1,8 @@
 #ifndef NW4R_G3D_XSI_H
 #define NW4R_G3D_XSI_H
 #include "common.h"
-#include "g3d_anmtexsrt.h"
+#include "nw4r/math.h"
+#include "nw4r/g3d/g3d_anmtexsrt.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/include/nw4r/snd/snd_Sound3DActor.h b/include/nw4r/snd/snd_Sound3DActor.h
index 977eab1b..7cc15cc2 100644
--- a/include/nw4r/snd/snd_Sound3DActor.h
+++ b/include/nw4r/snd/snd_Sound3DActor.h
@@ -2,7 +2,7 @@
 #define NW4R_SND_SOUND_3D_ACTOR_H
 #include "snd_BasicSound.h"
 #include "snd_SoundActor.h"
-#include "math_types.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r
 {
@@ -37,4 +37,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/nw4r/snd/snd_Sound3DListener.h b/include/nw4r/snd/snd_Sound3DListener.h
index 4e12f717..b770f6d4 100644
--- a/include/nw4r/snd/snd_Sound3DListener.h
+++ b/include/nw4r/snd/snd_Sound3DListener.h
@@ -1,7 +1,7 @@
 #ifndef NW4R_SND_SOUND_3D_LISTENER_H
 #define NW4R_SND_SOUND_3D_LISTENER_H
 #include "common.h"
-#include "math_types.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace snd {
diff --git a/include/nw4r/snd/snd_Sound3DManager.h b/include/nw4r/snd/snd_Sound3DManager.h
index 432c30df..529a471f 100644
--- a/include/nw4r/snd/snd_Sound3DManager.h
+++ b/include/nw4r/snd/snd_Sound3DManager.h
@@ -4,7 +4,7 @@
 #include "snd_SoundArchive.h"
 #include "snd_SoundActor.h"
 #include "snd_InstancePool.h"
-#include "math_types.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r
 {
@@ -50,4 +50,4 @@ namespace nw4r
 	}
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/m/m3d/m3d.cpp b/src/m/m3d/m3d.cpp
new file mode 100644
index 00000000..4f365bb0
--- /dev/null
+++ b/src/m/m3d/m3d.cpp
@@ -0,0 +1,99 @@
+#include <m/m3d/m3d.h>
+#include <nw4r/g3d/g3d_init.h>
+#include <nw4r/g3d/g3d_state.h>
+#include <rvl/GX.h>
+
+namespace m3d {
+
+namespace internal {
+
+mAllocator_c *l_allocator_p;
+nw4r::g3d::ScnRoot *l_scnRoot_p;
+
+} // namespace internal
+
+extern "C" GXRenderModeObj **lbl_80575C88;
+
+bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4) {
+    internal::l_allocator_p = new (heap, 0x04) mAllocator_c();
+
+    if (!internal::l_allocator_p) {
+        return false;
+    }
+
+    internal::l_allocator_p->attach(heap, 0x20);
+    nw4r::g3d::G3dInit(true);
+    nw4r::g3d::G3DState::SetRenderModeObj(**lbl_80575C88);
+    u32 allocedSize;
+    internal::l_scnRoot_p = nw4r::g3d::ScnRoot::Construct(internal::l_allocator_p, &allocedSize, u1, u2, u3, u4);
+    if (internal::l_scnRoot_p == nullptr) {
+        delete internal::l_allocator_p;
+        internal::l_allocator_p = nullptr;
+        return false;
+    }
+    return true;
+}
+
+nw4r::g3d::ScnRoot *getScnRoot() {
+    return internal::l_scnRoot_p;
+}
+
+nw4r::g3d::Camera getCamera(int id) {
+    return internal::l_scnRoot_p->GetCamera(id);
+}
+
+nw4r::g3d::Camera getCurrentCamera() {
+    return internal::l_scnRoot_p->GetCurrentCamera();
+}
+
+int getCurrentCameraID() {
+    return internal::l_scnRoot_p->GetCurrentCameraID();
+}
+
+void setCurrentCamera(int id) {
+    internal::l_scnRoot_p->SetCurrentCamera(id);
+}
+
+nw4r::g3d::LightSetting *getLightSettingP() {
+    return internal::l_scnRoot_p->getLightSetting();
+}
+
+// TODO EGG
+
+void calcWorld(int idx) {
+    internal::l_scnRoot_p->CalcWorld();
+}
+
+void calcMaterial() {
+    internal::l_scnRoot_p->CalcMaterial();
+}
+
+void drawOpa() {
+    internal::l_scnRoot_p->DrawOpa();
+}
+
+void drawXlu() {
+    internal::l_scnRoot_p->DrawXlu();
+}
+
+bool pushBack(nw4r::g3d::ScnObj *obj) {
+    return internal::l_scnRoot_p->PushBack(obj);
+}
+
+void clear() {
+    internal::l_scnRoot_p->Clear();
+}
+
+void reset() {
+    nw4r::g3d::G3dReset();
+    // TODO EGG
+}
+
+void resetMaterial() {
+    GXSetNumIndStages(0);
+    for (int i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
+        GXSetTevDirect((GXTevStageID)i);
+    }
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_anmchr.cpp b/src/m/m3d/m_anmchr.cpp
new file mode 100644
index 00000000..7d3d9f72
--- /dev/null
+++ b/src/m/m3d/m_anmchr.cpp
@@ -0,0 +1,30 @@
+#include <m/m3d/m_anmchr.h>
+#include <nw4r/g3d/g3d_anmobj.h>
+#include <nw4r/g3d/g3d_resanmchr.h>
+
+namespace m3d {
+
+anmChr_c::~anmChr_c() {}
+
+int anmChr_c::getType() {
+    return 0;
+}
+
+void anmChr_c::setAnm(bmdl_c &mdl, nw4r::g3d::ResAnmChr anm, playMode_e mode) {
+    mdl.removeAnm((nw4r::g3d::ScnMdlSimple::AnmObjType)getType());
+    setAnmAfter(mdl, anm, mode);
+}
+
+void anmChr_c::setAnmAfter(bmdl_c &mdl, nw4r::g3d::ResAnmChr anm, playMode_e mode) {
+    // TODO g3d headers
+    // void *parent = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrNode>(mpAnmObj->GetParent());
+}
+
+void anmChr_c::setFrmCtrlDefault(nw4r::g3d::ResAnmChr &anm, m3d::playMode_e mode) {
+    if (mode == 4) {
+        // TODO g3d headers
+    }
+    set(1.0f, mode, 1.0f, -1.0f);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_anmvis.cpp b/src/m/m3d/m_anmvis.cpp
new file mode 100644
index 00000000..84a30edf
--- /dev/null
+++ b/src/m/m3d/m_anmvis.cpp
@@ -0,0 +1,58 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmvis.h>
+#include <nw4r/g3d/g3d_anmvis.h>
+#include <nw4r/g3d/g3d_resanmvis.h>
+#include <nw4r/g3d/g3d_resmdl.h>
+
+
+namespace m3d {
+
+anmVis_c::~anmVis_c() {}
+
+int anmVis_c::getType() {
+    return 1;
+}
+
+bool anmVis_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmVis anm, mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 tmp;
+    if (pSize == nullptr) {
+        pSize = &tmp;
+    }
+
+    nw4r::g3d::AnmObjVisRes::Construct(nullptr, pSize, anm, mdl);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjVisRes::Construct(&mAllocator, &tmp, anm, mdl);
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    } else {
+        setFrmCtrlDefault(anm, PLAY_MODE_4);
+        return true;
+    }
+}
+
+void anmVis_c::setAnm(m3d::bmdl_c &mdl, nw4r::g3d::ResAnmVis anm, m3d::playMode_e mode) {
+    mdl.removeAnm((nw4r::g3d::ScnMdlSimple::AnmObjType)getType());
+    mpAnmObj->Release();
+    mpFrameHeap->free(0x3);
+    u32 tmp;
+    mpAnmObj = nw4r::g3d::AnmObjVisRes::Construct(&mAllocator, &tmp, anm, mdl.getResMdl());
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(anm, mode);
+}
+
+void anmVis_c::setFrmCtrlDefault(nw4r::g3d::ResAnmVis &anm, m3d::playMode_e mode) {
+    if (mode == PLAY_MODE_4) {
+        mode = anm.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
+    }
+    set(anm.GetNumFrame(), mode, 1.0f, -1.0f);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_banm.cpp b/src/m/m3d/m_banm.cpp
new file mode 100644
index 00000000..8fc17be8
--- /dev/null
+++ b/src/m/m3d/m_banm.cpp
@@ -0,0 +1,62 @@
+#include <m/m3d/m_banm.h>
+#include <m/m_heap.h>
+
+extern "C" u32 lbl_80575BE8;
+
+namespace m3d {
+banm_c::~banm_c() {
+    banm_c::remove();
+}
+
+void banm_c::remove() {
+    if (mpAnmObj != nullptr) {
+        mpAnmObj->DetachFromParent();
+        mpAnmObj->Destroy();
+        mpAnmObj = nullptr;
+    }
+
+    if (mpFrameHeap != nullptr) {
+        mHeap::destroyFrmHeap(mpFrameHeap);
+        mpFrameHeap = nullptr;
+    }
+}
+
+bool banm_c::createAllocator(mAllocator_c *alloc, u32 *pStart) {
+    // TODO understand this maybe?
+    int i1 = mHeap::frmHeapCost(*pStart, 0x20);
+    int i2 = mHeap::frmHeapCost(0, 0x20);
+    i2 = ROUND_UP(i1, 0x20) - i2;
+    i1 = mHeap::frmHeapCost(i2, 0x20);
+    *pStart = ROUND_UP(i1, 0x20);
+    mpFrameHeap = mHeap::createFrmHeap(i2, (EGG::Heap *)alloc->mHeap, "アニメ切り替え用アロケータ(m3d::banm_c::m_heap)",
+            lbl_80575BE8, 0);
+    mAllocator.attach(mpFrameHeap, 0x20);
+    return true;
+}
+
+bool banm_c::IsBound() const {
+    if (mpAnmObj == nullptr) {
+        return false;
+    }
+    return mpAnmObj->TestAnmFlag(nw4r::g3d::AnmObj::ANMFLAG_ISBOUND);
+}
+
+void banm_c::play() {}
+
+f32 banm_c::getFrame() const {
+    return mpAnmObj->GetFrame();
+}
+
+void banm_c::setFrameOnly(f32 frame) {
+    mpAnmObj->SetFrame(frame);
+}
+
+f32 banm_c::getRate() const {
+    return mpAnmObj->GetUpdateRate();
+}
+
+void banm_c::setRate(f32 f) {
+    mpAnmObj->SetUpdateRate(f);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_bmdl.cpp b/src/m/m3d/m_bmdl.cpp
new file mode 100644
index 00000000..2f6967eb
--- /dev/null
+++ b/src/m/m3d/m_bmdl.cpp
@@ -0,0 +1,62 @@
+#include <m/m3d/m_bmdl.h>
+#include <nw4r/g3d/g3d_scnmdl.h>
+
+namespace m3d {
+
+bmdl_c::~bmdl_c() {
+    remove();
+}
+
+int bmdl_c::getType() const {
+    return 0;
+}
+
+void bmdl_c::getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const {
+    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf)->GetScnMtxPos(out,
+            nw4r::g3d::ScnObj::MTX_TYPE_WORLD, p1);
+}
+
+void bmdl_c::setAnm(banm_c &anm) {
+    nw4r::g3d::ScnMdlSimple *mdl;
+    if (anm.getType() == 5) {
+        mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJ_6);
+    } else {
+        mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
+        if (anm.getType() == 0) {
+            mpCurrentAnm = &anm;
+        }
+        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJ_6);
+    }
+}
+
+void bmdl_c::play() {
+    if (mpCurrentAnm != nullptr) {
+        mpCurrentAnm->play();
+    }
+}
+
+nw4r::g3d::ResMdl bmdl_c::getResMdl() const {
+    return nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf)->GetResMdl();
+}
+
+nw4r::g3d::ResMat bmdl_c::getResMat(u32 index) const {
+    nw4r::g3d::ResMdl mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf)->GetResMdl();
+    return mdl.GetResMat(index);
+}
+
+void bmdl_c::removeAnm(nw4r::g3d::ScnMdlSimple::AnmObjType type) {
+    // TODO regswap
+    nw4r::g3d::ScnMdlSimple *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
+    if (type == nw4r::g3d::ScnMdlSimple::ANMOBJ_0) {
+        mpCurrentAnm = nullptr;
+    }
+    mdl->RemoveAnmObj(type);
+}
+
+void bmdl_c::remove() {
+    mpCurrentAnm = nullptr;
+    scnLeaf_c::remove();
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_calc_ratio.cpp b/src/m/m3d/m_calc_ratio.cpp
new file mode 100644
index 00000000..351e3444
--- /dev/null
+++ b/src/m/m3d/m_calc_ratio.cpp
@@ -0,0 +1,75 @@
+#include <m/m3d/m_calc_ratio.h>
+
+namespace m3d {
+
+calcRatio_c::calcRatio_c() {
+    mf1 = 0.0f;
+    mf2 = 1.0f;
+    mf3 = 0.0f;
+    mf4 = 0.0f;
+    mf5 = 1.0f;
+    mb1 = 0;
+    mb2 = 0;
+}
+
+calcRatio_c::~calcRatio_c() {}
+
+void calcRatio_c::remove() {
+    mb1 = 0;
+    reset();
+}
+
+void calcRatio_c::reset() {
+    mf1 = 0.0f;
+    mf2 = 1.0f;
+    mf4 = 0.0f;
+    mf5 = 1.0f;
+}
+
+void calcRatio_c::offUpdate() {
+    mb1 = 1;
+    mb2 = 0;
+}
+
+void calcRatio_c::set(f32 value) {
+    if (value == 0.0f) {
+        reset();
+        return;
+    }
+    mf1 = 1.0f;
+    mf2 = 0.0f;
+    mf4 = 1.0f;
+    mf5 = 0.0f;
+    mb2 = 1;
+    mf3 = 1.0f / value;
+}
+
+void calcRatio_c::calc() {
+    f32 max = 1.0f;
+    f32 start = mf1;
+    if (start == 0.0f) {
+        return;
+    }
+
+    mf2 += mf3;
+    if (mf2 >= 1.0f) {
+        reset();
+        return;
+    } else {
+        mb2 = 1;
+        mf1 = start - start * mf2 * mf2;
+        max = 1.0f - mf1;
+        // TODO here be ASM
+        f32 f4 = mf1 / start + max;
+        f32 f1 = __fres(f4);
+        f4 = -(f4 * f1 * f1 - (f1 + f1));
+        mf4 = (mf1 / start) * f4;
+        mf5 = max * f4;
+    }
+}
+
+bool calcRatio_c::isEnd() const {
+    return mf1 == 0.0f;
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_fanm.cpp b/src/m/m3d/m_fanm.cpp
new file mode 100644
index 00000000..31f6af33
--- /dev/null
+++ b/src/m/m3d/m_fanm.cpp
@@ -0,0 +1,83 @@
+#include <m/m3d/m_fanm.h>
+
+namespace m3d {
+
+fanm_c::fanm_c() {
+    mEndFrame = 0.0f;
+    mCurrentFrame = 0.0f;
+    mPlayState = 0.0f;
+}
+fanm_c::~fanm_c() {}
+
+void fanm_c::play() {
+    f32 frame = mpAnmObj->GetFrame();
+    f32 rate = mpAnmObj->GetUpdateRate();
+    bool rateWasNegative = rate < 0.0f;
+    if (rateWasNegative) {
+        rate *= -1.0f;
+    }
+    mCurrentFrame = frame;
+
+    f32 newFrame;
+
+    if (rateWasNegative || (mPlayState & 2)) {
+        newFrame = mStartFrame;
+        if (frame >= rate + mStartFrame) {
+            newFrame = frame - rate;
+        } else if ((mPlayState & 1) == 0) {
+            newFrame = frame + ((mEndFrame - rate) - mStartFrame);
+        }
+    } else {
+        newFrame = frame + rate;
+        if ((mPlayState & 1) == 0) {
+            if (newFrame >= mEndFrame) {
+                newFrame = newFrame - mEndFrame;
+            }
+        } else {
+            f32 t = mEndFrame - 1.0f;
+            if (newFrame >= t) {
+                newFrame = t;
+            }
+        }
+    }
+    mpAnmObj->SetFrame(newFrame);
+}
+
+void fanm_c::set(f32 startFrame, playMode_e mode, f32 updateRate, f32 currentFrame) {
+    if (currentFrame < 0.0f) {
+        // TODO shuffle
+        f32 newFrame = mode == PLAY_MODE_1 ? 0.0f : startFrame - 1.0f;
+        currentFrame = newFrame;
+    }
+
+    mEndFrame = startFrame;
+    mStartFrame = 0.0f;
+    mpAnmObj->SetFrame(currentFrame);
+    mpAnmObj->SetUpdateRate(updateRate);
+    mPlayState = (u8)mode;
+    mCurrentFrame = currentFrame;
+}
+
+void fanm_c::setFrame(f32) {
+    // TODO g3d headers
+}
+
+// assumed name
+void fanm_c::setFrameOnly(f32 frame) {
+    banm_c::setFrameOnly(frame);
+    mCurrentFrame = frame;
+}
+
+bool fanm_c::isStop() const {
+    // TODO g3d headers
+}
+
+bool fanm_c::checkFrame(f32) const {
+    // TODO g3d headers
+}
+
+bool fanm_c::unk_802EAE70() const {
+    // TODO g3d headers
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
new file mode 100644
index 00000000..3c643131
--- /dev/null
+++ b/src/m/m3d/m_mdl.cpp
@@ -0,0 +1,127 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_mdl.h>
+#include <nw4r/g3d/g3d_scnmdlsmpl.h>
+
+namespace m3d {
+
+mdl_c::mdlCallback_c::mdlCallback_c() {
+    mNumNode = 0;
+    mpNodes = nullptr;
+    mCallbackNum = 0;
+    mpAlloc = nullptr;
+}
+mdl_c::mdlCallback_c::~mdlCallback_c() {}
+
+bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 *pSize) {}
+
+void mdl_c::mdlCallback_c::remove() {
+    mCalcRatio.remove();
+    if (mpNodes != nullptr) {
+        // Probably an m_allocator inline
+        MEMFreeToAllocator(mpAlloc, mpNodes);
+    }
+    mpNodes = nullptr;
+    mpAlloc = nullptr;
+}
+
+void mdl_c::mdlCallback_c::setBlendFrame(f32 frame) {
+    mCalcRatio.set(frame);
+}
+void mdl_c::mdlCallback_c::calcBlend() {
+    if (!mCalcRatio.isEnd()) {
+        mCalcRatio.calc();
+    }
+}
+
+mdl_c::mdl_c() : mpOwnedCallback(nullptr), mpCallback(nullptr) {}
+mdl_c::~mdl_c() {
+    remove();
+}
+
+bool mdl_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize) {
+    return create(mdl, nullptr, alloc, bufferOption, nView, pSize);
+}
+
+bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c *alloc, u32 bufferOption, int nView,
+        u32 *pSize) {
+    nw4r::g3d::ScnMdlSimple *sMdl;
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 tmp1;
+    u32 tmp2;
+    u32 *pSize1 = nullptr;
+    u32 *pSize2 = nullptr;
+    if (pSize != nullptr) {
+        pSize1 = &tmp1;
+        pSize2 = &tmp2;
+    }
+
+    bool success;
+
+    if (!smdl_c::create(mdl, alloc, bufferOption, nView, pSize1)) {
+        return false;
+    }
+    if (cb == nullptr) {
+        // TODO sizeof
+        mpOwnedCallback = (mdlCallback_c *)alloc->alloc(0x30);
+        if (mpOwnedCallback == nullptr) {
+            remove();
+            return false;
+        }
+
+        new (mpOwnedCallback) mdlCallback_c();
+        mpCallback = (mdlCallback_c *)mpOwnedCallback;
+    } else {
+        mpCallback = cb;
+    }
+    if (!mpCallback->create(mdl, alloc, pSize2)) {
+        remove();
+        return false;
+    } else {
+        if (pSize != nullptr) {
+            *pSize = tmp1 + tmp2;
+        }
+
+        sMdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
+        sMdl->SetCallback(mpCallback);
+        sMdl->EnableScnObjCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
+        setCallback(nullptr);
+        return true;
+    }
+}
+
+void mdl_c::setCallback(mdlCallback_c *cb) {
+    mpCallback = cb;
+}
+
+void mdl_c::remove() {
+    if (mpOwnedCallback != nullptr) {
+        mAllocator_c *alloc = mpOwnedCallback->getAllocator();
+        mpOwnedCallback->remove();
+        mpOwnedCallback->~mdlCallback_c();
+        alloc->free(mpOwnedCallback);
+        mpOwnedCallback = nullptr;
+        mpCallback = nullptr;
+    }
+    bmdl_c::remove();
+}
+
+void mdl_c::setAnm(m3d::banm_c &anm) {
+    setAnm(anm, 0.0f);
+}
+
+void mdl_c::play() {
+    bmdl_c::play();
+    mpCallback->calcBlend();
+}
+
+void mdl_c::setAnm(m3d::banm_c &anm, f32 f) {
+    if (anm.getType() == 0) {
+        mpCallback->setBlendFrame(f);
+    }
+    bmdl_c::setAnm(anm);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_scnLeaf.cpp b/src/m/m3d/m_scnLeaf.cpp
new file mode 100644
index 00000000..b67793c8
--- /dev/null
+++ b/src/m/m3d/m_scnLeaf.cpp
@@ -0,0 +1,58 @@
+#include <m/m3d/m_scnleaf.h>
+
+namespace m3d {
+
+scnLeaf_c::scnLeaf_c() : mpScnLeaf(nullptr) {}
+
+scnLeaf_c::~scnLeaf_c() {
+    scnLeaf_c::remove();
+}
+
+void scnLeaf_c::remove() {
+    if (mpScnLeaf != nullptr) {
+        mpScnLeaf->Destroy();
+        mpScnLeaf = nullptr;
+    }
+}
+
+void scnLeaf_c::setOption(unsigned long flag, unsigned long set) {
+    mpScnLeaf->SetScnObjOption(flag, set);
+}
+
+void scnLeaf_c::setScale(f32 x, f32 y, f32 z) {
+    mpScnLeaf->SetScale(x, y, z);
+}
+
+void scnLeaf_c::setScale(const nw4r::math::VEC3 &scale) {
+    mpScnLeaf->SetScale(scale);
+}
+
+void scnLeaf_c::setLocalMtx(const nw4r::math::MTX34 *mtx) {
+    mpScnLeaf->SetMtx(nw4r::g3d::ScnObj::MTX_TYPE_LOCAL, mtx);
+}
+
+void scnLeaf_c::getLocalMtx(nw4r::math::MTX34 *mtx) const {
+    mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_LOCAL, mtx);
+}
+
+void scnLeaf_c::getViewMtx(nw4r::math::MTX34 *mtx) const {
+    mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_VIEW, mtx);
+}
+
+void scnLeaf_c::calc(bool b) {
+    setOption(/* DISABLE_CALC_WORLD */ 0x02, false);
+    mpScnLeaf->G3dProc(1, 0, nullptr);
+    if (b == false) {
+        setOption(/* DISABLE_CALC_WORLD */ 0x02, true);
+    }
+}
+
+void scnLeaf_c::calcVtx(bool b) {
+    setOption(/* DISABLE_CALC_VTX */ 0x04, false);
+    mpScnLeaf->G3dProc(3, 0, nullptr);
+    if (b == false) {
+        setOption(/* DISABLE_CALC_VTX */ 0x04, true);
+    }
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_smdl.cpp b/src/m/m3d/m_smdl.cpp
new file mode 100644
index 00000000..50460ef0
--- /dev/null
+++ b/src/m/m3d/m_smdl.cpp
@@ -0,0 +1,36 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_smdl.h>
+#include <nw4r/g3d/g3d_scnmdl.h>
+#include <nw4r/g3d/g3d_scnmdlsmpl.h>
+
+
+namespace m3d {
+
+smdl_c::smdl_c() {}
+smdl_c::~smdl_c() {}
+
+bool smdl_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 tmp;
+    if (pSize == nullptr) {
+        pSize = &tmp;
+    }
+
+    if (bufferOption != 0) {
+        mpScnLeaf = nw4r::g3d::ScnMdl::Construct(alloc, pSize, mdl, bufferOption, nView);
+    } else {
+        mpScnLeaf = nw4r::g3d::ScnMdlSimple::Construct(alloc, pSize, mdl, nView);
+    }
+
+    if (mpScnLeaf == nullptr) {
+        return false;
+    }
+    mpScnLeaf->SetPriorityDrawOpa(0x7f);
+    mpScnLeaf->SetPriorityDrawXlu(0x7f);
+    return true;
+}
+
+} // namespace m3d

From aa372b1b766785940f34c9603f7a340e7b6b8ae0 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Thu, 20 Jun 2024 23:19:44 +0200
Subject: [PATCH 02/33] d_a_obj_ring OK

---
 config/SOUE01/rels/d_a_obj_ringNP/splits.txt  |   3 +
 config/SOUE01/rels/d_a_obj_ringNP/symbols.txt | 100 +++++++++---------
 config/SOUE01/symbols.txt                     |  24 ++---
 configure.py                                  |   2 +-
 include/d/a/obj/d_a_obj_base.h                |  42 ++++++--
 include/d/a/obj/d_a_obj_ring.h                |  38 +++++++
 include/m/m_mtx.h                             |   1 +
 include/nw4r/g3d/g3d_resfile.h                |   2 +-
 src/REL/d/a/obj/d_a_obj_ring.cpp              |  76 +++++++++++++
 9 files changed, 219 insertions(+), 69 deletions(-)
 create mode 100644 include/d/a/obj/d_a_obj_ring.h

diff --git a/config/SOUE01/rels/d_a_obj_ringNP/splits.txt b/config/SOUE01/rels/d_a_obj_ringNP/splits.txt
index 89723ded..5f059099 100644
--- a/config/SOUE01/rels/d_a_obj_ringNP/splits.txt
+++ b/config/SOUE01/rels/d_a_obj_ringNP/splits.txt
@@ -17,3 +17,6 @@ REL/global_destructor_chain.c:
 REL/d/a/obj/d_a_obj_ring.cpp:
 	.text       start:0x000000F0 end:0x00000B98
 	.ctors      start:0x00000000 end:0x00000004
+	.rodata     start:0x00000000 end:0x00000010
+	.data       start:0x00000000 end:0x000001CC
+	.bss        start:0x00000008 end:0x00000048
diff --git a/config/SOUE01/rels/d_a_obj_ringNP/symbols.txt b/config/SOUE01/rels/d_a_obj_ringNP/symbols.txt
index 90c1c535..a0c313bb 100644
--- a/config/SOUE01/rels/d_a_obj_ringNP/symbols.txt
+++ b/config/SOUE01/rels/d_a_obj_ringNP/symbols.txt
@@ -3,56 +3,58 @@ _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
 __register_global_object = .text:0x00000070; // type:function size:0x1C scope:global
 __destroy_global_chain = .text:0x00000090; // type:function size:0x54 scope:global
-fn_193_F0 = .text:0x000000F0; // type:function size:0xB4
-fn_193_1B0 = .text:0x000001B0; // type:function size:0x58
-fn_193_210 = .text:0x00000210; // type:function size:0x6C
-fn_193_280 = .text:0x00000280; // type:function size:0xA0
-fn_193_320 = .text:0x00000320; // type:function size:0xA4
-fn_193_3D0 = .text:0x000003D0; // type:function size:0x7C
-fn_193_450 = .text:0x00000450; // type:function size:0x114
-fn_193_570 = .text:0x00000570; // type:function size:0x10
-fn_193_580 = .text:0x00000580; // type:function size:0x8
-fn_193_590 = .text:0x00000590; // type:function size:0xA4
-fn_193_640 = .text:0x00000640; // type:function size:0x10
-fn_193_650 = .text:0x00000650; // type:function size:0x28
-fn_193_680 = .text:0x00000680; // type:function size:0x4
-fn_193_690 = .text:0x00000690; // type:function size:0x58
-fn_193_6E8 = .text:0x000006E8; // type:function size:0x4
-fn_193_6F0 = .text:0x000006F0; // type:function size:0x4
-fn_193_700 = .text:0x00000700; // type:function size:0xCC
-fn_193_7D0 = .text:0x000007D0; // type:function size:0x10
-fn_193_7E0 = .text:0x000007E0; // type:function size:0x60
-fn_193_840 = .text:0x00000840; // type:function size:0xC
-fn_193_850 = .text:0x00000850; // type:function size:0x1C
-fn_193_870 = .text:0x00000870; // type:function size:0x1C
-fn_193_890 = .text:0x00000890; // type:function size:0x1C
-fn_193_8B0 = .text:0x000008B0; // type:function size:0x10
-fn_193_8C0 = .text:0x000008C0; // type:function size:0x10
-fn_193_8D0 = .text:0x000008D0; // type:function size:0x10
-fn_193_8E0 = .text:0x000008E0; // type:function size:0x10
-fn_193_8F0 = .text:0x000008F0; // type:function size:0x10
-fn_193_900 = .text:0x00000900; // type:function size:0x10
-fn_193_910 = .text:0x00000910; // type:function size:0x30
-fn_193_940 = .text:0x00000940; // type:function size:0x30
-fn_193_970 = .text:0x00000970; // type:function size:0x30
-fn_193_9A0 = .text:0x000009A0; // type:function size:0x10C
-fn_193_AB0 = .text:0x00000AB0; // type:function size:0x58
-fn_193_B10 = .text:0x00000B10; // type:function size:0x88
+dAcOring_c_classInit__Fv = .text:0x000000F0; // type:function size:0xB4
+__dt__23sFState_c<10dAcOring_c>Fv = .text:0x000001B0; // type:function size:0x58
+__dt__26sFStateFct_c<10dAcOring_c>Fv = .text:0x00000210; // type:function size:0x6C
+__dt__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000280; // type:function size:0xA0
+__dt__49sFStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c>Fv = .text:0x00000320; // type:function size:0xA4
+createHeap__10dAcOring_cFv = .text:0x000003D0; // type:function size:0x7C
+create__10dAcOring_cFv = .text:0x00000450; // type:function size:0x114
+changeState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>FRC12sStateIDIf_c = .text:0x00000570; // type:function size:0x10
+doDelete__10dAcOring_cFv = .text:0x00000580; // type:function size:0x8
+actorExecute__10dAcOring_cFv = .text:0x00000590; // type:function size:0xA4
+executeState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000640; // type:function size:0x10
+draw__10dAcOring_cFv = .text:0x00000650; // type:function size:0x28
+initializeState_Move__10dAcOring_cFv = .text:0x00000680; // type:function size:0x4
+executeState_Move__10dAcOring_cFv = .text:0x00000690; // type:function size:0x5C
+finalizeState_Move__10dAcOring_cFv = .text:0x000006F0; // type:function size:0x4
+__dt__10dAcOring_cFv = .text:0x00000700; // type:function size:0xCC
+getStateID__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000007D0; // type:function size:0x10
+build__26sFStateFct_c<10dAcOring_c>FRC12sStateIDIf_c = .text:0x000007E0; // type:function size:0x60
+dispose__26sFStateFct_c<10dAcOring_c>FRP10sStateIf_c = .text:0x00000840; // type:function size:0xC
+initialize__23sFState_c<10dAcOring_c>Fv = .text:0x00000850; // type:function size:0x1C
+execute__23sFState_c<10dAcOring_c>Fv = .text:0x00000870; // type:function size:0x1C
+finalize__23sFState_c<10dAcOring_c>Fv = .text:0x00000890; // type:function size:0x1C
+initializeState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008B0; // type:function size:0x10
+finalizeState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008C0; // type:function size:0x10
+refreshState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008D0; // type:function size:0x10
+getState__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000008E0; // type:function size:0x10
+getNewStateID__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000008F0; // type:function size:0x10
+getOldStateID__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000900; // type:function size:0x10
+finalizeState__25sFStateID_c<10dAcOring_c>CFR10dAcOring_c = .text:0x00000910; // type:function size:0x30
+executeState__25sFStateID_c<10dAcOring_c>CFR10dAcOring_c = .text:0x00000940; // type:function size:0x30
+initializeState__25sFStateID_c<10dAcOring_c>CFR10dAcOring_c = .text:0x00000970; // type:function size:0x30
+__sinit_\d_a_obj_ring_cpp = .text:0x000009A0; // type:function size:0x10C scope:local
+__dt__25sFStateID_c<10dAcOring_c>Fv = .text:0x00000AB0; // type:function size:0x58
+isSameName__25sFStateID_c<10dAcOring_c>CFPCc = .text:0x00000B10; // type:function size:0x88
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
 __destroy_global_chain_reference = .dtors:0x00000000; // type:object size:0x4 scope:global
-lbl_193_rodata_0 = .rodata:0x00000000; // type:object size:0x10 data:float
-lbl_193_data_0 = .data:0x00000000; // type:object size:0x10 data:4byte
-lbl_193_data_10 = .data:0x00000010; // type:object size:0x8
-lbl_193_data_18 = .data:0x00000018; // type:object size:0x10
-lbl_193_data_28 = .data:0x00000028; // type:object size:0x18 data:string
-lbl_193_data_40 = .data:0x00000040; // type:object size:0x8 data:4byte
-lbl_193_data_48 = .data:0x00000048; // type:object size:0x80
-lbl_193_data_C8 = .data:0x000000C8; // type:object size:0x30
-lbl_193_data_F8 = .data:0x000000F8; // type:object size:0x30
-lbl_193_data_128 = .data:0x00000128; // type:object size:0x18
-lbl_193_data_140 = .data:0x00000140; // type:object size:0x58
-lbl_193_data_198 = .data:0x00000198; // type:object size:0x34
+lbl_193_rodata_0 = .rodata:0x00000000; // type:object size:0x4 scope:local data:float
+lbl_193_rodata_4 = .rodata:0x00000004; // type:object size:0x4 scope:local data:float
+lbl_193_rodata_8 = .rodata:0x00000008; // type:object size:0x4 scope:local data:float
+lbl_193_rodata_C = .rodata:0x0000000C; // type:object size:0x4 scope:local data:float
+g_profile_OBJ_RING = .data:0x00000000; // type:object size:0x10 data:4byte
+lbl_193_data_10 = .data:0x00000010; // type:object size:0x8 scope:local
+lbl_193_data_18 = .data:0x00000018; // type:object size:0x10 scope:local
+lbl_193_data_28 = .data:0x00000028; // type:object size:0x18 scope:local data:string
+ROT_PER_TICK__10dAcOring_c = .data:0x00000040; // type:object size:0x4 data:4byte
+__vt__10dAcOring_c = .data:0x00000048; // type:object size:0x80
+__vt__49sFStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c> = .data:0x000000C8; // type:object size:0x30
+__vt__79sStateMgr_c<10dAcOring_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c> = .data:0x000000F8; // type:object size:0x30
+__vt__26sFStateFct_c<10dAcOring_c> = .data:0x00000128; // type:object size:0x14
+__vt__23sFState_c<10dAcOring_c> = .data:0x00000140; // type:object size:0x18
+__vt__25sFStateID_c<10dAcOring_c> = .data:0x00000198; // type:object size:0x34
 __global_destructor_chain = .bss:0x00000000; // type:object size:0x4 scope:global
-lbl_193_bss_8 = .bss:0x00000008; // type:object size:0x10
-lbl_193_bss_18 = .bss:0x00000018; // type:object size:0x30 data:4byte
+lbl_193_bss_8 = .bss:0x00000008; // type:object size:0x10 scope:local
+StateID_Move__10dAcOring_c = .bss:0x00000018; // type:object size:0x30 data:4byte
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 7f71ec83..f9350d3d 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -1105,35 +1105,35 @@ FUN_8002ddd0__9dAcBase_cFv = .text:0x8002DDD0; // type:function size:0x54
 FUN_8002de30__9dAcBase_cFv = .text:0x8002DE30; // type:function size:0x4
 fn_8002DE40 = .text:0x8002DE40; // type:function size:0x14
 fn_8002DE60 = .text:0x8002DE60; // type:function size:0x14
-fn_8002DE80 = .text:0x8002DE80; // type:function size:0xC
+getOarcResFile__12dAcObjBase_cFPc = .text:0x8002DE80; // type:function size:0xC
 fn_8002DE90 = .text:0x8002DE90; // type:function size:0xC
 fn_8002DEA0 = .text:0x8002DEA0; // type:function size:0xC
 fn_8002DEB0 = .text:0x8002DEB0; // type:function size:0x14
 fn_8002DED0 = .text:0x8002DED0; // type:function size:0x14
 __ct__12dAcObjBase_cFv = .text:0x8002DEF0; // type:function size:0x158
 __dt__12dAcObjBase_cFv = .text:0x8002E050; // type:function size:0x84
-fn_8002E0E0 = .text:0x8002E0E0; // type:function size:0x70
-fn_8002E150 = .text:0x8002E150; // type:function size:0x148
-fn_8002E2A0 = .text:0x8002E2A0; // type:function size:0x90
-fn_8002E330 = .text:0x8002E330; // type:function size:0x27C
-fn_8002E5B0 = .text:0x8002E5B0; // type:function size:0x8
-fn_8002E5C0 = .text:0x8002E5C0; // type:function size:0x70
+preCreate__12dAcObjBase_cFv = .text:0x8002E0E0; // type:function size:0x70
+preExecute__12dAcObjBase_cFv = .text:0x8002E150; // type:function size:0x148
+postExecute__12dAcObjBase_cFQ27fBase_c12MAIN_STATE_e = .text:0x8002E2A0; // type:function size:0x90
+preDraw__12dAcObjBase_cFv = .text:0x8002E330; // type:function size:0x27C
+getObjectListEntry__12dAcObjBase_cFv = .text:0x8002E5B0; // type:function size:0x8
+restorePosRotFromCopy__12dAcObjBase_cFv = .text:0x8002E5C0; // type:function size:0x70
 create__12dAcObjBase_cFQ28fProfile14PROFILE_NAME_eScUlP7mVec3_cP7mAng3_cP7mVec3_cUl = .text:0x8002E630; // type:function size:0x98
 fn_8002E6D0 = .text:0x8002E6D0; // type:function size:0xA0
 fn_8002E770 = .text:0x8002E770; // type:function size:0x7C
 fn_8002E7F0 = .text:0x8002E7F0; // type:function size:0x58
 fn_8002E850 = .text:0x8002E850; // type:function size:0x14
 fn_8002E870 = .text:0x8002E870; // type:function size:0x68
-fn_8002E8E0 = .text:0x8002E8E0; // type:function size:0xB8
-fn_8002E9A0 = .text:0x8002E9A0; // type:function size:0x58
+calcVelocity__12dAcObjBase_cFv = .text:0x8002E8E0; // type:function size:0xB8
+updateMatrix__12dAcObjBase_cFv = .text:0x8002E9A0; // type:function size:0x58
 fn_8002EA00 = .text:0x8002EA00; // type:function size:0xC
-fn_8002EA10 = .text:0x8002EA10; // type:function size:0x8
-fn_8002EA20 = .text:0x8002EA20; // type:function size:0x8
+canBeLinkedToWoodTag__12dAcObjBase_cFv = .text:0x8002EA10; // type:function size:0x8
+drop__12dAcObjBase_cFv = .text:0x8002EA20; // type:function size:0x8
 fn_8002EA30 = .text:0x8002EA30; // type:function size:0x24
 fn_8002EA60 = .text:0x8002EA60; // type:function size:0xD0
 fn_8002EB30 = .text:0x8002EB30; // type:function size:0x5C
 fn_8002EB90 = .text:0x8002EB90; // type:function size:0xD8
-fn_8002EC70 = .text:0x8002EC70; // type:function size:0x60
+drawModelType1__12dAcObjBase_cFPQ23m3d6smdl_c = .text:0x8002EC70; // type:function size:0x60
 fn_8002ECD0 = .text:0x8002ECD0; // type:function size:0x50
 fn_8002ED20 = .text:0x8002ED20; // type:function size:0x88
 fn_8002EDB0 = .text:0x8002EDB0; // type:function size:0x8C
diff --git a/configure.py b/configure.py
index fa38ddf7..cd9894ab 100644
--- a/configure.py
+++ b/configure.py
@@ -906,7 +906,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_obj_rail_end", "REL/d/a/obj/d_a_obj_rail_end.cpp"),
     Rel(NonMatching, "d_a_obj_rail_post", "REL/d/a/obj/d_a_obj_rail_post.cpp"),
     Rel(NonMatching, "d_a_obj_ride_rock", "REL/d/a/obj/d_a_obj_ride_rock.cpp"),
-    Rel(NonMatching, "d_a_obj_ring", "REL/d/a/obj/d_a_obj_ring.cpp"),
+    Rel(Matching, "d_a_obj_ring", "REL/d/a/obj/d_a_obj_ring.cpp"),
     Rel(NonMatching, "d_a_obj_rock_boat", "REL/d/a/obj/d_a_obj_rock_boat.cpp"),
     Rel(NonMatching, "d_a_obj_rock_dragon", "REL/d/a/obj/d_a_obj_rock_dragon.cpp"),
     Rel(NonMatching, "d_a_obj_rock_sky", "REL/d/a/obj/d_a_obj_rock_sky.cpp"),
diff --git a/include/d/a/obj/d_a_obj_base.h b/include/d/a/obj/d_a_obj_base.h
index d4a30b6a..a18cfd23 100644
--- a/include/d/a/obj/d_a_obj_base.h
+++ b/include/d/a/obj/d_a_obj_base.h
@@ -4,7 +4,25 @@
 #include "d/a/d_a_base.h"
 #include "m/m_mtx.h"
 #include "m/types_m.h"
+#include "nw4r/g3d/g3d_resfile.h"
 
+// Size: 0xA8
+struct ActorCarryStruct {
+    /* 0x00 */ fLiNdBa_c actorLink;
+    /* 0x0C */ fLiNdBa_c *carriedActor;
+    /* 0x10 */ u32 carryFlags;
+    /* 0x14 */ int carryType;
+    /* 0x18 */ u16 field_0x18;
+    /* 0x1A */ u16 field_0x1A;
+    /* 0x1C */ mVec3_c field_0x1C;
+    /* 0x28 */ mMtx_c carryTransMtx;
+    /* 0x58 */ mMtx_c field_0x58;
+    /* 0x88 */ s32 isCarried;
+    /* 0x8C */ u8 field_0x8C[0x10]; // mQuat_c
+    /* 0x9C */ void *dtor; // ???
+    /* 0xA0 */ u32 field_0xA0;
+    /* 0xA4 */ u32 field_0xA4;
+};
 
 // Ghidra: ActorObjectBase
 //   size: 0x330
@@ -26,19 +44,25 @@ class dAcObjBase_c : public dAcBase_c {
     f32 forwardSpeed;
     f32 forwardAccel;
     f32 forwardMaxSpeed;
-    // TODO: add the rest
-    u8 unk1[0x15C - 0x150];
+    mVec3_c velocity;
     /* 0x15C */ mMtx_c worldMatrix;
-
     nw4r::math::AABB boundingBox;
 
-    u8 unk_0x1A4[0x330 - 0x1A4];
+    u8 unk_0x1A4[0x1E8 - 0x1A4];
+
+    /* 0x1E8 */ mVec3_c posIncrements;
+
+    u8 unk_0x1F4[0x210 - 0x1F4];
+
+    /* 0x210 */ ActorCarryStruct mActorCarryInfo;
+
+    u8 unk_0x1EC[0x330 - 0x2B8];
 
 public:
     // could be their own thing?
     /* 8002de40 */ static void *getOarcFile(char *oarcName, char *fileName);
     /* 8002de60 */ static void *getOarcSubEntry(char *oarcName, char *fileName);
-    /* 8002de80 */ static void *getOarcResFile(char *oarcName);
+    /* 8002de80 */ static nw4r::g3d::ResFile getOarcResFile(char *oarcName);
     /* 8002de90 */ static void *getOarcModelFile(char *oarcName);
     /* 8002dea0 */ static void *getOarcZev(char *oarcName);
     /* 8002deb0 */ static void *getOarcDZB(char *dzbName);
@@ -74,7 +98,7 @@ class dAcObjBase_c : public dAcBase_c {
     /* 8002eb30 */ void fn_8002EB30(void *);
     /* 8002eb90 */ void putInODesert(f32 depth, mVec3_c *position);
     // Disabling makes Items and Link Disappear
-    /* 8002ec70 */ void drawModelType1();
+    /* 8002ec70 */ void drawModelType1(m3d::smdl_c *smdl);
     // Disabling make Lava and other objects Disappear
     /* 8002ecd0 */ void drawModel2();
     /* 8002ed20 */ void fn_8002ed20();
@@ -93,6 +117,12 @@ class dAcObjBase_c : public dAcBase_c {
             mVec3_c *pos, mAng3_c *rot, mVec3_c *scale, u32 params2);
     /* 8002f260 */ static dAcBase_c *createActorUnkGroup3(char *name, u32 roomId, u32 params1, mVec3_c *pos,
             mAng3_c *rot, mVec3_c *scale, u32 params2, u16 id, u8 viewclipId);
+
+protected:
+    inline void setBoundingBox(mVec3_c min, mVec3_c max) {
+        boundingBox.min = min;
+        boundingBox.max = max;
+    }
 };
 
 #endif
diff --git a/include/d/a/obj/d_a_obj_ring.h b/include/d/a/obj/d_a_obj_ring.h
new file mode 100644
index 00000000..fcc63e31
--- /dev/null
+++ b/include/d/a/obj/d_a_obj_ring.h
@@ -0,0 +1,38 @@
+#ifndef D_A_OBJ_RING
+#define D_A_OBJ_RING
+
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_smdl.h>
+#include <s/s_State.hpp>
+#include <s/s_StateMgr.hpp>
+
+class dAcOring_c : public dAcObjBase_c {
+public:
+    dAcOring_c() : mStateMgr(*this, sStateID::null) {}
+    virtual ~dAcOring_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+    STATE_FUNC_DECLARE(dAcOring_c, Move);
+
+    u8 getArgFromParams() {
+        u32 p = (params & 3);
+        return p != 3 ? p : 0;
+    }
+
+private:
+
+    static u32 ROT_PER_TICK;
+
+    m3d::smdl_c mModel;
+    STATE_MGR_DECLARE(dAcOring_c);
+    u8 field_0x388;
+    f32 field_0x38C;
+    
+
+};
+
+#endif
diff --git a/include/m/m_mtx.h b/include/m/m_mtx.h
index ee60b72d..8158e9ac 100644
--- a/include/m/m_mtx.h
+++ b/include/m/m_mtx.h
@@ -42,6 +42,7 @@ class mMtx_c {
     union {
         EGG::Matrix34f mat;
         f32 m[3][4];
+        nw4r::math::MTX34 nw4rm;
         struct {
             f32 xx, xy, xz, xw;
             f32 yx, yy, yz, yw;
diff --git a/include/nw4r/g3d/g3d_resfile.h b/include/nw4r/g3d/g3d_resfile.h
index 37f9d0c8..c5dc7fea 100644
--- a/include/nw4r/g3d/g3d_resfile.h
+++ b/include/nw4r/g3d/g3d_resfile.h
@@ -3,7 +3,7 @@
 #include "common.h"
 #include "nw4r/g3d/g3d_rescommon.h"
 #include "nw4r/g3d/g3d_resdict.h"
-#include "ut_binaryFileFormat.h"
+#include "nw4r/ut/ut_binaryFileFormat.h"
 
 namespace nw4r {
 namespace g3d {
diff --git a/src/REL/d/a/obj/d_a_obj_ring.cpp b/src/REL/d/a/obj/d_a_obj_ring.cpp
index e69de29b..d489a287 100644
--- a/src/REL/d/a/obj/d_a_obj_ring.cpp
+++ b/src/REL/d/a/obj/d_a_obj_ring.cpp
@@ -0,0 +1,76 @@
+#include <d/a/obj/d_a_obj_ring.h>
+#include <d/d_player.h>
+
+SPECIAL_ACTOR_PROFILE(OBJ_RING, dAcOring_c, fProfile::OBJ_RING, 0x00f2, 0, 0x103);
+
+STATE_DEFINE(dAcOring_c, Move);
+
+bool dAcOring_c::createHeap() {
+    nw4r::g3d::ResFile f = getOarcResFile("PRing");
+    nw4r::g3d::ResMdl mdl = f.GetResMdl("PeehatRing");
+    // This matches but in a really weird way. Maybe an inline function?
+    bool success = mModel.create(mdl, &heap_allocator, 0x20, 1, nullptr);
+    bool rc = true;
+    if (success == false) {
+        rc = success;
+    }
+    return rc;
+}
+
+int dAcOring_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcOring_c::m_allocator", 0x20)) {
+        return 2;
+    }
+
+    field_0x388 = getArgFromParams();
+
+    setBoundingBox(mVec3_c(-100.0f, -100.0f, -100.0f), mVec3_c(100.0f, 100.0f, 100.0f));
+    updateMatrix();
+    mModel.setScale(scale);
+    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    forwardAccel = -5.0f;
+    forwardMaxSpeed = -40.0f;
+    field_0x38C = dPlayer::LINK->position.y;
+    mStateMgr.changeState(StateID_Move);
+    return 1;
+}
+
+int dAcOring_c::doDelete() {
+    return 1;
+}
+
+int dAcOring_c::actorExecute() {
+    mStateMgr.executeState();
+    calcVelocity();
+    position += velocity;
+    position += posIncrements;
+    updateMatrix();
+    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    return 1;
+}
+
+int dAcOring_c::draw() {
+    drawModelType1(&mModel);
+    return 1;
+}
+
+void dAcOring_c::initializeState_Move() {}
+
+u32 dAcOring_c::ROT_PER_TICK = 0x1000;
+
+void dAcOring_c::executeState_Move() {
+    bool isCarried = false;
+    if (mActorCarryInfo.isCarried == 1 && mActorCarryInfo.carryType == 7) {
+        isCarried = true;
+    }
+
+    if (isCarried) {
+        return;
+    }
+    rotation.x.mVal += ROT_PER_TICK;
+    if (field_0x38C >= position.y) {
+        deleteRequest();
+    }
+}
+
+void dAcOring_c::finalizeState_Move() {}

From f0e89143ff63ae2c5585e6247df11851e0918dfc Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Thu, 20 Jun 2024 23:43:07 +0200
Subject: [PATCH 03/33] m3d::calcRatio_c

---
 config/SOUE01/symbols.txt  |  2 +-
 configure.py               |  2 +-
 src/m/m3d/m_calc_ratio.cpp | 22 ++++++++++------------
 tools/rel_sieve.py         |  3 ---
 4 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index f9350d3d..f0781a37 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -35701,7 +35701,7 @@ __vt__Q23m3d6banm_c = .data:0x80542610; // type:object size:0x18
 lbl_80542628 = .data:0x80542628; // type:object size:0x30
 lbl_80542658 = .data:0x80542658; // type:object size:0x10
 __vt__Q23m3d6bmdl_c = .data:0x80542668; // type:object size:0x30
-__vt__Q23m3d11calcRatio_c = .data:0x80542698; // type:object size:0x10
+__vt__Q23m3d11calcRatio_c = .data:0x80542698; // type:object size:0xC
 __vt__Q23m3d6fanm_c = .data:0x805426A8; // type:object size:0x18
 __vt__Q23m3d5mdl_c = .data:0x805426C0; // type:object size:0x2C
 __vt__Q33m3d5mdl_c13mdlCallback_c = .data:0x805426F0; // type:object size:0x18
diff --git a/configure.py b/configure.py
index cd9894ab..b0b47667 100644
--- a/configure.py
+++ b/configure.py
@@ -340,7 +340,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
-            Object(NonMatching, "m/m3d/m_calc_ratio.cpp"),
+            Object(Matching, "m/m3d/m_calc_ratio.cpp"),
             Object(NonMatching, "m/m3d/m_fanm.cpp"),
             Object(NonMatching, "m/m3d/m_mdl.cpp"),
             Object(NonMatching, "m/m3d/m_scnLeaf.cpp"),
diff --git a/src/m/m3d/m_calc_ratio.cpp b/src/m/m3d/m_calc_ratio.cpp
index 351e3444..f4ae81f6 100644
--- a/src/m/m3d/m_calc_ratio.cpp
+++ b/src/m/m3d/m_calc_ratio.cpp
@@ -1,4 +1,5 @@
 #include <m/m3d/m_calc_ratio.h>
+#include <nw4r/math/math_arithmetic.h>
 
 namespace m3d {
 
@@ -45,26 +46,23 @@ void calcRatio_c::set(f32 value) {
 }
 
 void calcRatio_c::calc() {
-    f32 max = 1.0f;
-    f32 start = mf1;
-    if (start == 0.0f) {
+    if (mf1 == 0.0f) {
         return;
     }
 
     mf2 += mf3;
     if (mf2 >= 1.0f) {
         reset();
-        return;
     } else {
         mb2 = 1;
-        mf1 = start - start * mf2 * mf2;
-        max = 1.0f - mf1;
-        // TODO here be ASM
-        f32 f4 = mf1 / start + max;
-        f32 f1 = __fres(f4);
-        f4 = -(f4 * f1 * f1 - (f1 + f1));
-        mf4 = (mf1 / start) * f4;
-        mf5 = max * f4;
+        f32 start = mf1;
+        f32 tmp = start - start * mf2 * mf2;
+        mf1 = tmp;
+        f32 max = 1.0f - mf1;
+        tmp = mf1 / start;
+        f32 inv = nw4r::math::FInv(tmp + max);
+        mf4 = tmp * inv;
+        mf5 = max * inv;
     }
 }
 
diff --git a/tools/rel_sieve.py b/tools/rel_sieve.py
index e8b160bd..dcb0e425 100644
--- a/tools/rel_sieve.py
+++ b/tools/rel_sieve.py
@@ -32,14 +32,11 @@
     ['fn_80353FF0', 'UnkCollider::init'],
     ['fn_800C3EC0', 'ActorEventFlowManagerRelated::checkEventFinished'],
     ['fn_802E6000', 'mdlAnmChr_c::create'],
-    ['fn_802EB6F0', 'm3d::mdl_c::mdl_c'],
-    ['fn_802EDF30', 'm3d::smdl_c::smdl_c'],
     ['fn_800275C0', 'EffectsStruct::ctor'],
     ['fn_80027610', 'EffectsStruct::ctor'],
     ['fn_803465D0', 'ActorCollision::ctor'],
     ['fn_80341A70', 'checkCollision'],
     ['fn_80016C10', 'AnimModelWrapper::ctor'],
-    ['fn_802EBBD0', 'm3d::scnLeaf_c::scnLeaf_c'],
     ['fn_8009C910', 'ActorEventRelated::ctor'],
     ['fn_802E32B0', 'm2d::FrameCtrl_c::setFrame'],
     ['fn_802F04A0', 'mFader_c::draw'],

From c0b3821b5b3454ceec908f7a54696a3cb688f7bc Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 00:33:26 +0200
Subject: [PATCH 04/33] m_mdl cleanup

---
 config/SOUE01/splits.txt         |  1 +
 config/SOUE01/symbols.txt        |  4 +-
 include/m/m3d/m_mdl.h            | 35 +++++++++++++++--
 include/nw4r/g3d/g3d_calcworld.h |  3 ++
 src/m/m3d/m_mdl.cpp              | 66 +++++++++++++++++++++++---------
 5 files changed, 85 insertions(+), 24 deletions(-)

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index b2e71a12..db2bdf5c 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -301,6 +301,7 @@ m/m3d/m_fanm.cpp:
 m/m3d/m_mdl.cpp:
 	.text       start:0x802EAF80 end:0x802EBB5C
 	.data       start:0x805426C0 end:0x80542708
+	.sdata2     start:0x8057CD60 end:0x8057CD68
 
 m/m3d/m_scnLeaf.cpp:
 	.text       start:0x802EBBD0 end:0x802EBFF8
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index f0781a37..81bc7c45 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17628,7 +17628,7 @@ remove__Q23m3d5mdl_cFv = .text:0x802EB9F0; // type:function size:0x8C
 setAnm__Q23m3d5mdl_cFRQ23m3d6banm_c = .text:0x802EBA80; // type:function size:0x8
 play__Q23m3d5mdl_cFv = .text:0x802EBA90; // type:function size:0x34
 setAnm__Q23m3d5mdl_cFRQ23m3d6banm_cf = .text:0x802EBAD0; // type:function size:0x74
-setCallback__Q23m3d5mdl_cFPQ33m3d5mdl_c10callback_c = .text:0x802EBB50; // type:function size:0xC
+setCallback__Q23m3d5mdl_cFPQ23m3d10callback_c = .text:0x802EBB50; // type:function size:0xC
 fn_802EBB60 = .text:0x802EBB60; // type:function size:0x40
 fn_802EBBA0 = .text:0x802EBBA0; // type:function size:0x4
 fn_802EBBB0 = .text:0x802EBBB0; // type:function size:0x4
@@ -24935,7 +24935,7 @@ fn_8045CAD0 = .text:0x8045CAD0; // type:function size:0xE0
 fn_8045CBB0 = .text:0x8045CBB0; // type:function size:0x5C
 fn_8045CC10 = .text:0x8045CC10; // type:function size:0x5C
 fn_8045CC70 = .text:0x8045CC70; // type:function size:0xB8
-fn_8045CD30 = .text:0x8045CD30; // type:function size:0x40
+EnableScnObjCallbackTiming__Q34nw4r3g3d6ScnObjFQ44nw4r3g3d6ScnObj6Timing = .text:0x8045CD30; // type:function size:0x40
 fn_8045CD70 = .text:0x8045CD70; // type:function size:0x24
 fn_8045CDA0 = .text:0x8045CDA0; // type:function size:0x34
 fn_8045CDE0 = .text:0x8045CDE0; // type:function size:0x34
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
index f3f6c4af..d8abb1aa 100644
--- a/include/m/m3d/m_mdl.h
+++ b/include/m/m3d/m_mdl.h
@@ -4,11 +4,32 @@
 #include <m/m3d/m_banm.h>
 #include <m/m3d/m_calc_ratio.h>
 #include <m/m3d/m_smdl.h>
+#include <nw4r/g3d/g3d_resmdl.h>
+#include <nw4r/g3d/g3d_calcworld.h>
 
 class UnkClass3 {};
 
 namespace m3d {
 
+class callback_c {
+public:
+    virtual ~callback_c() {}
+    virtual void timingA(u32, nw4r::g3d::ChrAnmResult *, nw4r::g3d::ResMdl) {}
+    virtual void timingB(u32, nw4r::g3d::WorldMtxManip *, nw4r::g3d::ResMdl) {}
+    virtual void timingC(nw4r::math::MTX34*, nw4r::g3d::ResMdl) {}
+};
+
+struct UnkNode {
+    UNKWORD field_0x00;
+    f32 field_0x04;
+    f32 field_0x08;
+    f32 field_0x0C;
+    UNKWORD field_0x10;
+    UNKWORD field_0x14;
+    UNKWORD field_0x18;
+    nw4r::math::MTX34 mtx;
+};
+
 class mdl_c : smdl_c {
     class mdlCallback_c : public nw4r::g3d::IScnObjCallback {
     public:
@@ -30,11 +51,15 @@ class mdl_c : smdl_c {
             return mpAlloc;
         }
 
+        void setBaseCallback(callback_c *cb) {
+            mpBaseCallback = cb;
+        }
+
     private:
         calcRatio_c mCalcRatio;
-        u32 mNumNode;
-        void *mpNodes;
-        u32 mCallbackNum;
+        int mNumNode;
+        UnkNode *mpNodes;
+        callback_c *mpBaseCallback;
         mAllocator_c *mpAlloc;
     };
 
@@ -47,13 +72,15 @@ class mdl_c : smdl_c {
 
     virtual void remove();
 
-    void setCallback(mdlCallback_c *cb);
+    void setCallback(callback_c *cb);
     void play();
     void setAnm(banm_c &);
     void setAnm(banm_c &, f32);
 
 private:
+    /** If we allocated the callback ourselves, this is what we need to free */
     mdlCallback_c *mpOwnedCallback;
+    /** The actual callback to use */
     mdlCallback_c *mpCallback;
 };
 
diff --git a/include/nw4r/g3d/g3d_calcworld.h b/include/nw4r/g3d/g3d_calcworld.h
index ba2825df..0fa9e6d6 100644
--- a/include/nw4r/g3d/g3d_calcworld.h
+++ b/include/nw4r/g3d/g3d_calcworld.h
@@ -5,6 +5,9 @@
 
 namespace nw4r {
 namespace g3d {
+
+class WorldMtxManip {};
+
 void CalcWorld(math::MTX34 *, u32 *, const u8 *, const math::MTX34 *, ResMdl, AnmObjChr *, FuncObjCalcWorld *, u32);
 
 void CalcWorld(math::MTX34 *, u32 *, const u8 *, const math::MTX34 *, ResMdl, AnmObjChr *, FuncObjCalcWorld *);
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index 3c643131..65581c81 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -7,12 +7,42 @@ namespace m3d {
 mdl_c::mdlCallback_c::mdlCallback_c() {
     mNumNode = 0;
     mpNodes = nullptr;
-    mCallbackNum = 0;
+    mpBaseCallback = 0;
     mpAlloc = nullptr;
 }
 mdl_c::mdlCallback_c::~mdlCallback_c() {}
 
-bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 *pSize) {}
+bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+    0.0f;
+
+    u32 size = 0;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    mNumNode = mdl.GetResNodeNumEntries();
+    size_t bufSize = mNumNode * sizeof(UnkNode);
+    mpNodes = (UnkNode*)MEMAllocFromAllocator(alloc, bufSize);
+    if (mpNodes == nullptr) {
+        return false;
+    }
+
+    *pSize = ROUND_UP(bufSize + ROUND_UP(*pSize, 0x04), 0x04);
+    UnkNode *node = mpNodes;
+    for (int i = 0; i < mNumNode; i++) {
+        node->field_0x04 = 1.0f;
+        node->field_0x08 = 1.0f;
+        node->field_0x0C = 1.0f;
+        PSMTXIdentity(node->mtx.m);
+        node++;
+    }
+
+    mpAlloc = alloc;
+    return true;
+}
 
 void mdl_c::mdlCallback_c::remove() {
     mCalcRatio.remove();
@@ -44,7 +74,6 @@ bool mdl_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption,
 
 bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c *alloc, u32 bufferOption, int nView,
         u32 *pSize) {
-    nw4r::g3d::ScnMdlSimple *sMdl;
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -63,37 +92,34 @@ bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c
     if (!smdl_c::create(mdl, alloc, bufferOption, nView, pSize1)) {
         return false;
     }
+
     if (cb == nullptr) {
         // TODO sizeof
-        mpOwnedCallback = (mdlCallback_c *)alloc->alloc(0x30);
+        mpOwnedCallback = (mdlCallback_c *)alloc->alloc(sizeof(mdlCallback_c));
         if (mpOwnedCallback == nullptr) {
             remove();
             return false;
         }
 
         new (mpOwnedCallback) mdlCallback_c();
-        mpCallback = (mdlCallback_c *)mpOwnedCallback;
+        mpCallback = mpOwnedCallback;
     } else {
         mpCallback = cb;
     }
     if (!mpCallback->create(mdl, alloc, pSize2)) {
         remove();
         return false;
-    } else {
-        if (pSize != nullptr) {
-            *pSize = tmp1 + tmp2;
-        }
-
-        sMdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
-        sMdl->SetCallback(mpCallback);
-        sMdl->EnableScnObjCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
-        setCallback(nullptr);
-        return true;
     }
-}
 
-void mdl_c::setCallback(mdlCallback_c *cb) {
-    mpCallback = cb;
+    if (pSize != nullptr) {
+        *pSize = tmp1 + tmp2;
+    }
+    nw4r::g3d::ScnMdlSimple *sMdl;
+    sMdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
+    sMdl->SetCallback(mpCallback);
+    sMdl->EnableScnObjCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
+    setCallback(nullptr);
+    return true;
 }
 
 void mdl_c::remove() {
@@ -124,4 +150,8 @@ void mdl_c::setAnm(m3d::banm_c &anm, f32 f) {
     bmdl_c::setAnm(anm);
 }
 
+void mdl_c::setCallback(callback_c *cb) {
+    mpCallback->setBaseCallback(cb);
+}
+
 } // namespace m3d

From 669c42850aca9cda6eb80e7316b4412a105893b1 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 14:31:45 +0200
Subject: [PATCH 05/33] m_anmtexsrt

---
 config/SOUE01/splits.txt            |   5 +
 config/SOUE01/symbols.txt           |  22 +--
 configure.py                        |   1 +
 include/m/m3d/m_anmtexsrt.h         |  56 ++++++++
 include/m/m3d/m_fanm.h              |  17 +++
 include/nw4r/g3d/g3d_anmtexsrt.h    |  70 +++++++++
 include/nw4r/g3d/g3d_obj.h          |   5 +-
 include/nw4r/g3d/g3d_resanmtexsrt.h |  14 ++
 src/m/m3d/m_anmtexsrt.cpp           | 214 ++++++++++++++++++++++++++++
 tools/rel_sieve.py                  |   2 +
 10 files changed, 394 insertions(+), 12 deletions(-)
 create mode 100644 include/m/m3d/m_anmtexsrt.h
 create mode 100644 src/m/m3d/m_anmtexsrt.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index db2bdf5c..4058f474 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -278,6 +278,11 @@ m/m3d/m3d.cpp:
 m/m3d/m_anmchr.cpp:
 	.text       start:0x802E48E0 end:0x802E4D98
 
+m/m3d/m_anmtexsrt.cpp:
+	.text       start:0x802E6EC0 end:0x802E7A54
+	.data       start:0x80542598 end:0x805425C8
+	.sdata2     start:0x8057CD08 end:0x8057CD18
+
 m/m3d/m_anmvis.cpp:
 	.text       start:0x802E7A60 end:0x802E7D48
 	.data       start:0x805425C8 end:0x805425E0
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 81bc7c45..940df14d 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17514,8 +17514,8 @@ setRate__Q23m3d11anmTexPat_cFfl = .text:0x802E6E60; // type:function size:0x10
 isStop__Q23m3d11anmTexPat_cCFl = .text:0x802E6E70; // type:function size:0x10
 getFrameMax__Q23m3d11anmTexPat_cCFl = .text:0x802E6E80; // type:function size:0x14
 fn_802E6EA0 = .text:0x802E6EA0; // type:function size:0x14
-fn_802E6EC0 = .text:0x802E6EC0; // type:function size:0x8
-__dt__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E6ED0; // type:function size:0x58
+getType__Q33m3d11anmTexSrt_c7child_cFv = .text:0x802E6EC0; // type:function size:0x8
+__dt__Q33m3d11anmTexSrt_c7child_cFv = .text:0x802E6ED0; // type:function size:0x58
 heapCost__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtb = .text:0x802E6F30; // type:function size:0x7C
 create__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUl = .text:0x802E6FB0; // type:function size:0x12C
 setAnm__Q33m3d11anmTexSrt_c7child_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexSrtQ23m3d10playMode_e = .text:0x802E70E0; // type:function size:0xB4
@@ -17524,7 +17524,7 @@ setFrmCtrlDefault__Q33m3d11anmTexSrt_c7child_cFRQ34nw4r3g3d12ResAnmTexSrtQ23m3d1
 heapCost__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtlb = .text:0x802E7270; // type:function size:0xE0
 create__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUll = .text:0x802E7350; // type:function size:0x27C
 __dt__Q23m3d11anmTexSrt_cFv = .text:0x802E75D0; // type:function size:0x6C
-fn_802E7640 = .text:0x802E7640; // type:function size:0x8
+getType__Q23m3d11anmTexSrt_cFv = .text:0x802E7640; // type:function size:0x8
 remove__Q23m3d11anmTexSrt_cFv = .text:0x802E7650; // type:function size:0xEC
 setAnm__Q23m3d11anmTexSrt_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexSrtlQ23m3d10playMode_e = .text:0x802E7740; // type:function size:0x140
 play__Q23m3d11anmTexSrt_cFv = .text:0x802E7880; // type:function size:0xAC
@@ -17533,11 +17533,11 @@ getFrame__Q23m3d11anmTexSrt_cCFl = .text:0x802E7990; // type:function size:0x10
 setFrame__Q23m3d11anmTexSrt_cFfl = .text:0x802E79A0; // type:function size:0x10
 setRate__Q23m3d11anmTexSrt_cFfl = .text:0x802E79B0; // type:function size:0x10
 isStop__Q23m3d11anmTexSrt_cCFl = .text:0x802E79C0; // type:function size:0x10
-fn_802E79D0 = .text:0x802E79D0; // type:function size:0x10
+checkFrame__Q23m3d11anmTexSrt_cCFfl = .text:0x802E79D0; // type:function size:0x10
 setPlayMode__Q23m3d11anmTexSrt_cFQ23m3d10playMode_el = .text:0x802E79E0; // type:function size:0x14
 getFrameMax__Q23m3d11anmTexSrt_cCFl = .text:0x802E7A00; // type:function size:0x14
 setFrameStart__Q23m3d11anmTexSrt_cFfl = .text:0x802E7A20; // type:function size:0x14
-fn_802E7A40 = .text:0x802E7A40; // type:function size:0x14
+getFrameStart__Q23m3d11anmTexSrt_cCFl = .text:0x802E7A40; // type:function size:0x14
 __dt__Q23m3d8anmVis_cFv = .text:0x802E7A60; // type:function size:0x58
 getType__Q23m3d8anmVis_cFv = .text:0x802E7AC0; // type:function size:0x8
 create__Q23m3d8anmVis_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmVisP12mAllocator_cPUl = .text:0x802E7AD0; // type:function size:0x118
@@ -24561,9 +24561,9 @@ fn_80446910 = .text:0x80446910; // type:function size:0x54
 fn_80446970 = .text:0x80446970; // type:function size:0xF4
 fn_80446A70 = .text:0x80446A70; // type:function size:0xB8
 fn_80446B30 = .text:0x80446B30; // type:function size:0x7C
-fn_80446BB0 = .text:0x80446BB0; // type:function size:0x134
+Construct__Q34nw4r3g3d20AnmObjTexSrtOverrideFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdli = .text:0x80446BB0; // type:function size:0x134
 fn_80446CF0 = .text:0x80446CF0; // type:function size:0xC0
-fn_80446DB0 = .text:0x80446DB0; // type:function size:0x1F0
+Construct__Q34nw4r3g3d15AnmObjTexSrtResFP12MEMAllocatorPUlQ34nw4r3g3d12ResAnmTexSrtQ34nw4r3g3d6ResMdlb = .text:0x80446DB0; // type:function size:0x1F0
 fn_80446FA0 = .text:0x80446FA0; // type:function size:0xC0
 fn_80447060 = .text:0x80447060; // type:function size:0x8
 fn_80447070 = .text:0x80447070; // type:function size:0xB4
@@ -29099,8 +29099,8 @@ lbl_804F7130 = .rodata:0x804F7130; // type:object size:0x20
 lbl_804F7150 = .rodata:0x804F7150; // type:object size:0x18
 lbl_804F7168 = .rodata:0x804F7168; // type:object size:0x18
 lbl_804F7180 = .rodata:0x804F7180; // type:object size:0x18
-lbl_804F7198 = .rodata:0x804F7198; // type:object size:0x20
-lbl_804F71B8 = .rodata:0x804F71B8; // type:object size:0x18
+TYPE_NAME__Q34nw4r3g3d20AnmObjTexSrtOverride = .rodata:0x804F7198; // type:object size:0x20
+TYPE_NAME__Q34nw4r3g3d15AnmObjTexSrtRes = .rodata:0x804F71B8; // type:object size:0x18
 lbl_804F71D0 = .rodata:0x804F71D0; // type:object size:0x10
 lbl_804F71E0 = .rodata:0x804F71E0; // type:object size:0x14
 lbl_804F71F4 = .rodata:0x804F71F4; // type:object size:0x14
@@ -35693,8 +35693,8 @@ lbl_80542530 = .data:0x80542530; // type:object size:0x20
 lbl_80542550 = .data:0x80542550; // type:object size:0x18
 lbl_80542568 = .data:0x80542568; // type:object size:0x18
 lbl_80542580 = .data:0x80542580; // type:object size:0x18
-lbl_80542598 = .data:0x80542598; // type:object size:0x18
-lbl_805425B0 = .data:0x805425B0; // type:object size:0x18
+__vt__Q23m3d11anmTexSrt_c = .data:0x80542598; // type:object size:0x18
+__vt__Q33m3d11anmTexSrt_c7child_c = .data:0x805425B0; // type:object size:0x18
 __vt__Q23m3d8anmVis_c = .data:0x805425C8; // type:object size:0x18
 lbl_805425E0 = .data:0x805425E0; // type:object size:0x30
 __vt__Q23m3d6banm_c = .data:0x80542610; // type:object size:0x18
diff --git a/configure.py b/configure.py
index b0b47667..a5e48958 100644
--- a/configure.py
+++ b/configure.py
@@ -337,6 +337,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "f/f_manager.cpp"),
             Object(NonMatching, "m/m3d/m3d.cpp"),
             Object(NonMatching, "m/m3d/m_anmchr.cpp"),
+            Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
diff --git a/include/m/m3d/m_anmtexsrt.h b/include/m/m3d/m_anmtexsrt.h
new file mode 100644
index 00000000..8441d0ea
--- /dev/null
+++ b/include/m/m3d/m_anmtexsrt.h
@@ -0,0 +1,56 @@
+#ifndef M_ANMTEXSRT_H
+#define M_ANMTEXSRT_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+#include <nw4r/g3d/g3d_anmtexsrt.h>
+
+namespace m3d {
+
+class anmTexSrt_c : public banm_c {
+    class child_c : public fanm_c {
+    public:
+        child_c() {}
+        virtual ~child_c();
+        virtual int getType() override;
+
+        static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, bool);
+        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c*, u32*);
+        void setAnm(m3d::bmdl_c&, nw4r::g3d::ResAnmTexSrt, m3d::playMode_e);
+        void releaseAnm();
+        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexSrt&, playMode_e);
+    };
+
+public:
+    anmTexSrt_c() {}
+    virtual ~anmTexSrt_c();
+
+    virtual int getType() override;
+    virtual void remove() override;
+    virtual void play() override;
+
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c*, u32*, s32);
+    static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, s32, bool);
+
+    void setAnm(bmdl_c&, nw4r::g3d::ResAnmTexSrt, s32, playMode_e);
+
+    void play(s32);
+    f32 getFrame(s32) const;
+    void setFrame(f32, s32);
+    void setRate(f32, s32);
+    bool isStop(s32) const;
+
+    bool checkFrame(f32, s32) const;
+
+    void setPlayMode(playMode_e, s32);
+    f32 getFrameMax(s32) const;
+    void setFrameStart(f32, s32);
+    f32 getFrameStart(s32) const;
+
+private:
+    child_c *mpChildren;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index e6435c7a..fb756cfb 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -28,6 +28,23 @@ class fanm_c : public banm_c {
     bool checkFrame(f32) const;
     bool unk_802EAE70() const;
 
+    inline void setPlayState(playMode_e state) {
+        mPlayState = state;
+    }
+
+    inline f32 getEndFrame() {
+        return mEndFrame;
+    }
+
+    // Something about this is wrong
+    inline void setStartFrame(f32 f) {
+        mEndFrame = f;
+    }
+
+    inline f32 getStartFrame() {
+        return mStartFrame;
+    }
+
 private:
     f32 mEndFrame;
     f32 mStartFrame;
diff --git a/include/nw4r/g3d/g3d_anmtexsrt.h b/include/nw4r/g3d/g3d_anmtexsrt.h
index 5e5e99fc..21576b6a 100644
--- a/include/nw4r/g3d/g3d_anmtexsrt.h
+++ b/include/nw4r/g3d/g3d_anmtexsrt.h
@@ -1,11 +1,81 @@
 #ifndef NW4R_G3D_ANMTEXSRT_H
 #define NW4R_G3D_ANMTEXSRT_H
 #include "nw4r/g3d/g3d_restex.h"
+#include "nw4r/g3d/g3d_resanmtexsrt.h"
 
 namespace nw4r
 {
 	namespace g3d
 	{
+
+		class AnmObjTexSrtRes;
+		struct TexSrtAnmResult;
+
+		class AnmObjTexSrt : public AnmObj {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+				virtual TexSrtAnmResult *GetResult();           // at 0x38
+				virtual void Attach(s32, AnmObjTexSrtRes *res); // at 0x3C
+				virtual void Detach(s32);                          // at 0x40
+
+			private:
+				int mChildrenArraySize;
+				u16 *mpChildrenArray;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexSrt);
+		};
+
+		class AnmObjTexSrtNode : public AnmObjTexSrt {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+
+				inline int Size() {
+					return mNodeArraySize;
+				}
+
+				AnmObjTexSrtRes *GetNode(int i) {
+					return mpNodes[i];
+				}
+
+			private:
+				int mNodeArraySize;
+				AnmObjTexSrtRes **mpNodes;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexSrtNode);
+		};
+
+		class AnmObjTexSrtRes : public AnmObjTexSrt, public FrameCtrl {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+
+			static AnmObjTexSrtRes *Construct(MEMAllocator*, u32*, ResAnmTexSrt, ResMdl, bool);
+			private:
+				ResAnmTexSrt mRes;
+				TexSrtAnmResult *mpResultCache;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexSrtRes);
+		};
+
+		class AnmObjTexSrtOverride : public AnmObjTexSrtNode {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+			static AnmObjTexSrtOverride *Construct(MEMAllocator*, u32*, ResMdl, int);
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexSrtOverride);
+		};
+
 		struct TexSrtTypedef
 		{
 			enum TexMatrixMode
diff --git a/include/nw4r/g3d/g3d_obj.h b/include/nw4r/g3d/g3d_obj.h
index 978546ef..68b03706 100644
--- a/include/nw4r/g3d/g3d_obj.h
+++ b/include/nw4r/g3d/g3d_obj.h
@@ -116,7 +116,10 @@ class G3dObj {
 
     template <typename T>
     static T *DynamicCast(G3dObj *obj) {
-        return (obj != NULL && obj->IsDerivedFrom(T::GetTypeObjStatic())) ? static_cast<T *>(obj) : NULL;
+        if (obj != nullptr && obj->IsDerivedFrom(T::GetTypeObjStatic())) {
+            return static_cast<T *>(obj);
+        }
+        return nullptr;
     }
 
 private:
diff --git a/include/nw4r/g3d/g3d_resanmtexsrt.h b/include/nw4r/g3d/g3d_resanmtexsrt.h
index 94e2966a..e8a4e9f6 100644
--- a/include/nw4r/g3d/g3d_resanmtexsrt.h
+++ b/include/nw4r/g3d/g3d_resanmtexsrt.h
@@ -28,6 +28,10 @@ namespace nw4r
 			u32 mRevision; // at 0x8
 			char UNK_0xC[0x4];
 			s32 mMatDictOffset; // at 0x10
+			char UNK_0x14[0x0C];
+			u16 mNumFrames; // at 0x20
+			UNKWORD UNK_0x24;
+			AnmPolicy mAnmPolicy; // at 0x28
 		};
 
 		struct ResAnmTexSrt
@@ -55,6 +59,16 @@ namespace nw4r
 			{
 				return static_cast<const ResAnmTexSrtMatData *>(mAnmTexSrt.ofs_to_obj<ResDic>(ref().mMatDictOffset)[i]);
 			}
+
+			AnmPolicy GetAnmPolicy() const
+			{
+				return ref().mAnmPolicy;
+			}
+
+			int GetNumFrame() const
+			{
+				return ref().mNumFrames;
+			}
 			
 			void GetAnmResult(TexSrtAnmResult *, u32, float) const;
 		};
diff --git a/src/m/m3d/m_anmtexsrt.cpp b/src/m/m3d/m_anmtexsrt.cpp
new file mode 100644
index 00000000..df4ea1e5
--- /dev/null
+++ b/src/m/m3d/m_anmtexsrt.cpp
@@ -0,0 +1,214 @@
+#include <egg/core/eggFrmHeap.h>
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmtexsrt.h>
+#include <m/m_heap.h>
+
+namespace m3d {
+
+int anmTexSrt_c::child_c::getType() {
+    return 0x04;
+}
+
+anmTexSrt_c::child_c::~child_c() {}
+
+u32 anmTexSrt_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjTexSrtRes::Construct(nullptr, &size, srt, mdl, false);
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmTexSrt_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c* alloc, u32* pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = child_c::heapCost(mdl, srt, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjTexSrtRes::Construct(&mAllocator, nullptr, srt, mdl, false);
+    if (!mpAnmObj) {
+        return false;
+    }
+
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    }
+    setFrmCtrlDefault(srt, PLAY_MODE_4);
+    return true;
+}
+
+void anmTexSrt_c::child_c::setAnm(m3d::bmdl_c &mdl, nw4r::g3d::ResAnmTexSrt srt, m3d::playMode_e mode) {
+    releaseAnm();
+    mpAnmObj = nw4r::g3d::AnmObjTexSrtRes::Construct(&mAllocator, nullptr, srt, mdl.getResMdl(), false);
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(srt, mode);
+}
+
+void anmTexSrt_c::child_c::releaseAnm() {
+    if (mpAnmObj != nullptr) {
+        mpAnmObj->Release();
+        mpFrameHeap->free(3);
+        mpAnmObj = nullptr;
+    }
+}
+
+void anmTexSrt_c::child_c::setFrmCtrlDefault(nw4r::g3d::ResAnmTexSrt &srt, playMode_e mode) {
+    if (mode == PLAY_MODE_4) {
+        mode = srt.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
+    }
+    set(srt.GetNumFrame(), mode, 1.0f, -1.0f);
+}
+
+u32 anmTexSrt_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, s32 num, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjTexSrtOverride::Construct(nullptr, &size, mdl, num);
+    size += ROUND_UP(num * sizeof(child_c), 0x20);
+    u32 childCost = child_c::heapCost(mdl, srt, true);
+    size += num * ROUND_UP(childCost, 0x20);
+
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmTexSrt_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c* alloc, u32* pSize, s32 num) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size = 0;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = heapCost(mdl, srt, num, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjTexSrtOverride::Construct(&mAllocator, nullptr, mdl, num);
+    if (!mpAnmObj) {
+        return false;
+    }
+
+    // TODO inline?
+    mpChildren = (child_c*)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
+    if (!mpChildren) {
+        remove();
+        return false;
+    }
+
+    nw4r::g3d::AnmObjTexSrtOverride *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtOverride>(mpAnmObj);
+    child_c *child = mpChildren;
+    for (int i = 0; i < num; i++) {
+        new (child) child_c();
+        if (!child->create(mdl, srt, &mAllocator, nullptr)) {
+            mHeap::destroyFrmHeap(mpFrameHeap);
+            EGG::Heap::free(mpFrameHeap, nullptr);
+            return false;
+        }
+
+        if (i == 0) {
+            obj->Attach(i, nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtRes>(child->getAnimObj()));
+        } else {
+            child->releaseAnm();
+        }
+        child++;
+    }
+
+    return true;
+}
+
+anmTexSrt_c::~anmTexSrt_c() {
+    anmTexSrt_c::remove();
+}
+
+int anmTexSrt_c::getType() {
+    return 0x04;
+}
+
+void anmTexSrt_c::remove() {
+    nw4r::g3d::AnmObjTexSrtOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtOverride>(mpAnmObj);
+    if (o != nullptr && mpChildren != 0) {
+        int size = o->Size();
+        for (int i = 0; i < size; i++) {
+            mpChildren[i].remove();
+        }
+        mpChildren = nullptr;
+    }
+    banm_c::remove();
+}
+
+void anmTexSrt_c::setAnm(bmdl_c &mdl, nw4r::g3d::ResAnmTexSrt srt, s32 idx, playMode_e mode) {
+    nw4r::g3d::AnmObjTexSrtOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtOverride>(mpAnmObj);
+    o->Detach(idx);
+    mpChildren[idx].setAnm(mdl, srt, mode);
+
+    nw4r::g3d::AnmObjTexSrtRes *res =
+            nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtRes>(mpChildren[idx].getAnimObj());
+    o->Attach(idx, res);
+}
+
+void anmTexSrt_c::play() {
+    nw4r::g3d::AnmObjTexSrtOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexSrtOverride>(mpAnmObj);
+    int size = o->Size();
+    for (int i = 0; i < size; i++) {
+        play(i);
+    }
+}
+
+void anmTexSrt_c::play(s32 idx) {
+    if (mpChildren[idx].IsBound()) {
+        mpChildren[idx].play();
+    }
+}
+
+f32 anmTexSrt_c::getFrame(s32 idx) const {
+    return mpChildren[idx].getFrame();
+}
+
+void anmTexSrt_c::setFrame(f32 f, s32 idx) {
+    mpChildren[idx].setFrameOnly(f);
+}
+
+void anmTexSrt_c::setRate(f32 f, s32 idx) {
+    mpChildren[idx].setRate(f);
+}
+
+bool anmTexSrt_c::isStop(s32 idx) const {
+    return mpChildren[idx].isStop();
+}
+
+bool anmTexSrt_c::checkFrame(f32 f, s32 idx) const {
+    return mpChildren[idx].checkFrame(f);
+}
+
+void anmTexSrt_c::setPlayMode(playMode_e mode, s32 idx) {
+    mpChildren[idx].setPlayState(mode);
+}
+
+f32 anmTexSrt_c::getFrameMax(s32 idx) const {
+    return mpChildren[idx].getEndFrame();
+}
+
+void anmTexSrt_c::setFrameStart(f32 f, s32 idx) {
+    mpChildren[idx].setStartFrame(f);
+}
+
+f32 anmTexSrt_c::getFrameStart(s32 idx) const {
+    return mpChildren[idx].getStartFrame();
+}
+
+} // namespace m3d
diff --git a/tools/rel_sieve.py b/tools/rel_sieve.py
index dcb0e425..7c9e3cf4 100644
--- a/tools/rel_sieve.py
+++ b/tools/rel_sieve.py
@@ -53,6 +53,8 @@
     ['fn_801BB6F0', 'getCamera'],
     ['fn_800225F0', 'something light'],
     ['fn_80179250', 'shutter fence list'],
+    ['fn_8033AB50', 'getCollisionCheckContext'],
+    ['fn_383_D10', 'getAcOStageSink_ptr'],
 ]
 
 def main():

From fd4a6006e91301f957b22086c8a551979f0bf233 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 16:35:19 +0200
Subject: [PATCH 06/33] d_a_obj_sun_light OK

---
 .../rels/d_a_obj_sun_lightNP/splits.txt       |  3 +
 .../rels/d_a_obj_sun_lightNP/symbols.txt      | 96 +++++++++----------
 config/SOUE01/symbols.txt                     |  4 +-
 configure.py                                  |  2 +-
 include/d/a/obj/d_a_obj_ring.h                |  4 +-
 include/d/a/obj/d_a_obj_sun_light.h           | 36 +++++++
 include/m/m3d/m_anmtexsrt.h                   |  2 +-
 include/m/m3d/m_scnleaf.h                     |  4 +-
 include/nw4r/g3d/g3d_resfile.h                |  3 +
 .../arc_managers/current_stage_arc_manager.h  | 14 +--
 include/toBeSorted/spawn_struct.h             | 20 ++++
 src/REL/d/a/obj/d_a_obj_sun_light.cpp         | 69 +++++++++++++
 12 files changed, 196 insertions(+), 61 deletions(-)
 create mode 100644 include/d/a/obj/d_a_obj_sun_light.h
 create mode 100644 include/toBeSorted/spawn_struct.h

diff --git a/config/SOUE01/rels/d_a_obj_sun_lightNP/splits.txt b/config/SOUE01/rels/d_a_obj_sun_lightNP/splits.txt
index b8d525de..676fd6be 100644
--- a/config/SOUE01/rels/d_a_obj_sun_lightNP/splits.txt
+++ b/config/SOUE01/rels/d_a_obj_sun_lightNP/splits.txt
@@ -17,3 +17,6 @@ REL/global_destructor_chain.c:
 REL/d/a/obj/d_a_obj_sun_light.cpp:
 	.text       start:0x000000F0 end:0x00000BD8
 	.ctors      start:0x00000000 end:0x00000004
+	.rodata     start:0x00000000 end:0x00000014
+	.data       start:0x00000000 end:0x000001D8
+	.bss        start:0x00000008 end:0x00000048
diff --git a/config/SOUE01/rels/d_a_obj_sun_lightNP/symbols.txt b/config/SOUE01/rels/d_a_obj_sun_lightNP/symbols.txt
index edc8d734..df8edfce 100644
--- a/config/SOUE01/rels/d_a_obj_sun_lightNP/symbols.txt
+++ b/config/SOUE01/rels/d_a_obj_sun_lightNP/symbols.txt
@@ -3,55 +3,55 @@ _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
 __register_global_object = .text:0x00000070; // type:function size:0x1C scope:global
 __destroy_global_chain = .text:0x00000090; // type:function size:0x54 scope:global
-fn_434_F0 = .text:0x000000F0; // type:function size:0xEC
-fn_434_1E0 = .text:0x000001E0; // type:function size:0x58
-fn_434_240 = .text:0x00000240; // type:function size:0x6C
-fn_434_2B0 = .text:0x000002B0; // type:function size:0xA0
-fn_434_350 = .text:0x00000350; // type:function size:0xA4
-fn_434_400 = .text:0x00000400; // type:function size:0xE4
-fn_434_4F0 = .text:0x000004F0; // type:function size:0xE8
-fn_434_5E0 = .text:0x000005E0; // type:function size:0x10
-fn_434_5F0 = .text:0x000005F0; // type:function size:0x8
-fn_434_600 = .text:0x00000600; // type:function size:0x78
-fn_434_680 = .text:0x00000680; // type:function size:0x10
-fn_434_690 = .text:0x00000690; // type:function size:0x44
-fn_434_6E0 = .text:0x000006E0; // type:function size:0x4
-fn_434_6F0 = .text:0x000006F0; // type:function size:0x4
-fn_434_700 = .text:0x00000700; // type:function size:0x4
-fn_434_710 = .text:0x00000710; // type:function size:0x18
-fn_434_730 = .text:0x00000730; // type:function size:0xD8
-fn_434_810 = .text:0x00000810; // type:function size:0x10
-fn_434_820 = .text:0x00000820; // type:function size:0x60
-fn_434_880 = .text:0x00000880; // type:function size:0xC
-fn_434_890 = .text:0x00000890; // type:function size:0x1C
-fn_434_8B0 = .text:0x000008B0; // type:function size:0x1C
-fn_434_8D0 = .text:0x000008D0; // type:function size:0x1C
-fn_434_8F0 = .text:0x000008F0; // type:function size:0x10
-fn_434_900 = .text:0x00000900; // type:function size:0x10
-fn_434_910 = .text:0x00000910; // type:function size:0x10
-fn_434_920 = .text:0x00000920; // type:function size:0x10
-fn_434_930 = .text:0x00000930; // type:function size:0x10
-fn_434_940 = .text:0x00000940; // type:function size:0x10
-fn_434_950 = .text:0x00000950; // type:function size:0x30
-fn_434_980 = .text:0x00000980; // type:function size:0x30
-fn_434_9B0 = .text:0x000009B0; // type:function size:0x30
-fn_434_9E0 = .text:0x000009E0; // type:function size:0x10C
-fn_434_AF0 = .text:0x00000AF0; // type:function size:0x58
-fn_434_B50 = .text:0x00000B50; // type:function size:0x88
+dAcOsunLight_c_classInit__Fv = .text:0x000000F0; // type:function size:0xEC
+__dt__27sFState_c<14dAcOsunLight_c>Fv = .text:0x000001E0; // type:function size:0x58
+__dt__30sFStateFct_c<14dAcOsunLight_c>Fv = .text:0x00000240; // type:function size:0x6C
+__dt__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000002B0; // type:function size:0xA0
+__dt__53sFStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c>Fv = .text:0x00000350; // type:function size:0xA4
+createHeap__14dAcOsunLight_cFv = .text:0x00000400; // type:function size:0xE4
+create__14dAcOsunLight_cFv = .text:0x000004F0; // type:function size:0xE8
+changeState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>FRC12sStateIDIf_c = .text:0x000005E0; // type:function size:0x10
+doDelete__14dAcOsunLight_cFv = .text:0x000005F0; // type:function size:0x8
+actorExecute__14dAcOsunLight_cFv = .text:0x00000600; // type:function size:0x78
+executeState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000680; // type:function size:0x10
+draw__14dAcOsunLight_cFv = .text:0x00000690; // type:function size:0x44
+initializeState_Wait__14dAcOsunLight_cFv = .text:0x000006E0; // type:function size:0x4
+executeState_Wait__14dAcOsunLight_cFv = .text:0x000006F0; // type:function size:0x4
+finalizeState_Wait__14dAcOsunLight_cFv = .text:0x00000700; // type:function size:0x4
+isDay__14dAcOsunLight_cFv = .text:0x00000710; // type:function size:0x18
+__dt__14dAcOsunLight_cFv = .text:0x00000730; // type:function size:0xD8
+getStateID__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000810; // type:function size:0x10
+build__30sFStateFct_c<14dAcOsunLight_c>FRC12sStateIDIf_c = .text:0x00000820; // type:function size:0x60
+dispose__30sFStateFct_c<14dAcOsunLight_c>FRP10sStateIf_c = .text:0x00000880; // type:function size:0xC
+initialize__27sFState_c<14dAcOsunLight_c>Fv = .text:0x00000890; // type:function size:0x1C
+execute__27sFState_c<14dAcOsunLight_c>Fv = .text:0x000008B0; // type:function size:0x1C
+finalize__27sFState_c<14dAcOsunLight_c>Fv = .text:0x000008D0; // type:function size:0x1C
+initializeState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008F0; // type:function size:0x10
+finalizeState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000900; // type:function size:0x10
+refreshState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000910; // type:function size:0x10
+getState__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000920; // type:function size:0x10
+getNewStateID__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000930; // type:function size:0x10
+getOldStateID__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000940; // type:function size:0x10
+finalizeState__29sFStateID_c<14dAcOsunLight_c>CFR14dAcOsunLight_c = .text:0x00000950; // type:function size:0x30
+executeState__29sFStateID_c<14dAcOsunLight_c>CFR14dAcOsunLight_c = .text:0x00000980; // type:function size:0x30
+initializeState__29sFStateID_c<14dAcOsunLight_c>CFR14dAcOsunLight_c = .text:0x000009B0; // type:function size:0x30
+__sinit_\d_a_obj_sun_light_cpp = .text:0x000009E0; // type:function size:0x10C scope:local
+__dt__29sFStateID_c<14dAcOsunLight_c>Fv = .text:0x00000AF0; // type:function size:0x58
+isSameName__29sFStateID_c<14dAcOsunLight_c>CFPCc = .text:0x00000B50; // type:function size:0x88
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
 __destroy_global_chain_reference = .dtors:0x00000000; // type:object size:0x4 scope:global
-lbl_434_rodata_0 = .rodata:0x00000000; // type:object size:0x14 data:float
-lbl_434_data_0 = .data:0x00000000; // type:object size:0x10 data:4byte
-lbl_434_data_10 = .data:0x00000010; // type:object size:0x10 data:string
-lbl_434_data_20 = .data:0x00000020; // type:object size:0x10
-lbl_434_data_30 = .data:0x00000030; // type:object size:0x20
-lbl_434_data_50 = .data:0x00000050; // type:object size:0x80
-lbl_434_data_D0 = .data:0x000000D0; // type:object size:0x30
-lbl_434_data_100 = .data:0x00000100; // type:object size:0x30
-lbl_434_data_130 = .data:0x00000130; // type:object size:0x18
-lbl_434_data_148 = .data:0x00000148; // type:object size:0x5C
-lbl_434_data_1A4 = .data:0x000001A4; // type:object size:0x34
+lbl_434_rodata_0 = .rodata:0x00000000; // type:object size:0x14 scope:local data:float
+g_profile_OBJ_SUN_LIGHT = .data:0x00000000; // type:object size:0x10 data:4byte
+lbl_434_data_10 = .data:0x00000010; // type:object size:0x10 scope:local data:string
+lbl_434_data_20 = .data:0x00000020; // type:object size:0x10 scope:local
+lbl_434_data_30 = .data:0x00000030; // type:object size:0x20 scope:local
+__vt__14dAcOsunLight_c = .data:0x00000050; // type:object size:0x80
+__vt__53sFStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c> = .data:0x000000D0; // type:object size:0x30
+__vt__83sStateMgr_c<14dAcOsunLight_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c> = .data:0x00000100; // type:object size:0x30
+__vt__30sFStateFct_c<14dAcOsunLight_c> = .data:0x00000130; // type:object size:0x14
+__vt__27sFState_c<14dAcOsunLight_c> = .data:0x00000148; // type:object size:0x18
+__vt__29sFStateID_c<14dAcOsunLight_c> = .data:0x000001A4; // type:object size:0x34
 __global_destructor_chain = .bss:0x00000000; // type:object size:0x4 scope:global
-lbl_434_bss_8 = .bss:0x00000008; // type:object size:0x10
-lbl_434_bss_18 = .bss:0x00000018; // type:object size:0x30 data:4byte
+lbl_434_bss_8 = .bss:0x00000008; // type:object size:0x10 scope:local
+StateID_Wait__14dAcOsunLight_c = .bss:0x00000018; // type:object size:0x30 data:4byte
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 940df14d..1dd1e38f 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17653,7 +17653,7 @@ fn_802EBE10 = .text:0x802EBE10; // type:function size:0x8
 calc__Q23m3d9scnLeaf_cFb = .text:0x802EBE20; // type:function size:0x78
 calcVtx__Q23m3d9scnLeaf_cFb = .text:0x802EBEA0; // type:function size:0x78
 fn_802EBF20 = .text:0x802EBF20; // type:function size:0x84
-fn_802EBFB0 = .text:0x802EBFB0; // type:function size:0x48
+setPriorityDraw__Q23m3d9scnLeaf_cFii = .text:0x802EBFB0; // type:function size:0x48
 fn_802EC000 = .text:0x802EC000; // type:function size:0x88
 fn_802EC090 = .text:0x802EC090; // type:function size:0x7C
 fn_802EC110 = .text:0x802EC110; // type:function size:0x1E8
@@ -48788,7 +48788,7 @@ lbl_805B3760 = .bss:0x805B3760; // type:object size:0x80
 lbl_805B37E0 = .bss:0x805B37E0; // type:object size:0xC
 lbl_805B37EC = .bss:0x805B37EC; // type:object size:0x40 data:4byte
 lbl_805B382C = .bss:0x805B382C; // type:object size:0x34 data:4byte
-lbl_805B3860 = .bss:0x805B3860; // type:object size:0x2C data:4byte
+sInstance__11SpawnStruct = .bss:0x805B3860; // type:object size:0x2C data:4byte
 lbl_805B388C = .bss:0x805B388C; // type:object size:0x2C data:byte
 lbl_805B38B8 = .bss:0x805B38B8; // type:object size:0x50
 lbl_805B3908 = .bss:0x805B3908; // type:object size:0x40 data:4byte
diff --git a/configure.py b/configure.py
index a5e48958..bc58787e 100644
--- a/configure.py
+++ b/configure.py
@@ -964,7 +964,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_obj_stopper_rock", "REL/d/a/obj/d_a_obj_stopper_rock.cpp"),
     Rel(NonMatching, "d_a_obj_stopping_rope", "REL/d/a/obj/d_a_obj_stopping_rope.cpp"),
     Rel(NonMatching, "d_a_obj_stream_lava", "REL/d/a/obj/d_a_obj_stream_lava.cpp"),
-    Rel(NonMatching, "d_a_obj_sun_light", "REL/d/a/obj/d_a_obj_sun_light.cpp"),
+    Rel(Matching, "d_a_obj_sun_light", "REL/d/a/obj/d_a_obj_sun_light.cpp"),
     Rel(NonMatching, "d_a_obj_swhit", "REL/d/a/obj/d_a_obj_swhit.cpp"),
     Rel(NonMatching, "d_a_obj_switch_shutter", "REL/d/a/obj/d_a_obj_switch_shutter.cpp"),
     Rel(NonMatching, "d_a_obj_switch_wall", "REL/d/a/obj/d_a_obj_switch_wall.cpp"),
diff --git a/include/d/a/obj/d_a_obj_ring.h b/include/d/a/obj/d_a_obj_ring.h
index fcc63e31..5b5b69e7 100644
--- a/include/d/a/obj/d_a_obj_ring.h
+++ b/include/d/a/obj/d_a_obj_ring.h
@@ -1,5 +1,5 @@
-#ifndef D_A_OBJ_RING
-#define D_A_OBJ_RING
+#ifndef D_A_OBJ_RING_H
+#define D_A_OBJ_RING_H
 
 #include <d/a/obj/d_a_obj_base.h>
 #include <m/m3d/m_smdl.h>
diff --git a/include/d/a/obj/d_a_obj_sun_light.h b/include/d/a/obj/d_a_obj_sun_light.h
new file mode 100644
index 00000000..38756d16
--- /dev/null
+++ b/include/d/a/obj/d_a_obj_sun_light.h
@@ -0,0 +1,36 @@
+#ifndef D_A_OBJ_SUN_LIGHT_H
+#define D_A_OBJ_SUN_LIGHT_H
+
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_anmtexsrt.h>
+#include <m/m3d/m_smdl.h>
+#include <s/s_State.hpp>
+#include <s/s_StateMgr.hpp>
+
+class dAcOsunLight_c : public dAcObjBase_c {
+public:
+    dAcOsunLight_c() : mStateMgr(*this, sStateID::null) {}
+    virtual ~dAcOsunLight_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+    // This inline fixes stack alloc
+    inline bool createModel(nw4r::g3d::ResMdl mdl) {
+        return mModel.create(mdl, &heap_allocator, 0x120, 1, nullptr);
+    }
+
+    bool isDay();
+
+    STATE_FUNC_DECLARE(dAcOsunLight_c, Wait);
+
+private:
+    nw4r::g3d::ResFile mBrres;
+    m3d::smdl_c mModel;
+    STATE_MGR_DECLARE(dAcOsunLight_c);
+    m3d::anmTexSrt_c mTexAnm;
+};
+
+#endif
diff --git a/include/m/m3d/m_anmtexsrt.h b/include/m/m3d/m_anmtexsrt.h
index 8441d0ea..f6da84f8 100644
--- a/include/m/m3d/m_anmtexsrt.h
+++ b/include/m/m3d/m_anmtexsrt.h
@@ -22,7 +22,7 @@ class anmTexSrt_c : public banm_c {
     };
 
 public:
-    anmTexSrt_c() {}
+    anmTexSrt_c(): mpChildren(nullptr) {}
     virtual ~anmTexSrt_c();
 
     virtual int getType() override;
diff --git a/include/m/m3d/m_scnleaf.h b/include/m/m3d/m_scnleaf.h
index 32f63565..ab2ce20d 100644
--- a/include/m/m3d/m_scnleaf.h
+++ b/include/m/m3d/m_scnleaf.h
@@ -20,7 +20,7 @@ class scnLeaf_c : UnkClass, EGG::Disposer {
     virtual void remove();
     virtual int entry();
 
-    void setOption(unsigned long flag, unsigned long set);
+    void setOption(u32 flag, u32 set);
     void setScale(f32, f32, f32);
     void setScale(const nw4r::math::VEC3 &);
 
@@ -32,6 +32,8 @@ class scnLeaf_c : UnkClass, EGG::Disposer {
     void calc(bool);
     void calcVtx(bool);
 
+    void setPriorityDraw(int, int);
+
 protected:
     nw4r::g3d::ScnLeaf *mpScnLeaf;
 };
diff --git a/include/nw4r/g3d/g3d_resfile.h b/include/nw4r/g3d/g3d_resfile.h
index c5dc7fea..9a8c51cd 100644
--- a/include/nw4r/g3d/g3d_resfile.h
+++ b/include/nw4r/g3d/g3d_resfile.h
@@ -18,6 +18,9 @@ struct ResFileData {
 struct ResFile {
     ResCommon<ResFileData> mFile; // at 0x0
 
+    ResFile(): mFile((void*)nullptr) {}
+    ResFile(void *ptr): mFile(ptr) {}
+
     inline ResFileData &ref() const {
         return mFile.ref();
     }
diff --git a/include/toBeSorted/arc_managers/current_stage_arc_manager.h b/include/toBeSorted/arc_managers/current_stage_arc_manager.h
index f4f11ce5..26194c8d 100644
--- a/include/toBeSorted/arc_managers/current_stage_arc_manager.h
+++ b/include/toBeSorted/arc_managers/current_stage_arc_manager.h
@@ -6,15 +6,10 @@
 #include <sized_string.h>
 
 class CurrentStageArcManager {
+public:
     CurrentStageArcManager();
     virtual ~CurrentStageArcManager();
 
-    SizedString<32> mStageName;
-    SizedString<32> mCurrentLoadingStageArcName;
-    SizedString<32> mStageExtraLayerArcName;
-
-    dRawArcTable_c mArcTable;
-
     static CurrentStageArcManager *sInstance;
 
     static bool create(EGG::Heap *heap);
@@ -36,6 +31,13 @@ class CurrentStageArcManager {
     const char *getRoomArcDirectory(int room) const;
 
     static EGG::ExpHeap *getHeap();
+
+private:
+    SizedString<32> mStageName;
+    SizedString<32> mCurrentLoadingStageArcName;
+    SizedString<32> mStageExtraLayerArcName;
+
+    dRawArcTable_c mArcTable;
 };
 
 #endif
diff --git a/include/toBeSorted/spawn_struct.h b/include/toBeSorted/spawn_struct.h
new file mode 100644
index 00000000..8bad4516
--- /dev/null
+++ b/include/toBeSorted/spawn_struct.h
@@ -0,0 +1,20 @@
+#ifndef SPAWN_STRUCT_H
+#define SPAWN_STRUCT_H
+
+#include <common.h>
+
+struct SpawnStruct {
+    /* 0x00 */ char mName[32]; // Could be SizedString<32>
+    /* 0x20 */ u16 mTransitionFadeFrames;
+    /* 0x22 */ s8 mRoom;
+    /* 0x23 */ s8 mLayer;
+    /* 0x24 */ s8 mEntrance;
+    /* 0x25 */ bool mNight;
+    /* 0x26 */ bool mTrial;
+    /* 0x27 */ bool mTransitionType;
+    /* 0x28 */ u8 UNK_0x28[0x2C - 0x28];
+
+    static SpawnStruct sInstance;
+};
+
+#endif
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index e69de29b..7f03ecfe 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -0,0 +1,69 @@
+#include <d/a/obj/d_a_obj_sun_light.h>
+#include <toBeSorted/arc_managers/current_stage_arc_manager.h>
+#include <toBeSorted/spawn_struct.h>
+
+SPECIAL_ACTOR_PROFILE(OBJ_SUN_LIGHT, dAcOsunLight_c, fProfile::OBJ_SUN_LIGHT, 0x0219, 0, 3);
+
+STATE_DEFINE(dAcOsunLight_c, Wait);
+
+extern "C" void fn_801B42D0(nw4r::g3d::ResFile *);
+extern "C" void fn_801B4320(nw4r::g3d::ResFile *);
+
+bool dAcOsunLight_c::createHeap() {
+    // TODO stack order shuffle
+    mBrres = CurrentStageArcManager::sInstance->getData("g3d/stage.brres");
+    fn_801B42D0(&mBrres);
+    fn_801B4320(&mBrres);
+    nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("StageF000Light");
+    // This whole code is a bit weird again
+    bool success1 = createModel(mdl);
+    if (!success1) {
+        return success1;
+    }
+    nw4r::g3d::ResAnmTexSrt srt = mBrres.GetResAnmTexSrt("StageF000Light");
+    bool success2 = mTexAnm.create(mdl, srt, &heap_allocator, nullptr, 1);
+    return success2 ? true : success2;
+}
+
+int dAcOsunLight_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcOsunLight_c::m_allocator", 0x20)) {
+        return 2;
+    }
+
+    mModel.setAnm(mTexAnm);
+    mStateMgr.changeState(StateID_Wait);
+    mModel.setPriorityDraw(0x1C, 9);
+    setBoundingBox(mVec3_c(-200.0f, -100.0f, -200.0f), mVec3_c(200.0f, 600.0f, 500.0f));
+    return 1;
+}
+
+int dAcOsunLight_c::doDelete() {
+    return 1;
+}
+
+int dAcOsunLight_c::actorExecute() {
+    mStateMgr.executeState();
+    if (isDay()) {
+        mTexAnm.play();
+    }
+    updateMatrix();
+    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    return 1;
+}
+
+int dAcOsunLight_c::draw() {
+    if (isDay()) {
+        drawModelType1(&mModel);
+    }
+    return 1;
+}
+
+void dAcOsunLight_c::initializeState_Wait() {}
+
+void dAcOsunLight_c::executeState_Wait() {}
+
+void dAcOsunLight_c::finalizeState_Wait() {}
+
+bool dAcOsunLight_c::isDay() {
+    return !SpawnStruct::sInstance.mNight;
+}

From 5014135e4b00f9680d5bda01d590908c65c2a559 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 20:54:43 +0200
Subject: [PATCH 07/33] m_anmChr OK

---
 config/SOUE01/splits.txt         |  2 +
 config/SOUE01/symbols.txt        |  8 ++--
 configure.py                     |  2 +-
 include/m/m3d/m_anmchr.h         |  6 ++-
 include/m/m3d/m_anmtexsrt.h      |  4 +-
 include/m/m3d/m_anmvis.h         |  5 ++-
 include/nw4r/g3d/g3d_anmchr.h    | 74 ++++++++++++++++++++++++++++++--
 include/nw4r/g3d/g3d_resanmchr.h | 15 +++++++
 include/nw4r/g3d/g3d_resnode.h   |  4 +-
 src/m/m3d/m_anmchr.cpp           | 67 ++++++++++++++++++++++++++---
 10 files changed, 165 insertions(+), 22 deletions(-)

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 4058f474..46d1b97d 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -277,6 +277,8 @@ m/m3d/m3d.cpp:
 
 m/m3d/m_anmchr.cpp:
 	.text       start:0x802E48E0 end:0x802E4D98
+	.data       start:0x805424D0 end:0x805424E8
+	.sdata2     start:0x8057CCC0 end:0x8057CCD0
 
 m/m3d/m_anmtexsrt.cpp:
 	.text       start:0x802E6EC0 end:0x802E7A54
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 1dd1e38f..694fb18d 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -24613,7 +24613,7 @@ fn_80448460 = .text:0x80448460; // type:function size:0x178
 fn_804485E0 = .text:0x804485E0; // type:function size:0x640
 fn_80448C20 = .text:0x80448C20; // type:function size:0x10
 fn_80448C30 = .text:0x80448C30; // type:function size:0x10
-fn_80448C40 = .text:0x80448C40; // type:function size:0x1F0
+Construct__Q34nw4r3g3d12AnmObjChrResFP12MEMAllocatorPUlQ34nw4r3g3d9ResAnmChrQ34nw4r3g3d6ResMdlb = .text:0x80448C40; // type:function size:0x1F0
 fn_80448E30 = .text:0x80448E30; // type:function size:0xC0
 fn_80448EF0 = .text:0x80448EF0; // type:function size:0x8
 fn_80448F00 = .text:0x80448F00; // type:function size:0xB4
@@ -29102,9 +29102,9 @@ lbl_804F7180 = .rodata:0x804F7180; // type:object size:0x18
 TYPE_NAME__Q34nw4r3g3d20AnmObjTexSrtOverride = .rodata:0x804F7198; // type:object size:0x20
 TYPE_NAME__Q34nw4r3g3d15AnmObjTexSrtRes = .rodata:0x804F71B8; // type:object size:0x18
 lbl_804F71D0 = .rodata:0x804F71D0; // type:object size:0x10
-lbl_804F71E0 = .rodata:0x804F71E0; // type:object size:0x14
+TYPE_NAME__Q34nw4r3g3d13AnmObjChrNode = .rodata:0x804F71E0; // type:object size:0x14
 lbl_804F71F4 = .rodata:0x804F71F4; // type:object size:0x14
-lbl_804F7208 = .rodata:0x804F7208; // type:object size:0x18
+TYPE_NAME__Q34nw4r3g3d12AnmObjChrRes = .rodata:0x804F7208; // type:object size:0x18
 lbl_804F7220 = .rodata:0x804F7220; // type:object size:0x10
 lbl_804F7230 = .rodata:0x804F7230; // type:object size:0x18
 lbl_804F7248 = .rodata:0x804F7248; // type:object size:0x10
@@ -35685,7 +35685,7 @@ lbl_8054243C = .data:0x8054243C; // type:object size:0xC
 lbl_80542448 = .data:0x80542448; // type:object size:0x18
 lbl_80542460 = .data:0x80542460; // type:object size:0x40
 lbl_805424A0 = .data:0x805424A0; // type:object size:0x30
-lbl_805424D0 = .data:0x805424D0; // type:object size:0x18
+__vt__Q23m3d8anmChr_c = .data:0x805424D0; // type:object size:0x18
 lbl_805424E8 = .data:0x805424E8; // type:object size:0x18
 lbl_80542500 = .data:0x80542500; // type:object size:0x18
 lbl_80542518 = .data:0x80542518; // type:object size:0x18
diff --git a/configure.py b/configure.py
index bc58787e..0ae97f98 100644
--- a/configure.py
+++ b/configure.py
@@ -336,7 +336,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "f/f_list.cpp"),
             Object(Matching, "f/f_manager.cpp"),
             Object(NonMatching, "m/m3d/m3d.cpp"),
-            Object(NonMatching, "m/m3d/m_anmchr.cpp"),
+            Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
diff --git a/include/m/m3d/m_anmchr.h b/include/m/m3d/m_anmchr.h
index 775d9fab..4dab7f75 100644
--- a/include/m/m3d/m_anmchr.h
+++ b/include/m/m3d/m_anmchr.h
@@ -1,5 +1,5 @@
-#ifndef M_ANMCHR_H
-#define M_ANMCHR_H
+#ifndef M3D_M_ANMCHR_H
+#define M3D_M_ANMCHR_H
 
 #include <m/m3d/m_bmdl.h>
 #include <m/m3d/m_fanm.h>
@@ -9,10 +9,12 @@
 namespace m3d {
 
 class anmChr_c : public fanm_c {
+public:
     virtual ~anmChr_c();
 
     virtual int getType() override;
 
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmChr, mAllocator_c*, u32*);
     void setAnm(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
     void setAnmAfter(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
     void setFrmCtrlDefault(nw4r::g3d::ResAnmChr &, m3d::playMode_e);
diff --git a/include/m/m3d/m_anmtexsrt.h b/include/m/m3d/m_anmtexsrt.h
index f6da84f8..151f87ad 100644
--- a/include/m/m3d/m_anmtexsrt.h
+++ b/include/m/m3d/m_anmtexsrt.h
@@ -1,5 +1,5 @@
-#ifndef M_ANMTEXSRT_H
-#define M_ANMTEXSRT_H
+#ifndef M3D_M_ANMTEXSRT_H
+#define M3D_M_ANMTEXSRT_H
 
 #include <m/m3d/m_bmdl.h>
 #include <m/m3d/m_fanm.h>
diff --git a/include/m/m3d/m_anmvis.h b/include/m/m3d/m_anmvis.h
index 800ea00b..1787a278 100644
--- a/include/m/m3d/m_anmvis.h
+++ b/include/m/m3d/m_anmvis.h
@@ -1,5 +1,5 @@
-#ifndef M_ANMVIS_H
-#define M_ANMVIS_H
+#ifndef M3D_M_ANMVIS_H
+#define M3D_M_ANMVIS_H
 
 #include <m/m3d/m_bmdl.h>
 #include <m/m3d/m_fanm.h>
@@ -8,6 +8,7 @@
 namespace m3d {
 
 class anmVis_c : public fanm_c {
+public:
     virtual ~anmVis_c();
 
     virtual int getType() override;
diff --git a/include/nw4r/g3d/g3d_anmchr.h b/include/nw4r/g3d/g3d_anmchr.h
index deed7bdb..c43b4e18 100644
--- a/include/nw4r/g3d/g3d_anmchr.h
+++ b/include/nw4r/g3d/g3d_anmchr.h
@@ -1,6 +1,7 @@
 #ifndef NW4R_G3D_ANMCHR_H
 #define NW4R_G3D_ANMCHR_H
 #include "common.h"
+#include "nw4r/g3d/g3d_anmobj.h"
 #include "nw4r/math/math_types.h"
 
 namespace nw4r {
@@ -12,13 +13,78 @@ struct ChrAnmResult {
     math::MTX34 mMtx; // at 0x1C
 };
 
-struct AnmObjChr {};
+class AnmObjChrRes;
 
-struct AnmObjChrNode {};
+class AnmObjChr : public AnmObj {
+public:
 
-struct AnmObjChrBlend {};
+    enum BindOption {};
+
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    virtual ChrAnmResult *GetResult(ChrAnmResult *, u32); // at 0x38
+    virtual void Attach(int, AnmObjChrRes*);              // at 0x3C
+    virtual void Detach(int);                             // at 0x40
+    virtual void DetachAll();                             // at 0x44
+    virtual void SetWeight(int, f32);                     // at 0x48
+    virtual f32  GetWeight(int) const;                    // at 0x4C
+    virtual void Bind(ResMdl, u32, BindOption) = 0;       // at 0x50
+    virtual void Release(ResMdl, u32, BindOption) = 0;    // at 0x54
+
+private:
+    UNKWORD field_0x10;
+    UNKWORD field_0x14;
+
+protected:
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjChr);
+};
+
+class AnmObjChrNode : public AnmObjChr {
+public:
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    inline int Size() {
+        return mNodeArraySize;
+    }
+
+    AnmObjChrRes *GetNode(int i) {
+        return mpNodes[i];
+    }
+
+private:
+    int mNodeArraySize;
+    AnmObjChrRes **mpNodes;
+
+protected:
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrNode);
+};
+
+struct AnmObjChrBlend : public AnmObjChrNode {
+public:
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+protected:
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrBlend);
+};
+
+struct AnmObjChrRes : public AnmObjChr, public FrameCtrl {
+public:
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    static AnmObjChrRes *Construct(MEMAllocator*, u32*, nw4r::g3d::ResAnmChr, nw4r::g3d::ResMdl, bool);
+
+protected:
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrRes);
+};
 
-struct AnmObjChrRes {};
 } // namespace g3d
 } // namespace nw4r
 
diff --git a/include/nw4r/g3d/g3d_resanmchr.h b/include/nw4r/g3d/g3d_resanmchr.h
index 68b69e98..76e08a34 100644
--- a/include/nw4r/g3d/g3d_resanmchr.h
+++ b/include/nw4r/g3d/g3d_resanmchr.h
@@ -8,6 +8,11 @@ namespace g3d {
 struct ResAnmChrData {
     char UNK_0x0[0x8];
     u32 mRevision; // at 0x8
+    char UNK_0xC[0x4];
+    s32 mMatDictOffset; // at 0x10
+    char UNK_0x14[0x0C];
+    u16 mNumFrames; // at 0x20
+    AnmPolicy mAnmPolicy; // at 0x24
 };
 
 struct ResAnmChr {
@@ -20,6 +25,16 @@ struct ResAnmChr {
     inline bool CheckRevision() const {
         return mAnmChr.ref().mRevision == REVISION;
     }
+
+    AnmPolicy GetAnmPolicy() const
+    {
+        return mAnmChr.ref().mAnmPolicy;
+    }
+
+    int GetNumFrame() const
+    {
+        return mAnmChr.ref().mNumFrames;
+    }
 };
 } // namespace g3d
 } // namespace nw4r
diff --git a/include/nw4r/g3d/g3d_resnode.h b/include/nw4r/g3d/g3d_resnode.h
index 9c9fb445..67506985 100644
--- a/include/nw4r/g3d/g3d_resnode.h
+++ b/include/nw4r/g3d/g3d_resnode.h
@@ -1,12 +1,14 @@
 #ifndef NW4R_G3D_RESNODE_H
 #define NW4R_G3D_RESNODE_H
 #include "common.h"
-#include "nw4r/g3d/g3d_anmchr.h"
 #include "nw4r/g3d/g3d_rescommon.h"
 #include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace g3d {
+
+class ChrAnmResult;
+
 struct ResNodeData {
     u32 INT_0x0;
     s32 INT_0x4;
diff --git a/src/m/m3d/m_anmchr.cpp b/src/m/m3d/m_anmchr.cpp
index 7d3d9f72..7ec4baaa 100644
--- a/src/m/m3d/m_anmchr.cpp
+++ b/src/m/m3d/m_anmchr.cpp
@@ -1,5 +1,6 @@
 #include <m/m3d/m_anmchr.h>
-#include <nw4r/g3d/g3d_anmobj.h>
+#include <m/m3d/m3d.h>
+#include <nw4r/g3d/g3d_anmchr.h>
 #include <nw4r/g3d/g3d_resanmchr.h>
 
 namespace m3d {
@@ -10,21 +11,75 @@ int anmChr_c::getType() {
     return 0;
 }
 
+bool anmChr_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmChr anm, mAllocator_c* alloc, u32* pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    nw4r::g3d::AnmObjChrRes::Construct(nullptr, pSize, anm, mdl, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjChrRes::Construct(&mAllocator, &size, anm, mdl, false);
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    }
+
+    setFrmCtrlDefault(anm, PLAY_MODE_4);
+    return true;
+}
+
 void anmChr_c::setAnm(bmdl_c &mdl, nw4r::g3d::ResAnmChr anm, playMode_e mode) {
     mdl.removeAnm((nw4r::g3d::ScnMdlSimple::AnmObjType)getType());
     setAnmAfter(mdl, anm, mode);
 }
 
 void anmChr_c::setAnmAfter(bmdl_c &mdl, nw4r::g3d::ResAnmChr anm, playMode_e mode) {
-    // TODO g3d headers
-    // void *parent = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrNode>(mpAnmObj->GetParent());
+    nw4r::g3d::AnmObjChrNode *node;
+    nw4r::g3d::G3dObj *parent = mpAnmObj->GetParent();
+    f32 weight;
+    int idx;
+    if (parent != nullptr) {
+        node = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrNode>(parent);
+        int size = node->Size();
+        idx = 0;
+        for (int i = 0; i < size; i++, idx++) {
+            if (node->GetNode(i) == mpAnmObj) {
+                break;
+            }
+        }
+        weight = node->GetWeight(idx);
+        node->Detach(idx);
+    }
+    mpAnmObj->Release();
+    mpAnmObj->Destroy();
+    mpFrameHeap->free(3);
+    u32 tmp;
+    mpAnmObj = nw4r::g3d::AnmObjChrRes::Construct(&mAllocator, &tmp, anm, mdl.getResMdl(), false);
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(anm, mode);
+
+    if (parent != nullptr) {
+        nw4r::g3d::AnmObjChrRes *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrRes>(mpAnmObj);
+        node->SetWeight(idx, weight);
+        node->Attach(idx, o);
+
+    }
+
 }
 
 void anmChr_c::setFrmCtrlDefault(nw4r::g3d::ResAnmChr &anm, m3d::playMode_e mode) {
-    if (mode == 4) {
-        // TODO g3d headers
+    if (mode == PLAY_MODE_4) {
+        mode = anm.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
     }
-    set(1.0f, mode, 1.0f, -1.0f);
+    set(anm.GetNumFrame(), mode, 1.0f, -1.0f);
 }
 
 } // namespace m3d

From 15a81771239416458d6e301f74f930b3a763efb6 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 21:23:34 +0200
Subject: [PATCH 08/33] m_anmChrBlend OK

---
 config/SOUE01/splits.txt      |  4 +++
 config/SOUE01/symbols.txt     | 22 +++++++-------
 configure.py                  |  1 +
 include/m/m3d/m_anmchrblend.h | 28 ++++++++++++++++++
 include/nw4r/g3d/g3d_anmchr.h |  8 +++--
 src/m/m3d/m_anmchrblend.cpp   | 56 +++++++++++++++++++++++++++++++++++
 6 files changed, 105 insertions(+), 14 deletions(-)
 create mode 100644 include/m/m3d/m_anmchrblend.h
 create mode 100644 src/m/m3d/m_anmchrblend.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 46d1b97d..0fc90051 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -280,6 +280,10 @@ m/m3d/m_anmchr.cpp:
 	.data       start:0x805424D0 end:0x805424E8
 	.sdata2     start:0x8057CCC0 end:0x8057CCD0
 
+m/m3d/m_anmchrblend.cpp:
+	.text       start:0x802E4DA0 end:0x802E5260
+	.data       start:0x805424E8 end:0x80542500
+
 m/m3d/m_anmtexsrt.cpp:
 	.text       start:0x802E6EC0 end:0x802E7A54
 	.data       start:0x80542598 end:0x805425C8
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 694fb18d..d3b894bd 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17446,14 +17446,14 @@ create__Q23m3d8anmChr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmChrP12mAllocator_cPU
 setAnm__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4A70; // type:function size:0x84
 setAnmAfter__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4B00; // type:function size:0x22C
 setFrmCtrlDefault__Q23m3d8anmChr_cFRQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4D30; // type:function size:0x68
-fn_802E4DA0 = .text:0x802E4DA0; // type:function size:0x58
-fn_802E4E00 = .text:0x802E4E00; // type:function size:0x8
-fn_802E4E10 = .text:0x802E4E10; // type:function size:0xB8
-fn_802E4ED0 = .text:0x802E4ED0; // type:function size:0xD8
-fn_802E4FB0 = .text:0x802E4FB0; // type:function size:0xB4
-fn_802E5070 = .text:0x802E5070; // type:function size:0xA0
-fn_802E5110 = .text:0x802E5110; // type:function size:0xB0
-fn_802E51C0 = .text:0x802E51C0; // type:function size:0xA0
+__dt__Q23m3d13anmChrBlend_cFv = .text:0x802E4DA0; // type:function size:0x58
+getType__Q23m3d13anmChrBlend_cFv = .text:0x802E4E00; // type:function size:0x8
+create__Q23m3d13anmChrBlend_cFQ34nw4r3g3d6ResMdliP12mAllocator_cPUl = .text:0x802E4E10; // type:function size:0xB8
+attach__Q23m3d13anmChrBlend_cFiPQ34nw4r3g3d12AnmObjChrResf = .text:0x802E4ED0; // type:function size:0xD8
+attach__Q23m3d13anmChrBlend_cFiPQ23m3d8anmChr_cf = .text:0x802E4FB0; // type:function size:0xB4
+detach__Q23m3d13anmChrBlend_cFi = .text:0x802E5070; // type:function size:0xA0
+setWeight__Q23m3d13anmChrBlend_cFif = .text:0x802E5110; // type:function size:0xB0
+getWeight__Q23m3d13anmChrBlend_cCFi = .text:0x802E51C0; // type:function size:0xA0
 fn_802E5260 = .text:0x802E5260; // type:function size:0x58
 fn_802E52C0 = .text:0x802E52C0; // type:function size:0x8
 fn_802E52D0 = .text:0x802E52D0; // type:function size:0x7C
@@ -24609,7 +24609,7 @@ fn_804480C0 = .text:0x804480C0; // type:function size:0x104
 fn_804481D0 = .text:0x804481D0; // type:function size:0xB8
 fn_80448290 = .text:0x80448290; // type:function size:0x148
 fn_804483E0 = .text:0x804483E0; // type:function size:0x7C
-fn_80448460 = .text:0x80448460; // type:function size:0x178
+Construct__Q34nw4r3g3d14AnmObjChrBlendFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdli = .text:0x80448460; // type:function size:0x178
 fn_804485E0 = .text:0x804485E0; // type:function size:0x640
 fn_80448C20 = .text:0x80448C20; // type:function size:0x10
 fn_80448C30 = .text:0x80448C30; // type:function size:0x10
@@ -29103,7 +29103,7 @@ TYPE_NAME__Q34nw4r3g3d20AnmObjTexSrtOverride = .rodata:0x804F7198; // type:objec
 TYPE_NAME__Q34nw4r3g3d15AnmObjTexSrtRes = .rodata:0x804F71B8; // type:object size:0x18
 lbl_804F71D0 = .rodata:0x804F71D0; // type:object size:0x10
 TYPE_NAME__Q34nw4r3g3d13AnmObjChrNode = .rodata:0x804F71E0; // type:object size:0x14
-lbl_804F71F4 = .rodata:0x804F71F4; // type:object size:0x14
+TYPE_NAME__Q34nw4r3g3d14AnmObjChrBlend = .rodata:0x804F71F4; // type:object size:0x14
 TYPE_NAME__Q34nw4r3g3d12AnmObjChrRes = .rodata:0x804F7208; // type:object size:0x18
 lbl_804F7220 = .rodata:0x804F7220; // type:object size:0x10
 lbl_804F7230 = .rodata:0x804F7230; // type:object size:0x18
@@ -35686,7 +35686,7 @@ lbl_80542448 = .data:0x80542448; // type:object size:0x18
 lbl_80542460 = .data:0x80542460; // type:object size:0x40
 lbl_805424A0 = .data:0x805424A0; // type:object size:0x30
 __vt__Q23m3d8anmChr_c = .data:0x805424D0; // type:object size:0x18
-lbl_805424E8 = .data:0x805424E8; // type:object size:0x18
+__vt__Q23m3d13anmChrBlend_c = .data:0x805424E8; // type:object size:0x18
 lbl_80542500 = .data:0x80542500; // type:object size:0x18
 lbl_80542518 = .data:0x80542518; // type:object size:0x18
 lbl_80542530 = .data:0x80542530; // type:object size:0x20
diff --git a/configure.py b/configure.py
index 0ae97f98..dae7e318 100644
--- a/configure.py
+++ b/configure.py
@@ -337,6 +337,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "f/f_manager.cpp"),
             Object(NonMatching, "m/m3d/m3d.cpp"),
             Object(Matching, "m/m3d/m_anmchr.cpp"),
+            Object(Matching, "m/m3d/m_anmchrblend.cpp"),
             Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
diff --git a/include/m/m3d/m_anmchrblend.h b/include/m/m3d/m_anmchrblend.h
new file mode 100644
index 00000000..0852d2c5
--- /dev/null
+++ b/include/m/m3d/m_anmchrblend.h
@@ -0,0 +1,28 @@
+#ifndef M3D_M_ANMCHRBLEND_H
+#define M3D_M_ANMCHRBLEND_H
+
+#include <m/m3d/m_anmchr.h>
+#include <m/m3d/m_banm.h>
+#include <m/m3d/m_bmdl.h>
+#include <nw4r/g3d/g3d_anmchr.h>
+
+namespace m3d {
+
+class anmChrBlend_c : public banm_c {
+public:
+    virtual ~anmChrBlend_c();
+
+    virtual int getType() override;
+
+    bool create(nw4r::g3d::ResMdl, int, mAllocator_c *, u32 *);
+    bool attach(int, nw4r::g3d::AnmObjChrRes *, f32);
+    bool attach(int, anmChr_c *, f32);
+    void detach(int);
+    // Not in NSMBW
+    void setWeight(int, f32);
+    f32 getWeight(int) const;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/nw4r/g3d/g3d_anmchr.h b/include/nw4r/g3d/g3d_anmchr.h
index c43b4e18..49cc40c8 100644
--- a/include/nw4r/g3d/g3d_anmchr.h
+++ b/include/nw4r/g3d/g3d_anmchr.h
@@ -63,23 +63,25 @@ class AnmObjChrNode : public AnmObjChr {
     NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrNode);
 };
 
-struct AnmObjChrBlend : public AnmObjChrNode {
+class AnmObjChrBlend : public AnmObjChrNode {
 public:
     static const TypeObj GetTypeObjStatic() {
         return TypeObj(TYPE_NAME);
     }
 
+    static AnmObjChrBlend *Construct(MEMAllocator*, u32*, ResMdl, int);
+
 protected:
     NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrBlend);
 };
 
-struct AnmObjChrRes : public AnmObjChr, public FrameCtrl {
+class AnmObjChrRes : public AnmObjChr, public FrameCtrl {
 public:
     static const TypeObj GetTypeObjStatic() {
         return TypeObj(TYPE_NAME);
     }
 
-    static AnmObjChrRes *Construct(MEMAllocator*, u32*, nw4r::g3d::ResAnmChr, nw4r::g3d::ResMdl, bool);
+    static AnmObjChrRes *Construct(MEMAllocator*, u32*, ResAnmChr, ResMdl, bool);
 
 protected:
     NW4R_G3D_TYPE_OBJ_DECL(AnmObjChrRes);
diff --git a/src/m/m3d/m_anmchrblend.cpp b/src/m/m3d/m_anmchrblend.cpp
new file mode 100644
index 00000000..a0313840
--- /dev/null
+++ b/src/m/m3d/m_anmchrblend.cpp
@@ -0,0 +1,56 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmchrblend.h>
+#include <nw4r/g3d/g3d_anmchr.h>
+#include <nw4r/g3d/g3d_resanmchr.h>
+
+namespace m3d {
+
+anmChrBlend_c::~anmChrBlend_c() {}
+
+int anmChrBlend_c::getType() {
+    return 0;
+}
+
+bool anmChrBlend_c::create(nw4r::g3d::ResMdl mdl, int num, mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    nw4r::g3d::AnmObjChrBlend::Construct(nullptr, pSize, mdl, num);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjChrBlend::Construct(&mAllocator, &size, mdl, num);
+    return true;
+}
+
+bool anmChrBlend_c::attach(int idx, nw4r::g3d::AnmObjChrRes *anm, f32 weight) {
+    nw4r::g3d::AnmObjChrBlend *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj);
+    obj->SetWeight(idx, weight);
+    obj->Attach(idx, anm);
+}
+
+bool anmChrBlend_c::attach(int idx, anmChr_c *anm, f32 weight) {
+    nw4r::g3d::AnmObjChrRes *res = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrRes>(anm->getAnimObj());
+    attach(idx, res, weight);
+}
+
+void anmChrBlend_c::detach(int idx) {
+    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj)->Detach(idx);
+}
+
+void anmChrBlend_c::setWeight(int idx, f32 weight) {
+    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj)->SetWeight(idx, weight);
+}
+
+f32 anmChrBlend_c::getWeight(int idx) const {
+    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj)->GetWeight(idx);
+}
+
+} // namespace m3d

From a4d16c868e15bd775fa6e357300513ebed236336 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 22:12:44 +0200
Subject: [PATCH 09/33] m_anmMatClr_c OK

---
 config/SOUE01/splits.txt         |   5 +
 config/SOUE01/symbols.txt        |  62 +++++-----
 configure.py                     |   1 +
 include/m/m3d/m_anmmatclr.h      |  57 +++++++++
 include/nw4r/g3d/g3d_anmclr.h    |  69 +++++++++++
 include/nw4r/g3d/g3d_resanmclr.h |  13 ++
 src/m/m3d/m_anmmatclr.cpp        | 203 +++++++++++++++++++++++++++++++
 7 files changed, 379 insertions(+), 31 deletions(-)
 create mode 100644 include/m/m3d/m_anmmatclr.h
 create mode 100644 src/m/m3d/m_anmmatclr.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 0fc90051..457a3d9c 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -284,6 +284,11 @@ m/m3d/m_anmchrblend.cpp:
 	.text       start:0x802E4DA0 end:0x802E5260
 	.data       start:0x805424E8 end:0x80542500
 
+m/m3d/m_anmmatclr.cpp:
+	.text       start:0x802E5260 end:0x802E5D94
+	.data       start:0x80542500 end:0x80542530
+	.sdata2     start:0x8057CCD0 end:0x8057CCE0
+
 m/m3d/m_anmtexsrt.cpp:
 	.text       start:0x802E6EC0 end:0x802E7A54
 	.data       start:0x80542598 end:0x805425C8
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index d3b894bd..080eb71a 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17454,30 +17454,30 @@ attach__Q23m3d13anmChrBlend_cFiPQ23m3d8anmChr_cf = .text:0x802E4FB0; // type:fun
 detach__Q23m3d13anmChrBlend_cFi = .text:0x802E5070; // type:function size:0xA0
 setWeight__Q23m3d13anmChrBlend_cFif = .text:0x802E5110; // type:function size:0xB0
 getWeight__Q23m3d13anmChrBlend_cCFi = .text:0x802E51C0; // type:function size:0xA0
-fn_802E5260 = .text:0x802E5260; // type:function size:0x58
-fn_802E52C0 = .text:0x802E52C0; // type:function size:0x8
-fn_802E52D0 = .text:0x802E52D0; // type:function size:0x7C
-fn_802E5350 = .text:0x802E5350; // type:function size:0x11C
-fn_802E5470 = .text:0x802E5470; // type:function size:0xB4
-fn_802E5530 = .text:0x802E5530; // type:function size:0x5C
-fn_802E5590 = .text:0x802E5590; // type:function size:0x68
-fn_802E5600 = .text:0x802E5600; // type:function size:0xE0
-fn_802E56E0 = .text:0x802E56E0; // type:function size:0x23C
-fn_802E5920 = .text:0x802E5920; // type:function size:0x6C
-fn_802E5990 = .text:0x802E5990; // type:function size:0x8
-fn_802E59A0 = .text:0x802E59A0; // type:function size:0xEC
-fn_802E5A90 = .text:0x802E5A90; // type:function size:0x140
-fn_802E5BD0 = .text:0x802E5BD0; // type:function size:0xAC
-fn_802E5C80 = .text:0x802E5C80; // type:function size:0x60
-fn_802E5CE0 = .text:0x802E5CE0; // type:function size:0x10
-fn_802E5CF0 = .text:0x802E5CF0; // type:function size:0x10
-fn_802E5D00 = .text:0x802E5D00; // type:function size:0x10
-fn_802E5D10 = .text:0x802E5D10; // type:function size:0x10
-fn_802E5D20 = .text:0x802E5D20; // type:function size:0x10
-fn_802E5D30 = .text:0x802E5D30; // type:function size:0x10
-fn_802E5D40 = .text:0x802E5D40; // type:function size:0x14
-fn_802E5D60 = .text:0x802E5D60; // type:function size:0x14
-fn_802E5D80 = .text:0x802E5D80; // type:function size:0x14
+__dt__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E5260; // type:function size:0x58
+getType__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E52C0; // type:function size:0x8
+heapCost__Q33m3d11anmMatClr_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrb = .text:0x802E52D0; // type:function size:0x7C
+create__Q33m3d11anmMatClr_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrP12mAllocator_cPUl = .text:0x802E5350; // type:function size:0x11C
+setAnm__Q33m3d11anmMatClr_c7child_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmClrQ23m3d10playMode_e = .text:0x802E5470; // type:function size:0xB4
+releaseAnm__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E5530; // type:function size:0x5C
+setFrmCtrlDefault__Q33m3d11anmMatClr_c7child_cFRQ34nw4r3g3d9ResAnmClrQ23m3d10playMode_e = .text:0x802E5590; // type:function size:0x68
+heapCost__Q23m3d11anmMatClr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrlb = .text:0x802E5600; // type:function size:0xE0
+create__Q23m3d11anmMatClr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrP12mAllocator_cPUll = .text:0x802E56E0; // type:function size:0x23C
+__dt__Q23m3d11anmMatClr_cFv = .text:0x802E5920; // type:function size:0x6C
+getType__Q23m3d11anmMatClr_cFv = .text:0x802E5990; // type:function size:0x8
+remove__Q23m3d11anmMatClr_cFv = .text:0x802E59A0; // type:function size:0xEC
+setAnm__Q23m3d11anmMatClr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmClrlQ23m3d10playMode_e = .text:0x802E5A90; // type:function size:0x140
+play__Q23m3d11anmMatClr_cFv = .text:0x802E5BD0; // type:function size:0xAC
+play__Q23m3d11anmMatClr_cFl = .text:0x802E5C80; // type:function size:0x60
+getFrame__Q23m3d11anmMatClr_cCFl = .text:0x802E5CE0; // type:function size:0x10
+setFrame__Q23m3d11anmMatClr_cFfl = .text:0x802E5CF0; // type:function size:0x10
+getRate__Q23m3d11anmMatClr_cCFl = .text:0x802E5D00; // type:function size:0x10
+setRate__Q23m3d11anmMatClr_cFfl = .text:0x802E5D10; // type:function size:0x10
+isStop__Q23m3d11anmMatClr_cCFl = .text:0x802E5D20; // type:function size:0x10
+checkFrame__Q23m3d11anmMatClr_cCFfl = .text:0x802E5D30; // type:function size:0x10
+setPlayMode__Q23m3d11anmMatClr_cFQ23m3d10playMode_el = .text:0x802E5D40; // type:function size:0x14
+getFrameMax__Q23m3d11anmMatClr_cCFl = .text:0x802E5D60; // type:function size:0x14
+getFrameStart__Q23m3d11anmMatClr_cCFl = .text:0x802E5D80; // type:function size:0x14
 fn_802E5DA0 = .text:0x802E5DA0; // type:function size:0x68
 fn_802E5E10 = .text:0x802E5E10; // type:function size:0x14
 fn_802E5E30 = .text:0x802E5E30; // type:function size:0x8
@@ -17494,7 +17494,7 @@ fn_802E6140 = .text:0x802E6140; // type:function size:0x120
 fn_802E6260 = .text:0x802E6260; // type:function size:0xEC
 fn_802E6350 = .text:0x802E6350; // type:function size:0x68
 fn_802E63C0 = .text:0x802E63C0; // type:function size:0x8
-__dt__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E63D0; // type:function size:0x58
+fn_802E63D0 = .text:0x802E63D0; // type:function size:0x58
 heapCost__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatb = .text:0x802E6430; // type:function size:0x7C
 create__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatP12mAllocator_cPUl = .text:0x802E64B0; // type:function size:0x11C
 fn_802E65D0 = .text:0x802E65D0; // type:function size:0xB4
@@ -24473,9 +24473,9 @@ fn_80443B40 = .text:0x80443B40; // type:function size:0x54
 fn_80443BA0 = .text:0x80443BA0; // type:function size:0xF4
 fn_80443CA0 = .text:0x80443CA0; // type:function size:0xB8
 fn_80443D60 = .text:0x80443D60; // type:function size:0x7C
-fn_80443DE0 = .text:0x80443DE0; // type:function size:0x134
+Construct__Q34nw4r3g3d20AnmObjMatClrOverrideFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdli = .text:0x80443DE0; // type:function size:0x134
 fn_80443F20 = .text:0x80443F20; // type:function size:0xB0
-fn_80443FD0 = .text:0x80443FD0; // type:function size:0x1F0
+Construct__Q34nw4r3g3d15AnmObjMatClrResFP12MEMAllocatorPUlQ34nw4r3g3d9ResAnmClrQ34nw4r3g3d6ResMdlb = .text:0x80443FD0; // type:function size:0x1F0
 fn_804441C0 = .text:0x804441C0; // type:function size:0xC0
 fn_80444280 = .text:0x80444280; // type:function size:0x8
 fn_80444290 = .text:0x80444290; // type:function size:0xB4
@@ -29091,8 +29091,8 @@ lbl_804F7070 = .rodata:0x804F7070; // type:object size:0x10
 lbl_804F7080 = .rodata:0x804F7080; // type:object size:0x18
 lbl_804F7098 = .rodata:0x804F7098; // type:object size:0x18
 lbl_804F70B0 = .rodata:0x804F70B0; // type:object size:0x18
-lbl_804F70C8 = .rodata:0x804F70C8; // type:object size:0x20
-lbl_804F70E8 = .rodata:0x804F70E8; // type:object size:0x18
+TYPE_NAME__Q34nw4r3g3d20AnmObjMatClrOverride = .rodata:0x804F70C8; // type:object size:0x20
+TYPE_NAME__Q34nw4r3g3d15AnmObjMatClrRes = .rodata:0x804F70E8; // type:object size:0x18
 lbl_804F7100 = .rodata:0x804F7100; // type:object size:0x18
 lbl_804F7118 = .rodata:0x804F7118; // type:object size:0x18
 lbl_804F7130 = .rodata:0x804F7130; // type:object size:0x20
@@ -35687,8 +35687,8 @@ lbl_80542460 = .data:0x80542460; // type:object size:0x40
 lbl_805424A0 = .data:0x805424A0; // type:object size:0x30
 __vt__Q23m3d8anmChr_c = .data:0x805424D0; // type:object size:0x18
 __vt__Q23m3d13anmChrBlend_c = .data:0x805424E8; // type:object size:0x18
-lbl_80542500 = .data:0x80542500; // type:object size:0x18
-lbl_80542518 = .data:0x80542518; // type:object size:0x18
+__vt__Q23m3d11anmMatClr_c = .data:0x80542500; // type:object size:0x18
+__vt__Q33m3d11anmMatClr_c7child_c = .data:0x80542518; // type:object size:0x18
 lbl_80542530 = .data:0x80542530; // type:object size:0x20
 lbl_80542550 = .data:0x80542550; // type:object size:0x18
 lbl_80542568 = .data:0x80542568; // type:object size:0x18
diff --git a/configure.py b/configure.py
index dae7e318..3862c6d8 100644
--- a/configure.py
+++ b/configure.py
@@ -339,6 +339,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmchrblend.cpp"),
             Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
+            Object(Matching, "m/m3d/m_anmmatclr.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
diff --git a/include/m/m3d/m_anmmatclr.h b/include/m/m3d/m_anmmatclr.h
new file mode 100644
index 00000000..c12bf296
--- /dev/null
+++ b/include/m/m3d/m_anmmatclr.h
@@ -0,0 +1,57 @@
+#ifndef M3D_M_ANMTEXSRT_H
+#define M3D_M_ANMTEXSRT_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+#include <nw4r/g3d/g3d_anmclr.h>
+
+namespace m3d {
+
+class anmMatClr_c : public banm_c {
+    class child_c : public fanm_c {
+    public:
+        child_c() {}
+        virtual ~child_c();
+        virtual int getType() override;
+
+        static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, bool);
+        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, mAllocator_c *, u32 *);
+        void setAnm(bmdl_c &, nw4r::g3d::ResAnmClr, playMode_e);
+        void releaseAnm();
+        void setFrmCtrlDefault(nw4r::g3d::ResAnmClr &, playMode_e);
+    };
+
+public:
+    anmMatClr_c() : mpChildren(nullptr) {}
+    virtual ~anmMatClr_c();
+
+    virtual int getType() override;
+    virtual void remove() override;
+    virtual void play() override;
+
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, mAllocator_c *, u32 *, s32);
+    static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, s32, bool);
+
+    void setAnm(bmdl_c &, nw4r::g3d::ResAnmClr, s32, playMode_e);
+
+    void play(s32);
+    f32 getFrame(s32) const;
+    void setFrame(f32, s32);
+    f32 getRate(s32) const;
+    void setRate(f32, s32);
+    bool isStop(s32) const;
+
+    bool checkFrame(f32, s32) const;
+
+    void setPlayMode(playMode_e, s32);
+    f32 getFrameMax(s32) const;
+    void setFrameStart(f32, s32);
+    f32 getFrameStart(s32) const;
+
+private:
+    child_c *mpChildren;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/nw4r/g3d/g3d_anmclr.h b/include/nw4r/g3d/g3d_anmclr.h
index 6a009f19..892abe70 100644
--- a/include/nw4r/g3d/g3d_anmclr.h
+++ b/include/nw4r/g3d/g3d_anmclr.h
@@ -1,6 +1,8 @@
 #ifndef NW4R_G3D_ANMCLR_H
 #define NW4R_G3D_ANMCLR_H
 #include "common.h"
+#include "nw4r/g3d/g3d_anmobj.h"
+#include "nw4r/g3d/g3d_resanmclr.h"
 
 namespace nw4r {
 namespace g3d {
@@ -11,6 +13,73 @@ struct ClrAnmResult {
     u32 COLORS_0x4[ANM_COUNT];
     UNKWORD WORDS_0x30[ANM_COUNT];
 };
+
+class AnmObjMatClrRes;
+
+class AnmObjMatClr : public AnmObj {
+public:
+    static const G3dObj::TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    virtual ClrAnmResult *GetResult();              // at 0x38
+    virtual void Attach(s32, AnmObjMatClrRes *res); // at 0x3C
+    virtual void Detach(s32);                       // at 0x40
+
+private:
+    int mChildrenArraySize;
+    u16 *mpChildrenArray;
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjMatClr);
+};
+
+class AnmObjMatClrNode : public AnmObjMatClr {
+public:
+    static const G3dObj::TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    inline int Size() {
+        return mNodeArraySize;
+    }
+
+    AnmObjMatClrRes *GetNode(int i) {
+        return mpNodes[i];
+    }
+
+private:
+    int mNodeArraySize;
+    AnmObjMatClrRes **mpNodes;
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjMatClrNode);
+};
+
+class AnmObjMatClrRes : public AnmObjMatClr, public FrameCtrl {
+public:
+    static const G3dObj::TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    static AnmObjMatClrRes *Construct(MEMAllocator *, u32 *, ResAnmClr, ResMdl, bool);
+
+private:
+    ResAnmClr mRes;
+    TexSrtAnmResult *mpResultCache;
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjMatClrRes);
+};
+
+class AnmObjMatClrOverride : public AnmObjMatClrNode {
+public:
+    static const G3dObj::TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    static AnmObjMatClrOverride *Construct(MEMAllocator *, u32 *, ResMdl, int);
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjMatClrOverride);
+};
+
 } // namespace g3d
 } // namespace nw4r
 
diff --git a/include/nw4r/g3d/g3d_resanmclr.h b/include/nw4r/g3d/g3d_resanmclr.h
index 85c5327c..78feba26 100644
--- a/include/nw4r/g3d/g3d_resanmclr.h
+++ b/include/nw4r/g3d/g3d_resanmclr.h
@@ -33,6 +33,9 @@ namespace nw4r
 			char UNK_0xC[0x4];
 			s32 mMatDictOffset; // at 0x10
 			ResAnmClrInfoData mInfo; // at 0x14
+			char UNK_0x1E[0x2];
+			u16 mNumFrames; // at 0x20
+			AnmPolicy mAnmPolicy; // at 0x24
 		};
 		
 		struct ResAnmClr
@@ -55,6 +58,16 @@ namespace nw4r
 			{
 				return ref().mRevision == REVISION;
 			}
+
+			AnmPolicy GetAnmPolicy() const
+			{
+				return ref().mAnmPolicy;
+			}
+
+			int GetNumFrame() const
+			{
+				return ref().mNumFrames;
+			}
 			
 			inline const ResAnmClrMatData * GetMatAnm(u32 i) const
 			{
diff --git a/src/m/m3d/m_anmmatclr.cpp b/src/m/m3d/m_anmmatclr.cpp
new file mode 100644
index 00000000..64012032
--- /dev/null
+++ b/src/m/m3d/m_anmmatclr.cpp
@@ -0,0 +1,203 @@
+#include <egg/core/eggFrmHeap.h>
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmmatclr.h>
+#include <m/m_heap.h>
+
+namespace m3d {
+
+anmMatClr_c::child_c::~child_c() {}
+
+int anmMatClr_c::child_c::getType() {
+    return 0x02;
+}
+
+u32 anmMatClr_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmClr clr, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjMatClrRes::Construct(nullptr, &size, clr, mdl, false);
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmMatClr_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmClr clr, mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = child_c::heapCost(mdl, clr, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjMatClrRes::Construct(&mAllocator, nullptr, clr, mdl, false);
+
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    }
+    setFrmCtrlDefault(clr, PLAY_MODE_4);
+    return true;
+}
+
+void anmMatClr_c::child_c::setAnm(m3d::bmdl_c &mdl, nw4r::g3d::ResAnmClr clr, m3d::playMode_e mode) {
+    releaseAnm();
+    mpAnmObj = nw4r::g3d::AnmObjMatClrRes::Construct(&mAllocator, nullptr, clr, mdl.getResMdl(), false);
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(clr, mode);
+}
+
+void anmMatClr_c::child_c::releaseAnm() {
+    if (mpAnmObj != nullptr) {
+        mpAnmObj->Release();
+        mpFrameHeap->free(3);
+        mpAnmObj = nullptr;
+    }
+}
+
+void anmMatClr_c::child_c::setFrmCtrlDefault(nw4r::g3d::ResAnmClr &clr, playMode_e mode) {
+    if (mode == PLAY_MODE_4) {
+        mode = clr.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
+    }
+    set(clr.GetNumFrame(), mode, 1.0f, -1.0f);
+}
+
+u32 anmMatClr_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmClr clr, s32 num, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjMatClrOverride::Construct(nullptr, &size, mdl, num);
+    size += ROUND_UP(num * sizeof(child_c), 0x20);
+    u32 childCost = child_c::heapCost(mdl, clr, true);
+    size += num * ROUND_UP(childCost, 0x20);
+
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmMatClr_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmClr clr, mAllocator_c *alloc, u32 *pSize, s32 num) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size = 0;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = heapCost(mdl, clr, num, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjMatClrOverride::Construct(&mAllocator, nullptr, mdl, num);
+
+    // TODO inline?
+    mpChildren = (child_c *)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
+
+    nw4r::g3d::AnmObjMatClrOverride *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrOverride>(mpAnmObj);
+    child_c *child = mpChildren;
+    for (int i = 0; i < num; i++) {
+        new (child) child_c();
+        if (!child->create(mdl, clr, &mAllocator, nullptr)) {
+            mHeap::destroyFrmHeap(mpFrameHeap);
+            return false;
+        }
+
+        if (i == 0) {
+            obj->Attach(i, nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrRes>(child->getAnimObj()));
+        } else {
+            child->releaseAnm();
+        }
+        child++;
+    }
+
+    return true;
+}
+
+anmMatClr_c::~anmMatClr_c() {
+    anmMatClr_c::remove();
+}
+
+int anmMatClr_c::getType() {
+    return 0x02;
+}
+
+void anmMatClr_c::remove() {
+    nw4r::g3d::AnmObjMatClrOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrOverride>(mpAnmObj);
+    if (o != nullptr && mpChildren != 0) {
+        int size = o->Size();
+        for (int i = 0; i < size; i++) {
+            mpChildren[i].remove();
+        }
+        mpChildren = nullptr;
+    }
+    banm_c::remove();
+}
+
+void anmMatClr_c::setAnm(bmdl_c &mdl, nw4r::g3d::ResAnmClr clr, s32 idx, playMode_e mode) {
+    nw4r::g3d::AnmObjMatClrOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrOverride>(mpAnmObj);
+    o->Detach(idx);
+    mpChildren[idx].setAnm(mdl, clr, mode);
+
+    nw4r::g3d::AnmObjMatClrRes *res =
+            nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrRes>(mpChildren[idx].getAnimObj());
+    o->Attach(idx, res);
+}
+
+void anmMatClr_c::play() {
+    nw4r::g3d::AnmObjMatClrOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjMatClrOverride>(mpAnmObj);
+    int size = o->Size();
+    for (int i = 0; i < size; i++) {
+        play(i);
+    }
+}
+
+void anmMatClr_c::play(s32 idx) {
+    if (mpChildren[idx].IsBound()) {
+        mpChildren[idx].play();
+    }
+}
+
+f32 anmMatClr_c::getFrame(s32 idx) const {
+    return mpChildren[idx].getFrame();
+}
+
+void anmMatClr_c::setFrame(f32 f, s32 idx) {
+    mpChildren[idx].setFrameOnly(f);
+}
+
+f32 anmMatClr_c::getRate(s32 idx) const {
+    mpChildren[idx].getRate();
+}
+
+void anmMatClr_c::setRate(f32 f, s32 idx) {
+    mpChildren[idx].setRate(f);
+}
+
+bool anmMatClr_c::isStop(s32 idx) const {
+    return mpChildren[idx].isStop();
+}
+
+bool anmMatClr_c::checkFrame(f32 f, s32 idx) const {
+    return mpChildren[idx].checkFrame(f);
+}
+
+void anmMatClr_c::setPlayMode(playMode_e mode, s32 idx) {
+    mpChildren[idx].setPlayState(mode);
+}
+
+f32 anmMatClr_c::getFrameMax(s32 idx) const {
+    return mpChildren[idx].getEndFrame();
+}
+
+f32 anmMatClr_c::getFrameStart(s32 idx) const {
+    return mpChildren[idx].getStartFrame();
+}
+
+} // namespace m3d

From 144c874b00e5d4ac8fbe0f54ba357cf9582e6171 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 22:57:10 +0200
Subject: [PATCH 10/33] anmTexPat_c OK

---
 config/SOUE01/splits.txt            |   8 ++
 config/SOUE01/symbols.txt           |  36 +++---
 configure.py                        |   2 +
 include/m/m3d/m_anmchr.h            |   2 +-
 include/m/m3d/m_anmchrblend.h       |   2 +-
 include/m/m3d/m_anmmatclr.h         |   4 +-
 include/m/m3d/m_anmtexpat.h         |  52 ++++++++
 include/m/m3d/m_anmtexsrt.h         |   4 +-
 include/m/m3d/m_anmvis.h            |   2 +-
 include/m/m3d/m_banm.h              |   2 +-
 include/m/m3d/m_fanm.h              |   2 +-
 include/nw4r/g3d/g3d_anmtexpat.h    |  70 ++++++++++
 include/nw4r/g3d/g3d_resanmtexpat.h |  12 ++
 src/m/m3d/m_anmchr.cpp              |   2 +-
 src/m/m3d/m_anmchrblend.cpp         |   2 +-
 src/m/m3d/m_anmmatclr.cpp           |   4 +-
 src/m/m3d/m_anmtexpat.cpp           | 191 ++++++++++++++++++++++++++++
 src/m/m3d/m_anmtexsrt.cpp           |   4 +-
 src/m/m3d/m_anmvis.cpp              |   2 +-
 19 files changed, 369 insertions(+), 34 deletions(-)
 create mode 100644 include/m/m3d/m_anmtexpat.h
 create mode 100644 src/m/m3d/m_anmtexpat.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 457a3d9c..3a5c16eb 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -275,6 +275,9 @@ m/m3d/m3d.cpp:
 	.text       start:0x802E3E50 end:0x802E4714
 	.sbss       start:0x80575BD0 end:0x80575BE8
 
+m/m3d/m_proc.cpp:
+	.text       start:0x802E4720 end:0x802E48D4
+
 m/m3d/m_anmchr.cpp:
 	.text       start:0x802E48E0 end:0x802E4D98
 	.data       start:0x805424D0 end:0x805424E8
@@ -289,6 +292,11 @@ m/m3d/m_anmmatclr.cpp:
 	.data       start:0x80542500 end:0x80542530
 	.sdata2     start:0x8057CCD0 end:0x8057CCE0
 
+m/m3d/m_anmtexpat.cpp:
+	.text       start:0x802E63C0 end:0x802E6EB4
+	.data       start:0x80542568 end:0x80542598
+	.sdata2     start:0x8057CCF8 end:0x8057CD08
+
 m/m3d/m_anmtexsrt.cpp:
 	.text       start:0x802E6EC0 end:0x802E7A54
 	.data       start:0x80542598 end:0x805425C8
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 080eb71a..f932ced7 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17441,13 +17441,13 @@ fn_802E47C0 = .text:0x802E47C0; // type:function size:0x4
 fn_802E47D0 = .text:0x802E47D0; // type:function size:0x4
 create__Q23m3d6proc_cFP12mAllocator_cPUl = .text:0x802E47E0; // type:function size:0xF4
 __dt__Q23m3d8anmChr_cFv = .text:0x802E48E0; // type:function size:0x58
-getType__Q23m3d8anmChr_cFv = .text:0x802E4940; // type:function size:0x8
+getType__Q23m3d8anmChr_cCFv = .text:0x802E4940; // type:function size:0x8
 create__Q23m3d8anmChr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmChrP12mAllocator_cPUl = .text:0x802E4950; // type:function size:0x120
 setAnm__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4A70; // type:function size:0x84
 setAnmAfter__Q23m3d8anmChr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4B00; // type:function size:0x22C
 setFrmCtrlDefault__Q23m3d8anmChr_cFRQ34nw4r3g3d9ResAnmChrQ23m3d10playMode_e = .text:0x802E4D30; // type:function size:0x68
 __dt__Q23m3d13anmChrBlend_cFv = .text:0x802E4DA0; // type:function size:0x58
-getType__Q23m3d13anmChrBlend_cFv = .text:0x802E4E00; // type:function size:0x8
+getType__Q23m3d13anmChrBlend_cCFv = .text:0x802E4E00; // type:function size:0x8
 create__Q23m3d13anmChrBlend_cFQ34nw4r3g3d6ResMdliP12mAllocator_cPUl = .text:0x802E4E10; // type:function size:0xB8
 attach__Q23m3d13anmChrBlend_cFiPQ34nw4r3g3d12AnmObjChrResf = .text:0x802E4ED0; // type:function size:0xD8
 attach__Q23m3d13anmChrBlend_cFiPQ23m3d8anmChr_cf = .text:0x802E4FB0; // type:function size:0xB4
@@ -17455,7 +17455,7 @@ detach__Q23m3d13anmChrBlend_cFi = .text:0x802E5070; // type:function size:0xA0
 setWeight__Q23m3d13anmChrBlend_cFif = .text:0x802E5110; // type:function size:0xB0
 getWeight__Q23m3d13anmChrBlend_cCFi = .text:0x802E51C0; // type:function size:0xA0
 __dt__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E5260; // type:function size:0x58
-getType__Q33m3d11anmMatClr_c7child_cFv = .text:0x802E52C0; // type:function size:0x8
+getType__Q33m3d11anmMatClr_c7child_cCFv = .text:0x802E52C0; // type:function size:0x8
 heapCost__Q33m3d11anmMatClr_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrb = .text:0x802E52D0; // type:function size:0x7C
 create__Q33m3d11anmMatClr_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrP12mAllocator_cPUl = .text:0x802E5350; // type:function size:0x11C
 setAnm__Q33m3d11anmMatClr_c7child_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmClrQ23m3d10playMode_e = .text:0x802E5470; // type:function size:0xB4
@@ -17464,7 +17464,7 @@ setFrmCtrlDefault__Q33m3d11anmMatClr_c7child_cFRQ34nw4r3g3d9ResAnmClrQ23m3d10pla
 heapCost__Q23m3d11anmMatClr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrlb = .text:0x802E5600; // type:function size:0xE0
 create__Q23m3d11anmMatClr_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmClrP12mAllocator_cPUll = .text:0x802E56E0; // type:function size:0x23C
 __dt__Q23m3d11anmMatClr_cFv = .text:0x802E5920; // type:function size:0x6C
-getType__Q23m3d11anmMatClr_cFv = .text:0x802E5990; // type:function size:0x8
+getType__Q23m3d11anmMatClr_cCFv = .text:0x802E5990; // type:function size:0x8
 remove__Q23m3d11anmMatClr_cFv = .text:0x802E59A0; // type:function size:0xEC
 setAnm__Q23m3d11anmMatClr_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmClrlQ23m3d10playMode_e = .text:0x802E5A90; // type:function size:0x140
 play__Q23m3d11anmMatClr_cFv = .text:0x802E5BD0; // type:function size:0xAC
@@ -17493,17 +17493,17 @@ fn_802E6130 = .text:0x802E6130; // type:function size:0x8
 fn_802E6140 = .text:0x802E6140; // type:function size:0x120
 fn_802E6260 = .text:0x802E6260; // type:function size:0xEC
 fn_802E6350 = .text:0x802E6350; // type:function size:0x68
-fn_802E63C0 = .text:0x802E63C0; // type:function size:0x8
-fn_802E63D0 = .text:0x802E63D0; // type:function size:0x58
+getType__Q33m3d11anmTexPat_c7child_cCFv = .text:0x802E63C0; // type:function size:0x8
+__dt__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E63D0; // type:function size:0x58
 heapCost__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatb = .text:0x802E6430; // type:function size:0x7C
 create__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatP12mAllocator_cPUl = .text:0x802E64B0; // type:function size:0x11C
-fn_802E65D0 = .text:0x802E65D0; // type:function size:0xB4
+setAnm__Q33m3d11anmTexPat_c7child_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexPatQ23m3d10playMode_e = .text:0x802E65D0; // type:function size:0xB4
 releaseAnm__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E6690; // type:function size:0x5C
 setFrmCtrlDefault__Q33m3d11anmTexPat_c7child_cFRQ34nw4r3g3d12ResAnmTexPatQ23m3d10playMode_e = .text:0x802E66F0; // type:function size:0x68
 heapCost__Q23m3d11anmTexPat_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatlb = .text:0x802E6760; // type:function size:0xE0
 create__Q23m3d11anmTexPat_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatP12mAllocator_cPUll = .text:0x802E6840; // type:function size:0x23C
 __dt__Q23m3d11anmTexPat_cFv = .text:0x802E6A80; // type:function size:0x6C
-fn_802E6AF0 = .text:0x802E6AF0; // type:function size:0x8
+getType__Q23m3d11anmTexPat_cCFv = .text:0x802E6AF0; // type:function size:0x8
 remove__Q23m3d11anmTexPat_cFv = .text:0x802E6B00; // type:function size:0xEC
 setAnm__Q23m3d11anmTexPat_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexPatlQ23m3d10playMode_e = .text:0x802E6BF0; // type:function size:0x140
 play__Q23m3d11anmTexPat_cFv = .text:0x802E6D30; // type:function size:0xAC
@@ -17513,8 +17513,8 @@ setFrame__Q23m3d11anmTexPat_cFfl = .text:0x802E6E50; // type:function size:0x10
 setRate__Q23m3d11anmTexPat_cFfl = .text:0x802E6E60; // type:function size:0x10
 isStop__Q23m3d11anmTexPat_cCFl = .text:0x802E6E70; // type:function size:0x10
 getFrameMax__Q23m3d11anmTexPat_cCFl = .text:0x802E6E80; // type:function size:0x14
-fn_802E6EA0 = .text:0x802E6EA0; // type:function size:0x14
-getType__Q33m3d11anmTexSrt_c7child_cFv = .text:0x802E6EC0; // type:function size:0x8
+setFrameStart__Q23m3d11anmTexPat_cFfl = .text:0x802E6EA0; // type:function size:0x14
+getType__Q33m3d11anmTexSrt_c7child_cCFv = .text:0x802E6EC0; // type:function size:0x8
 __dt__Q33m3d11anmTexSrt_c7child_cFv = .text:0x802E6ED0; // type:function size:0x58
 heapCost__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtb = .text:0x802E6F30; // type:function size:0x7C
 create__Q33m3d11anmTexSrt_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUl = .text:0x802E6FB0; // type:function size:0x12C
@@ -17524,7 +17524,7 @@ setFrmCtrlDefault__Q33m3d11anmTexSrt_c7child_cFRQ34nw4r3g3d12ResAnmTexSrtQ23m3d1
 heapCost__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtlb = .text:0x802E7270; // type:function size:0xE0
 create__Q23m3d11anmTexSrt_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexSrtP12mAllocator_cPUll = .text:0x802E7350; // type:function size:0x27C
 __dt__Q23m3d11anmTexSrt_cFv = .text:0x802E75D0; // type:function size:0x6C
-getType__Q23m3d11anmTexSrt_cFv = .text:0x802E7640; // type:function size:0x8
+getType__Q23m3d11anmTexSrt_cCFv = .text:0x802E7640; // type:function size:0x8
 remove__Q23m3d11anmTexSrt_cFv = .text:0x802E7650; // type:function size:0xEC
 setAnm__Q23m3d11anmTexSrt_cFRQ23m3d6bmdl_cQ34nw4r3g3d12ResAnmTexSrtlQ23m3d10playMode_e = .text:0x802E7740; // type:function size:0x140
 play__Q23m3d11anmTexSrt_cFv = .text:0x802E7880; // type:function size:0xAC
@@ -17539,7 +17539,7 @@ getFrameMax__Q23m3d11anmTexSrt_cCFl = .text:0x802E7A00; // type:function size:0x
 setFrameStart__Q23m3d11anmTexSrt_cFfl = .text:0x802E7A20; // type:function size:0x14
 getFrameStart__Q23m3d11anmTexSrt_cCFl = .text:0x802E7A40; // type:function size:0x14
 __dt__Q23m3d8anmVis_cFv = .text:0x802E7A60; // type:function size:0x58
-getType__Q23m3d8anmVis_cFv = .text:0x802E7AC0; // type:function size:0x8
+getType__Q23m3d8anmVis_cCFv = .text:0x802E7AC0; // type:function size:0x8
 create__Q23m3d8anmVis_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmVisP12mAllocator_cPUl = .text:0x802E7AD0; // type:function size:0x118
 setAnm__Q23m3d8anmVis_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmVisQ23m3d10playMode_e = .text:0x802E7BF0; // type:function size:0xE8
 setFrmCtrlDefault__Q23m3d8anmVis_cFRQ34nw4r3g3d9ResAnmVisQ23m3d10playMode_e = .text:0x802E7CE0; // type:function size:0x68
@@ -24517,9 +24517,9 @@ fn_80445310 = .text:0x80445310; // type:function size:0x54
 fn_80445370 = .text:0x80445370; // type:function size:0xF4
 fn_80445470 = .text:0x80445470; // type:function size:0xB8
 fn_80445530 = .text:0x80445530; // type:function size:0x7C
-fn_804455B0 = .text:0x804455B0; // type:function size:0x134
+Construct__Q34nw4r3g3d20AnmObjTexPatOverrideFP12MEMAllocatorPUlQ34nw4r3g3d6ResMdli = .text:0x804455B0; // type:function size:0x134
 fn_804456F0 = .text:0x804456F0; // type:function size:0xC0
-fn_804457B0 = .text:0x804457B0; // type:function size:0x1F0
+Construct__Q34nw4r3g3d15AnmObjTexPatResFP12MEMAllocatorPUlQ34nw4r3g3d12ResAnmTexPatQ34nw4r3g3d6ResMdlb = .text:0x804457B0; // type:function size:0x1F0
 fn_804459A0 = .text:0x804459A0; // type:function size:0xC0
 fn_80445A60 = .text:0x80445A60; // type:function size:0x8
 fn_80445A70 = .text:0x80445A70; // type:function size:0xB4
@@ -29095,8 +29095,8 @@ TYPE_NAME__Q34nw4r3g3d20AnmObjMatClrOverride = .rodata:0x804F70C8; // type:objec
 TYPE_NAME__Q34nw4r3g3d15AnmObjMatClrRes = .rodata:0x804F70E8; // type:object size:0x18
 lbl_804F7100 = .rodata:0x804F7100; // type:object size:0x18
 lbl_804F7118 = .rodata:0x804F7118; // type:object size:0x18
-lbl_804F7130 = .rodata:0x804F7130; // type:object size:0x20
-lbl_804F7150 = .rodata:0x804F7150; // type:object size:0x18
+TYPE_NAME__Q34nw4r3g3d20AnmObjTexPatOverride = .rodata:0x804F7130; // type:object size:0x20
+TYPE_NAME__Q34nw4r3g3d15AnmObjTexPatRes = .rodata:0x804F7150; // type:object size:0x18
 lbl_804F7168 = .rodata:0x804F7168; // type:object size:0x18
 lbl_804F7180 = .rodata:0x804F7180; // type:object size:0x18
 TYPE_NAME__Q34nw4r3g3d20AnmObjTexSrtOverride = .rodata:0x804F7198; // type:object size:0x20
@@ -35691,8 +35691,8 @@ __vt__Q23m3d11anmMatClr_c = .data:0x80542500; // type:object size:0x18
 __vt__Q33m3d11anmMatClr_c7child_c = .data:0x80542518; // type:object size:0x18
 lbl_80542530 = .data:0x80542530; // type:object size:0x20
 lbl_80542550 = .data:0x80542550; // type:object size:0x18
-lbl_80542568 = .data:0x80542568; // type:object size:0x18
-lbl_80542580 = .data:0x80542580; // type:object size:0x18
+__vt__Q23m3d11anmTexPat_c = .data:0x80542568; // type:object size:0x18
+__vt__Q33m3d11anmTexPat_c7child_c = .data:0x80542580; // type:object size:0x18
 __vt__Q23m3d11anmTexSrt_c = .data:0x80542598; // type:object size:0x18
 __vt__Q33m3d11anmTexSrt_c7child_c = .data:0x805425B0; // type:object size:0x18
 __vt__Q23m3d8anmVis_c = .data:0x805425C8; // type:object size:0x18
diff --git a/configure.py b/configure.py
index 3862c6d8..7c6c4fe3 100644
--- a/configure.py
+++ b/configure.py
@@ -336,8 +336,10 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "f/f_list.cpp"),
             Object(Matching, "f/f_manager.cpp"),
             Object(NonMatching, "m/m3d/m3d.cpp"),
+            Object(NonMatching, "m/m3d/m_proc.cpp"),
             Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmchrblend.cpp"),
+            Object(Matching, "m/m3d/m_anmtexpat.cpp"),
             Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
             Object(Matching, "m/m3d/m_anmmatclr.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
diff --git a/include/m/m3d/m_anmchr.h b/include/m/m3d/m_anmchr.h
index 4dab7f75..f451bfb7 100644
--- a/include/m/m3d/m_anmchr.h
+++ b/include/m/m3d/m_anmchr.h
@@ -12,7 +12,7 @@ class anmChr_c : public fanm_c {
 public:
     virtual ~anmChr_c();
 
-    virtual int getType() override;
+    virtual int getType() const override;
 
     bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmChr, mAllocator_c*, u32*);
     void setAnm(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
diff --git a/include/m/m3d/m_anmchrblend.h b/include/m/m3d/m_anmchrblend.h
index 0852d2c5..bcb9464a 100644
--- a/include/m/m3d/m_anmchrblend.h
+++ b/include/m/m3d/m_anmchrblend.h
@@ -12,7 +12,7 @@ class anmChrBlend_c : public banm_c {
 public:
     virtual ~anmChrBlend_c();
 
-    virtual int getType() override;
+    virtual int getType() const override;
 
     bool create(nw4r::g3d::ResMdl, int, mAllocator_c *, u32 *);
     bool attach(int, nw4r::g3d::AnmObjChrRes *, f32);
diff --git a/include/m/m3d/m_anmmatclr.h b/include/m/m3d/m_anmmatclr.h
index c12bf296..7b48cd5a 100644
--- a/include/m/m3d/m_anmmatclr.h
+++ b/include/m/m3d/m_anmmatclr.h
@@ -12,7 +12,7 @@ class anmMatClr_c : public banm_c {
     public:
         child_c() {}
         virtual ~child_c();
-        virtual int getType() override;
+        virtual int getType() const override;
 
         static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, bool);
         bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmClr, mAllocator_c *, u32 *);
@@ -25,7 +25,7 @@ class anmMatClr_c : public banm_c {
     anmMatClr_c() : mpChildren(nullptr) {}
     virtual ~anmMatClr_c();
 
-    virtual int getType() override;
+    virtual int getType() const override;
     virtual void remove() override;
     virtual void play() override;
 
diff --git a/include/m/m3d/m_anmtexpat.h b/include/m/m3d/m_anmtexpat.h
new file mode 100644
index 00000000..923bbbc8
--- /dev/null
+++ b/include/m/m3d/m_anmtexpat.h
@@ -0,0 +1,52 @@
+#ifndef M3D_M_ANMTEXPAT_H
+#define M3D_M_ANMTEXPAT_H
+
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+#include <nw4r/g3d/g3d_anmtexpat.h>
+
+namespace m3d {
+
+class anmTexPat_c : public banm_c {
+    class child_c : public fanm_c {
+    public:
+        child_c() {}
+        virtual ~child_c();
+        virtual int getType() const override;
+
+        static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, bool);
+        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c*, u32*);
+        void setAnm(m3d::bmdl_c&, nw4r::g3d::ResAnmTexPat, m3d::playMode_e);
+        void releaseAnm();
+        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexPat&, playMode_e);
+    };
+
+public:
+    anmTexPat_c(): mpChildren(nullptr) {}
+    virtual ~anmTexPat_c();
+
+    virtual int getType() const override;
+    virtual void remove() override;
+    virtual void play() override;
+
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c*, u32*, s32);
+    static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, s32, bool);
+
+    void setAnm(bmdl_c&, nw4r::g3d::ResAnmTexPat, s32, playMode_e);
+
+    void play(s32);
+    f32 getFrame(s32) const;
+    void setFrame(f32, s32);
+    void setRate(f32, s32);
+    bool isStop(s32) const;
+
+    f32 getFrameMax(s32) const;
+    void setFrameStart(f32, s32);
+
+private:
+    child_c *mpChildren;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_anmtexsrt.h b/include/m/m3d/m_anmtexsrt.h
index 151f87ad..4aed956e 100644
--- a/include/m/m3d/m_anmtexsrt.h
+++ b/include/m/m3d/m_anmtexsrt.h
@@ -12,7 +12,7 @@ class anmTexSrt_c : public banm_c {
     public:
         child_c() {}
         virtual ~child_c();
-        virtual int getType() override;
+        virtual int getType() const override;
 
         static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, bool);
         bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c*, u32*);
@@ -25,7 +25,7 @@ class anmTexSrt_c : public banm_c {
     anmTexSrt_c(): mpChildren(nullptr) {}
     virtual ~anmTexSrt_c();
 
-    virtual int getType() override;
+    virtual int getType() const override;
     virtual void remove() override;
     virtual void play() override;
 
diff --git a/include/m/m3d/m_anmvis.h b/include/m/m3d/m_anmvis.h
index 1787a278..0f3fc80d 100644
--- a/include/m/m3d/m_anmvis.h
+++ b/include/m/m3d/m_anmvis.h
@@ -11,7 +11,7 @@ class anmVis_c : public fanm_c {
 public:
     virtual ~anmVis_c();
 
-    virtual int getType() override;
+    virtual int getType() const override;
 
     bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmVis, mAllocator_c *, u32 *);
     void setAnm(m3d::bmdl_c &, nw4r::g3d::ResAnmVis, m3d::playMode_e);
diff --git a/include/m/m3d/m_banm.h b/include/m/m3d/m_banm.h
index d5647a71..35b1b896 100644
--- a/include/m/m3d/m_banm.h
+++ b/include/m/m3d/m_banm.h
@@ -12,7 +12,7 @@ class banm_c {
     banm_c() : mpAnmObj(nullptr), mpFrameHeap(nullptr) {}
     virtual ~banm_c();
 
-    virtual int getType() = 0;
+    virtual int getType() const = 0;
     virtual void remove();
     virtual void play();
 
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index fb756cfb..4f047ca4 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -18,7 +18,7 @@ class fanm_c : public banm_c {
     fanm_c();
     virtual ~fanm_c();
 
-    virtual int getType() = 0;
+    virtual int getType() const  = 0;
     virtual void play();
 
     void set(f32, playMode_e, f32, f32);
diff --git a/include/nw4r/g3d/g3d_anmtexpat.h b/include/nw4r/g3d/g3d_anmtexpat.h
index 031c3264..9506d745 100644
--- a/include/nw4r/g3d/g3d_anmtexpat.h
+++ b/include/nw4r/g3d/g3d_anmtexpat.h
@@ -1,6 +1,8 @@
 #ifndef NW4R_G3D_ANMTEXPAT_H
 #define NW4R_G3D_ANMTEXPAT_H
+#include "nw4r/g3d/g3d_anmobj.h"
 #include "nw4r/g3d/g3d_restex.h"
+#include "nw4r/g3d/g3d_resanmtexpat.h"
 
 namespace nw4r
 {
@@ -18,6 +20,74 @@ namespace nw4r
 			ResTex mTexs[ANM_COUNT]; // at 0x4
 			ResPltt mPltts[ANM_COUNT]; // at 0x24
 		};
+
+		class AnmObjTexPatRes;
+
+		class AnmObjTexPat : public AnmObj {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+				virtual TexPatAnmResult *GetResult();           // at 0x38
+				virtual void Attach(s32, AnmObjTexPatRes *res); // at 0x3C
+				virtual void Detach(s32);                          // at 0x40
+
+			private:
+				int mChildrenArraySize;
+				u16 *mpChildrenArray;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexPat);
+		};
+
+		class AnmObjTexPatNode : public AnmObjTexPat {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+
+				inline int Size() {
+					return mNodeArraySize;
+				}
+
+				AnmObjTexPatRes *GetNode(int i) {
+					return mpNodes[i];
+				}
+
+			private:
+				int mNodeArraySize;
+				AnmObjTexPatRes **mpNodes;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexPatNode);
+		};
+
+		class AnmObjTexPatRes : public AnmObjTexPat, public FrameCtrl {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+
+			static AnmObjTexPatRes *Construct(MEMAllocator*, u32*, ResAnmTexPat, ResMdl, bool);
+			private:
+				ResAnmTexPat mRes;
+				TexPatAnmResult *mpResultCache;
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexPatRes);
+		};
+
+		class AnmObjTexPatOverride : public AnmObjTexPatNode {
+			public:
+				static const G3dObj::TypeObj GetTypeObjStatic() {
+					return TypeObj(TYPE_NAME);
+				}
+
+			static AnmObjTexPatOverride *Construct(MEMAllocator*, u32*, ResMdl, int);
+
+			NW4R_G3D_TYPE_OBJ_DECL(AnmObjTexPatOverride);
+		};
+
 	}
 }
 
diff --git a/include/nw4r/g3d/g3d_resanmtexpat.h b/include/nw4r/g3d/g3d_resanmtexpat.h
index 09b362de..d7fac863 100644
--- a/include/nw4r/g3d/g3d_resanmtexpat.h
+++ b/include/nw4r/g3d/g3d_resanmtexpat.h
@@ -52,6 +52,8 @@ namespace nw4r
 			char UNK_0x24[0xC];
 			u16 mTexCount; // at 0x30
 			u16 mPlttCount; // at 0x32
+			char UNK_0x34[0x4];
+			AnmPolicy mAnmPolicy; // at 0x38
 		};
 
 		struct ResAnmTexPat
@@ -79,6 +81,16 @@ namespace nw4r
 			{
 				return static_cast<const ResAnmTexPatMatData *>(mAnmTexPat.ofs_to_obj<ResDic>(ref().mMatDictOffset)[i]);
 			}
+
+			AnmPolicy GetAnmPolicy() const
+			{
+				return ref().mAnmPolicy;
+			}
+
+			int GetNumFrame() const
+			{
+				return ref().mTexCount;
+			}
 			
 			void GetAnmResult(TexPatAnmResult *, u32, float) const;
 			
diff --git a/src/m/m3d/m_anmchr.cpp b/src/m/m3d/m_anmchr.cpp
index 7ec4baaa..5eb519b6 100644
--- a/src/m/m3d/m_anmchr.cpp
+++ b/src/m/m3d/m_anmchr.cpp
@@ -7,7 +7,7 @@ namespace m3d {
 
 anmChr_c::~anmChr_c() {}
 
-int anmChr_c::getType() {
+int anmChr_c::getType() const {
     return 0;
 }
 
diff --git a/src/m/m3d/m_anmchrblend.cpp b/src/m/m3d/m_anmchrblend.cpp
index a0313840..4959df4b 100644
--- a/src/m/m3d/m_anmchrblend.cpp
+++ b/src/m/m3d/m_anmchrblend.cpp
@@ -7,7 +7,7 @@ namespace m3d {
 
 anmChrBlend_c::~anmChrBlend_c() {}
 
-int anmChrBlend_c::getType() {
+int anmChrBlend_c::getType() const {
     return 0;
 }
 
diff --git a/src/m/m3d/m_anmmatclr.cpp b/src/m/m3d/m_anmmatclr.cpp
index 64012032..dc741412 100644
--- a/src/m/m3d/m_anmmatclr.cpp
+++ b/src/m/m3d/m_anmmatclr.cpp
@@ -7,7 +7,7 @@ namespace m3d {
 
 anmMatClr_c::child_c::~child_c() {}
 
-int anmMatClr_c::child_c::getType() {
+int anmMatClr_c::child_c::getType() const {
     return 0x02;
 }
 
@@ -124,7 +124,7 @@ anmMatClr_c::~anmMatClr_c() {
     anmMatClr_c::remove();
 }
 
-int anmMatClr_c::getType() {
+int anmMatClr_c::getType() const {
     return 0x02;
 }
 
diff --git a/src/m/m3d/m_anmtexpat.cpp b/src/m/m3d/m_anmtexpat.cpp
new file mode 100644
index 00000000..2b790e90
--- /dev/null
+++ b/src/m/m3d/m_anmtexpat.cpp
@@ -0,0 +1,191 @@
+#include <egg/core/eggFrmHeap.h>
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmtexpat.h>
+#include <m/m_heap.h>
+
+namespace m3d {
+
+int anmTexPat_c::child_c::getType() const {
+    return 0x03;
+}
+
+anmTexPat_c::child_c::~child_c() {}
+
+u32 anmTexPat_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjTexPatRes::Construct(nullptr, &size, pat, mdl, false);
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmTexPat_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c* alloc, u32* pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = child_c::heapCost(mdl, pat, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjTexPatRes::Construct(&mAllocator, nullptr, pat, mdl, false);
+
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    }
+    setFrmCtrlDefault(pat, PLAY_MODE_4);
+    return true;
+}
+
+void anmTexPat_c::child_c::setAnm(m3d::bmdl_c &mdl, nw4r::g3d::ResAnmTexPat pat, m3d::playMode_e mode) {
+    releaseAnm();
+    mpAnmObj = nw4r::g3d::AnmObjTexPatRes::Construct(&mAllocator, nullptr, pat, mdl.getResMdl(), false);
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(pat, mode);
+}
+
+void anmTexPat_c::child_c::releaseAnm() {
+    if (mpAnmObj != nullptr) {
+        mpAnmObj->Release();
+        mpFrameHeap->free(3);
+        mpAnmObj = nullptr;
+    }
+}
+
+void anmTexPat_c::child_c::setFrmCtrlDefault(nw4r::g3d::ResAnmTexPat &pat, playMode_e mode) {
+    if (mode == PLAY_MODE_4) {
+        mode = pat.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
+    }
+    set(pat.GetNumFrame(), mode, 1.0f, -1.0f);
+}
+
+u32 anmTexPat_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, s32 num, bool b) {
+    u32 size = 0;
+    nw4r::g3d::AnmObjTexPatOverride::Construct(nullptr, &size, mdl, num);
+    size += ROUND_UP(num * sizeof(child_c), 0x20);
+    u32 childCost = child_c::heapCost(mdl, pat, true);
+    size += num * ROUND_UP(childCost, 0x20);
+
+    if (b) {
+        size = ROUND_UP(mHeap::frmHeapCost(size, 0x20), 0x20);
+    }
+    return size;
+}
+
+bool anmTexPat_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c* alloc, u32* pSize, s32 num) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size = 0;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    *pSize = heapCost(mdl, pat, num, false);
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjTexPatOverride::Construct(&mAllocator, nullptr, mdl, num);
+
+    // TODO inline?
+    mpChildren = (child_c*)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
+
+    nw4r::g3d::AnmObjTexPatOverride *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatOverride>(mpAnmObj);
+    child_c *child = mpChildren;
+    for (int i = 0; i < num; i++) {
+        new (child) child_c();
+        if (!child->create(mdl, pat, &mAllocator, nullptr)) {
+            mHeap::destroyFrmHeap(mpFrameHeap);
+            return false;
+        }
+
+        if (i == 0) {
+            obj->Attach(i, nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatRes>(child->getAnimObj()));
+        } else {
+            child->releaseAnm();
+        }
+        child++;
+    }
+
+    return true;
+}
+
+anmTexPat_c::~anmTexPat_c() {
+    anmTexPat_c::remove();
+}
+
+int anmTexPat_c::getType() const {
+    return 0x03;
+}
+
+void anmTexPat_c::remove() {
+    nw4r::g3d::AnmObjTexPatOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatOverride>(mpAnmObj);
+    if (o != nullptr && mpChildren != 0) {
+        int size = o->Size();
+        for (int i = 0; i < size; i++) {
+            mpChildren[i].remove();
+        }
+        mpChildren = nullptr;
+    }
+    banm_c::remove();
+}
+
+void anmTexPat_c::setAnm(bmdl_c &mdl, nw4r::g3d::ResAnmTexPat pat, s32 idx, playMode_e mode) {
+    nw4r::g3d::AnmObjTexPatOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatOverride>(mpAnmObj);
+    o->Detach(idx);
+    mpChildren[idx].setAnm(mdl, pat, mode);
+
+    nw4r::g3d::AnmObjTexPatRes *res =
+            nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatRes>(mpChildren[idx].getAnimObj());
+    o->Attach(idx, res);
+}
+
+void anmTexPat_c::play() {
+    nw4r::g3d::AnmObjTexPatOverride *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatOverride>(mpAnmObj);
+    int size = o->Size();
+    for (int i = 0; i < size; i++) {
+        play(i);
+    }
+}
+
+void anmTexPat_c::play(s32 idx) {
+    if (mpChildren[idx].IsBound()) {
+        mpChildren[idx].play();
+    }
+}
+
+f32 anmTexPat_c::getFrame(s32 idx) const {
+    return mpChildren[idx].getFrame();
+}
+
+void anmTexPat_c::setFrame(f32 f, s32 idx) {
+    mpChildren[idx].setFrameOnly(f);
+}
+
+void anmTexPat_c::setRate(f32 f, s32 idx) {
+    mpChildren[idx].setRate(f);
+}
+
+bool anmTexPat_c::isStop(s32 idx) const {
+    return mpChildren[idx].isStop();
+}
+
+f32 anmTexPat_c::getFrameMax(s32 idx) const {
+    return mpChildren[idx].getEndFrame();
+}
+
+void anmTexPat_c::setFrameStart(f32 f, s32 idx) {
+    mpChildren[idx].setStartFrame(f);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_anmtexsrt.cpp b/src/m/m3d/m_anmtexsrt.cpp
index df4ea1e5..e712e958 100644
--- a/src/m/m3d/m_anmtexsrt.cpp
+++ b/src/m/m3d/m_anmtexsrt.cpp
@@ -5,7 +5,7 @@
 
 namespace m3d {
 
-int anmTexSrt_c::child_c::getType() {
+int anmTexSrt_c::child_c::getType() const {
     return 0x04;
 }
 
@@ -135,7 +135,7 @@ anmTexSrt_c::~anmTexSrt_c() {
     anmTexSrt_c::remove();
 }
 
-int anmTexSrt_c::getType() {
+int anmTexSrt_c::getType() const {
     return 0x04;
 }
 
diff --git a/src/m/m3d/m_anmvis.cpp b/src/m/m3d/m_anmvis.cpp
index 84a30edf..2d3f8ad2 100644
--- a/src/m/m3d/m_anmvis.cpp
+++ b/src/m/m3d/m_anmvis.cpp
@@ -9,7 +9,7 @@ namespace m3d {
 
 anmVis_c::~anmVis_c() {}
 
-int anmVis_c::getType() {
+int anmVis_c::getType() const {
     return 1;
 }
 

From 9b9d760ae661a33657cd6a25ef08a2c593ad8b79 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 21 Jun 2024 23:19:14 +0200
Subject: [PATCH 11/33] proc_c OK

---
 config/SOUE01/splits.txt  |  1 +
 config/SOUE01/symbols.txt | 14 ++++++------
 configure.py              |  2 +-
 include/m/m3d/m_proc.h    | 21 ++++++++++++++++++
 src/m/m3d/m_proc.cpp      | 46 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 76 insertions(+), 8 deletions(-)
 create mode 100644 include/m/m3d/m_proc.h
 create mode 100644 src/m/m3d/m_proc.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 3a5c16eb..e764a362 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -277,6 +277,7 @@ m/m3d/m3d.cpp:
 
 m/m3d/m_proc.cpp:
 	.text       start:0x802E4720 end:0x802E48D4
+	.data       start:0x805424A0 end:0x805424D0
 
 m/m3d/m_anmchr.cpp:
 	.text       start:0x802E48E0 end:0x802E4D98
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index f932ced7..72fcb547 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17435,10 +17435,10 @@ fn_802E4640 = .text:0x802E4640; // type:function size:0x40
 getNodeID__3m3dFQ34nw4r3g3d6ResMdlPCc = .text:0x802E4680; // type:function size:0x4C
 resetMaterial__3m3dFv = .text:0x802E46D0; // type:function size:0x44
 proc_c_drawProc__3m3dFPQ34nw4r3g3d7ScnProcb = .text:0x802E4720; // type:function size:0x2C
-fn_802E4750 = .text:0x802E4750; // type:function size:0x58
-fn_802E47B0 = .text:0x802E47B0; // type:function size:0x8
-fn_802E47C0 = .text:0x802E47C0; // type:function size:0x4
-fn_802E47D0 = .text:0x802E47D0; // type:function size:0x4
+__dt__Q23m3d6proc_cFv = .text:0x802E4750; // type:function size:0x58
+getType__Q23m3d6proc_cCFv = .text:0x802E47B0; // type:function size:0x8
+drawOpa__Q23m3d6proc_cFv = .text:0x802E47C0; // type:function size:0x4
+drawXlu__Q23m3d6proc_cFv = .text:0x802E47D0; // type:function size:0x4
 create__Q23m3d6proc_cFP12mAllocator_cPUl = .text:0x802E47E0; // type:function size:0xF4
 __dt__Q23m3d8anmChr_cFv = .text:0x802E48E0; // type:function size:0x58
 getType__Q23m3d8anmChr_cCFv = .text:0x802E4940; // type:function size:0x8
@@ -24979,7 +24979,7 @@ fn_8045FDA0 = .text:0x8045FDA0; // type:function size:0xC
 fn_8045FDB0 = .text:0x8045FDB0; // type:function size:0x234
 G3dInit__Q24nw4r3g3dFb = .text:0x8045FFF0; // type:function size:0xF0
 fn_804600E0 = .text:0x804600E0; // type:function size:0x8
-fn_804600F0 = .text:0x804600F0; // type:function size:0x110
+Construct__Q34nw4r3g3d7ScnProcFP12MEMAllocatorPUlPFPQ34nw4r3g3d7ScnProcb_vbbUl = .text:0x804600F0; // type:function size:0x110
 fn_80460200 = .text:0x80460200; // type:function size:0x100
 fn_80460300 = .text:0x80460300; // type:function size:0x68
 fn_80460370 = .text:0x80460370; // type:function size:0x30
@@ -29127,7 +29127,7 @@ lbl_804F7B20 = .rodata:0x804F7B20; // type:object size:0x10
 TYPE_NAME__Q34nw4r3g3d12ScnMdlSimple = .rodata:0x804F7B30; // type:object size:0x18
 TYPE_NAME__Q34nw4r3g3d6ScnMdl = .rodata:0x804F7B48; // type:object size:0x10
 lbl_804F7B58 = .rodata:0x804F7B58; // type:object size:0x18
-lbl_804F7B70 = .rodata:0x804F7B70; // type:object size:0x10
+TYPE_NAME__Q34nw4r3g3d7ScnProc = .rodata:0x804F7B70; // type:object size:0x10
 lbl_804F7B80 = .rodata:0x804F7B80; // type:object size:0x460
 lbl_804F7FE0 = .rodata:0x804F7FE0; // type:object size:0x3CC
 lbl_804F83AC = .rodata:0x804F83AC; // type:object size:0x4C4
@@ -35684,7 +35684,7 @@ lbl_80542430 = .data:0x80542430; // type:object size:0xC
 lbl_8054243C = .data:0x8054243C; // type:object size:0xC
 lbl_80542448 = .data:0x80542448; // type:object size:0x18
 lbl_80542460 = .data:0x80542460; // type:object size:0x40
-lbl_805424A0 = .data:0x805424A0; // type:object size:0x30
+__vt__Q23m3d6proc_c = .data:0x805424A0; // type:object size:0x2C
 __vt__Q23m3d8anmChr_c = .data:0x805424D0; // type:object size:0x18
 __vt__Q23m3d13anmChrBlend_c = .data:0x805424E8; // type:object size:0x18
 __vt__Q23m3d11anmMatClr_c = .data:0x80542500; // type:object size:0x18
diff --git a/configure.py b/configure.py
index 7c6c4fe3..d37dd608 100644
--- a/configure.py
+++ b/configure.py
@@ -336,7 +336,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "f/f_list.cpp"),
             Object(Matching, "f/f_manager.cpp"),
             Object(NonMatching, "m/m3d/m3d.cpp"),
-            Object(NonMatching, "m/m3d/m_proc.cpp"),
+            Object(Matching, "m/m3d/m_proc.cpp"),
             Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmchrblend.cpp"),
             Object(Matching, "m/m3d/m_anmtexpat.cpp"),
diff --git a/include/m/m3d/m_proc.h b/include/m/m3d/m_proc.h
new file mode 100644
index 00000000..47fc4c84
--- /dev/null
+++ b/include/m/m3d/m_proc.h
@@ -0,0 +1,21 @@
+#ifndef M_M3D_PROC_H
+#define M_M3D_PROC_H
+
+#include <common.h>
+#include <m/m3d/m_scnleaf.h>
+
+namespace m3d {
+
+class proc_c : public scnLeaf_c {
+public:
+    virtual ~proc_c();
+    virtual int getType() const override;
+    bool create(mAllocator_c *, u32 *);
+
+    virtual void drawOpa();
+    virtual void drawXlu();
+};
+
+} // namespace m3d
+
+#endif
diff --git a/src/m/m3d/m_proc.cpp b/src/m/m3d/m_proc.cpp
new file mode 100644
index 00000000..394cacfd
--- /dev/null
+++ b/src/m/m3d/m_proc.cpp
@@ -0,0 +1,46 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_proc.h>
+#include <nw4r/g3d/g3d_scnproc.h>
+
+namespace m3d {
+
+void proc_c_drawProc(nw4r::g3d::ScnProc *proc, bool b) {
+    proc_c *p = static_cast<proc_c *>(proc->GetUserData());
+    if (b) {
+        p->drawOpa();
+    } else {
+        p->drawXlu();
+    }
+}
+
+proc_c::~proc_c() {}
+
+int proc_c::getType() const {
+    return 0x2;
+}
+
+void proc_c::drawOpa() {}
+void proc_c::drawXlu() {}
+
+bool proc_c::create(mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 size;
+    if (pSize == nullptr) {
+        pSize = &size;
+    }
+
+    mpScnLeaf = nw4r::g3d::ScnProc::Construct(alloc, pSize, proc_c_drawProc, true, true, 0);
+    if (!mpScnLeaf) {
+        return false;
+    }
+
+    mpScnLeaf->SetPriorityDrawOpa(0x7f);
+    mpScnLeaf->SetPriorityDrawXlu(0x7f);
+    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnProc>(mpScnLeaf)->SetUserData(this);
+    return true;
+}
+
+} // namespace m3d

From 01830693db0e91dd55459cb646df12a168e44c68 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sat, 22 Jun 2024 11:44:07 +0200
Subject: [PATCH 12/33] anmMdl_c, anmShp_c OK

---
 config/SOUE01/splits.txt         | 10 ++++
 config/SOUE01/symbols.txt        | 38 ++++++------
 configure.py                     |  2 +
 include/m/m3d/m_anmmdl.h         | 42 ++++++++++++++
 include/m/m3d/m_anmshp.h         | 24 ++++++++
 include/m/m3d/m_mdl.h            |  8 +--
 include/nw4r/g3d/g3d_anmshp.h    | 99 ++++++++++++++++++++++++++++++++
 include/nw4r/g3d/g3d_resanmshp.h | 12 ++++
 src/m/m3d/m_anmmdl.cpp           | 77 +++++++++++++++++++++++++
 src/m/m3d/m_anmshp.cpp           | 60 +++++++++++++++++++
 10 files changed, 349 insertions(+), 23 deletions(-)
 create mode 100644 include/m/m3d/m_anmmdl.h
 create mode 100644 include/m/m3d/m_anmshp.h
 create mode 100644 include/nw4r/g3d/g3d_anmshp.h
 create mode 100644 src/m/m3d/m_anmmdl.cpp
 create mode 100644 src/m/m3d/m_anmshp.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index e764a362..75c6271d 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -293,6 +293,16 @@ m/m3d/m_anmmatclr.cpp:
 	.data       start:0x80542500 end:0x80542530
 	.sdata2     start:0x8057CCD0 end:0x8057CCE0
 
+m/m3d/m_anmmdl.cpp:
+	.text       start:0x802E5DA0 end:0x802E60D0
+	.data       start:0x80542530 end:0x80542550
+	.sdata2     start:0x8057CCE0 end:0x8057CCE8
+
+m/m3d/m_anmshp.cpp:
+	.text       start:0x802E60D0 end:0x802E63B8
+	.data       start:0x80542550 end:0x80542568
+	.sdata2     start:0x8057CCE8 end:0x8057CCF8
+
 m/m3d/m_anmtexpat.cpp:
 	.text       start:0x802E63C0 end:0x802E6EB4
 	.data       start:0x80542568 end:0x80542598
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 72fcb547..3e3cb25a 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17478,21 +17478,21 @@ checkFrame__Q23m3d11anmMatClr_cCFfl = .text:0x802E5D30; // type:function size:0x
 setPlayMode__Q23m3d11anmMatClr_cFQ23m3d10playMode_el = .text:0x802E5D40; // type:function size:0x14
 getFrameMax__Q23m3d11anmMatClr_cCFl = .text:0x802E5D60; // type:function size:0x14
 getFrameStart__Q23m3d11anmMatClr_cCFl = .text:0x802E5D80; // type:function size:0x14
-fn_802E5DA0 = .text:0x802E5DA0; // type:function size:0x68
-fn_802E5E10 = .text:0x802E5E10; // type:function size:0x14
-fn_802E5E30 = .text:0x802E5E30; // type:function size:0x8
-fn_802E5E40 = .text:0x802E5E40; // type:function size:0x14
-fn_802E5E60 = .text:0x802E5E60; // type:function size:0x8
-fn_802E5E70 = .text:0x802E5E70; // type:function size:0x38
-fn_802E5EB0 = .text:0x802E5EB0; // type:function size:0x28
-fn_802E5EE0 = .text:0x802E5EE0; // type:function size:0x11C
-fn_802E6000 = .text:0x802E6000; // type:function size:0x40
-fn_802E6040 = .text:0x802E6040; // type:function size:0x90
-fn_802E60D0 = .text:0x802E60D0; // type:function size:0x58
-fn_802E6130 = .text:0x802E6130; // type:function size:0x8
-fn_802E6140 = .text:0x802E6140; // type:function size:0x120
-fn_802E6260 = .text:0x802E6260; // type:function size:0xEC
-fn_802E6350 = .text:0x802E6350; // type:function size:0x68
+__dt__Q23m3d9mdlAnmChrFv = .text:0x802E5DA0; // type:function size:0x68
+play__Q23m3d9mdlAnmChrFv = .text:0x802E5E10; // type:function size:0x14
+setFrame__Q23m3d9mdlAnmChrFf = .text:0x802E5E30; // type:function size:0x8
+setAnm__Q23m3d9mdlAnmChrFPCcQ23m3d10playMode_e = .text:0x802E5E40; // type:function size:0x14
+setRate__Q23m3d9mdlAnmChrFf = .text:0x802E5E60; // type:function size:0x8
+create__Q23m3d9mdlAnmChrFPvPvPCcPCcP12mAllocator_cUliPUl = .text:0x802E5E70; // type:function size:0x38
+create2__Q23m3d9mdlAnmChrFPvPvPCcPCcP12mAllocator_cUliPUl = .text:0x802E5EB0; // type:function size:0x28
+create__Q23m3d9mdlAnmChrFPvPvPCcPCcPQ33m3d5mdl_c13mdlCallback_cP12mAllocator_cUliPUl = .text:0x802E5EE0; // type:function size:0x11C
+create__Q23m3d9mdlAnmChrFPvPCcPCcP12mAllocator_cUliPUl = .text:0x802E6000; // type:function size:0x40
+setAnm__Q23m3d9mdlAnmChrFPCcQ23m3d10playMode_ef = .text:0x802E6040; // type:function size:0x90
+__dt__Q23m3d8anmShp_cFv = .text:0x802E60D0; // type:function size:0x58
+getType__Q23m3d8anmShp_cCFv = .text:0x802E6130; // type:function size:0x8
+create__Q23m3d8anmShp_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d9ResAnmShpP12mAllocator_cPUl = .text:0x802E6140; // type:function size:0x120
+setAnm__Q23m3d8anmShp_cFRQ23m3d6bmdl_cQ34nw4r3g3d9ResAnmShpQ23m3d10playMode_e = .text:0x802E6260; // type:function size:0xEC
+setFrmCtrlDefault__Q23m3d8anmShp_cFRQ34nw4r3g3d9ResAnmShpQ23m3d10playMode_e = .text:0x802E6350; // type:function size:0x68
 getType__Q33m3d11anmTexPat_c7child_cCFv = .text:0x802E63C0; // type:function size:0x8
 __dt__Q33m3d11anmTexPat_c7child_cFv = .text:0x802E63D0; // type:function size:0x58
 heapCost__Q33m3d11anmTexPat_c7child_cFQ34nw4r3g3d6ResMdlQ34nw4r3g3d12ResAnmTexPatb = .text:0x802E6430; // type:function size:0x7C
@@ -24646,7 +24646,7 @@ fn_80449A50 = .text:0x80449A50; // type:function size:0x4
 fn_80449A60 = .text:0x80449A60; // type:function size:0x8
 fn_80449A70 = .text:0x80449A70; // type:function size:0x4
 fn_80449A80 = .text:0x80449A80; // type:function size:0x40
-fn_80449AC0 = .text:0x80449AC0; // type:function size:0x240
+Construct__Q34nw4r3g3d12AnmObjShpResFP12MEMAllocatorPUlQ34nw4r3g3d9ResAnmShpQ34nw4r3g3d6ResMdli = .text:0x80449AC0; // type:function size:0x240
 fn_80449D00 = .text:0x80449D00; // type:function size:0xC4
 fn_80449DD0 = .text:0x80449DD0; // type:function size:0x8
 fn_80449DE0 = .text:0x80449DE0; // type:function size:0xB8
@@ -35689,8 +35689,8 @@ __vt__Q23m3d8anmChr_c = .data:0x805424D0; // type:object size:0x18
 __vt__Q23m3d13anmChrBlend_c = .data:0x805424E8; // type:object size:0x18
 __vt__Q23m3d11anmMatClr_c = .data:0x80542500; // type:object size:0x18
 __vt__Q33m3d11anmMatClr_c7child_c = .data:0x80542518; // type:object size:0x18
-lbl_80542530 = .data:0x80542530; // type:object size:0x20
-lbl_80542550 = .data:0x80542550; // type:object size:0x18
+__vt__Q23m3d9mdlAnmChr = .data:0x80542530; // type:object size:0x20
+__vt__Q23m3d8anmShp_c = .data:0x80542550; // type:object size:0x18
 __vt__Q23m3d11anmTexPat_c = .data:0x80542568; // type:object size:0x18
 __vt__Q33m3d11anmTexPat_c7child_c = .data:0x80542580; // type:object size:0x18
 __vt__Q23m3d11anmTexSrt_c = .data:0x80542598; // type:object size:0x18
@@ -45840,7 +45840,7 @@ lbl_8057CCC8 = .sdata2:0x8057CCC8; // type:object size:0x8 data:double
 lbl_8057CCD0 = .sdata2:0x8057CCD0; // type:object size:0x4 data:float
 lbl_8057CCD4 = .sdata2:0x8057CCD4; // type:object size:0x4 data:float
 lbl_8057CCD8 = .sdata2:0x8057CCD8; // type:object size:0x8 data:double
-lbl_8057CCE0 = .sdata2:0x8057CCE0; // type:object size:0x8 data:float
+lbl_8057CCE0 = .sdata2:0x8057CCE0; // type:object size:0x4 data:float
 lbl_8057CCE8 = .sdata2:0x8057CCE8; // type:object size:0x4 data:float
 lbl_8057CCEC = .sdata2:0x8057CCEC; // type:object size:0x4 data:float
 lbl_8057CCF0 = .sdata2:0x8057CCF0; // type:object size:0x8 data:double
diff --git a/configure.py b/configure.py
index d37dd608..8e89d542 100644
--- a/configure.py
+++ b/configure.py
@@ -339,6 +339,8 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_proc.cpp"),
             Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmchrblend.cpp"),
+            Object(Matching, "m/m3d/m_anmmdl.cpp"),
+            Object(Matching, "m/m3d/m_anmshp.cpp"),
             Object(Matching, "m/m3d/m_anmtexpat.cpp"),
             Object(Matching, "m/m3d/m_anmtexsrt.cpp"),
             Object(Matching, "m/m3d/m_anmmatclr.cpp"),
diff --git a/include/m/m3d/m_anmmdl.h b/include/m/m3d/m_anmmdl.h
new file mode 100644
index 00000000..2ee01679
--- /dev/null
+++ b/include/m/m3d/m_anmmdl.h
@@ -0,0 +1,42 @@
+#ifndef M3D_M_ANMMDL_H
+#define M3D_M_ANMMDL_H
+
+#include <m/m3d/m_anmchr.h>
+#include <m/m3d/m_mdl.h>
+#include <nw4r/g3d/g3d_resfile.h>
+
+namespace m3d {
+
+// Not in NSMBW, name pulled from City Folk
+class mdlAnmChr {
+public:
+    mdlAnmChr() {}
+    virtual ~mdlAnmChr();
+
+    virtual void play();
+    virtual void setFrame(f32);
+    virtual void setAnm(const char *name, playMode_e mode, f32);
+    virtual void setAnm(const char *name, playMode_e mode);
+    virtual void setRate(f32);
+
+    bool create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
+            u32 bufferOption, int nView, u32 *pSize);
+    // Unknown overload
+    bool create2(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
+            u32 bufferOption, int nView, u32 *pSize);
+    bool create(void *resFile, const char *mdlName, const char *anmName, mAllocator_c *alloc, u32 bufferOption,
+            int nView, u32 *pSize);
+    bool create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mdl_c::mdlCallback_c *callback,
+            mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize);
+
+private:
+    nw4r::g3d::ResFile mMdlFile;
+    nw4r::g3d::ResFile mAnmFile;
+
+    mdl_c mMdl;
+    anmChr_c mAnm;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_anmshp.h b/include/m/m3d/m_anmshp.h
new file mode 100644
index 00000000..2e550802
--- /dev/null
+++ b/include/m/m3d/m_anmshp.h
@@ -0,0 +1,24 @@
+#ifndef M_M3D_ANMSHP_H
+#define M_M3D_ANMSHP_H
+
+#include <common.h>
+#include <m/m3d/m_bmdl.h>
+#include <m/m3d/m_fanm.h>
+#include <nw4r/g3d/g3d_resanmshp.h>
+
+namespace m3d {
+
+class anmShp_c : public fanm_c {
+public:
+    virtual ~anmShp_c();
+
+    virtual int getType() const override;
+
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmShp, mAllocator_c *, u32 *);
+    void setAnm(m3d::bmdl_c &, nw4r::g3d::ResAnmShp, m3d::playMode_e);
+    void setFrmCtrlDefault(nw4r::g3d::ResAnmShp &, m3d::playMode_e);
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
index d8abb1aa..59f57215 100644
--- a/include/m/m3d/m_mdl.h
+++ b/include/m/m3d/m_mdl.h
@@ -4,8 +4,8 @@
 #include <m/m3d/m_banm.h>
 #include <m/m3d/m_calc_ratio.h>
 #include <m/m3d/m_smdl.h>
-#include <nw4r/g3d/g3d_resmdl.h>
 #include <nw4r/g3d/g3d_calcworld.h>
+#include <nw4r/g3d/g3d_resmdl.h>
 
 class UnkClass3 {};
 
@@ -16,7 +16,7 @@ class callback_c {
     virtual ~callback_c() {}
     virtual void timingA(u32, nw4r::g3d::ChrAnmResult *, nw4r::g3d::ResMdl) {}
     virtual void timingB(u32, nw4r::g3d::WorldMtxManip *, nw4r::g3d::ResMdl) {}
-    virtual void timingC(nw4r::math::MTX34*, nw4r::g3d::ResMdl) {}
+    virtual void timingC(nw4r::math::MTX34 *, nw4r::g3d::ResMdl) {}
 };
 
 struct UnkNode {
@@ -30,7 +30,8 @@ struct UnkNode {
     nw4r::math::MTX34 mtx;
 };
 
-class mdl_c : smdl_c {
+class mdl_c : public smdl_c {
+public:
     class mdlCallback_c : public nw4r::g3d::IScnObjCallback {
     public:
         mdlCallback_c();
@@ -63,7 +64,6 @@ class mdl_c : smdl_c {
         mAllocator_c *mpAlloc;
     };
 
-public:
     mdl_c();
     virtual ~mdl_c();
 
diff --git a/include/nw4r/g3d/g3d_anmshp.h b/include/nw4r/g3d/g3d_anmshp.h
new file mode 100644
index 00000000..e5d798c9
--- /dev/null
+++ b/include/nw4r/g3d/g3d_anmshp.h
@@ -0,0 +1,99 @@
+#ifndef NW4R_G3D_ANMSHP_H
+#define NW4R_G3D_ANMSHP_H
+#include "common.h"
+#include "nw4r/g3d/g3d_anmobj.h"
+#include "nw4r/g3d/g3d_resanmshp.h"
+
+// This header is based on the SS ghidra, the TypeNames in the binary,
+// and an assumed symmetry to AnmObjVis. Everything about it could be wrong.
+
+namespace nw4r {
+namespace g3d {
+
+class AnmObjShpRes;
+
+class AnmObjShp : public AnmObj {
+public:
+    AnmObjShp(MEMAllocator *, u16 *, int);
+    virtual bool IsDerivedFrom(TypeObj other) const // at 0x8
+    {
+        return (other == GetTypeObjStatic()) ? true : AnmObj::IsDerivedFrom(other);
+    }
+    virtual void G3dProc(u32, u32, void *);  // at 0xC
+    virtual ~AnmObjShp() {}                  // at 0x10
+    virtual const TypeObj GetTypeObj() const // at 0x14
+    {
+        return TypeObj(TYPE_NAME);
+    }
+    virtual const char *GetTypeName() const // at 0x18
+    {
+        return GetTypeObj().GetTypeName();
+    }
+    virtual void SetFrame(f32) = 0;                    // at 0x1C
+    virtual f32 GetFrame() const = 0;                  // at 0x20
+    virtual void UpdateFrame() = 0;                    // at 0x24
+    virtual void SetUpdateRate(f32) = 0;               // at 0x28
+    virtual f32 GetUpdateRate() const = 0;             // at 0x2C
+    virtual bool Bind(ResMdl) = 0;                     // at 0x30
+    virtual void Release();                            // at 0x34
+    virtual bool GetResult(u32) = 0;                   // at 0x38
+    virtual AnmObjShpRes *Attach(int, AnmObjShpRes *); // at 0x3C
+    virtual AnmObjShpRes *Detach(int);                 // at 0x40
+
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    bool TestDefined(u32 idx) const;
+    bool TestExistence(u32 idx) const;
+    void DetachAll();
+
+protected:
+    static const int MAX_RESOURCES = 4;
+
+    int mNumBinds; // at 0x10
+    u16 *mBinds;   // at 0x14
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjShp);
+};
+
+class AnmObjShpRes : public AnmObjShp, public FrameCtrl {
+public:
+    AnmObjShpRes(MEMAllocator *, ResAnmShp, u16 *, int);
+
+    virtual bool IsDerivedFrom(TypeObj other) const // at 0x8
+    {
+        return (other == GetTypeObjStatic()) ? true : AnmObjShp::IsDerivedFrom(other);
+    }
+    virtual ~AnmObjShpRes() {}               // at 0x10
+    virtual const TypeObj GetTypeObj() const // at 0x14
+    {
+        return TypeObj(TYPE_NAME);
+    }
+    virtual const char *GetTypeName() const // at 0x18
+    {
+        return GetTypeObj().GetTypeName();
+    }
+    virtual void SetFrame(f32);        // at 0x1C
+    virtual f32 GetFrame() const;      // at 0x20
+    virtual void UpdateFrame();        // at 0x24
+    virtual void SetUpdateRate(f32);   // at 0x28
+    virtual f32 GetUpdateRate() const; // at 0x2C
+    virtual bool Bind(ResMdl);         // at 0x30
+    virtual bool GetResult(u32);       // at 0x38
+
+    static const TypeObj GetTypeObjStatic() {
+        return TypeObj(TYPE_NAME);
+    }
+
+    static AnmObjShpRes *Construct(MEMAllocator *, u32 *, ResAnmShp, ResMdl, int);
+
+private:
+    ResAnmShp mResAnmShp; // at 0x2C
+
+    NW4R_G3D_TYPE_OBJ_DECL(AnmObjShpRes);
+};
+} // namespace g3d
+} // namespace nw4r
+
+#endif
diff --git a/include/nw4r/g3d/g3d_resanmshp.h b/include/nw4r/g3d/g3d_resanmshp.h
index c9a08db2..332065da 100644
--- a/include/nw4r/g3d/g3d_resanmshp.h
+++ b/include/nw4r/g3d/g3d_resanmshp.h
@@ -1,6 +1,7 @@
 #ifndef NW4R_G3D_RESANMSHP_H
 #define NW4R_G3D_RESANMSHP_H
 #include "common.h"
+#include "nw4r/g3d/g3d_anmobj.h"
 #include "nw4r/g3d/g3d_rescommon.h"
 
 namespace nw4r {
@@ -8,6 +9,9 @@ namespace g3d {
 struct ResAnmShpData {
     char UNK_0x0[0x8];
     u32 mRevision; // at 0x8
+    char UNK_0xC[0x24 - 0xC];
+    u16 mNumFrames;       // at 0x24
+    AnmPolicy mAnmPolicy; // at 0x28
 };
 
 struct ResAnmShp {
@@ -20,6 +24,14 @@ struct ResAnmShp {
     inline bool CheckRevision() const {
         return mAnmShp.ref().mRevision == REVISION;
     }
+
+    AnmPolicy GetAnmPolicy() const {
+        return mAnmShp.ref().mAnmPolicy;
+    }
+
+    int GetNumFrame() const {
+        return mAnmShp.ref().mNumFrames;
+    }
 };
 } // namespace g3d
 } // namespace nw4r
diff --git a/src/m/m3d/m_anmmdl.cpp b/src/m/m3d/m_anmmdl.cpp
new file mode 100644
index 00000000..70c49fe8
--- /dev/null
+++ b/src/m/m3d/m_anmmdl.cpp
@@ -0,0 +1,77 @@
+#include <m/m3d/m_anmmdl.h>
+
+namespace m3d {
+
+mdlAnmChr::~mdlAnmChr() {}
+
+void mdlAnmChr::play() {
+    mMdl.play();
+}
+
+void mdlAnmChr::setFrame(f32 f) {
+    mAnm.setFrameOnly(f);
+}
+
+void mdlAnmChr::setAnm(const char *name, playMode_e mode) {
+    setAnm(name, mode, 0.0f);
+}
+
+void mdlAnmChr::setRate(f32 rate) {
+    mAnm.setRate(rate);
+}
+
+bool mdlAnmChr::create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
+        u32 bufferOption, int nView, u32 *pSize) {
+    return create(mdlFile, anmFile, mdlName, anmName, nullptr, alloc, bufferOption, nView, pSize);
+}
+
+bool mdlAnmChr::create2(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
+        u32 bufferOption, int nView, u32 *pSize) {
+    return create(mdlFile, anmFile, mdlName, anmName, alloc, bufferOption, nView, pSize);
+}
+
+bool mdlAnmChr::create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName,
+        mdl_c::mdlCallback_c *callback, mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize) {
+    mMdlFile = mdlFile;
+    mAnmFile = anmFile;
+
+    nw4r::g3d::ResMdl resMdl = mMdlFile.GetResMdl(mdlName);
+    if (!mMdl.create(resMdl, callback, alloc, bufferOption, nView, pSize)) {
+        return false;
+    }
+
+    nw4r::g3d::ResAnmChr resAnm = mAnmFile.GetResAnmChr(anmName);
+    if (!resAnm.mAnmChr.IsValid()) {
+        resAnm = mMdlFile.GetResAnmChr(anmName);
+    }
+    u32 oldSize;
+    if (pSize != nullptr) {
+        oldSize = *pSize;
+    }
+
+    if (!mAnm.create(resMdl, resAnm, alloc, pSize)) {
+        mMdl.remove();
+        return false;
+    }
+
+    if (pSize != nullptr) {
+        *pSize += oldSize;
+    }
+    return true;
+}
+
+bool mdlAnmChr::create(void *resFile, const char *mdlName, const char *anmName, mAllocator_c *alloc, u32 bufferOption,
+        int nView, u32 *pSize) {
+    return create(resFile, resFile, mdlName, anmName, nullptr, alloc, bufferOption, nView, pSize);
+}
+
+void mdlAnmChr::setAnm(const char *name, playMode_e mode, f32 blend) {
+    nw4r::g3d::ResAnmChr anm = mAnmFile.GetResAnmChr(name);
+    if (!anm.mAnmChr.IsValid()) {
+        anm = mMdlFile.GetResAnmChr(name);
+    }
+    mAnm.setAnm(mMdl, anm, mode);
+    mMdl.setAnm(mAnm, blend);
+}
+
+} // namespace m3d
diff --git a/src/m/m3d/m_anmshp.cpp b/src/m/m3d/m_anmshp.cpp
new file mode 100644
index 00000000..6606b043
--- /dev/null
+++ b/src/m/m3d/m_anmshp.cpp
@@ -0,0 +1,60 @@
+#include <m/m3d/m3d.h>
+#include <m/m3d/m_anmshp.h>
+#include <nw4r/g3d/g3d_anmshp.h>
+
+namespace m3d {
+
+anmShp_c::~anmShp_c() {}
+
+int anmShp_c::getType() const {
+    return 0x5;
+}
+
+bool anmShp_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmShp anm, mAllocator_c *alloc, u32 *pSize) {
+    if (alloc == nullptr) {
+        alloc = internal::l_allocator_p;
+    }
+
+    u32 tmp;
+    if (pSize == nullptr) {
+        pSize = &tmp;
+    }
+
+    // This might be an nw4r inline? Note that some of the roundings
+    // are no-ops that still emit instructions because mwcc doesn't do
+    // math
+    u32 numEntries = mdl.GetResVtxPosNumEntries();
+    *pSize = ROUND_UP(ROUND_UP(numEntries * 2 + 0x38, 0x04) + numEntries * 0x0C, 0x04);
+
+    if (!createAllocator(alloc, pSize)) {
+        return false;
+    }
+
+    mpAnmObj = nw4r::g3d::AnmObjShpRes::Construct(&mAllocator, &tmp, anm, mdl, 0);
+    if (!mpAnmObj->Bind(mdl)) {
+        remove();
+        return false;
+    } else {
+        setFrmCtrlDefault(anm, PLAY_MODE_4);
+        return true;
+    }
+}
+
+void anmShp_c::setAnm(m3d::bmdl_c &mdl, nw4r::g3d::ResAnmShp anm, m3d::playMode_e mode) {
+    mdl.removeAnm((nw4r::g3d::ScnMdlSimple::AnmObjType)getType());
+    mpAnmObj->Release();
+    mpFrameHeap->free(0x3);
+    u32 tmp;
+    mpAnmObj = nw4r::g3d::AnmObjShpRes::Construct(&mAllocator, &tmp, anm, mdl.getResMdl(), 0);
+    mpAnmObj->Bind(mdl.getResMdl());
+    setFrmCtrlDefault(anm, mode);
+}
+
+void anmShp_c::setFrmCtrlDefault(nw4r::g3d::ResAnmShp &anm, m3d::playMode_e mode) {
+    if (mode == PLAY_MODE_4) {
+        mode = anm.GetAnmPolicy() == nw4r::g3d::ANM_POLICY_ONETIME ? PLAY_MODE_1 : PLAY_MODE_0;
+    }
+    set(anm.GetNumFrame(), mode, 1.0f, -1.0f);
+}
+
+} // namespace m3d

From 1a5dfa2258832a879aa54af062bc50bfeb9a0a51 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sat, 22 Jun 2024 14:21:21 +0200
Subject: [PATCH 13/33] Cleanup and progress

---
 config/SOUE01/splits.txt         |  2 +
 config/SOUE01/symbols.txt        |  4 +-
 include/m/m3d/m_calc_ratio.h     | 30 +++++++++---
 include/m/m3d/m_fanm.h           |  1 +
 include/m/m3d/m_mdl.h            | 32 +++++-------
 include/nw4r/g3d/g3d_calcworld.h | 15 ++++++
 src/m/m3d/m_fanm.cpp             | 68 ++++++++++++++++++++++----
 src/m/m3d/m_mdl.cpp              | 83 +++++++++++++++++++++++++++++---
 8 files changed, 190 insertions(+), 45 deletions(-)

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 75c6271d..921f9061 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -332,6 +332,8 @@ m/m3d/m_calc_ratio.cpp:
 
 m/m3d/m_fanm.cpp:
 	.text       start:0x802EA8A0 end:0x802EAF10
+	.data       start:0x805426A8 end:0x805426C0
+	.sdata2     start:0x8057CD50 end:0x8057CD60
 
 m/m3d/m_mdl.cpp:
 	.text       start:0x802EAF80 end:0x802EBB5C
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 3e3cb25a..3a19c3be 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17605,11 +17605,11 @@ __ct__Q23m3d6fanm_cFv = .text:0x802EA8A0; // type:function size:0x70
 __dt__Q23m3d6fanm_cFv = .text:0x802EA910; // type:function size:0x58
 play__Q23m3d6fanm_cFv = .text:0x802EA970; // type:function size:0x130
 set__Q23m3d6fanm_cFfQ23m3d10playMode_eff = .text:0x802EAAA0; // type:function size:0xC4
-setFrame__Q23m3d6fanm_cFf = .text:0x802EAB70; // type:function size:0xC0
+set2__Q23m3d6fanm_cFfQ23m3d10playMode_efff = .text:0x802EAB70; // type:function size:0xC0
 setFrameOnly__Q23m3d6fanm_cFf = .text:0x802EAC30; // type:function size:0x3C
 isStop__Q23m3d6fanm_cCFv = .text:0x802EAC70; // type:function size:0xC0
 checkFrame__Q23m3d6fanm_cCFf = .text:0x802EAD30; // type:function size:0x138
-fn_802EAE70 = .text:0x802EAE70; // type:function size:0xA0
+unk_802EAE70__Q23m3d6fanm_cCFv = .text:0x802EAE70; // type:function size:0xA0
 fn_802EAF10 = .text:0x802EAF10; // type:function size:0x70
 __ct__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EAF80; // type:function size:0x54
 __dt__Q33m3d5mdl_c13mdlCallback_cFv = .text:0x802EAFE0; // type:function size:0x5C
diff --git a/include/m/m3d/m_calc_ratio.h b/include/m/m3d/m_calc_ratio.h
index 3b9140a8..ee850bc9 100644
--- a/include/m/m3d/m_calc_ratio.h
+++ b/include/m/m3d/m_calc_ratio.h
@@ -18,14 +18,30 @@ class calcRatio_c {
     void calc();
     bool isEnd() const;
 
+    f32 get0x10() const {
+        return mf4;
+    }
+
+    f32 get0x14() const {
+        return mf5;
+    }
+
+    bool is0x18() const {
+        return mb1;
+    }
+
+    bool is0x19() const {
+        return mb2;
+    }
+
 private:
-    f32 mf1;
-    f32 mf2;
-    f32 mf3;
-    f32 mf4;
-    f32 mf5;
-    u8 mb1;
-    u8 mb2;
+    /* 0x04 */ f32 mf1;
+    /* 0x08 */ f32 mf2;
+    /* 0x0C */ f32 mf3;
+    /* 0x10 */ f32 mf4;
+    /* 0x14 */ f32 mf5;
+    /* 0x18 */ bool mb1;
+    /* 0x19 */ bool mb2;
 };
 
 } // namespace m3d
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index 4f047ca4..d294963d 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -22,6 +22,7 @@ class fanm_c : public banm_c {
     virtual void play();
 
     void set(f32, playMode_e, f32, f32);
+    void set2(f32, playMode_e, f32, f32, f32);
     void setFrame(f32);
     void setFrameOnly(f32);
     bool isStop() const;
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
index 59f57215..66193ff7 100644
--- a/include/m/m3d/m_mdl.h
+++ b/include/m/m3d/m_mdl.h
@@ -4,6 +4,7 @@
 #include <m/m3d/m_banm.h>
 #include <m/m3d/m_calc_ratio.h>
 #include <m/m3d/m_smdl.h>
+#include <nw4r/g3d/g3d_anmchr.h>
 #include <nw4r/g3d/g3d_calcworld.h>
 #include <nw4r/g3d/g3d_resmdl.h>
 
@@ -19,19 +20,11 @@ class callback_c {
     virtual void timingC(nw4r::math::MTX34 *, nw4r::g3d::ResMdl) {}
 };
 
-struct UnkNode {
-    UNKWORD field_0x00;
-    f32 field_0x04;
-    f32 field_0x08;
-    f32 field_0x0C;
-    UNKWORD field_0x10;
-    UNKWORD field_0x14;
-    UNKWORD field_0x18;
-    nw4r::math::MTX34 mtx;
-};
-
 class mdl_c : public smdl_c {
 public:
+    // TODO this is not the correct callback class, since NSMBW has
+    // the A, B, C names and also the _CALC_X from IScnObjCallback,
+    // so there must be a different callback interface
     class mdlCallback_c : public nw4r::g3d::IScnObjCallback {
     public:
         mdlCallback_c();
@@ -39,10 +32,9 @@ class mdl_c : public smdl_c {
 
         bool create(nw4r::g3d::ResMdl, mAllocator_c *, u32 *);
 
-        // TODO g3d headers
-        virtual void ExecCallbackA();
-        virtual void ExecCallbackB();
-        virtual void ExecCallbackC();
+        virtual void ExecCallbackA(nw4r::g3d::ChrAnmResult *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *);
+        virtual void ExecCallbackB(nw4r::g3d::WorldMtxManip *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *);
+        virtual void ExecCallbackC(nw4r::math::MTX34 *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *);
 
         void remove();
         void setBlendFrame(f32);
@@ -57,11 +49,11 @@ class mdl_c : public smdl_c {
         }
 
     private:
-        calcRatio_c mCalcRatio;
-        int mNumNode;
-        UnkNode *mpNodes;
-        callback_c *mpBaseCallback;
-        mAllocator_c *mpAlloc;
+        /* 0x04 */ calcRatio_c mCalcRatio;
+        /* 0x20 */ int mNumNode;
+        /* 0x24 */ nw4r::g3d::ChrAnmResult *mpNodes;
+        /* 0x28 */ callback_c *mpBaseCallback;
+        /* 0x2C */ mAllocator_c *mpAlloc;
     };
 
     mdl_c();
diff --git a/include/nw4r/g3d/g3d_calcworld.h b/include/nw4r/g3d/g3d_calcworld.h
index 0fa9e6d6..7ea59a78 100644
--- a/include/nw4r/g3d/g3d_calcworld.h
+++ b/include/nw4r/g3d/g3d_calcworld.h
@@ -6,6 +6,21 @@
 namespace nw4r {
 namespace g3d {
 
+struct FuncObjCalcWorld {
+private:
+    u8 UNK_0x00[0x06];
+    /** 0x06 */ u16 mNodeId;
+
+public:
+    u32 GetNodeId() const {
+        return mNodeId;
+    }
+
+    void SetNodeId(u32 n) {
+        mNodeId = n;
+    }
+};
+
 class WorldMtxManip {};
 
 void CalcWorld(math::MTX34 *, u32 *, const u8 *, const math::MTX34 *, ResMdl, AnmObjChr *, FuncObjCalcWorld *, u32);
diff --git a/src/m/m3d/m_fanm.cpp b/src/m/m3d/m_fanm.cpp
index 31f6af33..4eadfc98 100644
--- a/src/m/m3d/m_fanm.cpp
+++ b/src/m/m3d/m_fanm.cpp
@@ -26,6 +26,7 @@ void fanm_c::play() {
             newFrame = frame - rate;
         } else if ((mPlayState & 1) == 0) {
             newFrame = frame + ((mEndFrame - rate) - mStartFrame);
+            // TODO there's a duplicate branch to the function end here
         }
     } else {
         newFrame = frame + rate;
@@ -45,9 +46,12 @@ void fanm_c::play() {
 
 void fanm_c::set(f32 startFrame, playMode_e mode, f32 updateRate, f32 currentFrame) {
     if (currentFrame < 0.0f) {
-        // TODO shuffle
-        f32 newFrame = mode == PLAY_MODE_1 ? 0.0f : startFrame - 1.0f;
-        currentFrame = newFrame;
+        currentFrame = startFrame;
+        if (mode == PLAY_MODE_1) {
+            currentFrame = 0.0f;
+        } else {
+            currentFrame -= 1.0f;
+        }
     }
 
     mEndFrame = startFrame;
@@ -58,8 +62,17 @@ void fanm_c::set(f32 startFrame, playMode_e mode, f32 updateRate, f32 currentFra
     mCurrentFrame = currentFrame;
 }
 
-void fanm_c::setFrame(f32) {
-    // TODO g3d headers
+void fanm_c::set2(f32 startFrame, playMode_e mode, f32 endFrame, f32 rate, f32 currentFrame) {
+    if (currentFrame < 0.0f) {
+        currentFrame = rate < 0.0f ? endFrame - 1.0f : startFrame;
+    }
+
+    mStartFrame = startFrame;
+    mEndFrame = endFrame;
+    mpAnmObj->SetFrame(currentFrame);
+    mpAnmObj->SetUpdateRate(rate);
+    mPlayState = (u8)mode;
+    mCurrentFrame = currentFrame;
 }
 
 // assumed name
@@ -69,15 +82,52 @@ void fanm_c::setFrameOnly(f32 frame) {
 }
 
 bool fanm_c::isStop() const {
-    // TODO g3d headers
+    f32 frame = mpAnmObj->GetFrame();
+    f32 rate = mpAnmObj->GetUpdateRate();
+    if (rate < 0.0f || mPlayState == PLAY_MODE_3) {
+        return frame <= mStartFrame;
+    } else if (mPlayState == PLAY_MODE_1) {
+        return frame >= mEndFrame - 1.0f;
+    }
+    return false;
 }
 
-bool fanm_c::checkFrame(f32) const {
-    // TODO g3d headers
+bool fanm_c::checkFrame(f32 frame) const {
+    f32 stored = mpAnmObj->GetFrame();
+    if (mCurrentFrame == stored) {
+        return stored == frame;
+    }
+
+    f32 rate = mpAnmObj->GetUpdateRate();
+    if (rate < 0.0f || (mPlayState & 2)) {
+        if (mCurrentFrame > stored) {
+            if (mCurrentFrame > frame && stored <= frame) {
+                return true;
+            }
+        } else if (frame < mCurrentFrame || frame >= stored) {
+            return true;
+        }
+    } else {
+        if (mCurrentFrame < stored) {
+            if (mCurrentFrame < frame && stored >= frame) {
+                return true;
+            }
+        } else if (frame > mCurrentFrame || frame <= stored) {
+            return true;
+        }
+    }
+
+    return false;
 }
 
 bool fanm_c::unk_802EAE70() const {
-    // TODO g3d headers
+    f32 frame = mpAnmObj->GetFrame();
+    f32 rate = mpAnmObj->GetUpdateRate();
+    if (rate < 0.0f || (mPlayState & 2)) {
+        return mCurrentFrame < frame;
+    } else {
+        return mCurrentFrame > frame;
+    }
 }
 
 } // namespace m3d
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index 65581c81..acd027e0 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -12,6 +12,75 @@ mdl_c::mdlCallback_c::mdlCallback_c() {
 }
 mdl_c::mdlCallback_c::~mdlCallback_c() {}
 
+void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::g3d::ResMdl mdl,
+        nw4r::g3d::FuncObjCalcWorld *o) {
+    nw4r::g3d::ChrAnmResult *resPtr = &mpNodes[o->GetNodeId()];
+    if (mCalcRatio.is0x18() && !mCalcRatio.isEnd()) {
+        if (!mCalcRatio.is0x19()) {
+            *result = *resPtr;
+        } else {
+            u32 flags = result->mFlags;
+            f32 f2 = mCalcRatio.get0x10();
+            f32 f1 = mCalcRatio.get0x14();
+
+            // TODO the math operators cause ps inline assembly
+            if ((flags & 8) == 0) {
+                result->VEC3_0x4.x = (result->VEC3_0x4.x * f1 + resPtr->VEC3_0x4.x * f2);
+                result->VEC3_0x4.y = (result->VEC3_0x4.y * f1 + resPtr->VEC3_0x4.y * f2);
+                result->VEC3_0x4.z = (result->VEC3_0x4.z * f1 + resPtr->VEC3_0x4.z * f2);
+            } else {
+                result->VEC3_0x4.x = (f1 + resPtr->VEC3_0x4.x * f2);
+                result->VEC3_0x4.y = (f1 + resPtr->VEC3_0x4.y * f2);
+                result->VEC3_0x4.z = (f1 + resPtr->VEC3_0x4.z * f2);
+            }
+
+            nw4r::math::QUAT q1;
+            nw4r::math::QUAT q2;
+            C_QUATMtx(q1, resPtr->mMtx);
+            // TODO ...
+            if ((flags & 0x20) == 0) {
+                C_QUATMtx(q2, result->mMtx);
+            } else {
+                q2.x = 0.0f;
+                q2.y = 0.0f;
+                q2.z = 0.0f;
+                q2.w = 1.0f;
+            }
+            nw4r::math::QUAT q3;
+            C_QUATSlerp(q1, q2, q3, f1);
+            PSMTXQuat(result->mMtx, q3);
+            result->VEC3_0x10 = (flags & 0x40) == 0 ?
+                    nw4r::math::VEC3(result->VEC3_0x10.x * f1, result->VEC3_0x10.y * f1, result->VEC3_0x10.z * f1) :
+                    result->VEC3_0x10;
+            *resPtr = *result;
+        }
+    } else {
+        *resPtr = *result;
+    }
+
+    if (mpBaseCallback != nullptr) {
+        mpBaseCallback->timingA(mNumNode, result, mdl);
+    }
+}
+
+void mdl_c::mdlCallback_c::ExecCallbackB(nw4r::g3d::WorldMtxManip *m, nw4r::g3d::ResMdl mdl,
+        nw4r::g3d::FuncObjCalcWorld *o) {
+    u16 nodeId = o->GetNodeId();
+    if (mpBaseCallback != nullptr) {
+        mpBaseCallback->timingB(nodeId, m, mdl);
+    }
+    u32 num = mdl.GetResNodeNumEntries();
+    u32 numInc = num + 1;
+    o->SetNodeId(numInc - (numInc / num) * num);
+}
+
+void mdl_c::mdlCallback_c::ExecCallbackC(nw4r::math::MTX34 *mat, nw4r::g3d::ResMdl mdl, nw4r::g3d::FuncObjCalcWorld *) {
+    if (mpBaseCallback != nullptr) {
+        mpBaseCallback->timingC(mat, mdl);
+    }
+    mCalcRatio.offUpdate();
+}
+
 bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 *pSize) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
@@ -24,19 +93,19 @@ bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u3
     }
 
     mNumNode = mdl.GetResNodeNumEntries();
-    size_t bufSize = mNumNode * sizeof(UnkNode);
-    mpNodes = (UnkNode*)MEMAllocFromAllocator(alloc, bufSize);
+    size_t bufSize = mNumNode * sizeof(nw4r::g3d::ChrAnmResult);
+    mpNodes = (nw4r::g3d::ChrAnmResult *)MEMAllocFromAllocator(alloc, bufSize);
     if (mpNodes == nullptr) {
         return false;
     }
 
     *pSize = ROUND_UP(bufSize + ROUND_UP(*pSize, 0x04), 0x04);
-    UnkNode *node = mpNodes;
+    nw4r::g3d::ChrAnmResult *node = mpNodes;
     for (int i = 0; i < mNumNode; i++) {
-        node->field_0x04 = 1.0f;
-        node->field_0x08 = 1.0f;
-        node->field_0x0C = 1.0f;
-        PSMTXIdentity(node->mtx.m);
+        node->VEC3_0x4.x = 1.0f;
+        node->VEC3_0x4.y = 1.0f;
+        node->VEC3_0x4.z = 1.0f;
+        PSMTXIdentity(node->mMtx);
         node++;
     }
 

From 1d9827cdb07898086fedc5e3aa0a3dd333bb706c Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sun, 23 Jun 2024 11:44:46 +0200
Subject: [PATCH 14/33] m_mdl "OK"

---
 config/SOUE01/symbols.txt         |  2 +-
 configure.py                      |  2 +-
 include/m/m3d/m_mdl.h             |  5 +---
 include/nw4r/g3d/g3d_calcworld.h  | 11 ++++++++
 include/nw4r/g3d/g3d_scnmdlsmpl.h |  7 +++++
 src/m/m3d/m_mdl.cpp               | 46 ++++++++++++++++++++++---------
 6 files changed, 54 insertions(+), 19 deletions(-)

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 3a19c3be..f968a58b 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -22199,7 +22199,7 @@ PSMTXTrans = .text:0x803B8770; // type:function size:0x34
 fn_803B87B0 = .text:0x803B87B0; // type:function size:0x4C
 fn_803B8800 = .text:0x803B8800; // type:function size:0x28
 fn_803B8830 = .text:0x803B8830; // type:function size:0x58
-fn_803B8890 = .text:0x803B8890; // type:function size:0xA4
+PSMTXQuat = .text:0x803B8890; // type:function size:0xA4
 fn_803B8940 = .text:0x803B8940; // type:function size:0x174
 fn_803B8AC0 = .text:0x803B8AC0; // type:function size:0xA4
 C_MTXLightPerspective = .text:0x803B8B70; // type:function size:0xF8
diff --git a/configure.py b/configure.py
index 8e89d542..60670cbc 100644
--- a/configure.py
+++ b/configure.py
@@ -349,7 +349,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
             Object(Matching, "m/m3d/m_calc_ratio.cpp"),
             Object(NonMatching, "m/m3d/m_fanm.cpp"),
-            Object(NonMatching, "m/m3d/m_mdl.cpp"),
+            Object(Matching, "m/m3d/m_mdl.cpp"),
             Object(NonMatching, "m/m3d/m_scnLeaf.cpp"),
             Object(Matching, "m/m3d/m_smdl.cpp"),
             Object(Matching, "m/m_allocator.cpp"),
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
index 66193ff7..d118e841 100644
--- a/include/m/m3d/m_mdl.h
+++ b/include/m/m3d/m_mdl.h
@@ -22,10 +22,7 @@ class callback_c {
 
 class mdl_c : public smdl_c {
 public:
-    // TODO this is not the correct callback class, since NSMBW has
-    // the A, B, C names and also the _CALC_X from IScnObjCallback,
-    // so there must be a different callback interface
-    class mdlCallback_c : public nw4r::g3d::IScnObjCallback {
+    class mdlCallback_c : public nw4r::g3d::ICalcWorldCallback {
     public:
         mdlCallback_c();
         virtual ~mdlCallback_c();
diff --git a/include/nw4r/g3d/g3d_calcworld.h b/include/nw4r/g3d/g3d_calcworld.h
index 7ea59a78..7c9ef69d 100644
--- a/include/nw4r/g3d/g3d_calcworld.h
+++ b/include/nw4r/g3d/g3d_calcworld.h
@@ -23,6 +23,17 @@ struct FuncObjCalcWorld {
 
 class WorldMtxManip {};
 
+// Name from ketteiban
+class ICalcWorldCallback {
+public:
+    virtual ~ICalcWorldCallback() {}
+
+    virtual void ExecCallbackA(nw4r::g3d::ChrAnmResult *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *) {}
+    virtual void ExecCallbackB(nw4r::g3d::WorldMtxManip *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *) {}
+    virtual void ExecCallbackC(nw4r::math::MTX34 *, nw4r::g3d::ResMdl, nw4r::g3d::FuncObjCalcWorld *) {}
+};
+
+
 void CalcWorld(math::MTX34 *, u32 *, const u8 *, const math::MTX34 *, ResMdl, AnmObjChr *, FuncObjCalcWorld *, u32);
 
 void CalcWorld(math::MTX34 *, u32 *, const u8 *, const math::MTX34 *, ResMdl, AnmObjChr *, FuncObjCalcWorld *);
diff --git a/include/nw4r/g3d/g3d_scnmdlsmpl.h b/include/nw4r/g3d/g3d_scnmdlsmpl.h
index f788a0e9..8082a4a2 100644
--- a/include/nw4r/g3d/g3d_scnmdlsmpl.h
+++ b/include/nw4r/g3d/g3d_scnmdlsmpl.h
@@ -4,6 +4,7 @@
 #include "nw4r/math/math_types.h"
 #include "nw4r/g3d/g3d_resmdl.h"
 #include "nw4r/g3d/g3d_scnobj.h"
+#include "nw4r/g3d/g3d_calcworld.h"
 
 namespace nw4r {
 namespace g3d {
@@ -75,6 +76,10 @@ class ScnMdlSimple : public ScnLeaf {
         return mNumViewMtx;
     }
 
+    void SetCalcWorldCallback(ICalcWorldCallback *cb) {
+        mpCalcWorldCallback = cb;
+    }
+
 private:
     ResMdl mResMdl;               // at 0xE8
     math::MTX34 *mWldMatrixArray; // at 0xEC
@@ -90,6 +95,8 @@ class ScnMdlSimple : public ScnLeaf {
     void *mByteCodeMix;     // at 0x10C
     void *mByteCodeDrawOpa; // at 0x110
     void *mByteCodeDrawXlu; // at 0x114
+    UNKWORD WORD_0x118;
+    ICalcWorldCallback *mpCalcWorldCallback; // at 0x11C
 
     NW4R_G3D_TYPE_OBJ_DECL(ScnMdlSimple);
 };
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index acd027e0..9d908857 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -14,7 +14,8 @@ mdl_c::mdlCallback_c::~mdlCallback_c() {}
 
 void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::g3d::ResMdl mdl,
         nw4r::g3d::FuncObjCalcWorld *o) {
-    nw4r::g3d::ChrAnmResult *resPtr = &mpNodes[o->GetNodeId()];
+    u16 nodeId = o->GetNodeId();
+    nw4r::g3d::ChrAnmResult *resPtr = &mpNodes[nodeId];
     if (mCalcRatio.is0x18() && !mCalcRatio.isEnd()) {
         if (!mCalcRatio.is0x19()) {
             *result = *resPtr;
@@ -23,7 +24,8 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
             f32 f2 = mCalcRatio.get0x10();
             f32 f1 = mCalcRatio.get0x14();
 
-            // TODO the math operators cause ps inline assembly
+            // TODO clean up this code, what does it even do, why do operators
+            // break
             if ((flags & 8) == 0) {
                 result->VEC3_0x4.x = (result->VEC3_0x4.x * f1 + resPtr->VEC3_0x4.x * f2);
                 result->VEC3_0x4.y = (result->VEC3_0x4.y * f1 + resPtr->VEC3_0x4.y * f2);
@@ -36,8 +38,8 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
 
             nw4r::math::QUAT q1;
             nw4r::math::QUAT q2;
+
             C_QUATMtx(q1, resPtr->mMtx);
-            // TODO ...
             if ((flags & 0x20) == 0) {
                 C_QUATMtx(q2, result->mMtx);
             } else {
@@ -46,12 +48,28 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
                 q2.z = 0.0f;
                 q2.w = 1.0f;
             }
-            nw4r::math::QUAT q3;
-            C_QUATSlerp(q1, q2, q3, f1);
-            PSMTXQuat(result->mMtx, q3);
-            result->VEC3_0x10 = (flags & 0x40) == 0 ?
-                    nw4r::math::VEC3(result->VEC3_0x10.x * f1, result->VEC3_0x10.y * f1, result->VEC3_0x10.z * f1) :
-                    result->VEC3_0x10;
+
+            C_QUATSlerp(q1, q2, q1, f1);
+            // TODO weird hacks to force register order, probably more inlines
+            f32 tmp3, tmp2, tmp1;
+            tmp1 = result->mMtx._03;
+            tmp2 = result->mMtx._13;
+            tmp3 = result->mMtx._23;
+            nw4r::math::VEC3 tmp(tmp1, tmp2, tmp3);
+            PSMTXQuat(result->mMtx, q1);
+            result->mMtx._03 = tmp.x;
+            result->mMtx._13 = tmp.y;
+            result->mMtx._23 = tmp.z;
+            if ((flags & 0x40) == 0) {
+                result->mMtx._03 = tmp.x * f1;
+                result->mMtx._13 = tmp.y * f1;
+                result->mMtx._23 = tmp.z * f1;
+            }
+            u32 flags2 = result->mFlags & ~(0x80000000 | 0x80000040 | 0x80000020 | 0x80000008);
+            result->mMtx._03 += resPtr->mMtx._03 * f2;
+            result->mMtx._13 += resPtr->mMtx._13 * f2;
+            result->mMtx._23 += resPtr->mMtx._23 * f2;
+            result->mFlags = flags2;
             *resPtr = *result;
         }
     } else {
@@ -59,7 +77,7 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
     }
 
     if (mpBaseCallback != nullptr) {
-        mpBaseCallback->timingA(mNumNode, result, mdl);
+        mpBaseCallback->timingA(nodeId, result, mdl);
     }
 }
 
@@ -69,9 +87,11 @@ void mdl_c::mdlCallback_c::ExecCallbackB(nw4r::g3d::WorldMtxManip *m, nw4r::g3d:
     if (mpBaseCallback != nullptr) {
         mpBaseCallback->timingB(nodeId, m, mdl);
     }
+    // Not sure what this does
     u32 num = mdl.GetResNodeNumEntries();
-    u32 numInc = num + 1;
-    o->SetNodeId(numInc - (numInc / num) * num);
+    u32 nodeInc = nodeId + 1;
+    u32 tmp = (nodeInc / num);
+    o->SetNodeId(nodeInc - tmp * num);
 }
 
 void mdl_c::mdlCallback_c::ExecCallbackC(nw4r::math::MTX34 *mat, nw4r::g3d::ResMdl mdl, nw4r::g3d::FuncObjCalcWorld *) {
@@ -185,7 +205,7 @@ bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c
     }
     nw4r::g3d::ScnMdlSimple *sMdl;
     sMdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
-    sMdl->SetCallback(mpCallback);
+    sMdl->SetCalcWorldCallback(mpCallback);
     sMdl->EnableScnObjCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
     setCallback(nullptr);
     return true;

From 4c6b151f611826b52c8477b5ed0a76f47e9c458b Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sun, 23 Jun 2024 11:51:24 +0200
Subject: [PATCH 15/33] cleanup

---
 src/REL/d/a/obj/d_a_obj_sun_light.cpp | 1 -
 src/m/m3d/m_mdl.cpp                   | 4 ----
 2 files changed, 5 deletions(-)

diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index 7f03ecfe..fb8eaf27 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -10,7 +10,6 @@ extern "C" void fn_801B42D0(nw4r::g3d::ResFile *);
 extern "C" void fn_801B4320(nw4r::g3d::ResFile *);
 
 bool dAcOsunLight_c::createHeap() {
-    // TODO stack order shuffle
     mBrres = CurrentStageArcManager::sInstance->getData("g3d/stage.brres");
     fn_801B42D0(&mBrres);
     fn_801B4320(&mBrres);
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index 9d908857..a5709388 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -105,7 +105,6 @@ bool mdl_c::mdlCallback_c::create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u3
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
-    0.0f;
 
     u32 size = 0;
     if (pSize == nullptr) {
@@ -176,14 +175,11 @@ bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c
         pSize2 = &tmp2;
     }
 
-    bool success;
-
     if (!smdl_c::create(mdl, alloc, bufferOption, nView, pSize1)) {
         return false;
     }
 
     if (cb == nullptr) {
-        // TODO sizeof
         mpOwnedCallback = (mdlCallback_c *)alloc->alloc(sizeof(mdlCallback_c));
         if (mpOwnedCallback == nullptr) {
             remove();

From b43adc82a38452f1326c801df0db845e16ffc791 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sun, 23 Jun 2024 19:25:41 +0200
Subject: [PATCH 16/33] m_fanm OK

---
 config/SOUE01/symbols.txt     |  8 ++++----
 configure.py                  |  2 +-
 include/m/m3d/m_fanm.h        |  3 +++
 include/m/m3d/m_scnleaf.h     |  4 +++-
 include/nw4r/g3d/g3d_scnobj.h |  6 ++++++
 src/m/m3d/m_fanm.cpp          | 26 ++++++++++++++------------
 src/m/m3d/m_scnLeaf.cpp       | 19 ++++++++++++++++++-
 7 files changed, 49 insertions(+), 19 deletions(-)

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index f968a58b..0b663acb 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17640,11 +17640,11 @@ entry__Q23m3d9scnLeaf_cFv = .text:0x802EBCF0; // type:function size:0x8
 setOption__Q23m3d9scnLeaf_cFUlUl = .text:0x802EBD00; // type:function size:0x14
 setScale__Q23m3d9scnLeaf_cFfff = .text:0x802EBD20; // type:function size:0x14
 setScale__Q23m3d9scnLeaf_cFRCQ34nw4r4math4VEC3 = .text:0x802EBD40; // type:function size:0x20
-fn_802EBD60 = .text:0x802EBD60; // type:function size:0x28
+getScale__Q23m3d9scnLeaf_cCFPQ34nw4r4math4VEC3 = .text:0x802EBD60; // type:function size:0x28
 setLocalMtx__Q23m3d9scnLeaf_cFPCQ34nw4r4math5MTX34 = .text:0x802EBD90; // type:function size:0x10
 getLocalMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDA0; // type:function size:0x10
-fn_802EBDB0 = .text:0x802EBDB0; // type:function size:0xC
-fn_802EBDC0 = .text:0x802EBDC0; // type:function size:0x10
+getLocalMtx__Q23m3d9scnLeaf_cCFv = .text:0x802EBDB0; // type:function size:0xC
+getWorldMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDC0; // type:function size:0x10
 getViewMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDD0; // type:function size:0x10
 fn_802EBDE0 = .text:0x802EBDE0; // type:function size:0xC
 fn_802EBDF0 = .text:0x802EBDF0; // type:function size:0x10
@@ -24978,7 +24978,7 @@ fn_8045FD70 = .text:0x8045FD70; // type:function size:0x30
 fn_8045FDA0 = .text:0x8045FDA0; // type:function size:0xC
 fn_8045FDB0 = .text:0x8045FDB0; // type:function size:0x234
 G3dInit__Q24nw4r3g3dFb = .text:0x8045FFF0; // type:function size:0xF0
-fn_804600E0 = .text:0x804600E0; // type:function size:0x8
+G3dReset__Q24nw4r3g3dFv = .text:0x804600E0; // type:function size:0x8
 Construct__Q34nw4r3g3d7ScnProcFP12MEMAllocatorPUlPFPQ34nw4r3g3d7ScnProcb_vbbUl = .text:0x804600F0; // type:function size:0x110
 fn_80460200 = .text:0x80460200; // type:function size:0x100
 fn_80460300 = .text:0x80460300; // type:function size:0x68
diff --git a/configure.py b/configure.py
index 60670cbc..570e0d81 100644
--- a/configure.py
+++ b/configure.py
@@ -348,7 +348,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_banm.cpp"),
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
             Object(Matching, "m/m3d/m_calc_ratio.cpp"),
-            Object(NonMatching, "m/m3d/m_fanm.cpp"),
+            Object(Matching, "m/m3d/m_fanm.cpp"),
             Object(Matching, "m/m3d/m_mdl.cpp"),
             Object(NonMatching, "m/m3d/m_scnLeaf.cpp"),
             Object(Matching, "m/m3d/m_smdl.cpp"),
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index d294963d..e4f0a969 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -29,6 +29,7 @@ class fanm_c : public banm_c {
     bool checkFrame(f32) const;
     bool unk_802EAE70() const;
 
+
     inline void setPlayState(playMode_e state) {
         mPlayState = state;
     }
@@ -47,6 +48,8 @@ class fanm_c : public banm_c {
     }
 
 private:
+    f32 getNextFrame(f32 frame, f32 rate, bool rateWasNegative);
+
     f32 mEndFrame;
     f32 mStartFrame;
     f32 mCurrentFrame;
diff --git a/include/m/m3d/m_scnleaf.h b/include/m/m3d/m_scnleaf.h
index ab2ce20d..1bbcb484 100644
--- a/include/m/m3d/m_scnleaf.h
+++ b/include/m/m3d/m_scnleaf.h
@@ -23,10 +23,12 @@ class scnLeaf_c : UnkClass, EGG::Disposer {
     void setOption(u32 flag, u32 set);
     void setScale(f32, f32, f32);
     void setScale(const nw4r::math::VEC3 &);
+    void getScale(nw4r::math::VEC3 *) const;
 
     void setLocalMtx(const nw4r::math::MTX34 *);
     void getLocalMtx(nw4r::math::MTX34 *) const;
-
+    const nw4r::math::MTX34 *getLocalMtx() const;
+    void getWorldMtx(nw4r::math::MTX34 *) const;
     void getViewMtx(nw4r::math::MTX34 *) const;
 
     void calc(bool);
diff --git a/include/nw4r/g3d/g3d_scnobj.h b/include/nw4r/g3d/g3d_scnobj.h
index 73456000..0bf2dece 100644
--- a/include/nw4r/g3d/g3d_scnobj.h
+++ b/include/nw4r/g3d/g3d_scnobj.h
@@ -216,6 +216,12 @@ class ScnLeaf : public ScnObj {
         mScale = scale;
     }
 
+    inline void GetScale(math::VEC3* scale) const {
+        if (scale) {
+            *scale = mScale;
+        }
+    }
+
 private:
     math::VEC3 mScale;
 
diff --git a/src/m/m3d/m_fanm.cpp b/src/m/m3d/m_fanm.cpp
index 4eadfc98..33d8171c 100644
--- a/src/m/m3d/m_fanm.cpp
+++ b/src/m/m3d/m_fanm.cpp
@@ -17,31 +17,33 @@ void fanm_c::play() {
         rate *= -1.0f;
     }
     mCurrentFrame = frame;
+    mpAnmObj->SetFrame(getNextFrame(frame, rate, rateWasNegative));
+}
 
-    f32 newFrame;
-
+inline f32 fanm_c::getNextFrame(f32 frame, f32 rate, bool rateWasNegative) {
+    // This inline is needed for the double branch
     if (rateWasNegative || (mPlayState & 2)) {
-        newFrame = mStartFrame;
         if (frame >= rate + mStartFrame) {
-            newFrame = frame - rate;
+            return frame - rate;
         } else if ((mPlayState & 1) == 0) {
-            newFrame = frame + ((mEndFrame - rate) - mStartFrame);
-            // TODO there's a duplicate branch to the function end here
+            return frame + ((mEndFrame - rate) - mStartFrame);
+        } else {
+            return mStartFrame;
         }
     } else {
-        newFrame = frame + rate;
+        frame += rate;
         if ((mPlayState & 1) == 0) {
-            if (newFrame >= mEndFrame) {
-                newFrame = newFrame - mEndFrame;
+            if (frame >= mEndFrame) {
+                frame = frame - mEndFrame;
             }
         } else {
             f32 t = mEndFrame - 1.0f;
-            if (newFrame >= t) {
-                newFrame = t;
+            if (frame >= t) {
+                frame = t;
             }
         }
+        return frame;
     }
-    mpAnmObj->SetFrame(newFrame);
 }
 
 void fanm_c::set(f32 startFrame, playMode_e mode, f32 updateRate, f32 currentFrame) {
diff --git a/src/m/m3d/m_scnLeaf.cpp b/src/m/m3d/m_scnLeaf.cpp
index b67793c8..09bd34c4 100644
--- a/src/m/m3d/m_scnLeaf.cpp
+++ b/src/m/m3d/m_scnLeaf.cpp
@@ -1,3 +1,4 @@
+#include <m//m3d/m3d.h>
 #include <m/m3d/m_scnleaf.h>
 
 namespace m3d {
@@ -15,7 +16,7 @@ void scnLeaf_c::remove() {
     }
 }
 
-void scnLeaf_c::setOption(unsigned long flag, unsigned long set) {
+void scnLeaf_c::setOption(u32 flag, u32 set) {
     mpScnLeaf->SetScnObjOption(flag, set);
 }
 
@@ -23,6 +24,14 @@ void scnLeaf_c::setScale(f32 x, f32 y, f32 z) {
     mpScnLeaf->SetScale(x, y, z);
 }
 
+void scnLeaf_c::getScale(nw4r::math::VEC3 *vec) const {
+    mpScnLeaf->GetScale(vec);
+}
+
+int scnLeaf_c::entry() {
+    return pushBack(mpScnLeaf);
+}
+
 void scnLeaf_c::setScale(const nw4r::math::VEC3 &scale) {
     mpScnLeaf->SetScale(scale);
 }
@@ -35,6 +44,14 @@ void scnLeaf_c::getLocalMtx(nw4r::math::MTX34 *mtx) const {
     mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_LOCAL, mtx);
 }
 
+const nw4r::math::MTX34 *scnLeaf_c::getLocalMtx() const {
+    return mpScnLeaf->GetMtxPtr(nw4r::g3d::ScnObj::MTX_TYPE_LOCAL);
+}
+
+void scnLeaf_c::getWorldMtx(nw4r::math::MTX34 *mtx) const {
+    mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_WORLD, mtx);
+}
+
 void scnLeaf_c::getViewMtx(nw4r::math::MTX34 *mtx) const {
     mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_VIEW, mtx);
 }

From 11eed4b387a85752f05d759570abe8e47b93675b Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sun, 23 Jun 2024 19:39:42 +0200
Subject: [PATCH 17/33] simpler fanm match (thank you LagoLunatic!)

---
 include/m/m3d/m_fanm.h |  2 --
 src/m/m3d/m_fanm.cpp   | 24 +++++++++++-------------
 2 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index e4f0a969..dfbf027d 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -48,8 +48,6 @@ class fanm_c : public banm_c {
     }
 
 private:
-    f32 getNextFrame(f32 frame, f32 rate, bool rateWasNegative);
-
     f32 mEndFrame;
     f32 mStartFrame;
     f32 mCurrentFrame;
diff --git a/src/m/m3d/m_fanm.cpp b/src/m/m3d/m_fanm.cpp
index 33d8171c..21cd0686 100644
--- a/src/m/m3d/m_fanm.cpp
+++ b/src/m/m3d/m_fanm.cpp
@@ -17,33 +17,31 @@ void fanm_c::play() {
         rate *= -1.0f;
     }
     mCurrentFrame = frame;
-    mpAnmObj->SetFrame(getNextFrame(frame, rate, rateWasNegative));
-}
 
-inline f32 fanm_c::getNextFrame(f32 frame, f32 rate, bool rateWasNegative) {
-    // This inline is needed for the double branch
+    f32 newFrame;
     if (rateWasNegative || (mPlayState & 2)) {
         if (frame >= rate + mStartFrame) {
-            return frame - rate;
+            newFrame = frame - rate;
         } else if ((mPlayState & 1) == 0) {
-            return frame + ((mEndFrame - rate) - mStartFrame);
+            newFrame = frame + ((mEndFrame - rate) - mStartFrame);
         } else {
-            return mStartFrame;
+            newFrame = mStartFrame;
         }
     } else {
-        frame += rate;
+        newFrame = frame + rate;
         if ((mPlayState & 1) == 0) {
-            if (frame >= mEndFrame) {
-                frame = frame - mEndFrame;
+            if (newFrame >= mEndFrame) {
+                newFrame = newFrame - mEndFrame;
             }
         } else {
             f32 t = mEndFrame - 1.0f;
-            if (frame >= t) {
-                frame = t;
+            if (newFrame >= t) {
+                newFrame = t;
             }
         }
-        return frame;
     }
+
+    mpAnmObj->SetFrame(newFrame);
 }
 
 void fanm_c::set(f32 startFrame, playMode_e mode, f32 updateRate, f32 currentFrame) {

From 4797d9edd1efad3ee62cbc70a99da6c3046c0d61 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Sun, 23 Jun 2024 20:02:28 +0200
Subject: [PATCH 18/33] m_scnmdl OK

---
 config/SOUE01/splits.txt          |  2 +-
 config/SOUE01/symbols.txt         | 18 ++++++------
 configure.py                      |  2 +-
 include/m/m3d/m_scnleaf.h         |  7 +++++
 include/nw4r/g3d/g3d_scnmdlsmpl.h |  1 +
 include/nw4r/g3d/g3d_scnobj.h     |  4 +++
 src/m/m3d/m_mdl.cpp               |  2 +-
 src/m/m3d/m_scnLeaf.cpp           | 49 ++++++++++++++++++++++++++-----
 8 files changed, 65 insertions(+), 20 deletions(-)

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 921f9061..1eec0c9e 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -340,7 +340,7 @@ m/m3d/m_mdl.cpp:
 	.data       start:0x805426C0 end:0x80542708
 	.sdata2     start:0x8057CD60 end:0x8057CD68
 
-m/m3d/m_scnLeaf.cpp:
+m/m3d/m_scnleaf.cpp:
 	.text       start:0x802EBBD0 end:0x802EBFF8
 	.data       start:0x80542708 end:0x8054272C
 
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 0b663acb..b501662f 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17646,13 +17646,13 @@ getLocalMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDA0; // type:f
 getLocalMtx__Q23m3d9scnLeaf_cCFv = .text:0x802EBDB0; // type:function size:0xC
 getWorldMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDC0; // type:function size:0x10
 getViewMtx__Q23m3d9scnLeaf_cCFPQ34nw4r4math5MTX34 = .text:0x802EBDD0; // type:function size:0x10
-fn_802EBDE0 = .text:0x802EBDE0; // type:function size:0xC
-fn_802EBDF0 = .text:0x802EBDF0; // type:function size:0x10
-fn_802EBE00 = .text:0x802EBE00; // type:function size:0x8
-fn_802EBE10 = .text:0x802EBE10; // type:function size:0x8
+setCallback__Q23m3d9scnLeaf_cFPQ34nw4r3g3d15IScnObjCallback = .text:0x802EBDE0; // type:function size:0xC
+swapCallback__Q23m3d9scnLeaf_cFPQ34nw4r3g3d15IScnObjCallback = .text:0x802EBDF0; // type:function size:0x10
+enableCallbackTiming__Q23m3d9scnLeaf_cFQ44nw4r3g3d6ScnObj6Timing = .text:0x802EBE00; // type:function size:0x8
+enableCallbackOp__Q23m3d9scnLeaf_cFQ44nw4r3g3d6ScnObj6ExecOp = .text:0x802EBE10; // type:function size:0x8
 calc__Q23m3d9scnLeaf_cFb = .text:0x802EBE20; // type:function size:0x78
 calcVtx__Q23m3d9scnLeaf_cFb = .text:0x802EBEA0; // type:function size:0x78
-fn_802EBF20 = .text:0x802EBF20; // type:function size:0x84
+calcView__Q23m3d9scnLeaf_cFPvb = .text:0x802EBF20; // type:function size:0x84
 setPriorityDraw__Q23m3d9scnLeaf_cFii = .text:0x802EBFB0; // type:function size:0x48
 fn_802EC000 = .text:0x802EC000; // type:function size:0x88
 fn_802EC090 = .text:0x802EC090; // type:function size:0x7C
@@ -24854,9 +24854,9 @@ fn_804583C0 = .text:0x804583C0; // type:function size:0xC
 fn_804583D0 = .text:0x804583D0; // type:function size:0x8
 SetPriorityDrawOpa__Q34nw4r3g3d6ScnObjFi = .text:0x804583E0; // type:function size:0x24
 SetPriorityDrawXlu__Q34nw4r3g3d6ScnObjFi = .text:0x80458410; // type:function size:0x24
-fn_80458440 = .text:0x80458440; // type:function size:0x40
+EnableScnObjCallbackTiming__Q34nw4r3g3d6ScnObjFQ44nw4r3g3d6ScnObj6Timing = .text:0x80458440; // type:function size:0x40
 fn_80458480 = .text:0x80458480; // type:function size:0x40
-fn_804584C0 = .text:0x804584C0; // type:function size:0x14
+EnableScnObjCallbackExecOp__Q34nw4r3g3d6ScnObjFQ44nw4r3g3d6ScnObj6ExecOp = .text:0x804584C0; // type:function size:0x14
 fn_804584E0 = .text:0x804584E0; // type:function size:0x80
 fn_80458560 = .text:0x80458560; // type:function size:0x4
 fn_80458570 = .text:0x80458570; // type:function size:0x60
@@ -24935,7 +24935,7 @@ fn_8045CAD0 = .text:0x8045CAD0; // type:function size:0xE0
 fn_8045CBB0 = .text:0x8045CBB0; // type:function size:0x5C
 fn_8045CC10 = .text:0x8045CC10; // type:function size:0x5C
 fn_8045CC70 = .text:0x8045CC70; // type:function size:0xB8
-EnableScnObjCallbackTiming__Q34nw4r3g3d6ScnObjFQ44nw4r3g3d6ScnObj6Timing = .text:0x8045CD30; // type:function size:0x40
+EnableScnMdlCallbackTiming__Q34nw4r3g3d12ScnMdlSimpleFQ44nw4r3g3d6ScnObj6Timing = .text:0x8045CD30; // type:function size:0x40
 fn_8045CD70 = .text:0x8045CD70; // type:function size:0x24
 fn_8045CDA0 = .text:0x8045CDA0; // type:function size:0x34
 fn_8045CDE0 = .text:0x8045CDE0; // type:function size:0x34
@@ -45863,7 +45863,7 @@ lbl_8057CD48 = .sdata2:0x8057CD48; // type:object size:0x4 data:float
 lbl_8057CD4C = .sdata2:0x8057CD4C; // type:object size:0x4 data:float
 lbl_8057CD50 = .sdata2:0x8057CD50; // type:object size:0x4 data:float
 lbl_8057CD54 = .sdata2:0x8057CD54; // type:object size:0x4 data:float
-lbl_8057CD58 = .sdata2:0x8057CD58; // type:object size:0x8 data:float
+lbl_8057CD58 = .sdata2:0x8057CD58; // type:object size:0x4 data:float
 lbl_8057CD60 = .sdata2:0x8057CD60; // type:object size:0x4 data:float
 lbl_8057CD64 = .sdata2:0x8057CD64; // type:object size:0x4 data:float
 lbl_8057CD68 = .sdata2:0x8057CD68; // type:object size:0x4 data:float
diff --git a/configure.py b/configure.py
index 570e0d81..a3504658 100644
--- a/configure.py
+++ b/configure.py
@@ -350,7 +350,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_calc_ratio.cpp"),
             Object(Matching, "m/m3d/m_fanm.cpp"),
             Object(Matching, "m/m3d/m_mdl.cpp"),
-            Object(NonMatching, "m/m3d/m_scnLeaf.cpp"),
+            Object(Matching, "m/m3d/m_scnleaf.cpp"),
             Object(Matching, "m/m3d/m_smdl.cpp"),
             Object(Matching, "m/m_allocator.cpp"),
             Object(Matching, "m/m_angle.cpp"),
diff --git a/include/m/m3d/m_scnleaf.h b/include/m/m3d/m_scnleaf.h
index 1bbcb484..7a33ba61 100644
--- a/include/m/m3d/m_scnleaf.h
+++ b/include/m/m3d/m_scnleaf.h
@@ -31,8 +31,15 @@ class scnLeaf_c : UnkClass, EGG::Disposer {
     void getWorldMtx(nw4r::math::MTX34 *) const;
     void getViewMtx(nw4r::math::MTX34 *) const;
 
+    void setCallback(nw4r::g3d::IScnObjCallback *cb);
+    nw4r::g3d::IScnObjCallback *swapCallback(nw4r::g3d::IScnObjCallback *cb);
+
+    void enableCallbackTiming(nw4r::g3d::ScnObj::Timing);
+    void enableCallbackOp(nw4r::g3d::ScnObj::ExecOp);
+
     void calc(bool);
     void calcVtx(bool);
+    void calcView(void *, bool);
 
     void setPriorityDraw(int, int);
 
diff --git a/include/nw4r/g3d/g3d_scnmdlsmpl.h b/include/nw4r/g3d/g3d_scnmdlsmpl.h
index 8082a4a2..2b64af30 100644
--- a/include/nw4r/g3d/g3d_scnmdlsmpl.h
+++ b/include/nw4r/g3d/g3d_scnmdlsmpl.h
@@ -57,6 +57,7 @@ class ScnMdlSimple : public ScnLeaf {
     virtual bool RemoveAnmObj(AnmObjType type);
 
     const u8 *GetByteCode(ByteCodeType) const;
+    void EnableScnMdlCallbackTiming(Timing);
 
     const ResMdl GetResMdl() const {
         return mResMdl;
diff --git a/include/nw4r/g3d/g3d_scnobj.h b/include/nw4r/g3d/g3d_scnobj.h
index 0bf2dece..3b8cdda0 100644
--- a/include/nw4r/g3d/g3d_scnobj.h
+++ b/include/nw4r/g3d/g3d_scnobj.h
@@ -92,6 +92,10 @@ class ScnObj : public G3dObj {
         mCallback = cb;
     }
 
+    IScnObjCallback* GetScnObjCallback() {
+        return mCallback;
+    }
+
     const math::MTX34 *GetMtxPtr(ScnObjMtxType type) const {
         return &mMatrices[type];
     }
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index a5709388..aaf03c89 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -202,7 +202,7 @@ bool mdl_c::create(nw4r::g3d::ResMdl mdl, mdl_c::mdlCallback_c *cb, mAllocator_c
     nw4r::g3d::ScnMdlSimple *sMdl;
     sMdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
     sMdl->SetCalcWorldCallback(mpCallback);
-    sMdl->EnableScnObjCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
+    sMdl->EnableScnMdlCallbackTiming(nw4r::g3d::ScnObj::TIMING_ALL);
     setCallback(nullptr);
     return true;
 }
diff --git a/src/m/m3d/m_scnLeaf.cpp b/src/m/m3d/m_scnLeaf.cpp
index 09bd34c4..50873e8a 100644
--- a/src/m/m3d/m_scnLeaf.cpp
+++ b/src/m/m3d/m_scnLeaf.cpp
@@ -16,6 +16,10 @@ void scnLeaf_c::remove() {
     }
 }
 
+int scnLeaf_c::entry() {
+    return pushBack(mpScnLeaf);
+}
+
 void scnLeaf_c::setOption(u32 flag, u32 set) {
     mpScnLeaf->SetScnObjOption(flag, set);
 }
@@ -24,18 +28,14 @@ void scnLeaf_c::setScale(f32 x, f32 y, f32 z) {
     mpScnLeaf->SetScale(x, y, z);
 }
 
-void scnLeaf_c::getScale(nw4r::math::VEC3 *vec) const {
-    mpScnLeaf->GetScale(vec);
-}
-
-int scnLeaf_c::entry() {
-    return pushBack(mpScnLeaf);
-}
-
 void scnLeaf_c::setScale(const nw4r::math::VEC3 &scale) {
     mpScnLeaf->SetScale(scale);
 }
 
+void scnLeaf_c::getScale(nw4r::math::VEC3 *vec) const {
+    mpScnLeaf->GetScale(vec);
+}
+
 void scnLeaf_c::setLocalMtx(const nw4r::math::MTX34 *mtx) {
     mpScnLeaf->SetMtx(nw4r::g3d::ScnObj::MTX_TYPE_LOCAL, mtx);
 }
@@ -56,6 +56,25 @@ void scnLeaf_c::getViewMtx(nw4r::math::MTX34 *mtx) const {
     mpScnLeaf->GetMtx(nw4r::g3d::ScnObj::MTX_TYPE_VIEW, mtx);
 }
 
+// unofficial
+void scnLeaf_c::setCallback(nw4r::g3d::IScnObjCallback *cb) {
+    mpScnLeaf->SetCallback(cb);
+}
+
+nw4r::g3d::IScnObjCallback *scnLeaf_c::swapCallback(nw4r::g3d::IScnObjCallback *cb) {
+    nw4r::g3d::IScnObjCallback *tmp = mpScnLeaf->GetScnObjCallback();
+    mpScnLeaf->SetCallback(cb);
+    return tmp;
+}
+
+void scnLeaf_c::enableCallbackTiming(nw4r::g3d::ScnObj::Timing timing) {
+    mpScnLeaf->EnableScnObjCallbackTiming(timing);
+}
+
+void scnLeaf_c::enableCallbackOp(nw4r::g3d::ScnObj::ExecOp op) {
+    mpScnLeaf->EnableScnObjCallbackExecOp(op);
+}
+
 void scnLeaf_c::calc(bool b) {
     setOption(/* DISABLE_CALC_WORLD */ 0x02, false);
     mpScnLeaf->G3dProc(1, 0, nullptr);
@@ -72,4 +91,18 @@ void scnLeaf_c::calcVtx(bool b) {
     }
 }
 
+/** Unknown name/arg type */
+void scnLeaf_c::calcView(void *p, bool b) {
+    setOption(/* DISABLE_CALC_VIEW */ 0x05, false);
+    mpScnLeaf->G3dProc(4, 0, p);
+    if (b == false) {
+        setOption(/* DISABLE_CALC_VIEW */ 0x05, true);
+    }
+}
+
+void scnLeaf_c::setPriorityDraw(int opa, int xlu) {
+    mpScnLeaf->SetPriorityDrawOpa(opa);
+    mpScnLeaf->SetPriorityDrawXlu(xlu);
+}
+
 } // namespace m3d

From 5a5811a1e23be2dd20e6117b9f8d24a6a82ad681 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Tue, 25 Jun 2024 20:02:16 +0200
Subject: [PATCH 19/33] dAcOmoleSoil_c OK

---
 .../rels/d_a_obj_mole_soilNP/splits.txt       |   3 +
 .../rels/d_a_obj_mole_soilNP/symbols.txt      | 114 ++++++++--------
 configure.py                                  |   2 +-
 include/d/a/obj/d_a_obj_mole_soil.h           |  39 ++++++
 include/d/a/obj/d_a_obj_sun_light.h           |   5 -
 include/m/m3d/m_smdl.h                        |   3 +
 include/m/m_mtx.h                             |   4 +
 include/s/README.txt                          |   6 +
 include/s/s_StateID.hpp                       |   4 +-
 include/s/s_StateInterfaces.hpp               |   4 +-
 include/s/s_StateMgr.hpp                      |   5 +
 src/REL/d/a/obj/d_a_obj_mole_soil.cpp         | 128 ++++++++++++++++++
 src/REL/d/a/obj/d_a_obj_ring.cpp              |   4 +-
 src/REL/d/a/obj/d_a_obj_sun_light.cpp         |   4 +-
 src/m/m3d/m_anmmatclr.cpp                     |   2 +-
 src/s/s_StateID.cpp                           |   4 +-
 16 files changed, 257 insertions(+), 74 deletions(-)
 create mode 100644 include/d/a/obj/d_a_obj_mole_soil.h

diff --git a/config/SOUE01/rels/d_a_obj_mole_soilNP/splits.txt b/config/SOUE01/rels/d_a_obj_mole_soilNP/splits.txt
index d317971d..2e881ae6 100644
--- a/config/SOUE01/rels/d_a_obj_mole_soilNP/splits.txt
+++ b/config/SOUE01/rels/d_a_obj_mole_soilNP/splits.txt
@@ -17,3 +17,6 @@ REL/global_destructor_chain.c:
 REL/d/a/obj/d_a_obj_mole_soil.cpp:
 	.text       start:0x000000F0 end:0x00000F18
 	.ctors      start:0x00000000 end:0x00000004
+	.rodata     start:0x00000000 end:0x00000030
+	.data       start:0x00000000 end:0x0000024C
+	.bss        start:0x00000008 end:0x000000C8
diff --git a/config/SOUE01/rels/d_a_obj_mole_soilNP/symbols.txt b/config/SOUE01/rels/d_a_obj_mole_soilNP/symbols.txt
index a60ebd39..1c3c22e8 100644
--- a/config/SOUE01/rels/d_a_obj_mole_soilNP/symbols.txt
+++ b/config/SOUE01/rels/d_a_obj_mole_soilNP/symbols.txt
@@ -3,64 +3,64 @@ _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
 __register_global_object = .text:0x00000070; // type:function size:0x1C scope:global
 __destroy_global_chain = .text:0x00000090; // type:function size:0x54 scope:global
-fn_101_F0 = .text:0x000000F0; // type:function size:0xC0
-fn_101_1B0 = .text:0x000001B0; // type:function size:0x58
-fn_101_210 = .text:0x00000210; // type:function size:0x6C
-fn_101_280 = .text:0x00000280; // type:function size:0xA0
-fn_101_320 = .text:0x00000320; // type:function size:0xA4
-fn_101_3D0 = .text:0x000003D0; // type:function size:0x9C
-fn_101_470 = .text:0x00000470; // type:function size:0xCC
-fn_101_540 = .text:0x00000540; // type:function size:0x10
-fn_101_550 = .text:0x00000550; // type:function size:0x8
-fn_101_560 = .text:0x00000560; // type:function size:0x174
-fn_101_6E0 = .text:0x000006E0; // type:function size:0x10
-fn_101_6F0 = .text:0x000006F0; // type:function size:0x10
-fn_101_700 = .text:0x00000700; // type:function size:0x34
-fn_101_740 = .text:0x00000740; // type:function size:0xC
-fn_101_750 = .text:0x00000750; // type:function size:0x4
-fn_101_760 = .text:0x00000760; // type:function size:0x4
-fn_101_770 = .text:0x00000770; // type:function size:0xC
-fn_101_780 = .text:0x00000780; // type:function size:0x7C
-fn_101_800 = .text:0x00000800; // type:function size:0x24
-fn_101_830 = .text:0x00000830; // type:function size:0xC
-fn_101_840 = .text:0x00000840; // type:function size:0xA8
-fn_101_8F0 = .text:0x000008F0; // type:function size:0x24
-fn_101_920 = .text:0x00000920; // type:function size:0xCC
-fn_101_9F0 = .text:0x000009F0; // type:function size:0x60
-fn_101_A50 = .text:0x00000A50; // type:function size:0xC
-fn_101_A60 = .text:0x00000A60; // type:function size:0x1C
-fn_101_A80 = .text:0x00000A80; // type:function size:0x1C
-fn_101_AA0 = .text:0x00000AA0; // type:function size:0x1C
-fn_101_AC0 = .text:0x00000AC0; // type:function size:0x10
-fn_101_AD0 = .text:0x00000AD0; // type:function size:0x10
-fn_101_AE0 = .text:0x00000AE0; // type:function size:0x10
-fn_101_AF0 = .text:0x00000AF0; // type:function size:0x10
-fn_101_B00 = .text:0x00000B00; // type:function size:0x10
-fn_101_B10 = .text:0x00000B10; // type:function size:0x10
-fn_101_B20 = .text:0x00000B20; // type:function size:0x30
-fn_101_B50 = .text:0x00000B50; // type:function size:0x30
-fn_101_B80 = .text:0x00000B80; // type:function size:0x30
-fn_101_BB0 = .text:0x00000BB0; // type:function size:0x27C
-fn_101_E30 = .text:0x00000E30; // type:function size:0x58
-fn_101_E90 = .text:0x00000E90; // type:function size:0x88
+dAcOmoleSoil_c_classInit__Fv = .text:0x000000F0; // type:function size:0xC0
+__dt__27sFState_c<14dAcOmoleSoil_c>Fv = .text:0x000001B0; // type:function size:0x58
+__dt__30sFStateFct_c<14dAcOmoleSoil_c>Fv = .text:0x00000210; // type:function size:0x6C
+__dt__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000280; // type:function size:0xA0
+__dt__53sFStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c>Fv = .text:0x00000320; // type:function size:0xA4
+createHeap__14dAcOmoleSoil_cFv = .text:0x000003D0; // type:function size:0x9C
+create__14dAcOmoleSoil_cFv = .text:0x00000470; // type:function size:0xCC
+changeState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>FRC12sStateIDIf_c = .text:0x00000540; // type:function size:0x10
+doDelete__14dAcOmoleSoil_cFv = .text:0x00000550; // type:function size:0x8
+actorExecute__14dAcOmoleSoil_cFv = .text:0x00000560; // type:function size:0x174
+getStateID__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000006E0; // type:function size:0x10
+executeState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000006F0; // type:function size:0x10
+draw__14dAcOmoleSoil_cFv = .text:0x00000700; // type:function size:0x34
+initializeState_Wait__14dAcOmoleSoil_cFv = .text:0x00000740; // type:function size:0xC
+executeState_Wait__14dAcOmoleSoil_cFv = .text:0x00000750; // type:function size:0x4
+finalizeState_Wait__14dAcOmoleSoil_cFv = .text:0x00000760; // type:function size:0x4
+initializeState_Appear__14dAcOmoleSoil_cFv = .text:0x00000770; // type:function size:0xC
+executeState_Appear__14dAcOmoleSoil_cFv = .text:0x00000780; // type:function size:0x7C
+finalizeState_Appear__14dAcOmoleSoil_cFv = .text:0x00000800; // type:function size:0x24
+initializeState_DisAppear__14dAcOmoleSoil_cFv = .text:0x00000830; // type:function size:0xC
+executeState_DisAppear__14dAcOmoleSoil_cFv = .text:0x00000840; // type:function size:0xA8
+finalizeState_DisAppear__14dAcOmoleSoil_cFv = .text:0x000008F0; // type:function size:0x24
+__dt__14dAcOmoleSoil_cFv = .text:0x00000920; // type:function size:0xCC
+build__30sFStateFct_c<14dAcOmoleSoil_c>FRC12sStateIDIf_c = .text:0x000009F0; // type:function size:0x60
+dispose__30sFStateFct_c<14dAcOmoleSoil_c>FRP10sStateIf_c = .text:0x00000A50; // type:function size:0xC
+initialize__27sFState_c<14dAcOmoleSoil_c>Fv = .text:0x00000A60; // type:function size:0x1C
+execute__27sFState_c<14dAcOmoleSoil_c>Fv = .text:0x00000A80; // type:function size:0x1C
+finalize__27sFState_c<14dAcOmoleSoil_c>Fv = .text:0x00000AA0; // type:function size:0x1C
+initializeState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000AC0; // type:function size:0x10
+finalizeState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000AD0; // type:function size:0x10
+refreshState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000AE0; // type:function size:0x10
+getState__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000AF0; // type:function size:0x10
+getNewStateID__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000B00; // type:function size:0x10
+getOldStateID__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x00000B10; // type:function size:0x10
+finalizeState__29sFStateID_c<14dAcOmoleSoil_c>CFR14dAcOmoleSoil_c = .text:0x00000B20; // type:function size:0x30
+executeState__29sFStateID_c<14dAcOmoleSoil_c>CFR14dAcOmoleSoil_c = .text:0x00000B50; // type:function size:0x30
+initializeState__29sFStateID_c<14dAcOmoleSoil_c>CFR14dAcOmoleSoil_c = .text:0x00000B80; // type:function size:0x30
+__sinit_\d_a_obj_mole_soil_cpp = .text:0x00000BB0; // type:function size:0x27C scope:local
+__dt__29sFStateID_c<14dAcOmoleSoil_c>Fv = .text:0x00000E30; // type:function size:0x58
+isSameName__29sFStateID_c<14dAcOmoleSoil_c>CFPCc = .text:0x00000E90; // type:function size:0x88
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
 __destroy_global_chain_reference = .dtors:0x00000000; // type:object size:0x4 scope:global
-lbl_101_rodata_0 = .rodata:0x00000000; // type:object size:0x8 data:float
-lbl_101_rodata_8 = .rodata:0x00000008; // type:object size:0x10 data:float
-lbl_101_rodata_18 = .rodata:0x00000018; // type:object size:0x4 data:float
-lbl_101_rodata_1C = .rodata:0x0000001C; // type:object size:0x14 data:float
-lbl_101_data_0 = .data:0x00000000; // type:object size:0x10 data:4byte
-lbl_101_data_10 = .data:0x00000010; // type:object size:0xC
-lbl_101_data_1C = .data:0x0000001C; // type:object size:0x1C data:string
-lbl_101_data_38 = .data:0x00000038; // type:object size:0x80
-lbl_101_data_B8 = .data:0x000000B8; // type:object size:0x30
-lbl_101_data_E8 = .data:0x000000E8; // type:object size:0x30
-lbl_101_data_118 = .data:0x00000118; // type:object size:0x18
-lbl_101_data_130 = .data:0x00000130; // type:object size:0xE8
-lbl_101_data_218 = .data:0x00000218; // type:object size:0x34
+lbl_101_rodata_0 = .rodata:0x00000000; // type:object size:0x8 scope:local data:float
+lbl_101_rodata_8 = .rodata:0x00000008; // type:object size:0x10 scope:local data:float
+lbl_101_rodata_18 = .rodata:0x00000018; // type:object size:0x4 scope:local data:float
+lbl_101_rodata_1C = .rodata:0x0000001C; // type:object size:0x14 scope:local data:float
+g_profile_OBJ_MOLE_SOIL = .data:0x00000000; // type:object size:0x10 data:4byte
+lbl_101_data_10 = .data:0x00000010; // type:object size:0xA scope:local data:string
+lbl_101_data_1C = .data:0x0000001C; // type:object size:0x1C scope:local data:string
+__vt__14dAcOmoleSoil_c = .data:0x00000038; // type:object size:0x80
+__vt__53sFStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c> = .data:0x000000B8; // type:object size:0x30
+__vt__83sStateMgr_c<14dAcOmoleSoil_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c> = .data:0x000000E8; // type:object size:0x30
+__vt__30sFStateFct_c<14dAcOmoleSoil_c> = .data:0x00000118; // type:object size:0x14
+__vt__27sFState_c<14dAcOmoleSoil_c> = .data:0x00000130; // type:object size:0x18
+__vt__29sFStateID_c<14dAcOmoleSoil_c> = .data:0x00000218; // type:object size:0x34
 __global_destructor_chain = .bss:0x00000000; // type:object size:0x4 scope:global
-lbl_101_bss_8 = .bss:0x00000008; // type:object size:0x10 data:4byte
-lbl_101_bss_18 = .bss:0x00000018; // type:object size:0x40 data:4byte
-lbl_101_bss_58 = .bss:0x00000058; // type:object size:0x40 data:4byte
-lbl_101_bss_98 = .bss:0x00000098; // type:object size:0x30 data:4byte
+lbl_101_bss_8 = .bss:0x00000008; // type:object size:0xC scope:local data:4byte
+StateID_Wait__14dAcOmoleSoil_c = .bss:0x00000018; // type:object size:0x30 data:4byte
+StateID_Appear__14dAcOmoleSoil_c = .bss:0x00000058; // type:object size:0x30 data:4byte
+StateID_DisAppear__14dAcOmoleSoil_c = .bss:0x00000098; // type:object size:0x30 data:4byte
diff --git a/configure.py b/configure.py
index a3504658..7c7cf228 100644
--- a/configure.py
+++ b/configure.py
@@ -888,7 +888,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_obj_megami_island", "REL/d/a/obj/d_a_obj_megami_island.cpp"),
     Rel(NonMatching, "d_a_obj_mg_pumpkin", "REL/d/a/obj/d_a_obj_mg_pumpkin.cpp"),
     Rel(NonMatching, "d_a_obj_mole_cover", "REL/d/a/obj/d_a_obj_mole_cover.cpp"),
-    Rel(NonMatching, "d_a_obj_mole_soil", "REL/d/a/obj/d_a_obj_mole_soil.cpp"),
+    Rel(Matching, "d_a_obj_mole_soil", "REL/d/a/obj/d_a_obj_mole_soil.cpp"),
     Rel(NonMatching, "d_a_obj_move_elec", "REL/d/a/obj/d_a_obj_move_elec.cpp"),
     Rel(NonMatching, "d_a_obj_move_lift_vol", "REL/d/a/obj/d_a_obj_move_lift_vol.cpp"),
     Rel(NonMatching, "d_a_obj_musasabi", "REL/d/a/obj/d_a_obj_musasabi.cpp"),
diff --git a/include/d/a/obj/d_a_obj_mole_soil.h b/include/d/a/obj/d_a_obj_mole_soil.h
new file mode 100644
index 00000000..34342bf8
--- /dev/null
+++ b/include/d/a/obj/d_a_obj_mole_soil.h
@@ -0,0 +1,39 @@
+#ifndef D_A_OBJ_MOLE_SOIL_H
+#define D_A_OBJ_MOLE_SOIL_H
+
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_smdl.h>
+#include <s/s_State.hpp>
+#include <s/s_StateMgr.hpp>
+
+class dAcOmoleSoil_c : public dAcObjBase_c {
+public:
+    dAcOmoleSoil_c() : mStateMgr(*this, sStateID::null) {}
+    virtual ~dAcOmoleSoil_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+    STATE_FUNC_DECLARE(dAcOmoleSoil_c, Wait);
+    STATE_FUNC_DECLARE(dAcOmoleSoil_c, Appear);
+    STATE_FUNC_DECLARE(dAcOmoleSoil_c, DisAppear);
+
+private:
+    const static f32 sHalfScale;
+    const static f32 sFullScale;
+
+    u8 getNextState() {
+        return mDesiredNextState;
+    }
+
+    nw4r::g3d::ResFile mBrres;
+    m3d::smdl_c mModel;
+    STATE_MGR_DECLARE(dAcOmoleSoil_c);
+    f32 mScale;
+    u8 mDesiredNextState;
+    bool field_0x391;
+};
+
+#endif
diff --git a/include/d/a/obj/d_a_obj_sun_light.h b/include/d/a/obj/d_a_obj_sun_light.h
index 38756d16..b3badc71 100644
--- a/include/d/a/obj/d_a_obj_sun_light.h
+++ b/include/d/a/obj/d_a_obj_sun_light.h
@@ -17,11 +17,6 @@ class dAcOsunLight_c : public dAcObjBase_c {
     virtual int draw() override;
     virtual int doDelete() override;
 
-    // This inline fixes stack alloc
-    inline bool createModel(nw4r::g3d::ResMdl mdl) {
-        return mModel.create(mdl, &heap_allocator, 0x120, 1, nullptr);
-    }
-
     bool isDay();
 
     STATE_FUNC_DECLARE(dAcOsunLight_c, Wait);
diff --git a/include/m/m3d/m_smdl.h b/include/m/m3d/m_smdl.h
index 7089d07a..e55f759a 100644
--- a/include/m/m3d/m_smdl.h
+++ b/include/m/m3d/m_smdl.h
@@ -15,6 +15,9 @@ class smdl_c : public bmdl_c, UnkClass2 {
     virtual ~smdl_c();
 
     bool create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize);
+    bool create(nw4r::g3d::ResMdl mdl, mAllocator_c *alloc, u32 bufferOption) {
+        return create(mdl, alloc, bufferOption, 1, nullptr);
+    }
 };
 
 } // namespace m3d
diff --git a/include/m/m_mtx.h b/include/m/m_mtx.h
index 8158e9ac..7172d8bc 100644
--- a/include/m/m_mtx.h
+++ b/include/m/m_mtx.h
@@ -38,6 +38,10 @@ class mMtx_c {
     /* 802f1c40 */ void rot(int, int); // does some werrd operation to rotate the matrix
     /* 802f1e60 */ bool quatRelated();
 
+    operator nw4r::math::MTX34*() {
+        return &nw4rm;
+    }
+
 public:
     union {
         EGG::Matrix34f mat;
diff --git a/include/s/README.txt b/include/s/README.txt
index 5be519ee..48466fb0 100644
--- a/include/s/README.txt
+++ b/include/s/README.txt
@@ -16,3 +16,9 @@ In order for vtable ordering to match, some classes had to be extracted to a new
 
 S_StateMethod_c's destructors calls back into another destructors. sStateMethodIf_c follows the example of
 the other abstract interface classes and provides this dtor.
+
+## operator== / != return BOOL instead of bool
+
+We're observing a lot of word-to-bool casts in code after these
+operators are invoked, and while there are ways to force the conversion,
+this seems the most reasonable.
diff --git a/include/s/s_StateID.hpp b/include/s/s_StateID.hpp
index 422d9143..4c2ee84f 100644
--- a/include/s/s_StateID.hpp
+++ b/include/s/s_StateID.hpp
@@ -27,8 +27,8 @@ class sStateID_c : public sStateIDIf_c {
     virtual bool isNull() const;
     virtual bool isEqual(const sStateIDIf_c &other) const;
 
-    virtual bool operator==(const sStateIDIf_c &other) const;
-    virtual bool operator!=(const sStateIDIf_c &other) const;
+    virtual BOOL operator==(const sStateIDIf_c &other) const;
+    virtual BOOL operator!=(const sStateIDIf_c &other) const;
 
     virtual bool isSameName(const char *name) const;
     virtual const char *name() const;
diff --git a/include/s/s_StateInterfaces.hpp b/include/s/s_StateInterfaces.hpp
index 8e28f44c..92101b2c 100644
--- a/include/s/s_StateInterfaces.hpp
+++ b/include/s/s_StateInterfaces.hpp
@@ -23,8 +23,8 @@ class sStateIDIf_c {
     virtual bool isNull() const = 0; ///<  Returns whether this is a null state.
     virtual bool isEqual(const sStateIDIf_c &other) const = 0; ///<  Returns whether both states have the same number.
 
-    virtual bool operator==(const sStateIDIf_c &other) const = 0; ///<  Overloaded equality operator, using ::isEqual.
-    virtual bool operator!=(const sStateIDIf_c &other) const = 0; ///<  Overloaded inequality operator, using ::isEqual.
+    virtual BOOL operator==(const sStateIDIf_c &other) const = 0; ///<  Overloaded equality operator, using ::isEqual.
+    virtual BOOL operator!=(const sStateIDIf_c &other) const = 0; ///<  Overloaded inequality operator, using ::isEqual.
 
     virtual bool isSameName(const char *name) const = 0; ///<  Returns whether this state ID is called @p name.
     virtual const char *name() const = 0; ///<  Returns the name of this state ID.
diff --git a/include/s/s_StateMgr.hpp b/include/s/s_StateMgr.hpp
index c043eb71..09f5f217 100644
--- a/include/s/s_StateMgr.hpp
+++ b/include/s/s_StateMgr.hpp
@@ -35,6 +35,11 @@ class sStateMgr_c : sStateMgrIf_c {
     virtual const sStateIDIf_c *getStateID() const { return mMethod.getStateID(); }
     virtual const sStateIDIf_c *getOldStateID() const { return mMethod.getOldStateID(); }
 
+    // SS addition
+    bool isState(const sStateIDIf_c& other) {
+        return *getStateID() == other;
+    }
+
 private:
     Check mCheck;
     Factory<T> mFactory;
diff --git a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
index e69de29b..72c9415e 100644
--- a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
+++ b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
@@ -0,0 +1,128 @@
+#include <d/a/obj/d_a_obj_mole_soil.h>
+#include <toBeSorted/arc_managers/current_stage_arc_manager.h>
+#include <toBeSorted/spawn_struct.h>
+
+SPECIAL_ACTOR_PROFILE(OBJ_MOLE_SOIL, dAcOmoleSoil_c, fProfile::OBJ_MOLE_SOIL, 0x008B, 0, 6);
+
+STATE_DEFINE(dAcOmoleSoil_c, Wait);
+STATE_DEFINE(dAcOmoleSoil_c, Appear);
+STATE_DEFINE(dAcOmoleSoil_c, DisAppear);
+
+const f32 dAcOmoleSoil_c::sHalfScale = 0.5f;
+const f32 dAcOmoleSoil_c::sFullScale = 1.0f;
+
+bool dAcOmoleSoil_c::createHeap() {
+    nw4r::g3d::ResFile file = getOarcResFile("MogumaMud");
+    if (!file.mFile.IsValid()) {
+        return false;
+    }
+    mBrres = file;
+    nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("MogumaMud");
+    // This whole code is a bit weird again
+    bool success1 = mModel.create(mdl, &heap_allocator, 0x120, 1, nullptr);
+    if (!success1) {
+        return success1;
+    }
+    mModel.setPriorityDraw(0x1c, 9);
+    return true;
+}
+
+bool intToBool(int a) {
+    return a;
+}
+
+int dAcOmoleSoil_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcOmoleSoil_c::m_allocator", 0x20)) {
+        return 2;
+    }
+
+    mScale = sHalfScale;
+    scale.set(sHalfScale, sHalfScale, sHalfScale);
+    mStateMgr.changeState(StateID_Wait);
+
+    setBoundingBox(mVec3_c(-100.0f, -0.0f, -100.0f), mVec3_c(100.0f, 100.0f, 100.0f));
+    return 1;
+}
+
+int dAcOmoleSoil_c::doDelete() {
+    return 1;
+}
+
+int dAcOmoleSoil_c::actorExecute() {
+    switch (getNextState()) {
+    case 1:
+        if (!mStateMgr.isState(StateID_Appear)) {
+            mStateMgr.changeState(StateID_Appear);
+        }
+        break;
+    case 2:
+        if (!mStateMgr.isState(StateID_DisAppear)) {
+            mStateMgr.changeState(StateID_DisAppear);
+        }
+        break;
+    case 3:
+        if (!mStateMgr.isState(StateID_Appear)) {
+            mStateMgr.changeState(StateID_Appear);
+        }
+        break;
+    }
+    mStateMgr.executeState();
+    updateMatrix();
+    mModel.setLocalMtx(worldMatrix);
+    return 1;
+}
+
+int dAcOmoleSoil_c::draw() {
+    if (field_0x391) {
+        drawModelType1(&mModel);
+    }
+    return 1;
+}
+
+// sLib::chase
+extern "C" bool fn_802DECC0(f32 *, f32, f32);
+
+void dAcOmoleSoil_c::initializeState_Wait() {
+    mDesiredNextState = 0;
+}
+void dAcOmoleSoil_c::executeState_Wait() {}
+void dAcOmoleSoil_c::finalizeState_Wait() {}
+
+void dAcOmoleSoil_c::initializeState_Appear() {
+    field_0x391 = true;
+}
+void dAcOmoleSoil_c::executeState_Appear() {
+    f32 stepSize = 0.05f;
+    if (fn_802DECC0(&mScale, sFullScale, stepSize)) {
+        mStateMgr.changeState(StateID_Wait);
+    } else {
+        mModel.setScale(mScale, mScale, mScale);
+    }
+}
+void dAcOmoleSoil_c::finalizeState_Appear() {
+    mDesiredNextState = 0;
+    mScale = sFullScale;
+    mModel.setScale(sFullScale, sFullScale, sFullScale);
+}
+
+void dAcOmoleSoil_c::initializeState_DisAppear() {
+    field_0x391 = true;
+}
+
+void dAcOmoleSoil_c::executeState_DisAppear() {
+    // Nothing about this makes sense but this creates the necessary casts
+    // and operations. Note that Ghidra constant-folds this despite
+    // all the stack operations for the cast.
+    int scalar = 0;
+    f32 stepSize = 0.01f * scalar + 0.05f;
+    if (fn_802DECC0(&mScale, sHalfScale, stepSize)) {
+        mStateMgr.changeState(StateID_Wait);
+    } else {
+        mModel.setScale(mScale, mScale, mScale);
+    }
+}
+void dAcOmoleSoil_c::finalizeState_DisAppear() {
+    mDesiredNextState = 0;
+    mScale = sHalfScale;
+    mModel.setScale(sHalfScale, sHalfScale, sHalfScale);
+}
diff --git a/src/REL/d/a/obj/d_a_obj_ring.cpp b/src/REL/d/a/obj/d_a_obj_ring.cpp
index d489a287..1e279ee3 100644
--- a/src/REL/d/a/obj/d_a_obj_ring.cpp
+++ b/src/REL/d/a/obj/d_a_obj_ring.cpp
@@ -27,7 +27,7 @@ int dAcOring_c::create() {
     setBoundingBox(mVec3_c(-100.0f, -100.0f, -100.0f), mVec3_c(100.0f, 100.0f, 100.0f));
     updateMatrix();
     mModel.setScale(scale);
-    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    mModel.setLocalMtx(worldMatrix);
     forwardAccel = -5.0f;
     forwardMaxSpeed = -40.0f;
     field_0x38C = dPlayer::LINK->position.y;
@@ -45,7 +45,7 @@ int dAcOring_c::actorExecute() {
     position += velocity;
     position += posIncrements;
     updateMatrix();
-    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    mModel.setLocalMtx(worldMatrix);
     return 1;
 }
 
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index fb8eaf27..5de9958f 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -15,7 +15,7 @@ bool dAcOsunLight_c::createHeap() {
     fn_801B4320(&mBrres);
     nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("StageF000Light");
     // This whole code is a bit weird again
-    bool success1 = createModel(mdl);
+    bool success1 = mModel.create(mdl, &heap_allocator, 0x120);
     if (!success1) {
         return success1;
     }
@@ -46,7 +46,7 @@ int dAcOsunLight_c::actorExecute() {
         mTexAnm.play();
     }
     updateMatrix();
-    mModel.setLocalMtx(&worldMatrix.nw4rm);
+    mModel.setLocalMtx(worldMatrix);
     return 1;
 }
 
diff --git a/src/m/m3d/m_anmmatclr.cpp b/src/m/m3d/m_anmmatclr.cpp
index dc741412..3b0d2706 100644
--- a/src/m/m3d/m_anmmatclr.cpp
+++ b/src/m/m3d/m_anmmatclr.cpp
@@ -173,7 +173,7 @@ void anmMatClr_c::setFrame(f32 f, s32 idx) {
 }
 
 f32 anmMatClr_c::getRate(s32 idx) const {
-    mpChildren[idx].getRate();
+    return mpChildren[idx].getRate();
 }
 
 void anmMatClr_c::setRate(f32 f, s32 idx) {
diff --git a/src/s/s_StateID.cpp b/src/s/s_StateID.cpp
index bf9100ec..c03ca5a7 100644
--- a/src/s/s_StateID.cpp
+++ b/src/s/s_StateID.cpp
@@ -25,11 +25,11 @@ bool sStateID_c::isEqual(const sStateIDIf_c &other) const {
     return number() == other.number();
 }
 
-bool sStateID_c::operator==(const sStateIDIf_c &other) const {
+BOOL sStateID_c::operator==(const sStateIDIf_c &other) const {
     return isEqual(other);
 }
 
-bool sStateID_c::operator!=(const sStateIDIf_c &other) const {
+BOOL sStateID_c::operator!=(const sStateIDIf_c &other) const {
     return !isEqual(other);
 }
 

From 25cf78374c1eff9740c8f8dee4a545f6166c76bd Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Tue, 25 Jun 2024 22:27:32 +0200
Subject: [PATCH 20/33] d_a_obj_smoke

---
 config/SOUE01/rels/d_a_obj_smokeNP/splits.txt |   3 +
 .../SOUE01/rels/d_a_obj_smokeNP/symbols.txt   | 100 +++++++++---------
 config/SOUE01/symbols.txt                     |   2 +-
 configure.py                                  |   2 +-
 include/d/a/obj/d_a_obj_base.h                |  17 +--
 include/d/a/obj/d_a_obj_smoke.h               |  30 ++++++
 src/REL/d/a/obj/d_a_obj_mole_soil.cpp         |   8 +-
 src/REL/d/a/obj/d_a_obj_ring.cpp              |   8 +-
 src/REL/d/a/obj/d_a_obj_smoke.cpp             |  60 +++++++++++
 src/REL/d/a/obj/d_a_obj_sun_light.cpp         |  10 +-
 10 files changed, 161 insertions(+), 79 deletions(-)
 create mode 100644 include/d/a/obj/d_a_obj_smoke.h

diff --git a/config/SOUE01/rels/d_a_obj_smokeNP/splits.txt b/config/SOUE01/rels/d_a_obj_smokeNP/splits.txt
index 9a2ad048..608b0ace 100644
--- a/config/SOUE01/rels/d_a_obj_smokeNP/splits.txt
+++ b/config/SOUE01/rels/d_a_obj_smokeNP/splits.txt
@@ -17,3 +17,6 @@ REL/global_destructor_chain.c:
 REL/d/a/obj/d_a_obj_smoke.cpp:
 	.text       start:0x000000F0 end:0x00000B88
 	.ctors      start:0x00000000 end:0x00000004
+	.rodata     start:0x00000000 end:0x00000018
+	.data       start:0x00000000 end:0x000001CC
+	.bss        start:0x00000008 end:0x00000048
diff --git a/config/SOUE01/rels/d_a_obj_smokeNP/symbols.txt b/config/SOUE01/rels/d_a_obj_smokeNP/symbols.txt
index bba32d86..7be9f140 100644
--- a/config/SOUE01/rels/d_a_obj_smokeNP/symbols.txt
+++ b/config/SOUE01/rels/d_a_obj_smokeNP/symbols.txt
@@ -3,57 +3,57 @@ _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
 __register_global_object = .text:0x00000070; // type:function size:0x1C scope:global
 __destroy_global_chain = .text:0x00000090; // type:function size:0x54 scope:global
-fn_373_F0 = .text:0x000000F0; // type:function size:0xEC
-fn_373_1E0 = .text:0x000001E0; // type:function size:0x58
-fn_373_240 = .text:0x00000240; // type:function size:0x6C
-fn_373_2B0 = .text:0x000002B0; // type:function size:0xA0
-fn_373_350 = .text:0x00000350; // type:function size:0xA4
-fn_373_400 = .text:0x00000400; // type:function size:0xF4
-fn_373_500 = .text:0x00000500; // type:function size:0xE8
-fn_373_5F0 = .text:0x000005F0; // type:function size:0x10
-fn_373_600 = .text:0x00000600; // type:function size:0x8
-fn_373_610 = .text:0x00000610; // type:function size:0x54
-fn_373_670 = .text:0x00000670; // type:function size:0x10
-fn_373_680 = .text:0x00000680; // type:function size:0x28
-fn_373_6B0 = .text:0x000006B0; // type:function size:0x4
-fn_373_6C0 = .text:0x000006C0; // type:function size:0x4
-fn_373_6D0 = .text:0x000006D0; // type:function size:0x4
-fn_373_6E0 = .text:0x000006E0; // type:function size:0xD8
-fn_373_7C0 = .text:0x000007C0; // type:function size:0x10
-fn_373_7D0 = .text:0x000007D0; // type:function size:0x60
-fn_373_830 = .text:0x00000830; // type:function size:0xC
-fn_373_840 = .text:0x00000840; // type:function size:0x1C
-fn_373_860 = .text:0x00000860; // type:function size:0x1C
-fn_373_880 = .text:0x00000880; // type:function size:0x1C
-fn_373_8A0 = .text:0x000008A0; // type:function size:0x10
-fn_373_8B0 = .text:0x000008B0; // type:function size:0x10
-fn_373_8C0 = .text:0x000008C0; // type:function size:0x10
-fn_373_8D0 = .text:0x000008D0; // type:function size:0x10
-fn_373_8E0 = .text:0x000008E0; // type:function size:0x10
-fn_373_8F0 = .text:0x000008F0; // type:function size:0x10
-fn_373_900 = .text:0x00000900; // type:function size:0x30
-fn_373_930 = .text:0x00000930; // type:function size:0x30
-fn_373_960 = .text:0x00000960; // type:function size:0x30
-fn_373_990 = .text:0x00000990; // type:function size:0x10C
-fn_373_AA0 = .text:0x00000AA0; // type:function size:0x58
-fn_373_B00 = .text:0x00000B00; // type:function size:0x88
+dAcOsmoke_c_classInit__Fv = .text:0x000000F0; // type:function size:0xEC
+__dt__24sFState_c<11dAcOsmoke_c>Fv = .text:0x000001E0; // type:function size:0x58
+__dt__27sFStateFct_c<11dAcOsmoke_c>Fv = .text:0x00000240; // type:function size:0x6C
+__dt__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000002B0; // type:function size:0xA0
+__dt__50sFStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c>Fv = .text:0x00000350; // type:function size:0xA4
+createHeap__11dAcOsmoke_cFv = .text:0x00000400; // type:function size:0xF4
+create__11dAcOsmoke_cFv = .text:0x00000500; // type:function size:0xE8
+changeState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>FRC12sStateIDIf_c = .text:0x000005F0; // type:function size:0x10
+doDelete__11dAcOsmoke_cFv = .text:0x00000600; // type:function size:0x8
+actorExecute__11dAcOsmoke_cFv = .text:0x00000610; // type:function size:0x54
+executeState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x00000670; // type:function size:0x10
+draw__11dAcOsmoke_cFv = .text:0x00000680; // type:function size:0x28
+initializeState_Wait__11dAcOsmoke_cFv = .text:0x000006B0; // type:function size:0x4
+executeState_Wait__11dAcOsmoke_cFv = .text:0x000006C0; // type:function size:0x4
+finalizeState_Wait__11dAcOsmoke_cFv = .text:0x000006D0; // type:function size:0x4
+__dt__11dAcOsmoke_cFv = .text:0x000006E0; // type:function size:0xD8
+getStateID__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000007C0; // type:function size:0x10
+build__27sFStateFct_c<11dAcOsmoke_c>FRC12sStateIDIf_c = .text:0x000007D0; // type:function size:0x60
+dispose__27sFStateFct_c<11dAcOsmoke_c>FRP10sStateIf_c = .text:0x00000830; // type:function size:0xC
+initialize__24sFState_c<11dAcOsmoke_c>Fv = .text:0x00000840; // type:function size:0x1C
+execute__24sFState_c<11dAcOsmoke_c>Fv = .text:0x00000860; // type:function size:0x1C
+finalize__24sFState_c<11dAcOsmoke_c>Fv = .text:0x00000880; // type:function size:0x1C
+initializeState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008A0; // type:function size:0x10
+finalizeState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008B0; // type:function size:0x10
+refreshState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x000008C0; // type:function size:0x10
+getState__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000008D0; // type:function size:0x10
+getNewStateID__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000008E0; // type:function size:0x10
+getOldStateID__80sStateMgr_c<11dAcOsmoke_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>CFv = .text:0x000008F0; // type:function size:0x10
+finalizeState__26sFStateID_c<11dAcOsmoke_c>CFR11dAcOsmoke_c = .text:0x00000900; // type:function size:0x30
+executeState__26sFStateID_c<11dAcOsmoke_c>CFR11dAcOsmoke_c = .text:0x00000930; // type:function size:0x30
+initializeState__26sFStateID_c<11dAcOsmoke_c>CFR11dAcOsmoke_c = .text:0x00000960; // type:function size:0x30
+__sinit_\d_a_obj_smoke_cpp = .text:0x00000990; // type:function size:0x10C scope:local
+__dt__26sFStateID_c<11dAcOsmoke_c>Fv = .text:0x00000AA0; // type:function size:0x58
+isSameName__26sFStateID_c<11dAcOsmoke_c>CFPCc = .text:0x00000B00; // type:function size:0x88
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
 __destroy_global_chain_reference = .dtors:0x00000000; // type:object size:0x4 scope:global
-lbl_373_rodata_0 = .rodata:0x00000000; // type:object size:0x8
-lbl_373_rodata_8 = .rodata:0x00000008; // type:object size:0x8
-lbl_373_rodata_10 = .rodata:0x00000010; // type:object size:0x4 data:float
-lbl_373_rodata_14 = .rodata:0x00000014; // type:object size:0x4 data:float
-lbl_373_data_0 = .data:0x00000000; // type:object size:0xC data:4byte
-lbl_373_data_C = .data:0x0000000C; // type:object size:0xC
-lbl_373_data_18 = .data:0x00000018; // type:object size:0x10
-lbl_373_data_28 = .data:0x00000028; // type:object size:0x20
-lbl_373_data_48 = .data:0x00000048; // type:object size:0x80
-lbl_373_data_C8 = .data:0x000000C8; // type:object size:0x30
-lbl_373_data_F8 = .data:0x000000F8; // type:object size:0x30
-lbl_373_data_128 = .data:0x00000128; // type:object size:0x18
-lbl_373_data_140 = .data:0x00000140; // type:object size:0x58
-lbl_373_data_198 = .data:0x00000198; // type:object size:0x34
+lbl_373_rodata_0 = .rodata:0x00000000; // type:object size:0x8 scope:local
+lbl_373_rodata_8 = .rodata:0x00000008; // type:object size:0x8 scope:local
+lbl_373_rodata_10 = .rodata:0x00000010; // type:object size:0x4 scope:local data:float
+lbl_373_rodata_14 = .rodata:0x00000014; // type:object size:0x4 scope:local data:float
+lbl_373_data_0 = .data:0x00000000; // type:object size:0xC scope:local data:4byte
+lbl_373_data_C = .data:0x0000000C; // type:object size:0xC scope:local
+g_profile_OBJ_SMOKE = .data:0x00000018; // type:object size:0x10
+lbl_373_data_28 = .data:0x00000028; // type:object size:0x20 scope:local
+lbl_373_data_48 = .data:0x00000048; // type:object size:0x80 scope:local
+lbl_373_data_C8 = .data:0x000000C8; // type:object size:0x30 scope:local
+lbl_373_data_F8 = .data:0x000000F8; // type:object size:0x30 scope:local
+lbl_373_data_128 = .data:0x00000128; // type:object size:0x18 scope:local
+lbl_373_data_140 = .data:0x00000140; // type:object size:0x58 scope:local
+lbl_373_data_198 = .data:0x00000198; // type:object size:0x34 scope:local
 __global_destructor_chain = .bss:0x00000000; // type:object size:0x4 scope:global
-lbl_373_bss_8 = .bss:0x00000008; // type:object size:0x10
-lbl_373_bss_18 = .bss:0x00000018; // type:object size:0x30 data:4byte
+lbl_373_bss_8 = .bss:0x00000008; // type:object size:0x10 scope:local
+StateID_Wait__11dAcOsmoke_c = .bss:0x00000018; // type:object size:0x30 data:4byte
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index b501662f..64c977f6 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -1105,7 +1105,7 @@ FUN_8002ddd0__9dAcBase_cFv = .text:0x8002DDD0; // type:function size:0x54
 FUN_8002de30__9dAcBase_cFv = .text:0x8002DE30; // type:function size:0x4
 fn_8002DE40 = .text:0x8002DE40; // type:function size:0x14
 fn_8002DE60 = .text:0x8002DE60; // type:function size:0x14
-getOarcResFile__12dAcObjBase_cFPc = .text:0x8002DE80; // type:function size:0xC
+getOarcResFile__12dAcObjBase_cFPCc = .text:0x8002DE80; // type:function size:0xC
 fn_8002DE90 = .text:0x8002DE90; // type:function size:0xC
 fn_8002DEA0 = .text:0x8002DEA0; // type:function size:0xC
 fn_8002DEB0 = .text:0x8002DEB0; // type:function size:0x14
diff --git a/configure.py b/configure.py
index 7c7cf228..3796d608 100644
--- a/configure.py
+++ b/configure.py
@@ -952,7 +952,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_obj_skull", "REL/d/a/obj/d_a_obj_skull.cpp"),
     Rel(NonMatching, "d_a_obj_slice_log", "REL/d/a/obj/d_a_obj_slice_log.cpp"),
     Rel(NonMatching, "d_a_obj_slice_log_parts", "REL/d/a/obj/d_a_obj_slice_log_parts.cpp"),
-    Rel(NonMatching, "d_a_obj_smoke", "REL/d/a/obj/d_a_obj_smoke.cpp"),
+    Rel(Matching, "d_a_obj_smoke", "REL/d/a/obj/d_a_obj_smoke.cpp"),
     Rel(NonMatching, "d_a_obj_soil", "REL/d/a/obj/d_a_obj_soil.cpp"),
     Rel(NonMatching, "d_a_obj_spider_line", "REL/d/a/obj/d_a_obj_spider_line.cpp"),
     Rel(NonMatching, "d_a_obj_spike", "REL/d/a/obj/d_a_obj_spike.cpp"),
diff --git a/include/d/a/obj/d_a_obj_base.h b/include/d/a/obj/d_a_obj_base.h
index a18cfd23..614f1499 100644
--- a/include/d/a/obj/d_a_obj_base.h
+++ b/include/d/a/obj/d_a_obj_base.h
@@ -60,13 +60,13 @@ class dAcObjBase_c : public dAcBase_c {
 
 public:
     // could be their own thing?
-    /* 8002de40 */ static void *getOarcFile(char *oarcName, char *fileName);
-    /* 8002de60 */ static void *getOarcSubEntry(char *oarcName, char *fileName);
-    /* 8002de80 */ static nw4r::g3d::ResFile getOarcResFile(char *oarcName);
-    /* 8002de90 */ static void *getOarcModelFile(char *oarcName);
-    /* 8002dea0 */ static void *getOarcZev(char *oarcName);
-    /* 8002deb0 */ static void *getOarcDZB(char *dzbName);
-    /* 8002ded0 */ static void *getOarcPLC(char *plcName);
+    /* 8002de40 */ static void *getOarcFile(const char *oarcName, const char *fileName);
+    /* 8002de60 */ static void *getOarcSubEntry(const char *oarcName, const char *fileName);
+    /* 8002de80 */ static nw4r::g3d::ResFile getOarcResFile(const char *oarcName);
+    /* 8002de90 */ static void *getOarcModelFile(const char *oarcName);
+    /* 8002dea0 */ static void *getOarcZev(const char *oarcName);
+    /* 8002deb0 */ static void *getOarcDZB(const char *dzbName);
+    /* 8002ded0 */ static void *getOarcPLC(const char *plcName);
 
 public:
     /* 8002def0 */ dAcObjBase_c();
@@ -125,4 +125,7 @@ class dAcObjBase_c : public dAcBase_c {
     }
 };
 
+// Actors' createHeap functions often have patterns that can be matched with this macro
+#define TRY_CREATE(thing) do { bool result = (thing); if (!result) return result; } while (0);
+
 #endif
diff --git a/include/d/a/obj/d_a_obj_smoke.h b/include/d/a/obj/d_a_obj_smoke.h
new file mode 100644
index 00000000..dd1f404b
--- /dev/null
+++ b/include/d/a/obj/d_a_obj_smoke.h
@@ -0,0 +1,30 @@
+#ifndef D_A_OBJ_SMOKE_H
+#define D_A_OBJ_SMOKE_H
+
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_anmtexsrt.h>
+#include <m/m3d/m_smdl.h>
+#include <s/s_State.hpp>
+#include <s/s_StateMgr.hpp>
+
+class dAcOsmoke_c : public dAcObjBase_c {
+public:
+    dAcOsmoke_c() : mStateMgr(*this, sStateID::null) {}
+    virtual ~dAcOsmoke_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+    STATE_FUNC_DECLARE(dAcOsmoke_c, Wait);
+
+private:
+    nw4r::g3d::ResFile mBrres;
+    m3d::smdl_c mModel;
+    m3d::anmTexSrt_c mTexAnm;
+    STATE_MGR_DECLARE(dAcOsmoke_c);
+    u8 mType;
+};
+
+#endif
diff --git a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
index 72c9415e..51202a33 100644
--- a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
+++ b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
@@ -1,6 +1,4 @@
 #include <d/a/obj/d_a_obj_mole_soil.h>
-#include <toBeSorted/arc_managers/current_stage_arc_manager.h>
-#include <toBeSorted/spawn_struct.h>
 
 SPECIAL_ACTOR_PROFILE(OBJ_MOLE_SOIL, dAcOmoleSoil_c, fProfile::OBJ_MOLE_SOIL, 0x008B, 0, 6);
 
@@ -18,11 +16,7 @@ bool dAcOmoleSoil_c::createHeap() {
     }
     mBrres = file;
     nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("MogumaMud");
-    // This whole code is a bit weird again
-    bool success1 = mModel.create(mdl, &heap_allocator, 0x120, 1, nullptr);
-    if (!success1) {
-        return success1;
-    }
+    TRY_CREATE(mModel.create(mdl, &heap_allocator, 0x120, 1, nullptr));
     mModel.setPriorityDraw(0x1c, 9);
     return true;
 }
diff --git a/src/REL/d/a/obj/d_a_obj_ring.cpp b/src/REL/d/a/obj/d_a_obj_ring.cpp
index 1e279ee3..d411bfff 100644
--- a/src/REL/d/a/obj/d_a_obj_ring.cpp
+++ b/src/REL/d/a/obj/d_a_obj_ring.cpp
@@ -9,12 +9,8 @@ bool dAcOring_c::createHeap() {
     nw4r::g3d::ResFile f = getOarcResFile("PRing");
     nw4r::g3d::ResMdl mdl = f.GetResMdl("PeehatRing");
     // This matches but in a really weird way. Maybe an inline function?
-    bool success = mModel.create(mdl, &heap_allocator, 0x20, 1, nullptr);
-    bool rc = true;
-    if (success == false) {
-        rc = success;
-    }
-    return rc;
+    TRY_CREATE(mModel.create(mdl, &heap_allocator, 0x20, 1, nullptr));
+    return true;
 }
 
 int dAcOring_c::create() {
diff --git a/src/REL/d/a/obj/d_a_obj_smoke.cpp b/src/REL/d/a/obj/d_a_obj_smoke.cpp
index e69de29b..b2470b4e 100644
--- a/src/REL/d/a/obj/d_a_obj_smoke.cpp
+++ b/src/REL/d/a/obj/d_a_obj_smoke.cpp
@@ -0,0 +1,60 @@
+#include <d/a/obj/d_a_obj_smoke.h>
+
+static const char *const sSmokeNames1[] = {
+        "SmokeF200",
+        "SmokeF202",
+};
+
+static const char *const sSmokeNames2[] = {
+        "SmokeF200",
+        "SmokeF202",
+};
+
+SPECIAL_ACTOR_PROFILE(OBJ_SMOKE, dAcOsmoke_c, fProfile::OBJ_SMOKE, 0x01DB, 0, 4);
+
+STATE_DEFINE(dAcOsmoke_c, Wait);
+
+bool dAcOsmoke_c::createHeap() {
+    mType = params & 3;
+    mBrres = getOarcResFile(sSmokeNames1[mType]);
+    nw4r::g3d::ResMdl mdl = mBrres.GetResMdl(sSmokeNames2[mType]);
+    TRY_CREATE(mModel.create(mdl, &heap_allocator, 0x324));
+    nw4r::g3d::ResAnmTexSrt srt = mBrres.GetResAnmTexSrt(sSmokeNames1[mType]);
+    TRY_CREATE(mTexAnm.create(mdl, srt, &heap_allocator, nullptr, 1));
+    return true;
+}
+
+int dAcOsmoke_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcOsmoke_c::m_allocator", 0x20)) {
+        return 2;
+    }
+
+    mModel.setAnm(mTexAnm);
+    updateMatrix();
+    mModel.setLocalMtx(worldMatrix);
+    mStateMgr.changeState(StateID_Wait);
+    mModel.setPriorityDraw(0x1C, 9);
+    setBoundingBox(mVec3_c(-0.0f, -0.0f, -0.0f), mVec3_c(0.0f, 0.0f, 0.0f));
+    return 1;
+}
+
+int dAcOsmoke_c::doDelete() {
+    return 1;
+}
+
+int dAcOsmoke_c::actorExecute() {
+    mStateMgr.executeState();
+    mTexAnm.play();
+    return 1;
+}
+
+int dAcOsmoke_c::draw() {
+    drawModelType1(&mModel);
+    return 1;
+}
+
+void dAcOsmoke_c::initializeState_Wait() {}
+
+void dAcOsmoke_c::executeState_Wait() {}
+
+void dAcOsmoke_c::finalizeState_Wait() {}
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index 5de9958f..68bb61df 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -14,14 +14,10 @@ bool dAcOsunLight_c::createHeap() {
     fn_801B42D0(&mBrres);
     fn_801B4320(&mBrres);
     nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("StageF000Light");
-    // This whole code is a bit weird again
-    bool success1 = mModel.create(mdl, &heap_allocator, 0x120);
-    if (!success1) {
-        return success1;
-    }
+    TRY_CREATE(mModel.create(mdl, &heap_allocator, 0x120));
     nw4r::g3d::ResAnmTexSrt srt = mBrres.GetResAnmTexSrt("StageF000Light");
-    bool success2 = mTexAnm.create(mdl, srt, &heap_allocator, nullptr, 1);
-    return success2 ? true : success2;
+    TRY_CREATE(mTexAnm.create(mdl, srt, &heap_allocator, nullptr, 1));
+    return true;
 }
 
 int dAcOsunLight_c::create() {

From df9010f2e132dad441346860ad774ade493270b9 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Tue, 25 Jun 2024 23:57:00 +0200
Subject: [PATCH 21/33] dAcOlavaF200_c OK

---
 .../rels/d_a_obj_lava_F200NP/splits.txt       |  2 +
 .../rels/d_a_obj_lava_F200NP/symbols.txt      | 32 ++++-----
 config/SOUE01/symbols.txt                     |  4 +-
 configure.py                                  |  2 +-
 include/d/a/obj/d_a_obj_lava_F200.h           | 26 ++++++++
 include/m/m3d/m_anmmatclr.h                   |  4 +-
 src/REL/d/a/obj/d_a_obj_lava_F200.cpp         | 66 +++++++++++++++++++
 src/REL/d/a/obj/d_a_obj_sun_light.cpp         |  8 +--
 8 files changed, 118 insertions(+), 26 deletions(-)
 create mode 100644 include/d/a/obj/d_a_obj_lava_F200.h

diff --git a/config/SOUE01/rels/d_a_obj_lava_F200NP/splits.txt b/config/SOUE01/rels/d_a_obj_lava_F200NP/splits.txt
index d72ee203..83d45df3 100644
--- a/config/SOUE01/rels/d_a_obj_lava_F200NP/splits.txt
+++ b/config/SOUE01/rels/d_a_obj_lava_F200NP/splits.txt
@@ -11,3 +11,5 @@ REL/executor.c:
 
 REL/d/a/obj/d_a_obj_lava_F200.cpp:
 	.text       start:0x00000070 end:0x000006C4
+	.rodata     start:0x00000000 end:0x00000008
+	.data       start:0x00000000 end:0x000000C8
diff --git a/config/SOUE01/rels/d_a_obj_lava_F200NP/symbols.txt b/config/SOUE01/rels/d_a_obj_lava_F200NP/symbols.txt
index 2b7d318c..a44b8497 100644
--- a/config/SOUE01/rels/d_a_obj_lava_F200NP/symbols.txt
+++ b/config/SOUE01/rels/d_a_obj_lava_F200NP/symbols.txt
@@ -1,21 +1,21 @@
 _prolog = .text:0x00000000; // type:function size:0x2C scope:global
 _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
-fn_429_70 = .text:0x00000070; // type:function size:0xB4
-fn_429_130 = .text:0x00000130; // type:function size:0x64
-fn_429_1A0 = .text:0x000001A0; // type:function size:0x64
-fn_429_210 = .text:0x00000210; // type:function size:0x1C4
-fn_429_3E0 = .text:0x000003E0; // type:function size:0x14C
-fn_429_530 = .text:0x00000530; // type:function size:0x8
-fn_429_540 = .text:0x00000540; // type:function size:0x7C
-fn_429_5C0 = .text:0x000005C0; // type:function size:0x60
-fn_429_620 = .text:0x00000620; // type:function size:0xA4
+dAcOlavaF200_c_classInit__Fv = .text:0x00000070; // type:function size:0xB4
+__ct__Q23m3d11anmTexSrt_cFv = .text:0x00000130; // type:function size:0x64
+__ct__Q23m3d11anmMatClr_cFv = .text:0x000001A0; // type:function size:0x64
+createHeap__14dAcOlavaF200_cFv = .text:0x00000210; // type:function size:0x1C4
+create__14dAcOlavaF200_cFv = .text:0x000003E0; // type:function size:0x14C
+doDelete__14dAcOlavaF200_cFv = .text:0x00000530; // type:function size:0x8
+actorExecute__14dAcOlavaF200_cFv = .text:0x00000540; // type:function size:0x7C
+draw__14dAcOlavaF200_cFv = .text:0x000005C0; // type:function size:0x60
+__dt__14dAcOlavaF200_cFv = .text:0x00000620; // type:function size:0xA4
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
-lbl_429_rodata_0 = .rodata:0x00000000; // type:object size:0x4 data:float
-lbl_429_rodata_4 = .rodata:0x00000004; // type:object size:0x4 data:float
-lbl_429_data_0 = .data:0x00000000; // type:object size:0x10
-lbl_429_data_10 = .data:0x00000010; // type:object size:0xC
-lbl_429_data_1C = .data:0x0000001C; // type:object size:0xC
-lbl_429_data_28 = .data:0x00000028; // type:object size:0x20
-lbl_429_data_48 = .data:0x00000048; // type:object size:0x80
+lbl_429_rodata_0 = .rodata:0x00000000; // type:object size:0x4 scope:local data:float
+lbl_429_rodata_4 = .rodata:0x00000004; // type:object size:0x4 scope:local data:float
+g_profile_OBJ_LAVA_F200 = .data:0x00000000; // type:object size:0x10
+lbl_429_data_10 = .data:0x00000010; // type:object size:0xC scope:local
+lbl_429_data_1C = .data:0x0000001C; // type:object size:0xC scope:local
+lbl_429_data_28 = .data:0x00000028; // type:object size:0x20 scope:local
+__vt__14dAcOlavaF200_c = .data:0x00000048; // type:object size:0x80
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 64c977f6..645c1551 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -10983,8 +10983,8 @@ fn_801B40E0 = .text:0x801B40E0; // type:function size:0x12C
 fn_801B4210 = .text:0x801B4210; // type:function size:0x28
 fn_801B4240 = .text:0x801B4240; // type:function size:0x68
 getRoom__11RoomManagerFi = .text:0x801B42B0; // type:function size:0x20
-fn_801B42D0 = .text:0x801B42D0; // type:function size:0x50
-fn_801B4320 = .text:0x801B4320; // type:function size:0x54
+bindStageResToFile__11RoomManagerFPQ34nw4r3g3d7ResFile = .text:0x801B42D0; // type:function size:0x50
+bindSkyCmnToResFile__11RoomManagerFPQ34nw4r3g3d7ResFile = .text:0x801B4320; // type:function size:0x54
 fn_801B4380 = .text:0x801B4380; // type:function size:0xA0
 fn_801B4420 = .text:0x801B4420; // type:function size:0x9C
 changeLoadedEntities__11RoomManagerFP11RoomManagerUlb = .text:0x801B44C0; // type:function size:0x50
diff --git a/configure.py b/configure.py
index 3796d608..c38530dc 100644
--- a/configure.py
+++ b/configure.py
@@ -871,7 +871,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_obj_kumite_wall", "REL/d/a/obj/d_a_obj_kumite_wall.cpp"),
     Rel(NonMatching, "d_a_obj_lamp", "REL/d/a/obj/d_a_obj_lamp.cpp"),
     Rel(NonMatching, "d_a_obj_lava_d201", "REL/d/a/obj/d_a_obj_lava_d201.cpp"),
-    Rel(NonMatching, "d_a_obj_lava_F200", "REL/d/a/obj/d_a_obj_lava_F200.cpp"),
+    Rel(Matching, "d_a_obj_lava_F200", "REL/d/a/obj/d_a_obj_lava_F200.cpp"),
     Rel(NonMatching, "d_a_obj_lava_plate", "REL/d/a/obj/d_a_obj_lava_plate.cpp"),
     Rel(NonMatching, "d_a_obj_leaf_swing", "REL/d/a/obj/d_a_obj_leaf_swing.cpp"),
     Rel(NonMatching, "d_a_obj_lighthouse_harp", "REL/d/a/obj/d_a_obj_lighthouse_harp.cpp"),
diff --git a/include/d/a/obj/d_a_obj_lava_F200.h b/include/d/a/obj/d_a_obj_lava_F200.h
new file mode 100644
index 00000000..8024801e
--- /dev/null
+++ b/include/d/a/obj/d_a_obj_lava_F200.h
@@ -0,0 +1,26 @@
+#ifndef D_A_OBJ_LAVA_F200_H
+#define D_A_OBJ_LAVA_F200_H
+
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_anmmatclr.h>
+#include <m/m3d/m_anmtexsrt.h>
+#include <m/m3d/m_smdl.h>
+
+class dAcOlavaF200_c : public dAcObjBase_c {
+public:
+    dAcOlavaF200_c() {}
+    virtual ~dAcOlavaF200_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+private:
+    nw4r::g3d::ResFile mBrres;
+    m3d::smdl_c mModels[2];
+    m3d::anmTexSrt_c mTexAnms[2];
+    m3d::anmMatClr_c mClrAnms[2];
+};
+
+#endif
diff --git a/include/m/m3d/m_anmmatclr.h b/include/m/m3d/m_anmmatclr.h
index 7b48cd5a..1a2db51c 100644
--- a/include/m/m3d/m_anmmatclr.h
+++ b/include/m/m3d/m_anmmatclr.h
@@ -1,5 +1,5 @@
-#ifndef M3D_M_ANMTEXSRT_H
-#define M3D_M_ANMTEXSRT_H
+#ifndef M3D_M_ANMMATCLR_H
+#define M3D_M_ANMMATCLR_H
 
 #include <m/m3d/m_bmdl.h>
 #include <m/m3d/m_fanm.h>
diff --git a/src/REL/d/a/obj/d_a_obj_lava_F200.cpp b/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
index e69de29b..682e3ff6 100644
--- a/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
+++ b/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
@@ -0,0 +1,66 @@
+#include <d/a/obj/d_a_obj_lava_F200.h>
+#include <toBeSorted/room_manager.h>
+
+SPECIAL_ACTOR_PROFILE(OBJ_LAVA_F200, dAcOlavaF200_c, fProfile::OBJ_LAVA_F200, 0x0214, 0, 0);
+
+bool dAcOlavaF200_c::createHeap() {
+    mBrres = getOarcResFile("LavaF200");
+    RoomManager::bindStageResToFile(&mBrres);
+
+    nw4r::g3d::ResMdl mdl0 = mBrres.GetResMdl("LavaF200");
+    TRY_CREATE(mModels[0].create(mdl0, &heap_allocator, 0x32C));
+    nw4r::g3d::ResAnmTexSrt anmTex0 = mBrres.GetResAnmTexSrt("LavaF200");
+    TRY_CREATE(mTexAnms[0].create(mdl0, anmTex0, &heap_allocator, nullptr, 1));
+    nw4r::g3d::ResAnmClr anmClr0 = mBrres.GetResAnmClr("LavaF200");
+    TRY_CREATE(mClrAnms[0].create(mdl0, anmClr0, &heap_allocator, nullptr, 1));
+
+    nw4r::g3d::ResMdl mdl1 = mBrres.GetResMdl("LavaF200_s");
+    TRY_CREATE(mModels[1].create(mdl1, &heap_allocator, 0x32C));
+    nw4r::g3d::ResAnmTexSrt anmTex1 = mBrres.GetResAnmTexSrt("LavaF200_s");
+    TRY_CREATE(mTexAnms[1].create(mdl1, anmTex1, &heap_allocator, nullptr, 1));
+    nw4r::g3d::ResAnmClr anmClr1 = mBrres.GetResAnmClr("LavaF200_s");
+    TRY_CREATE(mClrAnms[1].create(mdl1, anmClr1, &heap_allocator, nullptr, 1));
+
+    return true;
+}
+
+int dAcOlavaF200_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcOlavaF200_c::m_allocator", 0x20)) {
+        return 2;
+    }
+
+    mModels[0].setAnm(mTexAnms[0]);
+    mModels[1].setAnm(mTexAnms[1]);
+    mModels[0].setAnm(mClrAnms[0]);
+    mModels[1].setAnm(mClrAnms[1]);
+
+    updateMatrix();
+    for (int i = 0; i < 2; i++) {
+        mModels[i].setLocalMtx(worldMatrix);
+    }
+
+    mModels[0].setPriorityDraw(0x1C, 9);
+    mModels[1].setPriorityDraw(0x22, 9);
+    setBoundingBox(mVec3_c(-0.0f, -0.0f, -0.0f), mVec3_c(0.0f, 0.0f, 0.0f));
+
+    return 1;
+}
+
+int dAcOlavaF200_c::doDelete() {
+    return 1;
+}
+
+int dAcOlavaF200_c::actorExecute() {
+    mTexAnms[0].play();
+    mTexAnms[1].play();
+    mClrAnms[0].play();
+    mClrAnms[1].play();
+    return 1;
+}
+
+int dAcOlavaF200_c::draw() {
+    for (int i = 0; i < 2; i++) {
+        drawModelType1(&mModels[i]);
+    }
+    return 1;
+}
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index 68bb61df..bc5c1453 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -1,18 +1,16 @@
 #include <d/a/obj/d_a_obj_sun_light.h>
 #include <toBeSorted/arc_managers/current_stage_arc_manager.h>
 #include <toBeSorted/spawn_struct.h>
+#include <toBeSorted/room_manager.h>
 
 SPECIAL_ACTOR_PROFILE(OBJ_SUN_LIGHT, dAcOsunLight_c, fProfile::OBJ_SUN_LIGHT, 0x0219, 0, 3);
 
 STATE_DEFINE(dAcOsunLight_c, Wait);
 
-extern "C" void fn_801B42D0(nw4r::g3d::ResFile *);
-extern "C" void fn_801B4320(nw4r::g3d::ResFile *);
-
 bool dAcOsunLight_c::createHeap() {
     mBrres = CurrentStageArcManager::sInstance->getData("g3d/stage.brres");
-    fn_801B42D0(&mBrres);
-    fn_801B4320(&mBrres);
+    RoomManager::bindStageResToFile(&mBrres);
+    RoomManager::bindSkyCmnToResFile(&mBrres);
     nw4r::g3d::ResMdl mdl = mBrres.GetResMdl("StageF000Light");
     TRY_CREATE(mModel.create(mdl, &heap_allocator, 0x120));
     nw4r::g3d::ResAnmTexSrt srt = mBrres.GetResAnmTexSrt("StageF000Light");

From 2f32c5b42bff02e47fddeab74a3a124f27c6cba3 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Wed, 26 Jun 2024 19:26:59 +0200
Subject: [PATCH 22/33] Fix create return values

---
 include/d/tg/d_tg.h                   | 2 +-
 src/REL/d/a/obj/d_a_obj_lava_F200.cpp | 4 ++--
 src/REL/d/a/obj/d_a_obj_mole_soil.cpp | 4 ++--
 src/REL/d/a/obj/d_a_obj_ring.cpp      | 4 ++--
 src/REL/d/a/obj/d_a_obj_smoke.cpp     | 4 ++--
 src/REL/d/a/obj/d_a_obj_sun_light.cpp | 4 ++--
 src/REL/d/t/d_t_map_inst.cpp          | 2 +-
 src/REL/d/t/d_t_rock_boat.cpp         | 2 +-
 src/REL/d/t/d_t_sound_area.cpp        | 2 +-
 src/REL/d/t/d_t_sound_area_mgr.cpp    | 2 +-
 src/REL/d/t/d_t_timer.cpp             | 2 +-
 src/REL/d/t/d_t_tumble_weed.cpp       | 2 +-
 12 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/include/d/tg/d_tg.h b/include/d/tg/d_tg.h
index ceb14579..af3579aa 100644
--- a/include/d/tg/d_tg.h
+++ b/include/d/tg/d_tg.h
@@ -16,7 +16,7 @@ class dTg_c : public dAcBase_c {
         return 1;
     }
     virtual int create() override {
-        return 1;
+        return SUCCEEDED;
     }
 };
 
diff --git a/src/REL/d/a/obj/d_a_obj_lava_F200.cpp b/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
index 682e3ff6..78ad3147 100644
--- a/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
+++ b/src/REL/d/a/obj/d_a_obj_lava_F200.cpp
@@ -26,7 +26,7 @@ bool dAcOlavaF200_c::createHeap() {
 
 int dAcOlavaF200_c::create() {
     if (!initAllocatorWork1Heap(-1, "dAcOlavaF200_c::m_allocator", 0x20)) {
-        return 2;
+        return FAILED;
     }
 
     mModels[0].setAnm(mTexAnms[0]);
@@ -43,7 +43,7 @@ int dAcOlavaF200_c::create() {
     mModels[1].setPriorityDraw(0x22, 9);
     setBoundingBox(mVec3_c(-0.0f, -0.0f, -0.0f), mVec3_c(0.0f, 0.0f, 0.0f));
 
-    return 1;
+    return SUCCEEDED;
 }
 
 int dAcOlavaF200_c::doDelete() {
diff --git a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
index 51202a33..8a6cc1c3 100644
--- a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
+++ b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
@@ -27,7 +27,7 @@ bool intToBool(int a) {
 
 int dAcOmoleSoil_c::create() {
     if (!initAllocatorWork1Heap(-1, "dAcOmoleSoil_c::m_allocator", 0x20)) {
-        return 2;
+        return FAILED;
     }
 
     mScale = sHalfScale;
@@ -35,7 +35,7 @@ int dAcOmoleSoil_c::create() {
     mStateMgr.changeState(StateID_Wait);
 
     setBoundingBox(mVec3_c(-100.0f, -0.0f, -100.0f), mVec3_c(100.0f, 100.0f, 100.0f));
-    return 1;
+    return SUCCEEDED;
 }
 
 int dAcOmoleSoil_c::doDelete() {
diff --git a/src/REL/d/a/obj/d_a_obj_ring.cpp b/src/REL/d/a/obj/d_a_obj_ring.cpp
index d411bfff..c6885766 100644
--- a/src/REL/d/a/obj/d_a_obj_ring.cpp
+++ b/src/REL/d/a/obj/d_a_obj_ring.cpp
@@ -15,7 +15,7 @@ bool dAcOring_c::createHeap() {
 
 int dAcOring_c::create() {
     if (!initAllocatorWork1Heap(-1, "dAcOring_c::m_allocator", 0x20)) {
-        return 2;
+        return FAILED;
     }
 
     field_0x388 = getArgFromParams();
@@ -28,7 +28,7 @@ int dAcOring_c::create() {
     forwardMaxSpeed = -40.0f;
     field_0x38C = dPlayer::LINK->position.y;
     mStateMgr.changeState(StateID_Move);
-    return 1;
+    return SUCCEEDED;
 }
 
 int dAcOring_c::doDelete() {
diff --git a/src/REL/d/a/obj/d_a_obj_smoke.cpp b/src/REL/d/a/obj/d_a_obj_smoke.cpp
index b2470b4e..36c1e232 100644
--- a/src/REL/d/a/obj/d_a_obj_smoke.cpp
+++ b/src/REL/d/a/obj/d_a_obj_smoke.cpp
@@ -26,7 +26,7 @@ bool dAcOsmoke_c::createHeap() {
 
 int dAcOsmoke_c::create() {
     if (!initAllocatorWork1Heap(-1, "dAcOsmoke_c::m_allocator", 0x20)) {
-        return 2;
+        return FAILED;
     }
 
     mModel.setAnm(mTexAnm);
@@ -35,7 +35,7 @@ int dAcOsmoke_c::create() {
     mStateMgr.changeState(StateID_Wait);
     mModel.setPriorityDraw(0x1C, 9);
     setBoundingBox(mVec3_c(-0.0f, -0.0f, -0.0f), mVec3_c(0.0f, 0.0f, 0.0f));
-    return 1;
+    return SUCCEEDED;
 }
 
 int dAcOsmoke_c::doDelete() {
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index bc5c1453..83f375be 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -20,14 +20,14 @@ bool dAcOsunLight_c::createHeap() {
 
 int dAcOsunLight_c::create() {
     if (!initAllocatorWork1Heap(-1, "dAcOsunLight_c::m_allocator", 0x20)) {
-        return 2;
+        return FAILED;
     }
 
     mModel.setAnm(mTexAnm);
     mStateMgr.changeState(StateID_Wait);
     mModel.setPriorityDraw(0x1C, 9);
     setBoundingBox(mVec3_c(-200.0f, -100.0f, -200.0f), mVec3_c(200.0f, 600.0f, 500.0f));
-    return 1;
+    return SUCCEEDED;
 }
 
 int dAcOsunLight_c::doDelete() {
diff --git a/src/REL/d/t/d_t_map_inst.cpp b/src/REL/d/t/d_t_map_inst.cpp
index bcea34c0..8e9c8d80 100644
--- a/src/REL/d/t/d_t_map_inst.cpp
+++ b/src/REL/d/t/d_t_map_inst.cpp
@@ -4,5 +4,5 @@ SPECIAL_ACTOR_PROFILE(TAG_MAP_INST, dTgMapInst_c, fProfile::TAG_MAP_INST, 0x021d
 
 int dTgMapInst_c::create() {
     field_0xfc = params & 0xff;
-    return 1;
+    return SUCCEEDED;
 }
diff --git a/src/REL/d/t/d_t_rock_boat.cpp b/src/REL/d/t/d_t_rock_boat.cpp
index c22d2b7c..7af93533 100644
--- a/src/REL/d/t/d_t_rock_boat.cpp
+++ b/src/REL/d/t/d_t_rock_boat.cpp
@@ -8,7 +8,7 @@ STATE_DEFINE(dTgRockBoat_c, Wait);
 int dTgRockBoat_c::create() {
     mStateMgr.changeState(StateID_Wait);
     cooldown = 1;
-    return 1;
+    return SUCCEEDED;
 }
 
 int dTgRockBoat_c::doDelete() {
diff --git a/src/REL/d/t/d_t_sound_area.cpp b/src/REL/d/t/d_t_sound_area.cpp
index 83aa1907..66159477 100644
--- a/src/REL/d/t/d_t_sound_area.cpp
+++ b/src/REL/d/t/d_t_sound_area.cpp
@@ -40,7 +40,7 @@ int dTgSndAr_c::create() {
             ac->setBit_field_0xE8(params & 0xFF);
         }
     }
-    return 1;
+    return SUCCEEDED;
 }
 
 int dTgSndAr_c::doDelete() {
diff --git a/src/REL/d/t/d_t_sound_area_mgr.cpp b/src/REL/d/t/d_t_sound_area_mgr.cpp
index 26da7f90..ef4eea02 100644
--- a/src/REL/d/t/d_t_sound_area_mgr.cpp
+++ b/src/REL/d/t/d_t_sound_area_mgr.cpp
@@ -7,7 +7,7 @@ int dTgSndMg_c::create() {
     s8 tmpRoomId = roomid;
     addActorToRoom(-1);
     roomid = tmpRoomId;
-    return 1;
+    return SUCCEEDED;
 }
 
 int dTgSndMg_c::doDelete() {
diff --git a/src/REL/d/t/d_t_timer.cpp b/src/REL/d/t/d_t_timer.cpp
index 4eef47a2..540e9ee2 100644
--- a/src/REL/d/t/d_t_timer.cpp
+++ b/src/REL/d/t/d_t_timer.cpp
@@ -27,7 +27,7 @@ int dTgTimer_c::create() {
         break;
     }
     resetTimer();
-    return 1;
+    return SUCCEEDED;
 }
 
 int dTgTimer_c::actorExecute() {
diff --git a/src/REL/d/t/d_t_tumble_weed.cpp b/src/REL/d/t/d_t_tumble_weed.cpp
index 5534cb98..671073f3 100644
--- a/src/REL/d/t/d_t_tumble_weed.cpp
+++ b/src/REL/d/t/d_t_tumble_weed.cpp
@@ -12,7 +12,7 @@ int dTgTumbleWeed_c::create() {
     tumbleweedTimer = 0;
     windTimer = 0x96;
     mStateMgr.changeState(StateID_AreaOut);
-    return 1;
+    return SUCCEEDED;
 }
 
 u16 decr(u16 *num);

From 3fd8ac36c47dac7d9039b43c39c282519f40c6b8 Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 28 Jun 2024 21:10:45 +0200
Subject: [PATCH 23/33] d_a_e_hb_leaf OK

---
 config/SOUE01/rels/d_a_e_hb_leafNP/splits.txt |   2 +
 .../SOUE01/rels/d_a_e_hb_leafNP/symbols.txt   |  30 ++---
 config/SOUE01/symbols.txt                     |   4 +-
 configure.py                                  |   2 +-
 include/d/a/e/d_a_e_hb_leaf.h                 |  59 ++++++++++
 include/d/a/{en => e}/d_a_en_base.h           |   0
 include/d/a/obj/d_a_obj_base.h                |   2 +-
 include/m/m3d/m_anmmdl.h                      |  20 +++-
 src/REL/d/a/e/d_a_e_hb_leaf.cpp               | 109 ++++++++++++++++++
 tools/rel_sieve.py                            |   1 -
 10 files changed, 205 insertions(+), 24 deletions(-)
 create mode 100644 include/d/a/e/d_a_e_hb_leaf.h
 rename include/d/a/{en => e}/d_a_en_base.h (100%)

diff --git a/config/SOUE01/rels/d_a_e_hb_leafNP/splits.txt b/config/SOUE01/rels/d_a_e_hb_leafNP/splits.txt
index 92dad929..75446b97 100644
--- a/config/SOUE01/rels/d_a_e_hb_leafNP/splits.txt
+++ b/config/SOUE01/rels/d_a_e_hb_leafNP/splits.txt
@@ -11,3 +11,5 @@ REL/executor.c:
 
 REL/d/a/e/d_a_e_hb_leaf.cpp:
 	.text       start:0x00000070 end:0x000005E4
+	.rodata     start:0x00000000 end:0x00000024
+	.data       start:0x00000000 end:0x000000D0
diff --git a/config/SOUE01/rels/d_a_e_hb_leafNP/symbols.txt b/config/SOUE01/rels/d_a_e_hb_leafNP/symbols.txt
index 8263afc8..7dc7d3c9 100644
--- a/config/SOUE01/rels/d_a_e_hb_leafNP/symbols.txt
+++ b/config/SOUE01/rels/d_a_e_hb_leafNP/symbols.txt
@@ -1,18 +1,22 @@
 _prolog = .text:0x00000000; // type:function size:0x2C scope:global
 _epilog = .text:0x00000030; // type:function size:0x2C scope:global
 _unresolved = .text:0x00000060; // type:function size:0x4 scope:global
-fn_157_70 = .text:0x00000070; // type:function size:0xD4
-fn_157_150 = .text:0x00000150; // type:function size:0xE8
-fn_157_240 = .text:0x00000240; // type:function size:0x1A0
-fn_157_3E0 = .text:0x000003E0; // type:function size:0x8
-fn_157_3F0 = .text:0x000003F0; // type:function size:0xF0
-fn_157_4E0 = .text:0x000004E0; // type:function size:0x6C
-fn_157_550 = .text:0x00000550; // type:function size:0x14
-fn_157_570 = .text:0x00000570; // type:function size:0x74
+dAcEhb_leaf_c_classInit__Fv = .text:0x00000070; // type:function size:0xD4
+createHeap__13dAcEhb_leaf_cFv = .text:0x00000150; // type:function size:0xE8
+create__13dAcEhb_leaf_cFv = .text:0x00000240; // type:function size:0x1A0
+doDelete__13dAcEhb_leaf_cFv = .text:0x000003E0; // type:function size:0x8
+actorExecute__13dAcEhb_leaf_cFv = .text:0x000003F0; // type:function size:0xF0
+draw__13dAcEhb_leaf_cFv = .text:0x000004E0; // type:function size:0x6C
+setAnm__13dAcEhb_leaf_cFPCcf = .text:0x00000550; // type:function size:0x14
+__dt__13dAcEhb_leaf_cFv = .text:0x00000570; // type:function size:0x74
 _ctors = .ctors:0x00000000; // type:label scope:global
 _dtors = .dtors:0x00000000; // type:label scope:global
-lbl_157_rodata_0 = .rodata:0x00000000; // type:object size:0x20 data:float
-lbl_157_rodata_20 = .rodata:0x00000020; // type:object size:0x4 data:float
-lbl_157_data_0 = .data:0x00000000; // type:object size:0x34
-lbl_157_data_34 = .data:0x00000034; // type:object size:0x1C
-lbl_157_data_50 = .data:0x00000050; // type:object size:0x80
+unkFloat0__13dAcEhb_leaf_c = .rodata:0x00000000; // type:object size:0x4 data:float
+unkFloat1__13dAcEhb_leaf_c = .rodata:0x00000004; // type:object size:0x4 data:float
+lbl_157_rodata_20 = .rodata:0x00000020; // type:object size:0x4 scope:local data:float
+g_profile_E_HB_LEAF = .data:0x00000000; // type:object size:0x10
+lbl_157_data_10 = .data:0x00000010; // type:object size:0x9 scope:local data:string
+lbl_157_data_1C = .data:0x0000001C; // type:object size:0x7 scope:local data:string
+lbl_157_data_24 = .data:0x00000024; // type:object size:0xE scope:local data:string
+lbl_157_data_34 = .data:0x00000034; // type:object size:0x1B scope:local data:string
+__vt__13dAcEhb_leaf_c = .data:0x00000050; // type:object size:0x80
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 645c1551..6c60ae5c 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17290,7 +17290,7 @@ fn_802E0CB0 = .text:0x802E0CB0; // type:function size:0x48
 fn_802E0D00 = .text:0x802E0D00; // type:function size:0x8
 rnd__2cMFv = .text:0x802E0D10; // type:function size:0x8
 fn_802E0D20 = .text:0x802E0D20; // type:function size:0x58
-fn_802E0D80 = .text:0x802E0D80; // type:function size:0x38
+rndF__2cMFf = .text:0x802E0D80; // type:function size:0x38
 fn_802E0DC0 = .text:0x802E0DC0; // type:function size:0x48
 fn_802E0E10 = .text:0x802E0E10; // type:function size:0xC
 fn_802E0E20 = .text:0x802E0E20; // type:function size:0x44
@@ -22197,7 +22197,7 @@ fn_803B8640 = .text:0x803B8640; // type:function size:0xB0
 fn_803B86F0 = .text:0x803B86F0; // type:function size:0x7C
 PSMTXTrans = .text:0x803B8770; // type:function size:0x34
 fn_803B87B0 = .text:0x803B87B0; // type:function size:0x4C
-fn_803B8800 = .text:0x803B8800; // type:function size:0x28
+PSMTXScale = .text:0x803B8800; // type:function size:0x28
 fn_803B8830 = .text:0x803B8830; // type:function size:0x58
 PSMTXQuat = .text:0x803B8890; // type:function size:0xA4
 fn_803B8940 = .text:0x803B8940; // type:function size:0x174
diff --git a/configure.py b/configure.py
index c38530dc..3c16dede 100644
--- a/configure.py
+++ b/configure.py
@@ -582,7 +582,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
     Rel(NonMatching, "d_a_e_gunhob", "REL/d/a/e/d_a_e_gunhob.cpp"),
     Rel(NonMatching, "d_a_e_gunho", "REL/d/a/e/d_a_e_gunho.cpp"),
     Rel(NonMatching, "d_a_e_hb", "REL/d/a/e/d_a_e_hb.cpp"),
-    Rel(NonMatching, "d_a_e_hb_leaf", "REL/d/a/e/d_a_e_hb_leaf.cpp"),
+    Rel(Matching, "d_a_e_hb_leaf", "REL/d/a/e/d_a_e_hb_leaf.cpp"),
     Rel(NonMatching, "d_a_e_hidokari", "REL/d/a/e/d_a_e_hidokari.cpp"),
     Rel(NonMatching, "d_a_e_hidokaris", "REL/d/a/e/d_a_e_hidokaris.cpp"),
     Rel(NonMatching, "d_a_e_hidory", "REL/d/a/e/d_a_e_hidory.cpp"),
diff --git a/include/d/a/e/d_a_e_hb_leaf.h b/include/d/a/e/d_a_e_hb_leaf.h
new file mode 100644
index 00000000..5ac98cd0
--- /dev/null
+++ b/include/d/a/e/d_a_e_hb_leaf.h
@@ -0,0 +1,59 @@
+#ifndef D_A_E_HB_LEAF_H
+#define D_A_E_HB_LEAF_H
+
+#include <c/c_math.h>
+#include <d/a/obj/d_a_obj_base.h>
+#include <m/m3d/m_anmmdl.h>
+#include <m/m3d/m_anmtexpat.h>
+
+extern "C" bool fn_800B9D80(f32, f32, f32 *, int, const mVec3_c &, u32);
+
+class dAcEhb_leaf_c : public dAcObjBase_c {
+public:
+    dAcEhb_leaf_c() : someFloat(unkFloat0), field_0x3CC(0), field_0x3DE(0) {}
+    virtual ~dAcEhb_leaf_c() {}
+    virtual bool createHeap() override;
+    virtual int create() override;
+    virtual int actorExecute() override;
+    virtual int draw() override;
+    virtual int doDelete() override;
+
+    void setAnm(const char *anm, f32 pos);
+
+private:
+    const static f32 unkFloat0;
+    const static f32 unkFloat1;
+
+    m3d::mdlAnmChr mModel; // 0x330
+    m3d::anmTexPat_c mAnm; // 0x398
+
+    f32 someFloat;        // 0x3C4
+    UNKWORD field_0x3C8;  // 0x3C8
+    u8 field_0x3CC;       // 0x3CC
+    mVec3_c mStartingPos; // 0x3D0
+    u16 someRot;          // 0x3DC
+    u8 field_0x3DE;       // 0x3DE
+    u8 mType;             // 0x3DF
+
+    bool isNearZero() {
+        return fabsf(someFloat) <= FLT_EPSILON;
+    }
+
+    bool isHidden() {
+        return mType != 0 && isNearZero() ? true : false;
+    }
+
+    void doCheck() {
+        fn_800B9D80(10.0f, 0.2f, &someFloat, roomid, mStartingPos, 0);
+    }
+
+    int getParam1() {
+        return params & 7;
+    }
+
+    int getParam2() {
+        return params >> 7 & 3;
+    }
+};
+
+#endif
diff --git a/include/d/a/en/d_a_en_base.h b/include/d/a/e/d_a_en_base.h
similarity index 100%
rename from include/d/a/en/d_a_en_base.h
rename to include/d/a/e/d_a_en_base.h
diff --git a/include/d/a/obj/d_a_obj_base.h b/include/d/a/obj/d_a_obj_base.h
index 614f1499..02a1b24e 100644
--- a/include/d/a/obj/d_a_obj_base.h
+++ b/include/d/a/obj/d_a_obj_base.h
@@ -62,7 +62,7 @@ class dAcObjBase_c : public dAcBase_c {
     // could be their own thing?
     /* 8002de40 */ static void *getOarcFile(const char *oarcName, const char *fileName);
     /* 8002de60 */ static void *getOarcSubEntry(const char *oarcName, const char *fileName);
-    /* 8002de80 */ static nw4r::g3d::ResFile getOarcResFile(const char *oarcName);
+    /* 8002de80 */ static void *getOarcResFile(const char *oarcName);
     /* 8002de90 */ static void *getOarcModelFile(const char *oarcName);
     /* 8002dea0 */ static void *getOarcZev(const char *oarcName);
     /* 8002deb0 */ static void *getOarcDZB(const char *dzbName);
diff --git a/include/m/m3d/m_anmmdl.h b/include/m/m3d/m_anmmdl.h
index 2ee01679..55b82f39 100644
--- a/include/m/m3d/m_anmmdl.h
+++ b/include/m/m3d/m_anmmdl.h
@@ -11,13 +11,13 @@ namespace m3d {
 class mdlAnmChr {
 public:
     mdlAnmChr() {}
-    virtual ~mdlAnmChr();
+    virtual ~mdlAnmChr(); // 0x08
 
-    virtual void play();
-    virtual void setFrame(f32);
-    virtual void setAnm(const char *name, playMode_e mode, f32);
-    virtual void setAnm(const char *name, playMode_e mode);
-    virtual void setRate(f32);
+    virtual void play(); // 0x0C
+    virtual void setFrame(f32); // 0x10
+    virtual void setAnm(const char *name, playMode_e mode, f32); // 0x14
+    virtual void setAnm(const char *name, playMode_e mode); // 0x18
+    virtual void setRate(f32); // 0x20
 
     bool create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
             u32 bufferOption, int nView, u32 *pSize);
@@ -29,6 +29,14 @@ class mdlAnmChr {
     bool create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mdl_c::mdlCallback_c *callback,
             mAllocator_c *alloc, u32 bufferOption, int nView, u32 *pSize);
 
+    inline bool create(void *resFile, const char *mdlName, const char *anmName, mAllocator_c *alloc, u32 bufferOption) {
+        return create(resFile, mdlName, anmName, alloc, bufferOption, 1, nullptr);
+    }
+
+    inline mdl_c &getModel() {
+        return mMdl;
+    }
+
 private:
     nw4r::g3d::ResFile mMdlFile;
     nw4r::g3d::ResFile mAnmFile;
diff --git a/src/REL/d/a/e/d_a_e_hb_leaf.cpp b/src/REL/d/a/e/d_a_e_hb_leaf.cpp
index e69de29b..e055631e 100644
--- a/src/REL/d/a/e/d_a_e_hb_leaf.cpp
+++ b/src/REL/d/a/e/d_a_e_hb_leaf.cpp
@@ -0,0 +1,109 @@
+#include <d/a/e/d_a_e_hb_leaf.h>
+
+const f32 dAcEhb_leaf_c::unkFloat0 = 0.0f;
+const f32 dAcEhb_leaf_c::unkFloat1 = -0.0f;
+
+SPECIAL_ACTOR_PROFILE(E_HB_LEAF, dAcEhb_leaf_c, fProfile::E_HB_LEAF, 0x100, 0, 3);
+
+bool dAcEhb_leaf_c::createHeap() {
+    // This is ever so slightly weird but our m_anmMdl really doesn't take ResFile by value
+    // but only ever a single pointer.
+    void *fp = getOarcResFile("Degubaba");
+    TRY_CREATE(mModel.create(fp, "degubaba_leaf", "shake2", &heap_allocator, 0x123));
+    nw4r::g3d::ResFile f = fp;
+    nw4r::g3d::ResMdl mdl = f.GetResMdl("degubaba_leaf");
+    nw4r::g3d::ResAnmTexPat anm = f.GetResAnmTexPat("degubaba_leaf");
+    TRY_CREATE(mAnm.create(mdl, anm, &heap_allocator, nullptr, 1));
+    mModel.getModel().setAnm(mAnm);
+    return true;
+}
+
+int dAcEhb_leaf_c::create() {
+    if (!initAllocatorWork1Heap(-1, "dAcEhb_leaf_c::m_allocator", 0x20)) {
+        return FAILED;
+    }
+
+    setBoundingBox(mVec3_c(-100.0f, -100.0f, -100.0f), mVec3_c(100.0f, 100.0f, 100.0f));
+
+    rotation.y = cM::rndF(65535.0f);
+    someRot = rotation.y.mVal;
+
+    switch (getParam1()) {
+    case 0:
+    case 7:
+        mType = 0;
+        break;
+    case 1:
+        mType = 1;
+        break;
+    case 2:
+        mType = 2;
+        break;
+    case 3:
+    case 4:
+    case 5:
+        rotation.z = -0x8000;
+        break;
+    default:
+        break;
+    }
+
+    if (mType == 0) {
+        u32 param2 = getParam2();
+        if (param2 == 1) {
+            mType = 1;
+        } else if (param2 == 2) {
+            mType = 2;
+        }
+    }
+
+    if (mType == 2) {
+        field_0x3CC = 1;
+        someFloat = 1.0f;
+    }
+
+    if (mType != 0) {
+        actor_properties &= ~1;
+    }
+
+    mStartingPos = position;
+
+    return SUCCEEDED;
+}
+
+int dAcEhb_leaf_c::doDelete() {
+    return 1;
+}
+
+int dAcEhb_leaf_c::actorExecute() {
+    if (mType != 0) {
+        doCheck();
+        if (isNearZero()) {
+            return 1;
+        }
+    }
+
+    updateMatrix();
+    if (mType != 0) {
+        Mtx m;
+        PSMTXScale(m, someFloat, someFloat, someFloat);
+        PSMTXConcat(worldMatrix.m, m, worldMatrix.m);
+    }
+    mModel.getModel().setLocalMtx(worldMatrix);
+    mModel.getModel().calc(false);
+    someRot++;
+
+    return 1;
+}
+
+int dAcEhb_leaf_c::draw() {
+    if (!isHidden()) {
+        drawModelType1(&mModel.getModel());
+    }
+
+    return 1;
+}
+
+void dAcEhb_leaf_c::setAnm(const char *anm, f32 pos) {
+    mModel.setAnm(anm, m3d::PLAY_MODE_4, pos);
+}
diff --git a/tools/rel_sieve.py b/tools/rel_sieve.py
index 7c9e3cf4..fbf17e24 100644
--- a/tools/rel_sieve.py
+++ b/tools/rel_sieve.py
@@ -31,7 +31,6 @@
     ['fn_80353F30', 'UnkCollider::ctor2'],
     ['fn_80353FF0', 'UnkCollider::init'],
     ['fn_800C3EC0', 'ActorEventFlowManagerRelated::checkEventFinished'],
-    ['fn_802E6000', 'mdlAnmChr_c::create'],
     ['fn_800275C0', 'EffectsStruct::ctor'],
     ['fn_80027610', 'EffectsStruct::ctor'],
     ['fn_803465D0', 'ActorCollision::ctor'],

From 548da27b994b3c4e9b06384bd154d0e29f6a33af Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Mon, 1 Jul 2024 16:49:04 +0200
Subject: [PATCH 24/33] Cleanup

---
 src/m/m3d/m_mdl.cpp | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index aaf03c89..954cb4ff 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -50,12 +50,10 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
             }
 
             C_QUATSlerp(q1, q2, q1, f1);
-            // TODO weird hacks to force register order, probably more inlines
-            f32 tmp3, tmp2, tmp1;
-            tmp1 = result->mMtx._03;
-            tmp2 = result->mMtx._13;
-            tmp3 = result->mMtx._23;
-            nw4r::math::VEC3 tmp(tmp1, tmp2, tmp3);
+            nw4r::math::VEC3 tmp;
+            tmp.x = result->mMtx._03;
+            tmp.y = result->mMtx._13;
+            tmp.z = result->mMtx._23;
             PSMTXQuat(result->mMtx, q1);
             result->mMtx._03 = tmp.x;
             result->mMtx._13 = tmp.y;
@@ -65,11 +63,10 @@ void mdl_c::mdlCallback_c::ExecCallbackA(nw4r::g3d::ChrAnmResult *result, nw4r::
                 result->mMtx._13 = tmp.y * f1;
                 result->mMtx._23 = tmp.z * f1;
             }
-            u32 flags2 = result->mFlags & ~(0x80000000 | 0x80000040 | 0x80000020 | 0x80000008);
             result->mMtx._03 += resPtr->mMtx._03 * f2;
             result->mMtx._13 += resPtr->mMtx._13 * f2;
             result->mMtx._23 += resPtr->mMtx._23 * f2;
-            result->mFlags = flags2;
+            result->mFlags = result->mFlags & ~(0x80000000 | 0x00000040 | 0x00000020 | 0x00000008);
             *resPtr = *result;
         }
     } else {

From a3a8396ba7251fcbf6dea49f3708055b661085db Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Mon, 1 Jul 2024 16:57:06 +0200
Subject: [PATCH 25/33] Rebase/warning fixes

---
 config/SOUE01/symbols.txt     |  2 +-
 include/m/m3d/m_anmchrblend.h |  4 ++--
 src/m/m3d/m_anmchrblend.cpp   |  6 +++---
 src/m/m_fader.cpp             | 11 ++++-------
 4 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 6c60ae5c..1a2a879b 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -24725,7 +24725,7 @@ fn_8044E230 = .text:0x8044E230; // type:function size:0x1C
 fn_8044E250 = .text:0x8044E250; // type:function size:0x68
 SetRenderModeObj__Q34nw4r3g3d8G3DStateFRC16_GXRenderModeObj = .text:0x8044E2C0; // type:function size:0xD4
 fn_8044E3A0 = .text:0x8044E3A0; // type:function size:0xC
-fn_8044E3B0 = .text:0x8044E3B0; // type:function size:0x16C
+Invalidate__Q34nw4r3g3d8G3DStateFUl = .text:0x8044E3B0; // type:function size:0x16C
 fn_8044E520 = .text:0x8044E520; // type:function size:0x15C
 fn_8044E680 = .text:0x8044E680; // type:function size:0x50
 fn_8044E6D0 = .text:0x8044E6D0; // type:function size:0x16C
diff --git a/include/m/m3d/m_anmchrblend.h b/include/m/m3d/m_anmchrblend.h
index bcb9464a..e8d038d3 100644
--- a/include/m/m3d/m_anmchrblend.h
+++ b/include/m/m3d/m_anmchrblend.h
@@ -15,8 +15,8 @@ class anmChrBlend_c : public banm_c {
     virtual int getType() const override;
 
     bool create(nw4r::g3d::ResMdl, int, mAllocator_c *, u32 *);
-    bool attach(int, nw4r::g3d::AnmObjChrRes *, f32);
-    bool attach(int, anmChr_c *, f32);
+    void attach(int, nw4r::g3d::AnmObjChrRes *, f32);
+    void attach(int, anmChr_c *, f32);
     void detach(int);
     // Not in NSMBW
     void setWeight(int, f32);
diff --git a/src/m/m3d/m_anmchrblend.cpp b/src/m/m3d/m_anmchrblend.cpp
index 4959df4b..afbdd2ce 100644
--- a/src/m/m3d/m_anmchrblend.cpp
+++ b/src/m/m3d/m_anmchrblend.cpp
@@ -30,13 +30,13 @@ bool anmChrBlend_c::create(nw4r::g3d::ResMdl mdl, int num, mAllocator_c *alloc,
     return true;
 }
 
-bool anmChrBlend_c::attach(int idx, nw4r::g3d::AnmObjChrRes *anm, f32 weight) {
+void anmChrBlend_c::attach(int idx, nw4r::g3d::AnmObjChrRes *anm, f32 weight) {
     nw4r::g3d::AnmObjChrBlend *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj);
     obj->SetWeight(idx, weight);
     obj->Attach(idx, anm);
 }
 
-bool anmChrBlend_c::attach(int idx, anmChr_c *anm, f32 weight) {
+void anmChrBlend_c::attach(int idx, anmChr_c *anm, f32 weight) {
     nw4r::g3d::AnmObjChrRes *res = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrRes>(anm->getAnimObj());
     attach(idx, res, weight);
 }
@@ -50,7 +50,7 @@ void anmChrBlend_c::setWeight(int idx, f32 weight) {
 }
 
 f32 anmChrBlend_c::getWeight(int idx) const {
-    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj)->GetWeight(idx);
+    return nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrBlend>(mpAnmObj)->GetWeight(idx);
 }
 
 } // namespace m3d
diff --git a/src/m/m_fader.cpp b/src/m/m_fader.cpp
index 8f88a4ab..7e537c46 100644
--- a/src/m/m_fader.cpp
+++ b/src/m/m_fader.cpp
@@ -1,13 +1,10 @@
 #include <m/m_fader.h>
-
-// nw4r::g3d::G3DState::Invalidate
-extern "C" void fn_8044E3B0(u32 flags);
-// m3d::resetMaterial according to NSMBW
-extern "C" void fn_802E46D0();
+#include <m/m3d/m3d.h>
+#include <nw4r/g3d/g3d_state.h>
 
 void mFader_c::draw() {
-    fn_8044E3B0(0x7ff);
-    fn_802E46D0();
+    nw4r::g3d::G3DState::Invalidate(0x7ff);
+    m3d::resetMaterial();
     mpFader->draw();
 }
 

From 66a7b0a1b55f0990f52c982e36ffd1384821881d Mon Sep 17 00:00:00 2001
From: robojumper <robojumper@gmail.com>
Date: Fri, 5 Jul 2024 19:30:19 +0200
Subject: [PATCH 26/33] Don't reinvent SpawnStruct

---
 config/SOUE01/symbols.txt             |  2 +-
 include/toBeSorted/scgame.h           |  7 ++++++-
 include/toBeSorted/spawn_struct.h     | 20 --------------------
 src/REL/d/a/obj/d_a_obj_sun_light.cpp |  4 ++--
 4 files changed, 9 insertions(+), 24 deletions(-)
 delete mode 100644 include/toBeSorted/spawn_struct.h

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 1a2a879b..d107ae14 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -48788,7 +48788,7 @@ lbl_805B3760 = .bss:0x805B3760; // type:object size:0x80
 lbl_805B37E0 = .bss:0x805B37E0; // type:object size:0xC
 lbl_805B37EC = .bss:0x805B37EC; // type:object size:0x40 data:4byte
 lbl_805B382C = .bss:0x805B382C; // type:object size:0x34 data:4byte
-sInstance__11SpawnStruct = .bss:0x805B3860; // type:object size:0x2C data:4byte
+currentSpawnInfo__6ScGame = .bss:0x805B3860; // type:object size:0x2C data:4byte
 lbl_805B388C = .bss:0x805B388C; // type:object size:0x2C data:byte
 lbl_805B38B8 = .bss:0x805B38B8; // type:object size:0x50
 lbl_805B3908 = .bss:0x805B3908; // type:object size:0x40 data:4byte
diff --git a/include/toBeSorted/scgame.h b/include/toBeSorted/scgame.h
index aa465d4a..d8b5e649 100644
--- a/include/toBeSorted/scgame.h
+++ b/include/toBeSorted/scgame.h
@@ -4,7 +4,7 @@
 #include <common.h>
 
 struct SpawnInfo {
-    /* 0x00 */ char stageName[32];
+    /* 0x00 */ char stageName[32]; // Probably SizedString<32>
     /* 0x20 */ s16 transitionFadeFrames;
     /* 0x22 */ s8 room;
     /* 0x23 */ s8 layer;
@@ -13,6 +13,11 @@ struct SpawnInfo {
     /* 0x26 */ s8 trial;
     /* 0x27 */ s8 transitionType;
     /* 0x28 */ s32 unk;
+
+    // TODO figure out correct types
+    bool isNight() {
+        return (u8)night;
+    }
 };
 
 class ScGame {
diff --git a/include/toBeSorted/spawn_struct.h b/include/toBeSorted/spawn_struct.h
deleted file mode 100644
index 8bad4516..00000000
--- a/include/toBeSorted/spawn_struct.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef SPAWN_STRUCT_H
-#define SPAWN_STRUCT_H
-
-#include <common.h>
-
-struct SpawnStruct {
-    /* 0x00 */ char mName[32]; // Could be SizedString<32>
-    /* 0x20 */ u16 mTransitionFadeFrames;
-    /* 0x22 */ s8 mRoom;
-    /* 0x23 */ s8 mLayer;
-    /* 0x24 */ s8 mEntrance;
-    /* 0x25 */ bool mNight;
-    /* 0x26 */ bool mTrial;
-    /* 0x27 */ bool mTransitionType;
-    /* 0x28 */ u8 UNK_0x28[0x2C - 0x28];
-
-    static SpawnStruct sInstance;
-};
-
-#endif
diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index 83f375be..d0f889a2 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -1,6 +1,6 @@
 #include <d/a/obj/d_a_obj_sun_light.h>
 #include <toBeSorted/arc_managers/current_stage_arc_manager.h>
-#include <toBeSorted/spawn_struct.h>
+#include <toBeSorted/scgame.h>
 #include <toBeSorted/room_manager.h>
 
 SPECIAL_ACTOR_PROFILE(OBJ_SUN_LIGHT, dAcOsunLight_c, fProfile::OBJ_SUN_LIGHT, 0x0219, 0, 3);
@@ -58,5 +58,5 @@ void dAcOsunLight_c::executeState_Wait() {}
 void dAcOsunLight_c::finalizeState_Wait() {}
 
 bool dAcOsunLight_c::isDay() {
-    return !SpawnStruct::sInstance.mNight;
+    return !ScGame::currentSpawnInfo.isNight();
 }

From 57280519d39ce73d6b76d6e3fcfa404d821a79cb Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Mon, 9 Sep 2024 00:02:34 -0400
Subject: [PATCH 27/33] Update m3d

bmdl more fleshed out, temp names for missed section, formatting
---
 config/SOUE01/splits.txt          |   1 +
 config/SOUE01/symbols.txt         |  82 ++++++-------
 include/m/m3d/m3d.h               |   5 +-
 include/m/m3d/m_anmchr.h          |   3 +-
 include/m/m3d/m_anmmdl.h          |   8 +-
 include/m/m3d/m_anmtexpat.h       |  12 +-
 include/m/m3d/m_anmtexsrt.h       |  12 +-
 include/m/m3d/m_anmvis.h          |   1 -
 include/m/m3d/m_bmdl.h            |  20 +++-
 include/m/m3d/m_fanm.h            |   3 +-
 include/m/m3d/m_scnleaf.h         |   6 +
 include/m/m_mtx.h                 |   6 +-
 include/nw4r/g3d/g3d_resdict.h    |  28 ++---
 include/nw4r/g3d/g3d_resmat.h     | 140 ++++++++++++++++++-----
 include/nw4r/g3d/g3d_scnmdl.h     |  10 +-
 include/nw4r/g3d/g3d_scnmdlsmpl.h |  27 +++--
 src/m/m3d/m3d.cpp                 |  22 ++++
 src/m/m3d/m_anmchr.cpp            |   9 +-
 src/m/m3d/m_anmchrblend.cpp       |   2 +-
 src/m/m3d/m_anmmatclr.cpp         |   4 +-
 src/m/m3d/m_anmshp.cpp            |   2 +-
 src/m/m3d/m_anmtexpat.cpp         |  10 +-
 src/m/m3d/m_anmtexsrt.cpp         |  10 +-
 src/m/m3d/m_anmvis.cpp            |   3 +-
 src/m/m3d/m_bmdl.cpp              | 184 ++++++++++++++++++++++++++++--
 src/m/m3d/m_mdl.cpp               |   2 +-
 src/m/m3d/m_proc.cpp              |   2 +-
 src/m/m3d/m_scnLeaf.cpp           |   2 +-
 src/m/m3d/m_smdl.cpp              |   1 -
 29 files changed, 457 insertions(+), 160 deletions(-)

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 1eec0c9e..d043b6d2 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -324,6 +324,7 @@ m/m3d/m_banm.cpp:
 
 m/m3d/m_bmdl.cpp:
 	.text       start:0x802E9270 end:0x802EA6EC
+	.data       start:0x80542668 end:0x80542694
 
 m/m3d/m_calc_ratio.cpp:
 	.text       start:0x802EA6F0 end:0x802EA898
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index d107ae14..b7cb3444 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17431,7 +17431,7 @@ drawXlu__3m3dFv = .text:0x802E4580; // type:function size:0x8
 pushBack__3m3dFPQ34nw4r3g3d6ScnObj = .text:0x802E4590; // type:function size:0x20
 clear__3m3dFv = .text:0x802E45B0; // type:function size:0x50
 reset__3m3dFv = .text:0x802E4600; // type:function size:0x3C
-fn_802E4640 = .text:0x802E4640; // type:function size:0x40
+getMatID__3m3dFQ34nw4r3g3d6ResMdlPCc = .text:0x802E4640; // type:function size:0x40
 getNodeID__3m3dFQ34nw4r3g3d6ResMdlPCc = .text:0x802E4680; // type:function size:0x4C
 resetMaterial__3m3dFv = .text:0x802E46D0; // type:function size:0x44
 proc_c_drawProc__3m3dFPQ34nw4r3g3d7ScnProcb = .text:0x802E4720; // type:function size:0x2C
@@ -17552,46 +17552,46 @@ getFrame__Q23m3d6banm_cCFv = .text:0x802E7F20; // type:function size:0x14
 setFrameOnly__Q23m3d6banm_cFf = .text:0x802E7F40; // type:function size:0x14
 getRate__Q23m3d6banm_cCFv = .text:0x802E7F60; // type:function size:0x14
 setRate__Q23m3d6banm_cFf = .text:0x802E7F80; // type:function size:0x14
-fn_802E7FA0 = .text:0x802E7FA0; // type:function size:0x88
-fn_802E8030 = .text:0x802E8030; // type:function size:0x148
-fn_802E8180 = .text:0x802E8180; // type:function size:0x30
-fn_802E81B0 = .text:0x802E81B0; // type:function size:0x54
-fn_802E8210 = .text:0x802E8210; // type:function size:0xA8
-fn_802E82C0 = .text:0x802E82C0; // type:function size:0x8
-fn_802E82D0 = .text:0x802E82D0; // type:function size:0x3B8
-fn_802E8690 = .text:0x802E8690; // type:function size:0x98
-fn_802E8730 = .text:0x802E8730; // type:function size:0x98
-fn_802E87D0 = .text:0x802E87D0; // type:function size:0x60
-fn_802E8830 = .text:0x802E8830; // type:function size:0x1A0
-fn_802E89D0 = .text:0x802E89D0; // type:function size:0x4
-fn_802E89E0 = .text:0x802E89E0; // type:function size:0x40
-fn_802E8A20 = .text:0x802E8A20; // type:function size:0x4
-fn_802E8A30 = .text:0x802E8A30; // type:function size:0xA4
-fn_802E8AE0 = .text:0x802E8AE0; // type:function size:0x66C
-fn_802E9150 = .text:0x802E9150; // type:function size:0x11C
+ImageLineGroup_dtor = .text:0x802E7FA0; // type:function size:0x88
+ImageLineGroup_init = .text:0x802E8030; // type:function size:0x148
+ImageLine_Ct = .text:0x802E8180; // type:function size:0x30
+ImageLineGroup_remove = .text:0x802E81B0; // type:function size:0x54
+ImageLineGroup_BuildVtx = .text:0x802E8210; // type:function size:0xA8
+ImageLineGroup_GetImageLine = .text:0x802E82C0; // type:function size:0x8
+ImageLineGroup_SetupGX = .text:0x802E82D0; // type:function size:0x3B8
+ImageLineGroup_DrawOpaque = .text:0x802E8690; // type:function size:0x98
+ImageLineGroup_DrawTransparent = .text:0x802E8730; // type:function size:0x98
+ImageLine_dtor = .text:0x802E87D0; // type:function size:0x60
+ImageLine_Init = .text:0x802E8830; // type:function size:0x1A0
+ImageLine_VtxPosCt = .text:0x802E89D0; // type:function size:0x4
+ImageLine_VtxPosDt = .text:0x802E89E0; // type:function size:0x40
+ImageLine_VtxNrmCt = .text:0x802E8A20; // type:function size:0x4
+ImageLine_remove = .text:0x802E8A30; // type:function size:0xA4
+ImageLine_BuildVtx = .text:0x802E8AE0; // type:function size:0x66C
+ImageLine_Draw = .text:0x802E9150; // type:function size:0x11C
 __dt__Q23m3d6bmdl_cFv = .text:0x802E9270; // type:function size:0x7C
 getType__Q23m3d6bmdl_cCFv = .text:0x802E92F0; // type:function size:0x8
-fn_802E9300 = .text:0x802E9300; // type:function size:0x3C
-fn_802E9340 = .text:0x802E9340; // type:function size:0x3C
+getMatID__Q23m3d6bmdl_cCFPCc = .text:0x802E9300; // type:function size:0x3C
+getNodeID__Q23m3d6bmdl_cCFPCc = .text:0x802E9340; // type:function size:0x3C
 getNodeWorldMtx__Q23m3d6bmdl_cCFUlPQ34nw4r4math5MTX34 = .text:0x802E9380; // type:function size:0xA8
-fn_802E9430 = .text:0x802E9430; // type:function size:0x5C
-getNodeWorldMtxMultVecZero__Q23m3d6bmdl_cCFUlRQ34nw4r4math4VEC3 = .text:0x802E9490; // type:function size:0x60
+getNodeWorldMtxMultVecZero__Q23m3d6bmdl_cCFUlRQ34nw4r4math4VEC3 = .text:0x802E9430; // type:function size:0x5C
+getNodeWorldMtxMultVec__Q23m3d6bmdl_cCFUlRQ34nw4r4math4VEC3RQ34nw4r4math4VEC3 = .text:0x802E9490; // type:function size:0x60
 setAnm__Q23m3d6bmdl_cFRQ23m3d6banm_c = .text:0x802E94F0; // type:function size:0x15C
 play__Q23m3d6bmdl_cFv = .text:0x802E9650; // type:function size:0x20
 getResMdl__Q23m3d6bmdl_cCFv = .text:0x802E9670; // type:function size:0x80
 getResMat__Q23m3d6bmdl_cCFUl = .text:0x802E96F0; // type:function size:0x9C
 removeAnm__Q23m3d6bmdl_cFQ44nw4r3g3d12ScnMdlSimple10AnmObjType = .text:0x802E9790; // type:function size:0xBC
-fn_802E9850 = .text:0x802E9850; // type:function size:0xA0
-fn_802E98F0 = .text:0x802E98F0; // type:function size:0x130
-fn_802E9A20 = .text:0x802E9A20; // type:function size:0x16C
-fn_802E9B90 = .text:0x802E9B90; // type:function size:0x130
-fn_802E9CC0 = .text:0x802E9CC0; // type:function size:0x16C
-fn_802E9E30 = .text:0x802E9E30; // type:function size:0x170
-fn_802E9FA0 = .text:0x802E9FA0; // type:function size:0xE4
-fn_802EA090 = .text:0x802EA090; // type:function size:0x120
-fn_802EA1B0 = .text:0x802EA1B0; // type:function size:0xE4
-fn_802EA2A0 = .text:0x802EA2A0; // type:function size:0x2F0
-fn_802EA590 = .text:0x802EA590; // type:function size:0x150
+getAnmObj__Q23m3d6bmdl_cCFQ44nw4r3g3d12ScnMdlSimple10AnmObjType = .text:0x802E9850; // type:function size:0xA0
+setTevColor__Q23m3d6bmdl_cFUl11_GXTevRegID8_GXColorb = .text:0x802E98F0; // type:function size:0x130
+setTevColorAll__Q23m3d6bmdl_cF11_GXTevRegID8_GXColorb = .text:0x802E9A20; // type:function size:0x16C
+setTevKColor__Q23m3d6bmdl_cFUl14_GXTevKColorID8_GXColorb = .text:0x802E9B90; // type:function size:0x130
+setTevKColorAll__Q23m3d6bmdl_cF14_GXTevKColorID8_GXColorb = .text:0x802E9CC0; // type:function size:0x16C
+setBlendModeAll__Q23m3d6bmdl_cF12_GXBlendMode14_GXBlendFactor14_GXBlendFactor10_GXLogicOpb = .text:0x802E9E30; // type:function size:0x170
+setCullMode__Q23m3d6bmdl_cFUl11_GXCullModeb = .text:0x802E9FA0; // type:function size:0xE4
+setCullModeAll__Q23m3d6bmdl_cF11_GXCullModeb = .text:0x802EA090; // type:function size:0x120
+setMatVisible__Q23m3d6bmdl_cFUlb = .text:0x802EA1B0; // type:function size:0xE4
+setMatTexture__Q23m3d6bmdl_cFPcP9_GXTexObjbPvii = .text:0x802EA2A0; // type:function size:0x2F0
+getBounds__Q23m3d6bmdl_cFP7mVec3_cP7mVec3_c = .text:0x802EA590; // type:function size:0x150
 remove__Q23m3d6bmdl_cFv = .text:0x802EA6E0; // type:function size:0xC
 __ct__Q23m3d11calcRatio_cFv = .text:0x802EA6F0; // type:function size:0x38
 __dt__Q23m3d11calcRatio_cFv = .text:0x802EA730; // type:function size:0x40
@@ -24332,13 +24332,13 @@ fn_8043CB00 = .text:0x8043CB00; // type:function size:0x3C
 fn_8043CB40 = .text:0x8043CB40; // type:function size:0xB8
 fn_8043CC00 = .text:0x8043CC00; // type:function size:0x24
 fn_8043CC30 = .text:0x8043CC30; // type:function size:0x20
-fn_8043CC50 = .text:0x8043CC50; // type:function size:0xB0
+GXSetBlendMode__Q34nw4r3g3d9ResMatPixF12_GXBlendMode14_GXBlendFactor14_GXBlendFactor10_GXLogicOp = .text:0x8043CC50; // type:function size:0xB0
 fn_8043CD00 = .text:0x8043CD00; // type:function size:0x74
 fn_8043CD80 = .text:0x8043CD80; // type:function size:0x18
 fn_8043CDA0 = .text:0x8043CDA0; // type:function size:0xBC
-fn_8043CE60 = .text:0x8043CE60; // type:function size:0x94
+GXSetTevColor__Q34nw4r3g3d14ResMatTevColorF11_GXTevRegID8_GXColor = .text:0x8043CE60; // type:function size:0x94
 fn_8043CF00 = .text:0x8043CF00; // type:function size:0xB8
-fn_8043CFC0 = .text:0x8043CFC0; // type:function size:0x84
+GXSetTevKColor__Q34nw4r3g3d14ResMatTevColorF14_GXTevKColorID8_GXColor = .text:0x8043CFC0; // type:function size:0x84
 fn_8043D050 = .text:0x8043D050; // type:function size:0x34
 fn_8043D090 = .text:0x8043D090; // type:function size:0x2B8
 fn_8043D350 = .text:0x8043D350; // type:function size:0x1E8
@@ -24947,14 +24947,14 @@ fn_8045D220 = .text:0x8045D220; // type:function size:0x30
 fn_8045D250 = .text:0x8045D250; // type:function size:0xC
 fn_8045D260 = .text:0x8045D260; // type:function size:0x48
 fn_8045D2B0 = .text:0x8045D2B0; // type:function size:0x48
-fn_8045D300 = .text:0x8045D300; // type:function size:0x48
+GetResGenMode__Q44nw4r3g3d6ScnMdl15CopiedMatAccessFb = .text:0x8045D300; // type:function size:0x48
 fn_8045D350 = .text:0x8045D350; // type:function size:0x48
-fn_8045D3A0 = .text:0x8045D3A0; // type:function size:0x48
-fn_8045D3F0 = .text:0x8045D3F0; // type:function size:0x48
+GetResMatPix__Q44nw4r3g3d6ScnMdl15CopiedMatAccessFb = .text:0x8045D3A0; // type:function size:0x48
+GetResMatTevColor__Q44nw4r3g3d6ScnMdl15CopiedMatAccessFb = .text:0x8045D3F0; // type:function size:0x48
 fn_8045D440 = .text:0x8045D440; // type:function size:0x48
 fn_8045D490 = .text:0x8045D490; // type:function size:0x60
 fn_8045D4F0 = .text:0x8045D4F0; // type:function size:0x60
-fn_8045D550 = .text:0x8045D550; // type:function size:0x280
+__ct__Q44nw4r3g3d6ScnMdl15CopiedMatAccessFPQ34nw4r3g3d6ScnMdlUl = .text:0x8045D550; // type:function size:0x280
 fn_8045D7D0 = .text:0x8045D7D0; // type:function size:0x7C
 fn_8045D850 = .text:0x8045D850; // type:function size:0xD0
 fn_8045D920 = .text:0x8045D920; // type:function size:0xBC
@@ -35700,7 +35700,7 @@ lbl_805425E0 = .data:0x805425E0; // type:object size:0x30
 __vt__Q23m3d6banm_c = .data:0x80542610; // type:object size:0x18
 lbl_80542628 = .data:0x80542628; // type:object size:0x30
 lbl_80542658 = .data:0x80542658; // type:object size:0x10
-__vt__Q23m3d6bmdl_c = .data:0x80542668; // type:object size:0x30
+__vt__Q23m3d6bmdl_c = .data:0x80542668; // type:object size:0x2C
 __vt__Q23m3d11calcRatio_c = .data:0x80542698; // type:object size:0xC
 __vt__Q23m3d6fanm_c = .data:0x805426A8; // type:object size:0x18
 __vt__Q23m3d5mdl_c = .data:0x805426C0; // type:object size:0x2C
diff --git a/include/m/m3d/m3d.h b/include/m/m3d/m3d.h
index 835ff57a..8b7d82f4 100644
--- a/include/m/m3d/m3d.h
+++ b/include/m/m3d/m3d.h
@@ -12,7 +12,7 @@ namespace internal {
 extern mAllocator_c *l_allocator_p;
 extern nw4r::g3d::ScnRoot *l_scnRoot_p;
 
-}
+} // namespace internal
 
 bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4);
 
@@ -34,6 +34,9 @@ void clear();
 
 void reset();
 
+int getMatID(nw4r::g3d::ResMdl, const char *);
+int getNodeID(nw4r::g3d::ResMdl, const char *);
+
 void resetMaterial();
 
 } // namespace m3d
diff --git a/include/m/m3d/m_anmchr.h b/include/m/m3d/m_anmchr.h
index f451bfb7..082cf504 100644
--- a/include/m/m3d/m_anmchr.h
+++ b/include/m/m3d/m_anmchr.h
@@ -5,7 +5,6 @@
 #include <m/m3d/m_fanm.h>
 #include <nw4r/g3d/g3d_resanmchr.h>
 
-
 namespace m3d {
 
 class anmChr_c : public fanm_c {
@@ -14,7 +13,7 @@ class anmChr_c : public fanm_c {
 
     virtual int getType() const override;
 
-    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmChr, mAllocator_c*, u32*);
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmChr, mAllocator_c *, u32 *);
     void setAnm(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
     void setAnmAfter(bmdl_c &, nw4r::g3d::ResAnmChr, playMode_e);
     void setFrmCtrlDefault(nw4r::g3d::ResAnmChr &, m3d::playMode_e);
diff --git a/include/m/m3d/m_anmmdl.h b/include/m/m3d/m_anmmdl.h
index 55b82f39..1673b421 100644
--- a/include/m/m3d/m_anmmdl.h
+++ b/include/m/m3d/m_anmmdl.h
@@ -13,11 +13,11 @@ class mdlAnmChr {
     mdlAnmChr() {}
     virtual ~mdlAnmChr(); // 0x08
 
-    virtual void play(); // 0x0C
-    virtual void setFrame(f32); // 0x10
+    virtual void play();                                         // 0x0C
+    virtual void setFrame(f32);                                  // 0x10
     virtual void setAnm(const char *name, playMode_e mode, f32); // 0x14
-    virtual void setAnm(const char *name, playMode_e mode); // 0x18
-    virtual void setRate(f32); // 0x20
+    virtual void setAnm(const char *name, playMode_e mode);      // 0x18
+    virtual void setRate(f32);                                   // 0x20
 
     bool create(void *mdlFile, void *anmFile, const char *mdlName, const char *anmName, mAllocator_c *alloc,
             u32 bufferOption, int nView, u32 *pSize);
diff --git a/include/m/m3d/m_anmtexpat.h b/include/m/m3d/m_anmtexpat.h
index 923bbbc8..1fc5b1d0 100644
--- a/include/m/m3d/m_anmtexpat.h
+++ b/include/m/m3d/m_anmtexpat.h
@@ -15,24 +15,24 @@ class anmTexPat_c : public banm_c {
         virtual int getType() const override;
 
         static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, bool);
-        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c*, u32*);
-        void setAnm(m3d::bmdl_c&, nw4r::g3d::ResAnmTexPat, m3d::playMode_e);
+        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c *, u32 *);
+        void setAnm(m3d::bmdl_c &, nw4r::g3d::ResAnmTexPat, m3d::playMode_e);
         void releaseAnm();
-        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexPat&, playMode_e);
+        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexPat &, playMode_e);
     };
 
 public:
-    anmTexPat_c(): mpChildren(nullptr) {}
+    anmTexPat_c() : mpChildren(nullptr) {}
     virtual ~anmTexPat_c();
 
     virtual int getType() const override;
     virtual void remove() override;
     virtual void play() override;
 
-    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c*, u32*, s32);
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, mAllocator_c *, u32 *, s32);
     static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexPat, s32, bool);
 
-    void setAnm(bmdl_c&, nw4r::g3d::ResAnmTexPat, s32, playMode_e);
+    void setAnm(bmdl_c &, nw4r::g3d::ResAnmTexPat, s32, playMode_e);
 
     void play(s32);
     f32 getFrame(s32) const;
diff --git a/include/m/m3d/m_anmtexsrt.h b/include/m/m3d/m_anmtexsrt.h
index 4aed956e..d736a7b1 100644
--- a/include/m/m3d/m_anmtexsrt.h
+++ b/include/m/m3d/m_anmtexsrt.h
@@ -15,24 +15,24 @@ class anmTexSrt_c : public banm_c {
         virtual int getType() const override;
 
         static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, bool);
-        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c*, u32*);
-        void setAnm(m3d::bmdl_c&, nw4r::g3d::ResAnmTexSrt, m3d::playMode_e);
+        bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c *, u32 *);
+        void setAnm(m3d::bmdl_c &, nw4r::g3d::ResAnmTexSrt, m3d::playMode_e);
         void releaseAnm();
-        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexSrt&, playMode_e);
+        void setFrmCtrlDefault(nw4r::g3d::ResAnmTexSrt &, playMode_e);
     };
 
 public:
-    anmTexSrt_c(): mpChildren(nullptr) {}
+    anmTexSrt_c() : mpChildren(nullptr) {}
     virtual ~anmTexSrt_c();
 
     virtual int getType() const override;
     virtual void remove() override;
     virtual void play() override;
 
-    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c*, u32*, s32);
+    bool create(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, mAllocator_c *, u32 *, s32);
     static u32 heapCost(nw4r::g3d::ResMdl, nw4r::g3d::ResAnmTexSrt, s32, bool);
 
-    void setAnm(bmdl_c&, nw4r::g3d::ResAnmTexSrt, s32, playMode_e);
+    void setAnm(bmdl_c &, nw4r::g3d::ResAnmTexSrt, s32, playMode_e);
 
     void play(s32);
     f32 getFrame(s32) const;
diff --git a/include/m/m3d/m_anmvis.h b/include/m/m3d/m_anmvis.h
index 0f3fc80d..053c330d 100644
--- a/include/m/m3d/m_anmvis.h
+++ b/include/m/m3d/m_anmvis.h
@@ -4,7 +4,6 @@
 #include <m/m3d/m_bmdl.h>
 #include <m/m3d/m_fanm.h>
 
-
 namespace m3d {
 
 class anmVis_c : public fanm_c {
diff --git a/include/m/m3d/m_bmdl.h b/include/m/m3d/m_bmdl.h
index fd1a24ed..aa692b29 100644
--- a/include/m/m3d/m_bmdl.h
+++ b/include/m/m3d/m_bmdl.h
@@ -3,9 +3,9 @@
 
 #include <m/m3d/m_banm.h>
 #include <m/m3d/m_scnleaf.h>
+#include <m/m_math.h>
 #include <nw4r/g3d/g3d_scnmdlsmpl.h>
 
-
 namespace m3d {
 
 class bmdl_c : public scnLeaf_c {
@@ -18,12 +18,26 @@ class bmdl_c : public scnLeaf_c {
     virtual void setAnm(banm_c &anm);
     virtual void play();
 
-    void getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const;
-
+    int getMatID(const char *name) const;
+    int getNodeID(const char *name) const;
+    bool getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const;
+    bool getNodeWorldMtxMultVecZero(u32 p1, nw4r::math::VEC3 &out) const;
+    bool getNodeWorldMtxMultVec(u32, nw4r::math::VEC3 &, nw4r::math::VEC3 &) const;
     nw4r::g3d::ResMdl getResMdl() const;
     nw4r::g3d::ResMat getResMat(u32 index) const;
 
     void removeAnm(nw4r::g3d::ScnMdlSimple::AnmObjType);
+    nw4r::g3d::AnmObj *getAnmObj(nw4r::g3d::ScnMdlSimple::AnmObjType) const;
+    void setTevColor(u32, GXTevRegID, GXColor, bool);
+    void setTevColorAll(GXTevRegID, GXColor, bool);
+    void setTevKColor(u32, GXTevKColorID, GXColor, bool);
+    void setTevKColorAll(GXTevKColorID, GXColor, bool);
+    void setBlendModeAll(GXBlendMode, GXBlendFactor, GXBlendFactor, GXLogicOp, bool bMarkDirty);
+    void setCullMode(u32 matId, GXCullMode cullMode, bool bMarkDirty);
+    void setCullModeAll(GXCullMode cullMode, bool bMarkDirty);
+    void setMatVisible(u32 matId, bool bVisble);
+    int setMatTexture(char *name, GXTexObj *texObj, bool copy, void *unk, int, int);
+    bool getBounds(mVec3_c *min, mVec3_c *max);
 
 private:
     banm_c *mpCurrentAnm;
diff --git a/include/m/m3d/m_fanm.h b/include/m/m3d/m_fanm.h
index dfbf027d..1dc0d9f2 100644
--- a/include/m/m3d/m_fanm.h
+++ b/include/m/m3d/m_fanm.h
@@ -18,7 +18,7 @@ class fanm_c : public banm_c {
     fanm_c();
     virtual ~fanm_c();
 
-    virtual int getType() const  = 0;
+    virtual int getType() const = 0;
     virtual void play();
 
     void set(f32, playMode_e, f32, f32);
@@ -29,7 +29,6 @@ class fanm_c : public banm_c {
     bool checkFrame(f32) const;
     bool unk_802EAE70() const;
 
-
     inline void setPlayState(playMode_e state) {
         mPlayState = state;
     }
diff --git a/include/m/m3d/m_scnleaf.h b/include/m/m3d/m_scnleaf.h
index 7a33ba61..18e9db9c 100644
--- a/include/m/m3d/m_scnleaf.h
+++ b/include/m/m3d/m_scnleaf.h
@@ -7,6 +7,12 @@
 
 namespace m3d {
 
+// NON-OFFICIAL
+enum ScnLeafType_e {
+    SCN_LEAF_MODEL = 0,
+    SCN_LEAF_PROC = 2,
+};
+
 class UnkClass {
 public:
     UnkClass() {}
diff --git a/include/m/m_mtx.h b/include/m/m_mtx.h
index 7172d8bc..879e62cf 100644
--- a/include/m/m_mtx.h
+++ b/include/m/m_mtx.h
@@ -8,6 +8,8 @@
 #include "nw4r/nw4r_types.h"
 #include <common.h>
 
+#pragma push
+#pragma warning off(10402)
 class mMtx_c {
 public:
     mMtx_c(){};
@@ -38,7 +40,7 @@ class mMtx_c {
     /* 802f1c40 */ void rot(int, int); // does some werrd operation to rotate the matrix
     /* 802f1e60 */ bool quatRelated();
 
-    operator nw4r::math::MTX34*() {
+    operator nw4r::math::MTX34 *() {
         return &nw4rm;
     }
 
@@ -58,4 +60,6 @@ class mMtx_c {
     static mMtx_c Identity;
 };
 
+#pragma pop
+
 #endif
diff --git a/include/nw4r/g3d/g3d_resdict.h b/include/nw4r/g3d/g3d_resdict.h
index 237ad095..9c29db73 100644
--- a/include/nw4r/g3d/g3d_resdict.h
+++ b/include/nw4r/g3d/g3d_resdict.h
@@ -6,26 +6,26 @@
 
 namespace nw4r {
 namespace g3d {
-struct ResDicEntry {
-    u16 INT_0x0;    // at 0x0
-    u16 INT_0x2;    // at 0x2
-    u16 unk_index;  // at 0x4
-    u16 unk_index2; // at 0x6
-    u32 INT_0x8;    // at 0x8
-    u32 INT_0xC;    // at 0xC
+struct ResDicNodeData {
+    u16 ref;       // at 0x0
+    u16 flag;      // at 0x2
+    u16 idxLeft;   // at 0x4
+    u16 idxRight;  // at 0x6
+    u32 ofsString; // at 0x8
+    u32 ofsData;   // at 0xC
 };
 
 struct ResDicData {
-    u32 mSize;              // at 0x0
-    u32 mNumData;           // at 0x4
-    ResDicEntry mEntries[]; // 0x8
+    u32 size;               // at 0x0
+    u32 numData;            // at 0x4
+    ResDicNodeData data[1]; // 0x8
 };
 
 struct ResDic {
     ResCommon<ResDicData> mDict;
 
-    ResDicEntry *Get(ResName) const;
-    ResDicEntry *Get(const char *, u32) const;
+    ResDicNodeData *Get(ResName) const;
+    ResDicNodeData *Get(const char *, u32) const;
     void *operator[](const char *) const;
     void *operator[](ResName) const;
     s32 GetIndex(ResName) const;
@@ -34,7 +34,7 @@ struct ResDic {
 
     inline void *operator[](int i) const {
         if (mDict.IsValid()) {
-            return (void *)mDict.ofs_to_ptr<void>(mDict.ref().mEntries[i + 1].INT_0xC);
+            return (void *)mDict.ofs_to_ptr<void>(mDict.ref().data[i + 1].ofsData);
         }
 
         return NULL;
@@ -42,7 +42,7 @@ struct ResDic {
 
     inline u32 GetNumData() const {
         if (mDict.IsValid()) {
-            return mDict.ref().mNumData;
+            return mDict.ref().numData;
         }
 
         return 0;
diff --git a/include/nw4r/g3d/g3d_resmat.h b/include/nw4r/g3d/g3d_resmat.h
index 6c495685..7c26d9e9 100644
--- a/include/nw4r/g3d/g3d_resmat.h
+++ b/include/nw4r/g3d/g3d_resmat.h
@@ -5,17 +5,59 @@
 #include "nw4r/math/math_types.h"
 #include <rvl/GX.h>
 
-
 namespace nw4r {
 namespace g3d {
-struct ResMatTexCoordGenData {};
 
-struct ResMatTexCoordGen {
-    ResCommon<ResMatTexCoordGenData> mTexGen; // at 0x0
+struct ResTevColorDL {
+    union {
+        struct {
+            u8 tevColor[3][20];  // offset 0x0, size 0x3C
+            u8 _0[4];            // offset 0x3C, size 0x4
+            u8 tevKColor[4][10]; // offset 0x40, size 0x28
+            u8 _1[24];           // offset 0x68, size 0x18
+        } dl;
+        u8 data[128];
+    };
+};
+
+struct ResMatTevColor : public ResCommon<ResTevColorDL> {
+    ResMatTevColor(void *vptr) : ResCommon<ResTevColorDL>(vptr) {}
+    void DCStore(bool sync);
+    void CallDisplayList(bool bSync) const;
+    ResMatTevColor CopyTo(void *p) const;
+    void GXGetTevColor(GXTevRegID id, GXColor *color);
+    void GXSetTevColor(GXTevRegID id, GXColor color);
+    void GXSetTevKColor(GXTevKColorID id, GXColor color);
+};
+
+struct ResPixDL {
+    union {
+        struct {
+            unsigned char alphaCompare[5]; // offset 0x0, size 0x5
+            unsigned char zMode[5];        // offset 0x5, size 0x5
+            unsigned char blendMode[10];   // offset 0xA, size 0xA
+            unsigned char setDstAlpha[5];  // offset 0x14, size 0x5
+            unsigned char _[7];            // offset 0x19, size 0x7
+        } dl;                              // offset 0x0, size 0x20
+        unsigned char data[32];            // offset 0x0, size 0x20
+    };
+};
 
-    inline ResMatTexCoordGen(void *vptr) : mTexGen(vptr) {}
+struct ResMatPix : public ResCommon<ResPixDL> {
+    ResMatPix(void *vptr) : ResCommon(vptr) {}
+    void DCStore(bool sync);
+    void CallDisplayList(bool bSync) const;
+    ResMatPix CopyTo(void *p) const;
+
+    void GXSetBlendMode(GXBlendMode, GXBlendFactor, GXBlendFactor, GXLogicOp);
+};
+
+struct ResTexCoordGenDL {};
+
+struct ResMatTexCoordGen : public ResCommon<ResTexCoordGenDL> {
+    inline ResMatTexCoordGen(void *vptr) : ResCommon(vptr) {}
     bool IsValid() const {
-        return mTexGen.IsValid();
+        return ResCommon::IsValid();
     }
 
     bool GXGetTexCoordGen2(GXTexCoordID, GXTexGenType *, GXTexGenSrc *, u8 *, u32 *);
@@ -23,15 +65,18 @@ struct ResMatTexCoordGen {
     void DCStore(bool);
 };
 
-struct ResTexSrtData {};
-
-struct ResTexSrt {
-    ResCommon<ResTexSrtData> mTexSrt; // at 0x0
+struct ResTexSrtData {
+    union {
+        struct {
+            unsigned char texCoordGen[8][18]; // offset 0x0, size 0x90
+            unsigned char _[16];              // offset 0x90, size 0x10
+        } dl;                                 // offset 0x0, size 0xA0
+        unsigned char data[160];              // offset 0x0, size 0xA0
+    };
+};
 
-    inline ResTexSrt(void *vptr) : mTexSrt(vptr) {}
-    bool IsValid() const {
-        return mTexSrt.IsValid();
-    }
+struct ResTexSrt : public ResCommon<ResTexSrtData> {
+    inline ResTexSrt(void *vptr) : ResCommon(vptr) {}
 
     void SetMapMode(u32, u32, int, int);
 };
@@ -52,32 +97,71 @@ struct ResTexPlttInfo {
 };
 
 struct ResMatDLData {
-    char UNK_0x0[0xE0];
-    ResMatTexCoordGenData texCoordGenData; // at 0xE0
+    ResPixDL dlPix;                                      // offset 0x0, size 0x20
+    ResTevColorDL dlTevColor;                            // offset 0x20, size 0x80
+    /* ResIndMtxAndScaleDL */ u8 dlIndMtxAndScale[0x40]; // offset 0xA0, size 0x40
+    ResTexCoordGenDL dlTexCoordGen;                      // offset 0xE0, size 0xA0
+};
+
+struct ResGenModeData {
+    u8 nTexGens;         // offset 0x0, size 0x1
+    u8 nChans;           // offset 0x1, size 0x1
+    u8 nTevs;            // offset 0x2, size 0x1
+    u8 nInds;            // offset 0x3, size 0x1
+    GXCullMode cullMode; // offset 0x4, size 0x4
+};
+
+struct ResGenMode : public ResCommon<ResGenModeData> {
+    ResGenMode(void *vptr) : ResCommon(vptr) {}
+    void DCStore(bool sync);
+    ResGenMode CopyTo(void *p) const;
+    GXCullMode GXGetCullMode();
+    u8 GXGetNumIndStages() const;
+    u8 GXGetNumTevStages() const;
+    u8 GXGetNumChans() const;
+    u8 GXGetNumTexGens() const;
+    void GXSetCullMode(GXCullMode);
 };
 
 struct ResMatData {
-    char UNK_0x0[0x38];
-    u32 resMatDLOfs; // at 0x38
-    char UNK_0x3C[0x1A4 - 0x3C];
-    ResTexSrtData texSrtData; // at 0x1A4
+    u32 size;                                  // offset 0x0, size 0x4
+    s32 toResMdlData;                          // offset 0x4, size 0x4
+    s32 name;                                  // offset 0x8, size 0x4
+    u32 id;                                    // offset 0xC, size 0x4
+    u32 flag;                                  // offset 0x10, size 0x
+    ResGenModeData genMode;                    // offset 0x14, size 0x8
+    /* ResMatMiscData */ u8 misc[0xC];         // offset 0x1C, size 0xC
+    s32 toResTevData;                          // offset 0x28, size 0x4
+    u32 numResTexPlttInfo;                     // offset 0x2C, size 0x4
+    s32 toResTexPlttInfo;                      // offset 0x30, size 0x4
+    s32 toResMatFurData;                       // offset 0x34, size 0x4
+    u32 toResUserData;                         // offset 0x38, size 0x4
+    u32 toResMatDLData;                        // offset 0x3C, size 0x4
+    /* ResTexObjData */ u8 texObjData[0x104];  // offset 0x40, size 0x104
+    /* ResTlutObjData */ u8 tlutObjData[0x64]; // offset 0x144, size 0x64
+    ResTexSrtData texSrtData;                  // offset 0x1A8, size 0x248
+    /* ResChanData */ u8 chan[0x20];           // offset 0x3F0, size 0x28
 };
 
-struct ResMat {
-    ResCommon<ResMatData> mMat;
+struct ResMat : public ResCommon<ResMatData> {
+    ResMatTevColor GetResMatTevColor() {
+        return ResMatTevColor(&ofs_to_ptr<ResMatDLData>(ref().toResMatDLData)->dlTevColor);
+    }
 
-    inline ResMat(void *vptr) : mMat(vptr) {}
-    bool IsValid() const {
-        return mMat.IsValid();
+    ResMatPix GetResMatPix() {
+        return ResMatPix(&ofs_to_ptr<ResMatDLData>(ref().toResMatDLData)->dlPix);
+    }
+
+    ResGenMode GetResGenMode() {
+        return ResGenMode(&ref().genMode);
     }
 
     ResTexSrt GetResTexSrt() {
-        return ResTexSrt(&mMat.ref().texSrtData);
+        return ResTexSrt(&ref().texSrtData);
     }
 
     ResMatTexCoordGen GetResMatTexCoordGen() {
-        ResMatDLData *dlData = mMat.ofs_to_ptr<ResMatDLData>(mMat.ref().resMatDLOfs);
-        return ResMatTexCoordGen(&dlData->texCoordGenData);
+        return ResMatTexCoordGen(&ofs_to_ptr<ResMatDLData>(ref().toResMatDLData)->dlTexCoordGen);
     }
 
     bool Bind(ResFile);
diff --git a/include/nw4r/g3d/g3d_scnmdl.h b/include/nw4r/g3d/g3d_scnmdl.h
index 778d0d96..420b59c7 100644
--- a/include/nw4r/g3d/g3d_scnmdl.h
+++ b/include/nw4r/g3d/g3d_scnmdl.h
@@ -12,17 +12,19 @@ class ScnMdl : public ScnMdlSimple {
     class CopiedMatAccess {
     public:
         CopiedMatAccess(ScnMdl *, u32);
-        ResTexSrt GetResTexSrtEx();
+        ResTexSrt GetResTexSrtEx(bool);
+        ResMatPix GetResMatPix(bool);
+        ResMatTevColor GetResMatTevColor(bool);
+        ResGenMode GetResGenMode(bool);
 
     private:
         char UNK_0x0[0x2C];
     };
 
 public:
+    virtual bool SetAnmObj(AnmObj *p, AnmObjType type) override;
 
-    virtual bool SetAnmObj(AnmObj* p, AnmObjType type) override;
-
-    static ScnMdl *Construct(MEMAllocator*, unsigned long*, nw4r::g3d::ResMdl, u32 bufferOption, int);
+    static ScnMdl *Construct(MEMAllocator *, unsigned long *, nw4r::g3d::ResMdl, u32 bufferOption, int);
 
     static const G3dObj::TypeObj GetTypeObjStatic() {
         return TypeObj(TYPE_NAME);
diff --git a/include/nw4r/g3d/g3d_scnmdlsmpl.h b/include/nw4r/g3d/g3d_scnmdlsmpl.h
index 2b64af30..342845da 100644
--- a/include/nw4r/g3d/g3d_scnmdlsmpl.h
+++ b/include/nw4r/g3d/g3d_scnmdlsmpl.h
@@ -1,10 +1,10 @@
 #ifndef NW4R_G3D_SCN_MDL_SIMPLE_H
 #define NW4R_G3D_SCN_MDL_SIMPLE_H
 #include "common.h"
-#include "nw4r/math/math_types.h"
+#include "nw4r/g3d/g3d_calcworld.h"
 #include "nw4r/g3d/g3d_resmdl.h"
 #include "nw4r/g3d/g3d_scnobj.h"
-#include "nw4r/g3d/g3d_calcworld.h"
+#include "nw4r/math/math_types.h"
 
 namespace nw4r {
 namespace g3d {
@@ -18,21 +18,22 @@ class ScnMdlSimple : public ScnLeaf {
     };
 
     enum AnmObjType {
-        ANMOBJ_0,
-        ANMOBJ_1,
-        ANMOBJ_2,
-        ANMOBJ_3,
-        ANMOBJ_4,
-        ANMOBJ_5,
-        ANMOBJ_6,
+        ANMOBJTYPE_CHR = 0,
+        ANMOBJTYPE_VIS = 1,
+        ANMOBJTYPE_MATCLR = 2,
+        ANMOBJTYPE_TEXPAT = 3,
+        ANMOBJTYPE_TEXSRT = 4,
+        ANMOBJTYPE_SHP = 5,
+        ANMOBJTYPE_NOT_SPECIFIED = 6,
+        ANMOBJTYPE_VTX = 5,
     };
 
 public:
     ScnMdlSimple(MEMAllocator *, ResMdl, math::MTX34 *, u32 *, math::MTX34 *, math::MTX33 *, math::MTX34 *, int, int);
 
-    static ScnMdlSimple *Construct(MEMAllocator*, unsigned long*, nw4r::g3d::ResMdl, int);
+    static ScnMdlSimple *Construct(MEMAllocator *, unsigned long *, nw4r::g3d::ResMdl, int);
 
-    bool GetScnMtxPos(math::MTX34* pOut, ScnObjMtxType tp, u32 nodeID) const;
+    bool GetScnMtxPos(math::MTX34 *pOut, ScnObjMtxType tp, u32 nodeID) const;
 
     virtual bool IsDerivedFrom(TypeObj other) const // at 0x8
     {
@@ -52,9 +53,11 @@ class ScnMdlSimple : public ScnLeaf {
         return GetTypeObj().GetTypeName();
     }
 
-    virtual bool SetAnmObj(AnmObj* p, AnmObjType type);
+    virtual bool SetAnmObj(AnmObj *p, AnmObjType type);
     virtual bool RemoveAnmObj(AnmObj *p);
     virtual bool RemoveAnmObj(AnmObjType type);
+    virtual AnmObj *GetAnmObj(AnmObjType type);
+    virtual AnmObj *GetAnmObj(AnmObjType type) const;
 
     const u8 *GetByteCode(ByteCodeType) const;
     void EnableScnMdlCallbackTiming(Timing);
diff --git a/src/m/m3d/m3d.cpp b/src/m/m3d/m3d.cpp
index 4f365bb0..6adf4075 100644
--- a/src/m/m3d/m3d.cpp
+++ b/src/m/m3d/m3d.cpp
@@ -1,5 +1,7 @@
 #include <m/m3d/m3d.h>
 #include <nw4r/g3d/g3d_init.h>
+#include <nw4r/g3d/g3d_resmat.h>
+#include <nw4r/g3d/g3d_resmdl.h>
 #include <nw4r/g3d/g3d_state.h>
 #include <rvl/GX.h>
 
@@ -89,6 +91,26 @@ void reset() {
     // TODO EGG
 }
 
+int getMatID(nw4r::g3d::ResMdl mdl, const char *name) {
+    if (mdl.IsValid()) {
+        nw4r::g3d::ResMat mat = mdl.GetResMat(name);
+        if (mat.IsValid()) {
+            return mat.ref().id;
+        }
+    }
+    return -1;
+}
+
+int getNodeID(nw4r::g3d::ResMdl mdl, const char *name) {
+    if (mdl.IsValid()) {
+        nw4r::g3d::ResNode node = mdl.GetResNode(name);
+        if (node.IsValid()) {
+            return node.GetID();
+        }
+    }
+    return -1;
+}
+
 void resetMaterial() {
     GXSetNumIndStages(0);
     for (int i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
diff --git a/src/m/m3d/m_anmchr.cpp b/src/m/m3d/m_anmchr.cpp
index 5eb519b6..985ef9d6 100644
--- a/src/m/m3d/m_anmchr.cpp
+++ b/src/m/m3d/m_anmchr.cpp
@@ -1,17 +1,16 @@
-#include <m/m3d/m_anmchr.h>
 #include <m/m3d/m3d.h>
+#include <m/m3d/m_anmchr.h>
 #include <nw4r/g3d/g3d_anmchr.h>
 #include <nw4r/g3d/g3d_resanmchr.h>
 
 namespace m3d {
 
 anmChr_c::~anmChr_c() {}
-
 int anmChr_c::getType() const {
-    return 0;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_CHR;
 }
 
-bool anmChr_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmChr anm, mAllocator_c* alloc, u32* pSize) {
+bool anmChr_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmChr anm, mAllocator_c *alloc, u32 *pSize) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -70,9 +69,7 @@ void anmChr_c::setAnmAfter(bmdl_c &mdl, nw4r::g3d::ResAnmChr anm, playMode_e mod
         nw4r::g3d::AnmObjChrRes *o = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjChrRes>(mpAnmObj);
         node->SetWeight(idx, weight);
         node->Attach(idx, o);
-
     }
-
 }
 
 void anmChr_c::setFrmCtrlDefault(nw4r::g3d::ResAnmChr &anm, m3d::playMode_e mode) {
diff --git a/src/m/m3d/m_anmchrblend.cpp b/src/m/m3d/m_anmchrblend.cpp
index afbdd2ce..2c7d67ef 100644
--- a/src/m/m3d/m_anmchrblend.cpp
+++ b/src/m/m3d/m_anmchrblend.cpp
@@ -8,7 +8,7 @@ namespace m3d {
 anmChrBlend_c::~anmChrBlend_c() {}
 
 int anmChrBlend_c::getType() const {
-    return 0;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_CHR;
 }
 
 bool anmChrBlend_c::create(nw4r::g3d::ResMdl mdl, int num, mAllocator_c *alloc, u32 *pSize) {
diff --git a/src/m/m3d/m_anmmatclr.cpp b/src/m/m3d/m_anmmatclr.cpp
index 3b0d2706..35ecd8f3 100644
--- a/src/m/m3d/m_anmmatclr.cpp
+++ b/src/m/m3d/m_anmmatclr.cpp
@@ -8,7 +8,7 @@ namespace m3d {
 anmMatClr_c::child_c::~child_c() {}
 
 int anmMatClr_c::child_c::getType() const {
-    return 0x02;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_MATCLR;
 }
 
 u32 anmMatClr_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmClr clr, bool b) {
@@ -125,7 +125,7 @@ anmMatClr_c::~anmMatClr_c() {
 }
 
 int anmMatClr_c::getType() const {
-    return 0x02;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_MATCLR;
 }
 
 void anmMatClr_c::remove() {
diff --git a/src/m/m3d/m_anmshp.cpp b/src/m/m3d/m_anmshp.cpp
index 6606b043..18ac5f48 100644
--- a/src/m/m3d/m_anmshp.cpp
+++ b/src/m/m3d/m_anmshp.cpp
@@ -7,7 +7,7 @@ namespace m3d {
 anmShp_c::~anmShp_c() {}
 
 int anmShp_c::getType() const {
-    return 0x5;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_SHP;
 }
 
 bool anmShp_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmShp anm, mAllocator_c *alloc, u32 *pSize) {
diff --git a/src/m/m3d/m_anmtexpat.cpp b/src/m/m3d/m_anmtexpat.cpp
index 2b790e90..60c96097 100644
--- a/src/m/m3d/m_anmtexpat.cpp
+++ b/src/m/m3d/m_anmtexpat.cpp
@@ -6,7 +6,7 @@
 namespace m3d {
 
 int anmTexPat_c::child_c::getType() const {
-    return 0x03;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_TEXPAT;
 }
 
 anmTexPat_c::child_c::~child_c() {}
@@ -20,7 +20,7 @@ u32 anmTexPat_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPa
     return size;
 }
 
-bool anmTexPat_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c* alloc, u32* pSize) {
+bool anmTexPat_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c *alloc, u32 *pSize) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -80,7 +80,7 @@ u32 anmTexPat_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, s3
     return size;
 }
 
-bool anmTexPat_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c* alloc, u32* pSize, s32 num) {
+bool anmTexPat_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAllocator_c *alloc, u32 *pSize, s32 num) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -98,7 +98,7 @@ bool anmTexPat_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexPat pat, mAl
     mpAnmObj = nw4r::g3d::AnmObjTexPatOverride::Construct(&mAllocator, nullptr, mdl, num);
 
     // TODO inline?
-    mpChildren = (child_c*)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
+    mpChildren = (child_c *)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
 
     nw4r::g3d::AnmObjTexPatOverride *obj = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::AnmObjTexPatOverride>(mpAnmObj);
     child_c *child = mpChildren;
@@ -125,7 +125,7 @@ anmTexPat_c::~anmTexPat_c() {
 }
 
 int anmTexPat_c::getType() const {
-    return 0x03;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_TEXPAT;
 }
 
 void anmTexPat_c::remove() {
diff --git a/src/m/m3d/m_anmtexsrt.cpp b/src/m/m3d/m_anmtexsrt.cpp
index e712e958..c45d8be0 100644
--- a/src/m/m3d/m_anmtexsrt.cpp
+++ b/src/m/m3d/m_anmtexsrt.cpp
@@ -6,7 +6,7 @@
 namespace m3d {
 
 int anmTexSrt_c::child_c::getType() const {
-    return 0x04;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_TEXSRT;
 }
 
 anmTexSrt_c::child_c::~child_c() {}
@@ -20,7 +20,7 @@ u32 anmTexSrt_c::child_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSr
     return size;
 }
 
-bool anmTexSrt_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c* alloc, u32* pSize) {
+bool anmTexSrt_c::child_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c *alloc, u32 *pSize) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -83,7 +83,7 @@ u32 anmTexSrt_c::heapCost(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, s3
     return size;
 }
 
-bool anmTexSrt_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c* alloc, u32* pSize, s32 num) {
+bool anmTexSrt_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAllocator_c *alloc, u32 *pSize, s32 num) {
     if (alloc == nullptr) {
         alloc = internal::l_allocator_p;
     }
@@ -104,7 +104,7 @@ bool anmTexSrt_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmTexSrt srt, mAl
     }
 
     // TODO inline?
-    mpChildren = (child_c*)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
+    mpChildren = (child_c *)MEMAllocFromAllocator(&mAllocator, ROUND_UP(num * sizeof(child_c), 0x20));
     if (!mpChildren) {
         remove();
         return false;
@@ -136,7 +136,7 @@ anmTexSrt_c::~anmTexSrt_c() {
 }
 
 int anmTexSrt_c::getType() const {
-    return 0x04;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_TEXSRT;
 }
 
 void anmTexSrt_c::remove() {
diff --git a/src/m/m3d/m_anmvis.cpp b/src/m/m3d/m_anmvis.cpp
index 2d3f8ad2..68d4bd21 100644
--- a/src/m/m3d/m_anmvis.cpp
+++ b/src/m/m3d/m_anmvis.cpp
@@ -4,13 +4,12 @@
 #include <nw4r/g3d/g3d_resanmvis.h>
 #include <nw4r/g3d/g3d_resmdl.h>
 
-
 namespace m3d {
 
 anmVis_c::~anmVis_c() {}
 
 int anmVis_c::getType() const {
-    return 1;
+    return nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_VIS;
 }
 
 bool anmVis_c::create(nw4r::g3d::ResMdl mdl, nw4r::g3d::ResAnmVis anm, mAllocator_c *alloc, u32 *pSize) {
diff --git a/src/m/m3d/m_bmdl.cpp b/src/m/m3d/m_bmdl.cpp
index 2f6967eb..c565fb5b 100644
--- a/src/m/m3d/m_bmdl.cpp
+++ b/src/m/m3d/m_bmdl.cpp
@@ -1,3 +1,4 @@
+#include <m/m3d/m3d.h>
 #include <m/m3d/m_bmdl.h>
 #include <nw4r/g3d/g3d_scnmdl.h>
 
@@ -8,25 +9,55 @@ bmdl_c::~bmdl_c() {
 }
 
 int bmdl_c::getType() const {
-    return 0;
+    return SCN_LEAF_MODEL;
 }
 
-void bmdl_c::getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const {
-    nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf)->GetScnMtxPos(out,
+int bmdl_c::getMatID(const char *name) const {
+    return m3d::getMatID(getResMdl(), name);
+}
+
+int bmdl_c::getNodeID(const char *name) const {
+    return m3d::getNodeID(getResMdl(), name);
+}
+
+bool bmdl_c::getNodeWorldMtx(u32 p1, nw4r::math::MTX34 *out) const {
+    return nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf)->GetScnMtxPos(out,
             nw4r::g3d::ScnObj::MTX_TYPE_WORLD, p1);
 }
 
+bool bmdl_c::getNodeWorldMtxMultVecZero(u32 p1, nw4r::math::VEC3 &out) const {
+    nw4r::math::MTX34 mtx;
+    if (!getNodeWorldMtx(p1, &mtx)) {
+        return false;
+    } else {
+        out.x = mtx[0][3];
+        out.y = mtx[1][3];
+        out.z = mtx[2][3];
+        return true;
+    }
+}
+
+bool bmdl_c::getNodeWorldMtxMultVec(u32 p1, nw4r::math::VEC3 &in, nw4r::math::VEC3 &out) const {
+    nw4r::math::MTX34 mtx;
+    if (!getNodeWorldMtx(p1, &mtx)) {
+        return false;
+    } else {
+        PSMTXMultVec(mtx, in, out);
+        return true;
+    }
+}
+
 void bmdl_c::setAnm(banm_c &anm) {
     nw4r::g3d::ScnMdlSimple *mdl;
-    if (anm.getType() == 5) {
+    if (anm.getType() == nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_SHP) {
         mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
-        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJ_6);
+        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_NOT_SPECIFIED);
     } else {
         mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
-        if (anm.getType() == 0) {
+        if (anm.getType() == nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_CHR) {
             mpCurrentAnm = &anm;
         }
-        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJ_6);
+        mdl->SetAnmObj(anm.getAnimObj(), nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_NOT_SPECIFIED);
     }
 }
 
@@ -46,14 +77,149 @@ nw4r::g3d::ResMat bmdl_c::getResMat(u32 index) const {
 }
 
 void bmdl_c::removeAnm(nw4r::g3d::ScnMdlSimple::AnmObjType type) {
-    // TODO regswap
     nw4r::g3d::ScnMdlSimple *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
-    if (type == nw4r::g3d::ScnMdlSimple::ANMOBJ_0) {
+    if (type == nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_CHR) {
         mpCurrentAnm = nullptr;
     }
     mdl->RemoveAnmObj(type);
 }
 
+nw4r::g3d::AnmObj *bmdl_c::getAnmObj(nw4r::g3d::ScnMdlSimple::AnmObjType type) const {
+    nw4r::g3d::ScnMdlSimple *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdlSimple>(mpScnLeaf);
+    return mdl->GetAnmObj(type);
+}
+
+void bmdl_c::setTevColor(u32 idx, GXTevRegID tevId, GXColor color, bool bMarkDirty) {
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+
+    if (mdl) {
+        nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, idx);
+        nw4r::g3d::ResMatTevColor resMatTevClr = matAccess.GetResMatTevColor(bMarkDirty);
+        resMatTevClr.GXSetTevColor(tevId, color);
+        resMatTevClr.DCStore(false);
+    } else {
+        nw4r::g3d::ResMatTevColor resMatTevClr = getResMat(idx).GetResMatTevColor();
+        resMatTevClr.GXSetTevColor(tevId, color);
+        resMatTevClr.DCStore(false);
+    }
+}
+
+void bmdl_c::setTevColorAll(GXTevRegID tevId, GXColor color, bool bMarkDirty) {
+    nw4r::g3d::ResMdl resMdl = getResMdl();
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+    if (mdl) {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, i);
+            nw4r::g3d::ResMatTevColor resMatTevClr = matAccess.GetResMatTevColor(bMarkDirty);
+            resMatTevClr.GXSetTevColor(tevId, color);
+            resMatTevClr.DCStore(false);
+        }
+    } else {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ResMatTevColor resMatTevClr = resMdl.GetResMat(i).GetResMatTevColor();
+            resMatTevClr.GXSetTevColor(tevId, color);
+            resMatTevClr.DCStore(false);
+        }
+    }
+}
+
+void bmdl_c::setTevKColor(u32 idx, GXTevKColorID tevKId, GXColor color, bool bMarkDirty) {
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+
+    if (mdl) {
+        nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, idx);
+        nw4r::g3d::ResMatTevColor resMatTevClr = matAccess.GetResMatTevColor(bMarkDirty);
+        resMatTevClr.GXSetTevKColor(tevKId, color);
+        resMatTevClr.DCStore(false);
+    } else {
+        nw4r::g3d::ResMatTevColor resMatTevClr = getResMat(idx).GetResMatTevColor();
+        resMatTevClr.GXSetTevKColor(tevKId, color);
+        resMatTevClr.DCStore(false);
+    }
+}
+
+void bmdl_c::setTevKColorAll(GXTevKColorID tevKId, GXColor color, bool bMarkDirty) {
+    nw4r::g3d::ResMdl resMdl = getResMdl();
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+    if (mdl) {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, i);
+            nw4r::g3d::ResMatTevColor resMatTevClr = matAccess.GetResMatTevColor(bMarkDirty);
+            resMatTevClr.GXSetTevKColor(tevKId, color);
+            resMatTevClr.DCStore(false);
+        }
+    } else {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ResMatTevColor resMatTevClr = resMdl.GetResMat(i).GetResMatTevColor();
+            resMatTevClr.GXSetTevKColor(tevKId, color);
+            resMatTevClr.DCStore(false);
+        }
+    }
+}
+
+void bmdl_c::setBlendModeAll(GXBlendMode blendMode, GXBlendFactor srcFactor, GXBlendFactor dstFactor, GXLogicOp op,
+        bool bMarkDirty) {
+    nw4r::g3d::ResMdl resMdl = getResMdl();
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+    if (mdl) {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, i);
+            nw4r::g3d::ResMatPix resMatPix = matAccess.GetResMatPix(bMarkDirty);
+            resMatPix.GXSetBlendMode(blendMode, srcFactor, dstFactor, op);
+            resMatPix.DCStore(false);
+        }
+    } else {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ResMatPix resMatPix = resMdl.GetResMat(i).GetResMatPix();
+            resMatPix.GXSetBlendMode(blendMode, srcFactor, dstFactor, op);
+            resMatPix.DCStore(false);
+        }
+    }
+}
+
+void bmdl_c::setCullMode(u32 matId, GXCullMode cullMode, bool bMarkDirty) {
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+    if (mdl) {
+        nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, matId);
+        nw4r::g3d::ResGenMode resGenMode = matAccess.GetResGenMode(bMarkDirty);
+        resGenMode.GXSetCullMode(cullMode);
+    } else {
+        nw4r::g3d::ResGenMode resGenMode = getResMat(matId).GetResGenMode();
+        resGenMode.GXSetCullMode(cullMode);
+    }
+}
+
+void bmdl_c::setCullModeAll(GXCullMode cullMode, bool bMarkDirty) {
+    // TODO Extra stack allocation?
+    nw4r::g3d::ResMdl resMdl = getResMdl();
+    nw4r::g3d::ScnMdl *mdl = nw4r::g3d::G3dObj::DynamicCast<nw4r::g3d::ScnMdl>(mpScnLeaf);
+    if (mdl) {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ScnMdl::CopiedMatAccess matAccess(mdl, i);
+            nw4r::g3d::ResGenMode resGenMode = matAccess.GetResGenMode(bMarkDirty);
+            resGenMode.GXSetCullMode(cullMode);
+        }
+    } else {
+        for (u32 i = 0; i < resMdl.GetResMatNumEntries(); i++) {
+            nw4r::g3d::ResMat resMat = getResMat(i);
+            nw4r::g3d::ResGenMode resGenMode = resMat.GetResGenMode();
+            resGenMode.GXSetCullMode(cullMode);
+        }
+    }
+}
+
+void bmdl_c::setMatVisible(u32 matId, bool bVisble) {
+    // TODO
+}
+
+int bmdl_c::setMatTexture(char *name, GXTexObj *texObj, bool copy, void *unk, int, int) {
+    // TODO
+}
+
+bool bmdl_c::getBounds(mVec3_c *min, mVec3_c *max) {
+    // TODO
+}
+
 void bmdl_c::remove() {
     mpCurrentAnm = nullptr;
     scnLeaf_c::remove();
diff --git a/src/m/m3d/m_mdl.cpp b/src/m/m3d/m_mdl.cpp
index 954cb4ff..a6879840 100644
--- a/src/m/m3d/m_mdl.cpp
+++ b/src/m/m3d/m_mdl.cpp
@@ -226,7 +226,7 @@ void mdl_c::play() {
 }
 
 void mdl_c::setAnm(m3d::banm_c &anm, f32 f) {
-    if (anm.getType() == 0) {
+    if (anm.getType() == nw4r::g3d::ScnMdlSimple::ANMOBJTYPE_CHR) {
         mpCallback->setBlendFrame(f);
     }
     bmdl_c::setAnm(anm);
diff --git a/src/m/m3d/m_proc.cpp b/src/m/m3d/m_proc.cpp
index 394cacfd..85ab31ec 100644
--- a/src/m/m3d/m_proc.cpp
+++ b/src/m/m3d/m_proc.cpp
@@ -16,7 +16,7 @@ void proc_c_drawProc(nw4r::g3d::ScnProc *proc, bool b) {
 proc_c::~proc_c() {}
 
 int proc_c::getType() const {
-    return 0x2;
+    return SCN_LEAF_PROC;
 }
 
 void proc_c::drawOpa() {}
diff --git a/src/m/m3d/m_scnLeaf.cpp b/src/m/m3d/m_scnLeaf.cpp
index 50873e8a..43f83c7c 100644
--- a/src/m/m3d/m_scnLeaf.cpp
+++ b/src/m/m3d/m_scnLeaf.cpp
@@ -1,4 +1,4 @@
-#include <m//m3d/m3d.h>
+#include <m/m3d/m3d.h>
 #include <m/m3d/m_scnleaf.h>
 
 namespace m3d {
diff --git a/src/m/m3d/m_smdl.cpp b/src/m/m3d/m_smdl.cpp
index 50460ef0..e5d96425 100644
--- a/src/m/m3d/m_smdl.cpp
+++ b/src/m/m3d/m_smdl.cpp
@@ -3,7 +3,6 @@
 #include <nw4r/g3d/g3d_scnmdl.h>
 #include <nw4r/g3d/g3d_scnmdlsmpl.h>
 
-
 namespace m3d {
 
 smdl_c::smdl_c() {}

From 51fe00e8122bd2d3a054b2d8c51a4012a23bfa00 Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Mon, 9 Sep 2024 00:14:38 -0400
Subject: [PATCH 28/33] fix extern

---
 src/REL/d/a/obj/d_a_obj_mole_soil.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
index 8a6cc1c3..92d688fc 100644
--- a/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
+++ b/src/REL/d/a/obj/d_a_obj_mole_soil.cpp
@@ -1,4 +1,5 @@
 #include <d/a/obj/d_a_obj_mole_soil.h>
+#include <s/s_Math.h>
 
 SPECIAL_ACTOR_PROFILE(OBJ_MOLE_SOIL, dAcOmoleSoil_c, fProfile::OBJ_MOLE_SOIL, 0x008B, 0, 6);
 
@@ -73,9 +74,6 @@ int dAcOmoleSoil_c::draw() {
     return 1;
 }
 
-// sLib::chase
-extern "C" bool fn_802DECC0(f32 *, f32, f32);
-
 void dAcOmoleSoil_c::initializeState_Wait() {
     mDesiredNextState = 0;
 }
@@ -87,7 +85,7 @@ void dAcOmoleSoil_c::initializeState_Appear() {
 }
 void dAcOmoleSoil_c::executeState_Appear() {
     f32 stepSize = 0.05f;
-    if (fn_802DECC0(&mScale, sFullScale, stepSize)) {
+    if (sLib::chase(&mScale, sFullScale, stepSize)) {
         mStateMgr.changeState(StateID_Wait);
     } else {
         mModel.setScale(mScale, mScale, mScale);
@@ -109,7 +107,7 @@ void dAcOmoleSoil_c::executeState_DisAppear() {
     // all the stack operations for the cast.
     int scalar = 0;
     f32 stepSize = 0.01f * scalar + 0.05f;
-    if (fn_802DECC0(&mScale, sHalfScale, stepSize)) {
+    if (sLib::chase(&mScale, sHalfScale, stepSize)) {
         mStateMgr.changeState(StateID_Wait);
     } else {
         mModel.setScale(mScale, mScale, mScale);

From ed5fe5c318c1005c83348d972f0a5ec3507ca0e3 Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Mon, 9 Sep 2024 14:07:14 -0400
Subject: [PATCH 29/33] fix CopiedMatAccess size

---
 include/nw4r/g3d/g3d_scnmdl.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/nw4r/g3d/g3d_scnmdl.h b/include/nw4r/g3d/g3d_scnmdl.h
index 420b59c7..d453a01e 100644
--- a/include/nw4r/g3d/g3d_scnmdl.h
+++ b/include/nw4r/g3d/g3d_scnmdl.h
@@ -18,7 +18,7 @@ class ScnMdl : public ScnMdlSimple {
         ResGenMode GetResGenMode(bool);
 
     private:
-        char UNK_0x0[0x2C];
+        char UNK_0x0[0x34];
     };
 
 public:

From 37da66e35552c46de1164d162c190fd8b6fdf7e7 Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Wed, 11 Sep 2024 12:17:26 -0400
Subject: [PATCH 30/33] Added m3d bline_c

---
 config/SOUE01/splits.txt   |   5 ++
 config/SOUE01/symbols.txt  |  38 +++++-----
 configure.py               |   1 +
 include/egg/core/eggHeap.h |   4 +-
 include/m/m3d/m_bline.h    |  79 ++++++++++++++++++++
 include/m/m_allocator.h    |   4 +-
 src/m/m3d/m_bline.cpp      | 147 +++++++++++++++++++++++++++++++++++++
 src/m/m_allocator.cpp      |   2 +-
 8 files changed, 257 insertions(+), 23 deletions(-)
 create mode 100644 include/m/m3d/m_bline.h
 create mode 100644 src/m/m3d/m_bline.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 4e68e44f..9968be83 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -329,6 +329,11 @@ m/m3d/m_banm.cpp:
 	.text       start:0x802E7D50 end:0x802E7F94
 	.data       start:0x805425E0 end:0x80542628
 
+m/m3d/m_bline.cpp:
+	.text       start:0x802E7FA0 end:0x802E926C
+	.data       start:0x80542628 end:0x80542664
+	.sdata2     start:0x8057CD28 end:0x8057CD48
+
 m/m3d/m_bmdl.cpp:
 	.text       start:0x802E9270 end:0x802EA6EC
 	.data       start:0x80542668 end:0x80542694
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index dd669eaa..fa22ca7d 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17553,23 +17553,23 @@ getFrame__Q23m3d6banm_cCFv = .text:0x802E7F20; // type:function size:0x14
 setFrameOnly__Q23m3d6banm_cFf = .text:0x802E7F40; // type:function size:0x14
 getRate__Q23m3d6banm_cCFv = .text:0x802E7F60; // type:function size:0x14
 setRate__Q23m3d6banm_cFf = .text:0x802E7F80; // type:function size:0x14
-ImageLineGroup_dtor = .text:0x802E7FA0; // type:function size:0x88
-ImageLineGroup_init = .text:0x802E8030; // type:function size:0x148
-ImageLine_Ct = .text:0x802E8180; // type:function size:0x30
-ImageLineGroup_remove = .text:0x802E81B0; // type:function size:0x54
-ImageLineGroup_BuildVtx = .text:0x802E8210; // type:function size:0xA8
-ImageLineGroup_GetImageLine = .text:0x802E82C0; // type:function size:0x8
-ImageLineGroup_SetupGX = .text:0x802E82D0; // type:function size:0x3B8
-ImageLineGroup_DrawOpaque = .text:0x802E8690; // type:function size:0x98
-ImageLineGroup_DrawTransparent = .text:0x802E8730; // type:function size:0x98
-ImageLine_dtor = .text:0x802E87D0; // type:function size:0x60
-ImageLine_Init = .text:0x802E8830; // type:function size:0x1A0
-ImageLine_VtxPosCt = .text:0x802E89D0; // type:function size:0x4
-ImageLine_VtxPosDt = .text:0x802E89E0; // type:function size:0x40
+__dt__Q23m3d10blineMat_cFv = .text:0x802E7FA0; // type:function size:0x88
+create__Q23m3d10blineMat_cFP12mAllocator_ciUsffR6mColorPvb = .text:0x802E8030; // type:function size:0x148
+__ct__Q23m3d7bline_cFv = .text:0x802E8180; // type:function size:0x30
+remove__Q23m3d10blineMat_cFv = .text:0x802E81B0; // type:function size:0x54
+update__Q23m3d10blineMat_cFv = .text:0x802E8210; // type:function size:0xA8
+getLine__Q23m3d10blineMat_cFUs = .text:0x802E82C0; // type:function size:0x8
+setupGX__Q23m3d10blineMat_cFb = .text:0x802E82D0; // type:function size:0x3B8
+drawOpa__Q23m3d10blineMat_cFv = .text:0x802E8690; // type:function size:0x98
+drawXlu__Q23m3d10blineMat_cFv = .text:0x802E8730; // type:function size:0x98
+__dt__Q23m3d7bline_cFv = .text:0x802E87D0; // type:function size:0x60
+create__Q23m3d7bline_cFPQ23EGG4HeapUsffR6mColor = .text:0x802E8830; // type:function size:0x1A0
+__ct__Q33m3d7bline_c6VtxPosFv = .text:0x802E89D0; // type:function size:0x4
+__dt__Q33m3d7bline_c6VtxPosFv = .text:0x802E89E0; // type:function size:0x40
 ImageLine_VtxNrmCt = .text:0x802E8A20; // type:function size:0x4
-ImageLine_remove = .text:0x802E8A30; // type:function size:0xA4
-ImageLine_BuildVtx = .text:0x802E8AE0; // type:function size:0x66C
-ImageLine_Draw = .text:0x802E9150; // type:function size:0x11C
+remove__Q23m3d7bline_cFv = .text:0x802E8A30; // type:function size:0xA4
+update__Q23m3d7bline_cFP7mVec3_c = .text:0x802E8AE0; // type:function size:0x66C
+draw__Q23m3d7bline_cFv = .text:0x802E9150; // type:function size:0x11C
 __dt__Q23m3d6bmdl_cFv = .text:0x802E9270; // type:function size:0x7C
 getType__Q23m3d6bmdl_cCFv = .text:0x802E92F0; // type:function size:0x8
 getMatID__Q23m3d6bmdl_cCFPCc = .text:0x802E9300; // type:function size:0x3C
@@ -17710,7 +17710,7 @@ adjustFrmHeap__16mHeapAllocator_cFv = .text:0x802EE430; // type:function size:0x
 adjustExpHeap__16mHeapAllocator_cFv = .text:0x802EE4A0; // type:function size:0x70
 createNewTempFrmHeap__16mHeapAllocator_cFlPQ23EGG4HeapPclUl = .text:0x802EE510; // type:function size:0x4C
 adjustFrmHeapRestoreCurrent__16mHeapAllocator_cFv = .text:0x802EE560; // type:function size:0x64
-allocOnHeap__16mHeapAllocator_cFUlP16mHeapAllocator_c = .text:0x802EE5D0; // type:function size:0x1C
+__nwa__FUlP12mAllocator_c = .text:0x802EE5D0; // type:function size:0x1C
 step__4mAngFslss = .text:0x802EE5F0; // type:function size:0xA4
 __sinit_\m_angle_cpp = .text:0x802EE6A0; // type:function size:0x18 scope:local
 fn_802EE6C0 = .text:0x802EE6C0; // type:function size:0x114
@@ -35699,8 +35699,8 @@ __vt__Q33m3d11anmTexSrt_c7child_c = .data:0x805425B0; // type:object size:0x18
 __vt__Q23m3d8anmVis_c = .data:0x805425C8; // type:object size:0x18
 lbl_805425E0 = .data:0x805425E0; // type:object size:0x30
 __vt__Q23m3d6banm_c = .data:0x80542610; // type:object size:0x18
-lbl_80542628 = .data:0x80542628; // type:object size:0x30
-lbl_80542658 = .data:0x80542658; // type:object size:0x10
+__vt__Q23m3d10blineMat_c = .data:0x80542628; // type:object size:0x30
+__vt__Q23m3d7bline_c = .data:0x80542658; // type:object size:0xC
 __vt__Q23m3d6bmdl_c = .data:0x80542668; // type:object size:0x2C
 __vt__Q23m3d11calcRatio_c = .data:0x80542698; // type:object size:0xC
 __vt__Q23m3d6fanm_c = .data:0x805426A8; // type:object size:0x18
diff --git a/configure.py b/configure.py
index a467a19b..27459f7b 100644
--- a/configure.py
+++ b/configure.py
@@ -357,6 +357,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
             Object(Matching, "m/m3d/m_anmmatclr.cpp"),
             Object(Matching, "m/m3d/m_anmvis.cpp"),
             Object(Matching, "m/m3d/m_banm.cpp"),
+            Object(NonMatching, "m/m3d/m_bline.cpp"),
             Object(NonMatching, "m/m3d/m_bmdl.cpp"),
             Object(Matching, "m/m3d/m_calc_ratio.cpp"),
             Object(Matching, "m/m3d/m_fanm.cpp"),
diff --git a/include/egg/core/eggHeap.h b/include/egg/core/eggHeap.h
index 548db726..e0730703 100644
--- a/include/egg/core/eggHeap.h
+++ b/include/egg/core/eggHeap.h
@@ -162,9 +162,9 @@ class Heap : public Disposer {
 } // namespace EGG
 
 /* 80495a60 */ void *operator new(size_t, void *p);
-/* 80495a70 */ void *operator new(size_t size, EGG::Heap *heap, int align);
+/* 80495a70 */ void *operator new(size_t size, EGG::Heap *heap, int align = 4);
 /* 80495a80 */ void *operator new(size_t size, EGG::Allocator *alloc);
 /* 80495a90 */ void *operator new[](size_t size, int align);
-/* 80495aa0 */ void *operator new[](size_t size, EGG::Heap *heap, int align);
+/* 80495aa0 */ void *operator new[](size_t size, EGG::Heap *heap, int align = 4);
 
 #endif
diff --git a/include/m/m3d/m_bline.h b/include/m/m3d/m_bline.h
new file mode 100644
index 00000000..f0ae1360
--- /dev/null
+++ b/include/m/m3d/m_bline.h
@@ -0,0 +1,79 @@
+#ifndef M3D_BLINE_H
+#define M3D_BLINE_H
+
+#include <m/m3d/m_proc.h>
+#include <m/m_color.h>
+#include <m/m_math.h>
+
+namespace m3d {
+
+// The Actual line
+class bline_c {
+public:
+    bline_c() : mpPathArr(0), mpVtxPosArr(0), mpVtxNrmArr(0), mpVtxTexArr(0), mFlags(0) {}
+    // This is mainly a Guess, When the array is created, it has both a ctor/dtor
+    struct VtxPos {
+        EGG::Vector3f vec[2];
+        VtxPos() {}
+        ~VtxPos() {}
+    };
+    // This is mainly a Guess, When the array is created, it has only a ctor
+    struct VtxNrm {
+        struct {
+            u8 x, y, z;
+        } nrm[2];
+        VtxNrm() {}
+    };
+    // This is mainly a Guess, When the array is created, it doesnt use the array alloc
+    struct VtxTex {
+        f32 tex;
+    };
+
+    /* 0x00 */ u8 field_0x00[0x8];
+
+    bool create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, mColor &color);
+    void update(mVec3_c *startPos);
+    void remove();
+    void draw();
+
+    // vt at 0x08
+    virtual ~bline_c();
+
+    /* 0x0C */ f32 mWidth; // could be a scale too
+    /* 0x10 */ mColor mColor;
+    /* 0x14 */ mVec3_c *mpPathArr;
+    /* 0x18 */ VtxPos *mpVtxPosArr;
+    /* 0x1C */ VtxNrm *mpVtxNrmArr;
+    /* 0x20 */ VtxTex *mpVtxTexArr;
+    /* 0x24 */ f32 mTexRepeat; // Higher value causes the texture to repeat more often
+    /* 0x28 */ u16 mPathNum;   // Guess
+    /* 0x2A */ u16 mVtxNum;    // Guess
+    /* 0x2C */ u16 field_0x2C;
+    /* 0x2E */ u8 mFlags;
+    /* 0x2F    u8 _pad; */
+};
+
+class blineMat_c : public proc_c {
+public:
+    virtual ~blineMat_c();
+    virtual void remove() override;
+    virtual void drawOpa() override;
+    virtual void drawXlu() override;
+    virtual void setupGX(bool bTransparent);
+
+    bool create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat, mColor &color,
+            void *pTex, bool);
+    void update();
+    bline_c *getLine(u16 idx);
+
+    /* 0x1C */ mAllocator_c mAllocator;
+    /* 0x34 */ void *mpTex;
+    /* 0x38 */ nw4r::ut::List mLines;
+    /* 0x44 */ bline_c *mpLineArr;
+    /* 0x48 */ short mLineArrNum;
+    /* 0x4A */ bool field_0x4A;
+};
+
+} // namespace m3d
+
+#endif
diff --git a/include/m/m_allocator.h b/include/m/m_allocator.h
index e4f2a175..4391ad0e 100644
--- a/include/m/m_allocator.h
+++ b/include/m/m_allocator.h
@@ -26,6 +26,8 @@ class mHeapAllocator_c : public mAllocator_c {
     s32 adjustExpHeap();
     bool createNewTempFrmHeap(s32 size, EGG::Heap *newHeap, char *heapName, s32 align, u32 unk);
     void adjustFrmHeapRestoreCurrent();
-    static void *allocOnHeap(u32 size, mHeapAllocator_c *allocator);
 };
+
+void *operator new[](size_t size, mAllocator_c *);
+
 #endif
diff --git a/src/m/m3d/m_bline.cpp b/src/m/m3d/m_bline.cpp
new file mode 100644
index 00000000..888314ba
--- /dev/null
+++ b/src/m/m3d/m_bline.cpp
@@ -0,0 +1,147 @@
+#include <egg/core/eggHeap.h>
+#include <m/m3d/m_bline.h>
+
+namespace m3d {
+
+blineMat_c::~blineMat_c() {
+    remove();
+}
+
+bool blineMat_c::create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat, mColor &color,
+        void *pTex, bool p9) {
+    if (!proc_c::create(pAllocator, nullptr)) {
+        return false;
+    }
+    nw4r::ut::List_Init(&mLines, 0);
+    if (numLines) {
+        mpLineArr = new (pAllocator) bline_c[numLines];
+
+        if (!mpLineArr) {
+            return false;
+        }
+        bline_c *pLineArr = mpLineArr;
+        EGG::Heap *pHeap = pAllocator->mHeap;
+        for (int i = 0; i < numLines; ++i) {
+            if (!pLineArr->create(pHeap, numLinePts, width, repeat, color)) {
+                remove();
+                return false;
+            }
+            nw4r::ut::List_Append(&mLines, pLineArr);
+            pLineArr++;
+        }
+    }
+
+    mLineArrNum = numLines;
+    mpTex = pTex;
+    field_0x4A = p9;
+
+    return true;
+}
+
+// void bline_c::bline_c() {}
+
+void blineMat_c::remove() {
+    if (mpLineArr) {
+        delete[] mpLineArr;
+        mpLineArr = nullptr;
+    }
+    scnLeaf_c::remove();
+}
+
+void blineMat_c::update() {}
+
+bline_c *blineMat_c::getLine(u16 idx) {
+    return (bline_c *)nw4r::ut::List_GetNth(&mLines, idx);
+}
+
+void blineMat_c::setupGX(bool bTransparent) {}
+
+void blineMat_c::drawOpa() {
+    update();
+    setupGX(false);
+    for (bline_c *line = (bline_c *)nw4r::ut::List_GetNext(&mLines, 0); line != nullptr;
+            line = (bline_c *)nw4r::ut::List_GetNext(&mLines, line)) {
+        if ((line->mFlags & 1) == 0 && line->mColor.a == 0xFF) {
+            line->draw();
+        }
+    }
+}
+
+void blineMat_c::drawXlu() {
+    update();
+    setupGX(true);
+    for (bline_c *line = (bline_c *)nw4r::ut::List_GetNext(&mLines, 0); line != nullptr;
+            line = (bline_c *)nw4r::ut::List_GetNext(&mLines, line)) {
+        if ((line->mFlags & 1) == 0 && line->mColor.a != 0xFF) {
+            line->draw();
+        }
+    }
+}
+
+bline_c::~bline_c() {
+    remove();
+}
+
+bool bline_c::create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, ::mColor &color) {
+    mpPathArr = new (pHeap) mVec3_c[numPts];
+    if (!mpPathArr) {
+        return false;
+    }
+
+    mpVtxPosArr = new (pHeap) VtxPos[numPts];
+    if (!mpVtxPosArr) {
+        remove();
+        return false;
+    }
+
+    mpVtxNrmArr = new (pHeap) VtxNrm[numPts];
+    if (!mpVtxNrmArr) {
+        remove();
+        return false;
+    }
+
+    mpVtxTexArr = new (pHeap) VtxTex[numPts];
+    if (!mpVtxTexArr) {
+        remove();
+        return false;
+    }
+
+    // Color being weird, dont really wanna try rn xD
+    mPathNum = numPts;
+    mVtxNum = numPts;
+    mWidth = width;
+    mColor = color;
+    mTexRepeat = repeat;
+    field_0x2C = 0;
+
+    return true;
+}
+
+// void bline_c::VtxPos::VtxPos() {}
+// void bline_c::VtxPos::~VtxPos() {}
+// void bline_c::VtxNrm::VtxNrm() {}
+
+void bline_c::remove() {
+    if (mpPathArr) {
+        delete[] mpPathArr;
+        mpPathArr = nullptr;
+    }
+    if (mpVtxPosArr) {
+        delete[] mpVtxPosArr;
+        mpVtxPosArr = nullptr;
+    }
+    if (mpVtxNrmArr) {
+        delete[] mpVtxNrmArr;
+        mpVtxNrmArr = nullptr;
+    }
+    if (mpVtxTexArr) {
+        delete[] mpVtxTexArr;
+        mpVtxTexArr = nullptr;
+    }
+}
+
+void bline_c::update(mVec3_c *startPos) {}
+
+void bline_c::draw() {}
+
+} // namespace m3d
diff --git a/src/m/m_allocator.cpp b/src/m/m_allocator.cpp
index e03651df..6211fa00 100644
--- a/src/m/m_allocator.cpp
+++ b/src/m/m_allocator.cpp
@@ -99,6 +99,6 @@ void mHeapAllocator_c::adjustFrmHeapRestoreCurrent() {
     mHeap::adjustFrmHeap(static_cast<EGG::FrmHeap *>(getHeapOfKind(heap, EGG::Heap::HEAP_KIND_FRAME)));
 }
 
-void *mHeapAllocator_c::allocOnHeap(u32 size, mHeapAllocator_c *allocator) {
+void *operator new[](size_t size, mAllocator_c *allocator) {
     return allocator->alloc(size);
 }

From 6fed118ad4461c1415bb71a0daa8cda001b6a3cf Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Wed, 11 Sep 2024 22:57:57 -0400
Subject: [PATCH 31/33] m3d matching + some EGG declarations

---
 config/SOUE01/splits.txt       |   3 +-
 config/SOUE01/symbols.txt      |  28 ++---
 configure.py                   |   2 +-
 include/egg/core/eggSystem.h   |   3 +
 include/egg/gfx/eggDrawGX.h    |  34 +++++++
 include/egg/gfx/eggFog.h       |  28 +++++
 include/egg/gfx/eggFrustum.h   |  24 +++++
 include/egg/gfx/eggGfxEngine.h |  30 ++++++
 include/egg/gfx/eggLight.h     |  53 ++++++++++
 include/egg/gfx/eggScreen.h    |  27 +++++
 include/egg/gfx/eggStateGX.h   |  45 +++++++++
 include/egg/prim/eggBinary.h   |  23 +++++
 include/m/m3d/m3d.h            |  19 +++-
 include/m/m_video.h            |  17 ++++
 include/nw4r/g3d/g3d_scnobj.h  |  35 ++++---
 include/nw4r/g3d/g3d_scnroot.h |   5 +-
 src/m/m3d/m3d.cpp              | 180 +++++++++++++++++++++++++++++++--
 src/m/m3d/m_banm.cpp           |   5 +-
 src/m/m3d/m_video.cpp          |  13 +++
 19 files changed, 523 insertions(+), 51 deletions(-)
 create mode 100644 include/egg/gfx/eggDrawGX.h
 create mode 100644 include/egg/gfx/eggFog.h
 create mode 100644 include/egg/gfx/eggFrustum.h
 create mode 100644 include/egg/gfx/eggGfxEngine.h
 create mode 100644 include/egg/gfx/eggLight.h
 create mode 100644 include/egg/gfx/eggScreen.h
 create mode 100644 include/egg/gfx/eggStateGX.h
 create mode 100644 include/egg/prim/eggBinary.h
 create mode 100644 include/m/m_video.h
 create mode 100644 src/m/m3d/m_video.cpp

diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 9968be83..b4d1e6f4 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -280,7 +280,8 @@ f/f_manager.cpp:
 
 m/m3d/m3d.cpp:
 	.text       start:0x802E3E50 end:0x802E4714
-	.sbss       start:0x80575BD0 end:0x80575BE8
+	.sbss       start:0x80575BD0 end:0x80575BEC
+	.sdata2     start:0x8057CCB8 end:0x8057CCBC
 
 m/m3d/m_proc.cpp:
 	.text       start:0x802E4720 end:0x802E48D4
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index fa22ca7d..d8510e5f 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -2859,7 +2859,7 @@ fn_80065050 = .text:0x80065050; // type:function size:0x164
 fn_800651C0 = .text:0x800651C0; // type:function size:0x204
 fn_800653D0 = .text:0x800653D0; // type:function size:0x194
 fn_80065570 = .text:0x80065570; // type:function size:0x20
-fn_80065590 = .text:0x80065590; // type:function size:0x54
+create__6mVideoFv = .text:0x80065590; // type:function size:0x54
 fn_800655F0 = .text:0x800655F0; // type:function size:0x4
 fn_80065600 = .text:0x80065600; // type:function size:0x3C
 fn_80065640 = .text:0x80065640; // type:function size:0x164
@@ -24784,7 +24784,7 @@ fn_80454B40 = .text:0x80454B40; // type:function size:0x9C
 fn_80454BE0 = .text:0x80454BE0; // type:function size:0x18
 fn_80454C00 = .text:0x80454C00; // type:function size:0x70
 fn_80454C70 = .text:0x80454C70; // type:function size:0x1DC
-fn_80454E50 = .text:0x80454E50; // type:function size:0x60
+GetCameraMtx__Q34nw4r3g3d6CameraCFPQ34nw4r4math5MTX34 = .text:0x80454E50; // type:function size:0x60
 fn_80454EB0 = .text:0x80454EB0; // type:function size:0xDC
 fn_80454F90 = .text:0x80454F90; // type:function size:0xD0
 fn_80455060 = .text:0x80455060; // type:function size:0x80
@@ -24894,11 +24894,11 @@ SetCurrentCamera__Q34nw4r3g3d7ScnRootFi = .text:0x80459AA0; // type:function siz
 fn_80459AB0 = .text:0x80459AB0; // type:function size:0x5C
 fn_80459B10 = .text:0x80459B10; // type:function size:0x234
 fn_80459D50 = .text:0x80459D50; // type:function size:0x188
-fn_80459EE0 = .text:0x80459EE0; // type:function size:0x4C
+CalcWorld__Q34nw4r3g3d7ScnRootFv = .text:0x80459EE0; // type:function size:0x4C
 CalcMaterial__Q34nw4r3g3d7ScnRootFv = .text:0x80459F30; // type:function size:0x1C
-fn_80459F50 = .text:0x80459F50; // type:function size:0x78
-fn_80459FD0 = .text:0x80459FD0; // type:function size:0xEC
-fn_8045A0C0 = .text:0x8045A0C0; // type:function size:0x60
+CalcView__Q34nw4r3g3d7ScnRootFv = .text:0x80459F50; // type:function size:0x78
+GatherDrawScnObj__Q34nw4r3g3d7ScnRootFv = .text:0x80459FD0; // type:function size:0xEC
+ZSort__Q34nw4r3g3d7ScnRootFv = .text:0x8045A0C0; // type:function size:0x60
 DrawOpa__Q34nw4r3g3d7ScnRootFv = .text:0x8045A120; // type:function size:0x74
 DrawXlu__Q34nw4r3g3d7ScnRootFv = .text:0x8045A1A0; // type:function size:0x74
 fn_8045A220 = .text:0x8045A220; // type:function size:0xFC
@@ -39811,7 +39811,7 @@ lbl_80575210 = .sbss:0x80575210; // type:object size:0x4 data:4byte
 lbl_80575214 = .sbss:0x80575214; // type:object size:0x4 data:4byte
 lbl_80575218 = .sbss:0x80575218; // type:object size:0x8 data:4byte
 lbl_80575220 = .sbss:0x80575220; // type:object size:0x8 data:4byte
-lbl_80575228 = .sbss:0x80575228; // type:object size:0x4 data:4byte
+ms_configuration_p__6dSys_c = .sbss:0x80575228; // type:object size:0x4 data:4byte
 lbl_8057522C = .sbss:0x8057522C; // type:object size:0x4 data:4byte
 lbl_80575230 = .sbss:0x80575230; // type:object size:0x4 data:4byte
 lbl_80575234 = .sbss:0x80575234; // type:object size:0x4 data:4byte
@@ -40298,11 +40298,11 @@ lbl_80575BC8 = .sbss:0x80575BC8; // type:object size:0x4 data:4byte
 lbl_80575BCC = .sbss:0x80575BCC; // type:object size:0x1 data:byte
 l_allocator_p__Q23m3d8internal = .sbss:0x80575BD0; // type:object size:0x4 data:4byte
 l_scnRoot_p__Q23m3d8internal = .sbss:0x80575BD4; // type:object size:0x4 data:4byte
-lbl_80575BD8 = .sbss:0x80575BD8; // type:object size:0x4 data:4byte
-lbl_80575BDC = .sbss:0x80575BDC; // type:object size:0x4 data:4byte
-lbl_80575BE0 = .sbss:0x80575BE0; // type:object size:0x4 data:4byte
-lbl_80575BE4 = .sbss:0x80575BE4; // type:object size:0x4 data:4byte
-lbl_80575BE8 = .sbss:0x80575BE8; // type:object size:0x8 data:4byte
+l_numLightMgr__Q23m3d8internal = .sbss:0x80575BD8; // type:object size:0x4 data:4byte
+l_lightMgr_pp__Q23m3d8internal = .sbss:0x80575BDC; // type:object size:0x4 data:4byte
+l_numFogMgr__Q23m3d8internal = .sbss:0x80575BE0; // type:object size:0x4 data:4byte
+l_fogMgr_pp__Q23m3d8internal = .sbss:0x80575BE4; // type:object size:0x4 data:4byte
+l_alignment__Q23m3d8internal = .sbss:0x80575BE8; // type:object size:0x4 data:4byte
 lbl_80575BF0 = .sbss:0x80575BF0; // type:object size:0x4 data:4byte
 lbl_80575BF4 = .sbss:0x80575BF4; // type:object size:0x4 data:4byte
 lbl_80575BF8 = .sbss:0x80575BF8; // type:object size:0x4 data:4byte
@@ -40336,7 +40336,7 @@ s_GetWPADInfoCount__4mPad = .sbss:0x80575C6C; // type:object size:0x4 data:4byte
 lbl_80575C70 = .sbss:0x80575C70; // type:object size:0x8 data:float
 lbl_80575C78 = .sbss:0x80575C78; // type:object size:0x8 data:float
 lbl_80575C80 = .sbss:0x80575C80; // type:object size:0x8 data:float
-lbl_80575C88 = .sbss:0x80575C88; // type:object size:0x8 data:4byte
+m_video__6mVideo = .sbss:0x80575C88; // type:object size:0x8 data:4byte
 lbl_80575C90 = .sbss:0x80575C90; // type:object size:0x4 data:4byte
 lbl_80575C94 = .sbss:0x80575C94; // type:object size:0x4 data:4byte
 lbl_80575C98 = .sbss:0x80575C98; // type:object size:0x8 data:4byte
@@ -45834,7 +45834,7 @@ lbl_8057CC9C = .sdata2:0x8057CC9C; // type:object size:0x4 data:float
 lbl_8057CCA0 = .sdata2:0x8057CCA0; // type:object size:0x4 data:float
 lbl_8057CCA8 = .sdata2:0x8057CCA8; // type:object size:0x8 data:double
 lbl_8057CCB0 = .sdata2:0x8057CCB0; // type:object size:0x8 data:float
-lbl_8057CCB8 = .sdata2:0x8057CCB8; // type:object size:0x8 data:float
+lbl_8057CCB8 = .sdata2:0x8057CCB8; // type:object size:0x4 data:float
 lbl_8057CCC0 = .sdata2:0x8057CCC0; // type:object size:0x4 data:float
 lbl_8057CCC4 = .sdata2:0x8057CCC4; // type:object size:0x4 data:float
 lbl_8057CCC8 = .sdata2:0x8057CCC8; // type:object size:0x8 data:double
diff --git a/configure.py b/configure.py
index 27459f7b..14e55284 100644
--- a/configure.py
+++ b/configure.py
@@ -346,7 +346,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]):
         "cflags": cflags_framework,
         "host": False,
         "objects": [
-            Object(NonMatching, "m/m3d/m3d.cpp"),
+            Object(Matching, "m/m3d/m3d.cpp"),
             Object(Matching, "m/m3d/m_proc.cpp"),
             Object(Matching, "m/m3d/m_anmchr.cpp"),
             Object(Matching, "m/m3d/m_anmchrblend.cpp"),
diff --git a/include/egg/core/eggSystem.h b/include/egg/core/eggSystem.h
index e124b306..9fe8e9db 100644
--- a/include/egg/core/eggSystem.h
+++ b/include/egg/core/eggSystem.h
@@ -57,6 +57,9 @@ class BaseSystem {
     }
 };
 
+template <class TVideo, class TDisplay, class TXfbManager, class TAudioManager, class TSceneManager, class TPerfView>
+class TSystem : ConfigurationData {};
+
 } // namespace EGG
 
 #endif
diff --git a/include/egg/gfx/eggDrawGX.h b/include/egg/gfx/eggDrawGX.h
new file mode 100644
index 00000000..3f8dcce6
--- /dev/null
+++ b/include/egg/gfx/eggDrawGX.h
@@ -0,0 +1,34 @@
+#ifndef EGG_DRAWGX_H
+#define EGG_DRAWGX_H
+
+#include <common.h>
+#include <egg/core/eggHeap.h>
+#include <egg/math/eggMatrix.h>
+#include <rvl/GX.h>
+
+namespace EGG {
+
+class DrawGX {
+public:
+    static void Initialize(Heap *);
+
+    static GXTexMapID GetTexMapDefault();
+
+    // TODO MORE
+
+    /* static ?? s_DL */
+
+    static Matrix34f s_cameraMtx;
+
+    /* static ?? s_flag */
+
+    static GXColor BLACK;
+    static GXColor WHITE;
+    static GXColor GREEN;
+    static GXColor RED;
+    static GXColor BLUE;
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggFog.h b/include/egg/gfx/eggFog.h
new file mode 100644
index 00000000..d9318066
--- /dev/null
+++ b/include/egg/gfx/eggFog.h
@@ -0,0 +1,28 @@
+#ifndef EGG_FOG_H
+#define EGG_FOG_H
+
+#include <egg/prim/eggBinary.h>
+#include <nw4r/types_nw4r.h>
+
+namespace EGG {
+
+// TODO: Add members
+class FogManager : IBinary<FogManager> {
+public:
+    FogManager(u16);
+    virtual ~FogManager();
+    virtual void SetBinaryInner(Bin &) override;
+    virtual void GetBinaryInner(Bin *) const override;
+    virtual size_t GetBinarySize() override;
+    virtual void SetBinaryInner(const Bin &, const Bin &, f32) override;
+
+    void Calc();
+    void CopyToG3D(nw4r::g3d::ScnRoot *) const;
+
+    /* 0x04 */ u8 mFlag;
+    /* 0x05 */ u8 TODO[0x10 - 0x05];
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggFrustum.h b/include/egg/gfx/eggFrustum.h
new file mode 100644
index 00000000..770dd328
--- /dev/null
+++ b/include/egg/gfx/eggFrustum.h
@@ -0,0 +1,24 @@
+#ifndef EGG_FRUSTUM_H
+#define EGG_FRUSTUM_H
+
+#include <common.h>
+#include <nw4r/g3d/g3d_camera.h>
+
+namespace EGG {
+
+// TODO: Fill out more
+class Frustum {
+public:
+    enum CanvasMode {};
+    enum ProjectionType {};
+
+    u8 TODO_0x00[0x38];
+    // vt at 0x38
+    virtual ~Frustum();
+    virtual void SetProjectionGX() const;
+    virtual void CopyToG3D(nw4r::g3d::Camera) const;
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggGfxEngine.h b/include/egg/gfx/eggGfxEngine.h
new file mode 100644
index 00000000..8844587a
--- /dev/null
+++ b/include/egg/gfx/eggGfxEngine.h
@@ -0,0 +1,30 @@
+#ifndef EGG_GFXENGINE_H
+#define EGG_GFXENGINE_H
+
+#include <common.h>
+#include <rvl/GX.h>
+
+namespace EGG {
+
+namespace GfxEngine {
+
+struct Configuration {
+    /* 0x00 */ u16 efbWidth;
+    /* 0x02 */ u16 efbHeight;
+    /* 0x04 */ GXColor clearColor;
+    /* 0x08 */ u32 field_0x08;
+    /* 0x0C */ u16 field_0x0C;
+    /* 0x0E */ u16 field_0x0E;
+    /* 0x10 */ u32 bufferSize;
+    /* 0x14 */ u16 field_0x14;
+    /* 0x16 */ u16 field_0x16;
+    /* 0x18 */ u8 UNK_0x18[4];
+    Configuration();
+    virtual ~Configuration() {}
+};
+
+} // namespace GfxEngine
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggLight.h b/include/egg/gfx/eggLight.h
new file mode 100644
index 00000000..073705a7
--- /dev/null
+++ b/include/egg/gfx/eggLight.h
@@ -0,0 +1,53 @@
+#ifndef EGG_LIGHT_H
+#define EGG_LIGHT_H
+
+#include <egg/prim/eggBinary.h>
+#include <nw4r/types_nw4r.h>
+
+namespace EGG {
+
+// TODO: Fill out more
+class LightTexture {
+public:
+    static void initialize(u16 textureSize, Heap *pHeap);
+};
+
+// TODO: Fill out more
+class LightTextureManager : public IBinary<LightTextureManager> {
+public:
+    virtual ~LightTextureManager();
+    virtual void SetBinaryInner(Bin &) override;
+    virtual void GetBinaryInner(Bin *) const override;
+    virtual size_t GetBinarySize() override;
+    virtual void SetBinaryInner(const Bin &, const Bin &, f32) override;
+
+    void drawAndCaptureTexture(f32, f32, f32, f32);
+};
+
+// TODO: Fill out more
+class LightManager : public IBinary<LightManager> {
+public:
+    // vt at 0x00
+    LightManager(u32, u32, u8);
+    virtual ~LightManager();
+    virtual void SetBinaryInner(Bin &) override;
+    virtual void GetBinaryInner(Bin *) const override;
+    virtual size_t GetBinarySize() override;
+    virtual void SetBinaryInner(const Bin &, const Bin &, f32) override;
+    virtual void Reset();
+    virtual void Calc(nw4r::g3d::ScnRoot *);
+    virtual void CalcView(const nw4r::math::MTX34 &, u8, nw4r::g3d::ScnRoot *);
+    virtual void DoneDraw();
+
+    LightTextureManager *GetTextureMgr() const {
+        return mTextureMgr;
+    }
+
+    u8 TODO_0x04[0x14 - 0x04];
+    LightTextureManager *mTextureMgr;
+    u8 TODO_0x18[0x28 - 0x18];
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggScreen.h b/include/egg/gfx/eggScreen.h
new file mode 100644
index 00000000..b1b1c422
--- /dev/null
+++ b/include/egg/gfx/eggScreen.h
@@ -0,0 +1,27 @@
+#ifndef EGG_SCREEN_H
+#define EGG_SCREEN_H
+
+#include <common.h>
+#include <egg/gfx/eggFrustum.h>
+
+namespace EGG {
+
+// TODO: Fill out more
+class Screen : public Frustum {
+public:
+    Screen();
+    Screen(f32, f32, f32, f32, Screen *, CanvasMode);
+
+    virtual ~Screen();
+    virtual void SetProjectionGX() const override;
+    virtual void CopyToG3D(nw4r::g3d::Camera) const override;
+
+    static void Initialize(const u16 *, const u16 *, Screen *);
+    static void SetTVModeDefault();
+
+    u8 TODO_0x3C[0x88 - 0x3C];
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/gfx/eggStateGX.h b/include/egg/gfx/eggStateGX.h
new file mode 100644
index 00000000..5d2badb6
--- /dev/null
+++ b/include/egg/gfx/eggStateGX.h
@@ -0,0 +1,45 @@
+#ifndef EGG_STATE_GX_H
+#define EGG_STATE_GX_H
+
+#include <common.h>
+#include <rvl/GX.h>
+
+namespace EGG {
+
+class StateGX {
+public:
+    static void initialize(u16, u16, GXColor, GXPixelFmt);
+    static void frameInit();
+    static void textureInit(); // Guess for 804b4810
+
+    static void resetGX();
+    static void resetVtx();
+    static void resetColorChannel();
+    static void resetIndirect();
+    static void resetTexture();
+    static void resetTexGen();
+    static void resetTev();
+    static void resetPE();
+
+    static void resetGXCache();
+
+    static void GXSetPixelFmt(GXPixelFmt pixelFmt, GXZFmt16 zFmt);
+
+    static void invalidateTexAllGX();
+
+    static bool GXSetColorUpdate(bool);
+    static bool GXSetAlphaUpdate(bool);
+    static bool GXSetDither();
+    static void GXCopyTex();
+    static bool GXSetDstAlpha();
+    // Unk func here
+    static void GXSetProjection(Mtx44, int);
+    static void GXSetProjectionv(const f32 *);
+    static void GXSetViewport(f32, f32, f32, f32, f32, f32);
+    static void GZSetScissor(u32, u32, u32, u32);
+    static void GZSetScissorBoxOffset(s32, s32);
+};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/prim/eggBinary.h b/include/egg/prim/eggBinary.h
new file mode 100644
index 00000000..6f127955
--- /dev/null
+++ b/include/egg/prim/eggBinary.h
@@ -0,0 +1,23 @@
+#ifndef EGG_BINARY_H
+#define EGG_BINARY_H
+
+template <class T>
+class IBinary {
+public:
+    class Bin {};
+    virtual void SetBinaryInner(Bin &) = 0;
+    virtual void GetBinaryInner(Bin *) const = 0;
+    virtual size_t GetBinarySize() = 0;
+    virtual void SetBinaryInner(const Bin &, const Bin &, f32) = 0;
+
+    static const char *GetBinaryType();
+    int GetVersion();
+
+    void GetBinary(void *) const;
+
+    void SetBinary(const void *);
+
+    void SetBinaryBlend(const void *, const void *, f32);
+};
+
+#endif
diff --git a/include/m/m3d/m3d.h b/include/m/m3d/m3d.h
index 8b7d82f4..d8ba87fc 100644
--- a/include/m/m3d/m3d.h
+++ b/include/m/m3d/m3d.h
@@ -2,6 +2,8 @@
 #define M3D_H
 
 #include <egg/core/eggHeap.h>
+#include <egg/gfx/eggFog.h>
+#include <egg/gfx/eggLight.h>
 #include <m/m_allocator.h>
 #include <nw4r/g3d/g3d_scnroot.h>
 
@@ -11,20 +13,33 @@ namespace internal {
 
 extern mAllocator_c *l_allocator_p;
 extern nw4r::g3d::ScnRoot *l_scnRoot_p;
+extern u32 l_numLightMgr;
+extern EGG::LightManager **l_lightMgr_pp;
+extern u32 l_numFogMgr;
+extern EGG::FogManager **l_fogMgr_pp;
+extern size_t l_alignment;
 
 } // namespace internal
 
-bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4);
+bool create(EGG::Heap *pHeap, u32 maxNumChildren, u32 maxNumScnObj, u32 numLightObj, u32 numLightSet);
+bool create(EGG::Heap *pHeap, GXPixelFmt pxlFmt, GXColor clearColor, u32 maxNumChildren, u32 maxNumScnObj,
+        u32 numLightObj, u32 numLightSet, u32 numLightMgr, u32 numFogMgr);
+bool createLightMgr(EGG::Heap *, u16, u16, u8, bool, int);
+bool createFogMgr(EGG::Heap *, int, int);
 
 nw4r::g3d::ScnRoot *getScnRoot();
 nw4r::g3d::Camera getCamera(int id);
 nw4r::g3d::Camera getCurrentCamera();
 int getCurrentCameraID();
 
-// TODO EGG
+EGG::LightManager *getLightMgr(int idx);
+EGG::FogManager *getFogMgr(int idx);
+void drawDone(int idx);
+void drawLightMapTexture(int idx);
 
 void calcWorld(int idx);
 void calcMaterial();
+void calcView(int idx);
 
 void drawOpa();
 void drawXlu();
diff --git a/include/m/m_video.h b/include/m/m_video.h
new file mode 100644
index 00000000..5a0e382b
--- /dev/null
+++ b/include/m/m_video.h
@@ -0,0 +1,17 @@
+#ifndef M_VIDEO_H
+#define M_VIDEO_H
+
+#include <egg/core/eggVideo.h>
+
+namespace mVideo {
+
+// 0x80065590
+void create();
+
+extern EGG::Video *m_video;
+
+extern f32 l_fbAspect;
+
+} // namespace mVideo
+
+#endif
diff --git a/include/nw4r/g3d/g3d_scnobj.h b/include/nw4r/g3d/g3d_scnobj.h
index 3b8cdda0..b40ffbb5 100644
--- a/include/nw4r/g3d/g3d_scnobj.h
+++ b/include/nw4r/g3d/g3d_scnobj.h
@@ -23,20 +23,20 @@ class ScnObj : public G3dObj {
     enum ForEachResult { FOREACH_RESULT_0, FOREACH_RESULT_1 };
 
     enum ScnObjFlag {
-        FLAG_1 = 0x1,
-        FLAG_2 = 0x2,
-        FLAG_4 = 0x4,
-        FLAG_8 = 0x8,
-        FLAG_10 = 0x10,
-        FLAG_20 = 0x20,
-        FLAG_40 = 0x40,
-        FLAG_80 = 0x80,
-        FLAG_10000000 = 0x10000000,
-        FLAG_20000000 = 0x20000000,
-        FLAG_40000000 = 0x40000000,
-        FLAG_80000000 = 0x80000000,
-
-        FLAG_60 = FLAG_40 | FLAG_20
+        SCNOBJFLAG_DISABLE_CALC_WORLD = 1,
+        SCNOBJFLAG_DISABLE_CALC_MAT = 2,
+        SCNOBJFLAG_DISABLE_CALC_VTX = 4,
+        SCNOBJFLAG_DISABLE_CALC_VIEW = 8,
+        SCNOBJFLAG_DISABLE_GATHER_SCNOBJ = 16,
+        SCNOBJFLAG_DISABLE_DRAW_OPA = 32,
+        SCNOBJFLAG_DISABLE_DRAW_XLU = 64,
+        SCNOBJFLAG_DISABLE_UPDATEFRAME = 128,
+        SCNOBJFLAG_IGNORE_ANMCHR_TRANS = 256,
+        SCNOBJFLAG_ENABLE_CULLING = 268435456,
+        SCNOBJFLAG_NOT_GATHER_DRAW_OPA = 536870912,
+        SCNOBJFLAG_NOT_GATHER_DRAW_XLU = 1073741824,
+        SCNOBJFLAG_MTX_LOCAL_IDENTITY = -2147483648,
+        SCNOBJFLAG_DISABLE_DRAW = 96,
     };
 
     enum ScnObjMtxType { MTX_TYPE_LOCAL, MTX_TYPE_WORLD, MTX_TYPE_VIEW, MTX_TYPE_MAX };
@@ -92,7 +92,7 @@ class ScnObj : public G3dObj {
         mCallback = cb;
     }
 
-    IScnObjCallback* GetScnObjCallback() {
+    IScnObjCallback *GetScnObjCallback() {
         return mCallback;
     }
 
@@ -215,12 +215,11 @@ class ScnLeaf : public ScnObj {
         mScale.z = z;
     }
 
-    inline void SetScale(const math::VEC3& scale)
-    {
+    inline void SetScale(const math::VEC3 &scale) {
         mScale = scale;
     }
 
-    inline void GetScale(math::VEC3* scale) const {
+    inline void GetScale(math::VEC3 *scale) const {
         if (scale) {
             *scale = mScale;
         }
diff --git a/include/nw4r/g3d/g3d_scnroot.h b/include/nw4r/g3d/g3d_scnroot.h
index 0de89925..c1e14298 100644
--- a/include/nw4r/g3d/g3d_scnroot.h
+++ b/include/nw4r/g3d/g3d_scnroot.h
@@ -3,8 +3,8 @@
 #include "common.h"
 #include "nw4r/g3d/g3d_camera.h"
 #include "nw4r/g3d/g3d_fog.h"
-#include "nw4r/g3d/g3d_scnobj.h"
 #include "nw4r/g3d/g3d_light.h"
+#include "nw4r/g3d/g3d_scnobj.h"
 
 namespace nw4r {
 namespace g3d {
@@ -15,7 +15,8 @@ class ScnRoot : public ScnGroup {
     void SetCurrentCamera(int);
     Fog GetFog(int);
 
-    static ScnRoot *Construct(MEMAllocator *param_1,u32 *param_2,u32 param_3,u32 param_4, u32 param_5,u32 param_6);
+    static ScnRoot *Construct(MEMAllocator *pHeap, u32 *pSize, u32 maxNumChildren, u32 maxNumScnObj, u32 numLightObj,
+            u32 numLightSet);
 
     void UpdateFrame();
     void CalcWorld();
diff --git a/src/m/m3d/m3d.cpp b/src/m/m3d/m3d.cpp
index 6adf4075..e4ec66b8 100644
--- a/src/m/m3d/m3d.cpp
+++ b/src/m/m3d/m3d.cpp
@@ -1,8 +1,18 @@
-#include <m/m3d/m3d.h>
+#include <egg/gfx/eggDrawGX.h>
+#include <egg/gfx/eggGfxEngine.h>
+#include <egg/gfx/eggScreen.h>
+#include <egg/gfx/eggStateGX.h>
+
+#include <nw4r/g3d/g3d_camera.h>
 #include <nw4r/g3d/g3d_init.h>
 #include <nw4r/g3d/g3d_resmat.h>
 #include <nw4r/g3d/g3d_resmdl.h>
 #include <nw4r/g3d/g3d_state.h>
+
+#include <m/m3d/m3d.h>
+#include <m/m_heap.h>
+#include <m/m_video.h>
+
 #include <rvl/GX.h>
 
 namespace m3d {
@@ -11,23 +21,27 @@ namespace internal {
 
 mAllocator_c *l_allocator_p;
 nw4r::g3d::ScnRoot *l_scnRoot_p;
+u32 l_numLightMgr;
+EGG::LightManager **l_lightMgr_pp;
+u32 l_numFogMgr;
+EGG::FogManager **l_fogMgr_pp;
+size_t l_alignment;
 
 } // namespace internal
 
-extern "C" GXRenderModeObj **lbl_80575C88;
-
-bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4) {
-    internal::l_allocator_p = new (heap, 0x04) mAllocator_c();
+bool create(EGG::Heap *pHeap, u32 maxNumChildren, u32 maxNumScnObj, u32 numLightObj, u32 numLightSet) {
+    internal::l_allocator_p = new (pHeap) mAllocator_c();
 
     if (!internal::l_allocator_p) {
         return false;
     }
 
-    internal::l_allocator_p->attach(heap, 0x20);
+    internal::l_allocator_p->attach(pHeap, 0x20);
     nw4r::g3d::G3dInit(true);
-    nw4r::g3d::G3DState::SetRenderModeObj(**lbl_80575C88);
+    nw4r::g3d::G3DState::SetRenderModeObj(*mVideo::m_video->pRenderMode);
     u32 allocedSize;
-    internal::l_scnRoot_p = nw4r::g3d::ScnRoot::Construct(internal::l_allocator_p, &allocedSize, u1, u2, u3, u4);
+    internal::l_scnRoot_p = nw4r::g3d::ScnRoot::Construct(internal::l_allocator_p, &allocedSize, maxNumChildren,
+            maxNumScnObj, numLightObj, numLightSet);
     if (internal::l_scnRoot_p == nullptr) {
         delete internal::l_allocator_p;
         internal::l_allocator_p = nullptr;
@@ -36,6 +50,101 @@ bool create(EGG::Heap *heap, u32 u1, u32 u2, u32 u3, u32 u4) {
     return true;
 }
 
+bool create(EGG::Heap *pHeap, GXPixelFmt pxlFmt, GXColor clearColor, u32 maxNumChildren, u32 maxNumScnObj,
+        u32 numLightObj, u32 numLightSet, u32 numLightMgr, u32 numFogMgr) {
+    if (internal::l_lightMgr_pp && internal::l_fogMgr_pp) {
+        return false;
+    }
+    EGG::GfxEngine::Configuration config = EGG::GfxEngine::Configuration();
+
+    u16 temp1[2] = {config.field_0x14, config.field_0x16};
+    u16 temp2[2] = {config.efbHeight, config.efbHeight};
+
+    EGG::StateGX::initialize(config.efbWidth, config.efbHeight, clearColor, pxlFmt);
+    EGG::Screen::Initialize(temp1, temp2, nullptr);
+    EGG::Screen::SetTVModeDefault();
+    EGG::DrawGX::Initialize(pHeap);
+
+    EGG::LightTexture::initialize(0x20, pHeap);
+
+    internal::l_lightMgr_pp = new (pHeap) EGG::LightManager *[numLightMgr];
+    if (internal::l_lightMgr_pp) {
+        internal::l_numLightMgr = numLightMgr;
+        internal::l_fogMgr_pp = new (pHeap) EGG::FogManager *[numFogMgr];
+        if (internal::l_fogMgr_pp) {
+            internal::l_numFogMgr = numFogMgr;
+            memset(internal::l_lightMgr_pp, 0, numLightMgr * 4);
+            memset(internal::l_fogMgr_pp, 0, numFogMgr * 4);
+
+            if (create(pHeap, maxNumChildren, maxNumScnObj, numLightObj, numLightSet)) {
+                return true;
+            }
+        }
+    }
+
+    // Creation failed :(
+    delete[] internal::l_lightMgr_pp;
+    internal::l_lightMgr_pp = nullptr;
+    delete[] internal::l_fogMgr_pp;
+    internal::l_fogMgr_pp = nullptr;
+    return false;
+}
+
+bool createLightMgr(EGG::Heap *pHeap, u16 p2, u16 p3, u8 p4, bool bCreateFogMgr, int idx) {
+    using namespace internal;
+
+    if (!l_lightMgr_pp || (idx < 0 || idx >= l_numLightMgr || idx >= l_numFogMgr) ||
+            (l_lightMgr_pp[idx] || l_fogMgr_pp[idx])) {
+        return false;
+    }
+
+    EGG::Heap *prevHeap = mHeap::setCurrentHeap(pHeap);
+    EGG::LightManager *lightMgr = new (pHeap) EGG::LightManager(p2, p3, p4);
+    mHeap::setCurrentHeap(prevHeap);
+
+    if (lightMgr) {
+        if (!bCreateFogMgr || createFogMgr(pHeap, 0x20, idx)) {
+            l_lightMgr_pp[idx] = lightMgr;
+            return true;
+        }
+    }
+    delete lightMgr;
+    return false;
+
+    u8 input;
+    u8 *output;
+    switch (input % 8) {
+        do {
+        case 0:
+            *output++ = input;
+        case 1:
+            *output++ = input;
+        case 2:
+            *output++ = input;
+        } while (true);
+    }
+}
+
+bool createFogMgr(EGG::Heap *pHeap, int numFog, int idx) {
+    using namespace internal;
+
+    if (idx < 0 || idx >= l_numFogMgr || l_fogMgr_pp[idx]) {
+        return false;
+    }
+
+    EGG::FogManager *fogMgr;
+    {
+        mHeap heap(pHeap);
+        fogMgr = new (pHeap) EGG::FogManager(numFog);
+    }
+
+    if (!fogMgr) {
+        return false;
+    }
+    l_fogMgr_pp[idx] = fogMgr;
+    return true;
+}
+
 nw4r::g3d::ScnRoot *getScnRoot() {
     return internal::l_scnRoot_p;
 }
@@ -60,9 +169,43 @@ nw4r::g3d::LightSetting *getLightSettingP() {
     return internal::l_scnRoot_p->getLightSetting();
 }
 
-// TODO EGG
+EGG::LightManager *getLightMgr(int idx) {
+    return internal::l_lightMgr_pp[idx];
+}
+
+EGG::FogManager *getFogMgr(int idx) {
+    return internal::l_fogMgr_pp[idx];
+}
+
+void drawDone(int idx) {
+    if (internal::l_lightMgr_pp && internal::l_lightMgr_pp[idx]) {
+        internal::l_lightMgr_pp[idx]->DoneDraw();
+    }
+    if (internal::l_fogMgr_pp && internal::l_fogMgr_pp[idx]) {
+        internal::l_fogMgr_pp[idx]->mFlag &= 0xFB;
+    }
+}
+
+void drawLightMapTexture(int idx) {
+    using namespace internal;
+
+    if (!l_lightMgr_pp) {
+        return;
+    }
+    if (!l_lightMgr_pp[idx]) {
+        return;
+    }
+
+    l_lightMgr_pp[idx]->GetTextureMgr()->drawAndCaptureTexture(0.0f, 0.0f, 0.0f, 0.0f);
+}
 
 void calcWorld(int idx) {
+    if (internal::l_lightMgr_pp && internal::l_lightMgr_pp[idx]) {
+        internal::l_lightMgr_pp[idx]->Calc(internal::l_scnRoot_p);
+    }
+    if (internal::l_fogMgr_pp && internal::l_fogMgr_pp[idx]) {
+        internal::l_fogMgr_pp[idx]->Calc();
+    }
     internal::l_scnRoot_p->CalcWorld();
 }
 
@@ -70,6 +213,21 @@ void calcMaterial() {
     internal::l_scnRoot_p->CalcMaterial();
 }
 
+void calcView(int idx) {
+    if (internal::l_lightMgr_pp && internal::l_lightMgr_pp[idx]) {
+        nw4r::math::MTX34 camMtx;
+        getCurrentCamera().GetCameraMtx(&camMtx);
+        internal::l_lightMgr_pp[idx]->CalcView(camMtx, getCurrentCameraID(), internal::l_scnRoot_p);
+    }
+    if (internal::l_fogMgr_pp && internal::l_fogMgr_pp[idx]) {
+        internal::l_fogMgr_pp[idx]->CopyToG3D(internal::l_scnRoot_p);
+    }
+
+    internal::l_scnRoot_p->CalcView();
+    internal::l_scnRoot_p->GatherDrawScnObj();
+    internal::l_scnRoot_p->ZSort();
+}
+
 void drawOpa() {
     internal::l_scnRoot_p->DrawOpa();
 }
@@ -88,7 +246,9 @@ void clear() {
 
 void reset() {
     nw4r::g3d::G3dReset();
-    // TODO EGG
+    if (internal::l_lightMgr_pp || internal::l_fogMgr_pp) {
+        EGG::StateGX::resetGXCache();
+    }
 }
 
 int getMatID(nw4r::g3d::ResMdl mdl, const char *name) {
diff --git a/src/m/m3d/m_banm.cpp b/src/m/m3d/m_banm.cpp
index 8fc17be8..026eaf45 100644
--- a/src/m/m3d/m_banm.cpp
+++ b/src/m/m3d/m_banm.cpp
@@ -1,8 +1,7 @@
+#include <m/m3d/m3d.h>
 #include <m/m3d/m_banm.h>
 #include <m/m_heap.h>
 
-extern "C" u32 lbl_80575BE8;
-
 namespace m3d {
 banm_c::~banm_c() {
     banm_c::remove();
@@ -29,7 +28,7 @@ bool banm_c::createAllocator(mAllocator_c *alloc, u32 *pStart) {
     i1 = mHeap::frmHeapCost(i2, 0x20);
     *pStart = ROUND_UP(i1, 0x20);
     mpFrameHeap = mHeap::createFrmHeap(i2, (EGG::Heap *)alloc->mHeap, "アニメ切り替え用アロケータ(m3d::banm_c::m_heap)",
-            lbl_80575BE8, 0);
+            internal::l_alignment, 0);
     mAllocator.attach(mpFrameHeap, 0x20);
     return true;
 }
diff --git a/src/m/m3d/m_video.cpp b/src/m/m3d/m_video.cpp
new file mode 100644
index 00000000..b5ef2eb6
--- /dev/null
+++ b/src/m/m3d/m_video.cpp
@@ -0,0 +1,13 @@
+#include <d/d_system.h>
+#include <m/m_video.h>
+
+// I dont know if this file actually exists, the place that the function exists is in an odd spot
+
+namespace mVideo {
+
+// 0x80065590
+void create() {
+    // TODO
+}
+
+} // namespace mVideo

From 108506359d7d267152903ad7cdd4fda0b4702459 Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Thu, 12 Sep 2024 16:21:13 -0400
Subject: [PATCH 32/33] bline progress, stopping for now

---
 config/SOUE01/symbols.txt    |   8 +--
 include/egg/gfx/eggDrawGX.h  |   3 +
 include/egg/gfx/eggTexture.h |  11 +++
 include/egg/math/eggVector.h |   1 +
 include/m/m3d/m_bline.h      |  32 +++++----
 include/m/m3d/m_mdl.h        |   2 -
 include/nw4r/g3d/g3d_state.h |   3 +
 src/m/m3d/m_bline.cpp        | 131 +++++++++++++++++++++++++++++++----
 8 files changed, 159 insertions(+), 32 deletions(-)
 create mode 100644 include/egg/gfx/eggTexture.h

diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index d8510e5f..d7fcf444 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -17554,7 +17554,7 @@ setFrameOnly__Q23m3d6banm_cFf = .text:0x802E7F40; // type:function size:0x14
 getRate__Q23m3d6banm_cCFv = .text:0x802E7F60; // type:function size:0x14
 setRate__Q23m3d6banm_cFf = .text:0x802E7F80; // type:function size:0x14
 __dt__Q23m3d10blineMat_cFv = .text:0x802E7FA0; // type:function size:0x88
-create__Q23m3d10blineMat_cFP12mAllocator_ciUsffR6mColorPvb = .text:0x802E8030; // type:function size:0x148
+create__Q23m3d10blineMat_cFP12mAllocator_ciUsffRQ34nw4r2ut5ColorPQ23EGG7ResTIMGb = .text:0x802E8030; // type:function size:0x148
 __ct__Q23m3d7bline_cFv = .text:0x802E8180; // type:function size:0x30
 remove__Q23m3d10blineMat_cFv = .text:0x802E81B0; // type:function size:0x54
 update__Q23m3d10blineMat_cFv = .text:0x802E8210; // type:function size:0xA8
@@ -17563,10 +17563,10 @@ setupGX__Q23m3d10blineMat_cFb = .text:0x802E82D0; // type:function size:0x3B8
 drawOpa__Q23m3d10blineMat_cFv = .text:0x802E8690; // type:function size:0x98
 drawXlu__Q23m3d10blineMat_cFv = .text:0x802E8730; // type:function size:0x98
 __dt__Q23m3d7bline_cFv = .text:0x802E87D0; // type:function size:0x60
-create__Q23m3d7bline_cFPQ23EGG4HeapUsffR6mColor = .text:0x802E8830; // type:function size:0x1A0
+create__Q23m3d7bline_cFPQ23EGG4HeapUsffRCQ34nw4r2ut5Color = .text:0x802E8830; // type:function size:0x1A0
 __ct__Q33m3d7bline_c6VtxPosFv = .text:0x802E89D0; // type:function size:0x4
 __dt__Q33m3d7bline_c6VtxPosFv = .text:0x802E89E0; // type:function size:0x40
-ImageLine_VtxNrmCt = .text:0x802E8A20; // type:function size:0x4
+__ct__Q33m3d7bline_c6VtxNrmFv = .text:0x802E8A20; // type:function size:0x4
 remove__Q23m3d7bline_cFv = .text:0x802E8A30; // type:function size:0xA4
 update__Q23m3d7bline_cFP7mVec3_c = .text:0x802E8AE0; // type:function size:0x66C
 draw__Q23m3d7bline_cFv = .text:0x802E9150; // type:function size:0x11C
@@ -24720,7 +24720,7 @@ fn_8044DF80 = .text:0x8044DF80; // type:function size:0x9C
 fn_8044E020 = .text:0x8044E020; // type:function size:0xB8
 fn_8044E0E0 = .text:0x8044E0E0; // type:function size:0x10
 fn_8044E0F0 = .text:0x8044E0F0; // type:function size:0x28
-fn_8044E120 = .text:0x8044E120; // type:function size:0x5C
+LoadLightSet__Q34nw4r3g3d8G3DStateFiPUlPUlPUlPUlPQ34nw4r3g3d11AmbLightObj = .text:0x8044E120; // type:function size:0x5C
 fn_8044E180 = .text:0x8044E180; // type:function size:0xA4
 fn_8044E230 = .text:0x8044E230; // type:function size:0x1C
 fn_8044E250 = .text:0x8044E250; // type:function size:0x68
diff --git a/include/egg/gfx/eggDrawGX.h b/include/egg/gfx/eggDrawGX.h
index 3f8dcce6..91ce5fc8 100644
--- a/include/egg/gfx/eggDrawGX.h
+++ b/include/egg/gfx/eggDrawGX.h
@@ -3,9 +3,11 @@
 
 #include <common.h>
 #include <egg/core/eggHeap.h>
+#include <egg/gfx/eggTexture.h>
 #include <egg/math/eggMatrix.h>
 #include <rvl/GX.h>
 
+
 namespace EGG {
 
 class DrawGX {
@@ -13,6 +15,7 @@ class DrawGX {
     static void Initialize(Heap *);
 
     static GXTexMapID GetTexMapDefault();
+    static void LoadTexture(const EGG::ResTIMG *, GXTexMapID);
 
     // TODO MORE
 
diff --git a/include/egg/gfx/eggTexture.h b/include/egg/gfx/eggTexture.h
new file mode 100644
index 00000000..dcf0e926
--- /dev/null
+++ b/include/egg/gfx/eggTexture.h
@@ -0,0 +1,11 @@
+#ifndef EGG_TEXTURE_H
+#define EGG_TEXTURE_H
+
+namespace EGG {
+
+// TODO: Implement Members
+struct ResTIMG {};
+
+} // namespace EGG
+
+#endif
diff --git a/include/egg/math/eggVector.h b/include/egg/math/eggVector.h
index 9aa50ae8..43d340dd 100644
--- a/include/egg/math/eggVector.h
+++ b/include/egg/math/eggVector.h
@@ -127,6 +127,7 @@ struct Vector3s {
     s16 x, y, z;
 
 public:
+    Vector3s() {}
     Vector3s(s16 sx, s16 sy, s16 sz) {
         x = sx;
         y = sy;
diff --git a/include/m/m3d/m_bline.h b/include/m/m3d/m_bline.h
index f0ae1360..d47f269c 100644
--- a/include/m/m3d/m_bline.h
+++ b/include/m/m3d/m_bline.h
@@ -1,9 +1,10 @@
 #ifndef M3D_BLINE_H
 #define M3D_BLINE_H
 
+#include <egg/gfx/eggTexture.h>
 #include <m/m3d/m_proc.h>
-#include <m/m_color.h>
 #include <m/m_math.h>
+#include <nw4r/ut/ut_Color.h>
 
 namespace m3d {
 
@@ -13,16 +14,21 @@ class bline_c {
     bline_c() : mpPathArr(0), mpVtxPosArr(0), mpVtxNrmArr(0), mpVtxTexArr(0), mFlags(0) {}
     // This is mainly a Guess, When the array is created, it has both a ctor/dtor
     struct VtxPos {
-        EGG::Vector3f vec[2];
-        VtxPos() {}
-        ~VtxPos() {}
+        mVec3_c pos1;
+        mVec3_c pos2;
+    };
+    struct Vec3u8 {
+        u8 x, y, z;
     };
     // This is mainly a Guess, When the array is created, it has only a ctor
     struct VtxNrm {
-        struct {
-            u8 x, y, z;
-        } nrm[2];
-        VtxNrm() {}
+        union {
+            struct {
+                Vec3u8 nrm1;
+                Vec3u8 nrm2;
+            };
+            EGG::Vector3s nrm_u16; // There is a short by short copy later
+        };
     };
     // This is mainly a Guess, When the array is created, it doesnt use the array alloc
     struct VtxTex {
@@ -31,7 +37,7 @@ class bline_c {
 
     /* 0x00 */ u8 field_0x00[0x8];
 
-    bool create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, mColor &color);
+    bool create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, const nw4r::ut::Color &color);
     void update(mVec3_c *startPos);
     void remove();
     void draw();
@@ -40,7 +46,7 @@ class bline_c {
     virtual ~bline_c();
 
     /* 0x0C */ f32 mWidth; // could be a scale too
-    /* 0x10 */ mColor mColor;
+    /* 0x10 */ nw4r::ut::Color mColor;
     /* 0x14 */ mVec3_c *mpPathArr;
     /* 0x18 */ VtxPos *mpVtxPosArr;
     /* 0x1C */ VtxNrm *mpVtxNrmArr;
@@ -61,13 +67,13 @@ class blineMat_c : public proc_c {
     virtual void drawXlu() override;
     virtual void setupGX(bool bTransparent);
 
-    bool create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat, mColor &color,
-            void *pTex, bool);
+    bool create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat, nw4r::ut::Color &color,
+            EGG::ResTIMG *pTex, bool);
     void update();
     bline_c *getLine(u16 idx);
 
     /* 0x1C */ mAllocator_c mAllocator;
-    /* 0x34 */ void *mpTex;
+    /* 0x34 */ EGG::ResTIMG *mpTex;
     /* 0x38 */ nw4r::ut::List mLines;
     /* 0x44 */ bline_c *mpLineArr;
     /* 0x48 */ short mLineArrNum;
diff --git a/include/m/m3d/m_mdl.h b/include/m/m3d/m_mdl.h
index d118e841..82b475e8 100644
--- a/include/m/m3d/m_mdl.h
+++ b/include/m/m3d/m_mdl.h
@@ -8,8 +8,6 @@
 #include <nw4r/g3d/g3d_calcworld.h>
 #include <nw4r/g3d/g3d_resmdl.h>
 
-class UnkClass3 {};
-
 namespace m3d {
 
 class callback_c {
diff --git a/include/nw4r/g3d/g3d_state.h b/include/nw4r/g3d/g3d_state.h
index f3af0bde..d299b6ec 100644
--- a/include/nw4r/g3d/g3d_state.h
+++ b/include/nw4r/g3d/g3d_state.h
@@ -13,6 +13,9 @@ void SetViewPosNrmMtxArray(const math::MTX34 *, const math::MTX33 *, const math:
 
 void SetRenderModeObj(const GXRenderModeObj &);
 GXRenderModeObj &GetRenderModeObj();
+void LoadLightSet(int idx_lightset, u32 *mask_diff_color /* r1+0xC */, u32 *mask_diff_alpha, u32 *mask_spec_color,
+        u32 *mask_spec_alpha, AmbLightObj *amb);
+
 void Invalidate(u32);
 } // namespace G3DState
 } // namespace g3d
diff --git a/src/m/m3d/m_bline.cpp b/src/m/m3d/m_bline.cpp
index 888314ba..326ae6c1 100644
--- a/src/m/m3d/m_bline.cpp
+++ b/src/m/m3d/m_bline.cpp
@@ -1,5 +1,10 @@
+#include <egg/core/eggColorFader.h>
 #include <egg/core/eggHeap.h>
+#include <egg/gfx/eggDrawGX.h>
+#include <m/m3d/m3d.h>
 #include <m/m3d/m_bline.h>
+#include <nw4r/g3d/g3d_light.h>
+#include <nw4r/g3d/g3d_state.h>
 
 namespace m3d {
 
@@ -7,8 +12,8 @@ blineMat_c::~blineMat_c() {
     remove();
 }
 
-bool blineMat_c::create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat, mColor &color,
-        void *pTex, bool p9) {
+bool blineMat_c::create(mAllocator_c *pAllocator, int numLines, u16 numLinePts, f32 width, f32 repeat,
+        nw4r::ut::Color &color, EGG::ResTIMG *pTex, bool p9) {
     if (!proc_c::create(pAllocator, nullptr)) {
         return false;
     }
@@ -38,8 +43,6 @@ bool blineMat_c::create(mAllocator_c *pAllocator, int numLines, u16 numLinePts,
     return true;
 }
 
-// void bline_c::bline_c() {}
-
 void blineMat_c::remove() {
     if (mpLineArr) {
         delete[] mpLineArr;
@@ -48,13 +51,96 @@ void blineMat_c::remove() {
     scnLeaf_c::remove();
 }
 
-void blineMat_c::update() {}
+void blineMat_c::update() {
+    nw4r::math::MTX34 camMtx;
+    mVec3_c vec;
+
+    getCurrentCamera().GetCameraMtx(&camMtx);
+    PSMTXInverse(camMtx, camMtx);
+    PSMTXMultVec(camMtx, mVec3_c::zero, vec);
+
+    for (bline_c *line = (bline_c *)nw4r::ut::List_GetNext(&mLines, 0); line != nullptr;
+            line = (bline_c *)nw4r::ut::List_GetNext(&mLines, line)) {
+        if ((line->mFlags & 1) == 0) {
+            line->update(&vec);
+        }
+    }
+}
 
 bline_c *blineMat_c::getLine(u16 idx) {
     return (bline_c *)nw4r::ut::List_GetNth(&mLines, idx);
 }
 
-void blineMat_c::setupGX(bool bTransparent) {}
+void blineMat_c::setupGX(bool bTransparent) {
+    EGG::DrawGX::LoadTexture(mpTex, GX_TEXMAP0);
+    if (field_0x4A) {
+        u32 mask_diff_color, mask_diff_alpha, mask_spec_color, mask_spec_alpha;
+        nw4r::g3d::AmbLightObj ambObj;
+
+        nw4r::g3d::G3DState::LoadLightSet(0, &mask_diff_color, &mask_diff_alpha, &mask_spec_color, &mask_spec_alpha,
+                &ambObj);
+
+        nw4r::ut::Color ambColor(0xFF, 0xFF, 0xFF, 0xFF);
+        nw4r::ut::Color matColor(0xFF, 0xFF, 0xFF, 0xFF);
+        GXSetChanMatColor(GX_COLOR0, matColor);
+
+        ambColor.r = ambObj.r;
+        ambColor.g = ambObj.g;
+        ambColor.b = ambObj.b;
+        ambColor.a = ambObj.a;
+        GXSetChanAmbColor(GX_COLOR0, ambColor);
+
+        GXSetNumChans(1);
+        GXSetChanCtrl(GX_COLOR0, TRUE, GX_SRC_REG, GX_SRC_REG, (GXLightID)mask_diff_color, GX_DF_CLAMP, GX_AF_NONE);
+        GXSetChanCtrl(GX_ALPHA0, FALSE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
+        GXSetNumTevStages(2);
+        GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+        GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_RASC, GX_CC_CPREV, GX_CC_ZERO);
+        GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
+        GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
+        GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
+    } else {
+        GXSetNumChans(0);
+        GXSetNumTevStages(1);
+    }
+    GXSetNumTexGens(1);
+    GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX_IDENT, 0, GX_DUALMTX_IDENT);
+    GXSetNumIndStages(0);
+    GXSetTevDirect(GX_TEVSTAGE0);
+    GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
+    GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_C0, GX_CC_TEXC, GX_CC_ZERO);
+    GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
+    GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_A0, GX_CA_TEXA, GX_CA_ZERO);
+    GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
+    GXSetZCompLoc(1);
+    if (!bTransparent) {
+        GXSetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_SET);
+        GXSetZMode(TRUE, GX_LEQUAL, TRUE);
+    } else {
+        GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
+        GXSetZMode(TRUE, GX_LEQUAL, FALSE);
+    }
+    GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_GREATER, 0);
+    nw4r::ut::Color fogColor = 0x0;
+    GXSetFog(GX_FOG_NONE, fogColor, 0.0f, 0.0f, 0.0f, 0.0f);
+    GXSetFogRangeAdj(FALSE, 0, NULL);
+    GXSetCullMode(GX_CULL_BACK);
+    GXSetDither(1);
+    GXSetClipMode(GX_CLIP_ENABLE);
+    GXClearVtxDesc();
+    GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+    GXSetVtxDesc(GX_VA_NRM, GX_DIRECT);
+    GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+    GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGBA, GX_F32, 0);
+    GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_CLR_RGB, GX_RGB8, 6);
+    GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_F32, 0);
+
+    nw4r::math::MTX34 camMtx;
+    getCurrentCamera().GetCameraMtx(&camMtx);
+    GXLoadPosMtxImm(camMtx, 0);
+    GXLoadNrmMtxImm(camMtx, 0);
+    GXSetCurrentMtx(0);
+}
 
 void blineMat_c::drawOpa() {
     update();
@@ -82,7 +168,7 @@ bline_c::~bline_c() {
     remove();
 }
 
-bool bline_c::create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, ::mColor &color) {
+bool bline_c::create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, const nw4r::ut::Color &color) {
     mpPathArr = new (pHeap) mVec3_c[numPts];
     if (!mpPathArr) {
         return false;
@@ -106,7 +192,6 @@ bool bline_c::create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, ::mCol
         return false;
     }
 
-    // Color being weird, dont really wanna try rn xD
     mPathNum = numPts;
     mVtxNum = numPts;
     mWidth = width;
@@ -117,10 +202,6 @@ bool bline_c::create(EGG::Heap *pHeap, u16 numPts, f32 width, f32 repeat, ::mCol
     return true;
 }
 
-// void bline_c::VtxPos::VtxPos() {}
-// void bline_c::VtxPos::~VtxPos() {}
-// void bline_c::VtxNrm::VtxNrm() {}
-
 void bline_c::remove() {
     if (mpPathArr) {
         delete[] mpPathArr;
@@ -142,6 +223,30 @@ void bline_c::remove() {
 
 void bline_c::update(mVec3_c *startPos) {}
 
-void bline_c::draw() {}
+void bline_c::draw() {
+    GXSetTevColor(GX_TEVREG0, mColor);
+
+    VtxPos *vtxPos = mpVtxPosArr;
+    VtxNrm *vtxNrm = mpVtxNrmArr;
+    VtxTex *vtxTex = mpVtxTexArr;
+
+    GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT0, mVtxNum * 2);
+
+    for (int i = 0; i < mVtxNum; i++) {
+        // First Vtx
+        GXPosition3f32(vtxPos->pos1.x, vtxPos->pos1.y, vtxPos->pos1.z);
+        GXNormal3s8(vtxNrm->nrm1.x, vtxNrm->nrm1.y, vtxNrm->nrm1.z);
+        GXTexCoord2f32(0.0f, vtxTex->tex);
+
+        // Second Vtx
+        GXPosition3f32(vtxPos->pos2.x, vtxPos->pos2.y, vtxPos->pos2.z);
+        GXNormal3s8(vtxNrm->nrm2.x, vtxNrm->nrm2.y, vtxNrm->nrm2.z);
+        GXTexCoord2f32(0.0f, vtxTex->tex);
+
+        vtxPos++;
+        vtxNrm++;
+        vtxTex++;
+    }
+}
 
 } // namespace m3d

From 48d627fc4e731f09fcdc31b0e3f407b2e0560f2a Mon Sep 17 00:00:00 2001
From: elijah-thomas774 <elijahthomas774@gmail.com>
Date: Thu, 12 Sep 2024 16:31:56 -0400
Subject: [PATCH 33/33] temp from a Dowsing PR merge

---
 src/REL/d/a/obj/d_a_obj_sun_light.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/REL/d/a/obj/d_a_obj_sun_light.cpp b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
index d0f889a2..d1983877 100644
--- a/src/REL/d/a/obj/d_a_obj_sun_light.cpp
+++ b/src/REL/d/a/obj/d_a_obj_sun_light.cpp
@@ -1,7 +1,7 @@
 #include <d/a/obj/d_a_obj_sun_light.h>
 #include <toBeSorted/arc_managers/current_stage_arc_manager.h>
-#include <toBeSorted/scgame.h>
 #include <toBeSorted/room_manager.h>
+#include <toBeSorted/scgame.h>
 
 SPECIAL_ACTOR_PROFILE(OBJ_SUN_LIGHT, dAcOsunLight_c, fProfile::OBJ_SUN_LIGHT, 0x0219, 0, 3);
 
@@ -58,5 +58,7 @@ void dAcOsunLight_c::executeState_Wait() {}
 void dAcOsunLight_c::finalizeState_Wait() {}
 
 bool dAcOsunLight_c::isDay() {
-    return !ScGame::currentSpawnInfo.isNight();
+    // return !ScGame::currentSpawnInfo.isNight();
+    // TODO TempFix?
+    return !(u8)ScGame::currentSpawnInfo.night;
 }