summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakaRikka <38417346+TakaRikka@users.noreply.github.com>2025-12-16 06:55:07 -0800
committerGitHub <noreply@github.com>2025-12-16 16:55:07 +0200
commit8185d87f855baff0d55d0e3ffc1a3cbdcfc4b5e2 (patch)
tree2a3c01f1a2a8b81de5a0748a5bcb187a97e7a114
parentc8104b6d62cc424318c7e0c44c92d94234bfef15 (diff)
copy homebuttonLib from oot-vc (#2960)
* initial copy of hbm from sdk_2009-12-11 * some more nw4hbm cleanup * nw4hbm db mostly done * nw4hbm snd copied from oot-vc * nw4hbm ut copied * nw4hbm lyt copied * nw4hbm copied, mostly matching usa 1.0 * setup nw4hbm debug define * fix HBMDataInfo struct * add rvl sdk card lib
-rw-r--r--config/RZDE01_00/splits.txt78
-rw-r--r--config/RZDE01_02/splits.txt70
-rw-r--r--config/RZDJ01/splits.txt70
-rwxr-xr-xconfigure.py72
-rw-r--r--include/dolphin/card.h4
-rw-r--r--include/global.h15
-rw-r--r--include/m_Do/m_Do_printf.h2
-rw-r--r--include/revolution/arc.h67
-rw-r--r--include/revolution/ax.h50
-rw-r--r--include/revolution/axfx.h24
-rw-r--r--include/revolution/card.h322
-rw-r--r--include/revolution/hbm.h55
-rw-r--r--include/revolution/mem.h8
-rw-r--r--include/revolution/mem/allocator.h37
-rw-r--r--include/revolution/mem/expHeap.h50
-rw-r--r--include/revolution/mem/heapCommon.h43
-rw-r--r--include/revolution/mem/list.h31
-rw-r--r--include/revolution/mtx.h2
-rw-r--r--include/revolution/os.h108
-rw-r--r--include/revolution/tpl.h52
-rw-r--r--include/revolution/wenc.h12
-rw-r--r--include/revolution/wpad.h83
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C++/MSL_Common/Include/new.h8
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cmath.h8
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h8
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/printf.h11
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h3
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Src/printf.c4
-rw-r--r--src/PowerPC_EABI_Support/Runtime/Inc/__va_arg.h16
-rw-r--r--src/Z2AudioLib/Z2SoundMgr.cpp5
-rw-r--r--src/revolution/card/CARDBios.c849
-rw-r--r--src/revolution/card/CARDBlock.c160
-rw-r--r--src/revolution/card/CARDCheck.c343
-rw-r--r--src/revolution/card/CARDCreate.c126
-rw-r--r--src/revolution/card/CARDDelete.c108
-rw-r--r--src/revolution/card/CARDDir.c89
-rw-r--r--src/revolution/card/CARDErase.c102
-rw-r--r--src/revolution/card/CARDFormat.c137
-rw-r--r--src/revolution/card/CARDMount.c394
-rw-r--r--src/revolution/card/CARDNet.c138
-rw-r--r--src/revolution/card/CARDOpen.c174
-rw-r--r--src/revolution/card/CARDProgram.c100
-rw-r--r--src/revolution/card/CARDRaw.c82
-rw-r--r--src/revolution/card/CARDRdwr.c105
-rw-r--r--src/revolution/card/CARDRead.c174
-rw-r--r--src/revolution/card/CARDRename.c70
-rw-r--r--src/revolution/card/CARDStat.c156
-rw-r--r--src/revolution/card/CARDStatEx.c124
-rw-r--r--src/revolution/card/CARDUnlock.c405
-rw-r--r--src/revolution/card/CARDWrite.c123
-rw-r--r--src/revolution/card/__card.h104
-rw-r--r--src/revolution/homebuttonLib/HBMAnmController.cpp33
-rw-r--r--src/revolution/homebuttonLib/HBMAnmController.h29
-rw-r--r--src/revolution/homebuttonLib/HBMBase.cpp2906
-rw-r--r--src/revolution/homebuttonLib/HBMBase.h295
-rw-r--r--src/revolution/homebuttonLib/HBMCommon.h11
-rw-r--r--src/revolution/homebuttonLib/HBMController.cpp356
-rw-r--r--src/revolution/homebuttonLib/HBMController.h102
-rw-r--r--src/revolution/homebuttonLib/HBMFrameController.cpp68
-rw-r--r--src/revolution/homebuttonLib/HBMFrameController.h76
-rw-r--r--src/revolution/homebuttonLib/HBMGUIManager.cpp433
-rw-r--r--src/revolution/homebuttonLib/HBMGUIManager.h234
-rw-r--r--src/revolution/homebuttonLib/HBMRemoteSpk.cpp259
-rw-r--r--src/revolution/homebuttonLib/HBMRemoteSpk.h65
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/DbgPrintBase.h12
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/assert.h111
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/console.h104
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp17
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/db_assert.cpp191
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/db_console.cpp315
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp464
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp367
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/directPrint.h23
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/db/mapFile.h20
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/animation.h209
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/arcResourceAccessor.h98
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/bounding.h21
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/common.h108
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/drawInfo.h67
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/group.h65
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/layout.h68
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp466
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp197
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp24
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp279
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp17
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp86
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp368
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp1198
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp495
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp130
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp15
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp558
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_types.h449
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp568
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/material.h154
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/pane.h175
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/picture.h44
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/resourceAccessor.h27
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/resources.h292
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/textBox.h86
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/lyt/window.h63
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/macros.h22
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/arithmetic.h164
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/constants.h25
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/equation.h14
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/geometry.h171
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp433
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/triangular.h47
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/math/types.h523
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/AxManager.h125
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/AxVoice.h321
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/Bank.h36
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/BankFile.h122
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/BasicPlayer.h73
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/BasicSound.h214
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/Channel.h185
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/ChannelManager.h23
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallback.h40
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallbackManager.h28
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/DvdSoundArchive.h56
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/EnvGenerator.h60
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/ExternalSoundPlayer.h66
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/FrameHeap.h75
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/FxBase.h31
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/InstanceManager.h53
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/InstancePool.h69
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/Lfo.h48
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MemorySoundArchive.h39
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqPlayer.h20
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqTrack.h20
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MmlParser.h155
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrack.h38
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrackAllocator.h37
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/MoveValue.h56
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/NandSoundArchive.h52
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/NoteOnCallback.h36
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/PlayerHeap.h43
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeaker.h72
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeakerManager.h42
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqFile.h52
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqPlayer.h199
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqSound.h83
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqSoundHandle.h27
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqTrack.h146
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SeqTrackAllocator.h22
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundArchive.h169
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveFile.h289
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveLoader.h59
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundArchivePlayer.h285
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundHandle.h105
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundHeap.h57
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundInstanceManager.h141
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundMemoryAllocatable.h16
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundPlayer.h111
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundStartable.h112
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundSystem.h50
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/SoundThread.h76
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/StrmChannel.h42
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/StrmFile.h156
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/StrmPlayer.h240
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/StrmSound.h59
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/StrmSoundHandle.h27
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/Task.h37
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/TaskManager.h53
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/TaskThread.h47
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/Util.h118
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WaveFile.h81
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WavePlayer.h127
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WaveSound.h49
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WaveSoundHandle.h25
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WsdFile.h143
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WsdPlayer.h116
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/WsdTrack.h69
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/debug.h37
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp14
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp24
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp21
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp22
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_global.h45
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/snd/snd_types.h121
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/CharStrmReader.h71
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/CharWriter.h160
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/Color.h69
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/DvdFileStream.h73
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/DvdLockedFileStream.h45
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/FileStream.h46
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/Font.h85
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/IOStream.h47
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/LinkList.h378
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/Lock.h44
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/NandFileStream.h71
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/Rect.h53
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ResFont.h78
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/RuntimeTypeInfo.h56
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/TagProcessor.h12
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/TagProcessorBase.h49
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/TextWriter.h12
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/TextWriterBase.h138
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/WideTagProcessor.h12
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/WideTextWriter.h12
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/binaryFileFormat.h29
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/fontResources.h77
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/inlines.h92
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/list.h48
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp79
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp414
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_DvdFileStream.cpp9
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_FileStream.cpp71
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp33
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_IOStream.cpp53
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp96
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_NandFileStream.cpp220
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp204
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp268
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp109
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp564
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp63
-rw-r--r--src/revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp102
-rw-r--r--src/revolution/homebuttonLib/sound/mix.cpp727
-rw-r--r--src/revolution/homebuttonLib/sound/mix.h51
-rw-r--r--src/revolution/homebuttonLib/sound/seq.cpp532
-rw-r--r--src/revolution/homebuttonLib/sound/seq.h70
-rw-r--r--src/revolution/homebuttonLib/sound/syn.cpp224
-rw-r--r--src/revolution/homebuttonLib/sound/syn.h121
-rw-r--r--src/revolution/homebuttonLib/sound/synctrl.cpp309
-rw-r--r--src/revolution/homebuttonLib/sound/synenv.cpp138
-rw-r--r--src/revolution/homebuttonLib/sound/synmix.cpp113
-rw-r--r--src/revolution/homebuttonLib/sound/synpitch.cpp193
-rw-r--r--src/revolution/homebuttonLib/sound/synprivate.h106
-rw-r--r--src/revolution/homebuttonLib/sound/synsample.cpp278
-rw-r--r--src/revolution/homebuttonLib/sound/synvoice.cpp78
-rw-r--r--src/revolution/wenc/wenc.c198
-rw-r--r--src/revolution/wpad/WPAD.c12
-rw-r--r--src/revolution/wpad/__wpad.h61
235 files changed, 32438 insertions, 320 deletions
diff --git a/config/RZDE01_00/splits.txt b/config/RZDE01_00/splits.txt
index 6eb2b35b4e..7cc246f20f 100644
--- a/config/RZDE01_00/splits.txt
+++ b/config/RZDE01_00/splits.txt
@@ -2345,7 +2345,7 @@ revolution/gf/GFPixel.cpp:
revolution/gf/GFTev.cpp:
.text start:0x802B20AC end:0x802B210C
-revolution/hbm/HBMBase.cpp:
+revolution/homebuttonLib/HBMBase.cpp:
.text start:0x802B210C end:0x802BA91C
.rodata start:0x803EF1C8 end:0x803EF478
.data start:0x804253D8 end:0x80426308
@@ -2354,125 +2354,125 @@ revolution/hbm/HBMBase.cpp:
.sbss start:0x8053B090 end:0x8053B098
.sdata2 start:0x8053FCD0 end:0x8053FD30
-revolution/hbm/HBMAnmController.cpp:
+revolution/homebuttonLib/HBMAnmController.cpp:
.text start:0x802BA91C end:0x802BAA3C
.data start:0x80426308 end:0x80426318
-revolution/hbm/HBMFrameController.cpp:
+revolution/homebuttonLib/HBMFrameController.cpp:
.text start:0x802BAA3C end:0x802BABDC
.sdata2 start:0x8053FD30 end:0x8053FD38
-revolution/hbm/HBMGUIManager.cpp:
+revolution/homebuttonLib/HBMGUIManager.cpp:
.text start:0x802BABDC end:0x802BC260
.data start:0x80426318 end:0x804264F8
.sdata start:0x8053A118 end:0x8053A120
.sbss start:0x8053B098 end:0x8053B0A0
.sdata2 start:0x8053FD38 end:0x8053FD48
-revolution/hbm/HBMController.cpp:
+revolution/homebuttonLib/HBMController.cpp:
.text start:0x802BC260 end:0x802BCAB8
.bss start:0x804C2448 end:0x804C25D8
.sbss start:0x8053B0A0 end:0x8053B0A8
.sdata2 start:0x8053FD48 end:0x8053FD58
-revolution/hbm/HBMRemoteSpk.cpp:
+revolution/homebuttonLib/HBMRemoteSpk.cpp:
.text start:0x802BCAB8 end:0x802BD2B8
.data start:0x804264F8 end:0x80426568
.sbss start:0x8053B0A8 end:0x8053B0B0
-revolution/db/db_assert.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_assert.cpp:
.text start:0x802BD2B8 end:0x802BD8C8
.data start:0x80426568 end:0x80426618
.bss start:0x804C25D8 end:0x804C2608
.sdata start:0x8053A120 end:0x8053A128
.sbss start:0x8053B0B0 end:0x8053B0C0
-revolution/db/db_console.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_console.cpp:
.text start:0x802BD8C8 end:0x802BE1C8
.data start:0x80426618 end:0x80426738
.bss start:0x804C2608 end:0x804C2A20
.sdata start:0x8053A128 end:0x8053A130
-revolution/db/db_DbgPrintBase.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp:
.text start:0x802BE1C8 end:0x802BE208
-revolution/db/db_directPrint.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp:
.text start:0x802BE208 end:0x802BEC18
.rodata start:0x803EF478 end:0x803EF730
.data start:0x80426738 end:0x804267E8
.bss start:0x804C2A20 end:0x804C2A60
.sbss start:0x8053B0C0 end:0x8053B0C8
-revolution/db/db_mapFile.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp:
.text start:0x802BEC18 end:0x802BF52C
.data start:0x804267E8 end:0x804269F0
.bss start:0x804C2A60 end:0x804C2CA0
.sdata start:0x8053A130 end:0x8053A138
.sbss start:0x8053B0C8 end:0x8053B0D8
-revolution/homebuttonLib/lyt_animation.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp:
.text start:0x802BF52C end:0x802C049C
.data start:0x804269F0 end:0x80426E60
.sdata start:0x8053A138 end:0x8053A140
.sdata2 start:0x8053FD58 end:0x8053FD78
-revolution/homebuttonLib/lyt_arcResourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp:
.text start:0x802C049C end:0x802C0BA0
.data start:0x80426E60 end:0x804270D8
.sdata start:0x8053A140 end:0x8053A148
-revolution/homebuttonLib/lyt_bounding.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp:
.text start:0x802C0BA0 end:0x802C0CB8
.ctors start:0x803CE384 end:0x803CE388
.data start:0x804270D8 end:0x80427140
.sbss start:0x8053B0D8 end:0x8053B0E8
-revolution/homebuttonLib/lyt_common.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp:
.text start:0x802C0CB8 end:0x802C1B38
.data start:0x80427140 end:0x804271B0
.bss start:0x804C2CA0 end:0x804C2CC0
.sdata2 start:0x8053FD78 end:0x8053FD88
-revolution/homebuttonLib/lyt_drawInfo.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp:
.text start:0x802C1B38 end:0x802C1BEC
.data start:0x804271B0 end:0x804271C0
.sdata2 start:0x8053FD88 end:0x8053FD90
-revolution/homebuttonLib/lyt_group.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp:
.text start:0x802C1BEC end:0x802C2094
.data start:0x804271C0 end:0x804272C0
-revolution/homebuttonLib/lyt_layout.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp:
.text start:0x802C2094 end:0x802C3048
.data start:0x804272C0 end:0x80427528
.sbss start:0x8053B0E8 end:0x8053B0F0
.sdata2 start:0x8053FD90 end:0x8053FD98
-revolution/homebuttonLib/lyt_material.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp:
.text start:0x802C3048 end:0x802C6768
.data start:0x80427528 end:0x80427B28
.sdata2 start:0x8053FD98 end:0x8053FDB8
.sbss2 start:0x80540BD0 end:0x80540BE8
-revolution/homebuttonLib/lyt_pane.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp:
.text start:0x802C6768 end:0x802C7AC0
.ctors start:0x803CE388 end:0x803CE38C
.data start:0x80427B28 end:0x80427EB8
.sbss start:0x8053B0F0 end:0x8053B0F8
.sdata2 start:0x8053FDB8 end:0x8053FDD8
-revolution/homebuttonLib/lyt_picture.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp:
.text start:0x802C7AC0 end:0x802C81E0
.ctors start:0x803CE38C end:0x803CE390
.data start:0x80427EB8 end:0x804280E0
.sbss start:0x8053B0F8 end:0x8053B100
.sdata2 start:0x8053FDD8 end:0x8053FDE0
-revolution/homebuttonLib/lyt_resourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp:
.text start:0x802C81E0 end:0x802C8238
.data start:0x804280E0 end:0x804280F8
-revolution/homebuttonLib/lyt_textBox.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp:
.text start:0x802C8238 end:0x802CB080
.ctors start:0x803CE390 end:0x803CE394
.data start:0x804280F8 end:0x80428770
@@ -2480,74 +2480,74 @@ revolution/homebuttonLib/lyt_textBox.cpp:
.sbss start:0x8053B100 end:0x8053B108
.sdata2 start:0x8053FDE0 end:0x8053FDF8
-revolution/homebuttonLib/lyt_window.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp:
.text start:0x802CB080 end:0x802CD954
.ctors start:0x803CE394 end:0x803CE398
.data start:0x80428770 end:0x804289D0
.sbss start:0x8053B108 end:0x8053B114
.sdata2 start:0x8053FDF8 end:0x8053FE10
-revolution/homebuttonLib/math_triangular.cpp:
+revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp:
.text start:0x802CD954 end:0x802CDBC4
.data start:0x804289D0 end:0x80429AE8
.sdata2 start:0x8053FE10 end:0x8053FE30
-revolution/homebuttonLib/snd_SoundArchivePlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp:
.text start:0x802CDBC4 end:0x802CDC34
.data start:0x80429AE8 end:0x80429B48
-revolution/homebuttonLib/snd_SoundHandle.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp:
.text start:0x802CDC34 end:0x802CDC84
-revolution/homebuttonLib/snd_SoundPlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp:
.text start:0x802CDC84 end:0x802CDD74
.data start:0x80429B48 end:0x80429BB8
.sdata2 start:0x8053FE30 end:0x8053FE38
-revolution/homebuttonLib/snd_SoundStartable.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp:
.text start:0x802CDD74 end:0x802CDDE0
-revolution/homebuttonLib/ut_binaryFileFormat.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp:
.text start:0x802CDDE0 end:0x802CE038
.data start:0x80429BB8 end:0x80429D08
-revolution/homebuttonLib/ut_CharStrmReader.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp:
.text start:0x802CE038 end:0x802CE998
.data start:0x80429D08 end:0x80429E50
-revolution/homebuttonLib/ut_CharWriter.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp:
.text start:0x802CE998 end:0x802D2EF4
.data start:0x80429E50 end:0x8042A130
.bss start:0x804C2CC0 end:0x804C2CE0
.sbss start:0x8053B114 end:0x8053B118
.sdata2 start:0x8053FE38 end:0x8053FE58
-revolution/homebuttonLib/ut_Font.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp:
.text start:0x802D2EF4 end:0x802D30B8
.data start:0x8042A130 end:0x8042A1A8
-revolution/homebuttonLib/ut_LinkList.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp:
.text start:0x802D30B8 end:0x802D3528
.data start:0x8042A1A8 end:0x8042A350
-revolution/homebuttonLib/ut_list.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp:
.text start:0x802D3528 end:0x802D38D0
.data start:0x8042A350 end:0x8042A3B0
-revolution/homebuttonLib/ut_ResFont.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp:
.text start:0x802D38D0 end:0x802D48E8
.data start:0x8042A3B0 end:0x8042AC68
-revolution/homebuttonLib/ut_ResFontBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp:
.text start:0x802D48E8 end:0x802D80C8
.data start:0x8042AC68 end:0x8042AF80
-revolution/homebuttonLib/ut_TagProcessorBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp:
.text start:0x802D80C8 end:0x802D92D0
.data start:0x8042AF80 end:0x8042B058
.sdata2 start:0x8053FE58 end:0x8053FE60
-revolution/homebuttonLib/ut_TextWriterBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp:
.text start:0x802D92D0 end:0x802E1FB4
.ctors start:0x803CE398 end:0x803CE39C
.data start:0x8042B058 end:0x8042B680
diff --git a/config/RZDE01_02/splits.txt b/config/RZDE01_02/splits.txt
index a265bdc599..739f5b328b 100644
--- a/config/RZDE01_02/splits.txt
+++ b/config/RZDE01_02/splits.txt
@@ -2348,7 +2348,7 @@ revolution/gf/GFPixel.cpp:
revolution/gf/GFTev.cpp:
.text start:0x802B1D5C end:0x802B1DBC
-revolution/hbm/HBMBase.cpp:
+revolution/homebuttonLib/HBMBase.cpp:
.text start:0x802B1DBC end:0x802BA3D4
.rodata start:0x803DA7C8 end:0x803DAA78
.data start:0x804107F0 end:0x80411398
@@ -2357,95 +2357,95 @@ revolution/hbm/HBMBase.cpp:
.sbss start:0x805210B0 end:0x805210B8
.sdata2 start:0x80525D58 end:0x80525DB8
-revolution/hbm/HBMAnmController.cpp:
+revolution/homebuttonLib/HBMAnmController.cpp:
.text start:0x802BA3D4 end:0x802BA4E0
.data start:0x80411398 end:0x804113A8
-revolution/hbm/HBMFrameController.cpp:
+revolution/homebuttonLib/HBMFrameController.cpp:
.text start:0x802BA4E0 end:0x802BA680
.sdata2 start:0x80525DB8 end:0x80525DC0
-revolution/hbm/HBMGUIManager.cpp:
+revolution/homebuttonLib/HBMGUIManager.cpp:
.text start:0x802BA680 end:0x802BBAB8
.data start:0x804113A8 end:0x80411500
.sbss start:0x805210B8 end:0x805210C0
.sdata2 start:0x80525DC0 end:0x80525DD0
-revolution/hbm/HBMController.cpp:
+revolution/homebuttonLib/HBMController.cpp:
.text start:0x802BBAB8 end:0x802BC310
.bss start:0x804A8B78 end:0x804A8D08
.sbss start:0x805210C0 end:0x805210C8
.sdata2 start:0x80525DD0 end:0x80525DE0
-revolution/hbm/HBMRemoteSpk.cpp:
+revolution/homebuttonLib/HBMRemoteSpk.cpp:
.text start:0x802BC310 end:0x802BCA6C
.data start:0x80411500 end:0x80411510
.sbss start:0x805210C8 end:0x805210D0
-revolution/db/db_DbgPrintBase.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp:
.text start:0x802BCA6C end:0x802BCAAC
-revolution/homebuttonLib/lyt_animation.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp:
.text start:0x802BCAAC end:0x802BD61C
.data start:0x80411510 end:0x80411530
.sdata2 start:0x80525DE0 end:0x80525E00
-revolution/homebuttonLib/lyt_arcResourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp:
.text start:0x802BD61C end:0x802BD990
.data start:0x80411530 end:0x80411548
.sdata start:0x80520160 end:0x80520168
-revolution/homebuttonLib/lyt_bounding.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp:
.text start:0x802BD990 end:0x802BDAA8
.ctors start:0x803B8DC8 end:0x803B8DCC
.data start:0x80411548 end:0x804115B0
.sbss start:0x805210D0 end:0x805210E0
-revolution/homebuttonLib/lyt_common.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp:
.text start:0x802BDAA8 end:0x802BE8A0
.bss start:0x804A8D08 end:0x804A8D28
.sdata2 start:0x80525E00 end:0x80525E10
-revolution/homebuttonLib/lyt_drawInfo.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp:
.text start:0x802BE8A0 end:0x802BE954
.data start:0x804115B0 end:0x804115C0
.sdata2 start:0x80525E10 end:0x80525E18
-revolution/homebuttonLib/lyt_group.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp:
.text start:0x802BE954 end:0x802BEC74
.data start:0x804115C0 end:0x804115D0
-revolution/homebuttonLib/lyt_layout.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp:
.text start:0x802BEC74 end:0x802BF8E4
.data start:0x804115D0 end:0x80411608
.sbss start:0x805210E0 end:0x805210E8
.sdata2 start:0x80525E18 end:0x80525E20
-revolution/homebuttonLib/lyt_material.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp:
.text start:0x802BF8E4 end:0x802C2CB8
.data start:0x80411608 end:0x80411670
.sdata2 start:0x80525E20 end:0x80525E40
.sbss2 start:0x80526C50 end:0x80526C68
-revolution/homebuttonLib/lyt_pane.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp:
.text start:0x802C2CB8 end:0x802C3BEC
.ctors start:0x803B8DCC end:0x803B8DD0
.data start:0x80411670 end:0x804116D8
.sbss start:0x805210E8 end:0x805210F0
.sdata2 start:0x80525E40 end:0x80525E60
-revolution/homebuttonLib/lyt_picture.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp:
.text start:0x802C3BEC end:0x802C410C
.ctors start:0x803B8DD0 end:0x803B8DD4
.data start:0x804116D8 end:0x80411748
.sbss start:0x805210F0 end:0x805210F8
.sdata2 start:0x80525E60 end:0x80525E68
-revolution/homebuttonLib/lyt_resourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp:
.text start:0x802C410C end:0x802C4164
.data start:0x80411748 end:0x80411760
-revolution/homebuttonLib/lyt_textBox.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp:
.text start:0x802C4164 end:0x802C5794
.ctors start:0x803B8DD4 end:0x803B8DD8
.data start:0x80411760 end:0x804117D8
@@ -2453,66 +2453,66 @@ revolution/homebuttonLib/lyt_textBox.cpp:
.sbss start:0x805210F8 end:0x80521100
.sdata2 start:0x80525E68 end:0x80525E80
-revolution/homebuttonLib/lyt_window.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp:
.text start:0x802C5794 end:0x802C7C10
.ctors start:0x803B8DD8 end:0x803B8DDC
.data start:0x804117D8 end:0x80411890
.sbss start:0x80521100 end:0x8052110C
.sdata2 start:0x80525E80 end:0x80525E98
-revolution/homebuttonLib/math_triangular.cpp:
+revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp:
.text start:0x802C7C10 end:0x802C7E80
.data start:0x80411890 end:0x804129A8
.sdata2 start:0x80525E98 end:0x80525EB8
-revolution/homebuttonLib/snd_SoundArchivePlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp:
.text start:0x802C7E80 end:0x802C7E90
-revolution/homebuttonLib/snd_SoundHandle.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp:
.text start:0x802C7E90 end:0x802C7EE0
-revolution/homebuttonLib/snd_SoundPlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp:
.text start:0x802C7EE0 end:0x802C7F50
-revolution/homebuttonLib/snd_SoundStartable.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp:
.text start:0x802C7F50 end:0x802C7FBC
-revolution/homebuttonLib/ut_binaryFileFormat.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp:
.text start:0x802C7FBC end:0x802C8030
-revolution/homebuttonLib/ut_CharStrmReader.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp:
.text start:0x802C8030 end:0x802C8140
-revolution/homebuttonLib/ut_CharWriter.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp:
.text start:0x802C8140 end:0x802C9BC4
.bss start:0x804A8D28 end:0x804A8D48
.sbss start:0x8052110C end:0x80521110
.sdata2 start:0x80525EB8 end:0x80525ED8
-revolution/homebuttonLib/ut_Font.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp:
.text start:0x802C9BC4 end:0x802C9C6C
.data start:0x804129A8 end:0x804129D8
-revolution/homebuttonLib/ut_LinkList.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp:
.text start:0x802C9C6C end:0x802C9D64
-revolution/homebuttonLib/ut_list.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp:
.text start:0x802C9D64 end:0x802C9EC4
-revolution/homebuttonLib/ut_ResFont.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp:
.text start:0x802C9EC4 end:0x802CA1C8
.data start:0x804129D8 end:0x80412A30
-revolution/homebuttonLib/ut_ResFontBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp:
.text start:0x802CA1C8 end:0x802CA798
.data start:0x80412A30 end:0x80412AF0
-revolution/homebuttonLib/ut_TagProcessorBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp:
.text start:0x802CA798 end:0x802CAF58
.data start:0x80412AF0 end:0x80412B48
.sdata2 start:0x80525ED8 end:0x80525EE0
-revolution/homebuttonLib/ut_TextWriterBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp:
.text start:0x802CAF58 end:0x802CC984
.ctors start:0x803B8DDC end:0x803B8DE0
.bss start:0x804A8D48 end:0x804A8D60
diff --git a/config/RZDJ01/splits.txt b/config/RZDJ01/splits.txt
index 3420fd04ba..4f9583ea9e 100644
--- a/config/RZDJ01/splits.txt
+++ b/config/RZDJ01/splits.txt
@@ -2348,7 +2348,7 @@ revolution/gf/GFPixel.cpp:
revolution/gf/GFTev.cpp:
.text start:0x802B387C end:0x802B38DC
-revolution/hbm/HBMBase.cpp:
+revolution/homebuttonLib/HBMBase.cpp:
.text start:0x802B38DC end:0x802BBEF4
.rodata start:0x803D8788 end:0x803D8A38
.data start:0x8040E670 end:0x8040F218
@@ -2357,95 +2357,95 @@ revolution/hbm/HBMBase.cpp:
.sbss start:0x8051EF20 end:0x8051EF28
.sdata2 start:0x80523BB0 end:0x80523C10
-revolution/hbm/HBMAnmController.cpp:
+revolution/homebuttonLib/HBMAnmController.cpp:
.text start:0x802BBEF4 end:0x802BC000
.data start:0x8040F218 end:0x8040F228
-revolution/hbm/HBMFrameController.cpp:
+revolution/homebuttonLib/HBMFrameController.cpp:
.text start:0x802BC000 end:0x802BC1A0
.sdata2 start:0x80523C10 end:0x80523C18
-revolution/hbm/HBMGUIManager.cpp:
+revolution/homebuttonLib/HBMGUIManager.cpp:
.text start:0x802BC1A0 end:0x802BD5D8
.data start:0x8040F228 end:0x8040F380
.sbss start:0x8051EF28 end:0x8051EF30
.sdata2 start:0x80523C18 end:0x80523C28
-revolution/hbm/HBMController.cpp:
+revolution/homebuttonLib/HBMController.cpp:
.text start:0x802BD5D8 end:0x802BDE30
.bss start:0x804A69F8 end:0x804A6B88
.sbss start:0x8051EF30 end:0x8051EF38
.sdata2 start:0x80523C28 end:0x80523C38
-revolution/hbm/HBMRemoteSpk.cpp:
+revolution/homebuttonLib/HBMRemoteSpk.cpp:
.text start:0x802BDE30 end:0x802BE58C
.data start:0x8040F380 end:0x8040F390
.sbss start:0x8051EF38 end:0x8051EF40
-revolution/db/db_DbgPrintBase.cpp:
+revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp:
.text start:0x802BE58C end:0x802BE5CC
-revolution/homebuttonLib/lyt_animation.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp:
.text start:0x802BE5CC end:0x802BF13C
.data start:0x8040F390 end:0x8040F3B0
.sdata2 start:0x80523C38 end:0x80523C58
-revolution/homebuttonLib/lyt_arcResourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp:
.text start:0x802BF13C end:0x802BF4B0
.data start:0x8040F3B0 end:0x8040F3C8
.sdata start:0x8051DFD0 end:0x8051DFD8
-revolution/homebuttonLib/lyt_bounding.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp:
.text start:0x802BF4B0 end:0x802BF5C8
.ctors start:0x803BA888 end:0x803BA88C
.data start:0x8040F3C8 end:0x8040F430
.sbss start:0x8051EF40 end:0x8051EF50
-revolution/homebuttonLib/lyt_common.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp:
.text start:0x802BF5C8 end:0x802C03C0
.bss start:0x804A6B88 end:0x804A6BA8
.sdata2 start:0x80523C58 end:0x80523C68
-revolution/homebuttonLib/lyt_drawInfo.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp:
.text start:0x802C03C0 end:0x802C0474
.data start:0x8040F430 end:0x8040F440
.sdata2 start:0x80523C68 end:0x80523C70
-revolution/homebuttonLib/lyt_group.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp:
.text start:0x802C0474 end:0x802C0794
.data start:0x8040F440 end:0x8040F450
-revolution/homebuttonLib/lyt_layout.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp:
.text start:0x802C0794 end:0x802C1404
.data start:0x8040F450 end:0x8040F488
.sbss start:0x8051EF50 end:0x8051EF58
.sdata2 start:0x80523C70 end:0x80523C78
-revolution/homebuttonLib/lyt_material.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp:
.text start:0x802C1404 end:0x802C47D8
.data start:0x8040F488 end:0x8040F4F0
.sdata2 start:0x80523C78 end:0x80523C98
.sbss2 start:0x80524A90 end:0x80524AA8
-revolution/homebuttonLib/lyt_pane.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp:
.text start:0x802C47D8 end:0x802C570C
.ctors start:0x803BA88C end:0x803BA890
.data start:0x8040F4F0 end:0x8040F558
.sbss start:0x8051EF58 end:0x8051EF60
.sdata2 start:0x80523C98 end:0x80523CB8
-revolution/homebuttonLib/lyt_picture.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp:
.text start:0x802C570C end:0x802C5C2C
.ctors start:0x803BA890 end:0x803BA894
.data start:0x8040F558 end:0x8040F5C8
.sbss start:0x8051EF60 end:0x8051EF68
.sdata2 start:0x80523CB8 end:0x80523CC0
-revolution/homebuttonLib/lyt_resourceAccessor.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp:
.text start:0x802C5C2C end:0x802C5C84
.data start:0x8040F5C8 end:0x8040F5E0
-revolution/homebuttonLib/lyt_textBox.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp:
.text start:0x802C5C84 end:0x802C72B4
.ctors start:0x803BA894 end:0x803BA898
.data start:0x8040F5E0 end:0x8040F658
@@ -2453,66 +2453,66 @@ revolution/homebuttonLib/lyt_textBox.cpp:
.sbss start:0x8051EF68 end:0x8051EF70
.sdata2 start:0x80523CC0 end:0x80523CD8
-revolution/homebuttonLib/lyt_window.cpp:
+revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp:
.text start:0x802C72B4 end:0x802C9730
.ctors start:0x803BA898 end:0x803BA89C
.data start:0x8040F658 end:0x8040F710
.sbss start:0x8051EF70 end:0x8051EF7C
.sdata2 start:0x80523CD8 end:0x80523CF0
-revolution/homebuttonLib/math_triangular.cpp:
+revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp:
.text start:0x802C9730 end:0x802C99A0
.data start:0x8040F710 end:0x80410828
.sdata2 start:0x80523CF0 end:0x80523D10
-revolution/homebuttonLib/snd_SoundArchivePlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp:
.text start:0x802C99A0 end:0x802C99B0
-revolution/homebuttonLib/snd_SoundHandle.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp:
.text start:0x802C99B0 end:0x802C9A00
-revolution/homebuttonLib/snd_SoundPlayer.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp:
.text start:0x802C9A00 end:0x802C9A70
-revolution/homebuttonLib/snd_SoundStartable.cpp:
+revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp:
.text start:0x802C9A70 end:0x802C9ADC
-revolution/homebuttonLib/ut_binaryFileFormat.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp:
.text start:0x802C9ADC end:0x802C9B50
-revolution/homebuttonLib/ut_CharStrmReader.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp:
.text start:0x802C9B50 end:0x802C9C60
-revolution/homebuttonLib/ut_CharWriter.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp:
.text start:0x802C9C60 end:0x802CB6E4
.bss start:0x804A6BA8 end:0x804A6BC8
.sbss start:0x8051EF7C end:0x8051EF80
.sdata2 start:0x80523D10 end:0x80523D30
-revolution/homebuttonLib/ut_Font.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp:
.text start:0x802CB6E4 end:0x802CB78C
.data start:0x80410828 end:0x80410858
-revolution/homebuttonLib/ut_LinkList.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp:
.text start:0x802CB78C end:0x802CB884
-revolution/homebuttonLib/ut_list.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp:
.text start:0x802CB884 end:0x802CB9E4
-revolution/homebuttonLib/ut_ResFont.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp:
.text start:0x802CB9E4 end:0x802CBCE8
.data start:0x80410858 end:0x804108B0
-revolution/homebuttonLib/ut_ResFontBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp:
.text start:0x802CBCE8 end:0x802CC2B8
.data start:0x804108B0 end:0x80410970
-revolution/homebuttonLib/ut_TagProcessorBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp:
.text start:0x802CC2B8 end:0x802CCA78
.data start:0x80410970 end:0x804109C8
.sdata2 start:0x80523D30 end:0x80523D38
-revolution/homebuttonLib/ut_TextWriterBase.cpp:
+revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp:
.text start:0x802CCA78 end:0x802CE4A4
.ctors start:0x803BA89C end:0x803BA8A0
.bss start:0x804A6BC8 end:0x804A6BE0
diff --git a/configure.py b/configure.py
index bdeded8988..e9eab046a4 100755
--- a/configure.py
+++ b/configure.py
@@ -496,6 +496,14 @@ def RevolutionLib(lib_name: str, objects: List[Object]) -> Dict[str, Any]:
"progress_category": "sdk",
"objects": objects,
}
+ elif config.version == "RZDE01_00":
+ return {
+ "lib": lib_name,
+ "mw_version": "GC/3.0a3",
+ "cflags": [*cflags_revolution_retail, "-DSDK_SEP2006", "-DNW4HBM_DEBUG"],
+ "progress_category": "sdk",
+ "objects": objects,
+ }
else:
return {
"lib": lib_name,
@@ -1615,6 +1623,25 @@ config.libs = [
],
),
RevolutionLib(
+ "card",
+ [
+ Object(NonMatching, "revolution/card/CARDBios.c"),
+ Object(NonMatching, "revolution/card/CARDUnlock.c"),
+ Object(NonMatching, "revolution/card/CARDRdwr.c"),
+ Object(NonMatching, "revolution/card/CARDBlock.c"),
+ Object(NonMatching, "revolution/card/CARDDir.c"),
+ Object(NonMatching, "revolution/card/CARDCheck.c"),
+ Object(NonMatching, "revolution/card/CARDMount.c"),
+ Object(NonMatching, "revolution/card/CARDFormat.c"),
+ Object(NonMatching, "revolution/card/CARDOpen.c", extra_cflags=["-char signed"]),
+ Object(NonMatching, "revolution/card/CARDCreate.c"),
+ Object(NonMatching, "revolution/card/CARDRead.c"),
+ Object(NonMatching, "revolution/card/CARDWrite.c"),
+ Object(NonMatching, "revolution/card/CARDStat.c"),
+ Object(NonMatching, "revolution/card/CARDNet.c"),
+ ],
+ ),
+ RevolutionLib(
"hio2",
[
Object(NonMatching, "revolution/hio2/hio2.c"),
@@ -1693,6 +1720,51 @@ config.libs = [
Object(NonMatching, "revolution/gd/GDGeometry.c"),
],
),
+ RevolutionLib(
+ "homebuttonLib",
+ [
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/db/db_assert.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/db/db_console.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp"),
+
+ Object(NonMatching, "revolution/homebuttonLib/HBMBase.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/HBMAnmController.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/HBMFrameController.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/HBMGUIManager.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/HBMController.cpp"),
+ Object(NonMatching, "revolution/homebuttonLib/HBMRemoteSpk.cpp"),
+ ],
+ ),
{
"lib": "Runtime.PPCEABI.H",
"mw_version": MWVersion(config.version),
diff --git a/include/dolphin/card.h b/include/dolphin/card.h
index 6ed175b71c..6487d88f43 100644
--- a/include/dolphin/card.h
+++ b/include/dolphin/card.h
@@ -1,6 +1,9 @@
#ifndef _DOLPHIN_CARD_H_
#define _DOLPHIN_CARD_H_
+#ifdef __REVOLUTION_SDK__
+#include <revolution/card.h>
+#else
#include <dolphin/os.h>
#include <dolphin/dsp.h>
#include <dolphin/dvd.h>
@@ -320,3 +323,4 @@ s32 CARDWrite(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset);
#endif
#endif
+#endif
diff --git a/include/global.h b/include/global.h
index b8bb5c02b2..f9057a67ae 100644
--- a/include/global.h
+++ b/include/global.h
@@ -45,6 +45,10 @@
#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
#define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
+#ifndef decltype
+#define decltype __decltype__
+#endif
+
#define JUT_EXPECT(...)
#define _SDA_BASE_(dummy) 0
@@ -77,6 +81,9 @@ void* __memcpy(void*, const void*, int);
#define SQUARE(x) ((x) * (x))
+#define POINTER_ADD_TYPE(type_, ptr_, offset_) ((type_)((unsigned long)(ptr_) + (unsigned long)(offset_)))
+#define POINTER_ADD(ptr_, offset_) POINTER_ADD_TYPE(__typeof__(ptr_), ptr_, offset_)
+
// floating-point constants
#define _HUGE_ENUF 1e+300
#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
@@ -91,6 +98,14 @@ static const float INF = 2000000000.0f;
#define READU32_BE(ptr, offset) \
(((u32)ptr[offset] << 24) | ((u32)ptr[offset + 1] << 16) | ((u32)ptr[offset + 2] << 8) | (u32)ptr[offset + 3]);
+#ifndef NO_INLINE
+#ifdef __MWERKS__
+#define NO_INLINE __attribute__((never_inline))
+#else
+#define NO_INLINE
+#endif
+#endif
+
// Hack to trick the compiler into not inlining functions that use this macro.
#define FORCE_DONT_INLINE \
(void*)0; (void*)0; (void*)0; (void*)0; (void*)0; (void*)0; (void*)0; (void*)0; (void*)0; (void*)0; \
diff --git a/include/m_Do/m_Do_printf.h b/include/m_Do/m_Do_printf.h
index 84d6c080b4..13ca5b0e01 100644
--- a/include/m_Do/m_Do_printf.h
+++ b/include/m_Do/m_Do_printf.h
@@ -1,7 +1,7 @@
#ifndef M_DO_M_DO_PRINTF_H
#define M_DO_M_DO_PRINTF_H
-#include "__va_arg.h"
+#include <dolphin/os.h>
void my_PutString(const char*);
void mDoPrintf_vprintf_Interrupt(char const*, va_list);
diff --git a/include/revolution/arc.h b/include/revolution/arc.h
new file mode 100644
index 0000000000..8e8b23b732
--- /dev/null
+++ b/include/revolution/arc.h
@@ -0,0 +1,67 @@
+#ifndef _REVOLUTION_ARC_H_
+#define _REVOLUTION_ARC_H_
+
+#include <revolution/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARC_ENTRY_NUM_INVALID (-1)
+
+typedef struct {
+ unsigned int magic;
+ int fstStart;
+ int fstSize;
+ int fileStart;
+ int pad[4];
+} ARCHeader;
+
+typedef struct {
+ void* archiveStartAddr; // 0x0
+ void* FSTStart; // 0x4
+ void* fileStart; // 0x8
+ u32 entryNum; // 0xC
+ char* FSTStringStart; // 0x10
+ u32 FSTLength; // 0x14
+ u32 currDir; // 0x18
+} ARCHandle;
+
+typedef struct {
+ ARCHandle* handle;
+ u32 startOffset;
+ u32 length;
+} ARCFileInfo;
+
+typedef struct {
+ ARCHandle* handle;
+ u32 entryNum;
+ u32 location;
+ u32 next;
+} ARCDir;
+
+typedef struct {
+ ARCHandle* handle;
+ u32 entryNum;
+ BOOL isDir;
+ char* name;
+} ARCDirEntry;
+
+BOOL ARCInitHandle(void*, ARCHandle*);
+BOOL ARCFastOpen(ARCHandle*, s32, ARCFileInfo*);
+s32 ARCConvertPathToEntrynum(ARCHandle*, const char*);
+void* ARCGetStartAddrInMem(ARCFileInfo*);
+u32 ARCGetLength(ARCFileInfo*);
+BOOL ARCClose(ARCFileInfo*);
+BOOL ARCChangeDir(ARCHandle*, const char*);
+BOOL ARCGetCurrentDir(ARCHandle*, char*, u32);
+
+BOOL ARCOpenDir(ARCHandle*, const char*, ARCDir*);
+BOOL ARCReadDir(ARCDir*, ARCDirEntry*);
+BOOL ARCCloseDir(ARCDir*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_ARC_H_
diff --git a/include/revolution/ax.h b/include/revolution/ax.h
index a1a85ddd87..37693da034 100644
--- a/include/revolution/ax.h
+++ b/include/revolution/ax.h
@@ -7,6 +7,16 @@
extern "C" {
#endif
+#define AX_SAMPLE_RATE 32000
+#define AX_SAMPLES_PER_FRAME 96
+#define AX_SAMPLE_DEPTH_BYTES sizeof(u32)
+#define AX_SAMPLES_PER_FRAME_RMT 18
+#define AX_FRAME_SIZE (AX_SAMPLES_PER_FRAME * AX_SAMPLE_DEPTH_BYTES)
+#define AX_MAX_VOLUME 32768
+
+#define AX_VOICE_STOP 0
+#define AX_VOICE_RUN 1
+
typedef struct _AXPBMIX {
/* 0x00 */ u16 vL;
/* 0x02 */ u16 vDeltaL;
@@ -210,7 +220,45 @@ typedef struct AX_AUX_DATA_DPL2 {
/* 0x00 */ s32* rs;
} AX_AUX_DATA_DPL2;
+typedef struct _AXPBRMTMIX {
+ /* 0x0 */ u16 vMain0;
+ /* 0x2 */ u16 vDeltaMain0;
+ /* 0x4 */ u16 vAux0;
+ /* 0x6 */ u16 vDeltaAux0;
+ /* 0x8 */ u16 vMain1;
+ /* 0xA */ u16 vDeltaMain1;
+ /* 0xC */ u16 vAux1;
+ /* 0xE */ u16 vDeltaAux1;
+ /* 0x10 */ u16 vMain2;
+ /* 0x12 */ u16 vDeltaMain2;
+ /* 0x14 */ u16 vAux2;
+ /* 0x16 */ u16 vDeltaAux2;
+ /* 0x18 */ u16 vMain3;
+ /* 0x1A */ u16 vDeltaMain3;
+ /* 0x1C */ u16 vAux3;
+ /* 0x1E */ u16 vDeltaAux3;
+} AXPBRMTMIX;
+
+typedef struct _AXPBBIQUAD {
+ /* 0x0 */ u16 on;
+ /* 0x2 */ u16 xn1;
+ /* 0x4 */ u16 xn2;
+ /* 0x6 */ u16 yn1;
+ /* 0x8 */ u16 yn2;
+ /* 0xA */ u16 b0;
+ /* 0xC */ u16 b1;
+ /* 0xE */ u16 b2;
+ /* 0x10 */ u16 a1;
+ /* 0x12 */ u16 a2;
+} AXPBBIQUAD;
+
+typedef union __AXPBRMTIIR {
+ AXPBLPF lpf;
+ AXPBBIQUAD biquad;
+} AXPBRMTIIR;
+
typedef void (*AXCallback)();
+typedef void (*AXAuxCallback)(void* chans, void* context);
#define AX_DSP_SLAVE_LENGTH 0xF80
#define AX_MAX_VOICES 64
@@ -268,7 +316,7 @@ AXVPB* AXAcquireVoice(u32 priority, void (*callback)(void *), u32 userContext);
void AXSetVoicePriority(AXVPB* p, u32 priority);
// AXAux
-void AXRegisterAuxACallback(void (*callback)(void*, void*), void* context);
+void AXRegisterAuxACallback(AXAuxCallback callback, void* context);
void AXRegisterAuxBCallback(void (*callback)(void*, void*), void* context);
// AXCL
diff --git a/include/revolution/axfx.h b/include/revolution/axfx.h
index 259e73e7b5..3c8b2a2d7e 100644
--- a/include/revolution/axfx.h
+++ b/include/revolution/axfx.h
@@ -2,6 +2,7 @@
#define _REVOLUTION_AXFX_H_
#include <revolution/types.h>
+#include <revolution/ax.h>
#ifdef __cplusplus
extern "C" {
@@ -158,6 +159,9 @@ typedef struct AXFX_CHORUS {
/* 0x98 */ u32 period;
} AXFX_CHORUS;
+typedef void* (*AXFXAllocFunc)(u32);
+typedef void (*AXFXFreeFunc)(void*);
+
// chorus
int AXFXChorusInit(AXFX_CHORUS* c);
int AXFXChorusShutdown(AXFX_CHORUS* c);
@@ -168,14 +172,13 @@ void AXFXChorusCallback(AXFX_BUFFERUPDATE* bufferUpdate, AXFX_CHORUS* chorus);
void AXFXDelayCallback(AXFX_BUFFERUPDATE* bufferUpdate, AXFX_DELAY* delay);
int AXFXDelaySettings(AXFX_DELAY* delay);
int AXFXDelayInit(AXFX_DELAY* delay);
-int AXFXDelayShutdown(AXFX_DELAY* delay);
+int AXFXDelayShutdown(AXFX_DELAY* delay);
// reverb_hi
void DoCrossTalk(s32* l, s32* r, f32 cross, f32 invcross);
int AXFXReverbHiInit(AXFX_REVERBHI* rev);
int AXFXReverbHiShutdown(AXFX_REVERBHI* rev);
int AXFXReverbHiSettings(AXFX_REVERBHI* rev);
-void AXFXReverbHiCallback(AXFX_BUFFERUPDATE* bufferUpdate, AXFX_REVERBHI* reverb);
// reverb_hi_4ch
int AXFXReverbHiInitDpl2(AXFX_REVERBHI_DPL2* reverb);
@@ -189,8 +192,23 @@ int AXFXReverbStdShutdown(AXFX_REVERBSTD* rev);
int AXFXReverbStdSettings(AXFX_REVERBSTD* rev);
void AXFXReverbStdCallback(AXFX_BUFFERUPDATE* bufferUpdate, AXFX_REVERBSTD* reverb);
+void AXFXReverbHiCallback(void *data, void *context);
+void AXGetAuxACallback(AXAuxCallback* cbOut, void** contextOut);
+void AXFXSetHooks(AXFXAllocFunc alloc, AXFXFreeFunc free);
+void AXFXGetHooks(AXFXAllocFunc* allocOut, AXFXFreeFunc* freeOut);
+BOOL AXFXReverbHiInit(AXFX_REVERBHI* reverbHi);
+BOOL AXFXReverbHiShutdown(AXFX_REVERBHI* reverbHi);
+u16 AXGetAuxAReturnVolume(void);
+u16 AXGetAuxBReturnVolume(void);
+u16 AXGetAuxCReturnVolume(void);
+void AXSetAuxAReturnVolume(u16 volume);
+void AXSetAuxBReturnVolume(u16 volume);
+void AXSetAuxCReturnVolume(u16 volume);
+void AXSetMasterVolume(u16 volume);
+u16 AXGetMasterVolume(void);
+
#ifdef __cplusplus
}
#endif
-#endif // _REVOLUTION_AXFX_H_
+#endif // _REVOLUTION_AXFX_H_
diff --git a/include/revolution/card.h b/include/revolution/card.h
new file mode 100644
index 0000000000..35c6e8b7d7
--- /dev/null
+++ b/include/revolution/card.h
@@ -0,0 +1,322 @@
+#ifndef _REVOLUTION_CARD_H_
+#define _REVOLUTION_CARD_H_
+
+#include <revolution/os.h>
+#include <revolution/dsp.h>
+#include <revolution/dvd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CARD_FILENAME_MAX 32
+#define CARD_MAX_FILE 127
+#define CARD_ICON_MAX 8
+
+typedef void (*CARDCallback)(s32 chan, s32 result);
+
+typedef struct CARDFileInfo {
+ s32 chan;
+ s32 fileNo;
+ s32 offset;
+ s32 length;
+ u16 iBlock;
+} CARDFileInfo;
+
+typedef struct CARDDir {
+ u8 gameName[4];
+ u8 company[2];
+ u8 _padding0;
+ u8 bannerFormat;
+ u8 fileName[CARD_FILENAME_MAX];
+ u32 time; // seconds since 01/01/2000 midnight
+ u32 iconAddr; // 0xffffffff if not used
+ u16 iconFormat;
+ u16 iconSpeed;
+ u8 permission;
+ u8 copyTimes;
+ u16 startBlock;
+ u16 length;
+ u8 _padding1[2];
+ u32 commentAddr; // 0xffffffff if not used
+} CARDDir;
+
+typedef struct CARDControl {
+ /* 0x000 */ BOOL attached;
+ /* 0x004 */ s32 result;
+ /* 0x008 */ u16 size;
+ /* 0x00A */ u16 pageSize;
+ /* 0x00C */ s32 sectorSize;
+ /* 0x010 */ u16 cBlock;
+ /* 0x012 */ u16 vendorID;
+ /* 0x014 */ s32 latency;
+ /* 0x018 */ u8 id[12];
+ /* 0x024 */ int mountStep;
+ /* 0x028 */ int formatStep;
+ /* 0x028 */ u32 scramble;
+ /* 0x02C */ DSPTaskInfo task;
+ /* 0x080 */ void* workArea;
+ /* 0x084 */ CARDDir *currentDir;
+ /* 0x088 */ u16* currentFat;
+ /* 0x08C */ OSThreadQueue threadQueue;
+ /* 0x094 */ u8 cmd[9];
+ /* 0x0A0 */ s32 cmdlen;
+ /* 0x0A4 */ volatile u32 mode;
+ /* 0x0A8 */ int retry;
+ /* 0x0AC */ int repeat;
+ /* 0x0B0 */ u32 addr;
+ /* 0x0B4 */ void* buffer;
+ /* 0x0B8 */ s32 xferred;
+ /* 0x0BC */ u16 freeNo;
+ /* 0x0BE */ u16 startBlock;
+ /* 0x0C0 */ CARDFileInfo* fileInfo;
+ /* 0x0C4 */ CARDCallback extCallback;
+ /* 0x0C8 */ CARDCallback txCallback;
+ /* 0x0CC */ CARDCallback exiCallback;
+ /* 0x0D0 */ CARDCallback apiCallback;
+ /* 0x0D4 */ CARDCallback xferCallback;
+ /* 0x0D8 */ CARDCallback eraseCallback;
+ /* 0x0DC */ CARDCallback unlockCallback;
+ /* 0x0E0 */ OSAlarm alarm;
+ /* 0x108 */ u32 cid;
+ /* 0x10C */ const DVDDiskID* diskID;
+} CARDControl;
+
+typedef struct CARDDecParam {
+ /* 0x00 */ u8* inputAddr;
+ /* 0x04 */ u32 inputLength;
+ /* 0x08 */ u32 aramAddr;
+ /* 0x0C */ u8* outputAddr;
+} CARDDecParam;
+
+typedef struct CARDID {
+ /* 0x000 */ u8 serial[32];
+ /* 0x020 */ u16 deviceID;
+ /* 0x022 */ u16 size;
+ /* 0x024 */ u16 encode;
+ /* 0x026 */ u8 padding[470];
+ /* 0x1FC */ u16 checkSum;
+ /* 0x1FE */ u16 checkSumInv;
+} CARDID;
+
+typedef struct CARDDirCheck {
+ /* 0x00 */ u8 padding0[56];
+ /* 0x38 */ u16 padding1;
+ /* 0x3A */ s16 checkCode;
+ /* 0x3C */ u16 checkSum;
+ /* 0x3E */ u16 checkSumInv;
+} CARDDirCheck;
+
+typedef struct CARDStat {
+ /* 0x00 */ char fileName[CARD_FILENAME_MAX];
+ /* 0x20 */ u32 length;
+ /* 0x24 */ u32 time;
+ /* 0x28 */ u8 gameName[4];
+ /* 0x2C */ u8 company[2];
+ /* 0x2E */ u8 bannerFormat;
+ /* 0x30 */ u32 iconAddr;
+ /* 0x34 */ u16 iconFormat;
+ /* 0x36 */ u16 iconSpeed;
+ /* 0x38 */ u32 commentAddr;
+ /* 0x3C */ u32 offsetBanner;
+ /* 0x40 */ u32 offsetBannerTlut;
+ /* 0x44 */ u32 offsetIcon[CARD_ICON_MAX];
+ /* 0x64 */ u32 offsetIconTlut;
+ /* 0x68 */ u32 offsetData;
+} CARDStat;
+
+#define CARD_ATTR_PUBLIC 0x04u
+#define CARD_ATTR_NO_COPY 0x08u
+#define CARD_ATTR_NO_MOVE 0x10u
+#define CARD_ATTR_GLOBAL 0x20u
+#define CARD_ATTR_COMPANY 0x40u
+
+#define CARD_FAT_AVAIL 0x0000u
+#define CARD_FAT_CHECKSUM 0x0000u
+#define CARD_FAT_CHECKSUMINV 0x0001u
+#define CARD_FAT_CHECKCODE 0x0002u
+#define CARD_FAT_FREEBLOCKS 0x0003u
+#define CARD_FAT_LASTSLOT 0x0004u
+
+#define CARD_WORKAREA_SIZE (5 * 8 * 1024)
+
+#define CARD_SEG_SIZE 0x200u
+#define CARD_PAGE_SIZE 0x80u
+#define CARD_MAX_SIZE 0x01000000U
+
+#define CARD_NUM_SYSTEM_BLOCK 5
+#define CARD_SYSTEM_BLOCK_SIZE (8 * 1024u)
+
+#define CARD_MAX_MOUNT_STEP (CARD_NUM_SYSTEM_BLOCK + 2)
+
+#define CARD_STAT_SPEED_END 0
+#define CARD_STAT_SPEED_FAST 1
+#define CARD_STAT_SPEED_MIDDLE 2
+#define CARD_STAT_SPEED_SLOW 3
+#define CARD_STAT_SPEED_MASK 3
+
+#define CARD_STAT_ANIM_LOOP 0
+#define CARD_STAT_ANIM_BOUNCE 4
+#define CARD_STAT_ANIM_MASK 0x4
+
+#define CARD_RESULT_UNLOCKED 1
+#define CARD_RESULT_READY 0
+#define CARD_RESULT_BUSY -1
+#define CARD_RESULT_WRONGDEVICE -2
+#define CARD_RESULT_NOCARD -3
+#define CARD_RESULT_NOFILE -4
+#define CARD_RESULT_IOERROR -5
+#define CARD_RESULT_BROKEN -6
+#define CARD_RESULT_EXIST -7
+#define CARD_RESULT_NOENT -8
+#define CARD_RESULT_INSSPACE -9
+#define CARD_RESULT_NOPERM -10
+#define CARD_RESULT_LIMIT -11
+#define CARD_RESULT_NAMETOOLONG -12
+#define CARD_RESULT_ENCODING -13
+#define CARD_RESULT_CANCELED -14
+#define CARD_RESULT_FATAL_ERROR -128
+
+#define CARDIsValidBlockNo(card, blockNo) ((blockNo) >= CARD_NUM_SYSTEM_BLOCK && (blockNo) < (card)->cBlock)
+
+#define CARD_READ_SIZE 512
+#define CARD_COMMENT_SIZE 64
+
+#define CARD_ICON_WIDTH 32
+#define CARD_ICON_HEIGHT 32
+
+#define CARD_BANNER_WIDTH 96
+#define CARD_BANNER_HEIGHT 32
+
+#define CARD_STAT_ICON_NONE 0
+#define CARD_STAT_ICON_C8 1
+#define CARD_STAT_ICON_RGB5A3 2
+#define CARD_STAT_ICON_MASK 3
+
+#define CARD_STAT_BANNER_NONE 0
+#define CARD_STAT_BANNER_C8 1
+#define CARD_STAT_BANNER_RGB5A3 2
+#define CARD_STAT_BANNER_MASK 3
+
+#define CARD_ENCODE_ANSI 0
+#define CARD_ENCODE_SJIS 1
+
+#define CARDGetDirCheck(dir) ((CARDDirCheck*)&(dir)[CARD_MAX_FILE])
+#define CARDGetBannerFormat(stat) (((stat)->bannerFormat) & CARD_STAT_BANNER_MASK)
+#define CARDGetIconAnim(stat) (((stat)->bannerFormat) & CARD_STAT_ANIM_MASK)
+#define CARDGetIconFormat(stat, n) (((stat)->iconFormat >> (2 * (n))) & CARD_STAT_ICON_MASK)
+#define CARDGetIconSpeed(stat, n) (((stat)->iconSpeed >> (2 * (n))) & CARD_STAT_SPEED_MASK)
+#define CARDSetBannerFormat(stat, f) \
+ ((stat)->bannerFormat = (u8)(((stat)->bannerFormat & ~CARD_STAT_BANNER_MASK) | (f)))
+#define CARDSetIconAnim(stat, f) \
+ ((stat)->bannerFormat = (u8)(((stat)->bannerFormat & ~CARD_STAT_ANIM_MASK) | (f)))
+#define CARDSetIconFormat(stat, n, f) \
+ ((stat)->iconFormat = \
+ (u16)(((stat)->iconFormat & ~(CARD_STAT_ICON_MASK << (2 * (n)))) | ((f) << (2 * (n)))))
+#define CARDSetIconSpeed(stat, n, f) \
+ ((stat)->iconSpeed = \
+ (u16)(((stat)->iconSpeed & ~(CARD_STAT_SPEED_MASK << (2 * (n)))) | ((f) << (2 * (n)))))
+#define CARDSetIconAddress(stat, addr) ((stat)->iconAddr = (u32)(addr))
+#define CARDSetCommentAddress(stat, addr) ((stat)->commentAddr = (u32)(addr))
+#define CARDGetFileNo(fileInfo) ((fileInfo)->fileNo)
+
+extern u32 __CARDFreq;
+
+#if DEBUG
+#define CARDFreq __CARDFreq
+#else
+#define CARDFreq EXI_FREQ_16M
+#endif
+
+void CARDInit(void);
+s32 CARDGetResultCode(s32 chan);
+s32 CARDCheckAsync(s32 chan, CARDCallback callback);
+s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed);
+s32 CARDRenameAsync(s32 chan, const char* oldName, const char* newName, CARDCallback callback);
+
+// CARDBios
+void CARDInit(void);
+s32 CARDGetResultCode(s32 chan);
+s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed);
+s32 CARDGetEncoding(s32 chan, u16* encode);
+s32 CARDGetMemSize(s32 chan, u16* size);
+s32 CARDGetSectorSize(s32 chan, u32* size);
+const DVDDiskID* CARDGetDiskID(s32 chan);
+s32 CARDSetDiskID(s32 chan, const DVDDiskID* diskID);
+BOOL CARDSetFastMode(BOOL enable);
+BOOL CARDGetFastMode(void);
+s32 CARDGetCurrentMode(s32 chan, u32* mode);
+
+// CARDCheck
+s32 CARDCheckExAsync(s32 chan, s32* xferBytes, CARDCallback callback);
+s32 CARDCheckAsync(s32 chan, CARDCallback callback);
+s32 CARDCheckEx(s32 chan, s32* xferBytes);
+s32 CARDCheck(s32 chan);
+
+// CARDCreate
+s32 CARDCreateAsync(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo, CARDCallback callback);
+s32 CARDCreate(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo);
+
+// CARDDelete
+s32 CARDFastDeleteAsync(s32 chan, s32 fileNo, CARDCallback callback);
+s32 CARDFastDelete(s32 chan, s32 fileNo);
+s32 CARDDeleteAsync(s32 chan, const char* fileName, CARDCallback callback);
+s32 CARDDelete(s32 chan, const char* fileName);
+
+// CARDErase
+s32 CARDEraseAsync(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDCallback callback);
+s32 CARDErase(CARDFileInfo* fileInfo, s32 length, s32 offset);
+
+// CARDFormat
+s32 CARDFormat(s32 chan);
+
+// CARDMount
+int CARDProbe(s32 chan);
+s32 CARDProbeEx(s32 chan, s32* memSize, s32* sectorSize);
+s32 CARDMountAsync(s32 chan, void* workArea, CARDCallback detachCallback, CARDCallback attachCallback);
+s32 CARDMount(s32 chan, void* workArea, CARDCallback detachCallback);
+s32 CARDUnmount(s32 chan);
+
+// CARDNet
+u16 CARDSetVendorID(u16 vendorID);
+u16 CARDGetVendorID();
+s32 CARDGetSerialNo(s32 chan, u64* serialNo);
+s32 CARDGetUniqueCode(s32 chan, u64* uniqueCode);
+s32 CARDGetAttributes(s32 chan, s32 fileNo, u8* attr);
+s32 CARDSetAttributesAsync(s32 chan, s32 fileNo, u8 attr, CARDCallback callback);
+s32 CARDSetAttributes(s32 chan, s32 fileNo, u8 attr);
+
+// CARDOpen
+s32 CARDFastOpen(s32 chan, s32 fileNo, CARDFileInfo* fileInfo);
+s32 CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo);
+s32 CARDClose(CARDFileInfo* fileInfo);
+
+// CARDProgram
+s32 CARDProgramAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback);
+s32 CARDProgram(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset);
+
+// CARDRdwr
+s32 CARDGetXferredBytes(s32 chan);
+
+// CARDRead
+s32 CARDReadAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback);
+s32 CARDRead(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset);
+s32 CARDCancel(CARDFileInfo* fileInfo);
+
+// CARDRename
+s32 CARDRename(s32 chan, const char* oldName, const char* newName);
+
+// CARDStat
+s32 CARDGetStatus(s32 chan, s32 fileNo, CARDStat* stat);
+s32 CARDSetStatusAsync(s32 chan, s32 fileNo, CARDStat* stat, CARDCallback callback);
+s32 CARDSetStatus(s32 chan, s32 fileNo, CARDStat* stat);
+
+// CARDWrite
+s32 CARDWriteAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback);
+s32 CARDWrite(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/revolution/hbm.h b/include/revolution/hbm.h
index ac3a34b0bf..f5fa0e28e7 100644
--- a/include/revolution/hbm.h
+++ b/include/revolution/hbm.h
@@ -2,10 +2,14 @@
#define _REVOLUTION_HBM_H_
#include <revolution/kpad.h>
+#include <revolution/mem.h>
#ifdef __cplusplus
extern "C" {
#endif
+
+#define NW4HBM_VERSION(major, minor) ((major & 0xFF) << 8 | minor & 0xFF)
+
typedef enum HBMSelectBtnNum {
HBM_SELECT_NULL = -1,
@@ -18,8 +22,45 @@ typedef enum HBMSelectBtnNum {
HBM_SELECT_MAX
} HBMSelectBtnNum;
-// TODO: move this later
-typedef struct MEMAllocator MEMAllocator;
+enum HBMSoundEvent_et {
+ HBM_SOUND_EVENT_0, // StartInitSound? and then num would not matter
+ HBM_SOUND_EVENT_1, // EndInitSound? and then num would not matter
+ HBM_SOUND_EVENT_2, // Fadeout, num = ms? see calc_fadeoutAnm
+ HBM_SOUND_EVENT_3, // Blackout, num = ms? from various
+ HBM_SOUND_EVENT_4, // ShutdownSound? and then num would not matter
+ HBM_SOUND_EVENT_PLAY, /* num = HBMSound_et (see below) */
+};
+
+enum HBMSound_et {
+ HBM_SOUND_HOME_BUTTON,
+ HBM_SOUND_RETURN_APP,
+ HBM_SOUND_GOTO_MENU,
+ HBM_SOUND_RESET_APP,
+ HBM_SOUND_FOCUS,
+ HBM_SOUND_SELECT,
+ HBM_SOUND_CANCEL,
+ HBM_SOUND_OPEN_CONTROLLER,
+ HBM_SOUND_CLOSE_CONTROLLER,
+ HBM_SOUND_VOLUME_PLUS,
+ HBM_SOUND_VOLUME_MINUS,
+ HBM_SOUND_VOLUME_PLUS_LIMIT,
+ HBM_SOUND_VOLUME_MINUS_LIMIT,
+ HBM_SOUND_NOTHING_DONE,
+ HBM_SOUND_VIBE_ON,
+ HBM_SOUND_VIBE_OFF,
+ HBM_SOUND_START_CONNECT_WINDOW,
+ HBM_SOUND_CONNECTED1,
+ HBM_SOUND_CONNECTED2,
+ HBM_SOUND_CONNECTED3,
+ HBM_SOUND_CONNECTED4,
+ HBM_SOUND_END_CONNECT_WINDOW,
+ HBM_SOUND_MANUAL_OPEN,
+ HBM_SOUND_MANUAL_FOCUS,
+ HBM_SOUND_MANUAL_SELECT,
+ HBM_SOUND_MANUAL_SCROLL,
+ HBM_SOUND_MANUAL_CANCEL,
+ HBM_SOUND_MANUAL_RETURN_APP
+};
typedef int HBMSoundCallback(int evt, int num);
typedef struct HBMDataInfo {
@@ -32,11 +73,11 @@ typedef struct HBMDataInfo {
/* 0x18 */ int backFlag;
/* 0x1C */ int region;
/* 0x20 */ int cursor;
- /* 0x24 */ u32 memSize;
- /* 0x28 */ f32 frameDelta;
- /* 0x2C */ Vec2 adjust;
- /* 0x34 */ MEMAllocator* pAllocator;
-} HBMDataInfo; // size 0x38
+ /* 0x28 */ u32 memSize;
+ /* 0x2C */ f32 frameDelta;
+ /* 0x30 */ Vec2 adjust;
+ /* 0x38 */ MEMAllocator* pAllocator;
+} HBMDataInfo; // size 0x3C
typedef struct HBMKPadData {
KPADStatus* kpad;
diff --git a/include/revolution/mem.h b/include/revolution/mem.h
new file mode 100644
index 0000000000..e93eb1a95c
--- /dev/null
+++ b/include/revolution/mem.h
@@ -0,0 +1,8 @@
+#ifndef _REVOLUTION_MEM_H_
+#define _REVOLUTION_MEM_H_
+
+#include <revolution/mem/allocator.h>
+#include <revolution/mem/expHeap.h>
+#include <revolution/mem/heapCommon.h>
+
+#endif // _REVOLUTION_MEM_H_
diff --git a/include/revolution/mem/allocator.h b/include/revolution/mem/allocator.h
new file mode 100644
index 0000000000..1f4e933fec
--- /dev/null
+++ b/include/revolution/mem/allocator.h
@@ -0,0 +1,37 @@
+#ifndef _REVOLUTION_MEM_ALLOCATOR_H_
+#define _REVOLUTION_MEM_ALLOCATOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <revolution/mem/heapCommon.h>
+#include <revolution/types.h>
+
+typedef struct MEMAllocator MEMAllocator;
+typedef void* (*MEMFuncAllocatorAlloc)(MEMAllocator* pAllocator, u32 size);
+typedef void (*MEMFuncAllocatorFree)(MEMAllocator* pAllocator, void* memBlock);
+typedef struct MEMAllocatorFunc MEMAllocatorFunc;
+
+struct MEMAllocatorFunc {
+ MEMFuncAllocatorAlloc pfAlloc;
+ MEMFuncAllocatorFree pfFree;
+};
+
+struct MEMAllocator {
+ const MEMAllocatorFunc* pFunc;
+ void* pHeap;
+ u32 heapParam1;
+ u32 heapParam2;
+};
+
+void* MEMAllocFromAllocator(MEMAllocator*, u32);
+void MEMFreeToAllocator(MEMAllocator*, void*);
+void MEMInitAllocatorForExpHeap(MEMAllocator*, MEMHeapHandle, int);
+void MEMInitAllocatorForFrmHeap(MEMAllocator* allocator, struct MEMiHeapHead* heap, s32 align);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_MEM_ALLOCATOR_H_
diff --git a/include/revolution/mem/expHeap.h b/include/revolution/mem/expHeap.h
new file mode 100644
index 0000000000..2d811ad2fa
--- /dev/null
+++ b/include/revolution/mem/expHeap.h
@@ -0,0 +1,50 @@
+#ifndef _REVOLUTION_MEM_EXPHEAP_H_
+#define _REVOLUTION_MEM_EXPHEAP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <revolution/mem/heapCommon.h>
+
+typedef struct MEMiExpHeapMBlockHead MEMiExpHeapMBlockHead;
+
+struct MEMiExPheapMBlockHead {
+ u16 signature;
+
+ union {
+ u16 val;
+
+ struct {
+ u16 allocDir : 1;
+ u16 alignment : 7;
+ u16 groupID : 8;
+ } fields;
+ } attribute;
+
+ u32 blockSize;
+ MEMiExpHeapMBlockHead* prev;
+ MEMiExpHeapMBlockHead* next;
+};
+
+MEMHeapHandle MEMCreateExpHeapEx(void*, u32, u16);
+void* MEMDestroyExpHeap(MEMHeapHandle);
+void* MEMAllocFromExpHeapEx(MEMHeapHandle, u32, int);
+void MEMFreeToExpHeap(MEMHeapHandle, void*);
+u32 MEMGetAllocatableSizeForExpHeapEx(MEMHeapHandle, int);
+MEMHeapHandle MEMCreateFrmHeapEx(void* start, u32 size, u16 opt);
+MEMHeapHandle MEMDestroyFrmHeap(MEMHeapHandle heap);
+
+static inline MEMHeapHandle MEMCreateExpHeap(void* start, u32 size) {
+ return MEMCreateExpHeapEx(start, size, 0);
+}
+
+static inline void* MEMAllocFromExpHeap(MEMHeapHandle heap, u32 size) {
+ return MEMAllocFromExpHeapEx(heap, size, 4);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_MEM_EXPHEAP_H_
diff --git a/include/revolution/mem/heapCommon.h b/include/revolution/mem/heapCommon.h
new file mode 100644
index 0000000000..5c156348a6
--- /dev/null
+++ b/include/revolution/mem/heapCommon.h
@@ -0,0 +1,43 @@
+#ifndef _REVOLUTION_MEM_HEAPCOMMON_H_
+#define _REVOLUTION_MEM_HEAPCOMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <revolution/mem/list.h>
+#include <revolution/os.h>
+
+typedef struct MEMiHeapHead MEMiHeapHead;
+
+struct MEMiHeapHead {
+ u32 signature;
+ MEMLink link;
+ MEMList childList;
+ void* heapStart;
+ void* heapEnd;
+ OSMutex mutex;
+
+ union {
+ u32 val;
+
+ struct {
+ u32 reserved : 24;
+ u32 optFlag : 8;
+ } fields;
+ } attribute;
+};
+
+typedef MEMiHeapHead* MEMHeapHandle;
+
+typedef u32 UIntPtr;
+
+static inline UIntPtr GetUIntPtr(const void* ptr) {
+ return (UIntPtr)(ptr);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_MEM_HEAPCOMMON_H_
diff --git a/include/revolution/mem/list.h b/include/revolution/mem/list.h
new file mode 100644
index 0000000000..9a713a6814
--- /dev/null
+++ b/include/revolution/mem/list.h
@@ -0,0 +1,31 @@
+#ifndef _REVOLUTION_MEM_LIST_H_
+#define _REVOLUTION_MEM_LIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <revolution/types.h>
+
+typedef struct {
+ void* prev;
+ void* next;
+} MEMLink;
+
+typedef struct {
+ void* head;
+ void* tail;
+ u16 num;
+ u16 offs;
+} MEMList;
+
+void MEMInitList(MEMList*, u16);
+void MEMAppendListObject(MEMList*, void*);
+void MEMRemoveListObject(MEMList*, void*);
+void* MEMGetNextListObject(MEMList*, void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_MEM_LIST_H_
diff --git a/include/revolution/mtx.h b/include/revolution/mtx.h
index b6687e0c52..38a4adebec 100644
--- a/include/revolution/mtx.h
+++ b/include/revolution/mtx.h
@@ -11,6 +11,8 @@ typedef struct Vec {
f32 x, y, z;
} Vec, *VecPtr, Point3d, *Point3dPtr;
+typedef const VecPtr CVecPtr;
+
typedef struct {
s16 x, y, z;
} S16Vec, *S16VecPtr;
diff --git a/include/revolution/os.h b/include/revolution/os.h
index 62603a97aa..c2b6248f20 100644
--- a/include/revolution/os.h
+++ b/include/revolution/os.h
@@ -320,46 +320,98 @@ extern BOOL __OSInReboot;
#define ASSERT(cond) ASSERTLINE(__LINE__, cond)
-inline s16 __OSf32tos16(__REGISTER f32 inF) {
-#ifdef __MWERKS__
- register s16 out;
- u32 tmp;
- register u32* tmpPtr = &tmp;
- // clang-format off
+static inline u8 __OSf32tou8(register f32 in) {
+ f32 a;
+ register f32* ptr = &a;
+ u8 r;
- asm {
- psq_st inF, 0(tmpPtr), 0x1, 5
- lha out, 0(tmpPtr)
- }
+#if defined(__MWERKS__)
+ asm { psq_st in, 0(ptr), 1, 2 };
+#else
+# pragma unused(in)
+#endif
- // clang-format on
- return out;
+ r = *(u8 *)ptr;
+
+ return r;
+}
+
+static inline u16 __OSf32tou16(register f32 in) {
+ f32 a;
+ register f32* ptr = &a;
+ u16 r;
+
+#if defined(__MWERKS__)
+ asm { psq_st in, 0(ptr), 1, 3 };
+#else
+# pragma unused(in)
#endif
+
+ r = *(u16 *)ptr;
+
+ return r;
}
-inline void OSf32tos16(f32* f, s16* out) {
- *out = __OSf32tos16(*f);
+static inline s16 __OSf32tos16(register f32 in) {
+ f32 a;
+ register f32* ptr = &a;
+ s16 r;
+
+#if defined(__MWERKS__)
+ asm { psq_st in, 0(ptr), 1, 5 };
+#else
+# pragma unused(in)
+#endif
+
+ r = *(s16*)ptr;
+
+ return r;
}
-inline u8 __OSf32tou8(__REGISTER f32 inF) {
-#ifdef __MWERKS__
- register u8 out;
- u32 tmp;
- register u32* tmpPtr = &tmp;
- // clang-format off
+static inline f32 __OSu16tof32(register u16 const* arg) {
+ register f32 ret;
- asm {
- psq_st inF, 0(tmpPtr), 0x1, 2
- lbz out, 0(tmpPtr)
- }
+#if defined(__MWERKS__)
+ asm { psq_l ret, 0(arg), 1, 3 };
+#else
+# pragma unused(arg)
+ ret = 0;
+#endif
- // clang-format on
- return out;
+ return ret;
+}
+
+static inline f32 __OSs16tof32(register s16 const* arg) {
+ register f32 ret;
+
+#if defined(__MWERKS__)
+ asm { psq_l ret, 0(arg), 1, 5 };
+#else
+# pragma unused(arg)
+ ret = 0;
#endif
+
+ return ret;
+}
+
+static inline void OSf32tou8(f32 const* in, u8* out) {
+ *out = __OSf32tou8(*in);
+}
+
+static inline void OSf32tou16(f32 const* in, u16* out) {
+ *out = __OSf32tou16(*in);
+}
+
+static inline void OSf32tos16(f32 const* in, s16* out) {
+ *out = __OSf32tos16(*in);
+}
+
+static inline void OSs16tof32(s16 const* in, f32* out) {
+ *out = __OSs16tof32(in);
}
-inline void OSf32tou8(f32* f, u8* out) {
- *out = __OSf32tou8(*f);
+static inline void OSu16tof32(u16 const* in, f32* out) {
+ *out = __OSu16tof32(in);
}
static inline void OSInitFastCast(void) {
diff --git a/include/revolution/tpl.h b/include/revolution/tpl.h
new file mode 100644
index 0000000000..aabc59d469
--- /dev/null
+++ b/include/revolution/tpl.h
@@ -0,0 +1,52 @@
+#ifndef _REVOLUTION_TPL_H_
+#define _REVOLUTION_TPL_H_
+
+#include <revolution/gx.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ u16 numEntries;
+ u8 unpacked;
+ u8 _4;
+ GXTlutFmt format;
+ char* data;
+} TPLClutHeader, *TPLClutHeaderPtr;
+
+typedef struct {
+ u16 height;
+ u16 width;
+ u32 format;
+ char* data;
+ GXTexWrapMode wrapS;
+ GXTexWrapMode wrapT;
+ GXTexFilter minFilter;
+ GXTexFilter magFilter;
+ f32 LODBias;
+ u8 edgeLODEnable;
+ u8 minLOD;
+ u8 maxLOD;
+ u8 unpacked;
+} TPLHeader, *TPLHeaderPtr;
+
+typedef struct {
+ TPLHeaderPtr textureHeader;
+ TPLClutHeaderPtr CLUTHeader;
+} TPLDescriptor, *TPLDescriptorPtr;
+
+typedef struct {
+ u32 versionNumber;
+ u32 numDescriptors;
+ TPLDescriptorPtr descriptorArray;
+} TPLPalette, *TPLPalettePtr;
+
+void TPLBind(TPLPalettePtr);
+TPLDescriptorPtr TPLGet(TPLPalettePtr, u32);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_TPL_H_
diff --git a/include/revolution/wenc.h b/include/revolution/wenc.h
index 39b9180977..d3c8117c09 100644
--- a/include/revolution/wenc.h
+++ b/include/revolution/wenc.h
@@ -11,15 +11,9 @@ typedef enum {
WENC_FLAG_USER_INFO = (1 << 0),
} WENCFlag;
-typedef struct WENCInfo {
- /* 0x00 */ s32 xn;
- /* 0x04 */ s32 dl;
- /* 0x08 */ s32 qn;
- /* 0x0C */ s32 dn;
- /* 0x10 */ s32 dlh;
- /* 0x14 */ s32 dlq;
- /* 0x18 */ u8 padding[8];
-} WENCInfo;
+typedef struct {
+ u8 data[32];
+} WENCInfo; // size 0x20
s32 WENCGetEncodeData(WENCInfo* info, u32 flag, const s16* pcmData, s32 samples, u8* adpcmData);
diff --git a/include/revolution/wpad.h b/include/revolution/wpad.h
index 2e7456e3e6..82f259b95b 100644
--- a/include/revolution/wpad.h
+++ b/include/revolution/wpad.h
@@ -2,6 +2,7 @@
#define _REVOLUTION_WPAD_H_
#include <revolution/types.h>
+#include <revolution/sc.h>
#ifdef __cplusplus
extern "C" {
@@ -21,6 +22,22 @@ extern "C" {
#define WPAD_BUTTON_C 0x4000
#define WPAD_BUTTON_HOME 0x8000
+#define WPAD_BUTTON_CL_UP (1 << 0)
+#define WPAD_BUTTON_CL_LEFT (1 << 1)
+#define WPAD_BUTTON_CL_ZR (1 << 2)
+#define WPAD_BUTTON_CL_X (1 << 3)
+#define WPAD_BUTTON_CL_A (1 << 4)
+#define WPAD_BUTTON_CL_Y (1 << 5)
+#define WPAD_BUTTON_CL_B (1 << 6)
+#define WPAD_BUTTON_CL_ZL (1 << 7)
+#define WPAD_BUTTON_CL_FULL_R (1 << 9)
+#define WPAD_BUTTON_CL_PLUS (1 << 10)
+#define WPAD_BUTTON_CL_HOME (1 << 11)
+#define WPAD_BUTTON_CL_MINUS (1 << 12)
+#define WPAD_BUTTON_CL_FULL_L (1 << 13)
+#define WPAD_BUTTON_CL_DOWN (1 << 14)
+#define WPAD_BUTTON_CL_RIGHT (1 << 15)
+
#define WPAD_MAX_DPD_OBJECTS 4
#define WPAD_CHAN0 0
@@ -62,11 +79,44 @@ enum WPADResult_et {
#define WPAD_CE6 (WPAD_E6 + 0)
#define WPAD_CEBADE (WPAD_EBADE + 0)
-typedef void WPADInitFunc(void);
-typedef void WPADCallback(s32 chan, s32 result);
-typedef void WPADExtensionCallback(s32 chan, s32 devType);
-typedef void WPADSamplingCallback(s32 chan);
-typedef void WPADConnectCallback(s32 chan, s32 result);
+#define WPAD_DEV_CORE 0
+#define WPAD_DEV_FS 1
+#define WPAD_DEV_CLASSIC 2
+#define WPAD_DEV_BALANCE_CHECKER 3
+#define WPAD_DEV_VSM 4
+#define WPAD_DEV_MOTION_PLUS 5
+#define WPAD_DEV_MPLS_PT_FS 6
+#define WPAD_DEV_MPLS_PT_CLASSIC 7
+
+#define WPAD_DEV_TRAIN 16
+#define WPAD_DEV_GUITAR 17
+#define WPAD_DEV_DRUM 18
+#define WPAD_DEV_TAIKO 19
+#define WPAD_DEV_TURNTABLE 20
+
+// seems to be like maybe general purpose non-specific device types
+// maybe this was for testing or something? idk
+#define WPAD_DEV_BULK_1 21
+#define WPAD_DEV_BULK_2 22
+#define WPAD_DEV_BULK_3 23
+#define WPAD_DEV_BULK_4 24
+#define WPAD_DEV_BULK_5 25
+#define WPAD_DEV_BULK_6 26
+#define WPAD_DEV_BULK_7 27
+#define WPAD_DEV_BULK_8 28
+
+#define WPAD_DEV_MPLS_PT_UNKNOWN 250
+#define WPAD_DEV_251 251
+#define WPAD_DEV_252 252 // invalid device mode?
+#define WPAD_DEV_NONE 253 // sort of like WPAD_ENODEV (see __wpadAbortInitExtension in WPADHIDParser.c)
+#define WPAD_DEV_INITIALIZING 255 // see __a1_20_status_report
+
+typedef void (*WPADInitFunc)(void);
+typedef void (*WPADCallback)(s32 chan, s32 result);
+typedef void (*WPADExtensionCallback)(s32 chan, s32 devType);
+typedef void (*WPADSamplingCallback)(s32 chan);
+typedef void (*WPADConnectCallback)(s32 chan, s32 result);
+typedef void (*WPADSimpleSyncCallback)(s32 result, s32 num);
typedef struct DPDObject {
/* 0x00 */ s16 x;
@@ -264,14 +314,33 @@ typedef struct WPADInfo {
#define WPAD_SPEAKER_PLAY 4
#define WPAD_SPEAKER_CMD_05 5 // does the same thing as ENABLE? unless i'm missing something. not used so i don't know the context
+#define WPAD_MOTOR_STOP 0
+#define WPAD_MOTOR_RUMBLE 1
+
s32 WPADProbe(s32 chan, u32* devType);
u8 WPADGetRadioSensitivity(s32 chan);
void WPADRead(s32 chan, WPADStatus* status);
BOOL WPADIsSpeakerEnabled(s32 chan);
-s32 WPADControlSpeaker(s32 chan, u32 command, WPADCallback* cb);
+s32 WPADControlSpeaker(s32 chan, u32 command, WPADCallback cb);
s32 WPADSendStreamData(s32 chan, void* p_buf, u16 len);
-WPADExtensionCallback* WPADSetExtensionCallback(s32 chan, WPADExtensionCallback* cb);
+WPADConnectCallback WPADSetConnectCallback(s32 chan, WPADConnectCallback cb);
+WPADExtensionCallback WPADSetExtensionCallback(s32 chan, WPADExtensionCallback cb);
+WPADSimpleSyncCallback WPADSetSimpleSyncCallback(WPADSimpleSyncCallback cb);
+
+BOOL WPADIsUsedCallbackByKPAD(void);
+void WPADSetCallbackByKPAD(BOOL isKPAD);
+s32 WPADGetInfoAsync(s32 chan, WPADInfo* info, WPADCallback cb);
+void WPADControlMotor(s32 chan, u32 command);
+BOOL WPADCanSendStreamData(s32 chan);
+BOOL WPADStopSimpleSync(void);
+void WPADDisconnect(s32 chan);
+BOOL WPADStartFastSimpleSync(void);
+BOOL WPADSaveConfig(SCFlushCallback* cb);
+void WPADSetSpeakerVolume(u8 volume);
+u8 WPADGetSpeakerVolume(void);
+void WPADEnableMotor(BOOL enabled);
+BOOL WPADIsMotorEnabled(void);
#ifdef __cplusplus
}
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C++/MSL_Common/Include/new.h b/src/PowerPC_EABI_Support/MSL/MSL_C++/MSL_Common/Include/new.h
new file mode 100644
index 0000000000..62446b959e
--- /dev/null
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C++/MSL_Common/Include/new.h
@@ -0,0 +1,8 @@
+#ifndef STD_NEW_H_
+#define STD_NEW_H_
+
+#include <stdio.h>
+
+inline void* (operator new)(size_t, void *ptr) { return ptr; }
+
+#endif
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cmath.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cmath.h
index 93524cc491..419e57fec5 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cmath.h
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cmath.h
@@ -39,6 +39,14 @@ inline float tan(float num) {
return ::i_tanf(num);
}
+inline float tanf(float num) {
+ return ::i_tanf(num);
+}
+
+inline float acos(float num) {
+ return ::acosf(num);
+}
+
inline float pow(float x, float y) {
return ::pow(x, y);
}
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h
index b6e8aec6b3..39088020e5 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h
@@ -6,6 +6,14 @@
namespace std {
using ::strlen;
using ::strcpy;
+using ::wcslen;
+using ::strncpy;
+using ::strcmp;
+using ::strncmp;
+using ::strcat;
+
+using ::memset;
+using ::memcpy;
inline char* strchr(char* str, int c) {
return ::strchr(str, c);
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/printf.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/printf.h
index 39e1c83f04..ea14c07a35 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/printf.h
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/printf.h
@@ -15,8 +15,17 @@ int snprintf(char* s, size_t n, const char* format, ...);
int vsnprintf(char* s, size_t n, const char* format, va_list arg);
int vprintf(const char* format, va_list arg);
+int vswprintf(wchar_t* s, size_t n, const wchar_t* format, va_list arg);
+
+#if defined(__cplusplus)
+namespace std {
+ extern "C" { using ::vsnprintf; }
+ extern "C" { using ::vswprintf; }
+}
+#endif
+
#ifdef __cplusplus
}
#endif
-#endif /* _MSL_COMMON_PRINTF_H */ \ No newline at end of file
+#endif /* _MSL_COMMON_PRINTF_H */
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
index 11453c10ca..311cd44fb2 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
@@ -2,6 +2,7 @@
#define _MSL_COMMON_STRING_H
#include "stddef.h"
+#include "ansi_files.h"
#ifdef __cplusplus
extern "C" {
@@ -22,6 +23,8 @@ char* strncpy(char* dst, const char* src, size_t n);
char* strcpy(char* dst, const char* src);
size_t strlen(const char* str);
+size_t wcslen(const wchar_t* s);
+
#ifdef __cplusplus
};
#endif
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Src/printf.c b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Src/printf.c
index 99f31c8a63..183eb71775 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Src/printf.c
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Src/printf.c
@@ -1000,7 +1000,7 @@ static int __pformatter(void* (*WriteProc)(void*, const char*, size_t), void* Wr
if (format.argument_options == long_double_argument) {
long_double_num = va_arg(arg, long double);
} else {
- long_double_num = va_arg(arg, f64);
+ long_double_num = va_arg(arg, double);
}
if (!(buff_ptr = float2str(long_double_num, buff + 512, format))) {
@@ -1015,7 +1015,7 @@ static int __pformatter(void* (*WriteProc)(void*, const char*, size_t), void* Wr
if (format.argument_options == long_double_argument) {
long_double_num = va_arg(arg, long double);
} else {
- long_double_num = va_arg(arg, f64);
+ long_double_num = va_arg(arg, double);
}
if (!(buff_ptr = double2hex(long_double_num, buff + 512, format))) {
diff --git a/src/PowerPC_EABI_Support/Runtime/Inc/__va_arg.h b/src/PowerPC_EABI_Support/Runtime/Inc/__va_arg.h
index 2297564021..4867f2d25a 100644
--- a/src/PowerPC_EABI_Support/Runtime/Inc/__va_arg.h
+++ b/src/PowerPC_EABI_Support/Runtime/Inc/__va_arg.h
@@ -1,7 +1,9 @@
#ifndef __VA_ARG_H
#define __VA_ARG_H
-#include "dolphin/types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
typedef struct __va_list_struct {
char gpr;
@@ -13,11 +15,7 @@ typedef struct __va_list_struct {
typedef _va_list_struct __va_list[1];
-#ifdef __cplusplus
-extern "C" void* __va_arg(_va_list_struct*, int);
-#else
void* __va_arg(_va_list_struct*, int);
-#endif
#ifndef __MWERKS__
#define __builtin_va_info(...)
@@ -37,4 +35,12 @@ void* __va_arg(_va_list_struct*, int);
#define __va_copy(a, b) (*(a) = *(b))
+#if defined(__cplusplus)
+namespace std { extern "C" { using ::va_list; } }
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __VA_ARG_H */
diff --git a/src/Z2AudioLib/Z2SoundMgr.cpp b/src/Z2AudioLib/Z2SoundMgr.cpp
index ef0f515829..add152850c 100644
--- a/src/Z2AudioLib/Z2SoundMgr.cpp
+++ b/src/Z2AudioLib/Z2SoundMgr.cpp
@@ -5,12 +5,15 @@
#include "Z2AudioLib/Z2SeqMgr.h"
#include "Z2AudioLib/Z2SeMgr.h"
#include "Z2AudioLib/Z2SoundInfo.h"
-#include "Z2AudioLib/Z2AudioCS.h"
#include "JSystem/JAudio2/JASCalc.h"
#include "JSystem/JAudio2/JASDriverIF.h"
#include "JSystem/JAudio2/JAUSectionHeap.h"
#include "d/d_com_inf_game.h"
+#if PLATFORM_WII || PLATFORM_SHIELD
+#include "Z2AudioLib/Z2AudioCS.h"
+#endif
+
u16 seqCallback(JASTrack* track, u16 command) {
switch (command) {
case 0x1000:
diff --git a/src/revolution/card/CARDBios.c b/src/revolution/card/CARDBios.c
new file mode 100644
index 0000000000..3bd2aede7f
--- /dev/null
+++ b/src/revolution/card/CARDBios.c
@@ -0,0 +1,849 @@
+#include <revolution/card.h>
+
+#include "__card.h"
+
+const char* __CARDVersion = "<< RVL_SDK - CARD \trelease build: Sep 7 2006 18:26:19 (0x4200_60422) >>";
+
+CARDControl __CARDBlock[2];
+
+static u16 __CARDEncode;
+static u16 __CARDFastMode;
+
+DVDDiskID __CARDDiskNone;
+
+// prototypes
+static void TimeoutHandler(OSAlarm* alarm, OSContext* context);
+static void SetupTimeoutAlarm(CARDControl* card);
+static s32 Retry(s32 chan);
+static void UnlockedCallback(s32 chan, s32 result);
+static BOOL OnShutdown(BOOL f, u32 event);
+
+static OSShutdownFunctionInfo ShutdownFunctionInfo = {OnShutdown, 127};
+
+void __CARDDefaultApiCallback(s32 chan, s32 result) {}
+
+void __CARDSyncCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+ OSWakeupThread(&card->threadQueue);
+}
+
+void __CARDExtHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ ASSERTLINE(232, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (card->attached) {
+ ASSERTLINE(239, card->txCallback == 0);
+ card->attached = FALSE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+ callback = card->exiCallback;
+
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+
+ if (card->result != CARD_RESULT_BUSY) {
+ card->result = CARD_RESULT_NOCARD;
+ }
+
+ callback = card->extCallback;
+ if (callback && CARD_MAX_MOUNT_STEP <= card->mountStep) {
+ card->extCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+ }
+}
+
+void __CARDExiHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ u8 status;
+ s32 result;
+
+ ASSERTLINE(283, 0 <= chan && chan < 2);
+ card = &__CARDBlock[chan];
+
+ OSCancelAlarm(&card->alarm);
+
+ if (!card->attached) {
+ return;
+ }
+
+ if (!EXILock(chan, 0, 0)) {
+ result = CARD_RESULT_FATAL_ERROR;
+ goto fatal;
+ }
+
+ if ((result = __CARDReadStatus(chan, &status)) < 0 || (result = __CARDClearStatus(chan)) < 0) {
+ goto error;
+ }
+
+ if ((result = (status & 0x18) ? CARD_RESULT_IOERROR : CARD_RESULT_READY) == CARD_RESULT_IOERROR &&
+ --card->retry > 0)
+ {
+ result = Retry(chan);
+ if (result >= 0)
+ {
+ return;
+ }
+ goto fatal;
+ }
+
+error:
+ EXIUnlock(chan);
+
+fatal:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+}
+
+void __CARDTxHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ int err;
+
+ ASSERTLINE(365, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ err = !EXIDeselect(chan);
+ EXIUnlock(chan);
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = NULL;
+ callback(chan, (!err && EXIProbe(chan)) ? CARD_RESULT_READY : CARD_RESULT_NOCARD);
+ }
+}
+
+void __CARDUnlockedHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ ASSERTLINE(412, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ callback = card->unlockCallback;
+ if (callback) {
+ card->unlockCallback = 0;
+ callback(chan, EXIProbe(chan) ? CARD_RESULT_UNLOCKED : CARD_RESULT_NOCARD);
+ }
+}
+
+s32 __CARDEnableInterrupt(s32 chan, BOOL enable) {
+ BOOL err;
+ u32 cmd;
+
+ ASSERTLINE(431, 0 <= chan && chan < 2);
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = enable ? 0x81010000 : 0x81000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDReadStatus(s32 chan, u8* status) {
+ BOOL err;
+ u32 cmd;
+
+ ASSERTLINE(450, 0 <= chan && chan < 2);
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x83000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, status, 1, EXI_READ, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+int __CARDReadVendorID(s32 chan, u16* id) {
+ BOOL err;
+ u32 cmd;
+
+ ASSERTLINE(471, 0 <= chan && chan < 2);
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+ cmd = 0x85000000;
+ err = 0;
+ err |= !EXIImm(chan, &cmd, 2, EXI_WRITE, 0);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, id, 2, EXI_READ, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDClearStatus(s32 chan) {
+ BOOL err;
+ u32 cmd;
+
+ ASSERTLINE(492, 0 <= chan && chan < 2);
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x89000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 1, EXI_WRITE, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDSleep(s32 chan) {
+ int err;
+ u32 cmd;
+
+ ASSERTLINE(511, 0 <= chan && chan < 2);
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+ cmd = 0x88000000;
+ err = 0;
+ err |= !EXIImm(chan, &cmd, 1, EXI_WRITE, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+
+ if(err) {
+ return CARD_RESULT_NOCARD;
+ }
+ return CARD_RESULT_READY;
+}
+
+s32 __CARDWakeup(s32 chan) {
+ int err;
+ u32 cmd;
+
+ ASSERTLINE(530, 0 <= chan && chan < 2);
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ return CARD_RESULT_NOCARD;
+ }
+ cmd = 0x87000000;
+ err = 0;
+ err |= !EXIImm(chan, &cmd, 1, EXI_WRITE, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+
+ if(err) {
+ return CARD_RESULT_NOCARD;
+ }
+ return CARD_RESULT_READY;
+}
+
+static void TimeoutHandler(OSAlarm* alarm, OSContext* context) {
+ s32 chan;
+ CARDControl* card;
+ CARDCallback callback;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if (alarm == &card->alarm) {
+ break;
+ }
+ }
+
+ ASSERTLINE(578, 0 <= chan && chan < 2);
+
+ if (!card->attached) {
+ return;
+ }
+
+ EXISetExiCallback(chan, NULL);
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_IOERROR);
+ }
+}
+
+static void SetupTimeoutAlarm(CARDControl* card) {
+ OSCancelAlarm(&card->alarm);
+ switch (card->cmd[0]) {
+ case 0xF2:
+ OSSetAlarm(&card->alarm, OSMillisecondsToTicks(100),
+ TimeoutHandler);
+ break;
+ case 0xF3:
+ break;
+ case 0xF4:
+ if (card->pageSize > 0x80) {
+ OSSetAlarm(&card->alarm, OSSecondsToTicks((OSTime)2) * (card->cBlock / 0x40),
+ TimeoutHandler);
+ break;
+ }
+ case 0xF1:
+ OSSetAlarm(&card->alarm, OSSecondsToTicks((OSTime)2) * (card->sectorSize / 0x2000),
+ TimeoutHandler);
+ break;
+ }
+}
+
+static s32 Retry(s32 chan) {
+ CARDControl* card;
+
+ ASSERTLINE(654, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ SetupTimeoutAlarm(card);
+
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, EXI_WRITE)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->cmd[0] == 0x52 &&
+ !EXIImmEx(chan, (u8* )card->workArea + sizeof(CARDID), card->latency, EXI_WRITE))
+ {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->mode == 0xffffffff) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_READY;
+ }
+
+ if (!EXIDma(chan, card->buffer, (s32)((card->cmd[0] == 0x52) ? 512 : card->pageSize), card->mode,
+ __CARDTxHandler))
+ {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ return CARD_RESULT_READY;
+}
+
+static void UnlockedCallback(s32 chan, s32 result) {
+ CARDCallback callback;
+ CARDControl* card;
+
+ ASSERTLINE(718, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ card->unlockCallback = UnlockedCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_READY;
+ } else {
+ card->unlockCallback = 0;
+ result = Retry(chan);
+ }
+ }
+
+ if (result < 0) {
+ switch (card->cmd[0]) {
+ case 0x52:
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = NULL;
+ callback(chan, result);
+ }
+ break;
+ case 0xF2:
+ case 0xF4:
+ case 0xF1:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+ break;
+ }
+ }
+}
+
+static s32 __CARDStart(s32 chan, CARDCallback txCallback, CARDCallback exiCallback) {
+ BOOL enabled;
+ CARDControl* card;
+ s32 result;
+
+ enabled = OSDisableInterrupts();
+
+ ASSERTLINE(784, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else {
+ if (txCallback) {
+ card->txCallback = txCallback;
+ }
+
+ if (exiCallback) {
+ card->exiCallback = exiCallback;
+ }
+
+ card->unlockCallback = UnlockedCallback;
+
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->unlockCallback = 0;
+
+ if (!EXISelect(chan, 0, CARDFreq)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ SetupTimeoutAlarm(card);
+ result = CARD_RESULT_READY;
+ }
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+#define AD1(x) ((u8)(((x) >> 17) & 0x7f))
+#define AD1EX(x) ((u8)(AD1(x) | 0x80));
+#define AD2(x) ((u8)(((x) >> 9) & 0xff))
+#define AD3(x) ((u8)(((x) >> 7) & 0x03))
+#define BA(x) ((u8)((x)&0x7f))
+
+s32 __CARDReadSegment(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(846, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ ASSERTLINE(848, card->addr % CARD_SEG_SIZE == 0);
+ ASSERTLINE(849, card->addr < (u32) card->size * 1024 * 1024 / 8);
+
+ card->cmd[0] = 0x52;
+ card->cmd[1] = AD1(card->addr);
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 0;
+ card->retry = 0;
+
+ result = __CARDStart(chan, callback, 0);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, EXI_WRITE) ||
+ !EXIImmEx(chan, (u8* )card->workArea + sizeof(CARDID), card->latency,
+ EXI_WRITE) || // XXX use DMA if possible
+ !EXIDma(chan, card->buffer, 512, card->mode, __CARDTxHandler))
+ {
+ card->txCallback = NULL;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+
+ return result;
+}
+
+s32 __CARDWritePage(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(903, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ ASSERTLINE(905, card->addr % card->pageSize == 0);
+ ASSERTLINE(906, card->addr < (u32) card->size * 1024 * 1024 / 8);
+ card->cmd[0] = 0xF2;
+
+ if (card->pageSize > 0x80) {
+ card->cmd[1] = AD1(card->addr) | 0x80;
+ } else {
+ card->cmd[1] = AD1(card->addr);
+ }
+
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, EXI_WRITE) ||
+ !EXIDma(chan, card->buffer, card->pageSize, card->mode, __CARDTxHandler))
+ {
+ card->exiCallback = 0;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+
+ return result;
+}
+
+s32 __CARDErase(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(962, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0xF4;
+ card->cmd[1] = 0;
+ card->cmd[2] = 0;
+ card->cmdlen = 3;
+ card->mode = -1;
+ card->retry = 3;
+ result = __CARDStart(chan, 0, callback);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (EXIImmEx(chan, &card->cmd, card->cmdlen, EXI_WRITE) == 0) {
+ result = CARD_RESULT_NOCARD;
+ card->exiCallback = 0;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ }
+
+ return result;
+}
+
+s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(1010, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ ASSERTLINE(1012, addr % card->sectorSize == 0);
+ ASSERTLINE(1013, addr < (u32) card->size * 1024 * 1024 / 8);
+
+ if (card->pageSize > 0x80) {
+ if (callback) {
+ callback(chan, 0);
+ }
+ return 0;
+ }
+
+ card->cmd[0] = 0xF1;
+ card->cmd[1] = AD1(addr);
+ card->cmd[2] = AD2(addr);
+ card->cmdlen = 3;
+ card->mode = -1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, EXI_WRITE)) {
+ result = CARD_RESULT_NOCARD;
+ card->exiCallback = NULL;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ }
+ return result;
+}
+
+void CARDInit(void) {
+ int chan;
+
+ if (__CARDBlock[0].diskID && __CARDBlock[1].diskID) {
+ return;
+ }
+
+ __CARDEncode = OSGetFontEncode();
+
+ OSRegisterVersion(__CARDVersion);
+
+ DSPInit();
+
+ for (chan = 0; chan < 2; ++chan) {
+ CARDControl* card = &__CARDBlock[chan];
+
+ card->result = CARD_RESULT_NOCARD;
+ OSInitThreadQueue(&card->threadQueue);
+ OSCreateAlarm(&card->alarm);
+ }
+ __CARDSetDiskID((void*)OSPhysicalToCached(0));
+
+ OSRegisterShutdownFunction(&ShutdownFunctionInfo);
+}
+
+u16 __CARDGetFontEncode(void) {
+ return __CARDEncode;
+}
+
+u16 __CARDSetFontEncode(u16 encode) {
+ u16 prev = __CARDEncode;
+
+ switch (encode) {
+ case CARD_ENCODE_ANSI:
+ case CARD_ENCODE_SJIS:
+ __CARDEncode = encode;
+ break;
+ }
+
+ return prev;
+}
+
+void __CARDSetDiskID(const DVDDiskID* id) {
+ __CARDBlock[0].diskID = id ? id : &__CARDDiskNone;
+ __CARDBlock[1].diskID = id ? id : &__CARDDiskNone;
+}
+
+const DVDDiskID* CARDGetDiskID(s32 chan) {
+ ASSERTLINE(1168, 0 <= chan && chan < 2);
+ return __CARDBlock[chan].diskID;
+}
+
+s32 CARDSetDiskID(s32 chan, const DVDDiskID* diskID) {
+ BOOL enabled;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ ASSERTLINE(1189, 0 <= chan && chan < 2);
+
+ enabled = OSDisableInterrupts();
+
+ if (card->result == CARD_RESULT_BUSY) {
+ return CARD_RESULT_BUSY;
+ }
+
+ card->diskID = diskID != 0 ? diskID : (const DVDDiskID*)OSPhysicalToCached(0);
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+s32 __CARDGetControlBlock(s32 chan, CARDControl** pcard) {
+ BOOL enabled;
+ s32 result;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+
+ if (chan < 0 || chan >= 2 || card->diskID == 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->result = CARD_RESULT_BUSY;
+ result = CARD_RESULT_READY;
+ card->apiCallback = NULL;
+ *pcard = card;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+s32 __CARDPutControlBlock(CARDControl* card, s32 result) {
+ BOOL enabled;
+
+ ASSERTLINE(1259, result != CARD_RESULT_BUSY);
+
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ card->result = result;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ card->result = result;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+s32 CARDGetResultCode(s32 chan) {
+ CARDControl* card;
+
+ ASSERTLINE(1292, 0 <= chan && chan < 2);
+
+ if (chan < 0 || chan >= 2) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ card = &__CARDBlock[chan];
+ return card->result;
+}
+
+s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
+ CARDControl* card;
+ s32 result;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ u16 fileNo;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ dir = __CARDGetDirBlock(card);
+ if (fat == 0 || dir == 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ if (byteNotUsed) {
+ *byteNotUsed = (s32)(card->sectorSize * fat[CARD_FAT_FREEBLOCKS]);
+ }
+
+ if (filesNotUsed) {
+ *filesNotUsed = 0;
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->fileName[0] == 0xff) {
+ ++*filesNotUsed;
+ }
+ }
+ }
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 CARDGetEncoding(s32 chan, u16* encode) {
+ CARDControl* card;
+ CARDID* id;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = card->workArea;
+ *encode = id->encode;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 CARDGetMemSize(s32 chan, u16* size) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ *size = card->size;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 CARDGetSectorSize(s32 chan, u32* size) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ *size = card->sectorSize;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 __CARDSync(s32 chan) {
+ CARDControl* block;
+ s32 result;
+ s32 enabled;
+
+ block = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+ while ((result = CARDGetResultCode(chan)) == CARD_RESULT_BUSY) {
+ OSSleepThread(&block->threadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+static BOOL OnShutdown(BOOL final, u32 event) {
+ if (!final) {
+ if (CARDUnmount(0) == CARD_RESULT_BUSY || CARDUnmount(1) == CARD_RESULT_BUSY) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL CARDSetFastMode(BOOL enable) {
+ u16 prev = __CARDFastMode;
+ __CARDFastMode = enable ? TRUE : FALSE;
+
+ return prev ? TRUE : FALSE;
+}
+
+BOOL CARDGetFastMode(void) {
+ return __CARDFastMode ? TRUE : FALSE;
+}
+
+s32 CARDGetCurrentMode(s32 chan, u32* mode) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ switch (card->pageSize) {
+ case 512:
+ *mode = 1;
+ break;
+ case 128:
+ default:
+ *mode = 0;
+ break;
+ }
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
diff --git a/src/revolution/card/CARDBlock.c b/src/revolution/card/CARDBlock.c
new file mode 100644
index 0000000000..b7af255ddf
--- /dev/null
+++ b/src/revolution/card/CARDBlock.c
@@ -0,0 +1,160 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void WriteCallback(s32 chan, s32 result);
+static void EraseCallback(s32 chan, s32 result);
+
+void* __CARDGetFatBlock(CARDControl* card) {
+ ASSERTLINE(57, card->currentFat);
+ return card->currentFat;
+}
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat0;
+ u16* fat1;
+
+ card = &__CARDBlock[chan];
+
+ if (result >= 0) {
+ fat0 = (u16*)((u8*)card->workArea + 0x6000);
+ fat1 = (u16*)((u8*)card->workArea + 0x8000);
+
+ ASSERTLINE(82, card->currentFat);
+ if (card->currentFat == fat0) {
+ card->currentFat = fat1;
+ memcpy(fat1, fat0, 0x2000);
+ } else {
+ ASSERTLINE(90, card->currentFat == fat1);
+ card->currentFat = fat0;
+ memcpy(fat0, fat1, 0x2000);
+ }
+ }
+
+ if (!card->apiCallback)
+ __CARDPutControlBlock(card, result);
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card = &__CARDBlock[chan];
+ CARDCallback callback;
+ u16* fat;
+ u32 addr;
+
+ if (result < 0)
+ goto error;
+
+ fat = __CARDGetFatBlock(card);
+ addr = ((u32)fat - (u32)card->workArea) / CARD_SYSTEM_BLOCK_SIZE * card->sectorSize;
+ result = __CARDWrite(chan, addr, CARD_SYSTEM_BLOCK_SIZE, fat, WriteCallback);
+ if (result < 0)
+ goto error;
+
+ return;
+
+error:
+ if (!card->apiCallback)
+ __CARDPutControlBlock(card, result);
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDAllocBlock(s32 chan, u32 cBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 iBlock;
+ u16 startBlock;
+ u16 prevBlock;
+ u16 count;
+
+ ASSERTLINE(182, 0 < cBlock);
+ ASSERTLINE(183, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!card->attached)
+ return CARD_RESULT_NOCARD;
+
+ fat = __CARDGetFatBlock(card);
+ if (fat[3] < cBlock)
+ return CARD_RESULT_INSSPACE;
+
+ fat[3] -= cBlock;
+ startBlock = 0xFFFF;
+ iBlock = fat[4];
+ count = 0;
+ while (0 < cBlock) {
+ if (card->cBlock - 5 < ++count)
+ return CARD_RESULT_BROKEN;
+
+ iBlock++;
+ if (!CARDIsValidBlockNo(card, iBlock))
+ iBlock = 5;
+
+ if (fat[iBlock] == 0x0000u) {
+ if (startBlock == 0xFFFF)
+ startBlock = iBlock;
+ else
+ fat[prevBlock] = iBlock;
+ prevBlock = iBlock;
+ fat[iBlock] = 0xFFFF;
+ --cBlock;
+ }
+ }
+
+ fat[4] = iBlock;
+ card->startBlock = startBlock;
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDFreeBlock(s32 chan, u16 nBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 nextBlock;
+
+ ASSERTLINE(253, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!card->attached)
+ return CARD_RESULT_NOCARD;
+
+ fat = __CARDGetFatBlock(card);
+ while (nBlock != 0xFFFF) {
+ if (!CARDIsValidBlockNo(card, nBlock))
+ return CARD_RESULT_BROKEN;
+
+ nextBlock = fat[nBlock];
+ fat[nBlock] = 0;
+ nBlock = nextBlock;
+ ++fat[3];
+ }
+
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDUpdateFatBlock(s32 chan, u16* fat, CARDCallback callback) {
+ CARDControl* card;
+ u32 addr;
+
+ ASSERTLINE(295, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ ++fat[2];
+ __CARDCheckSum(fat + 2, 0x1FFC, fat, fat + 1);
+ DCStoreRange(fat, 0x2000);
+ card->eraseCallback = callback;
+ addr = (((char*)fat - (char*)card->workArea) / 8192u) * card->sectorSize;
+ return __CARDEraseSector(chan, addr, EraseCallback);
+}
diff --git a/src/revolution/card/CARDCheck.c b/src/revolution/card/CARDCheck.c
new file mode 100644
index 0000000000..4a9b78c736
--- /dev/null
+++ b/src/revolution/card/CARDCheck.c
@@ -0,0 +1,343 @@
+#include <dolphin/card.h>
+
+#include "os/__os.h"
+#include "__card.h"
+
+// prototypes
+static s32 VerifyID(CARDControl* card);
+static s32 VerifyDir(CARDControl* card, int* pcurrent);
+static s32 VerifyFAT(CARDControl* card, int* pcurrent);
+
+void __CARDCheckSum(void* ptr, int length, u16* checksum, u16* checksumInv) {
+ u16* p;
+ int i;
+
+ ASSERTLINE(82, length % sizeof(u16) == 0);
+
+ length /= sizeof(u16);
+ *checksum = *checksumInv = 0;
+ for (i = 0, p = ptr; i < length; i++, p++) {
+ *checksum += *p;
+ *checksumInv += ~*p;
+ }
+
+ if (*checksum == 0xFFFF)
+ *checksum = 0;
+
+ if (*checksumInv == 0xFFFF)
+ *checksumInv = 0;
+}
+
+static s32 VerifyID(CARDControl* card) {
+ CARDID* id;
+ u16 checksum;
+ u16 checksumInv;
+ OSSramEx* sramEx;
+ OSTime rand;
+ int i;
+
+ id = card->workArea;
+
+ if (id->deviceID != 0 || id->size != card->size)
+ return CARD_RESULT_BROKEN;
+
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &checksum, &checksumInv);
+ if (id->checkSum != checksum || id->checkSumInv != checksumInv)
+ return CARD_RESULT_BROKEN;
+
+ rand = *(OSTime*)&id->serial[12];
+ sramEx = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ if (id->serial[i] != (u8)(sramEx->flashID[card - __CARDBlock][i] + rand)) {
+ __OSUnlockSramEx(FALSE);
+ return CARD_RESULT_BROKEN;
+ }
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+
+ __OSUnlockSramEx(FALSE);
+
+ if (id->encode != __CARDGetFontEncode())
+ return CARD_RESULT_ENCODING;
+
+ return CARD_RESULT_READY;
+}
+
+static s32 VerifyDir(CARDControl* card, int* pcurrent) {
+ CARDDir* dir[2];
+ CARDDirCheck* check[2];
+ u16 checkSum;
+ u16 checkSumInv;
+ int i;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ dir[i] = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ check[i] = CARDGetDirCheck(dir[i]);
+ __CARDCheckSum(dir[i], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum, &checkSumInv);
+ if (check[i]->checkSum != checkSum || check[i]->checkSumInv != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentDir = 0;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentDir == 0) {
+ if ((check[0]->checkCode - check[1]->checkCode) < 0)
+ current = 0;
+ else
+ current = 1;
+ card->currentDir = dir[current];
+ memcpy(dir[current], dir[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else {
+ current = (card->currentDir == dir[0]) ? 0 : 1;
+ }
+ }
+
+ if (pcurrent)
+ *pcurrent = current;
+
+ return errors;
+}
+
+static s32 VerifyFAT(CARDControl* card, int* pcurrent) {
+ u16* fat[2];
+ u16* fatp;
+ u16 nBlock;
+ u16 cFree;
+ int i;
+ u16 checkSum;
+ u16 checkSumInv;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ fatp = fat[i] = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+
+ __CARDCheckSum(&fatp[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum, &checkSumInv);
+ if (fatp[CARD_FAT_CHECKSUM] != checkSum || fatp[CARD_FAT_CHECKSUMINV] != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+
+ cFree = 0;
+ for (nBlock = CARD_NUM_SYSTEM_BLOCK; nBlock < card->cBlock; nBlock++) {
+ if (fatp[nBlock] == CARD_FAT_AVAIL)
+ cFree++;
+ }
+
+ if (cFree != fatp[CARD_FAT_FREEBLOCKS]) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentFat == 0) {
+ if (((s16)fat[0][CARD_FAT_CHECKCODE] - (s16)fat[1][CARD_FAT_CHECKCODE]) < 0)
+ current = 0;
+ else
+ current = 1;
+ card->currentFat = fat[current];
+ memcpy(fat[current], fat[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else
+ current = (card->currentFat == fat[0]) ? 0 : 1;
+ }
+
+ if (pcurrent)
+ *pcurrent = current;
+
+ return errors;
+}
+
+s32 __CARDVerify(CARDControl* card) {
+ s32 result;
+ int errors;
+
+ result = VerifyID(card);
+ if (result < 0)
+ return result;
+
+ errors = VerifyDir(card, NULL);
+ errors += VerifyFAT(card, NULL);
+ switch (errors) {
+ case 0:
+ ASSERTLINE(301, card->currentDir);
+ ASSERTLINE(302, card->currentFat);
+ return CARD_RESULT_READY;
+ case 1:
+ return CARD_RESULT_BROKEN;
+ default:
+ return CARD_RESULT_BROKEN;
+ }
+}
+
+s32 CARDCheckExAsync(s32 chan, s32* xferBytes, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir[2];
+ u16* fat[2];
+ u16* map;
+ s32 result;
+ int errors;
+ int currentFat;
+ int currentDir;
+ s32 fileNo;
+ u16 iBlock;
+ u16 cBlock;
+ u16 cFree;
+ BOOL updateFat = FALSE;
+ BOOL updateDir = FALSE;
+ BOOL updateOrphan = FALSE;
+
+ ASSERTLINE(346, 0 <= chan && chan < 2);
+
+ if (xferBytes) {
+ *xferBytes = 0;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ result = VerifyID(card);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ errors = VerifyDir(card, &currentDir);
+ errors += VerifyFAT(card, &currentFat);
+ if (1 < errors) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ dir[0] = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ dir[1] = (CARDDir*)((u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[0] = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[1] = (u16*)((u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+
+ ASSERTLINE(377, errors == 0 || errors == 1);
+
+ switch (errors) {
+ case 0:
+ ASSERTLINE(381, card->currentDir);
+ ASSERTLINE(382, card->currentFat);
+ break;
+ case 1:
+ if (!card->currentDir) {
+ ASSERTLINE(387, card->currentFat);
+ card->currentDir = dir[currentDir];
+ memcpy(dir[currentDir], dir[currentDir ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateDir = TRUE;
+ } else {
+ ASSERTLINE(394, !card->currentFat);
+ card->currentFat = fat[currentFat];
+ memcpy(fat[currentFat], fat[currentFat ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateFat = TRUE;
+ }
+ break;
+ }
+
+ map = fat[currentFat ^ 1];
+ memset(map, 0, CARD_SYSTEM_BLOCK_SIZE);
+
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ CARDDir* ent;
+
+ ent = &card->currentDir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ continue;
+ }
+
+ for (iBlock = ent->startBlock, cBlock = 0; iBlock != 0xFFFF && cBlock < ent->length;
+ iBlock = card->currentFat[iBlock], ++cBlock)
+ {
+ if (!CARDIsValidBlockNo(card, iBlock) || 1 < ++map[iBlock]) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ if (cBlock != ent->length || iBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ cFree = 0;
+ for (iBlock = CARD_NUM_SYSTEM_BLOCK; iBlock < card->cBlock; iBlock++) {
+ u16 nextBlock;
+
+ nextBlock = card->currentFat[iBlock];
+ if (map[iBlock] == 0) {
+ if (nextBlock != CARD_FAT_AVAIL) {
+ card->currentFat[iBlock] = CARD_FAT_AVAIL;
+ updateOrphan = TRUE;
+ }
+ cFree++;
+ } else if (!CARDIsValidBlockNo(card, nextBlock) && nextBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ if (cFree != card->currentFat[CARD_FAT_FREEBLOCKS]) {
+ card->currentFat[CARD_FAT_FREEBLOCKS] = cFree;
+ updateOrphan = TRUE;
+ }
+
+ if (updateOrphan) {
+ __CARDCheckSum(&card->currentFat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32),
+ &card->currentFat[CARD_FAT_CHECKSUM],
+ &card->currentFat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ memcpy(fat[currentFat ^ 1], fat[currentFat], CARD_SYSTEM_BLOCK_SIZE);
+
+ if (updateDir) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateDir(chan, callback);
+ }
+
+ if (updateFat | updateOrphan) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateFatBlock(chan, card->currentFat, callback);
+ }
+
+ __CARDPutControlBlock(card, CARD_RESULT_READY);
+ if (callback) {
+ BOOL enabled = OSDisableInterrupts();
+ callback(chan, CARD_RESULT_READY);
+ OSRestoreInterrupts(enabled);
+ }
+ return CARD_RESULT_READY;
+}
+
+s32 CARDCheckAsync(s32 chan, CARDCallback callback) {
+ s32 xferBytes;
+ return CARDCheckExAsync(chan, &xferBytes, callback);
+}
+
+s32 CARDCheckEx(s32 chan, s32* xferBytes) {
+ s32 result = CARDCheckExAsync(chan, xferBytes, __CARDSyncCallback);
+ if (result < 0 || xferBytes == 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
+
+s32 CARDCheck(s32 chan) {
+ s32 xferBytes;
+ return CARDCheckEx(chan, &xferBytes);
+}
diff --git a/src/revolution/card/CARDCreate.c b/src/revolution/card/CARDCreate.c
new file mode 100644
index 0000000000..7da5ab0911
--- /dev/null
+++ b/src/revolution/card/CARDCreate.c
@@ -0,0 +1,126 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void CreateCallbackFat(s32 chan, s32 result);
+
+static void CreateCallbackFat(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+
+ if (result >= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[card->freeNo];
+ memcpy(ent->gameName, card->diskID->gameName, sizeof(ent->gameName));
+ memcpy(ent->company, card->diskID->company, sizeof(ent->company));
+ ent->permission = 4;
+ ent->copyTimes = 0;
+
+ ASSERTLINE(111, CARDIsValidBlockNo(card, card->startBlock));
+ ent->startBlock = (u16)card->startBlock;
+ ent->bannerFormat = 0;
+ ent->iconAddr = -1;
+ ent->iconFormat = 0;
+ ent->iconSpeed = 0;
+ ent->commentAddr = -1;
+
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+ card->fileInfo->offset = 0;
+ card->fileInfo->iBlock = ent->startBlock;
+ ent->time = OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ goto after;
+ }
+ } else {
+after:;
+ __CARDPutControlBlock(card, result);
+ if (callback) {
+ callback(chan, result);
+ }
+ }
+}
+
+s32 CARDCreateAsync(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ u16 fileNo;
+ u16 freeNo;
+ u16* fat;
+ s32 result;
+
+ ASSERTLINE(175, 0 <= chan && chan < 2);
+ ASSERTLINE(176, strlen(fileName) <= CARD_FILENAME_MAX);
+
+ if (strlen(fileName) > (u32)CARD_FILENAME_MAX) {
+ return CARD_RESULT_NAMETOOLONG;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ ASSERTLINE(188, 0 < size && (size % card->sectorSize) == 0);
+
+ if (size <= 0 || (size % card->sectorSize) != 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ freeNo = (u16)-1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ if (freeNo == (u16)-1) {
+ freeNo = fileNo;
+ }
+ } else if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) == 0 &&
+ memcmp(ent->company, card->diskID->company, sizeof(ent->company)) == 0 &&
+ __CARDCompareFileName(ent, fileName)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+ }
+ }
+
+ if (freeNo == (u16)-1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_NOENT);
+ }
+
+ fat = __CARDGetFatBlock(card);
+ if (card->sectorSize * fat[CARD_FAT_FREEBLOCKS] < size) {
+ return __CARDPutControlBlock(card, CARD_RESULT_INSSPACE);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->freeNo = freeNo;
+ ent = &dir[freeNo];
+ ent->length = (u16)(size / card->sectorSize);
+ strncpy((char*)ent->fileName, fileName, CARD_FILENAME_MAX);
+
+ card->fileInfo = fileInfo;
+ fileInfo->chan = chan;
+ fileInfo->fileNo = freeNo;
+
+ result = __CARDAllocBlock(chan, size / card->sectorSize, CreateCallbackFat);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 CARDCreate(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo) {
+ s32 result = CARDCreateAsync(chan, fileName, size, fileInfo, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDDelete.c b/src/revolution/card/CARDDelete.c
new file mode 100644
index 0000000000..0acf24be1c
--- /dev/null
+++ b/src/revolution/card/CARDDelete.c
@@ -0,0 +1,108 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void DeleteCallback(s32 chan, s32 result);
+
+static void DeleteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+
+ if (result < 0)
+ goto error;
+
+ result = __CARDFreeBlock(chan, card->startBlock, callback);
+ if (result < 0)
+ goto error;
+ return;
+
+error:
+ __CARDPutControlBlock(card, result);
+ if (callback)
+ callback(chan, result);
+}
+
+s32 CARDFastDeleteAsync(s32 chan, s32 fileNo, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ ASSERTLINE(133, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+ ASSERTLINE(134, 0 <= chan && chan < 2);
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo)
+ return CARD_RESULT_FATAL_ERROR;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+
+ if (__CARDIsOpened(card, fileNo))
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 CARDFastDelete(s32 chan, s32 fileNo) {
+ s32 result = CARDFastDeleteAsync(chan, fileNo, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
+
+s32 CARDDeleteAsync(s32 chan, const char* fileName, CARDCallback callback) {
+ CARDControl* card;
+ s32 fileNo;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+ if (__CARDIsOpened(card, fileNo))
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 CARDDelete(s32 chan, const char* fileName) {
+ s32 result = CARDDeleteAsync(chan, fileName, __CARDSyncCallback);
+ if (result < 0)
+ return result;
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDDir.c b/src/revolution/card/CARDDir.c
new file mode 100644
index 0000000000..e154a9c428
--- /dev/null
+++ b/src/revolution/card/CARDDir.c
@@ -0,0 +1,89 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void WriteCallback(s32 chan, s32 result);
+static void EraseCallback(s32 chan, s32 result);
+
+CARDDir* __CARDGetDirBlock(CARDControl* card) {
+ ASSERTLINE(54, card->currentDir);
+ return card->currentDir;
+}
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card = &__CARDBlock[chan];
+ CARDCallback callback;
+
+ if (result >= 0) {
+ CARDDir* dir0 = (CARDDir*)((u8*)card->workArea + 0x2000);
+ CARDDir* dir1 = (CARDDir*)((u8*)card->workArea + 0x4000);
+
+ ASSERTLINE(79, card->currentDir);
+
+ if (card->currentDir == dir0) {
+ card->currentDir = dir1;
+ memcpy(dir1, dir0, 0x2000);
+ } else {
+ ASSERTLINE(87, card->currentDir == dir1);
+ card->currentDir = dir0;
+ memcpy(dir0, dir1, 0x2000);
+ }
+ }
+
+ if (!card->apiCallback)
+ __CARDPutControlBlock(card, result);
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card = &__CARDBlock[chan];
+ CARDCallback callback;
+ CARDDir* dir;
+ u32 addr;
+
+ if (result >= 0) {
+ dir = __CARDGetDirBlock(card);
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ result = __CARDWrite(chan, addr, 0x2000, dir, WriteCallback);
+ if (result >= 0)
+ return;
+ }
+
+ if (!card->apiCallback)
+ __CARDPutControlBlock(card, result);
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDUpdateDir(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ CARDDirCheck* check;
+ u32 addr;
+ CARDDir* dir;
+
+ ASSERTLINE(173, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!card->attached)
+ return CARD_RESULT_NOCARD;
+
+ dir = __CARDGetDirBlock(card);
+ check = CARDGetDirCheck(dir);
+ ++check->checkCode;
+ __CARDCheckSum(dir, 0x2000 - sizeof(u32), &check->checkSum, &check->checkSumInv);
+ DCStoreRange(dir, 0x2000);
+
+ card->eraseCallback = callback;
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ return __CARDEraseSector(chan, addr, EraseCallback);
+}
diff --git a/src/revolution/card/CARDErase.c b/src/revolution/card/CARDErase.c
new file mode 100644
index 0000000000..73bb8ef04b
--- /dev/null
+++ b/src/revolution/card/CARDErase.c
@@ -0,0 +1,102 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+
+ if (result >= 0) {
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ fileInfo->length -= card->sectorSize;
+ if (fileInfo->length <= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = dir + fileInfo->fileNo;
+ ent->time = OSTicksToSeconds(OSGetTime());
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+
+ result = __CARDUpdateDir(chan, callback);
+ } else {
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+
+ if (fileInfo->iBlock < 5 || fileInfo->iBlock >= card->cBlock) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+
+ result = __CARDEraseSector(chan, card->sectorSize * fileInfo->iBlock, EraseCallback);
+ }
+
+ if (result < 0) {
+ goto error;
+ }
+ return;
+ }
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(98, callback);
+ callback(chan, result);
+}
+
+s32 CARDEraseAsync(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ ASSERTLINE(132, 0 < length);
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ ASSERTLINE(138, OFFSET(offset, card->sectorSize) == 0);
+ ASSERTLINE(139, OFFSET(length, card->sectorSize) == 0);
+
+ if (OFFSET(offset, card->sectorSize) != 0 || OFFSET(length, card->sectorSize) != 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = dir + fileInfo->fileNo;
+ result = __CARDIsWritable(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+
+ result = __CARDEraseSector(fileInfo->chan, card->sectorSize * fileInfo->iBlock, EraseCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ return result;
+}
+
+s32 CARDErase(CARDFileInfo* fileInfo, s32 length, s32 offset) {
+ s32 result = CARDEraseAsync(fileInfo, length, offset, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(fileInfo->chan);
+}
diff --git a/src/revolution/card/CARDFormat.c b/src/revolution/card/CARDFormat.c
new file mode 100644
index 0000000000..7bcc76daa1
--- /dev/null
+++ b/src/revolution/card/CARDFormat.c
@@ -0,0 +1,137 @@
+#include <dolphin/card.h>
+
+#include "os/__os.h"
+#include "__card.h"
+
+static void FormatCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0)
+ goto error;
+
+ ++card->formatStep;
+ if (card->formatStep < CARD_NUM_SYSTEM_BLOCK) {
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (result >= 0)
+ return;
+ } else if (card->formatStep < 2 * CARD_NUM_SYSTEM_BLOCK) {
+ int step = card->formatStep - CARD_NUM_SYSTEM_BLOCK;
+ result = __CARDWrite(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8* )card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), FormatCallback);
+ if (result >= 0)
+ return;
+ } else {
+ card->currentDir = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentDir, (u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE, CARD_SYSTEM_BLOCK_SIZE);
+ card->currentFat = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentFat, (u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE, CARD_SYSTEM_BLOCK_SIZE);
+ }
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(133, callback);
+ callback(chan, result);
+}
+
+s32 __CARDFormatRegionAsync(s32 chan, u16 encode, CARDCallback callback) {
+ CARDControl* card;
+ CARDID* id;
+ CARDDir* dir;
+ u16* fat;
+ s16 i;
+ s32 result;
+ OSSram* sram;
+ OSSramEx* sramEx;
+ u16 dvdstatus;
+ OSTime time;
+ OSTime rand;
+
+ ASSERTLINE(167, encode == CARD_ENCODE_ANSI || encode == CARD_ENCODE_SJIS);
+ ASSERTLINE(168, 0 <= chan && chan < 2);
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ id = (CARDID*)card->workArea;
+ memset(id, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ dvdstatus = __VIRegs[55];
+
+ id->encode = encode;
+ sram = __OSLockSram();
+ *(u32*)&id->serial[20] = sram->counterBias;
+ *(u32*)&id->serial[24] = sram->language;
+ __OSUnlockSram(FALSE);
+
+ rand = time = OSGetTime();
+
+ sramEx = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ id->serial[i] = (u8)(sramEx->flashID[chan][i] + rand);
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+ __OSUnlockSramEx(FALSE);
+
+ *(u32*)&id->serial[28] = dvdstatus;
+ *(OSTime*)&id->serial[12] = time;
+
+ id->deviceID = 0;
+ id->size = card->size;
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &id->checkSum, &id->checkSumInv);
+
+ for (i = 0; i < 2; i++) {
+ CARDDirCheck* check;
+
+ dir = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(dir, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ check = CARDGetDirCheck(dir);
+ check->checkCode = i;
+ __CARDCheckSum(dir, CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &check->checkSum, &check->checkSumInv);
+ }
+
+ for (i = 0; i < 2; i++) {
+ fat = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(fat, 0x00, CARD_SYSTEM_BLOCK_SIZE);
+ fat[CARD_FAT_CHECKCODE] = (u16)i;
+ fat[CARD_FAT_FREEBLOCKS] = (u16)(card->cBlock - CARD_NUM_SYSTEM_BLOCK);
+ fat[CARD_FAT_LASTSLOT] = CARD_NUM_SYSTEM_BLOCK - 1;
+ __CARDCheckSum(&fat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &fat[CARD_FAT_CHECKSUM],
+ &fat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ DCStoreRange(card->workArea, CARD_WORKAREA_SIZE);
+
+ card->formatStep = 0;
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 __CARDFormatRegion(s32 chan, u16 encode) {
+ s32 result = __CARDFormatRegionAsync(chan, encode, &__CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
+
+s32 CARDFormatAsync(s32 chan, CARDCallback callback) {
+ return __CARDFormatRegionAsync(chan, __CARDGetFontEncode(), callback);
+}
+
+s32 CARDFormat(s32 chan) {
+ s32 result = __CARDFormatRegionAsync(chan, __CARDGetFontEncode(), &__CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDMount.c b/src/revolution/card/CARDMount.c
new file mode 100644
index 0000000000..4ca607dc3b
--- /dev/null
+++ b/src/revolution/card/CARDMount.c
@@ -0,0 +1,394 @@
+#include <dolphin/card.h>
+#include <dolphin/exi.h>
+
+#include "os/__os.h"
+#include "__card.h"
+
+static u32 SectorSizeTable[8] = {
+ 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024, 128 * 1024, 256 * 1024, 0, 0,
+};
+
+static u32 LatencyTable[8] = {
+ 4, 8, 16, 32, 64, 128, 256, 512,
+};
+
+// prototypes
+static s32 DoMount(s32 chan);
+static void DoUnmount(s32 chan, s32 result);
+
+static BOOL IsCard(u32 id) {
+ u32 size;
+ s32 sectorSize;
+ if (id & (0xFFFF0000) && (id != 0x80000004 || __CARDVendorID == 0xFFFF)) {
+ return FALSE;
+ }
+
+ if ((id & 3) != 0) {
+ return FALSE;
+ }
+
+ size = id & 0xfc;
+ switch (size) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ if (sectorSize == 0) {
+ return FALSE;
+ }
+
+ if ((size * 1024 * 1024 / 8) / sectorSize < 8) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void __CARDDisable(BOOL disable) {
+ BOOL enabled = OSDisableInterrupts();
+
+ __gUnknown800030E3 &= ~0x80;
+ if (disable) {
+ __gUnknown800030E3 |= 0x80;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+int CARDProbe(s32 chan) {
+ if (__gUnknown800030E3 & 0x80) {
+ return 0;
+ } else {
+ return EXIProbe(chan);
+ }
+}
+
+s32 CARDProbeEx(s32 chan, s32* memSize, s32* sectorSize) {
+ u32 id;
+ CARDControl* card;
+ BOOL enabled;
+ s32 result;
+ int probe;
+
+ if (chan < 0 || 2 <= chan)
+ return CARD_RESULT_FATAL_ERROR;
+
+ if (__gUnknown800030E3 & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+
+ probe = EXIProbeEx(chan);
+ if (probe == -1)
+ result = CARD_RESULT_NOCARD;
+ else if (probe == 0)
+ result = CARD_RESULT_BUSY;
+ else if (card->attached) {
+ if (card->mountStep < 1)
+ result = CARD_RESULT_BUSY;
+ else {
+ if (memSize)
+ *memSize = card->size;
+
+ if (sectorSize)
+ *sectorSize = card->sectorSize;
+
+ result = CARD_RESULT_READY;
+ }
+ }
+ else if ((EXIGetState(chan) & 8))
+ result = CARD_RESULT_WRONGDEVICE;
+ else if (!EXIGetID(chan, 0, &id))
+ result = CARD_RESULT_BUSY;
+ else if (IsCard(id)) {
+ if (memSize)
+ *memSize = (s32)(id & 0xfc);
+
+ if (sectorSize)
+ *sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+static s32 DoMount(s32 chan) {
+ CARDControl* card;
+ u32 id;
+ u8 status;
+ s32 result;
+ OSSramEx* sram;
+ int i;
+ u8 checkSum;
+ int step;
+
+ ASSERTLINE(399, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (card->mountStep == 0) {
+ if (EXIGetID(chan, 0, &id) == 0) {
+ result = CARD_RESULT_NOCARD;
+ } else if (IsCard(id)) {
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+
+ if (result < 0)
+ goto error;
+
+ card->cid = id;
+ card->size = (u16)(id & 0xFC);
+ ASSERTLINE(424, card->size);
+
+ card->sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ ASSERTLINE(426, card->sectorSize);
+
+ card->cBlock = (u16)((card->size * 1024 * 1024 / 8) / card->sectorSize);
+ ASSERTLINE(428, 8 <= card->cBlock);
+
+ card->latency = LatencyTable[(id & 0x00000700) >> 8];
+
+ result = __CARDReadVendorID(chan, &card->vendorID);
+ if (result < 0)
+ goto error;
+
+ if (CARDGetFastMode() && (card->vendorID >> 8) == 0xEC) {
+ card->pageSize = 512;
+ } else {
+ card->pageSize = 128;
+ }
+
+ result = __CARDClearStatus(chan);
+ if (result < 0)
+ goto error;
+
+ result = __CARDReadStatus(chan, &status);
+ if (result < 0)
+ goto error;
+
+ if (!EXIProbe(chan)) {
+ result = CARD_RESULT_NOCARD;
+ goto error;
+ }
+
+ if (!(status & 0x40)) {
+ result = __CARDUnlock(chan, card->id);
+ if (result < 0)
+ goto error;
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ sram->flashID[chan][i] = card->id[i];
+ checkSum += card->id[i];
+ }
+ sram->flashIDCheckSum[chan] = (u8)~checkSum;
+ __OSUnlockSramEx(TRUE);
+
+ return result;
+ } else {
+ card->mountStep = 1;
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++)
+ checkSum += sram->flashID[chan][i];
+
+ __OSUnlockSramEx(FALSE);
+ if (sram->flashIDCheckSum[chan] != (u8)~checkSum) {
+ result = CARD_RESULT_IOERROR;
+ goto error;
+ }
+ }
+ }
+
+ if (card->mountStep == 1) {
+ if (card->cid == 0x80000004) {
+ u16 vendorID;
+
+ sram = __OSLockSramEx();
+ vendorID = *(u16*)sram->flashID[chan];
+ __OSUnlockSramEx(FALSE);
+
+ if (__CARDVendorID == 0xFFFF || vendorID != __CARDVendorID) {
+ result = CARD_RESULT_WRONGDEVICE;
+ goto error;
+ }
+ }
+
+ card->mountStep = 2;
+
+ result = __CARDEnableInterrupt(chan, TRUE);
+ if (result < 0)
+ goto error;
+
+ EXISetExiCallback(chan, __CARDExiHandler);
+ EXIUnlock(chan);
+ DCInvalidateRange(card->workArea, CARD_WORKAREA_SIZE);
+ }
+
+ step = card->mountStep - 2;
+ result = __CARDRead(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8 *)card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), __CARDMountCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+
+error:
+ EXIUnlock(chan);
+ DoUnmount(chan, result);
+ return result;
+}
+
+void __CARDMountCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ ASSERTLINE(570, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ switch (result) {
+ case CARD_RESULT_READY:
+ if (++card->mountStep < CARD_MAX_MOUNT_STEP) {
+ result = DoMount(chan);
+ if (0 <= result)
+ return;
+ } else
+ result = __CARDVerify(card);
+ break;
+ case CARD_RESULT_UNLOCKED:
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ return;
+ }
+ card->unlockCallback = 0;
+
+ result = DoMount(chan);
+ if (result >= 0)
+ return;
+ break;
+ case CARD_RESULT_IOERROR:
+ case CARD_RESULT_NOCARD:
+ DoUnmount(chan, result);
+ break;
+ }
+
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(620, callback);
+ callback(chan, result);
+}
+
+s32 CARDMountAsync(s32 chan, void* workArea, CARDCallback detachCallback, CARDCallback attachCallback) {
+ CARDControl* card;
+ BOOL enabled;
+
+ ASSERTLINE(652, workArea && ((u32) workArea % 32 == 0));
+ ASSERTLINE(653, 0 <= chan && chan < 2);
+
+ if (chan < 0 || 2 <= chan)
+ return CARD_RESULT_FATAL_ERROR;
+
+ if (__gUnknown800030E3 & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card = &__CARDBlock[chan];
+
+ enabled = OSDisableInterrupts();
+ if (card->result == CARD_RESULT_BUSY) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_BUSY;
+ }
+
+ if (!card->attached && (EXIGetState(chan) & 0x08)) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_WRONGDEVICE;
+ }
+
+ card->result = CARD_RESULT_BUSY;
+ card->workArea = workArea;
+ card->extCallback = detachCallback;
+ card->apiCallback = attachCallback ? attachCallback : __CARDDefaultApiCallback;
+ card->exiCallback = 0;
+
+ if (!card->attached && !EXIAttach(chan, __CARDExtHandler)) {
+ card->result = CARD_RESULT_NOCARD;
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->mountStep = 0;
+ card->attached = TRUE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+
+ card->currentDir = 0;
+ card->currentFat = 0;
+
+ OSRestoreInterrupts(enabled);
+
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler))
+ return CARD_RESULT_READY;
+
+ card->unlockCallback = 0;
+ return DoMount(chan);
+}
+
+s32 CARDMount(s32 chan, void* workArea, CARDCallback detachCallback) {
+ s32 result = CARDMountAsync(chan, workArea, detachCallback, __CARDSyncCallback);
+
+ if (result < 0)
+ return result;
+ return __CARDSync(chan);
+}
+
+static void DoUnmount(s32 chan, s32 result) {
+ CARDControl* card;
+ BOOL enabled;
+
+ ASSERTLINE(758, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ EXISetExiCallback(chan, 0);
+ EXIDetach(chan);
+ OSCancelAlarm(&card->alarm);
+ card->attached = FALSE;
+ card->result = result;
+ card->mountStep = 0;
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+s32 CARDUnmount(s32 chan) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(793, 0 <= chan && chan < 2);
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ DoUnmount(chan, CARD_RESULT_NOCARD);
+ return CARD_RESULT_READY;
+}
diff --git a/src/revolution/card/CARDNet.c b/src/revolution/card/CARDNet.c
new file mode 100644
index 0000000000..5f43d3becf
--- /dev/null
+++ b/src/revolution/card/CARDNet.c
@@ -0,0 +1,138 @@
+#include <dolphin/card.h>
+
+#include "os/__os.h"
+#include "__card.h"
+
+u16 __CARDVendorID = 0xFFFF;
+u8 __CARDPermMask = 0x1C;
+
+u16 CARDSetVendorID(u16 vendorID) {
+ u16 prevID = __CARDVendorID;
+ __CARDVendorID = vendorID;
+
+ return prevID;
+}
+
+u16 CARDGetVendorID() {
+ return __CARDVendorID;
+}
+
+s32 CARDGetSerialNo(s32 chan, u64* serialNo) {
+ CARDControl* card;
+ s32 result;
+ CARDID* id;
+ u64 code;
+ int i;
+
+ ASSERTLINE(105, 0 <= chan && chan < 2);
+
+ if (!(0 <= chan && chan < 2)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = (CARDID*)card->workArea;
+ for (code = 0, i = 0; i < sizeof(id->serial) / sizeof(u64); ++i) {
+ code ^= *(u64*)&id->serial[sizeof(u64) * i];
+ }
+ *serialNo = code;
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 CARDGetUniqueCode(s32 chan, u64* uniqueCode) {
+ CARDControl* card;
+ s32 result;
+ OSSramEx* sram;
+
+ ASSERTLINE(146, 0 <= chan && chan < 2);
+
+ if (!(0 <= chan && chan < 2)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ sram = __OSLockSramEx();
+ memcpy(uniqueCode, &sram->flashID[chan][4], 8);
+ __OSUnlockSramEx(0);
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+s32 CARDGetAttributes(s32 chan, s32 fileNo, u8* attr) {
+ CARDDir dirent;
+ s32 result;
+
+ result = __CARDGetStatusEx(chan, fileNo, &dirent);
+ if (result == 0) {
+ *attr = dirent.permission;
+ }
+
+ return result;
+}
+
+#define CARDCheckAttr(attr, flag) ((u32)(attr & flag) != 0)
+
+s32 CARDSetAttributesAsync(s32 chan, s32 fileNo, u8 attr, CARDCallback callback) {
+ CARDDir dirent;
+ s32 result;
+
+ if (attr & ~__CARDPermMask) {
+ return CARD_RESULT_NOPERM;
+ }
+
+ result = __CARDGetStatusEx(chan, fileNo, &dirent);
+ if (result < 0) {
+ return result;
+ }
+
+ if ((CARDCheckAttr(dirent.permission, 0x20) && !CARDCheckAttr(attr, 0x20)) || (CARDCheckAttr(dirent.permission, 0x40) && !CARDCheckAttr(attr, 0x40))) {
+ return CARD_RESULT_NOPERM;
+ }
+
+ if ((CARDCheckAttr(attr, 0x20) && CARDCheckAttr(attr, 0x40)) || (CARDCheckAttr(attr, 0x20) && CARDCheckAttr(dirent.permission, 0x40)) || (CARDCheckAttr(attr, 0x40) && CARDCheckAttr(dirent.permission, 0x20))) {
+ return CARD_RESULT_NOPERM;
+ }
+
+ dirent.permission = attr;
+ return __CARDSetStatusExAsync(chan, fileNo, &dirent, callback);
+}
+
+s32 CARDSetAttributes(s32 chan, s32 fileNo, u8 attr) {
+ s32 result;
+
+ result = CARDSetAttributesAsync(chan, fileNo, attr, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
+
+static int __CARDEnablePerm(u8 perm, BOOL enable) {
+ int prev;
+ prev = __CARDPermMask & perm ? TRUE : FALSE;
+
+ if (enable) {
+ __CARDPermMask |= perm;
+ } else {
+ __CARDPermMask &= ~perm;
+ }
+
+ return prev;
+}
+
+int __CARDEnableGlobal(BOOL enable) {
+ return __CARDEnablePerm(0x20, enable);
+}
+
+int __CARDEnableCompany(BOOL enable) {
+ return __CARDEnablePerm(0x40, enable);
+}
diff --git a/src/revolution/card/CARDOpen.c b/src/revolution/card/CARDOpen.c
new file mode 100644
index 0000000000..bd86568291
--- /dev/null
+++ b/src/revolution/card/CARDOpen.c
@@ -0,0 +1,174 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+BOOL __CARDCompareFileName(CARDDir* ent, const char* fileName) {
+ char* entName = (char*)ent->fileName;
+ char c1;
+ char c2;
+ int n = CARD_FILENAME_MAX;
+
+ while (--n >= 0) {
+ if ((c1 = *entName++) != (c2 = *fileName++))
+ return FALSE;
+ else if (c2 == '\0')
+ return TRUE;
+ }
+
+ if (*fileName == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+s32 __CARDAccess(CARDControl* card, CARDDir* ent) {
+ const DVDDiskID* diskID = card->diskID;
+
+ if (ent->gameName[0] == 0xFF)
+ return CARD_RESULT_NOFILE;
+
+ if (diskID == &__CARDDiskNone
+ || (memcmp(ent->gameName, diskID->gameName, sizeof(ent->gameName)) == 0
+ && memcmp(ent->company, diskID->company, sizeof(ent->company)) == 0))
+ return CARD_RESULT_READY;
+
+ return CARD_RESULT_NOPERM;
+}
+
+s32 __CARDIsWritable(CARDControl* card, CARDDir* ent) {
+ const DVDDiskID* diskID = card->diskID;
+ s32 result;
+ u8 perm;
+
+ result = __CARDAccess(card, ent);
+ if (result == CARD_RESULT_NOPERM) {
+ perm = ent->permission & __CARDPermMask;
+ if (perm & 0x20 && (memcmp(ent->gameName, __CARDDiskNone.gameName, sizeof(ent->gameName)) == 0 &&
+ memcmp(ent->company, __CARDDiskNone.company, sizeof(ent->company)) == 0))
+ {
+ return CARD_RESULT_READY;
+ } else if (perm & 0x40 && (memcmp(ent->gameName, __CARDDiskNone.gameName, sizeof(ent->gameName)) == 0 &&
+ memcmp(ent->company, diskID->company, sizeof(ent->company)) == 0))
+ {
+ return CARD_RESULT_READY;
+ }
+ }
+
+ return result;
+}
+
+s32 __CARDIsReadable(CARDControl* card, CARDDir* ent) {
+ s32 result = __CARDIsWritable(card, ent);
+ if (result == CARD_RESULT_NOPERM && (ent->permission & 0x4)) {
+ return CARD_RESULT_READY;
+ }
+
+ return result;
+}
+
+s32 __CARDGetFileNo(CARDControl* card, const char* fileName, s32* pfileNo) {
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 fileNo;
+ s32 result;
+
+ if (!card->attached)
+ return CARD_RESULT_NOCARD;
+
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+
+ if (result < 0)
+ continue;
+ if (__CARDCompareFileName(ent, fileName)) {
+ *pfileNo = fileNo;
+ return CARD_RESULT_READY;
+ }
+ }
+
+ return CARD_RESULT_NOFILE;
+}
+
+s32 CARDFastOpen(s32 chan, s32 fileNo, CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ ASSERTLINE(278, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+ ASSERTLINE(279, 0 <= chan && chan < 2);
+
+ if (fileNo < 0 || fileNo >= CARD_MAX_FILE)
+ return CARD_RESULT_FATAL_ERROR;
+
+ fileInfo->chan = -1;
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsReadable(card, ent);
+ if (0 <= result) {
+ if (!CARDIsValidBlockNo(card, ent->startBlock))
+ result = CARD_RESULT_BROKEN;
+ else {
+ fileInfo->chan = chan;
+ fileInfo->fileNo = fileNo;
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ }
+ }
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ s32 fileNo;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ ASSERTLINE(336, 0 <= chan && chan < 2);
+
+ fileInfo->chan = -1;
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (result >= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ if (!CARDIsValidBlockNo(card, ent->startBlock))
+ result = CARD_RESULT_BROKEN;
+ else {
+ fileInfo->chan = chan;
+ fileInfo->fileNo = fileNo;
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ }
+ }
+
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDClose(CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(380, 0 <= fileInfo->chan && fileInfo->chan < 2);
+ ASSERTLINE(381, 0 <= fileInfo->fileNo && fileInfo->fileNo < CARD_MAX_FILE);
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0)
+ return result;
+
+ fileInfo->chan = -1;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+BOOL __CARDIsOpened(CARDControl* card, s32 fileNo) {
+ return FALSE;
+}
diff --git a/src/revolution/card/CARDProgram.c b/src/revolution/card/CARDProgram.c
new file mode 100644
index 0000000000..2e603777fa
--- /dev/null
+++ b/src/revolution/card/CARDProgram.c
@@ -0,0 +1,100 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+#define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
+#define CARD_PROGRAM_SIZE 128
+
+static void ProgramCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDFileInfo* fileInfo;
+ s32 length;
+
+ card = &__CARDBlock[chan];
+
+ if (result >= 0) {
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ length = TRUNC(fileInfo->offset + card->sectorSize, card->sectorSize) - fileInfo->offset;
+ fileInfo->length -= length;
+ if (fileInfo->length > 0) {
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += length;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+
+ if (fileInfo->iBlock < 5 || fileInfo->iBlock >= card->cBlock) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+
+ ASSERTLINE(94, OFFSET(fileInfo->length, CARD_PROGRAM_SIZE) == 0);
+ ASSERTLINE(95, OFFSET(fileInfo->offset, card->sectorSize) == 0);
+
+ result = __CARDWrite(chan, card->sectorSize * fileInfo->iBlock, fileInfo->length < card->sectorSize ? fileInfo->length : card->sectorSize, card->buffer, ProgramCallback);
+ if (result >= 0) {
+ return;
+ }
+ }
+ }
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(114, callback);
+ callback(chan, result);
+}
+
+s32 CARDProgramAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ ASSERTLINE(147, buf && OFFSET(buf, 32) == 0);
+ ASSERTLINE(148, OFFSET(offset, CARD_PROGRAM_SIZE) == 0);
+ ASSERTLINE(149, 0 < length && OFFSET(length, CARD_PROGRAM_SIZE) == 0);
+
+ if (offset & 0x7F || length & 0x7F) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ DCStoreRange(buf, length);
+
+ card->apiCallback = callback ? callback : &__CARDDefaultApiCallback;
+ offset = fileInfo->offset & (card->sectorSize - 1);
+ length = length < (card->sectorSize - offset) ? length : card->sectorSize - offset;
+
+ result = __CARDWrite(fileInfo->chan, offset + (card->sectorSize * fileInfo->iBlock), length, buf, ProgramCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ return result;
+}
+
+s32 CARDProgram(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset) {
+ s32 result = CARDProgramAsync(fileInfo, buf, length, offset, __CARDSyncCallback);
+ if (result < 0)
+ return result;
+
+ return __CARDSync(fileInfo->chan);
+}
diff --git a/src/revolution/card/CARDRaw.c b/src/revolution/card/CARDRaw.c
new file mode 100644
index 0000000000..584a9be9c2
--- /dev/null
+++ b/src/revolution/card/CARDRaw.c
@@ -0,0 +1,82 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+s32 __CARDRawReadAsync(s32 chan, void* buf, s32 length, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ ASSERTLINE(59, buf && ((u32) buf % 32) == 0);
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ ASSERTLINE(67, 0 < length && (length % CARD_SEG_SIZE) == 0 && length < CARD_MAX_SIZE);
+ ASSERTLINE(68, (offset % card->sectorSize) == 0);
+
+ DCInvalidateRange(buf, length);
+ result = __CARDRead(chan, offset, length, buf, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 __CARDRawRead(s32 chan, void* buf, s32 length, s32 offset) {
+ s32 result = __CARDRawReadAsync(chan, buf, length, offset, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+
+ __CARDPutControlBlock(card, result);
+
+ ASSERTLINE(117, callback);
+ callback(chan, result);
+}
+
+s32 __CARDRawEraseAsync(s32 chan, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ if (offset % card->sectorSize) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ if ((card->size * 1024 * 1024) / 8 <= offset) {
+ return __CARDPutControlBlock(card, CARD_RESULT_LIMIT);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDEraseSector(chan, offset, EraseCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 __CARDRawErase(s32 chan, s32 offset) {
+ s32 result = __CARDRawEraseAsync(chan, offset, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDRdwr.c b/src/revolution/card/CARDRdwr.c
new file mode 100644
index 0000000000..8350f57ac4
--- /dev/null
+++ b/src/revolution/card/CARDRdwr.c
@@ -0,0 +1,105 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void BlockReadCallback(s32 chan, s32 result);
+static void BlockWriteCallback(s32 chan, s32 result);
+
+static void BlockReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+
+ if ((result >= 0)) {
+ card->xferred += 0x200;
+ card->addr += 0x200;
+ ((u8*)card->buffer) += 0x200;
+
+ if (--card->repeat > 0) {
+ result = __CARDReadSegment(chan, BlockReadCallback);
+ if (result >= 0) {
+ return;
+ }
+ }
+ }
+
+ if (!card->apiCallback) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDRead(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+
+ ASSERTLINE(91, 0 < length && length % CARD_SEG_SIZE == 0);
+ ASSERTLINE(92, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (card->attached == 0) {
+ return CARD_RESULT_NOCARD;
+ }
+ card->xferCallback = callback;
+ card->repeat = (length / 512u);
+ card->addr = addr;
+ card->buffer = dst;
+ return __CARDReadSegment(chan, BlockReadCallback);
+}
+
+static void BlockWriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ card->xferred += card->pageSize;
+ card->addr += card->pageSize;
+ ((u8*)card->buffer) += card->pageSize;
+
+ if (--card->repeat > 0) {
+ result = __CARDWritePage(chan, BlockWriteCallback);
+ if (result >= 0) {
+ return;
+ }
+ }
+ }
+
+ if (!card->apiCallback) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDWrite(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+
+ ASSERTLINE(153, 0 < length && length % card->pageSize == 0);
+ ASSERTLINE(154, 0 <= chan && chan < 2);
+
+ if (card->attached == 0) {
+ return CARD_RESULT_NOCARD;
+ }
+ card->xferCallback = callback;
+ card->repeat = (length / card->pageSize);
+ card->addr = addr;
+ card->buffer = dst;
+ return __CARDWritePage(chan, BlockWriteCallback);
+}
+
+s32 CARDGetXferredBytes(s32 chan) {
+ ASSERTLINE(183, 0 <= chan && chan < 2);
+ return __CARDBlock[chan].xferred;
+}
diff --git a/src/revolution/card/CARDRead.c b/src/revolution/card/CARDRead.c
new file mode 100644
index 0000000000..74bd544a6f
--- /dev/null
+++ b/src/revolution/card/CARDRead.c
@@ -0,0 +1,174 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+#define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
+
+// prototypes
+static void ReadCallback(s32 chan, s32 result);
+
+s32 __CARDSeek(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDControl** pcard) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u16* fat;
+
+ ASSERTLINE(98, 0 <= fileInfo->chan && fileInfo->chan < 2);
+ ASSERTLINE(99, 0 <= fileInfo->fileNo && fileInfo->fileNo < CARD_MAX_FILE);
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0)
+ return result;
+
+ ASSERTLINE(106, CARDIsValidBlockNo(card, fileInfo->iBlock));
+ ASSERTLINE(107, fileInfo->offset < card->cBlock * card->sectorSize);
+
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock) || card->cBlock * card->sectorSize <= fileInfo->offset)
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+
+ ASSERTLINE(117, ent->gameName[0] != 0xff);
+
+ if (ent->length * card->sectorSize <= offset || ent->length * card->sectorSize < offset + length)
+ return __CARDPutControlBlock(card, CARD_RESULT_LIMIT);
+
+ card->fileInfo = fileInfo;
+ fileInfo->length = length;
+ if (offset < fileInfo->offset) {
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock))
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ fat = __CARDGetFatBlock(card);
+ while (fileInfo->offset < TRUNC(offset, card->sectorSize)) {
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock))
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ fileInfo->offset = offset;
+
+ *pcard = card;
+ return CARD_RESULT_READY;
+}
+
+static void ReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDFileInfo* fileInfo;
+ s32 length;
+
+ card = &__CARDBlock[chan];
+ if (result < 0)
+ goto error;
+
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ length = TRUNC(fileInfo->offset + card->sectorSize, card->sectorSize) - fileInfo->offset;
+ fileInfo->length -= length;
+ if (fileInfo->length <= 0)
+ goto error;
+
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += length;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+
+ ASSERTLINE(199, OFFSET(fileInfo->length, CARD_SEG_SIZE) == 0);
+ ASSERTLINE(200, OFFSET(fileInfo->offset, card->sectorSize) == 0);
+
+ result = __CARDRead(chan, card->sectorSize * (u32)fileInfo->iBlock,
+ (fileInfo->length < card->sectorSize) ? fileInfo->length : card->sectorSize, card->buffer,
+ ReadCallback);
+ if (result < 0)
+ goto error;
+
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(217, callback);
+ callback(chan, result);
+}
+
+s32 CARDReadAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ ASSERTLINE(250, buf && OFFSET(buf, 32) == 0);
+ ASSERTLINE(251, OFFSET(offset, CARD_SEG_SIZE) == 0);
+ ASSERTLINE(252, 0 < length && OFFSET(length, CARD_SEG_SIZE) == 0);
+
+ if (OFFSET(offset, CARD_SEG_SIZE) != 0 || OFFSET(length, CARD_SEG_SIZE) != 0)
+ return CARD_RESULT_FATAL_ERROR;
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0)
+ return result;
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDIsReadable(card, ent);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+
+ DCInvalidateRange(buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+
+ offset = (s32)OFFSET(fileInfo->offset, card->sectorSize);
+ length = (length < card->sectorSize - offset) ? length : card->sectorSize - offset;
+ result = __CARDRead(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock + offset, length, buf, ReadCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 CARDRead(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset) {
+ s32 result = CARDReadAsync(fileInfo, buf, length, offset, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(fileInfo->chan);
+}
+
+s32 CARDCancel(CARDFileInfo* fileInfo) {
+ BOOL enabled;
+ s32 result;
+ CARDControl* card;
+
+ ASSERTLINE(338, 0 <= fileInfo->chan && fileInfo->chan < 2);
+ ASSERTLINE(339, 0 <= fileInfo->fileNo && fileInfo->fileNo < CARD_MAX_FILE);
+
+ enabled = OSDisableInterrupts();
+
+ card = &__CARDBlock[fileInfo->chan];
+ result = CARD_RESULT_READY;
+ if (!card->attached)
+ result = CARD_RESULT_NOCARD;
+ else if (card->result == CARD_RESULT_BUSY && card->fileInfo == fileInfo) {
+ fileInfo->length = -1;
+ result = CARD_RESULT_CANCELED;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
diff --git a/src/revolution/card/CARDRename.c b/src/revolution/card/CARDRename.c
new file mode 100644
index 0000000000..0a76aca32b
--- /dev/null
+++ b/src/revolution/card/CARDRename.c
@@ -0,0 +1,70 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+s32 CARDRenameAsync(s32 chan, const char* old, const char* new, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ int fileNo;
+ int newNo;
+ int oldNo;
+
+ ASSERTLINE(0x56, 0 <= chan && chan < 2);
+ ASSERTLINE(0x57, *old != 0xff && *new != 0xff);
+ ASSERTLINE(0x58, *old != 0x00 && *new != 0x00);
+
+ if (old[0] == 0xFF || new[0] == 0xFF || old[0] == 0 || new[0] == 0)
+ return CARD_RESULT_FATAL_ERROR;
+ if (CARD_FILENAME_MAX < (u32)strlen(old) || CARD_FILENAME_MAX < (u32)strlen(new))
+ return CARD_RESULT_NAMETOOLONG;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ newNo = oldNo = -1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xFF)
+ continue;
+
+ if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) != 0
+ || memcmp(ent->company, card->diskID->company, sizeof(ent->company)) != 0)
+ continue;
+
+ if (__CARDCompareFileName(ent, old))
+ oldNo = fileNo;
+ if (__CARDCompareFileName(ent, new))
+ newNo = fileNo;
+ }
+
+ if (oldNo == -1)
+ return __CARDPutControlBlock(card, CARD_RESULT_NOFILE);
+ if (newNo != -1)
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+
+ ent = &dir[oldNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+
+ strncpy((char*)ent->fileName, new, CARD_FILENAME_MAX);
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+
+ return result;
+}
+
+s32 CARDRename(s32 chan, const char* old, const char* new) {
+ s32 result = CARDRenameAsync(chan, old, new, __CARDSyncCallback);
+ if (result < 0)
+ return result;
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDStat.c b/src/revolution/card/CARDStat.c
new file mode 100644
index 0000000000..f99455b42b
--- /dev/null
+++ b/src/revolution/card/CARDStat.c
@@ -0,0 +1,156 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+static void UpdateIconOffsets(CARDDir* ent, CARDStat* stat) {
+ u32 offset;
+ BOOL iconTlut;
+ int i;
+
+ offset = ent->iconAddr;
+ if (offset == 0xffffffff) {
+ stat->bannerFormat = 0;
+ stat->iconFormat = 0;
+ stat->iconSpeed = 0;
+ offset = 0;
+ }
+
+ iconTlut = FALSE;
+ switch (CARDGetBannerFormat(ent)) {
+ case CARD_STAT_BANNER_C8:
+ stat->offsetBanner = offset;
+ offset += CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = offset;
+ offset += 2 * 256;
+ break;
+ case CARD_STAT_BANNER_RGB5A3:
+ stat->offsetBanner = offset;
+ offset += 2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ default:
+ stat->offsetBanner = 0xffffffff;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ }
+
+ for (i = 0; i < CARD_ICON_MAX; ++i) {
+ switch (CARDGetIconFormat(ent, i)) {
+ case CARD_STAT_ICON_C8:
+ stat->offsetIcon[i] = offset;
+ offset += CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ iconTlut = TRUE;
+ break;
+ case CARD_STAT_ICON_RGB5A3:
+ stat->offsetIcon[i] = offset;
+ offset += 2 * CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ break;
+ default:
+ stat->offsetIcon[i] = 0xffffffff;
+ break;
+ }
+ }
+
+ if (iconTlut) {
+ stat->offsetIconTlut = offset;
+ offset += 2 * 256;
+ } else {
+ stat->offsetIconTlut = 0xffffffff;
+ }
+ stat->offsetData = offset;
+}
+
+s32 CARDGetStatus(s32 chan, s32 fileNo, CARDStat* stat) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ ASSERTLINE(172, 0 <= chan && chan < 2);
+ ASSERTLINE(173, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo)
+ return CARD_RESULT_FATAL_ERROR;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsReadable(card, ent);
+
+ if (result >= 0) {
+ memcpy(stat->gameName, ent->gameName, sizeof(stat->gameName));
+ memcpy(stat->company, ent->company, sizeof(stat->company));
+ stat->length = (u32)ent->length * card->sectorSize;
+ memcpy(stat->fileName, ent->fileName, CARD_FILENAME_MAX);
+ stat->time = ent->time;
+
+ stat->bannerFormat = ent->bannerFormat;
+ stat->iconAddr = ent->iconAddr;
+ stat->iconFormat = ent->iconFormat;
+ stat->iconSpeed = ent->iconSpeed;
+ stat->commentAddr = ent->commentAddr;
+
+ UpdateIconOffsets(ent, stat);
+ }
+
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDSetStatusAsync(s32 chan, s32 fileNo, CARDStat* stat, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ ASSERTLINE(231, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+ ASSERTLINE(232, 0 <= chan && chan < 2);
+ ASSERTMSGLINE(240, stat->iconAddr == 0xffffffff || stat->iconAddr < CARD_READ_SIZE, "CARDSetStatus[Async](): stat->iconAddr must be 0xffffffff or less than CARD_READ_SIZE.");
+ ASSERTMSGLINE(243, stat->commentAddr == 0xffffffff || (stat->commentAddr & 0x1FFF) <= 8128, "CARDSetStatus[Async](): comment strings (set by stat->commentAddr) must not cross 8KB byte boundary.");
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo ||
+ (stat->iconAddr != 0xffffffff && CARD_READ_SIZE <= stat->iconAddr) ||
+ (stat->commentAddr != 0xffffffff &&
+ CARD_SYSTEM_BLOCK_SIZE - CARD_COMMENT_SIZE < stat->commentAddr % CARD_SYSTEM_BLOCK_SIZE))
+ {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0)
+ return result;
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+
+ ent->bannerFormat = stat->bannerFormat;
+ ent->iconAddr = stat->iconAddr;
+ ent->iconFormat = stat->iconFormat;
+ ent->iconSpeed = stat->iconSpeed;
+ ent->commentAddr = stat->commentAddr;
+ UpdateIconOffsets(ent, stat);
+
+ if (ent->iconAddr == 0xffffffff) {
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+ }
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 CARDSetStatus(s32 chan, s32 fileNo, CARDStat* stat) {
+ s32 result = CARDSetStatusAsync(chan, fileNo, stat, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDStatEx.c b/src/revolution/card/CARDStatEx.c
new file mode 100644
index 0000000000..0ec84c1cea
--- /dev/null
+++ b/src/revolution/card/CARDStatEx.c
@@ -0,0 +1,124 @@
+#include <dolphin/dolphin.h>
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+s32 __CARDGetStatusEx(s32 chan, s32 fileNo, CARDDir* dirent) {
+ ASSERTLINE(85, 0 <= chan && chan < 2);
+ ASSERTLINE(86, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+
+ if ((fileNo < 0) || (fileNo >= CARD_MAX_FILE)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result = __CARDGetControlBlock(chan, &card);
+
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsReadable(card, ent);
+ if (result >= 0) {
+ memcpy(dirent, ent, 0x40);
+ }
+ return __CARDPutControlBlock(card, result);
+ }
+}
+
+s32 __CARDSetStatusExAsync(s32 chan, s32 fileNo, CARDDir* dirent, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u8* p;
+ s32 i;
+
+ ASSERTLINE(142, 0 <= fileNo && fileNo < CARD_MAX_FILE);
+ ASSERTLINE(143, 0 <= chan && chan < 2);
+ ASSERTLINE(144, *dirent->fileName != 0xff && *dirent->fileName != 0x00);
+
+ ASSERTMSGLINE(152, dirent->iconAddr == 0xffffffff || dirent->iconAddr < CARD_READ_SIZE, "CARDSetStatus[Async](): stat->iconAddr must be 0xffffffff or less than CARD_READ_SIZE.");
+ ASSERTMSGLINE(155, dirent->commentAddr == 0xffffffff || (dirent->commentAddr & 0x1FFF) <= 8128, "CARDSetStatus[Async](): comment strings (set by stat->commentAddr) must not cross 8KB byte boundary.");
+
+ if ((fileNo < 0) || (fileNo >= CARD_MAX_FILE) || ((u8) dirent->fileName[0] == 0xFF) || ((u8) dirent->fileName[0] == 0)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ for (p = dirent->fileName; p < (u8*)&dirent->time; p++) {
+ if (*p != 0) {
+ continue;
+ }
+ while ((++p) < (u8*)&dirent->time) {
+ *p = 0;
+ }
+ break;
+ }
+
+ if (dirent->permission & 0x20) {
+ memset(dirent->gameName, 0, sizeof(dirent->gameName));
+ memset(dirent->company, 0, sizeof(dirent->company));
+ }
+
+ if (dirent->permission & 0x40) {
+ memset(dirent->gameName, 0, sizeof(dirent->gameName));
+ }
+
+ if ((memcmp(&ent->fileName, &dirent->fileName, 32) != 0) || (memcmp(ent->gameName, dirent->gameName, 4) != 0) || (memcmp(ent->company, dirent->company, 2) != 0)) {
+ for(i = 0; i < CARD_MAX_FILE; i++) {
+ if (i != fileNo) {
+ CARDDir* ent = &dir[i]; // sure, just redeclare ent again...
+ if (((u8) ent->gameName[0] != 0xFF)
+ && (memcmp(&ent->gameName, &dirent->gameName, 4) == 0)
+ && (memcmp(&ent->company, &dirent->company, 2) == 0)
+ && (memcmp(&ent->fileName, &dirent->fileName, 0x20) == 0)) {
+ return __CARDPutControlBlock(card, -7);
+ }
+ }
+ }
+ memcpy(&ent->fileName, &dirent->fileName, 0x20);
+ memcpy(&ent->gameName, &dirent->gameName, 4);
+ memcpy(&ent->company, &dirent->company, 2);
+ }
+
+ ent->time = dirent->time;
+ ent->bannerFormat = dirent->bannerFormat;
+ ent->iconAddr = dirent->iconAddr;
+ ent->iconFormat = dirent->iconFormat;
+ ent->iconSpeed = dirent->iconSpeed;
+ ent->commentAddr = dirent->commentAddr;
+ ent->permission = dirent->permission;
+ ent->copyTimes = dirent->copyTimes;
+
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 __CARDSetStatusEx(s32 chan, s32 fileNo, CARDDir* dirent) {
+ s32 result = __CARDSetStatusExAsync(chan, fileNo, dirent, &__CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(chan);
+}
diff --git a/src/revolution/card/CARDUnlock.c b/src/revolution/card/CARDUnlock.c
new file mode 100644
index 0000000000..63313c5098
--- /dev/null
+++ b/src/revolution/card/CARDUnlock.c
@@ -0,0 +1,405 @@
+#include <stdlib.h>
+#include <dolphin/dolphin.h>
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+static u8 CardData[352] ATTRIBUTE_ALIGN(DOLPHIN_ALIGNMENT) = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x02, 0xFF, 0x00, 0x21, 0x13, 0x06, 0x12, 0x03, 0x12, 0x04,
+ 0x13, 0x05, 0x00, 0x92, 0x00, 0xFF, 0x00, 0x88, 0xFF, 0xFF, 0x00, 0x89, 0xFF, 0xFF, 0x00, 0x8A, 0xFF, 0xFF, 0x00,
+ 0x8B, 0xFF, 0xFF, 0x8F, 0x00, 0x02, 0xBF, 0x00, 0x88, 0x16, 0xFC, 0xDC, 0xD1, 0x16, 0xFD, 0x00, 0x00, 0x16, 0xFB,
+ 0x00, 0x01, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x03, 0x80, 0xFF, 0x00, 0x02, 0x94, 0x00, 0x27, 0x02, 0xBF, 0x00,
+ 0x8E, 0x1F, 0xDF, 0x24, 0xFF, 0x02, 0x40, 0x0F, 0xFF, 0x00, 0x98, 0x04, 0x00, 0x00, 0x9A, 0x00, 0x10, 0x00, 0x99,
+ 0x00, 0x00, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x94, 0x02, 0xBF, 0x86, 0x44, 0x02, 0xBF, 0x00, 0x88, 0x16, 0xFC, 0xDC,
+ 0xD1, 0x16, 0xFD, 0x00, 0x03, 0x16, 0xFB, 0x00, 0x01, 0x8F, 0x00, 0x02, 0xBF, 0x00, 0x8E, 0x03, 0x80, 0xCD, 0xD1,
+ 0x02, 0x94, 0x00, 0x48, 0x27, 0xFF, 0x03, 0x80, 0x00, 0x01, 0x02, 0x95, 0x00, 0x5A, 0x03, 0x80, 0x00, 0x02, 0x02,
+ 0x95, 0x80, 0x00, 0x02, 0x9F, 0x00, 0x48, 0x00, 0x21, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF,
+ 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC5, 0xFF, 0xFF, 0x03,
+ 0x40, 0x0F, 0xFF, 0x1C, 0x9F, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC7, 0xFF, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC6,
+ 0xFF, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC0, 0xFF, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x20, 0xFF, 0x03, 0x40, 0x0F,
+ 0xFF, 0x1F, 0x5F, 0x02, 0xBF, 0x00, 0x8E, 0x21, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x23, 0xFF, 0x12, 0x05, 0x12, 0x06,
+ 0x02, 0x9F, 0x80, 0xB5, 0x00, 0x21, 0x27, 0xFC, 0x03, 0xC0, 0x80, 0x00, 0x02, 0x9D, 0x00, 0x88, 0x02, 0xDF, 0x27,
+ 0xFE, 0x03, 0xC0, 0x80, 0x00, 0x02, 0x9C, 0x00, 0x8E, 0x02, 0xDF, 0x2E, 0xCE, 0x2C, 0xCF, 0x00, 0xF8, 0xFF, 0xCD,
+ 0x00, 0xF9, 0xFF, 0xC9, 0x00, 0xFA, 0xFF, 0xCB, 0x26, 0xC9, 0x02, 0xC0, 0x00, 0x04, 0x02, 0x9D, 0x00, 0x9C, 0x02,
+ 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static u32 next = 1;
+
+// prototypes
+static u32 exnor_1st(u32 data, u32 rshift);
+static u32 exnor(u32 data, u32 lshift);
+static u32 bitrev(u32 data);
+static s32 ReadArrayUnlock(s32 chan, u32 data, void* rbuf, s32 rlen, int mode);
+static u32 GetInitVal(void);
+static s32 DummyLen(void);
+static void InitCallback(void* _task);
+static void DoneCallback(void* _task);
+
+static int CARDRand(void) {
+ next = (next * 0x41C64E6D) + 0x3039;
+ return (next / 0x10000) & 0x7FFF;
+}
+
+static void CARDSrand(unsigned int seed) {
+ next = seed;
+}
+
+static u32 exnor_1st(u32 data, u32 rshift) {
+ u32 wk;
+ u32 work;
+ u32 i;
+
+ work = data;
+ for (i = 0; i < rshift; i++) {
+ wk = ~(work ^ (work >> 7) ^ (work >> 15) ^ (work >> 23));
+ work = (work >> 1) | ((wk << 30) & 0x40000000);
+ }
+
+ return work;
+}
+
+static u32 exnor(u32 data, u32 lshift) {
+ u32 wk;
+ u32 work;
+ u32 i;
+
+ work = data;
+ for (i = 0; i < lshift; i++) {
+ // 1bit Left Shift
+ wk = ~(work ^ (work << 7) ^ (work << 15) ^ (work << 23));
+ work = (work << 1) | ((wk >> 30) & 0x00000002);
+ }
+
+ return work;
+}
+
+static u32 bitrev(u32 data) {
+ u32 wk;
+ u32 i;
+ u32 k = 0;
+ u32 j = 1;
+
+ wk = 0;
+ for (i = 0; i < 32; i++) {
+ if (i > 15) {
+ if (i == 31)
+ wk |= (((data & (0x01 << 31)) >> 31) & 0x01);
+ else {
+ wk |= ((data & (0x01 << i)) >> j);
+ j += 2;
+ }
+ } else {
+ wk |= ((data & (0x01 << i)) << (31 - i - k));
+ k++;
+ }
+ }
+
+ return wk;
+}
+
+#define SEC_AD1(x) ((u8)(((x) >> 29) & 0x03))
+#define SEC_AD2(x) ((u8)(((x) >> 21) & 0xff))
+#define SEC_AD3(x) ((u8)(((x) >> 19) & 0x03))
+#define SEC_BA(x) ((u8)(((x) >> 12) & 0x7f))
+
+static s32 ReadArrayUnlock(s32 chan, u32 data, void* rbuf, s32 rlen, int mode) {
+ CARDControl* card;
+ BOOL err;
+ u8 cmd[5];
+
+ ASSERTLINE(240, 0 <= chan && chan < 2);
+
+ card = &__CARDBlock[chan];
+ if (!EXISelect(chan, 0, CARDFreq))
+ return CARD_RESULT_NOCARD;
+
+ data &= 0xfffff000;
+ memset(cmd, 0, 5);
+ cmd[0] = 0x52;
+ if (mode == 0) {
+ cmd[1] = SEC_AD1(data);
+ cmd[2] = SEC_AD2(data);
+ cmd[3] = SEC_AD3(data);
+ cmd[4] = SEC_BA(data);
+ } else {
+ cmd[1] = (u8)((data & 0xff000000) >> 24);
+ cmd[2] = (u8)((data & 0x00ff0000) >> 16);
+ }
+
+ err = FALSE;
+ err |= !EXIImmEx(chan, cmd, 5, 1);
+ err |= !EXIImmEx(chan, (u8* )card->workArea + (u32)sizeof(CARDID), card->latency, 1);
+ err |= !EXIImmEx(chan, rbuf, rlen, 0);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+static u32 GetInitVal(void) {
+ u32 tmp;
+ u32 tick;
+
+ tick = OSGetTick();
+ CARDSrand(tick);
+ tmp = 0x7fec8000;
+ tmp |= CARDRand();
+ tmp &= 0xfffff000;
+ return tmp;
+}
+
+static s32 DummyLen(void) {
+ u32 tick;
+ u32 wk;
+ s32 tmp;
+ u32 max;
+
+ wk = 1;
+ max = 0;
+ tick = OSGetTick();
+ CARDSrand(tick);
+
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ while ((tmp < 4) && (max < 10)) {
+ tick = OSGetTick();
+ tmp = (s32)(tick << wk);
+ wk++;
+ if (wk > 16)
+ wk = 1;
+ CARDSrand((u32)tmp);
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ max++;
+ }
+
+ if (tmp < 4)
+ tmp = 4;
+
+ return tmp;
+}
+
+s32 __CARDUnlock(s32 chan, u8 flashID[12]) {
+ u32 init_val;
+ u32 data;
+
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 fsts;
+ u32 wk, wk1;
+ u32 Ans1 = 0;
+ u32 Ans2 = 0;
+ u32* dp;
+ u8 rbuf[64];
+ u32 para1A = 0;
+ u32 para1B = 0;
+ u32 para2A = 0;
+ u32 para2B = 0;
+
+ CARDControl* card;
+ DSPTaskInfo* task;
+ CARDDecParam* param;
+ u8* input;
+ u8* output;
+
+ card = &__CARDBlock[chan];
+ task = &card->task;
+ param = (CARDDecParam*)card->workArea;
+ input = (u8*)((u8* )param + sizeof(CARDDecParam));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ fsts = 0;
+ init_val = GetInitVal();
+
+ dummy = DummyLen();
+ rlen = dummy;
+ if (ReadArrayUnlock(chan, init_val, rbuf, rlen, 0) < 0)
+ return CARD_RESULT_NOCARD;
+
+ rshift = (u32)(dummy * 8 + 1);
+ wk = exnor_1st(init_val, rshift);
+ wk1 = ~(wk ^ (wk >> 7) ^ (wk >> 15) ^ (wk >> 23));
+ card->scramble = (wk | ((wk1 << 31) & 0x80000000));
+ card->scramble = bitrev(card->scramble);
+ dummy = DummyLen();
+ rlen = 20 + dummy;
+ data = 0;
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0)
+ return CARD_RESULT_NOCARD;
+
+ dp = (u32* )rbuf;
+ para1A = *dp++;
+ para1B = *dp++;
+ Ans1 = *dp++;
+ para2A = *dp++;
+ para2B = *dp++;
+ para1A = (para1A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ para1B = (para1B ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ Ans1 ^= card->scramble;
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ para2A = (para2A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ para2B = (para2B ^ card->scramble);
+ rshift = (u32)(dummy * 8);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ rshift = 32 + 1;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ *(u32*)&input[0] = para2A;
+ *(u32*)&input[4] = para2B;
+
+ param->inputAddr = input;
+ param->inputLength = 8;
+ param->outputAddr = output;
+ param->aramAddr = 0;
+
+ DCFlushRange(input, 8);
+ DCInvalidateRange(output, 4);
+ DCFlushRange(param, sizeof(CARDDecParam));
+
+ task->priority = 255;
+ task->iram_mmem_addr = (u16*)OSCachedToPhysical(CardData);
+ task->iram_length = 0x160;
+ task->iram_addr = 0;
+ task->dsp_init_vector = 0x10;
+ task->init_cb = InitCallback;
+ task->res_cb = NULL;
+ task->done_cb = DoneCallback;
+ task->req_cb = NULL;
+ DSPAddTask(task);
+
+ dp = (u32*)flashID;
+ *dp++ = para1A;
+ *dp++ = para1B;
+ *dp = Ans1;
+
+ return CARD_RESULT_READY;
+}
+
+static void InitCallback(void* _task) {
+ s32 chan;
+ CARDControl* card;
+ DSPTaskInfo* task;
+ CARDDecParam* param;
+
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo*)&card->task == task)
+ break;
+ }
+
+ ASSERTLINE(514, 0 <= chan && chan < 2);
+
+ param = (CARDDecParam*)card->workArea;
+
+ DSPSendMailToDSP(0xff000000);
+ while (DSPCheckMailToDSP())
+ ;
+
+ DSPSendMailToDSP((u32)param);
+ while (DSPCheckMailToDSP())
+ ;
+}
+
+static void DoneCallback(void* _task) {
+ u8 rbuf[64];
+ u32 data;
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 unk;
+ u32 wk, wk1;
+ u32 Ans2;
+
+ s32 chan;
+ CARDControl* card;
+ s32 result;
+ DSPTaskInfo* task;
+ CARDDecParam* param;
+
+ u8* input;
+ u8* output;
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo* )&card->task == task)
+ break;
+ }
+
+ ASSERTLINE(563, 0 <= chan && chan < 2);
+
+ param = (CARDDecParam*)card->workArea;
+ input = (u8*)((u8*)param + sizeof(CARDDecParam));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ Ans2 = *(u32*)output;
+ dummy = DummyLen();
+ rlen = dummy;
+ data = ((Ans2 ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+
+ rshift = (u32)((dummy + 4 + card->latency) * 8 + 1);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ dummy = DummyLen();
+ rlen = dummy;
+ data = (((Ans2 << 16) ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+
+ result = __CARDReadStatus(chan, &unk);
+ if (!EXIProbe(chan)) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+
+ if (result == CARD_RESULT_READY && !(unk & 0x40)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_IOERROR;
+ }
+
+ __CARDMountCallback(chan, result);
+}
diff --git a/src/revolution/card/CARDWrite.c b/src/revolution/card/CARDWrite.c
new file mode 100644
index 0000000000..ad9fa4a273
--- /dev/null
+++ b/src/revolution/card/CARDWrite.c
@@ -0,0 +1,123 @@
+#include <dolphin/card.h>
+
+#include "__card.h"
+
+// prototypes
+static void WriteCallback(s32 chan, s32 result);
+static void EraseCallback(s32 chan, s32 result);
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto after;
+ }
+ fileInfo->length -= card->sectorSize;
+ if (fileInfo->length <= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = dir + fileInfo->fileNo;
+ ent->time = OSGetTime()/(__OSBusClock/4);
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ result = __CARDUpdateDir(chan, callback);
+ goto check;
+ } else {
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if ((fileInfo->iBlock < 5) || (fileInfo->iBlock >= card->cBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto after;
+ }
+ result = __CARDEraseSector(chan, card->sectorSize * fileInfo->iBlock, EraseCallback);
+check:;
+ if (result < 0) {
+ goto after;
+ }
+ }
+ } else {
+after:;
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(0x86, callback);
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ fileInfo = card->fileInfo;
+ ASSERTLINE(161, OFFSET(fileInfo->offset, card->sectorSize) == 0);
+ result = __CARDWrite(chan, card->sectorSize * fileInfo->iBlock, card->sectorSize, card->buffer, WriteCallback);
+ if (result < 0) {
+ goto after;
+ }
+ } else {
+after:;
+ callback = card->apiCallback;
+ card->apiCallback = NULL;
+ __CARDPutControlBlock(card, result);
+ ASSERTLINE(175, callback);
+ callback(chan, result);
+ }
+}
+
+s32 CARDWriteAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ ASSERTLINE(210, buf && ((u32) buf % 32) == 0);
+ ASSERTLINE(211, 0 < length);
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ ASSERTLINE(217, OFFSET(offset, card->sectorSize) == 0);
+ ASSERTLINE(218, OFFSET(length, card->sectorSize) == 0);
+
+ if (OFFSET(offset, card->sectorSize) != 0 || OFFSET(length, card->sectorSize) != 0)
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDIsWritable(card, ent);
+ if (result < 0)
+ return __CARDPutControlBlock(card, result);
+
+ DCStoreRange((void*)buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->buffer = (void*)buf;
+
+ result = __CARDEraseSector(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock, EraseCallback);
+ if (result < 0)
+ __CARDPutControlBlock(card, result);
+ return result;
+}
+
+s32 CARDWrite(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset) {
+ s32 result = CARDWriteAsync(fileInfo, buf, length, offset, __CARDSyncCallback);
+ if (result < 0) {
+ return result;
+ }
+
+ return __CARDSync(fileInfo->chan);
+}
diff --git a/src/revolution/card/__card.h b/src/revolution/card/__card.h
new file mode 100644
index 0000000000..de8a70b5ea
--- /dev/null
+++ b/src/revolution/card/__card.h
@@ -0,0 +1,104 @@
+#ifndef _REVOLUTION_CARD_INTERNAL_H_
+#define _REVOLUTION_CARD_INTERNAL_H_
+
+#include <revolution/card.h>
+#include <revolution/exi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// CARDStatEx
+s32 __CARDGetStatusEx(s32 chan, s32 fileNo, CARDDir* dirent);
+s32 __CARDSetStatusExAsync(s32 chan, s32 fileNo, CARDDir* dirent, CARDCallback callback);
+s32 __CARDSetStatusEx(s32 chan, s32 fileNo, CARDDir* dirent);
+
+// CARDUnlock
+s32 __CARDUnlock(s32 chan, u8 flashID[12]);
+
+// CARDRead
+s32 __CARDSeek(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDControl** pcard);
+
+// CARDRdwr
+s32 __CARDRead(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback);
+s32 __CARDWrite(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback);
+
+// CARDRaw
+s32 __CARDRawReadAsync(s32 chan, void* buf, s32 length, s32 offset, CARDCallback callback);
+s32 __CARDRawRead(s32 chan, void* buf, s32 length, s32 offset);
+s32 __CARDRawErase(s32 chan, s32 offset);
+s32 __CARDRawEraseAsync(s32 chan, s32 offset, CARDCallback callback);
+
+// CARDOpen
+BOOL __CARDCompareFileName(CARDDir* ent, const char* fileName);
+s32 __CARDAccess(CARDControl* card, CARDDir* ent);
+s32 __CARDIsPublic(CARDDir* ent);
+s32 __CARDGetFileNo(CARDControl* card, const char* fileName, s32* pfileNo);
+BOOL __CARDIsOpened(CARDControl* card, s32 fileNo);
+s32 __CARDIsWritable(CARDControl* card, CARDDir* ent);
+s32 __CARDIsReadable(CARDControl* card, CARDDir* ent);
+
+// CARDNet
+extern u16 __CARDVendorID;
+extern u8 __CARDPermMask;
+int __CARDEnableGlobal(int enable);
+int __CARDEnableCompany(int enable);
+
+// CARDMount
+void __CARDMountCallback(s32 chan, s32 result);
+void __CARDDisable(BOOL disable);
+
+// CARDFormat
+s32 CARDFormatAsync(s32 chan, CARDCallback callback);
+s32 __CARDFormatRegionAsync(s32 chan, u16 encode, CARDCallback callback);
+s32 __CARDFormatRegion(s32 chan, u16 encode);
+
+// CARDDir
+CARDDir* __CARDGetDirBlock(CARDControl* card);
+s32 __CARDUpdateDir(s32 chan, CARDCallback callback);
+
+// CARDCheck
+void __CARDCheckSum(void* ptr, int length, u16* checksum, u16* checksumInv);
+s32 __CARDVerify(CARDControl* card);
+
+// CARDBlock
+void* __CARDGetFatBlock(CARDControl* card);
+s32 __CARDAllocBlock(s32 chan, u32 cBlock, CARDCallback callback);
+s32 __CARDFreeBlock(s32 chan, u16 nBlock, CARDCallback callback);
+s32 __CARDUpdateFatBlock(s32 chan, u16* fat, CARDCallback callback);
+
+// CARDBios
+extern CARDControl __CARDBlock[2];
+
+extern DVDDiskID* __CARDDiskID;
+extern DVDDiskID __CARDDiskNone;
+
+void __CARDDefaultApiCallback(s32 chan, s32 result);
+void __CARDSyncCallback(s32 chan, s32 result);
+void __CARDExtHandler(s32 chan, OSContext* context);
+void __CARDExiHandler(s32 chan, OSContext* context);
+void __CARDTxHandler(s32 chan, OSContext* context);
+void __CARDUnlockedHandler(s32 chan, OSContext* context);
+int __CARDReadNintendoID(s32 chan, u32* id);
+s32 __CARDEnableInterrupt(s32 chan, BOOL enable);
+s32 __CARDReadStatus(s32 chan, u8* status);
+int __CARDReadVendorID(s32 chan, u16* id);
+s32 __CARDClearStatus(s32 chan);
+s32 __CARDSleep(s32 chan);
+s32 __CARDWakeup(s32 chan);
+s32 __CARDReadSegment(s32 chan, CARDCallback callback);
+s32 __CARDWritePage(s32 chan, CARDCallback callback);
+s32 __CARDErase(s32 chan, CARDCallback callback);
+s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback);
+void __CARDSetDiskID(const DVDDiskID* id);
+s32 __CARDGetControlBlock(s32 chan, CARDControl **pcard);
+s32 __CARDPutControlBlock(CARDControl* card, s32 result);
+s32 __CARDSync(s32 chan);
+u16 __CARDGetFontEncode(void);
+u16 __CARDSetFontEncode(u16 encode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DOLPHIN_CARD_INTERNAL_H_
diff --git a/src/revolution/homebuttonLib/HBMAnmController.cpp b/src/revolution/homebuttonLib/HBMAnmController.cpp
new file mode 100644
index 0000000000..d07f5e2b2f
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMAnmController.cpp
@@ -0,0 +1,33 @@
+#include "HBMAnmController.h"
+
+#include "nw4hbm/lyt/animation.h"
+#include "nw4hbm/lyt/group.h"
+
+namespace homebutton {
+
+ GroupAnmController::GroupAnmController() : mpGroup(), mpAnimGroup() {}
+
+ GroupAnmController::~GroupAnmController() {}
+
+ void GroupAnmController::do_calc() {
+ bool flag;
+
+ if (mState == ANIM_STATE_PLAY) {
+ calc();
+ flag = true;
+
+ mpAnimGroup->SetFrame(mFrame);
+ } else {
+ flag = false;
+ }
+
+ nw4hbm::lyt::PaneLinkList& list = mpGroup->GetPaneList();
+
+ for (nw4hbm::lyt::PaneLinkList::Iterator it = list.GetBeginIter(); it != list.GetEndIter();
+ it++)
+ {
+ it->mTarget->SetAnimationEnable(mpAnimGroup, flag, false);
+ }
+ }
+
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMAnmController.h b/src/revolution/homebuttonLib/HBMAnmController.h
new file mode 100644
index 0000000000..70621ee5d6
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMAnmController.h
@@ -0,0 +1,29 @@
+#ifndef HOMEBUTTON_ANM_CONTROLLER_H
+#define HOMEBUTTON_ANM_CONTROLLER_H
+
+#include "HBMFrameController.h"
+
+namespace nw4hbm {
+ namespace lyt {
+ class AnimTransform;
+ class Group;
+ } // namespace lyt
+} // namespace nw4hbm
+
+namespace homebutton {
+
+ class GroupAnmController : public FrameController {
+ public:
+ /* 0x00 (base) */
+ /* 0x20 */ nw4hbm::lyt::Group* mpGroup;
+ /* 0x24 */ nw4hbm::lyt::AnimTransform* mpAnimGroup;
+
+ GroupAnmController();
+ virtual ~GroupAnmController();
+
+ void do_calc();
+ }; // size = 0x28
+
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMBase.cpp b/src/revolution/homebuttonLib/HBMBase.cpp
new file mode 100644
index 0000000000..99beba4c99
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMBase.cpp
@@ -0,0 +1,2906 @@
+#include "HBMBase.h"
+
+#include <revolution/mem.h>
+#include <revolution/vi.h>
+#include "HBMAnmController.h"
+#include "HBMController.h"
+
+#include "nw4hbm/lyt/arcResourceAccessor.h"
+#include "nw4hbm/math/triangular.h"
+
+#include "new.h"
+
+#if HBM_REVISION == 1
+#define LN(rev1, rev2) rev1
+#else
+#define LN(rev1, rev2) rev2
+#endif
+
+struct AnmControllerTable {
+ /* 0x00 */ int pane;
+ /* 0x04 */ int anm;
+}; // size = 0x08
+
+static MEMAllocator sAllocator;
+static MEMAllocator sSoundAllocator;
+MEMAllocator* spAllocator = &sAllocator;
+homebutton::HomeButton* homebutton::HomeButton::spHomeButtonObj;
+#define gpHomeButton (homebutton::HomeButton::getInstance())
+
+/* DECOMP_FORCE(__FILE__);
+DECOMP_FORCE(NW4HBMAssert_String(mpDvdSoundArchive));
+DECOMP_FORCE("Cannot open \"%s\"");
+DECOMP_FORCE(NW4HBMAssert_String(mpMemorySoundArchive));
+DECOMP_FORCE("Cannot setup MemorySoundArchive"); */
+
+void* HBMAllocMem(u32 size) {
+ void* addr = MEMAllocFromAllocator(spAllocator, size);
+ return addr;
+}
+
+void HBMFreeMem(void* mem) {
+ MEMFreeToAllocator(spAllocator, mem);
+}
+
+void HBMCreate(const HBMDataInfo* pHBInfo) {
+ MEMHeapHandle hExpHeap = MEMCreateExpHeap(pHBInfo->mem, pHBInfo->memSize);
+
+ MEMInitAllocatorForExpHeap(&sAllocator, hExpHeap, 32);
+ spAllocator = &sAllocator;
+ nw4hbm::lyt::Layout::SetAllocator(&sAllocator);
+ homebutton::HomeButton::createInstance(pHBInfo);
+ gpHomeButton->create();
+}
+
+void HBMDelete() {
+ homebutton::HomeButton::deleteInstance();
+ MEMDestroyExpHeap((MEMHeapHandle)spAllocator->pHeap);
+}
+
+void HBMInit() {
+ gpHomeButton->init();
+}
+
+HBMSelectBtnNum HBMCalc(const HBMControllerData* pController) {
+ gpHomeButton->calc(pController);
+ return HBMGetSelectBtnNum();
+}
+
+void HBMDraw() {
+ gpHomeButton->draw_impl();
+}
+
+HBMSelectBtnNum HBMGetSelectBtnNum() {
+ return gpHomeButton->getSelectBtnNum();
+}
+
+void HBMSetAdjustFlag(bool flag) {
+ gpHomeButton->setAdjustFlag(flag);
+}
+
+void HBMStartBlackOut() {
+ gpHomeButton->startBlackOut();
+}
+
+void HBMPlaySound(int num) {
+ gpHomeButton->play_sound(num);
+}
+
+void HBMUpdateSoundArchivePlayer(void) {
+ gpHomeButton->updateSoundArchivePlayer();
+}
+
+void HBMSetSoundVolume(f32 volume) {
+ gpHomeButton->setSoundVolume(volume);
+}
+
+void HBMStopSound(void) {
+ gpHomeButton->stopSound(false);
+}
+
+void HBMCreateSound(const char* path, void* memBuf, u32 memSize) {
+ MEMInitAllocatorForFrmHeap(&sSoundAllocator, MEMCreateFrmHeapEx(memBuf, memSize, 0), 32);
+ gpHomeButton->initSound(path);
+}
+
+void HBMDeleteSound(void) {
+ gpHomeButton->deleteSound();
+ MEMDestroyFrmHeap((MEMHeapHandle)sSoundAllocator.pHeap);
+}
+
+void HBMUpdateSound(void) {
+ gpHomeButton->updateSound();
+}
+
+enum HBMAllocatorType {
+ HBM_ALLOCATOR_APPLI, /* application */
+ HBM_ALLOCATOR_LOCAL,
+ HBM_ALLOCATOR_NW4HBM,
+};
+
+static HBMAllocatorType getAllocatorType(const HBMDataInfo* pHBInfo) {
+ if (pHBInfo->pAllocator) {
+ return HBM_ALLOCATOR_APPLI;
+ } else if (pHBInfo->mem) {
+ return HBM_ALLOCATOR_LOCAL;
+ } else {
+ return HBM_ALLOCATOR_NW4HBM;
+ }
+}
+
+namespace homebutton {
+
+ void HomeButton::initSound(const char* path) {
+ if (!AICheckInit()) {
+ AIInit(NULL);
+ AXInit();
+ }
+
+ nw4hbm::snd::SoundSystem::InitSoundSystem();
+
+ void* pvVar4 =
+ MEMAllocFromAllocator(&sSoundAllocator, sizeof(nw4hbm::snd::NandSoundArchive));
+ if (pvVar4 != NULL) {
+ mpNandSoundArchive = new (pvVar4) nw4hbm::snd::NandSoundArchive();
+ }
+
+ NW4HBM_ASSERT(LN(3941, 3884), mpNandSoundArchive);
+ NW4R_DB_ASSERTMSG(LN(3946, 3889), mpNandSoundArchive->Open(path), "Cannot open \"%s\"",
+ path);
+
+ u32 size = mpNandSoundArchive->GetHeaderSize();
+ mpNandSoundArchive->LoadHeader(MEMAllocFromAllocator(&sSoundAllocator, size), size);
+ createSound(mpNandSoundArchive, 1);
+ }
+
+ void HomeButton::updateSound() {
+ updateSoundArchivePlayer();
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (i < ARRAY_SIZE(mpController)) {
+ mpController[i]->updateSound();
+ }
+ }
+ }
+
+ static const AnmControllerTable scAnmTable[12] = {
+ {0, 0}, {0, 2}, {4, 1}, {1, 0}, {1, 2}, {5, 1},
+ {2, 0}, {2, 2}, {6, 1}, {3, 0}, {3, 2}, {7, 1},
+ };
+
+ static const AnmControllerTable scGroupAnmTable[74] = {
+ {0, 0}, {1, 1}, {2, 0}, {3, 1}, {4, 2}, {4, 19}, {5, 3}, {5, 20}, {6, 4},
+ {6, 7}, {7, 4}, {7, 7}, {8, 4}, {8, 7}, {9, 4}, {9, 7}, {10, 4}, {10, 7},
+ {11, 5}, {12, 5}, {13, 6}, {13, 8}, {14, 14}, {14, 6}, {14, 8}, {15, 5}, {16, 6},
+ {16, 14}, {17, 11}, {17, 12}, {18, 11}, {18, 12}, {19, 13}, {20, 13}, {21, 9}, {21, 10},
+ {22, 9}, {22, 10}, {23, 9}, {23, 10}, {24, 9}, {24, 10}, {25, 9}, {25, 10}, {26, 9},
+ {26, 10}, {27, 9}, {27, 10}, {28, 9}, {28, 10}, {29, 9}, {29, 10}, {30, 9}, {30, 10},
+ {31, 15}, {31, 16}, {31, 17}, {31, 18}, {31, 21}, {32, 15}, {32, 16}, {32, 17}, {32, 18},
+ {32, 21}, {33, 15}, {33, 16}, {33, 17}, {33, 18}, {33, 21}, {34, 15}, {34, 16}, {34, 17},
+ {34, 18}, {34, 21},
+ };
+
+ const char* HomeButton::scCursorLytName[WPAD_MAX_CONTROLLERS] = {
+ "P1_Def.brlyt",
+ "P2_Def.brlyt",
+ "P3_Def.brlyt",
+ "P4_Def.brlyt",
+ };
+
+ const char* HomeButton::scCursorPaneName = "N_Trans";
+ const char* HomeButton::scCursorRotPaneName = "N_Rot";
+ const char* HomeButton::scCursorSRotPaneName = "N_SRot";
+
+ const char* HomeButton::scBtnName[4] = {"B_btnL_00", "B_btnL_01", "B_btnL_10", "B_btnL_11"};
+ const char* HomeButton::scTxtName[4] = {"T_btnL_00", "T_btnL_01", "T_btnL_10", "T_btnL_11"};
+
+ const char* HomeButton::scGrName[8] = {
+ "btnL_00_inOut", "btnL_01_inOut", "btnL_10_inOut", "btnL_11_inOut",
+ "btnL_00_psh", "btnL_01_psh", "btnL_10_psh", "btnL_11_psh",
+ };
+
+ const char* HomeButton::scAnimName[3] = {
+ "_cntBtn_in.brlan",
+ "_cntBtn_psh.brlan",
+ "_cntBtn_out.brlan",
+ };
+
+ const char* HomeButton::scPairGroupAnimName[15] = {
+ "_ltrIcn_on.brlan", "_optn_bar_psh.brlan", "_close_bar_psh.brlan",
+ "_hmMenu_bar_in.brlan", "_hmMenu_bar_psh.brlan", "_link_msg_in.brlan",
+ "_link_msg_out.brlan", "_cmn_msg_in.brlan", "_cmn_msg_out.brlan",
+ "_cntrl_up.brlan", "_cntrl_wndw_opn.brlan", "_cntrl_dwn.brlan",
+ "_hmMenu_bar_out.brlan", "_cmn_msg_rtrn.brlan", "_12btn_on.brlan",
+ };
+
+ const char* HomeButton::scPairGroupName[15] = {
+ "ltrIcn_on", "optn_bar_psh", "close_bar_psh", "hmMenu_bar_in", "hmMenu_bar_psh",
+ "link_msg_in", "link_msg_out", "cmn_msg_in", "cmn_msg_out", "cntrl_up",
+ "cntrl_wndw_opn", "cntrl_dwn", "hmMenu_bar_out", "cmn_msg_rtrn", "12btn_on",
+ };
+
+ const char* HomeButton::scGroupAnimName[22] = {
+ "_hmMenu_strt.brlan", "_hmMenu_fnsh.brlan", "_optn_bar_in.brlan",
+ "_optn_bar_out.brlan", "_optn_btn_in.brlan", "_optn_btn_psh.brlan",
+ "_vb_btn_wht_psh.brlan", "_optn_btn_out.brlan", "_vb_btn_ylw_psh.brlan",
+ "_sound_gry.brlan", "_sound_ylw.brlan", "_cmn_msg_btn_in.brlan",
+ "_cmn_msg_btn_out.brlan", "_cmn_msg_btn_psh.brlan", "_vb_btn_ylw_ylw.brlan",
+ "_btry_wink.brlan", "_btry_gry.brlan", "_btry_wht.brlan",
+ "_btry_wink_gry.brlan", "_close_bar_in.brlan", "_close_bar_out.brlan",
+ "_btry_red.brlan",
+ };
+
+ const char* HomeButton::scGroupName[35] = {
+ "hmMenu_strt", "hmMenu_fnsh", "hmMenuBck_strt", "hmMenuBck_fnsh",
+ "optn_bar_in", "optn_bar_out", "optnBtn_00_inOut", "optnBtn_01_inOut",
+ "optnBtn_10_inOut", "optnBtn_11_inOut", "optnBtn_20_inOut", "optnBtn_00_psh",
+ "optnBtn_01_psh", "optnBtn_10_psh", "optnBtn_11_psh", "optnBtn_20_psh",
+ "optnBtn_10_cntrl", "msgBtn_00_inOut", "msgBtn_01_inOut", "msgBtn_00_psh",
+ "msgBtn_01_psh", "vol_00", "vol_01", "vol_02",
+ "vol_03", "vol_04", "vol_05", "vol_06",
+ "vol_07", "vol_08", "vol_09", "plyr_00",
+ "plyr_01", "plyr_02", "plyr_03",
+ };
+
+ const char* HomeButton::scFuncPaneName[5] = {"let_icn_00", "N_plyr_00", "N_plyr_01",
+ "N_plyr_02", "N_plyr_03"};
+
+ const char* HomeButton::scFuncTouchPaneName[10] = {
+ "B_btn_00", "B_bar_10", "B_optnBtn_00", "B_optnBtn_01", "B_optnBtn_10",
+ "B_optnBtn_11", "B_optnBtn_20", "B_BtnA", "B_BtnB", "cntrl_00",
+ };
+
+ const char* HomeButton::scFuncTextPaneName[3] = {"T_msg_00", "T_msg_01", "T_Dialog"};
+
+ const char* HomeButton::scBatteryPaneName[WPAD_MAX_CONTROLLERS][4] = {
+ {"btryPwr_00_0", "btryPwr_00_1", "btryPwr_00_2", "btryPwr_00_3"},
+ {"btryPwr_01_0", "btryPwr_01_1", "btryPwr_01_2", "btryPwr_01_3"},
+ {"btryPwr_02_0", "btryPwr_02_1", "btryPwr_02_2", "btryPwr_02_3"},
+ {"btryPwr_03_0", "btryPwr_03_1", "btryPwr_03_2", "btryPwr_03_3"},
+ };
+
+ void HomeButton::createInstance(const HBMDataInfo* pHBInfo) {
+ if (void* pMem = HBMAllocMem(sizeof(*spHomeButtonObj))) {
+ spHomeButtonObj = new (pMem) HomeButton(pHBInfo);
+ }
+ }
+
+ void HomeButton::deleteInstance() {
+ spHomeButtonObj->~HomeButton();
+ HBMFreeMem(spHomeButtonObj);
+ spHomeButtonObj = NULL;
+ }
+
+ void HomeButton::BlackFader::init(int maxFrame) {
+ frame_ = 0;
+ maxFrame_ = maxFrame;
+ state_ = 0;
+ }
+
+ void HomeButton::BlackFader::calc() {
+ if (state_ == 1) {
+ frame_++;
+ } else if (state_ == 2) {
+ frame_--;
+ }
+
+ if (frame_ < 0) {
+ frame_ = 0;
+ } else if (frame_ > maxFrame_) {
+ frame_ = maxFrame_;
+ }
+ }
+
+ bool HomeButton::BlackFader::isDone() {
+ if (state_ == 1) {
+ if (frame_ == maxFrame_) {
+ return true;
+ }
+ }
+
+ if (state_ == 2) {
+ if (frame_ == 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void HomeButton::draw_impl() {
+ u8 alpha;
+ BlackFader* pFader;
+ HomeButton* pHBM;
+ int i;
+
+ mpLayout->Draw(mDrawInfo);
+
+ if (mpHBInfo->cursor == 0) {
+ for (i = WPAD_MAX_CONTROLLERS - 1; i >= WPAD_CHAN0; i--) {
+ mpCursorLayout[i]->Draw(mDrawInfo);
+ }
+ }
+
+ pFader = &mFader;
+ alpha = pFader->getFrame() * 255 / pFader->getMaxFrame();
+ initgx();
+ GXSetTevColor(GX_TEVREG0, pFader->GetColor(alpha));
+ drawBlackPlate(-1000.0f, -1000.0f, 1000.0f, 1000.0f);
+ }
+
+ int HomeButton::findGroupAnimator(int pane, int anm) {
+ for (int i = 0; i < (int)ARRAY_SIZE(scGroupAnmTable); i++) {
+ if (scGroupAnmTable[i].pane == pane && scGroupAnmTable[i].anm == anm) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ HBMSelectBtnNum HomeButton::getSelectBtnNum() {
+ if (mState != 18) {
+ return HBM_SELECT_NULL;
+ }
+
+ return mSelectBtnNum;
+ }
+
+ void HomeButton::updateSoundArchivePlayer() {
+ if (mpSoundArchivePlayer != NULL) {
+ mpSoundArchivePlayer->Update();
+ }
+ }
+
+ void HomeButton::fadeout_sound(f32 gain) {
+ if (mSelectBtnNum == HBM_SELECT_BTN3) {
+ return;
+ }
+
+ if (mEndInitSoundFlag) {
+ AXSetMasterVolume(gain * 32768.0f);
+ }
+
+ if (mpSoundArchivePlayer != NULL) {
+ for (int i = 0; i < mpSoundArchivePlayer->GetSoundPlayerCount(); i++) {
+ mpSoundArchivePlayer->GetSoundPlayer(i).SetVolume(gain);
+ }
+ }
+ }
+
+ void HomeButton::setSoundVolume(f32 volume) {
+ AXSetMasterVolume(volume * 32768.0f);
+
+ if (mpSoundArchivePlayer != NULL) {
+ for (int i = 0; i < mpSoundArchivePlayer->GetSoundPlayerCount(); i++) {
+ mpSoundArchivePlayer->GetSoundPlayer(i).SetVolume(volume);
+ }
+ }
+ }
+
+ void HomeButton::play_sound(int id) {
+ int ret = 0;
+
+ if (mpHBInfo->sound_callback != NULL) {
+ ret = mpHBInfo->sound_callback(5, id);
+ }
+
+ if (ret == 0) {
+ PlaySeq(id);
+ }
+ }
+
+ void HomeButton::stopSound(bool checkFlag) {
+ if (mpSoundArchivePlayer != NULL) {
+ for (int i = 0; i < mpSoundArchivePlayer->GetSoundPlayerCount(); i++) {
+ mpSoundArchivePlayer->GetSoundPlayer(i).StopAllSound(0);
+ }
+ }
+
+ if (checkFlag && !mEndInitSoundFlag) {
+ return;
+ }
+
+ AXFXReverbHiShutdown(&mAxFxReverb);
+ AXRegisterAuxACallback(mAuxCallback, mpAuxContext);
+ AXFXSetHooks(mAxFxAlloc, mAxFxFree);
+ AXSetMasterVolume(mAppVolume[0]);
+ AXSetAuxAReturnVolume(mAppVolume[1]);
+ AXSetAuxBReturnVolume(mAppVolume[2]);
+ }
+
+ HomeButton::HomeButton(const HBMDataInfo* pHBInfo)
+ : mpHBInfo(pHBInfo), mpLayout(NULL), mpPaneManager(NULL), mFader(30) {
+ mState = 2;
+ mSelectBtnNum = HBM_SELECT_NULL;
+ mSelectAnmNum = -1;
+ mMsgCount = 0;
+ mSequence = eSeq_Normal;
+ mForcusSEWaitTime = 0;
+ mLetterFlag = false;
+ mBar0AnmRev = 0;
+ mBar1AnmRev = 0;
+ mBar0AnmRevHold = 0;
+ mBar1AnmRevHold = 0;
+ mAdjustFlag = false;
+ mReassignedFlag = false;
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ OSCreateAlarm(&mAlarm[i]);
+ OSCreateAlarm(&mSpeakerAlarm[i]);
+ }
+
+ OSCreateAlarm(&mSimpleSyncAlarm);
+
+ mpSoundArchivePlayer = NULL;
+ mpDvdSoundArchive = NULL;
+ mpMemorySoundArchive = NULL;
+ mpNandSoundArchive = NULL;
+ mpSoundHeap = NULL;
+ mpSoundHandle = NULL;
+ }
+
+ HomeButton::~HomeButton() {
+ int i;
+
+ mpResAccessor->~ArcResourceAccessor();
+ HBMFreeMem(mpResAccessor);
+
+ mpLayout->~Layout();
+ HBMFreeMem(mpLayout);
+
+ if (mpHBInfo->cursor == 0) {
+ for (i = 0; i < (int)ARRAY_SIZE(mpCursorLayout); i++) {
+ mpCursorLayout[i]->~Layout();
+ HBMFreeMem(mpCursorLayout[i]);
+ }
+ }
+
+ for (i = 0; i < mAnmNum; i++) {
+ mpAnmController[i]->~GroupAnmController();
+ HBMFreeMem(mpAnmController[i]);
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpPairGroupAnmController); i++) {
+ mpPairGroupAnmController[i]->~GroupAnmController();
+ HBMFreeMem(mpPairGroupAnmController[i]);
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpGroupAnmController); i++) {
+ mpGroupAnmController[i]->~GroupAnmController();
+ HBMFreeMem(mpGroupAnmController[i]);
+ }
+
+ mpHomeButtonEventHandler->HomeButtonEventHandler::~HomeButtonEventHandler();
+ HBMFreeMem(mpHomeButtonEventHandler);
+
+ mpPaneManager->~PaneManager();
+ HBMFreeMem(mpPaneManager);
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ mpController[i]->~Controller();
+ HBMFreeMem(mpController[i]);
+ }
+
+ mpRemoteSpk->~RemoteSpk();
+ HBMFreeMem(mpRemoteSpk);
+ mpRemoteSpk = NULL;
+
+ HBMFreeMem(mpLayoutName);
+ HBMFreeMem(mpAnmName);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ OSCancelAlarm(&mAlarm[i]);
+ OSCancelAlarm(&mSpeakerAlarm[i]);
+ }
+
+ OSCancelAlarm(&mSimpleSyncAlarm);
+ }
+
+ void HomeButton::create() {
+ int i;
+ char anmNameBuf[64];
+
+ mInitFlag = false;
+ mForceSttInitProcFlag = false;
+ mForceSttFadeInProcFlag = false;
+
+ set_config();
+ set_text();
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpResAccessor))) {
+ mpResAccessor = new (pMem) nw4hbm::lyt::ArcResourceAccessor();
+ }
+
+ NW4HBM_ASSERT(623, mpResAccessor);
+ mpResAccessor->Attach(mpHBInfo->layoutBuf, "arc");
+
+ if (!mpHBInfo->cursor) {
+ for (i = 0; i < (int)ARRAY_SIZE(mpCursorLayout); i++) {
+ if (void* pMem = HBMAllocMem(sizeof(*mpCursorLayout)[i])) {
+ mpCursorLayout[i] = new (pMem) nw4hbm::lyt::Layout();
+ }
+
+ NW4HBM_ASSERT(635, mpCursorLayout[i]);
+
+ void* lytRes = mpResAccessor->GetResource(0, scCursorLytName[i], NULL);
+
+ mpCursorLayout[i]->Build(lytRes, mpResAccessor);
+ }
+ }
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpLayout))) {
+ mpLayout = new (pMem) nw4hbm::lyt::Layout();
+ }
+
+ {
+ void* lytRes = mpResAccessor->GetResource(0, mpLayoutName, NULL);
+ mpLayout->Build(lytRes, mpResAccessor);
+ }
+
+ for (i = 0; i < mAnmNum; i++) {
+ std::strcpy(anmNameBuf, mpAnmName);
+ std::strcat(anmNameBuf, scAnimName[scAnmTable[i].anm]);
+
+ void* lpaRes = mpResAccessor->GetResource(0, anmNameBuf, NULL);
+ NW4HBM_ASSERT_CHECK_NULL(665, lpaRes);
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpAnmController)[i])) {
+ mpAnmController[i] = new (pMem) GroupAnmController();
+ }
+
+ NW4HBM_ASSERT(671, mpAnmController[i]);
+
+ mpAnmController[i]->mpAnimGroup = mpLayout->CreateAnimTransform(lpaRes, mpResAccessor);
+
+ mpAnmController[i]->mpGroup =
+ mpLayout->GetGroupContainer()->FindGroupByName(scGrName[scAnmTable[i].pane]);
+
+ nw4hbm::lyt::PaneLinkList& list = mpAnmController[i]->mpGroup->GetPaneList();
+
+ for (nw4hbm::lyt::PaneLinkList::Iterator it = list.GetBeginIter();
+ it != list.GetEndIter(); it++)
+ {
+ it->mTarget->BindAnimation(mpAnmController[i]->mpAnimGroup, false);
+ }
+
+ mpAnmController[i]->init(ANIM_TYPE_FORWARD,
+ mpAnmController[i]->mpAnimGroup->GetFrameMax(), 0,
+ mpHBInfo->frameDelta);
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpGroupAnmController); i++) {
+ std::strcpy(anmNameBuf, mpAnmName);
+ std::strcat(anmNameBuf, scGroupAnimName[scGroupAnmTable[i].anm]);
+
+ void* lpaRes = mpResAccessor->GetResource(0, anmNameBuf, NULL);
+
+ NW4HBM_ASSERT_CHECK_NULL(697, lpaRes);
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpGroupAnmController)[i])) {
+ mpGroupAnmController[i] = new (pMem) GroupAnmController();
+ }
+
+ NW4HBM_ASSERT(703, mpGroupAnmController[i]);
+
+ mpGroupAnmController[i]->mpAnimGroup =
+ mpLayout->CreateAnimTransform(lpaRes, mpResAccessor);
+
+ mpGroupAnmController[i]->mpGroup = mpLayout->GetGroupContainer()->FindGroupByName(
+ scGroupName[scGroupAnmTable[i].pane]);
+
+ nw4hbm::lyt::PaneLinkList& list = mpGroupAnmController[i]->mpGroup->GetPaneList();
+
+ for (nw4hbm::lyt::PaneLinkList::Iterator it = list.GetBeginIter();
+ it != list.GetEndIter(); it++)
+ {
+ it->mTarget->BindAnimation(mpGroupAnmController[i]->mpAnimGroup, false);
+ }
+
+ mpGroupAnmController[i]->init(ANIM_TYPE_FORWARD,
+ mpGroupAnmController[i]->mpAnimGroup->GetFrameMax(), 0,
+ mpHBInfo->frameDelta);
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpPairGroupAnmController); i++) {
+ std::strcpy(anmNameBuf, mpAnmName);
+ std::strcat(anmNameBuf, scPairGroupAnimName[i]);
+
+ void* lpaRes = mpResAccessor->GetResource(0, anmNameBuf, NULL);
+
+ NW4HBM_ASSERT_CHECK_NULL(729, lpaRes);
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpPairGroupAnmController)[i])) {
+ mpPairGroupAnmController[i] = new (pMem) GroupAnmController();
+ }
+
+ NW4HBM_ASSERT(735, mpPairGroupAnmController[i]);
+
+ mpPairGroupAnmController[i]->mpAnimGroup =
+ mpLayout->CreateAnimTransform(lpaRes, mpResAccessor);
+
+ mpPairGroupAnmController[i]->mpGroup =
+ mpLayout->GetGroupContainer()->FindGroupByName(scPairGroupName[i]);
+
+ nw4hbm::lyt::PaneLinkList& list = mpPairGroupAnmController[i]->mpGroup->GetPaneList();
+
+ for (nw4hbm::lyt::PaneLinkList::Iterator it = list.GetBeginIter();
+ it != list.GetEndIter(); it++)
+ {
+ it->mTarget->BindAnimation(mpPairGroupAnmController[i]->mpAnimGroup, false);
+ }
+
+ mpPairGroupAnmController[i]->init(
+ ANIM_TYPE_FORWARD, mpPairGroupAnmController[i]->mpAnimGroup->GetFrameMax(), 0,
+ mpHBInfo->frameDelta);
+ }
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpHomeButtonEventHandler))) {
+ mpHomeButtonEventHandler = new (pMem) HomeButtonEventHandler(this);
+ }
+
+ NW4HBM_ASSERT(758, mpHomeButtonEventHandler);
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpPaneManager))) {
+ mpPaneManager =
+ new (pMem) gui::PaneManager(mpHomeButtonEventHandler, NULL, spAllocator);
+ }
+
+ NW4HBM_ASSERT(765, mpPaneManager);
+
+ mpPaneManager->createLayoutScene(*mpLayout);
+ mpPaneManager->setAllComponentTriggerTarget(false);
+
+ for (i = 0; i < mButtonNum; i++) {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scBtnName[i], true);
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(true);
+ }
+
+ if (void* pMem = HBMAllocMem(sizeof(*mpRemoteSpk))) {
+ mpRemoteSpk = new (pMem) RemoteSpk(mpHBInfo->spkSeBuf);
+ }
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (void* pMem = HBMAllocMem(sizeof(*mpController)[i])) {
+ mpController[i] = new (pMem) Controller(i, mpRemoteSpk);
+ }
+ }
+
+ mpPaneManager->setDrawInfo(&mDrawInfo);
+
+ nw4hbm::math::VEC2 ad_v(1.0f / mpHBInfo->adjust.x, 1.0f);
+ mDrawInfo.SetLocationAdjustScale(ad_v);
+ mDrawInfo.SetLocationAdjust(mAdjustFlag);
+
+ nw4hbm::math::MTX34 viewMtx;
+ nw4hbm::math::MTX34Identity(&viewMtx);
+ mDrawInfo.SetViewMtx(viewMtx);
+
+ init_msg();
+ }
+
+ static u32 get_comma_length(char* pBuf) {
+ u32 len;
+
+ for (len = 0; pBuf[len]; len++) {
+ if (pBuf[len] == ',') {
+ break;
+ }
+ }
+
+ return len;
+ }
+
+ void HomeButton::set_config() {
+ int i = 0, j = 0;
+
+ char* pConfig = static_cast<char*>(mpHBInfo->configBuf);
+ char* pEnd = static_cast<char*>(mpHBInfo->configBuf) + mpHBInfo->memSize;
+ u32 len = get_comma_length(pConfig);
+
+ mpLayoutName = static_cast<char*>(HBMAllocMem(len + 1));
+
+ NW4HBM_ASSERT(827, mpLayoutName);
+
+ std::strncpy(mpLayoutName, pConfig, len);
+ mpLayoutName[len] = '\0';
+
+ pConfig += len + 1;
+
+ len = get_comma_length(pConfig);
+ mpAnmName = static_cast<char*>(HBMAllocMem(len + 1));
+
+ NW4HBM_ASSERT(837, mpAnmName);
+
+ std::strncpy(mpAnmName, pConfig, len);
+ mpAnmName[len] = '\0';
+
+ pConfig += len;
+
+ // comma operator generates a temporary
+ i = 0;
+ j = 0;
+ for (; pConfig[i]; i++) {
+ if (pConfig[i] == ',') {
+ if (pConfig[i + 1] == '1') {
+ mDialogFlag[j] = true;
+ } else {
+ mDialogFlag[j] = false;
+ }
+
+ j++;
+ }
+ }
+
+ mButtonNum = j;
+ mAnmNum = mButtonNum * 3;
+ }
+
+ void HomeButton::set_text() {
+ int i = 0, j = 0, k = 0;
+ bool flag = false; // more accurately insideStringFlag
+
+ wchar_t* message = static_cast<wchar_t*>(mpHBInfo->msgBuf);
+ for (; message[i]; i++) {
+ if (message[i] == L'\"') {
+ message[i] = '\0';
+
+ if (!flag) {
+ flag = true;
+
+ mpText[j][k] = &message[i + 1];
+ j++;
+
+ if (j == 0x07) {
+ j = 0;
+ k++;
+ }
+ } else {
+ flag = false;
+ }
+ }
+ }
+ }
+
+ void HomeButton::init() {
+ int i;
+
+ if (mInitFlag) {
+ return;
+ }
+
+ mInitFlag = true;
+
+ mForceSttInitProcFlag = false;
+ mForceSttFadeInProcFlag = false;
+ mForceStopSyncFlag = false;
+
+ if (mSelectBtnNum != HBM_SELECT_BTN3) {
+ mEndInitSoundFlag = false;
+ }
+
+ GXSetCullMode(GX_CULL_NONE);
+
+ for (i = 0; i < (int)ARRAY_SIZE(mPaneCounter); i++) {
+ mPaneCounter[i] = 0;
+ }
+
+ mState = 0;
+ mSequence = eSeq_Normal;
+ mReassignedFlag = false;
+
+ updateTrigPane();
+
+ mpPaneManager->init();
+
+ reset_guiManager(-1);
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (i < WPAD_MAX_CONTROLLERS) {
+ mPadDrawTime[i] = 0;
+
+ mpController[i]->setInValidPos();
+ mpController[i]->clrKpadButton();
+ mpController[i]->disconnect();
+ mpController[i]->clrBatteryFlag();
+ mpController[i]->initCallback();
+ mpController[i]->initSound();
+
+ mOnPaneVibFrame[i] = 0.0f;
+ mOnPaneVibWaitFrame[i] = 0.0f;
+ }
+ }
+
+ mDrawInfo.SetViewRect(mpLayout->GetLayoutRect());
+ mpLayout->GetRootPane()->FindPaneByName(scFuncPaneName[0], true)->SetVisible(false);
+
+ // 2-6: "B_optnBtn_XX" entries in scFuncTouchPaneName
+ for (i = 2; i < 7; i++) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTouchPaneName[i], true)
+ ->SetVisible(false);
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(scFuncTextPaneName); i++) {
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTextPaneName[i], true)->SetVisible(false);
+ }
+
+ mpRemoteSpk->Start();
+
+ if (mpSoundArchivePlayer != NULL) {
+ for (i = 0; i < mpSoundArchivePlayer->GetSoundPlayerCount(); i++) {
+ mpSoundArchivePlayer->GetSoundPlayer(i).SetVolume(1.0f);
+ }
+ }
+
+ calc(NULL);
+ mFader.init(30);
+ }
+
+ void HomeButton::init_msg() {
+ int i, len;
+
+ for (i = 0; i < (int)ARRAY_SIZE(scFuncTextPaneName); i++) {
+ nw4hbm::lyt::Pane* p_pane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTextPaneName[i], true);
+ nw4hbm::lyt::TextBox* p_text =
+ nw4hbm::ut::DynamicCast<nw4hbm::lyt::TextBox*, nw4hbm::lyt::Pane>(p_pane);
+
+ p_text->SetString(mpText[mpHBInfo->region][i], 0);
+ }
+ }
+
+ void HomeButton::init_volume() {
+ int i, anm_no;
+
+ mVolumeNum = getVolume();
+ setVolume(10);
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->connect();
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (i < mVolumeNum) {
+ anm_no = findGroupAnimator(i + 0x15, 10);
+ mpGroupAnmController[anm_no]->start();
+ } else {
+ anm_no = findGroupAnimator(i + 0x15, 9);
+ mpGroupAnmController[anm_no]->start();
+ }
+ }
+ }
+
+ void HomeButton::init_vib() {
+ int anm_no;
+
+ mVibFlag = getVibFlag();
+ if (mVibFlag) {
+ anm_no = findGroupAnimator(13, 6);
+ mpGroupAnmController[anm_no]->start();
+
+ anm_no = findGroupAnimator(14, 8);
+ mpGroupAnmController[anm_no]->start();
+ } else {
+ anm_no = findGroupAnimator(13, 8);
+ mpGroupAnmController[anm_no]->start();
+
+ anm_no = findGroupAnimator(14, 6);
+ mpGroupAnmController[anm_no]->start();
+ }
+ }
+
+ void HomeButton::init_sound() {
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(0, 0);
+ }
+
+ mAppVolume[0] = AXGetMasterVolume();
+ mAppVolume[1] = AXGetAuxAReturnVolume();
+ mAppVolume[2] = AXGetAuxBReturnVolume();
+
+ AXFXGetHooks(&mAxFxAlloc, &mAxFxFree);
+ AXGetAuxACallback(&mAuxCallback, &mpAuxContext);
+ AXFXSetHooks(&HBMAllocMem, &HBMFreeMem);
+
+ mAxFxReverb.preDelay = 0.0f;
+ mAxFxReverb.time = 2.5f;
+ mAxFxReverb.coloration = 0.5f;
+ mAxFxReverb.damping = 0.0f;
+ mAxFxReverb.crosstalk = 0.0f;
+ mAxFxReverb.mix = 1.0f;
+
+ AXFXReverbHiInit(&mAxFxReverb);
+ AXRegisterAuxACallback(&AXFXReverbHiCallback, &mAxFxReverb);
+ AXSetMasterVolume(0x8000);
+ AXSetAuxAReturnVolume(0);
+ AXSetAuxBReturnVolume(0);
+
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(1, 0);
+ }
+
+ mEndInitSoundFlag = true;
+ }
+
+ void HomeButton::init_battery(const HBMControllerData* pController) {
+ int anm_no;
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (pController->wiiCon[i].kpad) {
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetVisible(true);
+ }
+
+ anm_no = findGroupAnimator(i + 31, 17);
+ mpGroupAnmController[anm_no]->start();
+ mControllerFlag[i] = true;
+
+ getController(i)->getInfoAsync(&mWpadInfo[i]);
+ } else {
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetVisible(false);
+ }
+
+ anm_no = findGroupAnimator(i + 31, 16);
+ mpGroupAnmController[anm_no]->start();
+ mControllerFlag[i] = false;
+ }
+ }
+
+ reset_battery();
+ mGetPadInfoTime = 0;
+ }
+
+ void HomeButton::calc(const HBMControllerData* pController) {
+ int i;
+
+ mpPaneManager->calc();
+
+ for (i = 0; i < mAnmNum; i++) {
+ mpAnmController[i]->do_calc();
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpPairGroupAnmController); i++) {
+ mpPairGroupAnmController[i]->do_calc();
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(mpGroupAnmController); i++) {
+ mpGroupAnmController[i]->do_calc();
+ }
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (mOnPaneVibFrame[i] > 0.0f) {
+ if (!mControllerFlag[i] || !getController(i)->isRumbling()) {
+ mOnPaneVibFrame[i] = 0.0f;
+ mOnPaneVibWaitFrame[i] = 0.0f;
+
+ if (getController(i)->isRumbling()) {
+ getController(i)->stopMotor();
+ }
+
+ continue;
+ }
+
+ mOnPaneVibFrame[i] -= mpHBInfo->frameDelta;
+ if (mOnPaneVibFrame[i] <= 0.0f || mState == 17) {
+ getController(i)->stopMotor();
+ mOnPaneVibFrame[i] = 0.0f;
+ mOnPaneVibWaitFrame[i] = 9.0f;
+ }
+ continue;
+ } else if (mOnPaneVibWaitFrame[i] > 0.0f) {
+ mOnPaneVibWaitFrame[i] -= mpHBInfo->frameDelta;
+
+ if (mOnPaneVibWaitFrame[i] <= 0.0f) {
+ mOnPaneVibWaitFrame[i] = 0.0f;
+ }
+ }
+ }
+
+ switch (mState) {
+ case 0:
+ if (mpHBInfo->backFlag) {
+ mSelectAnmNum = findGroupAnimator(2, 0);
+
+ mpLayout->GetRootPane()->FindPaneByName("back_00", true)->SetVisible(false);
+
+ mpLayout->GetRootPane()->FindPaneByName("back_02", true)->SetVisible(true);
+ } else {
+ mSelectAnmNum = findGroupAnimator(0, 0);
+
+ mpLayout->GetRootPane()->FindPaneByName("back_00", true)->SetVisible(true);
+
+ mpLayout->GetRootPane()->FindPaneByName("back_02", true)->SetVisible(false);
+ }
+
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ if (pController) {
+ mState = 1;
+ init_battery(pController);
+ }
+
+ break;
+
+ case 1:
+ if (!mpGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ init_volume();
+ init_vib();
+ init_sound();
+ play_sound(0);
+
+ mState = 2;
+ }
+
+ break;
+
+ case 2:
+ if (mLetterFlag && !mpPairGroupAnmController[0]->isPlaying()) {
+ mpLayout->GetRootPane()->FindPaneByName(scFuncPaneName[0], true)->SetVisible(true);
+ mpPairGroupAnmController[0]->setAnimType(2);
+ mpPairGroupAnmController[0]->start();
+ } else if (!mLetterFlag) {
+ mpLayout->GetRootPane()->FindPaneByName(scFuncPaneName[0], true)->SetVisible(false);
+ mpPairGroupAnmController[0]->setState(0);
+ }
+
+#if HBM_REVISION == 1
+ if (mpPairGroupAnmController[14]->isPlaying() &&
+ mpPairGroupAnmController[14]->getCurrentFrame() <
+ mpPairGroupAnmController[14]->getDelta())
+ {
+ mpPairGroupAnmController[14]->setCurrentFrame(0.0f);
+ mpPairGroupAnmController[14]->setState(0);
+ }
+#endif
+ break;
+
+ case 3:
+ if (!mpGroupAnmController[mSelectAnmNum]->isPlaying() && mSelectAnmNum != 5) {
+ reset_battery();
+ mSelectAnmNum = 5;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+ }
+
+ mWaitStopMotorCount--;
+ if (mWaitStopMotorCount <= 0) {
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ WPADDisconnect(i);
+ }
+
+ mState = 4;
+ }
+
+ break;
+
+ case 4:
+ if (mpGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ u32 type;
+ if (WPADProbe(i, &type) != WPAD_ENODEV) {
+ break;
+ }
+ }
+
+ if (i < WPAD_MAX_CONTROLLERS) {
+ break;
+ }
+
+ mState = 5;
+ mMsgCount = 0;
+
+#if HBM_REVISION > 1
+ mSoundRetryCnt = 0;
+#endif
+
+ mSimpleSyncCallback = WPADSetSimpleSyncCallback(&SimpleSyncCallback);
+ mEndSimpleSyncFlag = false;
+
+ mSimpleSyncFlag = WPADStartFastSimpleSync();
+
+ if (!mSimpleSyncFlag) {
+ setSimpleSyncAlarm(0);
+ }
+
+ break;
+
+ case 5:
+ case 6:
+ if (!mSimpleSyncFlag || mpPairGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ if (mMsgCount == 0) {
+ reset_control();
+ reset_btn();
+ mpPairGroupAnmController[14]->setAnimType(2);
+ mpPairGroupAnmController[14]->start();
+ }
+
+ if (mControllerFlag[3]) {
+ if (mState != 6) {
+ if (getController(3)->isPlayingSoundId(5)) {
+ mState = 6;
+ mMsgCount = 0xDF2;
+ }
+
+#if HBM_REVISION > 1
+ mSoundRetryCnt++;
+ if (mSoundRetryCnt > 0xDF2) {
+ mState = 6;
+ mMsgCount = 0xDF2;
+ }
+#endif
+ } else {
+ mMsgCount++;
+ if (mMsgCount > 0xE10) {
+ mState = 7;
+ }
+ }
+ } else {
+ mMsgCount++;
+ if (mMsgCount > 3600) {
+ mState = 7;
+
+ if (!WPADStopSimpleSync()) {
+ setSimpleSyncAlarm(1);
+ }
+ }
+ }
+
+ break;
+
+ case 7:
+ if (!mEndSimpleSyncFlag) {
+ break;
+ }
+
+ WPADSetSimpleSyncCallback(mSimpleSyncCallback);
+ mSimpleSyncCallback = NULL;
+
+#if HBM_REVISION > 1
+ mpRemoteSpk->ClearPcm();
+#endif
+
+ reset_guiManager(-1);
+
+ mSelectAnmNum = 6;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 8;
+
+#if HBM_REVISION > 1
+ mpPairGroupAnmController[14]->setAnimType(0);
+#endif
+
+ play_sound(21);
+
+ break;
+
+ case 8:
+ if (mpPairGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ if (mSelectAnmNum == 13) {
+ reset_window();
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[2], true)
+ ->SetVisible(false);
+ } else if (mSelectAnmNum == 6) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[0], true)
+ ->SetVisible(false);
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[1], true)
+ ->SetVisible(false);
+ }
+
+ mState = 2;
+ break;
+
+ case 9:
+ if (mpGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ if (mVibFlag) {
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->stopMotor();
+ }
+ }
+
+ mState = 2;
+
+ break;
+
+ case 10:
+ if (mpPairGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ mBar0AnmRev = 0;
+ mBar1AnmRev = 0;
+ mBar0AnmRevHold = 0;
+ mBar1AnmRevHold = 0;
+
+ if (mSequence != eSeq_Control) {
+ // 2-6: "B_optnBtn_XX" entries in scFuncTouchPaneName
+ for (i = 2; i < 7; i++) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTouchPaneName[i], true)
+ ->SetVisible(false);
+ }
+
+ mState = 2;
+ } else if (mSequence == eSeq_Control) // ? already true
+ {
+ // mpLayout->GetRootPane()->FindPaneByName("bar_00", true)->SetVisible(false);
+
+ mSelectAnmNum = 10;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mSelectAnmNum = 1;
+ play_sound(7);
+
+ mState = 8;
+ }
+
+ updateTrigPane();
+
+ break;
+
+ case 11:
+ if (mpAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ mSelectAnmNum = 7;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 12;
+
+ break;
+
+ case 12:
+ if (mpPairGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ updateTrigPane();
+ reset_btn();
+ mState = 2;
+
+ break;
+
+ case 13:
+ if (mpGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ // only case is SELECT_NULL
+ if (mSelectBtnNum >= 0) {
+ mFader.start();
+ mState = 19;
+ mFadeOutSeTime = mFader.getMaxFrame();
+
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(mSelectBtnNum != HBM_SELECT_BTN3 ? 3 : 6,
+ mFadeOutSeTime);
+ }
+ } else {
+ updateTrigPane();
+ mSelectAnmNum = 13;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+ mState = 8;
+ }
+
+ reset_guiManager(-1);
+ break;
+
+ case 14:
+ if (mpPairGroupAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ calc_fadeoutAnm();
+ break;
+
+ case 15:
+ if (mpAnmController[mSelectAnmNum]->isPlaying()) {
+ break;
+ }
+
+ mFader.start();
+ mState = 19;
+ mFadeOutSeTime = mFader.getMaxFrame();
+
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(mSelectBtnNum != HBM_SELECT_BTN3 ? 3 : 6, mFadeOutSeTime);
+ }
+
+ break;
+
+ case 16:
+ GroupAnmController* anim = mpGroupAnmController[mSelectAnmNum];
+
+ if (!anim->isPlaying()) {
+ mState = 17;
+
+ fadeout_sound(0.0f);
+ } else {
+ f32 restFrame = anim->getMaxFrame() - anim->getCurrentFrame();
+ fadeout_sound(restFrame / mFadeOutSeTime);
+ }
+ break;
+
+ case 17:
+ mState = 18;
+
+ if (mSelectBtnNum != HBM_SELECT_BTN3) {
+ stopSound(true);
+ }
+
+ setVolume(mVolumeNum);
+ bool result = WPADSaveConfig(NULL);
+ mpRemoteSpk->Stop();
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (i < WPAD_MAX_CONTROLLERS) {
+ mpController[i]->clearCallback();
+ }
+ }
+
+ NW4HBM_ASSERT(LN(1671, 1649), result);
+
+ if (mSelectBtnNum != HBM_SELECT_BTN3 && mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(4, 0);
+ }
+
+ mInitFlag = false;
+ break;
+
+ case 18:
+ mState = 2;
+ break;
+
+ case 19:
+ if (mForceSttInitProcFlag) {
+ init_battery(pController);
+ mForceSttInitProcFlag = false;
+ }
+
+ if (mForceSttFadeInProcFlag) {
+ init_volume();
+ init_vib();
+ mForceSttFadeInProcFlag = false;
+ }
+
+ if (mFader.isDone()) {
+ if (mForceStopSyncFlag) {
+ if (!mEndSimpleSyncFlag) {
+ break;
+ }
+
+ WPADSetSimpleSyncCallback(mSimpleSyncCallback);
+ mSimpleSyncCallback = NULL;
+ mForceStopSyncFlag = false;
+ }
+
+ if (mForceEndMsgAnmFlag) {
+ int anm_no;
+
+ anm_no = 5;
+ mpPairGroupAnmController[anm_no]->initFrame();
+ mpPairGroupAnmController[anm_no]->stop();
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[0], true)
+ ->SetVisible(false);
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[1], true)
+ ->SetVisible(false);
+
+ anm_no = 14;
+ if (mpPairGroupAnmController[anm_no]->isPlaying()) {
+ mpPairGroupAnmController[anm_no]->initFrame();
+ mpPairGroupAnmController[anm_no]->stop();
+ }
+ }
+
+ mState = 17;
+
+ VISetBlack(true);
+ VIFlush();
+ fadeout_sound(0.0f);
+ } else {
+ f32 restFrame = mFader.getMaxFrame() - mFader.getFrame();
+
+ fadeout_sound(restFrame / mFadeOutSeTime);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ if (mBar0AnmRev && isUpBarActive()) {
+ if (mBar0AnmRev && mBar0AnmRev != mBar0AnmRevHold) {
+ mpPairGroupAnmController[mBar0AnmRev]->start();
+ mBar0AnmRevHold = mBar0AnmRev;
+ }
+
+ mBar0AnmRev = 0;
+ }
+
+ if (mBar1AnmRev && isDownBarActive()) {
+ if (mBar1AnmRev && mBar1AnmRev != mBar1AnmRevHold) {
+ mpGroupAnmController[mBar1AnmRev]->start();
+ mBar1AnmRevHold = mBar1AnmRev;
+ }
+
+ mBar1AnmRev = 0;
+ }
+
+ if (pController) {
+ update(pController);
+ }
+
+ mpLayout->Animate(0);
+ mpLayout->CalculateMtx(mDrawInfo);
+
+ if (!mpHBInfo->cursor) {
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ mpCursorLayout[i]->CalculateMtx(mDrawInfo);
+ }
+ }
+
+ if (mForcusSEWaitTime <= 2) {
+ mForcusSEWaitTime++;
+ }
+ }
+
+ void HomeButton::calc_fadeoutAnm() {
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTextPaneName[2], true)->SetVisible(false);
+
+ if (mpHBInfo->backFlag) {
+ mSelectAnmNum = findGroupAnimator(3, 1);
+ } else {
+ mSelectAnmNum = findGroupAnimator(1, 1);
+ }
+
+ mpGroupAnmController[mSelectAnmNum]->start();
+ mState = 16;
+ mFadeOutSeTime = mpGroupAnmController[mSelectAnmNum]->getMaxFrame();
+
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(2, mFadeOutSeTime);
+ }
+ }
+
+ void HomeButton::calc_battery(int chan) {
+ // presumably j because it is the second index
+ for (int j = 0; j < (int)ARRAY_SIZE(scBatteryPaneName[chan]); j++) {
+ if (j < mWpadInfo[chan].battery) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scBatteryPaneName[chan][j], true)
+ ->SetVisible(true);
+ } else {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scBatteryPaneName[chan][j], true)
+ ->SetVisible(false);
+ }
+ }
+
+ if (mWpadInfo[chan].battery < 2) {
+ int anm_no = findGroupAnimator(chan + 31, 21);
+ mpGroupAnmController[anm_no]->start();
+ } else {
+ int anm_no = findGroupAnimator(chan + 31, 17);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ if (mGetPadInfoTime < 100) {
+ mGetPadInfoTime = 0;
+ }
+
+ getController(chan)->clrBatteryFlag();
+ }
+
+ static void SpeakerCallback(OSAlarm* alm, OSContext*) {
+ u32 data = (u32)OSGetAlarmUserData(alm);
+ int chan = (data >> 16) & 0xFFFF;
+ int id = data & 0xFFFF;
+
+ HomeButton* pHBObj = HomeButton::getInstance();
+
+ if (!WPADIsSpeakerEnabled(chan) || !pHBObj->getController(chan)->isPlayReady()) {
+ pHBObj->setSpeakerAlarm(chan, 50);
+ } else {
+ pHBObj->getController(chan)->playSound(pHBObj->GetSoundArchivePlayer(), id);
+ }
+ }
+
+ static void MotorCallback(OSAlarm* alm, OSContext*) {
+ Controller* pController = (Controller*)OSGetAlarmUserData(alm);
+
+ pController->stopMotor();
+ }
+
+ void HomeButton::setSpeakerAlarm(int chan, int msec) {
+ OSSetAlarmUserData(&mSpeakerAlarm[chan], (void*)((chan << 16) | (chan + 2)));
+ OSCancelAlarm(&mSpeakerAlarm[chan]);
+ OSSetAlarm(&mSpeakerAlarm[chan], OSMillisecondsToTicks(msec), &SpeakerCallback);
+ }
+
+ static void RetrySimpleSyncCallback(OSAlarm* alm, OSContext*) {
+ HomeButton* pHBObj = HomeButton::getInstance();
+ int type = (int)OSGetAlarmUserData(alm);
+ bool retrySuccessFlag = false;
+
+ if (type == 0) {
+ if (WPADStartFastSimpleSync()) {
+ pHBObj->setSimpleSyncFlag(true);
+ retrySuccessFlag = true;
+ }
+ } else {
+ if (WPADStopSimpleSync()) {
+ retrySuccessFlag = true;
+ }
+ }
+
+ if (!retrySuccessFlag) {
+ pHBObj->setSimpleSyncAlarm(type);
+ }
+ }
+
+ static void SimpleSyncCallback(s32 result, s32 num) {
+ if (result == 1) {
+ HomeButton::getInstance()->setEndSimpleSyncFlag(true);
+ }
+
+ HomeButton::getInstance()->callSimpleSyncCallback(result, num);
+ }
+
+ void HomeButton::setSimpleSyncAlarm(int type) {
+ OSCancelAlarm(&mSimpleSyncAlarm);
+ OSSetAlarmUserData(&mSimpleSyncAlarm, (void*)type);
+ OSSetAlarm(&mSimpleSyncAlarm, OSMillisecondsToTicks(100), &RetrySimpleSyncCallback);
+ }
+
+ void HomeButton::callSimpleSyncCallback(s32 result, s32 num) {
+ if (mSimpleSyncCallback) {
+ (*mSimpleSyncCallback)(result, num);
+ }
+ }
+
+ void HomeButton::update(const HBMControllerData* pController) {
+#define IsValidDevType_(x) \
+ ((x)->use_devtype != WPAD_DEV_CLASSIC && (x)->kpad->dev_type != WPAD_DEV_CLASSIC)
+#define IsValidDevType2_(x) \
+ ((x)->use_devtype == WPAD_DEV_CLASSIC && (x)->kpad->dev_type == WPAD_DEV_CLASSIC)
+
+ int i, anm_no;
+
+ mFader.calc();
+
+ for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (pController->wiiCon[i].kpad) {
+ if (pController->wiiCon[i].kpad->wpad_err != WPAD_ENODEV) {
+ if (mPadDrawTime[i] > 5) {
+ if (pController->wiiCon[i].kpad->wpad_err == WPAD_ESUCCESS) {
+ bool pointerEnableFlag;
+
+ if (IsValidDevType_(&pController->wiiCon[i])) {
+ if (pController->wiiCon[i].kpad->dpd_valid_fg > 0) {
+ pointerEnableFlag = true;
+ } else {
+ pointerEnableFlag = false;
+ }
+ } else {
+ pointerEnableFlag = true;
+ }
+
+ mpController[i]->setKpad(&pController->wiiCon[i], pointerEnableFlag);
+
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetVisible(true);
+ }
+ }
+ } else {
+ mPadDrawTime[i]++;
+ }
+
+ if (IsValidDevType_(&pController->wiiCon[i]) &&
+ pController->wiiCon[i].kpad->dpd_valid_fg <= 0)
+ {
+ s32 result;
+ u32 type;
+
+ result = WPADProbe(i, &type);
+
+ if (pController->wiiCon[i].kpad->wpad_err != WPAD_EBUSY &&
+ result != WPAD_EBUSY)
+ {
+ mpController[i]->setInValidPos();
+ }
+ }
+ } else {
+ mpController[i]->setInValidPos();
+
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetVisible(false);
+ }
+ }
+
+ if (!mControllerFlag[i]) {
+ mControllerFlag[i] = true;
+
+ getController(i)->getInfoAsync(&mWpadInfo[i]);
+
+ anm_no = findGroupAnimator(i + 31, 17);
+ mpGroupAnmController[anm_no]->start();
+
+#if HBM_REVISION > 1
+ anm_no = findGroupAnimator(i + 31, 18);
+ mpGroupAnmController[anm_no]->stop();
+#endif
+
+ anm_no = findGroupAnimator(i + 31, 15);
+ mpGroupAnmController[anm_no]->start();
+
+ play_sound(i + 17);
+ getController(i)->connect();
+ getController(i)->startMotor();
+
+ OSSetAlarmUserData(&mAlarm[i], getController(i));
+ OSCancelAlarm(&mAlarm[i]);
+ OSSetAlarm(&mAlarm[i], OSMillisecondsToTicks(300), &MotorCallback);
+
+ setSpeakerAlarm(i, 400);
+ }
+
+ if (pController->wiiCon[i].kpad->wpad_err == WPAD_ESUCCESS) {
+ nw4hbm::math::VEC3 vec;
+
+ if (IsValidDevType2_(&pController->wiiCon[i])) {
+ vec = nw4hbm::math::VEC3(0.0f, 0.0f, 15.0f);
+ } else {
+ Vec2 v = pController->wiiCon[i].kpad->horizon;
+ f32 mRad = nw4hbm::math::Atan2Deg(-v.y, v.x);
+
+ vec = nw4hbm::math::VEC3(0.0f, 0.0f, mRad);
+ }
+
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorRotPaneName, true)
+ ->SetRotate(vec);
+
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorSRotPaneName, true)
+ ->SetRotate(vec);
+ }
+
+ if (mGetPadInfoTime > 100) {
+ getController(i)->getInfoAsync(&mWpadInfo[i]);
+ }
+
+ update_controller(i);
+
+ if (!mpHBInfo->cursor) {
+ update_posController(i);
+ }
+ }
+
+ if (getController(i)->getBatteryFlag()) {
+ calc_battery(i);
+ }
+ } else {
+ if (mControllerFlag[i]) {
+ anm_no = findGroupAnimator(i + 31, 17);
+ mpGroupAnmController[anm_no]->start();
+
+ anm_no = findGroupAnimator(i + 31, 18);
+ mpGroupAnmController[anm_no]->start();
+
+ for (int j = 0; j < (int)ARRAY_SIZE(scBatteryPaneName[i]); j++) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scBatteryPaneName[i][j], true)
+ ->SetVisible(false);
+ }
+
+ if (!mpHBInfo->cursor) {
+ mpCursorLayout[i]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetVisible(false);
+ }
+
+ mPadDrawTime[i] = 0;
+ mControllerFlag[i] = false;
+
+ mpController[i]->setInValidPos();
+ mpController[i]->clrKpadButton();
+ mpController[i]->disconnect();
+ }
+
+ reset_guiManager(i);
+ }
+ }
+
+ if (mGetPadInfoTime > 100) {
+ mGetPadInfoTime = 0;
+ } else {
+ mGetPadInfoTime++;
+ }
+#undef IsValidDevType2_
+#undef IsValidDevType_
+ }
+
+ void HomeButton::update_controller(int id) {
+ int anm_no;
+
+ if (isActive()) {
+ HBController* pCon = mpController[id]->getController();
+
+ f32 x = pCon->x * 608.0f / 2.0f;
+ f32 y = pCon->y * 456.0f / 2.0f;
+
+ if (mAdjustFlag) {
+ x *= mpHBInfo->adjust.x;
+ y *= mpHBInfo->adjust.y;
+ }
+
+ mpPaneManager->update(id, x, -y, pCon->trig, pCon->hold, pCon->release, pCon);
+
+ //! @bug: probably meant to be the or operator instead of an OR?
+ if (((pCon->trig & 0x10000000) | (pCon->trig & WPAD_BUTTON_HOME)) && isActive()) {
+ if (mSequence == eSeq_Control) {
+ mpPaneManager->update(id, 0.0f, -180.0f, 0, 0, 0, 0);
+
+#if HBM_REVISION > 1
+ mSelectAnmNum = 4;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+#endif
+
+ mSelectAnmNum = 2;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+ int anm_no = 11; // ?
+ mpPairGroupAnmController[anm_no]->start();
+
+ mState = 10;
+ mSequence = eSeq_Normal;
+ play_sound(8);
+ } else if (mSequence == eSeq_Normal) {
+ if (mpPairGroupAnmController[3]->isPlaying()) {
+ mpPairGroupAnmController[3]->stop();
+ }
+
+ if (mpPairGroupAnmController[12]->isPlaying()) {
+ mpPairGroupAnmController[12]->stop();
+ }
+
+ mSelectBtnNum = HBM_SELECT_HOMEBTN;
+
+ mSelectAnmNum = 4;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 14;
+ play_sound(1);
+ }
+ } else if (mSequence == eSeq_Control && isActive()) {
+ if ((pCon->trig & WPAD_BUTTON_MINUS) || (pCon->trig & 0x10000)) {
+ if (mVolumeNum > 0) {
+ mVolumeNum--;
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 10);
+ mpGroupAnmController[anm_no]->stop();
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 9);
+ mpGroupAnmController[anm_no]->start();
+
+ anm_no = findGroupAnimator(11, 5);
+ mpGroupAnmController[anm_no]->start();
+
+ if (mVolumeNum == 0) {
+ play_sound(12);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ } else {
+ play_sound(10);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ }
+ } else {
+ play_sound(13);
+ }
+ } else if ((pCon->trig & WPAD_BUTTON_PLUS) || (pCon->trig & 0x20000)) {
+ if (mVolumeNum < 10) {
+ anm_no = findGroupAnimator(mVolumeNum + 21, 9);
+ mpGroupAnmController[anm_no]->stop();
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 10);
+ mpGroupAnmController[anm_no]->start();
+
+ mVolumeNum++;
+
+ anm_no = findGroupAnimator(12, 5);
+ mpGroupAnmController[anm_no]->start();
+
+ if (mVolumeNum == 10) {
+ play_sound(11);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ } else {
+ play_sound(9);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ }
+ } else {
+ play_sound(13);
+ }
+ }
+ }
+ } else if (mSequence == eSeq_Control && mState == 5 &&
+ !mpPairGroupAnmController[mSelectAnmNum]->isPlaying())
+ {
+ HBController* pCon = mpController[id]->getController();
+ if (pCon->trig) {
+ mMsgCount = 0xE10;
+ }
+ }
+ }
+
+ void HomeButton::update_posController(int id) {
+ HBController* pCon = mpController[id]->getController();
+ nw4hbm::ut::Rect layoutRect = mpLayout->GetLayoutRect();
+
+ f32 x = pCon->x * layoutRect.right;
+ f32 y = pCon->y * layoutRect.bottom;
+ nw4hbm::math::VEC2 pos(x, y);
+
+ mpCursorLayout[id]
+ ->GetRootPane()
+ ->FindPaneByName(scCursorPaneName, true)
+ ->SetTranslate(pos);
+ }
+
+ void HomeButton::updateTrigPane() {
+ int i;
+
+ switch (mSequence) {
+ case eSeq_Normal:
+ for (i = 0; i < (int)ARRAY_SIZE(scFuncTouchPaneName); i++) {
+ if (i < 2 || i == 9) {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(true);
+ } else {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(false);
+ }
+ }
+
+ break;
+
+ case eSeq_Control:
+ for (i = 0; i < (int)ARRAY_SIZE(scFuncTouchPaneName); i++) {
+ if ((i > 0 && i < 7) || i == 9) {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(true);
+ } else {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(false);
+ }
+ }
+
+ break;
+
+ case eSeq_Cmn:
+ for (i = 0; i < (int)ARRAY_SIZE(scFuncTouchPaneName); i++) {
+ if (i < 7 || i == 9) {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(false);
+ } else {
+ nw4hbm::lyt::Pane* pTouchPane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTouchPaneName[i], true);
+
+ mpPaneManager->getPaneComponentByPane(pTouchPane)->setTriggerTarget(true);
+ }
+ }
+
+ break;
+ }
+ }
+
+ void HomeButton::startPointEvent(const char* pPane, void* pData) {
+ int anm_no;
+ int btn_no = getPaneNo(pPane);
+ HBController* pCon = static_cast<HBController*>(pData);
+ bool onFlag = false;
+
+ if (isActive() && btn_no != -1 && !mPaneCounter[btn_no]) {
+ if (mSequence != eSeq_Cmn && btn_no < mButtonNum) {
+ anm_no = findAnimator(btn_no, 0);
+ mpAnmController[anm_no]->start();
+
+ if (mSequence == eSeq_Normal) {
+ setForcusSE();
+ onFlag = true;
+ }
+ } else {
+ switch (btn_no - mButtonNum) {
+ case 0:
+ if (mSequence == eSeq_Normal) {
+ if (isUpBarActive()) {
+ mpPairGroupAnmController[3]->start();
+ mBar0AnmRevHold = 3;
+ mBar0AnmRev = 0;
+ setForcusSE();
+ onFlag = true;
+ } else {
+ mBar0AnmRev = 3;
+ }
+ }
+
+ break;
+
+ case 1:
+ case 9:
+ if (mSequence == eSeq_Normal) {
+ anm_no = findGroupAnimator(4, 2);
+
+ if (homebutton::HomeButton::isDownBarActive()) {
+ mpGroupAnmController[anm_no]->start();
+ mBar1AnmRevHold = anm_no;
+ mBar1AnmRev = 0;
+ setForcusSE();
+ onFlag = true;
+ } else {
+ mBar1AnmRev = anm_no;
+ }
+ } else if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(4, 19);
+
+ if (isDownBarActive()) {
+ mpGroupAnmController[anm_no]->start();
+ mBar1AnmRevHold = anm_no;
+ mBar1AnmRev = 0;
+ setForcusSE();
+ onFlag = true;
+ } else {
+ mBar1AnmRev = anm_no;
+ }
+ }
+
+ break;
+
+ case 2:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(6, 4);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 3:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(7, 4);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 4:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(8, 4);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 5:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(9, 4);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 6:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(10, 4);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 7:
+ if (mSequence == eSeq_Cmn) {
+ anm_no = findGroupAnimator(17, 11);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+
+ case 8:
+ if (mSequence == eSeq_Cmn) {
+ anm_no = findGroupAnimator(18, 11);
+ mpGroupAnmController[anm_no]->start();
+
+ setForcusSE();
+ onFlag = true;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (btn_no == mButtonNum + 1 || btn_no == mButtonNum + 9) {
+ mPaneCounter[mButtonNum + 1]++;
+ mPaneCounter[mButtonNum + 9]++;
+ } else {
+ mPaneCounter[btn_no]++;
+ }
+
+ if (onFlag && pCon) {
+ if (!getController(pCon->chan)->isRumbling() && mOnPaneVibWaitFrame[pCon->chan] <= 0.0f)
+ {
+ mOnPaneVibFrame[pCon->chan] = 3.0f;
+ getController(pCon->chan)->startMotor();
+ }
+ }
+ }
+
+ void HomeButton::startLeftEvent(const char* pPane) {
+ int anm_no;
+ int btn_no = getPaneNo(pPane);
+
+ if (0 < mPaneCounter[btn_no]) {
+ if (btn_no == mButtonNum + 1 || btn_no == mButtonNum + 9) {
+ mPaneCounter[mButtonNum + 1]--;
+ mPaneCounter[mButtonNum + 9]--;
+ } else {
+ mPaneCounter[btn_no]--;
+ }
+ }
+
+ if (isActive() && btn_no != -1 && !mPaneCounter[btn_no]) {
+ if (mSequence != eSeq_Cmn && btn_no < mButtonNum) {
+ anm_no = findAnimator(btn_no, 2);
+ mpAnmController[anm_no]->start();
+ } else {
+ switch (btn_no - mButtonNum) {
+ case 0:
+ if (mSequence == eSeq_Normal) {
+ if (isUpBarActive()) {
+ mpPairGroupAnmController[12]->start();
+ mBar0AnmRevHold = 12;
+ mBar0AnmRev = 0;
+ } else {
+ mBar0AnmRev = 12;
+ }
+ }
+
+ break;
+
+ case 1:
+ case 9:
+ if (mSequence == eSeq_Normal) {
+ anm_no = findGroupAnimator(5, 3);
+
+ if (isDownBarActive()) {
+ mpGroupAnmController[anm_no]->start();
+ mBar1AnmRevHold = anm_no;
+ mBar1AnmRev = 0;
+ } else {
+ mBar1AnmRev = anm_no;
+ }
+ } else if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(5, 20);
+
+ if (isDownBarActive()) {
+ mpGroupAnmController[anm_no]->start();
+ mBar1AnmRevHold = anm_no;
+ mBar1AnmRev = 0;
+ } else {
+ mBar1AnmRev = anm_no;
+ }
+ }
+
+ break;
+
+ case 2:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(6, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 3:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(7, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 4:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(8, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 5:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(9, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 6:
+ if (mSequence == eSeq_Control) {
+ anm_no = findGroupAnimator(10, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 7:
+ if (mSequence == eSeq_Cmn) {
+ anm_no = findGroupAnimator(17, 12);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+
+ case 8:
+ if (mSequence == eSeq_Cmn) {
+ anm_no = findGroupAnimator(18, 12);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ void HomeButton::startTrigEvent(const char* pPane) {
+ int anm_no;
+ int btn_no;
+
+ btn_no = getPaneNo(pPane);
+
+ if (isActive() && btn_no != -1) {
+ if (mSequence == eSeq_Normal && btn_no < mButtonNum) {
+ mSelectBtnNum = static_cast<HBMSelectBtnNum>(btn_no + 1);
+
+ mSelectAnmNum = findAnimator(btn_no + 4, 1);
+ mpAnmController[mSelectAnmNum]->start();
+ play_sound(5);
+
+ if (mDialogFlag[btn_no]) {
+ mState = 11;
+ mSequence = eSeq_Cmn;
+
+ nw4hbm::lyt::Pane* p_pane =
+ mpLayout->GetRootPane()->FindPaneByName(scFuncTextPaneName[2], true);
+ nw4hbm::lyt::TextBox* p_text =
+ nw4hbm::ut::DynamicCast<nw4hbm::lyt::TextBox*>(p_pane);
+
+ u16 len;
+ p_text->SetString(mpText[mpHBInfo->region][btn_no + 2], 0);
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[2], true)
+ ->SetVisible(true);
+ } else {
+ mState = 15;
+ }
+ } else {
+ switch (btn_no - mButtonNum) {
+ case 0:
+ if (mpPairGroupAnmController[12]->isPlaying()) {
+ mpPairGroupAnmController[12]->stop();
+ }
+
+ if (mpPairGroupAnmController[3]->isPlaying()) {
+ mpPairGroupAnmController[3]->stop();
+ }
+
+ mSelectBtnNum = HBM_SELECT_HOMEBTN;
+ mSelectAnmNum = 4;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 14;
+ play_sound(1);
+
+ break;
+
+ case 1:
+ case 9:
+ if (mSequence == eSeq_Control) {
+#if HBM_REVISION > 1
+ mSelectAnmNum = 4;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+#endif
+
+ mSelectAnmNum = 2;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+
+ mpPairGroupAnmController[11]->start();
+
+ mState = 10;
+ mSequence = eSeq_Normal;
+ play_sound(8);
+ } else if (mSequence == eSeq_Normal) {
+ mpPairGroupAnmController[1]->start();
+
+ mSelectAnmNum = 9;
+ mpPairGroupAnmController[mSelectAnmNum]->start();
+ mState = 10;
+
+ for (int i = 2; i < 7; i++) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTouchPaneName[i], true)
+ ->SetVisible(true);
+ }
+
+ mSequence = eSeq_Control;
+ play_sound(5);
+ }
+
+ break;
+
+ case 2:
+ if (mVolumeNum > 0) {
+ mVolumeNum--;
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 10);
+ mpGroupAnmController[anm_no]->stop();
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 9);
+ mpGroupAnmController[anm_no]->start();
+
+ if (mVolumeNum == 0) {
+ play_sound(12);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ } else {
+ play_sound(10);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ }
+
+ anm_no = findGroupAnimator(11, 5);
+ mpGroupAnmController[anm_no]->start();
+ } else {
+ play_sound(13);
+ }
+
+ break;
+
+ case 3:
+ if (mVolumeNum < 10) {
+ anm_no = findGroupAnimator(mVolumeNum + 21, 9);
+ mpGroupAnmController[anm_no]->stop();
+
+ anm_no = findGroupAnimator(mVolumeNum + 21, 10);
+ mpGroupAnmController[anm_no]->start();
+
+ mVolumeNum++;
+
+ if (mVolumeNum == 10) {
+ play_sound(11);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ } else {
+ play_sound(9);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->setSpeakerVol(mVolumeNum / 10.0f);
+ getController(i)->playSound(mpSoundArchivePlayer, 1);
+ }
+ }
+
+ anm_no = findGroupAnimator(12, 5);
+ mpGroupAnmController[anm_no]->start();
+ } else {
+ play_sound(13);
+ }
+
+ break;
+
+ case 4:
+ if (!mVibFlag) {
+ mVibFlag = true;
+
+ setVibFlag(mVibFlag);
+
+ anm_no = findGroupAnimator(14, 8);
+ mpGroupAnmController[anm_no]->start();
+
+ mSelectAnmNum = findGroupAnimator(16, 6);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ play_sound(14);
+ } else {
+ mSelectAnmNum = findGroupAnimator(16, 14);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ play_sound(13);
+ }
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ getController(i)->startMotor();
+ mOnPaneVibFrame[i] = 0.0f;
+ mOnPaneVibWaitFrame[i] = 0.0f;
+ }
+
+ mState = 9;
+
+ break;
+
+ case 5:
+ if (mVibFlag) {
+ mVibFlag = false;
+ setVibFlag(mVibFlag);
+
+ anm_no = findGroupAnimator(14, 6);
+ mpGroupAnmController[anm_no]->start();
+
+ mSelectAnmNum = findGroupAnimator(13, 8);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ play_sound(15);
+ } else {
+ play_sound(13);
+ }
+
+ mState = 9;
+
+ break;
+
+ case 6:
+ mSelectAnmNum = findGroupAnimator(15, 5);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 3;
+ setReassignedFlag(true);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ mOnPaneVibFrame[i] = 0.0f;
+ mOnPaneVibWaitFrame[i] = 0.0f;
+ getController(i)->stopMotor();
+ }
+
+ mWaitStopMotorCount = 30;
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[0], true)
+ ->SetVisible(true);
+
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scFuncTextPaneName[1], true)
+ ->SetVisible(true);
+
+ play_sound(5);
+ play_sound(16);
+
+ break;
+
+ case 7:
+ mSelectAnmNum = findGroupAnimator(19, 13);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 13;
+ mSequence = eSeq_Normal;
+
+ if (mSelectBtnNum == HBM_SELECT_BTN1) {
+ play_sound(2);
+ } else if (mSelectBtnNum == HBM_SELECT_BTN2) {
+ play_sound(3);
+ }
+
+ break;
+
+ case 8:
+ mSelectAnmNum = findGroupAnimator(20, 13);
+ mpGroupAnmController[mSelectAnmNum]->start();
+
+ mState = 13;
+ mSelectBtnNum = HBM_SELECT_NULL;
+
+ mSequence = eSeq_Normal;
+
+ play_sound(6);
+
+ break;
+ }
+ }
+ }
+ }
+
+ void HomeButton::reset_btn() {
+ int anm_no;
+
+ for (int i = 0; i < mButtonNum; i++) {
+ if (!mPaneCounter[i]) {
+ continue;
+ }
+
+ anm_no = findAnimator(i, 2);
+ mpAnmController[anm_no]->start();
+ mPaneCounter[i] = 0;
+ }
+
+ if (mPaneCounter[mButtonNum]) {
+ mpPairGroupAnmController[12]->start();
+ mPaneCounter[mButtonNum + 1] = 0;
+ }
+
+ if (mPaneCounter[mButtonNum + 1]) {
+ if (mSequence == eSeq_Control) {
+ if (!mMsgCount) {
+ anm_no = findGroupAnimator(5, 20);
+ mpGroupAnmController[anm_no]->start();
+ mPaneCounter[mButtonNum] = 0;
+ }
+ } else {
+ anm_no = findGroupAnimator(5, 3);
+ mpGroupAnmController[anm_no]->start();
+ mPaneCounter[mButtonNum] = 0;
+ }
+ }
+ }
+
+ void HomeButton::reset_control() {
+ int anm_no;
+
+ for (int i = 0; i < 5; i++) {
+ anm_no = findGroupAnimator(i + 6, 7);
+ mpGroupAnmController[anm_no]->start();
+ }
+ }
+
+ void HomeButton::reset_window() {
+ int anm_no;
+
+ anm_no = findGroupAnimator(17, 12);
+ mpGroupAnmController[anm_no]->start();
+
+ anm_no = findGroupAnimator(18, 12);
+ mpGroupAnmController[anm_no]->start();
+ }
+
+ void HomeButton::reset_battery() {
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ for (int j = 0; j < 4; j++) {
+ mpLayout->GetRootPane()
+ ->FindPaneByName(scBatteryPaneName[i][j], true)
+ ->SetVisible(false);
+ }
+ }
+ }
+
+ void HomeButton::reset_guiManager(int num) {
+ if (num < 0) // presumably a -1 = all case
+ {
+ for (int i = 0; i < 8; i++) {
+ mpPaneManager->update(i, -10000.0f, -10000.0f, 0, 0, 0, NULL);
+ }
+ } else {
+ mpPaneManager->update(num, -10000.0f, -10000.0f, 0, 0, 0, NULL);
+ }
+ }
+
+ bool HomeButton::isActive() const {
+ return mState == 2;
+ }
+
+ bool HomeButton::isUpBarActive() const {
+ bool flag = true;
+
+ if (!isActive() || mpPairGroupAnmController[12]->isPlaying() ||
+ mpPairGroupAnmController[3]->isPlaying())
+ {
+ flag = false;
+ }
+
+ return flag;
+ }
+
+ bool HomeButton::isDownBarActive() {
+ bool flag = true;
+
+ int anm_no[4];
+ anm_no[0] = findGroupAnimator(4, 2);
+ anm_no[1] = findGroupAnimator(5, 3);
+ anm_no[2] = findGroupAnimator(4, 19);
+ anm_no[3] = findGroupAnimator(5, 20);
+
+ if (!isActive() || mpGroupAnmController[anm_no[0]]->isPlaying() ||
+ mpGroupAnmController[anm_no[1]]->isPlaying() ||
+ mpGroupAnmController[anm_no[2]]->isPlaying() ||
+ mpGroupAnmController[anm_no[3]]->isPlaying())
+ {
+ flag = false;
+ }
+
+ return flag;
+ }
+
+ int HomeButton::getPaneNo(const char* panename) {
+ int buttonNum = mButtonNum;
+ int ret = -1;
+
+ for (int i = 0; i < buttonNum; i++) {
+ if (!std::strcmp(panename, getPaneName(i))) {
+ ret = i;
+ break;
+ }
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if (!std::strcmp(panename, getFuncPaneName(i))) {
+ ret = i + buttonNum;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ int HomeButton::findAnimator(int pane, int anm) {
+ for (int i = 0; i < mAnmNum; i++) {
+ if (scAnmTable[i].pane == pane && scAnmTable[i].anm == anm) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ void HomeButton::setForcusSE() {
+ if (mForcusSEWaitTime <= 2) {
+ return;
+ }
+
+ play_sound(4);
+ mForcusSEWaitTime = 0;
+ }
+
+ void HomeButton::setAdjustFlag(int flag) {
+ nw4hbm::math::VEC2 sc_v;
+
+ mAdjustFlag = static_cast<bool>(flag);
+ mDrawInfo.SetLocationAdjust(mAdjustFlag);
+
+ if (mAdjustFlag) {
+ sc_v = nw4hbm::math::VEC2(mpHBInfo->adjust.x, mpHBInfo->adjust.y);
+ mpLayout->GetRootPane()->SetScale(sc_v);
+
+ if (!mpHBInfo->cursor) {
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ mpCursorLayout[i]->GetRootPane()->SetScale(sc_v);
+ }
+ }
+ } else {
+ sc_v = nw4hbm::math::VEC2(1.0f, 1.0f);
+ mpLayout->GetRootPane()->SetScale(sc_v);
+
+ if (!mpHBInfo->cursor) {
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ mpCursorLayout[i]->GetRootPane()->SetScale(sc_v);
+ }
+ }
+ }
+
+ GXSetCullMode(GX_CULL_NONE);
+ }
+
+ void HomeButton::setVolume(int vol) {
+ WPADSetSpeakerVolume(vol * 12.7f);
+ }
+
+ int HomeButton::getVolume() {
+ return WPADGetSpeakerVolume() * (1.0f / 12.7f) + 0.9f;
+ }
+
+ void HomeButton::setVibFlag(bool flag) {
+ WPADEnableMotor(flag);
+ }
+
+ bool HomeButton::getVibFlag() {
+ return WPADIsMotorEnabled() ? true : false;
+ }
+
+ void HomeButtonEventHandler::onEvent(u32 uID, u32 uEvent, void* pData) {
+ gui::PaneComponent* p_panecpt = (gui::PaneComponent*)mpManager->getComponent(uID);
+ const char* panename = p_panecpt->getPane()->GetName();
+
+ HomeButton* p_hbtn = getHomeButton();
+
+ HBController* pCon = static_cast<HBController*>(pData);
+
+ switch (uEvent) {
+ case 1:
+ p_hbtn->startPointEvent(panename, pData);
+ break;
+
+ case 2:
+ p_hbtn->startLeftEvent(panename);
+ break;
+
+ case 0:
+ if ((pCon->trig & WPAD_BUTTON_A) || (pCon->trig & 0x1000000)) {
+ p_hbtn->startTrigEvent(panename);
+ }
+ break;
+ }
+ }
+
+ void HomeButton::startBlackOut() {
+ mForceSttInitProcFlag = false;
+ mForceSttFadeInProcFlag = false;
+ mForceStopSyncFlag = false;
+ mForceEndMsgAnmFlag = false;
+
+ switch (mState) {
+ case 0:
+ mForceSttInitProcFlag = true;
+
+ case 1:
+ mForceSttFadeInProcFlag = true;
+ break;
+
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ if ((mState == 3 && mSelectAnmNum == 5) || (mState == 5 && !mSimpleSyncFlag)) {
+ OSCancelAlarm(&mSimpleSyncAlarm);
+ WPADSetSimpleSyncCallback(mSimpleSyncCallback);
+ mSimpleSyncCallback = NULL;
+ } else if (!mEndSimpleSyncFlag && mState > 3) {
+ mForceStopSyncFlag = true;
+
+ if (!WPADStopSimpleSync()) {
+ OSCancelAlarm(&mSimpleSyncAlarm);
+
+ OSSetAlarmUserData(&mSimpleSyncAlarm, (void*)1);
+ OSSetAlarm(&mSimpleSyncAlarm, OSMillisecondsToTicks(100),
+ &RetrySimpleSyncCallback);
+ }
+ } else {
+ WPADSetSimpleSyncCallback(mSimpleSyncCallback);
+ mSimpleSyncCallback = NULL;
+ }
+
+ mForceEndMsgAnmFlag = true;
+ break;
+ }
+
+ mState = 19;
+ mFader.start();
+
+ mSelectBtnNum = HBM_SELECT_BTN2;
+
+ f32 maxFrame = mFader.getMaxFrame();
+ mFadeOutSeTime = maxFrame;
+
+ if (mpHBInfo->sound_callback != NULL) {
+ mpHBInfo->sound_callback(3, maxFrame);
+ }
+ }
+
+ static void drawBlackPlate(f32 left, f32 top, f32 right, f32 bottom) {
+#if HBM_REVISION == 1
+ GXBegin(GX_QUADS, GX_VTXFMT0, 4);
+ GXPosition2f32(left, top);
+ GXPosition2f32(right, top);
+ GXPosition2f32(right, bottom);
+ GXPosition2f32(left, bottom);
+ GXEnd();
+#else
+ GXBegin(GX_QUADS, GX_VTXFMT0, 4);
+ GXPosition2f32(left, top);
+ GXPosition2f32(left, bottom);
+ GXPosition2f32(right, bottom);
+ GXPosition2f32(right, top);
+ GXEnd();
+#endif
+ }
+
+ static void initgx() {
+ Mtx view_mtx;
+
+ PSMTXIdentity(view_mtx);
+ GXLoadPosMtxImm(view_mtx, 0);
+ GXSetCurrentMtx(0);
+
+ GXClearVtxDesc();
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_F32, 0);
+
+ GXSetNumChans(1);
+ GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+ GXSetChanCtrl(GX_COLOR1A1, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+
+ GXSetNumTexGens(0);
+
+ GXSetNumTevStages(1);
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0);
+ GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0);
+ GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+
+ GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0);
+ GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
+ GXSetAlphaUpdate(false);
+ GXSetZMode(false, GX_ALWAYS, false);
+
+#if HBM_REVISION > 1
+ GXSetNumIndStages(0);
+ GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
+ GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+#endif
+ }
+
+ void HomeButton::createSound(nw4hbm::snd::NandSoundArchive* pNandSoundArchive,
+ bool bCreateSoundHeap) {
+ void* buffer =
+ MEMAllocFromAllocator(&sSoundAllocator, sizeof(nw4hbm::snd::SoundArchivePlayer));
+ if (buffer != NULL) {
+ mpSoundArchivePlayer = new (buffer) nw4hbm::snd::SoundArchivePlayer();
+ }
+ NW4HBM_ASSERT(LN(3752, 3695), mpSoundArchivePlayer);
+
+ void* memBuffer;
+ void* strmBuffer;
+ u32 memSize = mpSoundArchivePlayer->GetRequiredMemSize(pNandSoundArchive);
+ u32 strmSize = mpSoundArchivePlayer->GetRequiredStrmBufferSize(pNandSoundArchive);
+ strmBuffer = MEMAllocFromAllocator(&sSoundAllocator, strmSize);
+ memBuffer = MEMAllocFromAllocator(&sSoundAllocator, memSize);
+ bool result = mpSoundArchivePlayer->Setup(pNandSoundArchive, memBuffer, memSize, strmBuffer,
+ strmSize);
+ NW4HBM_ASSERT(LN(3770, 3713), result);
+
+ buffer = MEMAllocFromAllocator(&sSoundAllocator, sizeof(nw4hbm::snd::SoundHandle));
+ if (buffer != NULL) {
+ mpSoundHandle = new (buffer) nw4hbm::snd::SoundHandle();
+ }
+ NW4HBM_ASSERT(LN(3777, 3720), mpSoundHandle);
+
+ if (bCreateSoundHeap) {
+ buffer = MEMAllocFromAllocator(&sSoundAllocator, sizeof(nw4hbm::snd::SoundHeap));
+ if (buffer != NULL) {
+ mpSoundHeap = new (buffer) nw4hbm::snd::SoundHeap();
+ }
+ NW4HBM_ASSERT(LN(3786, 3729), mpSoundHeap);
+
+ u32 size = mButtonNum == 2 ? 0x60000 : 0x6F800;
+ mpSoundHeap->Create(MEMAllocFromAllocator(&sSoundAllocator, size), size);
+ NW4HBM_ASSERT(LN(3794, 3737), mpSoundHeap->IsValid());
+
+ bool result = mpSoundArchivePlayer->LoadGroup(0, mpSoundHeap, 0);
+ NW4HBM_ASSERT(LN(3797, 3740), result);
+ } else {
+ mpSoundHeap = NULL;
+ }
+ }
+
+ void HomeButton::deleteSound() {
+ if (mpDvdSoundArchive != NULL) {
+ mpDvdSoundArchive->Close();
+ mpDvdSoundArchive->~DvdSoundArchive();
+ }
+
+ if (mpMemorySoundArchive != NULL) {
+ mpMemorySoundArchive->Shutdown();
+ mpMemorySoundArchive->~MemorySoundArchive();
+ }
+
+ if (mpNandSoundArchive != NULL) {
+ mpNandSoundArchive->Close();
+ mpNandSoundArchive->~NandSoundArchive();
+ }
+
+ if (mpSoundHeap != NULL) {
+ mpSoundHeap->Destroy();
+ mpSoundHeap->~SoundHeap();
+ }
+
+ if (mpSoundArchivePlayer != NULL) {
+ mpSoundArchivePlayer->Shutdown();
+ mpSoundArchivePlayer->~SoundArchivePlayer();
+ }
+
+ if (mpSoundHandle != NULL) {
+ mpSoundHandle->~SoundHandle();
+ }
+
+ nw4hbm::snd::SoundSystem::ShutdownSoundSystem();
+ }
+
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMBase.h b/src/revolution/homebuttonLib/HBMBase.h
new file mode 100644
index 0000000000..8707cb1695
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMBase.h
@@ -0,0 +1,295 @@
+#ifndef HOMEBUTTON_BASE_H
+#define HOMEBUTTON_BASE_H
+
+#include <revolution/ax.h>
+#include <revolution/axfx.h>
+#include <revolution/wpad.h>
+#include "HBMCommon.h"
+#include "HBMController.h"
+#include "HBMGUIManager.h"
+#include "nw4hbm/lyt/drawInfo.h"
+#include "nw4hbm/lyt/layout.h"
+#include "nw4hbm/snd/DvdSoundArchive.h"
+#include "nw4hbm/snd/MemorySoundArchive.h"
+#include "nw4hbm/snd/NandSoundArchive.h"
+#include "nw4hbm/snd/SoundArchivePlayer.h"
+#include "nw4hbm/snd/SoundPlayer.h"
+#include "nw4hbm/snd/SoundSystem.h"
+
+
+#include "new.h"
+
+namespace homebutton {
+ static void initgx();
+ static void drawBlackPlate(f32 left, f32 top, f32 right, f32 bottom);
+ static u32 get_comma_length(char* pBuf);
+ static void SpeakerCallback(OSAlarm* alm, OSContext* ctx);
+ static void MotorCallback(OSAlarm* alm, OSContext* ctx);
+ static void RetrySimpleSyncCallback(OSAlarm* alm, OSContext* ctx);
+ static void SimpleSyncCallback(s32 result, s32 num);
+} // namespace homebutton
+
+namespace nw4hbm {
+
+ namespace lyt {
+ class ArcResourceAccessor;
+ class ArcResourceLink;
+ class Layout;
+ class MultiArcResourceAccessor;
+ class Pane;
+ } // namespace lyt
+
+ namespace ut {
+ class ResFont;
+ }
+
+} // namespace nw4hbm
+
+namespace homebutton {
+
+ class HomeButton;
+ class Controller;
+ class GroupAnmController;
+ class RemoteSpk;
+
+ class HomeButtonEventHandler : public gui::EventHandler {
+ public:
+ HomeButtonEventHandler(homebutton::HomeButton* pHomeButton) : mpHomeButton(pHomeButton) {}
+
+ /* 0x08 */ virtual void onEvent(u32 uID, u32 uEvent, void* pData);
+
+ homebutton::HomeButton* getHomeButton() { return mpHomeButton; }
+
+ private:
+ /* 0x00 (base) */
+ /* 0x08 */ HomeButton* mpHomeButton;
+ }; // size = 0x0C
+
+ class HomeButton {
+ private:
+ typedef enum {
+ /* 0 */ eSeq_Normal,
+ /* 1 */ eSeq_Control,
+ /* 2 */ eSeq_Cmn,
+ } eSeq;
+
+ class BlackFader {
+ public:
+ BlackFader(int maxFrame) {
+ init(maxFrame);
+ setColor(0, 0, 0);
+ }
+
+ void setColor(u8 r, u8 g, u8 b) {
+ red_ = r;
+ green_ = g;
+ blue_ = b;
+ }
+
+ int getFrame() const { return frame_; }
+ int getMaxFrame() const { return maxFrame_; }
+ void start() { state_ = 1; }
+ GXColor GetColor(u8 alpha) { return (GXColor){red_, green_, blue_, alpha}; }
+
+ bool isDone();
+ void init(int maxFrame);
+ void calc();
+ void draw();
+
+ private:
+ /* 0x00 */ int frame_;
+ /* 0x04 */ int maxFrame_;
+ /* 0x08 */ int state_;
+ /* 0x0D */ u8 red_;
+ /* 0x0E */ u8 green_;
+ /* 0x0F */ u8 blue_;
+ }; // size = 0x10
+
+ public:
+ HomeButton(const HBMDataInfo* dataInfo);
+ ~HomeButton();
+
+ int getVolume();
+ HBMSelectBtnNum getSelectBtnNum();
+ bool isActive() const;
+ bool isUpBarActive() const;
+ bool isDownBarActive();
+ void setAdjustFlag(int flag);
+ void setForcusSE();
+ void setSimpleSyncAlarm(int type);
+ void setSpeakerAlarm(int chan, int msec);
+ void setVolume(int vol);
+ bool getVibFlag();
+ int getPaneNo(const char*);
+ void setVibFlag(bool flag);
+ void create();
+ void init();
+ void calc(const HBMControllerData* pController);
+ void draw();
+ void update(const HBMControllerData* pController);
+ void updateTrigPane();
+ void startPointEvent(const char* pPane, void* pData);
+ void startLeftEvent(const char* pPane);
+ void startTrigEvent(const char* pPane);
+ int findAnimator(int pane, int anm);
+ int findGroupAnimator(int pane, int anm);
+ void callSimpleSyncCallback(s32 result, s32 num);
+ void startBlackOut();
+
+ const HBMDataInfo* getHBMDataInfo() { return mpHBInfo; }
+ Controller* getController(int chan) { return mpController[chan]; }
+ const char* getFuncPaneName(int no) { return scFuncTouchPaneName[no]; }
+ const char* getPaneName(int no) { return scBtnName[no]; }
+ bool getReassignedFlag() const { return mReassignedFlag; }
+ HomeButtonEventHandler* getEventHandler() const { return mpHomeButtonEventHandler; }
+ nw4hbm::snd::SoundArchivePlayer* GetSoundArchivePlayer() { return mpSoundArchivePlayer; }
+ void setEndSimpleSyncFlag(bool flag) { mEndSimpleSyncFlag = flag; }
+ void setReassignedFlag(bool flag) { mReassignedFlag = flag; }
+ void setSimpleSyncFlag(bool flag) { mSimpleSyncFlag = flag; }
+
+ static void createInstance(const HBMDataInfo* dataInfo);
+ static HomeButton* getInstance() { return spHomeButtonObj; }
+ static void deleteInstance();
+
+ private:
+ void init_battery(const HBMControllerData* pController);
+ void calc_battery(int chan);
+ void reset_battery();
+
+ void init_sound();
+ void fadeout_sound(f32 gain);
+
+ void init_msg();
+ void init_vib();
+ void init_volume();
+
+ void set_config();
+ void set_text();
+
+ void calc_fadeoutAnm();
+
+ void update_controller(int id);
+ void update_posController(int id);
+
+ void reset_btn();
+ void reset_control();
+ void reset_guiManager(int num);
+ void reset_window();
+
+ public:
+ void play_sound(int id);
+ void createSound(nw4hbm::snd::NandSoundArchive* pNandSoundArchive, bool bCreateSoundHeap);
+ void deleteSound();
+ void draw_impl();
+ void updateSoundArchivePlayer();
+ void setSoundVolume(f32 volume);
+ inline void stopSound(bool checkFlag);
+ void initSound(const char* path);
+ void updateSound();
+
+ void PlaySeq(int num) {
+ if (mpSoundArchivePlayer != NULL && mpSoundHandle != NULL) {
+ mpSoundHandle->DetachSound();
+ mpSoundArchivePlayer->StartSound(mpSoundHandle, num);
+ }
+ }
+
+ private:
+ /* 0x000 */ eSeq mSequence;
+ /* 0x004 */ const HBMDataInfo* mpHBInfo;
+ /* 0x008 */ int mButtonNum;
+ /* 0x00C */ int mAnmNum;
+ /* 0x010 */ int mState;
+ /* 0x014 */ int mSelectAnmNum;
+ /* 0x018 */ int mMsgCount;
+ /* 0x01C */ int mPaneCounter[14];
+ /* 0x054 */ int mPadDrawTime[WPAD_MAX_CONTROLLERS];
+ /* 0x064 */ int mForcusSEWaitTime;
+ /* 0x068 */ int mBar0AnmRev;
+ /* 0x06C */ int mBar1AnmRev;
+ /* 0x070 */ int mBar0AnmRevHold;
+ /* 0x074 */ int mBar1AnmRevHold;
+ /* 0x078 */ int mGetPadInfoTime;
+ /* 0x07C */ bool mControllerFlag[WPAD_MAX_CONTROLLERS];
+ /* 0x080 */ int mVolumeNum;
+ /* 0x084 */ bool mVibFlag;
+ /* 0x085 */ bool mControlFlag;
+ /* 0x086 */ bool mLetterFlag;
+ /* 0x087 */ bool mAdjustFlag;
+ /* 0x088 */ bool mReassignedFlag;
+ /* 0x089 */ bool mSimpleSyncFlag;
+ /* 0x08A */ bool mEndSimpleSyncFlag;
+ /* 0x08B */ bool mInitFlag;
+ /* 0x08C */ bool mForceSttInitProcFlag;
+ /* 0x08D */ bool mForceSttFadeInProcFlag;
+ /* 0x08E */ bool mEndInitSoundFlag;
+ /* 0x08F */ bool mForceStopSyncFlag;
+ /* 0x090 */ bool mForceEndMsgAnmFlag;
+#if HBM_REVISION > 1
+ /* 0x094 */ int mSoundRetryCnt;
+#endif
+ /* 0x098 */ int mDialogFlag[4];
+ /* 0x0A8 */ char* mpLayoutName;
+ /* 0x0AC */ char* mpAnmName;
+ /* 0x0B0 */ HBMSelectBtnNum mSelectBtnNum;
+ /* 0x0B4 */ wchar_t* mpText[7][6];
+ /* 0x15C */ WPADInfo mWpadInfo[WPAD_MAX_CONTROLLERS];
+ /* 0x1BC */ WPADSimpleSyncCallback mSimpleSyncCallback;
+ /* 0x1CC */ f32 mOnPaneVibFrame[4];
+ /* 0x1DC */ f32 mOnPaneVibWaitFrame[4];
+ /* 0x1E0 */ int mWaitStopMotorCount;
+ /* 0x1E4 */ nw4hbm::lyt::Layout* mpLayout;
+ /* 0x1E8 */ nw4hbm::lyt::Layout* mpCursorLayout[WPAD_MAX_CONTROLLERS];
+ /* 0x1F8 */ nw4hbm::lyt::ArcResourceAccessor* mpResAccessor;
+ /* 0x1FC */ gui::PaneManager* mpPaneManager;
+ /* 0x200 */ HomeButtonEventHandler* mpHomeButtonEventHandler;
+ /* 0x204 */ nw4hbm::lyt::DrawInfo mDrawInfo;
+ /* 0x258 */ Controller* mpController[WPAD_MAX_CONTROLLERS];
+ /* 0x268 */ RemoteSpk* mpRemoteSpk;
+ /* 0x26C */ GroupAnmController* mpAnmController[12];
+ /* 0x29C */ GroupAnmController* mpGroupAnmController[74];
+ /* 0x3C4 */ GroupAnmController* mpPairGroupAnmController[15];
+ /* 0x400 */ BlackFader mFader;
+ /* 0x410 */ OSAlarm mAlarm[WPAD_MAX_CONTROLLERS];
+ /* 0x4D0 */ OSAlarm mSpeakerAlarm[WPAD_MAX_CONTROLLERS];
+ /* 0x590 */ OSAlarm mSimpleSyncAlarm;
+ /* 0x5C0 */ nw4hbm::snd::SoundArchivePlayer* mpSoundArchivePlayer;
+ /* 0x5C4 */ nw4hbm::snd::DvdSoundArchive* mpDvdSoundArchive;
+ /* 0x5C8 */ nw4hbm::snd::MemorySoundArchive* mpMemorySoundArchive;
+ /* 0x5CC */ nw4hbm::snd::NandSoundArchive* mpNandSoundArchive;
+ /* 0x5D0 */ nw4hbm::snd::SoundHeap* mpSoundHeap;
+ /* 0x5D4 */ nw4hbm::snd::SoundHandle* mpSoundHandle;
+ /* 0x5D8 */ u16 mAppVolume[3];
+ /* 0x5E0 */ AXFXAllocFunc mAxFxAlloc;
+ /* 0x5E4 */ AXFXFreeFunc mAxFxFree;
+ /* 0x5E8 */ AXFX_REVERBHI mAxFxReverb;
+ /* 0x748 */ AXAuxCallback mAuxCallback;
+ /* 0x74C */ void* mpAuxContext;
+ /* 0x750 */ f32 mFadeOutSeTime;
+
+ // static members
+ private:
+ static HomeButton* spHomeButtonObj;
+
+ static const char* scCursorLytName[WPAD_MAX_CONTROLLERS];
+ static const char* scCursorPaneName;
+ static const char* scCursorRotPaneName;
+ static const char* scCursorSRotPaneName;
+
+ static const char* scBtnName[4];
+ static const char* scTxtName[4];
+ static const char* scGrName[8];
+ static const char* scAnimName[3];
+ static const char* scPairGroupAnimName[15];
+ static const char* scPairGroupName[15];
+ static const char* scGroupAnimName[22];
+ static const char* scGroupName[35];
+ static const char* scFuncPaneName[5];
+ static const char* scFuncTouchPaneName[10];
+ static const char* scFuncTextPaneName[3];
+ static const char* scBatteryPaneName[WPAD_MAX_CONTROLLERS][4];
+ }; // size = 0x740
+
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMCommon.h b/src/revolution/homebuttonLib/HBMCommon.h
new file mode 100644
index 0000000000..a7ed73b162
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMCommon.h
@@ -0,0 +1,11 @@
+#ifndef HOMEBUTTON_COMMON_H
+#define HOMEBUTTON_COMMON_H
+
+#include <revolution/mem.h>
+
+extern "C" MEMAllocator* spAllocator;
+
+void* HBMAllocMem(u32 length);
+void HBMFreeMem(void* ptr);
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMController.cpp b/src/revolution/homebuttonLib/HBMController.cpp
new file mode 100644
index 0000000000..207ee57130
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMController.cpp
@@ -0,0 +1,356 @@
+#include "HBMController.h"
+
+namespace homebutton {
+
+ bool Controller::sBatteryFlag[WPAD_MAX_CONTROLLERS];
+ OSAlarm Controller::sAlarm[WPAD_MAX_CONTROLLERS];
+ OSAlarm Controller::sAlarmSoundOff[WPAD_MAX_CONTROLLERS];
+ Controller* Controller::sThis[WPAD_MAX_CONTROLLERS];
+ bool Controller::sSetInfoAsync[WPAD_MAX_CONTROLLERS];
+ RemoteSpk* Controller::sPInstance;
+ s32 Controller::lbl_8025DBBC;
+
+ void Controller::wpadConnectCallback(s32 chan, s32 result) {
+ switch (result) {
+ case WPAD_ESUCCESS:
+ if (!sThis[chan]->mCallbackFlag) {
+ sThis[chan]->mOldExtensionCallback =
+ WPADSetExtensionCallback(chan, wpadExtensionCallback);
+ sThis[chan]->mCallbackFlag = true;
+ }
+
+ WPADControlSpeaker(chan, WPAD_SPEAKER_DISABLE, NULL);
+ break;
+
+ case WPAD_ENODEV:
+ WPADSetExtensionCallback(chan, sThis[chan]->mOldExtensionCallback);
+ sThis[chan]->mOldExtensionCallback = NULL;
+ sThis[chan]->mCallbackFlag = false;
+ sThis[chan]->mCheckSoundTimeFlag = false;
+ sThis[chan]->mCheckSoundIntervalFlag = false;
+ break;
+ }
+
+ if (sThis[chan]->mOldConnectCallback) {
+ (*sThis[chan]->mOldConnectCallback)(chan, result);
+ }
+ }
+
+ void Controller::wpadExtensionCallback(s32 chan, s32 result) {
+ switch (result) {
+ case WPAD_DEV_INITIALIZING:
+ sThis[chan]->soundOff(1000);
+ break;
+ }
+
+ if (sThis[chan]->mOldExtensionCallback) {
+ (*sThis[chan]->mOldExtensionCallback)(chan, result);
+ }
+ }
+
+ void Controller::soundOnCallback(OSAlarm* alm, OSContext*) {
+ int chan = (int)OSGetAlarmUserData(alm);
+ sThis[chan]->soundOn();
+ }
+
+ Controller::Controller(int chan, RemoteSpk* spk) {
+ mHBController.chan = chan;
+ mHBController.rumble = false;
+ mHBController.spVol = 1.0f;
+
+ remotespk = spk;
+ mOldConnectCallback = NULL;
+ mOldExtensionCallback = NULL;
+ mCallbackFlag = false;
+ mSoundOffFlag = false;
+
+ if (chan < WPAD_MAX_CONTROLLERS) {
+ sBatteryFlag[chan] = false;
+ OSCreateAlarm(&sAlarm[chan]);
+ OSCreateAlarm(&sAlarmSoundOff[chan]);
+ sThis[chan] = this;
+ }
+ }
+
+ Controller::~Controller() {
+ OSCancelAlarm(&sAlarm[mHBController.chan]);
+ OSCancelAlarm(&sAlarmSoundOff[mHBController.chan]);
+ }
+
+ void Controller::initCallback() {
+ u32 type;
+
+ mOldConnectCallback = WPADSetConnectCallback(mHBController.chan, &wpadConnectCallback);
+
+ switch (WPADProbe(mHBController.chan, &type)) {
+ case WPAD_ESUCCESS:
+ mOldExtensionCallback =
+ WPADSetExtensionCallback(mHBController.chan, &wpadExtensionCallback);
+ mCallbackFlag = true;
+ break;
+ case WPAD_ENODEV:
+ mCallbackFlag = false;
+ break;
+ }
+ }
+
+ void Controller::clearCallback() {
+ WPADSetConnectCallback(mHBController.chan, mOldConnectCallback);
+ mOldConnectCallback = NULL;
+
+ WPADSetExtensionCallback(mHBController.chan, mOldExtensionCallback);
+ mOldExtensionCallback = NULL;
+ }
+
+ void Controller::setKpad(const HBMKPadData* con, bool updatePos) {
+ if (!con->kpad) {
+ return;
+ }
+
+ if (updatePos) {
+ if (con->kpad->dev_type == WPAD_DEV_CLASSIC && con->use_devtype == WPAD_DEV_CLASSIC) {
+ mHBController.x = con->pos.x;
+ mHBController.y = con->pos.y;
+ } else {
+ mHBController.x = con->kpad->pos.x;
+ mHBController.y = con->kpad->pos.y;
+ }
+ }
+
+ mHBController.trig = con->kpad->trig;
+ mHBController.hold = con->kpad->hold;
+ mHBController.release = con->kpad->release;
+
+ if (con->kpad->dev_type == WPAD_DEV_CLASSIC) {
+ u32 h = con->kpad->ex_status.cl.hold;
+ u32 t = con->kpad->ex_status.cl.trig;
+ u32 r = con->kpad->ex_status.cl.release;
+
+ if (h & WPAD_BUTTON_CL_A) {
+ mHBController.hold |= WPAD_BUTTON_A;
+ }
+ if (t & WPAD_BUTTON_CL_A) {
+ mHBController.trig |= WPAD_BUTTON_A;
+ }
+ if (r & WPAD_BUTTON_CL_A) {
+ mHBController.release |= WPAD_BUTTON_A;
+ }
+
+ if (h & WPAD_BUTTON_CL_PLUS) {
+ mHBController.hold |= WPAD_BUTTON_PLUS;
+ }
+ if (t & WPAD_BUTTON_CL_PLUS) {
+ mHBController.trig |= WPAD_BUTTON_PLUS;
+ }
+ if (r & WPAD_BUTTON_CL_PLUS) {
+ mHBController.release |= WPAD_BUTTON_PLUS;
+ }
+
+ if (h & WPAD_BUTTON_CL_MINUS) {
+ mHBController.hold |= WPAD_BUTTON_MINUS;
+ }
+ if (t & WPAD_BUTTON_CL_MINUS) {
+ mHBController.trig |= WPAD_BUTTON_MINUS;
+ }
+ if (r & WPAD_BUTTON_CL_MINUS) {
+ mHBController.release |= WPAD_BUTTON_MINUS;
+ }
+
+ if (h & WPAD_BUTTON_CL_HOME) {
+ mHBController.hold |= WPAD_BUTTON_HOME;
+ }
+ if (t & WPAD_BUTTON_CL_HOME) {
+ mHBController.trig |= WPAD_BUTTON_HOME;
+ }
+ if (r & WPAD_BUTTON_CL_HOME) {
+ mHBController.release |= WPAD_BUTTON_HOME;
+ }
+ }
+ }
+
+ void Controller::clrKpadButton() {
+ mHBController.trig = 0;
+ mHBController.hold = 0;
+ mHBController.release = 0;
+ }
+
+ void Controller::setInValidPos() {
+ mHBController.x = -10000.0f;
+ mHBController.y = -10000.0f;
+ }
+
+ int Controller::getChan() const {
+ return mHBController.chan;
+ }
+
+ void Controller::connect() {
+ getRemoteSpk()->Connect(getChan());
+ }
+
+ void Controller::disconnect() { /* ... */ }
+
+ void Controller::setSpeakerVol(f32 vol) {
+ mHBController.spVol = vol;
+ }
+
+ f32 Controller::getSpeakerVol() const {
+ return mHBController.spVol;
+ }
+
+ void Controller::playSound(nw4hbm::snd::SoundArchivePlayer* pSoundArchivePlayer, int id) {
+ if (!mSoundOffFlag) {
+ getRemoteSpk()->Play(getChan(), id, getSpeakerVol() * 10.0f);
+
+ if (WPADIsSpeakerEnabled(getChan())) {
+ if (!mCheckSoundTimeFlag) {
+ mPlaySoundTime = OSGetTime();
+ }
+
+ mCheckSoundTimeFlag = true;
+ mCheckSoundIntervalFlag = false;
+ }
+ }
+ }
+
+ bool Controller::isPlayingSound() const {
+ return getRemoteSpk()->isPlaying(getChan());
+ }
+
+ bool Controller::isPlayingSoundId(int id) const {
+ if (!isPlayingSound()) {
+ return false;
+ }
+
+ if (!getRemoteSpk()->isPlayingId(getChan(), id)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ void Controller::initSound() {
+ mCheckSoundTimeFlag = false;
+ mCheckSoundIntervalFlag = false;
+ }
+
+ void Controller::updateSound() {
+ int chan = getChan();
+
+ if (!isPlayingSound()) {
+ if (mCheckSoundTimeFlag) {
+ if (!mCheckSoundIntervalFlag) {
+ mStopSoundTime = OSGetTime();
+ mCheckSoundIntervalFlag = true;
+ } else {
+ OSTime time = OSGetTime();
+ if (OSTicksToMilliseconds(time - mStopSoundTime) >= 1000) {
+ mCheckSoundTimeFlag = false;
+ mCheckSoundIntervalFlag = false;
+ }
+ }
+ }
+
+ return;
+ } else {
+ if (mCheckSoundTimeFlag) {
+ mCheckSoundIntervalFlag = false;
+
+ OSTime time = OSGetTime();
+ if (OSTicksToMilliseconds(time - mPlaySoundTime) >= 480000) {
+ mCheckSoundTimeFlag = false;
+ mCheckSoundIntervalFlag = false;
+ soundOff(1000);
+ return;
+ }
+ }
+
+ // Average radio sensitivity is 80 (see __wpadCalcRadioQuality)
+ if (!mSoundOffFlag && WPADGetRadioSensitivity(chan) <= 85) {
+ soundOff(1000);
+ }
+ }
+ }
+
+ void Controller::soundOff(int msec) {
+ int chan = getChan();
+
+ if (!WPADIsSpeakerEnabled(chan)) {
+ return;
+ }
+
+ WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE, NULL);
+
+ OSSetAlarmUserData(&sAlarmSoundOff[chan], (void*)chan);
+ OSCancelAlarm(&sAlarmSoundOff[chan]);
+ OSSetAlarm(&sAlarmSoundOff[chan], OSMillisecondsToTicks(msec), &soundOnCallback);
+
+ mSoundOffFlag = true;
+ }
+
+ void Controller::soundOn() {
+ int chan = getChan();
+
+ if (WPADIsSpeakerEnabled(chan)) {
+ WPADControlSpeaker(chan, WPAD_SPEAKER_UNMUTE, NULL);
+ }
+
+ mSoundOffFlag = false;
+ }
+
+ bool Controller::isPlayReady() const {
+ return getRemoteSpk()->isPlayReady(getChan());
+ }
+
+ HBController* Controller::getController() {
+ return &mHBController;
+ }
+
+ void Controller::startMotor() {
+ if (getChan() < WPAD_MAX_CONTROLLERS && !isPlayingSound()) {
+ setRumble();
+ WPADControlMotor(getChan(), WPAD_MOTOR_RUMBLE);
+ }
+ }
+
+ void Controller::stopMotor() {
+ if (getChan() < WPAD_MAX_CONTROLLERS && isRumbling()) {
+ clrRumble();
+ WPADControlMotor(getChan(), WPAD_MOTOR_STOP);
+ }
+ }
+
+ s32 Controller::getInfoAsync(WPADInfo* info) {
+ if (getChan() >= WPAD_MAX_CONTROLLERS) {
+ return -2;
+ }
+
+ if (isPlayingSound() || isRumbling()) {
+ return -2;
+ }
+
+ return WPADGetInfoAsync(getChan(), info, &ControllerCallback);
+ }
+
+ void Controller::ControllerCallback(s32 chan, s32 result) {
+ if (result == WPAD_ESUCCESS && chan < WPAD_MAX_CONTROLLERS) {
+ sBatteryFlag[chan] = true;
+ }
+ }
+
+ bool Controller::getBatteryFlag() const {
+ if (getChan() >= WPAD_MAX_CONTROLLERS) {
+ return false;
+ }
+
+ return sBatteryFlag[getChan()];
+ }
+
+ void Controller::clrBatteryFlag() {
+ if (getChan() >= WPAD_MAX_CONTROLLERS) {
+ return;
+ }
+
+ sBatteryFlag[getChan()] = false;
+ }
+
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMController.h b/src/revolution/homebuttonLib/HBMController.h
new file mode 100644
index 0000000000..41877549cc
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMController.h
@@ -0,0 +1,102 @@
+#ifndef HOMEBUTTON_CONTROLLER_H
+#define HOMEBUTTON_CONTROLLER_H
+
+#include <revolution/mtx.h>
+#include <revolution/os.h>
+#include <revolution/wpad.h>
+#include "HBMCommon.h"
+#include "HBMRemoteSpk.h"
+#include "nw4hbm/snd/SoundArchivePlayer.h"
+#include "nw4hbm/snd/SoundHandle.h"
+
+
+struct HBController {
+ /* 0x00 */ int chan;
+ /* 0x04 */ f32 spVol;
+ /* 0x08 */ f32 x;
+ /* 0x0C */ f32 y;
+ /* 0x10 */ u32 trig;
+ /* 0x14 */ u32 hold;
+ /* 0x18 */ u32 release;
+ /* 0x1C */ bool rumble;
+}; // size = 0x20
+
+namespace homebutton {
+
+ class Controller {
+ public:
+ Controller(int chan, RemoteSpk* spk);
+ ~Controller();
+
+ HBController* getController();
+ int getChan() const;
+ f32 getSpeakerVol() const;
+ RemoteSpk* getRemoteSpk() const { return remotespk; }
+ bool isRumbling() { return mHBController.rumble; }
+ bool getBatteryFlag() const;
+
+ void setSpeakerVol(f32 vol);
+ void setRumble() { mHBController.rumble = true; }
+ void clrRumble() { mHBController.rumble = false; }
+
+ s32 getInfoAsync(WPADInfo* info);
+ bool isPlayReady() const;
+ bool isPlayingSound() const;
+ bool isPlayingSoundId(int id) const;
+
+ void setKpad(const HBMKPadData* con, bool updatePos);
+ void setInValidPos();
+ void clrBatteryFlag();
+ void clrKpadButton();
+
+ void connect();
+ void disconnect();
+
+ void initSound();
+ void updateSound();
+ void playSound(nw4hbm::snd::SoundArchivePlayer* pSoundArchivePlayer, int id);
+
+ void soundOn();
+ void soundOff(int msec);
+
+ void startMotor();
+ void stopMotor();
+
+ void initCallback();
+ void clearCallback();
+
+ static RemoteSpk* GetInstance() { return sPInstance; }
+ static void SetInstance(RemoteSpk* p) { sPInstance = p; }
+
+ private:
+ static void wpadConnectCallback(s32 chan, s32 result);
+ static void wpadExtensionCallback(s32 chan, s32 result);
+ static void soundOnCallback(OSAlarm* alm, OSContext* context);
+ static void ControllerCallback(s32 chan, s32 result);
+
+ private:
+ /* 0x00 */ HBController mHBController;
+ /* 0x20 */ nw4hbm::snd::SoundHandle mSoundHandle;
+ /* 0x24 */ RemoteSpk* remotespk;
+ /* 0x28 */ WPADConnectCallback mOldConnectCallback;
+ /* 0x2C */ WPADExtensionCallback mOldExtensionCallback;
+ /* 0x30 */ OSTime mPlaySoundTime;
+ /* 0x38 */ OSTime mStopSoundTime;
+ /* 0x40 */ bool mCallbackFlag;
+ /* 0x41 */ bool mSoundOffFlag;
+ /* 0x42 */ bool mCheckSoundTimeFlag;
+ /* 0x43 */ bool mCheckSoundIntervalFlag;
+
+ private:
+ static bool sBatteryFlag[WPAD_MAX_CONTROLLERS];
+ static OSAlarm sAlarm[WPAD_MAX_CONTROLLERS];
+ static OSAlarm sAlarmSoundOff[WPAD_MAX_CONTROLLERS];
+ static Controller* sThis[WPAD_MAX_CONTROLLERS];
+ static bool sSetInfoAsync[WPAD_MAX_CONTROLLERS];
+ static RemoteSpk* sPInstance;
+ static s32 lbl_8025DBBC;
+ }; // size = 0x44
+
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMFrameController.cpp b/src/revolution/homebuttonLib/HBMFrameController.cpp
new file mode 100644
index 0000000000..dccaf15996
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMFrameController.cpp
@@ -0,0 +1,68 @@
+
+#include "HBMFrameController.h"
+
+namespace homebutton {
+
+ void FrameController::init(int anm_type, f32 max_frame, f32 min_frame, f32 delta) {
+ mAnmType = anm_type;
+ mMaxFrame = max_frame;
+ mMinFrame = min_frame;
+
+ mDelta = delta;
+ mState = ANIM_STATE_STOP;
+ mbAlternateBack = false;
+
+ initFrame();
+ }
+
+ void FrameController::initFrame() {
+ mFrame = mAnmType == ANIM_TYPE_BACKWARD ? mMaxFrame : mMinFrame;
+ }
+
+ void FrameController::calc() {
+ if (mState != ANIM_STATE_PLAY) {
+ return;
+ }
+
+ switch (mAnmType) {
+ case ANIM_TYPE_FORWARD:
+ if ((mFrame += mDelta) >= getLastFrame()) {
+ mFrame = getLastFrame();
+ stop();
+ }
+
+ break;
+
+ case ANIM_TYPE_BACKWARD:
+ if ((mFrame -= mDelta) <= mMinFrame) {
+ mFrame = mMinFrame;
+ stop();
+ }
+
+ break;
+
+ case ANIM_TYPE_LOOP:
+ if ((mFrame += mDelta) >= mMaxFrame) {
+ mFrame -= mMaxFrame - mMinFrame;
+ }
+
+ break;
+
+ case ANIM_TYPE_ALTERNATE:
+ if (!mbAlternateBack) {
+ if ((mFrame += mDelta) >= getLastFrame()) {
+ mFrame = getLastFrame();
+ mbAlternateBack = true;
+ }
+ } else {
+ if ((mFrame -= mDelta) <= mMinFrame) {
+ mFrame = mMinFrame;
+ mbAlternateBack = false;
+ }
+ }
+
+ break;
+ }
+ }
+
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMFrameController.h b/src/revolution/homebuttonLib/HBMFrameController.h
new file mode 100644
index 0000000000..67c8eb992b
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMFrameController.h
@@ -0,0 +1,76 @@
+#ifndef HOMEBUTTON_FRAME_CONTROLLER_H
+#define HOMEBUTTON_FRAME_CONTROLLER_H
+
+#include <revolution/types.h>
+
+namespace homebutton {
+
+ enum {
+ /* 0 */ ANIM_TYPE_FORWARD = 0,
+ /* 1 */ ANIM_TYPE_BACKWARD,
+ /* 2 */ ANIM_TYPE_LOOP,
+ /* 3 */ ANIM_TYPE_ALTERNATE
+ };
+
+ enum {
+ /* 0 */ ANIM_STATE_STOP = 0,
+ /* 1 */ ANIM_STATE_PLAY,
+ /* 2 */ ANIM_STATE_STOP_REQ,
+ };
+
+ class FrameController {
+ public:
+ FrameController() {}
+
+ /* 0x08 */ virtual ~FrameController() {}
+ /* 0x0C */ virtual void calc();
+
+ void init(int type, f32 maxFrame, f32 minFrame, f32 delta);
+ void initFrame();
+
+ void setMaxFrame(f32 value) { mMaxFrame = value; }
+ f32 getMaxFrame() const { return mMaxFrame; }
+
+ f32 getLastFrame() const { return mMaxFrame - 1.0f; }
+
+ void setMinFrame(f32 value) { mMinFrame = value; }
+ f32 getMinFrame() const { return mMinFrame; }
+
+ void setCurrentFrame(f32 value) { mFrame = value; }
+ f32 getCurrentFrame() const { return mFrame; }
+
+ void setDelta(f32 value) { mDelta = value; }
+ f32 getDelta() const { return mDelta; }
+
+ void setState(int value) { mState = value; }
+ int getState() const { return mState; }
+
+ void setAnimType(int value) { mAnmType = value; }
+ int getAnimType() const { return mAnmType; }
+
+ bool isPlaying() const { return mState == ANIM_STATE_PLAY; }
+
+ void start() {
+ initFrame();
+ restart();
+ }
+
+ void restart() { mState = ANIM_STATE_PLAY; }
+ void stop() { mState = ANIM_STATE_STOP; }
+
+ protected:
+ /* 0x00 (vtable) */
+ /* 0x04 */ f32 mMaxFrame;
+ /* 0x08 */ f32 mMinFrame;
+ /* 0x0C */ f32 mFrame;
+ /* 0x10 */ f32 mDelta;
+ /* 0x14 */ int mState;
+ /* 0x18 */ int mAnmType;
+
+ private:
+ /* 0x1C */ bool mbAlternateBack;
+ };
+
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMGUIManager.cpp b/src/revolution/homebuttonLib/HBMGUIManager.cpp
new file mode 100644
index 0000000000..99ab563da6
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMGUIManager.cpp
@@ -0,0 +1,433 @@
+#include "HBMGUIManager.h"
+
+#include "nw4hbm/lyt/bounding.h"
+#include "nw4hbm/lyt/layout.h"
+#include "nw4hbm/lyt/picture.h"
+#include "nw4hbm/lyt/window.h"
+
+#include "new.h"
+
+namespace homebutton {
+ namespace gui {
+
+ u32 PaneManager::suIDCounter;
+
+ static void drawLine_(f32 x0, f32 y0, f32 x1, f32 y1, f32 z, u8 uWidth, GXColor& rColor);
+
+ static bool is_visible(nw4hbm::lyt::Pane* pPane);
+
+ static void drawLine_(f32 x0, f32 y0, f32 x1, f32 y1, f32 z, u8 uWidth, GXColor& rColor) {
+ static const f32 cubeScale = 1.0f;
+
+ GXClearVtxDesc();
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGBA, GX_F32, 0);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
+
+ GXSetCullMode(GX_CULL_NONE);
+
+ GXSetNumChans(1);
+ GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+
+ GXSetNumTexGens(0);
+ GXSetNumTevStages(1);
+ GXSetTevOp(GX_TEVSTAGE0, GX_BLEND);
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+ GXSetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_NOOP);
+
+ Mtx modelMtx;
+ PSMTXTrans(modelMtx, 0.0f, 0.0f, 0.0f);
+ GXLoadPosMtxImm(modelMtx, 0);
+
+ GXSetLineWidth(uWidth, GX_TO_ZERO);
+
+ GXBegin(GX_LINES, GX_VTXFMT0, 2);
+ GXPosition3f32(x0, y0, z);
+ GXColor1u32(*reinterpret_cast<u32*>(&rColor));
+ GXPosition3f32(x1, y1, z);
+ GXColor1u32(*reinterpret_cast<u32*>(&rColor));
+ GXEnd();
+ }
+
+ bool Component::update(int i, f32 x, f32 y, u32, u32, u32, void* pData) {
+ bool bTouched = false;
+
+ if (!isVisible()) {
+ /* nothing */
+ } else {
+ if (contain(x, y)) {
+ if (isPointed(i)) {
+ onMove(x, y);
+ mpManager->onEvent(getID(), 3, pData);
+ } else {
+ setPointed(i, true);
+ onPoint();
+ mpManager->onEvent(getID(), 1, pData);
+ }
+
+ bTouched = true;
+ } else {
+ if (isPointed(i)) {
+ setPointed(i, false);
+ offPoint();
+ mpManager->onEvent(getID(), 2, pData);
+ }
+ }
+ }
+
+ return bTouched;
+ }
+
+ Manager::~Manager() {
+ void* p = nw4hbm::ut::List_GetFirst(&mIDToComponent);
+
+ for (; p; p = nw4hbm::ut::List_GetFirst(&mIDToComponent)) {
+ nw4hbm::ut::List_Remove(&mIDToComponent, p);
+
+ if (mpAllocator) {
+ MEMFreeToAllocator(mpAllocator, p);
+ } else {
+ delete static_cast<IDToComponent*>(p);
+ }
+ }
+ }
+
+ void Manager::init() {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, i));
+
+ p->mpComponent->init();
+ }
+ }
+
+ void Manager::addComponent(Component* pComponent) {
+ u32 uID = pComponent->getID();
+ pComponent->setManager(this);
+
+ if (mpAllocator) {
+ void* p = MEMAllocFromAllocator(mpAllocator, sizeof(IDToComponent));
+ nw4hbm::ut::List_Append(&mIDToComponent, new (p) IDToComponent(uID, pComponent));
+ } else {
+ nw4hbm::ut::List_Append(&mIDToComponent, new IDToComponent(uID, pComponent));
+ }
+ }
+
+ void Manager::delComponent(Component* pComponent) {
+ IDToComponent* p =
+ static_cast<IDToComponent*>(nw4hbm::ut::List_GetNext(&mIDToComponent, NULL));
+
+ while (p) {
+ if (p->mpComponent == pComponent) {
+ break;
+ }
+
+ p = static_cast<IDToComponent*>(nw4hbm::ut::List_GetNext(&mIDToComponent, p));
+ }
+
+ nw4hbm::ut::List_Remove(&mIDToComponent, p);
+
+ if (mpAllocator) {
+ MEMFreeToAllocator(mpAllocator, p);
+ } else {
+ delete p;
+ }
+ }
+
+ Component* Manager::getComponent(u32 uID) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, uID));
+
+ return p->mpComponent;
+ }
+
+ bool Manager::update(int i, f32 x, f32 y, u32 uTrigFlag, u32 uHoldFlag, u32 uReleaseFlag,
+ void* pData) {
+ bool bTouched = false;
+ Component* pLastContainedComponent = NULL;
+
+ for (u32 n = 0; n < nw4hbm::ut::List_GetSize(&mIDToComponent); n++) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, n));
+
+ if (p->mpComponent->update(i, x, y, uTrigFlag, uHoldFlag, uReleaseFlag, pData)) {
+ if (p->mpComponent->isTriggerTarger()) {
+ pLastContainedComponent = p->mpComponent;
+ }
+
+ bTouched = true;
+ }
+ }
+
+ if (pLastContainedComponent) {
+ if (uTrigFlag) {
+ Vec pos;
+ pLastContainedComponent->onTrig(uTrigFlag, pos);
+
+ onEvent(pLastContainedComponent->getID(), 0, pData);
+ }
+
+ if (uReleaseFlag) {
+ Vec pos;
+ pLastContainedComponent->onTrig(uReleaseFlag, pos);
+
+ onEvent(pLastContainedComponent->getID(), 5, pData);
+ }
+ }
+
+ return bTouched;
+ }
+
+ void Manager::calc() {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, i));
+
+ p->mpComponent->calc();
+ }
+ }
+
+ void Manager::draw() {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, i));
+
+ p->mpComponent->draw();
+ }
+ }
+
+ void Manager::setAllComponentTriggerTarget(bool b) {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ const IDToComponent* p =
+ static_cast<const IDToComponent*>(nw4hbm::ut::List_GetNth(&mIDToComponent, i));
+
+ p->mpComponent->setTriggerTarget(b);
+ }
+ }
+
+ PaneManager::~PaneManager() {
+ PaneToComponent* pPaneToComponent =
+ static_cast<PaneToComponent*>(nw4hbm::ut::List_GetFirst(&mPaneToComponent));
+
+ for (; pPaneToComponent; pPaneToComponent = static_cast<PaneToComponent*>(
+ nw4hbm::ut::List_GetFirst(&mPaneToComponent)))
+ {
+ nw4hbm::ut::List_Remove(&mPaneToComponent, pPaneToComponent);
+
+ if (mpAllocator) {
+ MEMFreeToAllocator(mpAllocator, pPaneToComponent->mpComponent);
+ MEMFreeToAllocator(mpAllocator, pPaneToComponent);
+ } else {
+ delete pPaneToComponent->mpComponent;
+ delete pPaneToComponent;
+ }
+ }
+ }
+
+ void PaneManager::createLayoutScene(const nw4hbm::lyt::Layout& rLayout) {
+ suIDCounter = 0;
+
+ nw4hbm::lyt::Pane* pRootPane = rLayout.GetRootPane();
+
+ walkInChildren(pRootPane->GetChildList());
+ }
+
+ void PaneManager::addLayoutScene(const nw4hbm::lyt::Layout& rLayout) {
+ nw4hbm::lyt::Pane* pRootPane = rLayout.GetRootPane();
+
+ walkInChildren(pRootPane->GetChildList());
+ }
+
+ void PaneManager::walkInChildren(nw4hbm::lyt::PaneList& rPaneList) {
+ for (nw4hbm::lyt::PaneList::Iterator it = rPaneList.GetBeginIter();
+ it != rPaneList.GetEndIter(); it++)
+ {
+ PaneComponent* pPaneComponent = NULL;
+ PaneToComponent* pPaneToComponent = NULL;
+
+ if (mpAllocator) {
+ void* p1 = MEMAllocFromAllocator(mpAllocator, sizeof(*pPaneComponent));
+ void* p2 = MEMAllocFromAllocator(mpAllocator, sizeof(*pPaneToComponent));
+
+ pPaneComponent = new (p1) PaneComponent(suIDCounter);
+ pPaneToComponent = new (p2) PaneToComponent(&(*it), pPaneComponent);
+ } else {
+ pPaneComponent = new PaneComponent(suIDCounter);
+ pPaneToComponent = new PaneToComponent(&(*it), pPaneComponent);
+ }
+
+ nw4hbm::ut::List_Append(&mPaneToComponent, pPaneToComponent);
+ suIDCounter++;
+
+ pPaneComponent->setPane(&(*it));
+
+ if (nw4hbm::ut::DynamicCast<nw4hbm::lyt::Picture*>(&(*it))) {
+ pPaneComponent->setTriggerTarget(true);
+ }
+
+ if (nw4hbm::ut::DynamicCast<nw4hbm::lyt::Window*>(&(*it))) {
+ pPaneComponent->setTriggerTarget(true);
+ }
+
+ addComponent(pPaneComponent);
+ walkInChildren(it->GetChildList());
+ }
+ }
+
+ void PaneManager::delLayoutScene(const nw4hbm::lyt::Layout& rLayout) {
+ nw4hbm::lyt::Pane* pRootPane = rLayout.GetRootPane();
+
+ walkInChildrenDel(pRootPane->GetChildList());
+ }
+
+ void PaneManager::walkInChildrenDel(nw4hbm::lyt::PaneList& rPaneList) {
+ for (nw4hbm::lyt::PaneList::Iterator it = rPaneList.GetBeginIter();
+ it != rPaneList.GetEndIter(); it++)
+ {
+ PaneToComponent* pPaneToComponent = static_cast<PaneToComponent*>(
+ nw4hbm::ut::List_GetNext(&mPaneToComponent, NULL));
+
+ while (pPaneToComponent) {
+ if (pPaneToComponent->mpPane == &(*it)) {
+ break;
+ }
+
+ pPaneToComponent = static_cast<PaneToComponent*>(
+ nw4hbm::ut::List_GetNext(&mPaneToComponent, pPaneToComponent));
+ }
+
+ delComponent(pPaneToComponent->mpComponent);
+ nw4hbm::ut::List_Remove(&mPaneToComponent, pPaneToComponent);
+ suIDCounter--;
+
+ if (mpAllocator) {
+ MEMFreeToAllocator(mpAllocator, pPaneToComponent->mpComponent);
+ MEMFreeToAllocator(mpAllocator, pPaneToComponent);
+ } else {
+ delete pPaneToComponent->mpComponent;
+ delete pPaneToComponent;
+ }
+
+ walkInChildrenDel(it->GetChildList());
+ }
+ }
+
+ PaneComponent* PaneManager::getPaneComponentByPane(nw4hbm::lyt::Pane* pPane) {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ PaneToComponent* p =
+ static_cast<PaneToComponent*>(nw4hbm::ut::List_GetNth(&mPaneToComponent, i));
+
+ if (p->mpPane == pPane) {
+ return p->mpComponent;
+ }
+ }
+
+ return NULL;
+ }
+
+#pragma push
+#pragma opt_propagation off // ???
+
+ void PaneManager::setAllBoundingBoxComponentTriggerTarget(bool b) {
+ for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) {
+ PaneToComponent* p =
+ static_cast<PaneToComponent*>(nw4hbm::ut::List_GetNth(&mPaneToComponent, i));
+
+ if (nw4hbm::ut::DynamicCast<nw4hbm::lyt::Bounding*>(p->mpPane)) {
+ p->mpComponent->setTriggerTarget(b);
+ }
+ }
+ }
+
+#pragma pop
+
+ bool PaneComponent::contain(f32 x_, f32 y_) {
+ if (!mpManager) {
+ return false;
+ }
+
+ // goes into PaneManager vtable?
+ const nw4hbm::lyt::DrawInfo* pDrawInfo =
+ static_cast<PaneManager*>(mpManager)->getDrawInfo();
+
+ if (!pDrawInfo) {
+ return false;
+ }
+
+ nw4hbm::math::MTX34 invGlbMtx;
+ PSMTXInverse(mpPane->GetGlobalMtx(), invGlbMtx);
+
+ nw4hbm::math::VEC3 lclPos;
+ PSMTXMultVec(invGlbMtx, nw4hbm::math::VEC3(x_, y_, 0.0f), lclPos);
+
+ nw4hbm::ut::Rect rect = mpPane->GetPaneRect(*pDrawInfo);
+
+ if (rect.left <= lclPos.x && lclPos.x <= rect.right && rect.bottom <= lclPos.y &&
+ lclPos.y <= rect.top)
+ {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void PaneComponent::draw() {
+ const nw4hbm::lyt::DrawInfo* pDrawInfo =
+ static_cast<PaneManager*>(mpManager)->getDrawInfo();
+
+ if (!pDrawInfo) {
+ return;
+ }
+
+ // some stripped debug thing?
+ const nw4hbm::math::VEC3& translate = mpPane->GetTranslate();
+
+ nw4hbm::lyt::Size size = mpPane->GetSize();
+
+ const nw4hbm::math::MTX34& gmtx = mpPane->GetGlobalMtx();
+
+ f32 x = gmtx.mtx[0][3];
+ f32 y = gmtx.mtx[1][3];
+
+ GXColor color = {0xff, 0x00, 0x00, 0xff}; // red
+
+ if (mabPointed[0]) {
+ color.r = 0x00;
+ color.b = 0xff; // now blue
+ }
+
+ // start at top left, go clockwise
+ drawLine_(x - size.width / 2.0f, y - size.height / 2.0f, x + size.width / 2.0f,
+ y - size.height / 2.0f, 0.0f, 8, color);
+ drawLine_(x + size.width / 2.0f, y - size.height / 2.0f, x + size.width / 2.0f,
+ y + size.height / 2.0f, 0.0f, 8, color);
+ drawLine_(x + size.width / 2.0f, y + size.height / 2.0f, x - size.width / 2.0f,
+ y + size.height / 2.0f, 0.0f, 8, color);
+ drawLine_(x - size.width / 2.0f, y + size.height / 2.0f, x - size.width / 2.0f,
+ y - size.height / 2.0f, 0.0f, 8, color);
+ }
+
+#pragma global_optimizer off // ...ok!
+
+ static bool is_visible(nw4hbm::lyt::Pane* pPane) {
+ if (!pPane->IsVisible()) {
+ return false;
+ }
+
+ if (!pPane->GetParent()) {
+ return true;
+ }
+
+ return is_visible(pPane->GetParent());
+ }
+
+ bool PaneComponent::isVisible() {
+ return is_visible(mpPane);
+ }
+
+#pragma global_optimizer reset
+
+ } // namespace gui
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMGUIManager.h b/src/revolution/homebuttonLib/HBMGUIManager.h
new file mode 100644
index 0000000000..44c11c146b
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMGUIManager.h
@@ -0,0 +1,234 @@
+#ifndef HOMEBUTTON_GUI_MANAGER_H
+#define HOMEBUTTON_GUI_MANAGER_H
+
+#include <revolution/kpad.h>
+#include <revolution/mem.h>
+#include <revolution/mtx.h>
+#include "nw4hbm/lyt/pane.h"
+#include "nw4hbm/ut/list.h"
+
+
+namespace nw4hbm {
+ namespace lyt {
+ class DrawInfo;
+ class Layout;
+ } // namespace lyt
+} // namespace nw4hbm
+
+namespace homebutton {
+ namespace gui {
+
+ class Manager;
+ class PaneComponent;
+
+ class Interface {
+ public:
+ Interface() {}
+
+ /* 0x08 */ virtual void create() {}
+ /* 0x0C */ virtual void init() {}
+ /* 0x10 */ virtual void calc() {}
+ /* 0x14 */ virtual void draw(Mtx&) {}
+ /* 0x18 */ virtual void draw() {}
+ /* 0x1C */ virtual ~Interface() {}
+
+ private:
+ /* 0x00 (vtable) */
+ }; // size = 0x04
+
+ class EventHandler {
+ public:
+ EventHandler() {}
+
+ /* 0x08 */ virtual void onEvent(u32, u32, void*) {}
+ /* 0x0C */ virtual void setManager(Manager* pManager) { mpManager = pManager; }
+
+ protected:
+ /* 0x00 (vtable) */
+ /* 0x04 */ Manager* mpManager;
+ }; // size = 0x08
+
+ class Component : public Interface {
+ public:
+ Component(u32 uID)
+ : mDragStartPos(), mbDragging(), muDraggingButton(), muID(uID), mbTriggerTarger(),
+ mpManager() {
+ init();
+ }
+
+ /* 0x0C */ virtual void init() {
+ mbDragging = false;
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mabPointed); i++) {
+ mabPointed[i] = false;
+ }
+ }
+
+ /* 0x1C */ virtual ~Component() {}
+ /* 0x20 */ virtual u32 getID() { return muID; }
+ /* 0x24 */ virtual int isPointed(int n) { return mabPointed[n]; }
+ /* 0x28 */ virtual void setPointed(int n, bool b) { mabPointed[n] = b; }
+ /* 0x2C */ virtual void onPoint() {}
+ /* 0x30 */ virtual void offPoint() {}
+ /* 0x34 */ virtual void onDrag(f32, f32) {}
+ /* 0x38 */ virtual void onMove(f32, f32) {}
+
+ /* 0x3C */ virtual void onTrig(u32 uFlag, Vec& vec) {
+ if (uFlag & muDraggingButton) {
+ mDragStartPos = vec;
+ mbDragging = true;
+ }
+ }
+
+ /* 0x40 */ virtual void setDraggingButton(u32 uDraggingButton) {
+ muDraggingButton = uDraggingButton;
+ }
+ /* 0x44 */ virtual bool update(int, const KPADStatus*, f32, f32, void*) {
+ return false;
+ }
+ /* 0x48 */ virtual bool update(int i, f32 x, f32 y, u32 uTrigFlag, u32 uHoldFlag,
+ u32 uReleaseFlag, void* pData);
+ /* 0x4C */ virtual bool isTriggerTarger() { return mbTriggerTarger; }
+ /* 0x50 */ virtual void setTriggerTarget(bool bTriggerTarget) {
+ mbTriggerTarger = bTriggerTarget;
+ }
+ /* 0x54 */ virtual void setManager(Manager* pManager) { mpManager = pManager; }
+ /* 0x58 */ virtual bool isVisible() { return true; }
+ /* 0x5C */ virtual bool contain(f32 x_, f32 y_) = 0;
+
+ protected:
+ /* 0x00 (base) */
+ /* 0x04 */ bool mabPointed[8];
+ /* 0x0C */ Vec mDragStartPos;
+ /* 0x18 */ bool mbDragging;
+ /* 0x1C */ u32 muDraggingButton;
+ /* 0x20 */ u32 muID;
+ /* 0x24 */ bool mbTriggerTarger;
+ /* 0x28 */ Manager* mpManager;
+ }; // size = 0x2C
+
+ class Manager : public Interface {
+ // nested types
+ private:
+ struct IDToComponent {
+ public:
+ /* 0x00 */ u32 muID;
+ /* 0x04 */ Component* mpComponent;
+ /* 0x08 */ nw4hbm::ut::Link mLink;
+
+ IDToComponent(u32 uID, Component* pComponent)
+ : muID(uID), mpComponent(pComponent) {}
+ }; // size = 0x10
+
+ public:
+ Manager(EventHandler* pEventHandler, MEMAllocator* pAllocator)
+ : mpEventHandler(pEventHandler), mpAllocator(pAllocator) {
+ if (mpEventHandler) {
+ mpEventHandler->setManager(this);
+ }
+
+ nw4hbm::ut::List_Init(&mIDToComponent, 8);
+ }
+
+ /* 0x0C */ virtual void init();
+ /* 0x10 */ virtual void calc();
+ /* 0x18 */ virtual void draw();
+ /* 0x1C */ virtual ~Manager();
+ /* 0x20 */ virtual void addComponent(Component* pComponent);
+ /* 0x24 */ virtual Component* getComponent(u32 uID);
+ /* 0x28 */ virtual bool update(int, const KPADStatus*, f32, f32, void*) {
+ return false;
+ }
+ /* 0x2C */ virtual bool update(int i, f32 x, f32 y, u32 uTrigFlag, u32 uHoldFlag,
+ u32 uReleaseFlag, void* pData);
+
+ /* 0x30 */ virtual void onEvent(u32 uID, u32 uEvent, void* pData) {
+ if (mpEventHandler) {
+ mpEventHandler->onEvent(uID, uEvent, pData);
+ }
+ }
+
+ /* 0x34 */ virtual void setAllComponentTriggerTarget(bool b);
+
+ /* 0x38 */ virtual void setEventHandler(EventHandler* pEventHandler) {
+ mpEventHandler = pEventHandler;
+
+ if (mpEventHandler) {
+ mpEventHandler->setManager(this);
+ }
+ }
+
+ void delComponent(Component* pComponent);
+
+ protected:
+ /* 0x00 (base) */
+ /* 0x04 */ EventHandler* mpEventHandler;
+ /* 0x08 */ nw4hbm::ut::List mIDToComponent;
+ /* 0x14 */ MEMAllocator* mpAllocator;
+ }; // size = 0x18
+
+ class PaneManager : public Manager {
+ // nested types
+ private:
+ struct PaneToComponent {
+ public:
+ PaneToComponent(nw4hbm::lyt::Pane* pPane, PaneComponent* pComponent)
+ : mpPane(pPane), mpComponent(pComponent) {}
+
+ public:
+ /* 0x00 */ nw4hbm::lyt::Pane* mpPane;
+ /* 0x04 */ PaneComponent* mpComponent;
+ /* 0x08 */ nw4hbm::ut::Link mLink;
+ }; // size = 0x10
+
+ public:
+ PaneManager(EventHandler* pEventHandler, const nw4hbm::lyt::DrawInfo* pDrawInfo,
+ MEMAllocator* pAllocator)
+ : Manager(pEventHandler, pAllocator), mpDrawInfo(pDrawInfo) {
+ nw4hbm::ut::List_Init(&mPaneToComponent, 8);
+ }
+
+ /* 0x1C */ virtual ~PaneManager();
+ /* 0x30 */ virtual void createLayoutScene(const nw4hbm::lyt::Layout& rLayout);
+ /* 0x34 */ virtual PaneComponent* getPaneComponentByPane(nw4hbm::lyt::Pane* pPane);
+ /* 0x38 */ virtual const nw4hbm::lyt::DrawInfo* getDrawInfo() { return mpDrawInfo; }
+ /* 0x3C */ virtual void setDrawInfo(const nw4hbm::lyt::DrawInfo* pDrawInfo) {
+ mpDrawInfo = pDrawInfo;
+ }
+ /* 0x40 */ virtual void setAllBoundingBoxComponentTriggerTarget(bool b);
+ /* 0x44 */ virtual void walkInChildren(nw4hbm::lyt::PaneList& rPaneList);
+
+ void walkInChildrenDel(nw4hbm::lyt::PaneList& rPaneList);
+ void delLayoutScene(const nw4hbm::lyt::Layout& rLayout);
+ void addLayoutScene(const nw4hbm::lyt::Layout& rLayout);
+
+ private:
+ /* 0x00 (base) */
+ /* 0x18 */ nw4hbm::ut::List mPaneToComponent;
+ /* 0x24 */ const nw4hbm::lyt::DrawInfo* mpDrawInfo;
+ /* 0x28 */ u16 muNumPoint;
+ u16 muPadding;
+
+ // static members
+ static u32 suIDCounter;
+ }; // size = 0x2C
+
+ class PaneComponent : public Component {
+ public:
+ PaneComponent(u32 uID) : Component(uID), mpPane() {}
+
+ /* 0x18 */ virtual void draw();
+ /* 0x1C */ virtual ~PaneComponent() {}
+ /* 0x20 */ virtual bool isVisible();
+ /* 0x24 */ virtual bool contain(f32 x_, f32 y_);
+ /* 0x28 */ virtual void setPane(nw4hbm::lyt::Pane* pPane) { mpPane = pPane; }
+ /* 0x2C */ virtual nw4hbm::lyt::Pane* getPane() { return mpPane; }
+
+ protected:
+ /* 0x00 (base) */
+ /* 0x2C */ nw4hbm::lyt::Pane* mpPane;
+ }; // size = 0x30
+ } // namespace gui
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/HBMRemoteSpk.cpp b/src/revolution/homebuttonLib/HBMRemoteSpk.cpp
new file mode 100644
index 0000000000..4d2bc75c2d
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMRemoteSpk.cpp
@@ -0,0 +1,259 @@
+#include "HBMRemoteSpk.h"
+
+#include "HBMController.h"
+
+#include "string.h"
+
+namespace homebutton {
+
+ static bool MakeVolumeData(const s16* src, s16* dst, int vol, u32 size);
+
+ void RemoteSpk::SetInstance(RemoteSpk* pThis) {
+ Controller::SetInstance(pThis);
+ }
+
+ RemoteSpk* RemoteSpk::GetInstance(void) {
+ return Controller::GetInstance();
+ }
+
+ void RemoteSpk::GetPCMFromSeID(int in_ID, s16*& out_wave, int& out_length) {
+ ARCFileInfo af;
+ ARCFastOpen(&handle, in_ID, &af);
+
+ out_wave = static_cast<s16*>(ARCGetStartAddrInMem(&af));
+ out_length = ARCGetLength(&af);
+
+ ARCClose(&af);
+ }
+
+ static bool MakeVolumeData(const s16* src, s16* dst, int vol, u32 size) {
+ u32 enc_size = size <= 40 ? size : 40;
+ for (int i = 0; (u32)i < enc_size; i++) {
+ *dst++ = static_cast<s16>(*src++ * vol / 10);
+ }
+
+ if (size > 40) {
+ return false;
+ }
+
+ u32 zero_size = 40 - size;
+ for (int i = 0; (u32)i < zero_size; i++) {
+ *dst++ = 0;
+ }
+
+ return true;
+ }
+
+ void RemoteSpk::UpdateSpeaker(OSAlarm*, OSContext*) {
+ s16 pcmBuffer[40];
+ u8 adpcmBuffer[20];
+
+ if (!GetInstance()) {
+ return;
+ }
+
+ ChanInfo* pinfo = GetInstance()->info;
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++, pinfo++) {
+ if (pinfo->in_pcm && WPADIsSpeakerEnabled(i)) {
+ int intrStatus = OSDisableInterrupts(); /* int intr */
+
+ if (WPADCanSendStreamData(i)) {
+ MakeVolumeData(pinfo->in_pcm, pcmBuffer, pinfo->vol,
+ static_cast<u32>(pinfo->length) / sizeof(s16));
+ WENCGetEncodeData(&pinfo->wencinfo, pinfo->first ? 0 : 1, pcmBuffer, 40,
+ adpcmBuffer);
+ WPADSendStreamData(i, adpcmBuffer, 20);
+
+ pinfo->first = false;
+ pinfo->cannotSendCnt = 0;
+ pinfo->in_pcm += 40;
+ pinfo->length -= 40 * sizeof(s16);
+
+ if (pinfo->length <= 0) {
+ pinfo->seId = -1;
+ pinfo->in_pcm = NULL;
+ }
+ } else {
+ pinfo->cannotSendCnt++;
+
+#if HBM_REVISION == 1
+ if (pinfo->cannotSendCnt > 300) {
+ pinfo->in_pcm = NULL;
+ }
+#else
+ if (pinfo->cannotSendCnt > 60) {
+ pinfo->in_pcm = NULL;
+ }
+#endif
+ }
+
+ OSRestoreInterrupts(intrStatus);
+ }
+ }
+ }
+
+ void RemoteSpk::ClearPcm(void) {
+ ChanInfo* info = GetInstance()->info;
+
+ info->seId = -1;
+ info->in_pcm = NULL;
+ }
+
+ RemoteSpk::RemoteSpk(void* spkSeBuf) {
+ SetInstance(this);
+
+ if (spkSeBuf) {
+ available = ARCInitHandle(spkSeBuf, &handle) ? TRUE : FALSE;
+ } else {
+ available = false;
+ }
+
+ OSCreateAlarm(&speakerAlarm);
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ OSCreateAlarm(&info[i].alarm);
+ info[i].in_pcm = NULL;
+ info[i].seId = -1;
+ info[i].first = true;
+ info[i].playReady = true;
+ }
+ }
+
+ RemoteSpk::~RemoteSpk(void) {
+#if HBM_REVISION > 1
+ SetInstance(NULL);
+#endif
+
+ available = false;
+
+ OSCancelAlarm(&speakerAlarm);
+
+ for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+#if HBM_REVISION == 1
+ WPADControlSpeaker(i, WPAD_SPEAKER_OFF, NULL);
+#endif
+
+ OSCancelAlarm(&info[i].alarm);
+ }
+
+#if HBM_REVISION == 1
+ SetInstance(NULL);
+#endif
+ }
+
+ void RemoteSpk::Start(void) {
+ if (!available) {
+ return;
+ }
+
+ OSCreateAlarm(&speakerAlarm);
+ OSSetPeriodicAlarm(&speakerAlarm, OSGetTime(), OSNanosecondsToTicks(6666667),
+ &UpdateSpeaker);
+ }
+
+ void RemoteSpk::Stop(void) {
+ OSCancelAlarm(&speakerAlarm);
+ }
+
+ void RemoteSpk::DelaySpeakerOnCallback(OSAlarm* alarm, OSContext*) {
+ s32 chan = (s32)OSGetAlarmUserData(alarm);
+ s32 result = WPADControlSpeaker(chan, WPAD_SPEAKER_ENABLE, SpeakerOnCallback);
+ }
+
+ void RemoteSpk::SpeakerOnCallback(s32 chan, s32 result) {
+ RemoteSpk* pRmtSpk = GetInstance();
+ if (!pRmtSpk) {
+ return;
+ }
+
+ switch (result) {
+ case WPAD_ESUCCESS:
+ pRmtSpk->info[chan].first = true;
+ result = WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, &SpeakerPlayCallback);
+ break;
+
+ case WPAD_EBUSY:
+ OSSetAlarmUserData(&pRmtSpk->info[chan].alarm, (void*)chan);
+ OSCancelAlarm(&pRmtSpk->info[chan].alarm);
+ OSSetAlarm(&pRmtSpk->info[chan].alarm, OSMillisecondsToTicks(50),
+ &DelaySpeakerOnCallback);
+ break;
+ }
+ }
+
+ void RemoteSpk::DelaySpeakerPlayCallback(OSAlarm* alarm, OSContext*) {
+ s32 chan = (s32)OSGetAlarmUserData(alarm);
+ s32 result = WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, &SpeakerPlayCallback);
+ }
+
+ void RemoteSpk::SpeakerPlayCallback(s32 chan, s32 result) {
+ RemoteSpk* pRmtSpk = GetInstance();
+ if (!pRmtSpk) {
+ return;
+ }
+
+ switch (result) {
+ case WPAD_ESUCCESS:
+ pRmtSpk->info[chan].playReady = true;
+ break;
+
+ case WPAD_ENODEV:
+ pRmtSpk->info[chan].playReady = false;
+ break;
+
+ case WPAD_EBUSY:
+ OSSetAlarmUserData(&pRmtSpk->info[chan].alarm, (void*)chan);
+ OSCancelAlarm(&pRmtSpk->info[chan].alarm);
+ OSSetAlarm(&pRmtSpk->info[chan].alarm, OSMillisecondsToTicks(50),
+ &DelaySpeakerPlayCallback);
+ break;
+ }
+ }
+
+ void RemoteSpk::Connect(s32 chan) {
+ if (!available) {
+ return;
+ }
+
+ // int?
+ int result = WPADControlSpeaker(chan, WPAD_SPEAKER_ENABLE, &SpeakerOnCallback);
+
+ u32* p = reinterpret_cast<u32*>(&info[chan].wencinfo);
+ memset(p, 0, sizeof(WENCInfo));
+
+ info[chan].first = true;
+ info[chan].playReady = false;
+ }
+
+ void RemoteSpk::Play(s32 chan, int seID, s8 vol) {
+ if (!available) {
+ return;
+ }
+
+ s16* pcm;
+ int length;
+ GetPCMFromSeID(seID, pcm, length);
+
+ info[chan].cannotSendCnt = 0;
+ info[chan].seId = seID;
+ info[chan].length = length;
+ info[chan].vol = vol;
+ info[chan].in_pcm = pcm;
+ }
+
+ bool RemoteSpk::isPlaying(s32 chan) const {
+ return info[chan].in_pcm != NULL;
+ }
+
+ bool RemoteSpk::isPlayingId(s32 chan, int seId) const {
+ if (isPlaying(chan) && info[chan].seId == seId) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool RemoteSpk::isPlayReady(s32 chan) const {
+ return info[chan].playReady != false;
+ }
+
+} // namespace homebutton
diff --git a/src/revolution/homebuttonLib/HBMRemoteSpk.h b/src/revolution/homebuttonLib/HBMRemoteSpk.h
new file mode 100644
index 0000000000..21d780fce6
--- /dev/null
+++ b/src/revolution/homebuttonLib/HBMRemoteSpk.h
@@ -0,0 +1,65 @@
+#ifndef HOMEBUTTON_REMOTE_SPK_H
+#define HOMEBUTTON_REMOTE_SPK_H
+
+#include <revolution/arc.h>
+#include <revolution/os.h>
+#include <revolution/wenc.h>
+#include <revolution/wpad.h>
+
+namespace homebutton {
+
+ class RemoteSpk {
+ private:
+ struct ChanInfo {
+ /* 0x00 */ OSAlarm alarm;
+ /* 0x30 */ WENCInfo wencinfo;
+ /* 0x50 */ const s16* in_pcm;
+ /* 0x54 */ int length;
+ /* 0x58 */ int seId;
+ /* 0x5C */ bool first;
+ /* 0x5D */ s8 vol;
+ /* 0x5E */ s8 cannotSendCnt;
+ /* 0x60 */ u16 pad_60;
+ /* 0x62 */ bool playReady;
+ }; // size = 0x68
+
+ public:
+ RemoteSpk(void* spkSeBuf);
+
+ bool isPlayReady(s32 chan) const;
+ bool isPlaying(s32 chan) const;
+ bool isPlayingId(s32 chan, int seId) const;
+
+ void GetPCMFromSeID(int in_ID, s16*& out_wave, int& out_length);
+ void ClearPcm();
+ void Start();
+ void Stop();
+ void Connect(s32 chan);
+ void Play(s32 chan, int seID, s8 vol);
+
+ static void SetInstance(RemoteSpk* pThis);
+ static RemoteSpk* GetInstance();
+
+ private:
+ static void UpdateSpeaker(OSAlarm* alarm, OSContext* context);
+
+ static void SpeakerOnCallback(s32 chan, s32 result);
+ static void DelaySpeakerOnCallback(OSAlarm* alarm, OSContext* context);
+
+ static void SpeakerPlayCallback(s32 chan, s32 result);
+ static void DelaySpeakerPlayCallback(OSAlarm* alarm, OSContext* context);
+
+ private:
+ /* 0x000 */ ChanInfo info[WPAD_MAX_CONTROLLERS];
+ /* 0x1A0 */ OSAlarm speakerAlarm;
+ /* 0x1D0 */ ARCHandle handle;
+ /* 0x1EC */ bool available;
+ /* 0x1F0 (vtable) */
+
+ public:
+ /* 0x08 */ virtual ~RemoteSpk();
+ }; // size = 0x1F8
+
+} // namespace homebutton
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/DbgPrintBase.h b/src/revolution/homebuttonLib/nw4hbm/db/DbgPrintBase.h
new file mode 100644
index 0000000000..54c3657af3
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/DbgPrintBase.h
@@ -0,0 +1,12 @@
+#ifndef NW4R_DB_DBG_PRINT_BASE_H
+#define NW4R_DB_DBG_PRINT_BASE_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace db {
+
+ }
+} // namespace nw4r
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/assert.h b/src/revolution/homebuttonLib/nw4hbm/db/assert.h
new file mode 100644
index 0000000000..3e62ab08d7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/assert.h
@@ -0,0 +1,111 @@
+#ifndef NW4R_DB_ASSERT_H
+#define NW4R_DB_ASSERT_H
+
+#include <revolution/types.h>
+
+#include <stdio.h>
+
+namespace nw4hbm {
+ namespace db {
+ // Forward declarations
+ namespace detail {
+ class ConsoleHead;
+ }
+
+ /* DECL_WEAK */ void VPanic(const char* file, int line, const char* fmt, std::va_list vlist);
+ /* DECL_WEAK */ void VWarning(const char* file, int line, const char* fmt, std::va_list vlist);
+
+ namespace detail {
+ void Log(const char* fmt, ...);
+ /* DECL_WEAK */ void Panic(const char* file, int line, const char* fmt, ...);
+ /* DECL_WEAK */ void Warning(const char* file, int line, const char* msg, ...);
+ }
+
+ detail::ConsoleHead* Assertion_SetConsole(detail::ConsoleHead* console);
+ detail::ConsoleHead* Assertion_GetConsole();
+ void Assertion_ShowConsole(u32 time);
+ void Assertion_HideConsole();
+ void Assertion_SetWarningTime(u32 time);
+ bool Assertion_SetAutoWarning(bool enable);
+ } // namespace db
+} // namespace nw4r
+
+#if NW4HBM_DEBUG
+#define NW4R_DB_WARNING(line, exp, ...) \
+ (void)((exp) || (nw4hbm::db::detail::Warning(__FILE__, line, __VA_ARGS__), 0))
+#define NW4R_DB_ASSERTMSG(line, exp, ...) \
+ (void)((exp) || (nw4hbm::db::detail::Panic(__FILE__, line, __VA_ARGS__), 0))
+#else
+#define NW4R_DB_WARNING(line, exp, ...) (void)0
+#define NW4R_DB_ASSERTMSG(line, exp, ...) (void)0
+#endif
+
+#define NW4R_ASSERT(line, exp) \
+ NW4R_DB_ASSERTMSG(line, (exp), "Failed assertion " #exp)
+
+#define NW4R_ASSERT_CHECK_NULL(line, ptr) \
+ NW4R_DB_ASSERTMSG(line, (ptr != NULL), "Pointer must not be NULL ("#ptr")")
+
+#define POINTER_VALID_TEST(ptr_) \
+ (((unsigned long)ptr_ & 0xFF000000) == 0x80000000 || ((unsigned long)ptr_ & 0xFF800000) == 0x81000000 || \
+ ((unsigned long)ptr_ & 0xF8000000) == 0x90000000 || ((unsigned long)ptr_ & 0xFF000000) == 0xC0000000 || \
+ ((unsigned long)ptr_ & 0xFF800000) == 0xC1000000 || ((unsigned long)ptr_ & 0xF8000000) == 0xD0000000 || \
+ ((unsigned long)ptr_ & 0xFFFFC000) == 0xE0000000)
+
+#define NW4R_ASSERT_VALID_PTR(line, ptr) \
+ NW4R_DB_ASSERTMSG(line, POINTER_VALID_TEST(ptr), "Pointer Error\n" #ptr "(=%p) is not valid pointer.", ptr)
+
+#define NW4R_ASSERT_MIN(line_, var_, minValue_) \
+ NW4R_DB_ASSERTMSG(line_, minValue_ <= var_, \
+ #var_ " is out of bounds(%d)\n%d <= " #var_ " not satisfied.", (int)(var_), \
+ (int)(minValue_))
+
+#define NW4R_ASSERT_MINMAX(line_, var_, minValue_, maxValue_) \
+ NW4R_DB_ASSERTMSG(line_, (var_) >= (minValue_) && (var_) < (maxValue_), \
+ #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), \
+ (int)(minValue_), (int)(maxValue_))
+
+#define NW4R_ASSERT_MINMAXLT(line_, var_, minValue_, maxValue_) \
+ NW4R_DB_ASSERTMSG(line_, (var_) >= (minValue_) && (var_) <= (maxValue_), \
+ #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), \
+ (int)(minValue_), (int)(maxValue_))
+
+#define NW4R_IS_ALIGNED_(x, align) \
+ (((unsigned long)(x) & ((align) - 1)) == 0)
+
+#define NW4R_ASSERT_ALIGN2(line, exp) \
+ NW4R_DB_ASSERTMSG(line, NW4R_IS_ALIGNED_(exp, 2), \
+ "Alignment Error(0x%x)\n" #exp " must be aligned to 2 bytes boundary.", \
+ exp)
+
+#define NW4R_ASSERT_ALIGN32(line, exp) \
+ NW4R_DB_ASSERTMSG(line, NW4R_IS_ALIGNED_(exp, 32), \
+ "Alignment Error(0x%x)\n" #exp " must be aligned to 32 bytes boundary.", \
+ exp)
+
+// NW4HBM variants
+#define NW4HBM_ASSERT_CHECK_NULL(line, ptr) \
+ NW4R_DB_ASSERTMSG(line, (ptr != NULL), "NW4HBM:Pointer must not be NULL ("#ptr")")
+
+#define NW4HBM_ASSERT(line, exp) \
+ NW4R_DB_ASSERTMSG(line, (exp), "NW4HBM:Failed assertion " #exp)
+
+#define NW4HBM_ASSERT_VALID_PTR(line, ptr) \
+ NW4R_DB_ASSERTMSG(line, POINTER_VALID_TEST(ptr), "NW4HBM:Pointer Error\n" #ptr "(=%p) is not valid pointer.", ptr)
+
+#define NW4HBM_ASSERT_ALIGN2(line, exp) \
+ NW4R_DB_ASSERTMSG(line, NW4R_IS_ALIGNED_(exp, 2), \
+ "NW4HBM:Alignment Error(0x%x)\n" #exp " must be aligned to 2 bytes boundary.", \
+ exp)
+
+#define NW4HBM_ASSERT_ALIGN32(line, exp) \
+ NW4R_DB_ASSERTMSG(line, NW4R_IS_ALIGNED_(exp, 32), \
+ "NW4HBM:Alignment Error(0x%x)\n" #exp " must be aligned to 32 bytes boundary.", \
+ exp)
+
+#define NW4HBM_ASSERT_ALIGNED(line, exp, align) \
+ NW4R_DB_ASSERTMSG(line, NW4R_IS_ALIGNED_(exp, align), \
+ "NW4HBM:Alignment Error(0x%x)\n" #exp " must be aligned to " #align " bytes boundary.", \
+ exp)
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/console.h b/src/revolution/homebuttonLib/nw4hbm/db/console.h
new file mode 100644
index 0000000000..53a9ac1ea9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/console.h
@@ -0,0 +1,104 @@
+#ifndef NW4R_DB_CONSOLE_H
+#define NW4R_DB_CONSOLE_H
+
+#include <revolution/types.h>
+
+#include "assert.h"
+#include "../ut/CharWriter.h"
+#include "../ut/TextWriterBase.h"
+
+namespace nw4hbm {
+ namespace db {
+ namespace detail {
+ struct ConsoleHead {
+ /* 0x00 */ u8* textBuf;
+ /* 0x04 */ u16 width;
+ /* 0x06 */ u16 height;
+ /* 0x08 */ u16 priority;
+ /* 0x0A */ u16 attr;
+ /* 0x0C */ u16 printTop;
+ /* 0x0E */ u16 printXPos;
+ /* 0x10 */ u16 printTopUsed;
+ /* 0x12 */ u16 ringTop;
+ /* 0x14 */ s32 ringTopLineCnt;
+ /* 0x18 */ s32 viewTopLine;
+ /* 0x1C */ s16 viewPosX;
+ /* 0x1E */ s16 viewPosY;
+ /* 0x20 */ u16 viewLines;
+ /* 0x22 */ bool isVisible;
+ /* 0x23 */ u8 padding_[1];
+ /* 0x24 */ ut::TextWriterBase<char>* writer;
+ /* 0x28 */ ConsoleHead* next;
+ };
+ } // namespace detail
+
+ enum ConsoleOutputType {
+ CONSOLE_OUTPUT_NONE,
+ CONSOLE_OUTPUT_TERMINAL,
+ CONSOLE_OUTPUT_DISPLAY,
+ CONSOLE_OUTPUT_ALL,
+ };
+
+ typedef detail::ConsoleHead* ConsoleHandle;
+
+ typedef void (*VisitStringCallback)(detail::ConsoleHead* console, u8* r4, long r5, u32 r6);
+
+ detail::ConsoleHead* Console_Create(void* buffer, u16 width, u16 height, u16 viewHeight,
+ u16 priority, u16 attr);
+ void Console_Destroy(detail::ConsoleHead* console);
+ void Console_Clear(detail::ConsoleHead* console);
+ void Console_Draw(detail::ConsoleHead* console, ut::TextWriterBase<char>& writer);
+ void Console_DrawDirect(detail::ConsoleHead* console);
+ void Console_DrawAll();
+ void Console_DrawDirectAll();
+ void Console_VFPrintf(ConsoleOutputType type, detail::ConsoleHead* console,
+ const char* format, std::va_list vlist);
+ void Console_FPrintf(ConsoleOutputType type, detail::ConsoleHead* console,
+ const char* format);
+ void Console_Printf(detail::ConsoleHead* console, const char* format, ...);
+ void Console_PrintfD(detail::ConsoleHead* console, const char* format, ...);
+ void Console_PrintfT(detail::ConsoleHead* console, const char* format, ...);
+ u16 Console_ChangePriority(detail::ConsoleHead* console, u16 r4);
+ void Console_VisitString(detail::ConsoleHead* console, VisitStringCallback visitor);
+ long Console_GetTotalLines(detail::ConsoleHead* console);
+
+ static long Console_SetViewBaseLine(detail::ConsoleHead* console, long line);
+ static u16 Console_GetViewHeight(detail::ConsoleHead* console);
+
+ static void Console_VPrintf(detail::ConsoleHead* console, const char* format, std::va_list vlist);
+
+ static long Console_ShowLatestLine(detail::ConsoleHead* console) {
+ long baseLine = Console_GetTotalLines(console) - Console_GetViewHeight(console);
+
+ if (baseLine < 0)
+ baseLine = 0;
+
+ Console_SetViewBaseLine(console, baseLine);
+
+ return baseLine;
+ }
+
+ static u16 Console_GetViewHeight(detail::ConsoleHead* console) {
+ NW4R_ASSERT_CHECK_NULL(434, console);
+ return console->viewLines;
+ }
+
+ static bool Console_SetVisible(detail::ConsoleHead* console, bool isVisible) {
+ NW4R_ASSERT_CHECK_NULL(497, console);
+
+ bool before = console->isVisible;
+ console->isVisible = isVisible;
+ return before;
+ }
+
+ static long Console_SetViewBaseLine(detail::ConsoleHead* console, long line) {
+ NW4R_ASSERT_CHECK_NULL(557, console);
+ long before = console->viewTopLine;
+ console->viewTopLine = line;
+ return before;
+ }
+
+ } // namespace db
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp b/src/revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp
new file mode 100644
index 0000000000..bd74822ea2
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/db_DbgPrintBase.cpp
@@ -0,0 +1,17 @@
+#include "DbgPrintBase.h"
+
+#include "../ut/Color.h"
+#include "../ut/CharWriter.h"
+
+// Unused file. Only here to force the function CharWriter::SetTextColor(ut::Color&)
+// into the right place.
+
+namespace nw4hbm {
+ namespace db {
+ // Dummy function
+ void dummy(ut::CharWriter* pCharWriter) {
+ ut::Color color;
+ pCharWriter->SetTextColor(color);
+ }
+ } // namespace db
+} // namespace nw4r
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/db_assert.cpp b/src/revolution/homebuttonLib/nw4hbm/db/db_assert.cpp
new file mode 100644
index 0000000000..c215a8efd9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/db_assert.cpp
@@ -0,0 +1,191 @@
+#include "assert.h"
+#include <revolution/os.h>
+#include <revolution/base/PPCArch.h>
+#include <revolution/vi.h>
+#include "console.h"
+#include "mapFile.h"
+#include "directPrint.h"
+
+#include "global.h"
+
+namespace nw4hbm {
+ namespace db {
+ using namespace detail;
+
+ static OSAlarm sWarningAlarm;
+ static u32 sWarningTime;
+ static ConsoleHead* sAssertionConsole;
+ static bool sDispWarningAuto;
+
+ static void Assertion_Printf_(const char* fmt, ...) {
+ va_list vlist;
+ va_start(vlist, fmt);
+
+ if (sAssertionConsole) {
+ Console_VFPrintf(CONSOLE_OUTPUT_ALL, sAssertionConsole, fmt, vlist);
+ } else {
+ OSVReport(fmt, vlist);
+ }
+
+ va_end(vlist);
+ }
+
+ static bool ShowMapInfoSubroutine_(u32 address, u8 preCRFlag) {
+ u8 strBuf[260];
+
+ if (!MapFile_Exists()) {
+ return false;
+ }
+
+ if (address < 0x80000000 || address > 0x82FFFFFF) {
+ return false;
+ }
+
+ if (MapFile_QuerySymbol(address, strBuf, sizeof(strBuf))) {
+ Assertion_Printf_("%s\n", strBuf);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ static void ShowStack_(u32 sp) NO_INLINE {
+ Assertion_Printf_("-------------------------------- TRACE\n");
+ Assertion_Printf_("Address: BackChain LR save\n");
+
+ u32* p = (u32*)sp;
+
+ for (u32 i = 0; i < 16; i++, p = (u32*)*p) {
+ if (p == NULL || p == (u32*)0xFFFFFFFF || !((u32)p & 0x80000000))
+ break;
+
+ Assertion_Printf_("%08X: %08X %08X ", p, p[0], p[1]);
+
+ if (!ShowMapInfoSubroutine_(p[1], false)) {
+ Assertion_Printf_("\n");
+ }
+ }
+ }
+
+ DECL_WEAK void VPanic(const char* file, int line, const char* fmt, std::va_list vlist) {
+ register u32 stackPointer;
+
+ asm {
+ lwz stackPointer, 0(r1)
+ }
+
+ OSDisableInterrupts();
+ VISetPreRetraceCallback(NULL);
+ VISetPostRetraceCallback(NULL);
+
+ if (sAssertionConsole) {
+ detail::DirectPrint_SetupFB(NULL);
+ }
+
+ ShowStack_(stackPointer);
+
+ if (sAssertionConsole != NULL) {
+ Console_Printf(sAssertionConsole, "%s:%d Panic:", file, line);
+ Console_VFPrintf(CONSOLE_OUTPUT_ALL, sAssertionConsole, fmt, vlist);
+ Console_Printf(sAssertionConsole, "\n");
+
+ Console_ShowLatestLine(sAssertionConsole);
+ Console_SetVisible(sAssertionConsole, true);
+ Console_DrawDirect(sAssertionConsole);
+ } else {
+ OSReport("%s:%d Panic:", file, line);
+ OSVReport(fmt, vlist);
+ OSReport("\n");
+ }
+
+ PPCHalt();
+ }
+
+ DECL_WEAK void detail::Panic(const char* file, int line, const char* fmt, ...) {
+ va_list vlist;
+ va_start(vlist, fmt);
+ VPanic(file, line, fmt, vlist);
+ }
+
+ static OSAlarm& GetWarningAlarm_();
+ static void WarningAlarmFunc_(OSAlarm* alarm, OSContext* context);
+
+ DECL_WEAK void VWarning(const char* file, int line, const char* fmt, std::va_list vlist) {
+ if (sAssertionConsole != NULL) {
+ Console_Printf(sAssertionConsole, "%s:%d Warning:", file, line);
+ Console_VFPrintf(CONSOLE_OUTPUT_ALL, sAssertionConsole, fmt, vlist);
+ Console_Printf(sAssertionConsole, "\n");
+ Console_ShowLatestLine(sAssertionConsole);
+ Console_SetVisible(sAssertionConsole, true);
+ } else {
+ OSReport("%s:%d Warning:", file, line);
+ OSVReport(fmt, vlist);
+ OSReport("\n");
+ }
+ }
+
+ DECL_WEAK void detail::Warning(const char* file, int line, const char* fmt, ...) {
+ OSAlarm& alarm = GetWarningAlarm_();
+
+ va_list vlist;
+ va_start(vlist, fmt);
+
+ VWarning(file, line, fmt, vlist);
+
+ va_end(vlist);
+
+ if (sWarningTime > 0) {
+ OSCancelAlarm(&sWarningAlarm);
+ OSSetAlarm(&sWarningAlarm, sWarningTime, WarningAlarmFunc_);
+ }
+ }
+
+ namespace detail {
+ void Log(const char* fmt, ...) {
+ va_list vlist;
+ }
+ } // namespace detail
+
+ ConsoleHead* Assertion_SetConsole(ConsoleHead* console) {
+ ConsoleHead* before = sAssertionConsole;
+ sAssertionConsole = console;
+ return before;
+ }
+
+ ConsoleHead* Assertion_GetConsole() {
+ return sAssertionConsole;
+ }
+
+ void Assertion_ShowConsole(u32 time) {
+ if (sAssertionConsole != NULL) {
+ OSAlarm& alarm = GetWarningAlarm_();
+ OSCancelAlarm(&alarm);
+ Console_SetVisible(sAssertionConsole, true);
+ if (time != 0) {
+ OSSetAlarm(&alarm, time, WarningAlarmFunc_);
+ }
+ }
+ }
+
+ void Assertion_HideConsole() {
+ // OSAlarm& alarm;
+ }
+
+ void Assertion_SetWarningTime(u32 time) {}
+
+ static OSAlarm& GetWarningAlarm_() {
+ static bool sInitializedAlarm;
+
+ if (!sInitializedAlarm) {
+ OSCreateAlarm(&sWarningAlarm);
+ sInitializedAlarm = true;
+ }
+
+ return sWarningAlarm;
+ }
+
+ static void WarningAlarmFunc_(OSAlarm* alarm, OSContext* context) {
+ Console_SetVisible(sAssertionConsole, false);
+ }
+ } // namespace db
+} // namespace nw4r
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/db_console.cpp b/src/revolution/homebuttonLib/nw4hbm/db/db_console.cpp
new file mode 100644
index 0000000000..b25daf1edb
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/db_console.cpp
@@ -0,0 +1,315 @@
+#include <revolution/os.h>
+#include "assert.h"
+#include "console.h"
+#include "directPrint.h"
+
+namespace nw4hbm {
+ namespace db {
+
+ static OSMutex sMutex;
+ static u8 sStrBuf[1024];
+
+ static inline u8* GetTextPtr_(ConsoleHandle console, u16 line, u16 xPos) {
+ return console->textBuf + xPos + (console->width + 1) * line;
+ }
+
+ static inline u32 CodeWidth_(u8 const* p) {
+ return *p >= 0x81 ? sizeof(wchar_t) : sizeof(char);
+ }
+
+ static inline u32 GetTabSize_(ConsoleHandle console) {
+ s32 tab = (console->attr & 0xC) >> 2;
+ return static_cast<u32>(2 << tab);
+ }
+
+ static inline u8 const* SearchEndOfLine_(u8 const* str) {
+ while (*str != '\n' && *str != '\0') {
+ str++;
+ }
+
+ return str;
+ }
+
+ static inline u16 GetRingUsedLines_(ConsoleHandle console) {
+ NW4HBM_ASSERT_CHECK_NULL(112, console);
+ {
+ s32 lines = console->printTop - console->ringTop;
+
+ if (lines < 0) {
+ lines += console->height;
+ }
+
+ return static_cast<u16>(lines);
+ }
+ }
+
+ static inline u16 GetActiveLines_(ConsoleHandle console) {
+ u16 lines = GetRingUsedLines_(console);
+
+ if (console->printTopUsed) {
+ lines++;
+ }
+
+ return lines;
+ }
+
+ static void TerminateLine_(ConsoleHandle console) {
+ *GetTextPtr_(console, console->printTop, console->printXPos) = '\0';
+ }
+
+ static u8* NextLine_(ConsoleHandle console) {
+ *GetTextPtr_(console, console->printTop, console->printXPos) = '\0';
+ console->printXPos = 0;
+ console->printTop++;
+ console->printTopUsed = 0;
+
+ if (console->printTop == console->height && !(console->attr & 2)) {
+ console->printTop = 0;
+ }
+
+ if (console->printTop == console->ringTop) {
+ console->ringTopLineCnt++;
+
+ if (++console->ringTop == console->height) {
+ console->ringTop = 0;
+ }
+ }
+
+ return GetTextPtr_(console, console->printTop, 0);
+ }
+
+ static u8* PutTab_(ConsoleHandle console, u8* dstPtr) {
+ u32 tabWidth = GetTabSize_(console);
+
+ do {
+ *dstPtr++ = ' ';
+ console->printXPos++;
+
+ if (console->printXPos >= console->width) {
+ break;
+ }
+ } while (console->printXPos & (tabWidth - 1));
+
+ return dstPtr;
+ }
+
+ static u32 PutChar_(ConsoleHandle console, u8 const* str, u8* dstPtr) {
+ u32 codeWidth = CodeWidth_(str);
+ u32 cnt;
+
+ if (console->printXPos + codeWidth <= console->width == 0) {
+ return false;
+ }
+
+ console->printXPos += codeWidth;
+
+ for (cnt = codeWidth; cnt; cnt--) {
+ *dstPtr++ = *str++;
+ }
+
+ return codeWidth;
+ }
+
+ static void UnlockMutex_(OSMutex* mutex) {
+ OSUnlockMutex(mutex);
+ }
+
+ static bool TryLockMutex_(OSMutex* mutex) {
+ OSLockMutex(mutex);
+ return true;
+ }
+
+ static void DoDrawString_(ConsoleHandle console, u32 printLine, u8 const* str,
+ ut::TextWriterBase<char>* writer) {
+ if (writer) {
+ writer->Printf("%s\n", str);
+ } else {
+ s32 height = (s32)((u32)console->viewPosY + printLine * 10);
+
+ DirectPrint_DrawString(console->viewPosX, height, false, "%s\n", str);
+ }
+ }
+
+ static void DoDrawConsole_(ConsoleHandle console, ut::TextWriterBase<char>* writer) {
+ s32 viewOffset;
+ u16 line;
+ u16 printLines;
+
+ viewOffset = console->viewTopLine - console->ringTopLineCnt;
+ printLines = 0;
+
+ if (viewOffset < 0) {
+ viewOffset = 0;
+ } else if (viewOffset > GetActiveLines_(console)) {
+ return;
+ }
+
+ line = static_cast<u16>(console->ringTop + viewOffset);
+
+ if (line >= console->height) {
+ line -= console->height;
+ }
+
+ while (true) {
+ if (line == console->printTop && console->printTopUsed == 0) {
+ break;
+ }
+
+ DoDrawString_(console, printLines, GetTextPtr_(console, line, 0), writer);
+
+ printLines++;
+
+ if (line == console->printTop) {
+ break;
+ }
+
+ line++;
+
+ if (line == console->height) {
+ if (console->attr & 2) {
+ return;
+ }
+
+ line = 0;
+ }
+
+ if (printLines >= console->viewLines) {
+ return;
+ }
+ }
+ }
+
+ void Console_DrawDirect(ConsoleHandle console) {
+ NW4HBM_ASSERT_CHECK_NULL(621, console);
+
+ if (DirectPrint_IsActive() && console->isVisible) {
+ TryLockMutex_(&sMutex);
+ int width = console->width * 6 + 12, height = console->viewLines * 10 + 4;
+
+ DirectPrint_EraseXfb(console->viewPosX - 6, console->viewPosY - 3, width, height);
+ DoDrawConsole_(console, NULL);
+ DirectPrint_StoreCache();
+ UnlockMutex_(&sMutex);
+ }
+ }
+
+ void dummyString(ConsoleHandle pConsole) {
+ NW4HBM_ASSERT_CHECK_NULL(0, pConsole);
+ OSReport("illegal console handle");
+ u8* buffer;
+ NW4HBM_ASSERT_CHECK_NULL(0, buffer);
+ OSReport("NW4HBM:Alignment Error(0x%x)\nbuffer must be aligned to 4 bytes boundary.");
+ }
+
+ static void PrintToBuffer_(ConsoleHandle console, u8 const* str) {
+ u8* storePtr;
+
+ NW4HBM_ASSERT_CHECK_NULL(747, console);
+ NW4HBM_ASSERT_CHECK_NULL(748, str);
+
+ storePtr = GetTextPtr_(console, console->printTop, console->printXPos);
+
+ while (*str) {
+ if (console->attr & 2 && console->printTop == console->height) {
+ break;
+ }
+
+ while (*str) // ? just use continue? am i missing something?
+ {
+ bool newLineFlag = false;
+
+ if (*str == '\n') {
+ str++;
+ storePtr = NextLine_(console);
+ break;
+ }
+
+ if (*str == '\t') {
+ str++;
+ storePtr = PutTab_(console, storePtr);
+ console->printTopUsed = 1;
+ } else {
+ u32 bytes = PutChar_(console, str, storePtr);
+
+ if (bytes) {
+ console->printTopUsed = 1;
+ str += bytes;
+ storePtr += bytes;
+ } else {
+ newLineFlag = true;
+ }
+ }
+
+ if (console->printXPos >= console->width) {
+ newLineFlag = true;
+ }
+
+ if (newLineFlag) {
+ if (console->attr & 1) {
+ str = SearchEndOfLine_(str);
+ break;
+ }
+
+ if (*str == '\n') {
+ str++;
+ }
+
+ storePtr = NextLine_(console);
+ break;
+ }
+ }
+ }
+ }
+
+ static void Console_PrintString_(ConsoleOutputType type, ConsoleHandle console,
+ u8 const* str) {
+ NW4HBM_ASSERT_CHECK_NULL(843, console);
+
+ if (type & CONSOLE_OUTPUT_DISPLAY) {
+ OSReport("%s", str);
+ }
+
+ if (type & CONSOLE_OUTPUT_TERMINAL) {
+ PrintToBuffer_(console, str);
+ }
+ }
+
+ void Console_VFPrintf(ConsoleOutputType type, ConsoleHandle console, char const* format,
+ std::va_list vlist) {
+ NW4HBM_ASSERT_CHECK_NULL(872, console);
+
+ if (TryLockMutex_(&sMutex)) {
+ std::vsnprintf(reinterpret_cast<char*>(sStrBuf), sizeof(sStrBuf), format, vlist);
+
+ Console_PrintString_(type, console, sStrBuf);
+
+ UnlockMutex_(&sMutex);
+ }
+ }
+
+ void Console_Printf(ConsoleHandle console, char const* format, ...) {
+ std::va_list vlist;
+ va_start(vlist, format);
+ Console_VFPrintf(CONSOLE_OUTPUT_ALL, console, format, vlist);
+ va_end(vlist);
+ }
+
+ s32 Console_GetTotalLines(ConsoleHandle console) {
+ s32 count;
+
+ // this is not part of this function but it's required to generate the dtor
+ // (`nw4hbm::ut::Color::~Color()`) it was probably part of a function that got stripped
+ // by the linker
+ ::nw4hbm::ut::Color unused;
+
+ NW4HBM_ASSERT_CHECK_NULL(1050, console);
+
+ TryLockMutex_(&sMutex);
+ count = GetActiveLines_(console) + console->ringTopLineCnt;
+ UnlockMutex_(&sMutex);
+
+ return count;
+ }
+
+ } // namespace db
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp b/src/revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp
new file mode 100644
index 0000000000..92d5ce2aa5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/db_directPrint.cpp
@@ -0,0 +1,464 @@
+#include <revolution/os.h>
+#include <revolution/vi.h>
+#include "assert.h"
+#include "directPrint.h"
+
+#include <string.h>
+
+#define ROUND_UP(x, align) (((x) + (align)-1) & (-(align)))
+#define ROUND_DOWN(x, align) ((x) & (~((align) - 1)))
+
+typedef struct FrameBufferInfo {
+ /* 0x00 */ u8* frameMemory;
+ /* 0x04 */ u32 frameSize;
+ /* 0x08 */ u16 frameWidth;
+ /* 0x0A */ u16 frameHeight;
+ /* 0x0C */ u16 frameRow;
+ /* 0x0E */ u16 reserved;
+} FrameBufferInfo;
+
+typedef struct YUVColorInfo {
+ /* 0x00 */ GXColor colorRGBA;
+ /* 0x04 */ u16 colorY256;
+ /* 0x06 */ u16 colorU;
+ /* 0x08 */ u16 colorU2;
+ /* 0x0A */ u16 colorU4;
+ /* 0x0C */ u16 colorV;
+ /* 0x0E */ u16 colorV2;
+ /* 0x10 */ u16 colorV4;
+ /* 0x12 */ u16 reserved;
+} YUVColorInfo;
+
+namespace nw4hbm {
+ namespace db {
+
+ static void DrawStringToXfb_(int posh, int posv, char const* str, bool turnOver,
+ bool backErase);
+ static char const* DrawStringLineToXfb_(int posh, int posv, char const* str, int width);
+ static void DrawCharToXfb_(int posh, int posv, int code);
+
+ namespace detail {
+ static void WaitVIRetrace_();
+ static void* CreateFB_(GXRenderModeObj const* rmode);
+ } // namespace detail
+
+ static inline int StrLineWidth_(char const* str) {
+ int len = 0;
+ char c;
+
+ NW4HBM_ASSERT_CHECK_NULL(306, str);
+
+ while (true) {
+ c = *str++;
+
+ if (c == '\0' || c == '\n') {
+ return len;
+ }
+
+ if (c == '\t') {
+ len = (len + 4) & -4;
+
+ continue;
+ }
+
+ len++;
+ }
+
+ return len;
+ }
+
+ static const u8 sAsciiTable[128] = {
+ 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0xFD, 0xFE, 0x7A, 0x7A,
+ 0x7A, 0x7A, 0x7A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, 0x64, 0x65, 0x66, 0x2B, 0x67,
+ 0x68, 0x25, 0x26, 0x69, 0x2A, 0x6A, 0x27, 0x2C, 0x6B, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x24, 0x6C, 0x6D, 0x6E, 0x6F, 0x28, 0x70,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0xFF, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90,
+ 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x76, 0x77, 0x78, 0x79, 0x7A,
+ };
+
+ static const u32 sFontData[64] = {
+ 0x70871C30, 0x8988A250, 0x88808290, 0x88830C90, 0x888402F8, 0x88882210, 0x71CF9C10,
+ 0xF9CF9C70, 0x8208A288, 0xF200A288, 0x0BC11C78, 0x0A222208, 0x8A222208, 0x71C21C70,
+ 0x23C738F8, 0x5228A480, 0x8A282280, 0x8BC822F0, 0xFA282280, 0x8A28A480, 0x8BC738F8,
+ 0xF9C89C08, 0x82288808, 0x82088808, 0xF2EF8808, 0x82288888, 0x82288888, 0x81C89C70,
+ 0x8A08A270, 0x920DA288, 0xA20AB288, 0xC20AAA88, 0xA208A688, 0x9208A288, 0x8BE8A270,
+ 0xF1CF1CF8, 0x8A28A220, 0x8A28A020, 0xF22F1C20, 0x82AA0220, 0x82492220, 0x81A89C20,
+ 0x8A28A288, 0x8A28A288, 0x8A289488, 0x8A2A8850, 0x894A9420, 0x894AA220, 0x70852220,
+ 0xF8011000, 0x08020800, 0x10840400, 0x20040470, 0x40840400, 0x80020800, 0xF8011000,
+ 0x70800000, 0x88822200, 0x08820400, 0x108F8800, 0x20821000, 0x00022200, 0x20800020,
+ 0x00000000,
+ };
+
+ static const u32 sFontData2[77] = {
+ 0x51421820, 0x53E7A420, 0x014A2C40, 0x01471000, 0x0142AA00, 0x03EAA400, 0x01471A78,
+ 0x00000000, 0x50008010, 0x20010820, 0xF8020040, 0x20420820, 0x50441010, 0x00880000,
+ 0x00070E00, 0x01088840, 0x78898820, 0x004A8810, 0x788A8810, 0x01098808, 0x00040E04,
+ 0x70800620, 0x11400820, 0x12200820, 0x10001020, 0x10000820, 0x100F8820, 0x70000620,
+ 0x60070000, 0x110F82A0, 0x12AA8AE0, 0x084F92A0, 0x100FBE1C, 0x10089008, 0x60070808,
+ 0x00000000, 0x02000200, 0x7A078270, 0x8BC81E88, 0x8A2822F8, 0x9A282280, 0x6BC79E78,
+ 0x30000000, 0x48080810, 0x41E80000, 0x422F1830, 0xFBE88810, 0x40288890, 0x43C89C60,
+ 0x81000000, 0x81000000, 0x990F3C70, 0xA10AA288, 0xE10AA288, 0xA10AA288, 0x98CAA270,
+ 0x00000000, 0x00000020, 0xF1EF1E20, 0x8A28A0F8, 0x8A281C20, 0xF1E80220, 0x80283C38,
+ 0x00000000, 0x00000000, 0x8A28B688, 0x8A2A8888, 0x8A2A8878, 0x894A8808, 0x788536F0,
+ 0x00000000, 0x00000000, 0xF8000000, 0x10000000, 0x20000000, 0x40000000, 0xF8000000,
+ };
+
+ static FrameBufferInfo sFrameBufferInfo;
+ static YUVColorInfo sFrameBufferColor;
+ static int sInitialized = false;
+
+ static inline int GetDotWidth_() {
+ return sFrameBufferInfo.frameWidth < 400 ? 1 : 2;
+ }
+ static inline int GetDotHeight_() {
+ return sFrameBufferInfo.frameHeight < 300 ? 1 : 2;
+ }
+
+ void DirectPrint_Init() {
+ if (!sInitialized) {
+ DirectPrint_ChangeXfb(NULL, 640, 480);
+ DirectPrint_SetColor(0xff, 0xff, 0xff);
+
+ sInitialized = true;
+ }
+ }
+
+ bool DirectPrint_IsActive() {
+ return sInitialized && sFrameBufferInfo.frameMemory;
+ }
+
+ void DirectPrint_EraseXfb(int posh, int posv, int sizeh, int sizev) {
+ int posEndH, posEndV;
+
+ if (sFrameBufferInfo.frameMemory == NULL) {
+ return;
+ }
+
+ if (GetDotWidth_() == 2) {
+ posh *= 2;
+ sizeh *= 2;
+ }
+
+ posEndH = posh + sizeh;
+ posh = posh >= 0 ? posh : 0;
+
+ posEndH = posEndH <= sFrameBufferInfo.frameWidth ? posEndH : sFrameBufferInfo.frameWidth;
+ sizeh = posEndH - posh;
+
+ if (GetDotHeight_() == 2) {
+ posv *= 2;
+ sizev *= 2;
+ }
+
+ posEndV = posv + sizev;
+ posv = posv >= 0 ? posv : 0;
+
+ posEndV = posEndV <= sFrameBufferInfo.frameHeight ? posEndV : sFrameBufferInfo.frameHeight;
+ sizev = posEndV - posv;
+
+ u16* pixel = reinterpret_cast<u16*>(sFrameBufferInfo.frameMemory) +
+ sFrameBufferInfo.frameRow * posv + posh;
+
+ for (int cntv = 0; cntv < sizev; cntv++) {
+ for (int cnth = 0; cnth < sizeh; cnth++) {
+ *pixel++ = 0x1080; // some sort of white or black?
+ }
+
+ pixel += sFrameBufferInfo.frameRow - sizeh;
+ }
+ }
+
+ void DirectPrint_ChangeXfb(void* framebuf, u16 width, u16 height) {
+ sFrameBufferInfo.frameMemory = static_cast<u8*>(framebuf);
+ sFrameBufferInfo.frameWidth = width;
+ sFrameBufferInfo.frameHeight = height;
+
+ sFrameBufferInfo.frameRow = ROUND_UP(static_cast<u16>(width), 16);
+ sFrameBufferInfo.frameSize =
+ sFrameBufferInfo.frameRow * sFrameBufferInfo.frameHeight * 2;
+ }
+
+ void DirectPrint_ChangeXfb(void* framebuf) {
+ sFrameBufferInfo.frameMemory = static_cast<u8*>(framebuf);
+ }
+
+ void DirectPrint_StoreCache(void) {
+ DCStoreRange(sFrameBufferInfo.frameMemory, sFrameBufferInfo.frameSize);
+ }
+
+ void DirectPrint_DrawString(int posh, int posv, bool turnOver, char const* format, ...) {
+ if (sFrameBufferInfo.frameMemory) {
+ std::va_list vargs;
+
+ va_start(vargs, format);
+ detail::DirectPrint_DrawStringToXfb(posh, posv, format, vargs, turnOver, false);
+ va_end(vargs);
+ }
+ }
+
+ // Intel IPP RGBToYCbCr algorithm, same as OSFatal.c::RGB2YUV
+ void DirectPrint_SetColor(u8 r, u8 g, u8 b) {
+ int y = (int)(0.257f * (int)r + 0.504f * (int)g + 0.098f * (int)b + 16.0f);
+ int u = (int)(-0.148f * (int)r - 0.291f * (int)g + 0.439f * (int)b + 128.0f);
+ int v = (int)(0.439f * (int)r - 0.368f * (int)g - 0.071f * (int)b + 128.0f);
+
+ sFrameBufferColor.colorRGBA.r = r;
+ sFrameBufferColor.colorRGBA.g = g;
+ sFrameBufferColor.colorRGBA.b = b;
+ sFrameBufferColor.colorRGBA.a = 0xff;
+
+ sFrameBufferColor.colorY256 = static_cast<u16>(y << 8);
+
+ sFrameBufferColor.colorU = static_cast<u16>(u);
+ sFrameBufferColor.colorU2 = static_cast<u16>(u / 2);
+ sFrameBufferColor.colorU4 = static_cast<u16>(u / 4);
+
+ sFrameBufferColor.colorV = static_cast<u16>(v);
+ sFrameBufferColor.colorV2 = static_cast<u16>(v / 2);
+ sFrameBufferColor.colorV4 = static_cast<u16>(v / 4);
+ }
+
+ void detail::DirectPrint_DrawStringToXfb(int posh, int posv, char const* format,
+ std::va_list vargs, bool turnOver,
+ bool backErase) {
+ char string[256];
+
+ NW4HBM_ASSERT(647, sFrameBufferInfo.frameMemory != NULL);
+
+ int length = std::vsnprintf(string, sizeof(string), format, vargs);
+ int posLeftStart = posh;
+
+ if (length > 0) {
+ DrawStringToXfb_(posh, posv, string, turnOver, backErase);
+ }
+ }
+
+ static void DrawStringToXfb_(int posh, int posv, char const* str, bool turnOver,
+ bool backErase) {
+ int basePosH = posh;
+ int width;
+ int frameWidth = sFrameBufferInfo.frameWidth / GetDotWidth_();
+
+ while (*str != '\0') {
+ int len;
+
+ if (backErase) {
+ len = StrLineWidth_(str);
+
+ DirectPrint_EraseXfb(posh - 6, posv - 3, (len + 2) * 6, 13);
+ }
+
+ width = (frameWidth - posh) / 6;
+ str = DrawStringLineToXfb_(posh, posv, str, width);
+ posv += 10;
+
+ if (*str == '\n') {
+ str++;
+ posh = basePosH;
+ continue;
+ }
+
+ if (*str == '\0') {
+ continue;
+ }
+
+ str++;
+
+ if (!turnOver) {
+ str = strchr(str, '\n');
+
+ if (str) {
+ str++;
+ posh = basePosH;
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ posh = 0;
+ }
+ }
+
+ static char const* DrawStringLineToXfb_(int posh, int posv, char const* str, int width) {
+ char c;
+ int code;
+ int cnt = 0;
+
+ NW4HBM_ASSERT_CHECK_NULL(745, str);
+ NW4HBM_ASSERT(746, width > 0);
+
+ for (; (c = *str) != '\0'; str++) {
+ if (c == '\n' || c == '\0') { // another check against null character?
+ return str;
+ }
+
+ code = sAsciiTable[c % sizeof(sAsciiTable)];
+
+ if (code == 0xfd) {
+ int tab_size = 4 - (cnt & 3);
+
+ posh += tab_size * 6;
+ cnt += tab_size;
+ } else {
+ if (code != 0xff) {
+ DrawCharToXfb_(posh, posv, code);
+ }
+
+ posh += 6;
+ cnt++;
+ }
+
+ if (cnt >= width) {
+ if (str[1] == '\n') {
+ str++;
+ }
+
+ return str;
+ }
+ }
+
+ return str;
+ }
+
+ static void DrawCharToXfb_(int posh, int posv, int code) {
+ static u32 twiceBit[4] = {0, 3, 12, 15};
+
+ int ncode = code >= 100 ? code - 100 : code;
+ int fonth = ncode % 5 * 6;
+ int fontv = ncode / 5 * 7;
+ const u32* fontLine = code < 100 ? &sFontData[fontv] : &sFontData2[fontv];
+
+ int wH = GetDotWidth_();
+ int wV = GetDotHeight_();
+
+ u16* pixel = reinterpret_cast<u16*>(sFrameBufferInfo.frameMemory) +
+ sFrameBufferInfo.frameRow * posv * wV + posh * wH;
+
+ if (posv < 0 || posh < 0) {
+ return;
+ }
+
+ if (sFrameBufferInfo.frameWidth <= wH * (posh + 6) ||
+ sFrameBufferInfo.frameHeight <= wV * (posv + 7))
+ {
+ return;
+ }
+
+ for (int cntv = 0; cntv < 7; cntv++) {
+ u32 fontBits = *fontLine++ << fonth;
+
+ if (wH == 1) {
+ fontBits = (fontBits & 0xfc000000) >> 1;
+ } else {
+ fontBits =
+ (twiceBit[(fontBits >> 26) & 3] | twiceBit[(fontBits >> 28) & 3] << 4 |
+ twiceBit[(fontBits >> 30) & 3] << 8)
+ << 19;
+ }
+
+ for (int cnth = 0; cnth < wH * 6;) {
+ u16 pixColor = (fontBits & (1u << 30) ? sFrameBufferColor.colorY256 : 0x00) |
+ ((fontBits & (1u << 31) ? sFrameBufferColor.colorU4 : 0x20) +
+ (fontBits & (1u << 30) ? sFrameBufferColor.colorU2 : 0x40) +
+ (fontBits & (1u << 29) ? sFrameBufferColor.colorU4 : 0x20));
+
+ *pixel = pixColor;
+
+ if (wV > 1) {
+ pixel[sFrameBufferInfo.frameRow] = pixColor;
+ }
+
+ pixel++;
+
+ pixColor = (fontBits & (1u << 29) ? sFrameBufferColor.colorY256 : 0x00) |
+ ((fontBits & (1u << 30) ? sFrameBufferColor.colorV4 : 0x20) +
+ (fontBits & (1u << 29) ? sFrameBufferColor.colorV2 : 0x40) +
+ (fontBits & (1u << 28) ? sFrameBufferColor.colorV4 : 0x20));
+
+ *pixel = pixColor;
+
+ if (wV > 1) {
+ pixel[sFrameBufferInfo.frameRow] = pixColor;
+ }
+
+ pixel++;
+
+ fontBits <<= 2;
+ cnth += 2;
+ }
+
+ pixel += sFrameBufferInfo.frameRow * wV - 6 * wH;
+ }
+ }
+
+ static void detail::WaitVIRetrace_(void) {
+ int intrStatus = OSEnableInterrupts(); /* int enabled; */
+ u32 preCnt = VIGetRetraceCount();
+
+ while (preCnt == VIGetRetraceCount()) { /* ... */
+ }
+
+ OSRestoreInterrupts(intrStatus);
+ }
+
+ static void* detail::CreateFB_(GXRenderModeObj const* rmode) {
+ u32 arenaHi = (u32)OSGetArenaHi();
+ u32 memSize = (u16)ROUND_UP((u16)rmode->fbWidth, 16) * rmode->xfbHeight * 2;
+ u32 frameBuf = ROUND_DOWN(arenaHi - memSize, 32);
+
+ VIConfigure(rmode);
+ VISetNextFrameBuffer(reinterpret_cast<void*>(frameBuf));
+
+ return reinterpret_cast<void*>(frameBuf);
+ }
+
+ void* detail::DirectPrint_SetupFB(GXRenderModeObj const* rmode) {
+ void* frameMemory;
+
+ DirectPrint_Init();
+
+ frameMemory = VIGetCurrentFrameBuffer();
+ if (!frameMemory) {
+ if (!rmode) {
+ switch (VIGetTvFormat()) {
+ case VI_TVMODE_NTSC_INT:
+ rmode = &GXNtsc480IntDf;
+ break;
+
+ case VI_TVMODE_NTSC_DS:
+ rmode = &GXPal528IntDf;
+ break;
+
+ case VI_TVMODE_PAL_DS:
+ rmode = &GXEurgb60Hz480IntDf;
+ break;
+
+ case VI_TVMODE_NTSC_PROG:
+ rmode = &GXMpal480IntDf;
+ break;
+ }
+ }
+
+ frameMemory = CreateFB_(rmode);
+ }
+
+ VISetBlack(false);
+ VIFlush();
+ WaitVIRetrace_();
+
+ if (rmode) {
+ DirectPrint_ChangeXfb(frameMemory, rmode->fbWidth, rmode->xfbHeight);
+ } else {
+ DirectPrint_ChangeXfb(frameMemory);
+ }
+
+ return frameMemory;
+ }
+
+ } // namespace db
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp b/src/revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp
new file mode 100644
index 0000000000..adc72b7c4a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/db_mapFile.cpp
@@ -0,0 +1,367 @@
+#include <revolution/dvd.h>
+#include <revolution/os.h>
+#include "assert.h"
+#include "mapFile.h"
+
+#include "global.h"
+
+typedef u8 GetCharFunc(u8 const* buf);
+
+namespace nw4hbm {
+ namespace db {
+ static u8 GetCharOnMem_(const u8* buf);
+ static u8 GetCharOnDvd_(u8 const* buf);
+
+ static u8* SearchNextLine_(u8* buf, s32 lines);
+ static u8* SearchNextSection_(u8* buf);
+ static u8* SearchParam_(u8* lineTop, u32 argNum, u8 splitter);
+
+ static u32 XStrToU32_(u8 const* str);
+ static u32 CopySymbol_(u8 const* buf, u8* str, u32 strLenMax, u8 splitter);
+
+ static bool QuerySymbolToMapFile_(u8* buf, OSModuleInfo const* moduleInfo, u32 address,
+ u8* strBuf, u32 strBufSize);
+ static bool QuerySymbolToSingleMapFile_(MapFile* pMapFile, u32 address, u8* strBuf,
+ u32 strBufSize) NO_INLINE;
+ } // namespace db
+} // namespace nw4hbm
+
+namespace nw4hbm {
+ namespace db {
+ static u8 sMapBuf[0x200];
+ static s32 sMapBufOffset = -1;
+ static DVDFileInfo sFileInfo;
+ static u32 sFileLength;
+ static MapFile* sMapFileList;
+ static GetCharFunc* GetCharPtr_;
+ } // namespace db
+} // namespace nw4hbm
+
+namespace nw4hbm {
+ namespace db {
+ bool MapFile_Exists(void) {
+ return sMapFileList ? true : false;
+ }
+
+ static u8 GetCharOnMem_(u8 const* buf) {
+ return *buf;
+ }
+
+ static s32 GetSize(s32 offset, u32 length) {
+ if (offset + ARRAY_SIZE(sMapBuf) >= length) {
+ return OSRoundUp32B(length - offset);
+ }
+
+ return ARRAY_SIZE(sMapBuf);
+ }
+
+ static u8 GetCharOnDvd_(u8 const* buf) {
+ s32 address = (u32)buf & ~0x80000000;
+ s32 offset = address - sMapBufOffset;
+
+ if (address >= sFileLength) {
+ return 0;
+ }
+
+ if (sMapBufOffset < 0 || offset < 0 || ARRAY_SIZE(sMapBuf) <= offset) {
+ s32 len;
+ s32 size;
+
+ sMapBufOffset = OSRoundDown32B(address);
+ offset = address - sMapBufOffset;
+ size = GetSize(sMapBufOffset, sFileLength);
+
+ BOOL enabled = OSEnableInterrupts();
+
+ len = DVDReadAsyncPrio(&sFileInfo, sMapBuf, size, sMapBufOffset, NULL, 2);
+
+ while (DVDGetCommandBlockStatus(&sFileInfo.cb)) {}
+
+ OSRestoreInterrupts(enabled);
+
+ if (len <= 0) {
+ return 0;
+ }
+ }
+
+ return sMapBuf[offset];
+ }
+
+ void dummyString() {
+ u8* buffer;
+ NW4HBM_ASSERT_CHECK_NULL(0, buffer);
+
+ u8* mapDataBuf;
+ NW4HBM_ASSERT_CHECK_NULL(0, mapDataBuf);
+
+ MapFile* pMapFile;
+ NW4HBM_ASSERT_CHECK_NULL(0, pMapFile);
+
+ NW4HBM_ASSERT(0, sMapFileList->moduleInfo != NULL);
+
+ u8* filePath;
+ NW4HBM_ASSERT_CHECK_NULL(0, filePath);
+
+ NW4HBM_ASSERT(0, pMapFile->fileEntry >= 0);
+ }
+
+ static u8* SearchNextLine_(u8* buf, s32 lines) {
+ u8 c;
+
+ NW4HBM_ASSERT_CHECK_NULL(361, GetCharPtr_);
+
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ for (; (c = (*GetCharPtr_)(buf)) != '\0'; buf++) {
+ if (c == '\n') {
+ if (--lines <= 0) {
+ return buf + 1;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ static u8* SearchNextSection_(u8* buf) {
+ NW4HBM_ASSERT_CHECK_NULL(397, GetCharPtr_);
+
+ do {
+ buf = SearchNextLine_(buf, 1);
+
+ if (!buf) {
+ return NULL;
+ }
+ } while ((*GetCharPtr_)(buf) != '.');
+
+ return buf;
+ }
+
+ static u8* SearchParam_(u8* lineTop, u32 argNum, u8 splitter) {
+ int inArg = 0;
+ u8* buf = lineTop;
+
+ NW4HBM_ASSERT_CHECK_NULL(432, GetCharPtr_);
+
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ while (true) {
+ u8 c = (*GetCharPtr_)(buf);
+
+ if (c == '\0' || c == '\n') {
+ return 0;
+ }
+
+ if (inArg) {
+ if (c == splitter) {
+ inArg = 0;
+ }
+ } else if (c != splitter) {
+ if (!argNum--) {
+ return buf;
+ }
+
+ inArg = 1;
+ }
+
+ buf++;
+ }
+
+ return 0;
+ }
+
+ static u32 XStrToU32_(u8 const* str) {
+ u32 val = 0;
+
+ NW4HBM_ASSERT_CHECK_NULL(486, str);
+ NW4HBM_ASSERT_CHECK_NULL(487, GetCharPtr_);
+
+ while (true) {
+ u32 num;
+ u8 c;
+
+ c = (*GetCharPtr_)(str);
+
+ if ('0' <= c && c <= '9') {
+ num = static_cast<u32>(c - '0');
+ } else if ('a' <= c && c <= 'z') {
+ num = static_cast<u32>(c - ('a' - 10)); // ?
+ } else if ('A' <= c && c <= 'Z') {
+ num = static_cast<u32>(c - ('A' - 10)); // What's the - 10 for
+ } else {
+ return val;
+ }
+
+ if (val >= 0x10000000) {
+ return 0;
+ }
+
+ val = num + (val << 4);
+ str++;
+ }
+
+ return 0;
+ }
+
+ static u32 CopySymbol_(const u8* buf, u8* str, u32 strLenMax, u8 splitter) {
+ u32 cnt = 0;
+
+ NW4HBM_ASSERT_CHECK_NULL(544, buf);
+ NW4HBM_ASSERT_CHECK_NULL(545, str);
+ NW4HBM_ASSERT_CHECK_NULL(546, GetCharPtr_);
+
+ while (true) {
+ u8 c = (*GetCharPtr_)(buf++);
+
+ if (c == splitter || c == '\0' || c == '\n') {
+ *str = '\0';
+ return cnt;
+ }
+
+ *str = c;
+ str++;
+ cnt++;
+
+ if (cnt >= strLenMax - 1) {
+ *str = '\0';
+ return cnt;
+ }
+ }
+
+ return 0;
+ }
+
+ static bool QuerySymbolToMapFile_(u8* buf, OSModuleInfo const* moduleInfo, u32 address,
+ u8* strBuf, u32 strBufSize) {
+ OSSectionInfo* sectionInfo = NULL;
+ u32 sectionCnt;
+
+ NW4HBM_ASSERT_CHECK_NULL(602, strBuf);
+ NW4HBM_ASSERT(603, strBufSize > 0);
+
+ if (moduleInfo) {
+ sectionInfo = reinterpret_cast<OSSectionInfo*>(moduleInfo->sectionInfoOffset);
+ sectionCnt = moduleInfo->numSections;
+ }
+
+ do {
+ u32 offset = 0;
+
+ buf = SearchNextSection_(buf);
+ buf = SearchNextLine_(buf, 3);
+
+ if (sectionInfo) {
+ offset = sectionInfo->offset;
+
+ if (address < offset) {
+ goto get_next_section_info;
+ }
+
+ if (address >= offset + sectionInfo->size) {
+ goto get_next_section_info;
+ }
+ }
+
+ while (true) {
+ u8* param;
+ u32 startAddr;
+ u32 size;
+
+ buf = SearchNextLine_(buf, 1);
+ if (!buf) {
+ return false;
+ }
+
+ param = SearchParam_(buf, 1, ' ');
+ if (!param) {
+ break;
+ }
+
+ size = XStrToU32_(param);
+ param = SearchParam_(buf, 2, ' ');
+ if (!param) {
+ break;
+ }
+
+ startAddr = XStrToU32_(param);
+ if (!startAddr) {
+ continue;
+ }
+
+ startAddr = startAddr + offset;
+ if (address < startAddr || startAddr + size <= address) {
+ continue;
+ }
+
+ param = SearchParam_(buf, 5, ' ');
+ if (!param) {
+ *strBuf = '\0';
+ return true;
+ }
+
+ if ((*GetCharPtr_)(param) == '.') {
+ continue;
+ }
+
+ CopySymbol_(param, strBuf, strBufSize, ' ');
+ return true;
+ }
+
+ get_next_section_info:
+ if (sectionInfo) {
+ if (!--sectionCnt) {
+ return false;
+ }
+
+ sectionInfo++;
+ }
+ } while (true);
+
+ return false;
+ }
+
+ static bool QuerySymbolToSingleMapFile_(MapFile* pMapFile, u32 address, u8* strBuf,
+ u32 strBufSize) {
+ NW4HBM_ASSERT_CHECK_NULL(723, pMapFile);
+ NW4HBM_ASSERT_CHECK_NULL(724, strBuf);
+
+ if (pMapFile->mapBuf) {
+ GetCharPtr_ = &GetCharOnMem_;
+ return QuerySymbolToMapFile_(pMapFile->mapBuf, pMapFile->moduleInfo, address,
+ strBuf, strBufSize);
+ }
+
+ if (pMapFile->fileEntry >= 0) {
+ bool ret;
+
+ if (DVDFastOpen(pMapFile->fileEntry, &sFileInfo)) {
+ sFileLength = sFileInfo.length;
+ GetCharPtr_ = &GetCharOnDvd_;
+ ret = QuerySymbolToMapFile_((u8*)(0x80000000), pMapFile->moduleInfo, address, strBuf, strBufSize);
+
+ DVDClose(&sFileInfo);
+ return ret;
+ }
+ }
+
+ *strBuf = '\0';
+ return false;
+ }
+
+ bool MapFile_QuerySymbol(u32 address, u8* strBuf, u32 strBufSize) {
+ MapFile* pMap;
+ for (pMap = sMapFileList; pMap; pMap = pMap->next) {
+ if (QuerySymbolToSingleMapFile_(pMap, address, strBuf, strBufSize)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ } // namespace db
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/directPrint.h b/src/revolution/homebuttonLib/nw4hbm/db/directPrint.h
new file mode 100644
index 0000000000..5df8b8af3c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/directPrint.h
@@ -0,0 +1,23 @@
+#ifndef NW4R_DB_DIRECTPRINT_H
+#define NW4R_DB_DIRECTPRINT_H
+
+#include <revolution/gx.h>
+
+namespace nw4hbm {
+ namespace db {
+ void DirectPrint_Init();
+ bool DirectPrint_IsActive();
+ void DirectPrint_EraseXfb(int posh, int posv, int sizeh, int sizev);
+ void DirectPrint_ChangeXfb(void* framebuf, u16 width, u16 height);
+ void DirectPrint_StoreCache();
+ void DirectPrint_DrawString(int posh, int posv, bool turnOver, const char* format, ...);
+ void DirectPrint_SetColor(u8 r, u8 g, u8 b);
+
+ namespace detail {
+ void DirectPrint_DrawStringToXfb(int posh, int posv, const char* format, va_list vargs, bool turnOver, bool backErase);
+ void* DirectPrint_SetupFB(const GXRenderModeObj* rmode);
+ }
+ } // namespace db
+} // namespace nw4r
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/db/mapFile.h b/src/revolution/homebuttonLib/nw4hbm/db/mapFile.h
new file mode 100644
index 0000000000..15f7059d0b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/db/mapFile.h
@@ -0,0 +1,20 @@
+#ifndef NW4R_DB_MAPFILE_H
+#define NW4R_DB_MAPFILE_H
+
+#include <revolution/os.h>
+
+namespace nw4hbm {
+ namespace db {
+ struct MapFile {
+ /* 0x00 */ u8* mapBuf;
+ /* 0x04 */ OSModuleInfo* moduleInfo;
+ /* 0x08 */ s32 fileEntry;
+ /* 0x0C */ MapFile* next;
+ }; // size = 0x10
+
+ bool MapFile_Exists();
+ bool MapFile_QuerySymbol(u32 address, u8* strBuf, u32 strBufSize);
+ } // namespace db
+} // namespace nw4r
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/animation.h b/src/revolution/homebuttonLib/nw4hbm/lyt/animation.h
new file mode 100644
index 0000000000..bb58853ad1
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/animation.h
@@ -0,0 +1,209 @@
+#ifndef NW4HBM_LYT_ANIMATION_H
+#define NW4HBM_LYT_ANIMATION_H
+
+#include "lyt_types.h"
+#include "resourceAccessor.h"
+#include "resources.h"
+
+
+#define TexMtxMax 10
+#define IndTexMtxMax 3
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class Pane;
+ class Material;
+
+ enum {
+ /* 0 */ ANIMTARGET_PANE_TRANSX = 0,
+ /* 1 */ ANIMTARGET_PANE_TRANSY,
+ /* 2 */ ANIMTARGET_PANE_TRANSZ,
+
+ /* 3 */ ANIMTARGET_PANE_ROTX,
+ /* 4 */ ANIMTARGET_PANE_ROTY,
+ /* 5 */ ANIMTARGET_PANE_ROTZ,
+
+ /* 6 */ ANIMTARGET_PANE_SCALEX,
+ /* 7 */ ANIMTARGET_PANE_SCALEY,
+
+ /* 8 */ ANIMTARGET_PANE_SIZEX,
+ /* 9 */ ANIMTARGET_PANE_SIZEY,
+
+ /* 10 */ ANIMTARGET_PANE_MAX,
+ /* 16 */ ANIMTARGET_PANE_COLOR_ALPHA = 16,
+ /* 17 */ ANIMTARGET_PANE_COLOR_MAX,
+ };
+
+ enum {
+ /* 0 */ ANIMTARGET_VERTEXCOLOR_LT_RED = 0,
+ /* 1 */ ANIMTARGET_VERTEXCOLOR_LT_GREEN,
+ /* 2 */ ANIMTARGET_VERTEXCOLOR_LT_BLUE,
+ /* 3 */ ANIMTARGET_VERTEXCOLOR_LT_ALPHA,
+
+ /* 4 */ ANIMTARGET_VERTEXCOLOR_RT_RED,
+ /* 5 */ ANIMTARGET_VERTEXCOLOR_RT_GREEN,
+ /* 6 */ ANIMTARGET_VERTEXCOLOR_RT_BLUE,
+ /* 7 */ ANIMTARGET_VERTEXCOLOR_RT_ALPHA,
+
+ /* 8 */ ANIMTARGET_VERTEXCOLOR_LB_RED,
+ /* 9 */ ANIMTARGET_VERTEXCOLOR_LB_GREEN,
+ /* 10 */ ANIMTARGET_VERTEXCOLOR_LB_BLUE,
+ /* 11 */ ANIMTARGET_VERTEXCOLOR_LB_ALPHA,
+
+ /* 12 */ ANIMTARGET_VERTEXCOLOR_RB_RED,
+ /* 13 */ ANIMTARGET_VERTEXCOLOR_RB_GREEN,
+ /* 14 */ ANIMTARGET_VERTEXCOLOR_RB_BLUE,
+ /* 15 */ ANIMTARGET_VERTEXCOLOR_RB_ALPHA,
+
+ /* 16 */ ANIMTARGET_VERTEXCOLOR_MAX
+ };
+
+ enum {
+ /* 0 */ ANIMTARGET_MATCOLOR_MATR = 0,
+ /* 1 */ ANIMTARGET_MATCOLOR_MATG,
+ /* 2 */ ANIMTARGET_MATCOLOR_MATB,
+ /* 3 */ ANIMTARGET_MATCOLOR_MATA,
+
+ /* 4 */ ANIMTARGET_MATCOLOR_TEV0R,
+ /* 5 */ ANIMTARGET_MATCOLOR_TEV0G,
+ /* 6 */ ANIMTARGET_MATCOLOR_TEV0B,
+ /* 7 */ ANIMTARGET_MATCOLOR_TEV0A,
+
+ /* 8 */ ANIMTARGET_MATCOLOR_TEV1R,
+ /* 9 */ ANIMTARGET_MATCOLOR_TEV1G,
+ /* 10 */ ANIMTARGET_MATCOLOR_TEV1B,
+ /* 11 */ ANIMTARGET_MATCOLOR_TEV1A,
+
+ /* 12 */ ANIMTARGET_MATCOLOR_TEV2R,
+ /* 13 */ ANIMTARGET_MATCOLOR_TEV2G,
+ /* 14 */ ANIMTARGET_MATCOLOR_TEV2B,
+ /* 15 */ ANIMTARGET_MATCOLOR_TEV2A,
+
+ /* 16 */ ANIMTARGET_MATCOLOR_TEVK0R,
+ /* 17 */ ANIMTARGET_MATCOLOR_TEVK0G,
+ /* 18 */ ANIMTARGET_MATCOLOR_TEVK0B,
+ /* 19 */ ANIMTARGET_MATCOLOR_TEVK0A,
+
+ /* 20 */ ANIMTARGET_MATCOLOR_TEVK1R,
+ /* 21 */ ANIMTARGET_MATCOLOR_TEVK1G,
+ /* 22 */ ANIMTARGET_MATCOLOR_TEVK1B,
+ /* 23 */ ANIMTARGET_MATCOLOR_TEVK1A,
+
+ /* 24 */ ANIMTARGET_MATCOLOR_TEVK2R,
+ /* 25 */ ANIMTARGET_MATCOLOR_TEVK2G,
+ /* 26 */ ANIMTARGET_MATCOLOR_TEVK2B,
+ /* 27 */ ANIMTARGET_MATCOLOR_TEVK2A,
+
+ /* 28 */ ANIMTARGET_MATCOLOR_TEVK3R,
+ /* 29 */ ANIMTARGET_MATCOLOR_TEVK3G,
+ /* 30 */ ANIMTARGET_MATCOLOR_TEVK3B,
+ /* 31 */ ANIMTARGET_MATCOLOR_TEVK3A,
+
+ /* 32 */ ANIMTARGET_MATCOLOR_MAX
+ };
+
+ enum {
+ /* 0 */ ANIMTARGET_TEXSRT_TRANSX = 0,
+ /* 1 */ ANIMTARGET_TEXSRT_TRANSY,
+ /* 2 */ ANIMTARGET_TEXSRT_ROT,
+ /* 3 */ ANIMTARGET_TEXSRT_SCALEX,
+ /* 4 */ ANIMTARGET_TEXSRT_SCALEY,
+ /* 5 */ ANIMTARGET_TEXSRT_MAX
+ };
+
+ enum {
+ /* 0 */ ANIMTARGET_TEXPATTURN_IMAGE = 0,
+ /* 1 */ ANIMTARGET_TEXPATTURN_MAX
+ };
+
+ enum {
+ /* 0 */ ANIMCURVE_NONE = 0,
+ /* 1 */ ANIMCURVE_STEP,
+ /* 2 */ ANIMCURVE_HERMITE,
+ /* 3 */ ANIMCURVE_MAX
+ };
+
+ class AnimTransform {
+ public:
+ AnimTransform();
+
+ /* 0x08 */ virtual ~AnimTransform();
+ /* 0x0C */ virtual void SetResource(const res::AnimationBlock* pRes,
+ ResourceAccessor* pResAccessor) = 0;
+ /* 0x10 */ virtual void Bind(Pane* pane, bool bRecursive) = 0;
+ /* 0x14 */ virtual void Bind(Material* pMaterial) = 0;
+ /* 0x18 */ virtual void Animate(u32 idx, Pane* pane) = 0;
+ /* 0x1C */ virtual void Animate(u32 idx, Material* pMaterial) = 0;
+
+ f32 GetFrameMax() const { return GetFrameSize(); }
+ u16 GetFrameSize() const;
+
+ void SetFrame(f32 frame) { mFrame = frame; }
+
+ bool IsLoopData() const;
+
+ /* 0x00 (vtable) */
+ /* 0x04 */ ut::LinkListNode mLink;
+
+ protected:
+ /* 0x0C */ const res::AnimationBlock* mpRes;
+ /* 0x10 */ f32 mFrame;
+ }; // size = 0x14
+ typedef ut::LinkList<AnimTransform, offsetof(AnimTransform, mLink)> AnimTransformList;
+
+ class AnimationLink {
+ public:
+ AnimationLink() : mLink(), mbDisable(false) { Reset(); }
+ ~AnimationLink() {}
+
+ AnimTransform* GetAnimTransform() const { return mAnimTrans; }
+
+ u16 GetIndex() const { return mIdx; }
+ bool IsEnable() const { return !mbDisable; }
+
+ void SetEnable(bool bEnable) { mbDisable = !bEnable; }
+
+ void Reset() { SetAnimTransform(NULL, 0); }
+
+ void SetAnimTransform(AnimTransform* animTrans, u16 idx) {
+ mAnimTrans = animTrans;
+ mIdx = idx;
+ }
+
+ /* 0x00 */ ut::LinkListNode mLink;
+
+ private:
+ /* 0x08 */ AnimTransform* mAnimTrans;
+ /* 0x0C */ u16 mIdx;
+ /* 0x0E */ bool mbDisable;
+ }; // size = 0x10
+ typedef ut::LinkList<AnimationLink, offsetof(AnimationLink, mLink)> AnimationLinkList;
+
+ class AnimTransformBasic : public AnimTransform {
+ public:
+ AnimTransformBasic();
+
+ /* 0x08 */ virtual ~AnimTransformBasic();
+ /* 0x0C */ virtual void SetResource(const res::AnimationBlock* pRes,
+ ResourceAccessor* pResAccessor);
+ /* 0x10 */ virtual void Bind(Pane* pane, bool bRecursive);
+ /* 0x14 */ virtual void Bind(Material* pMaterial);
+ /* 0x18 */ virtual void Animate(u32 idx, Pane* pane);
+ /* 0x1C */ virtual void Animate(u32 idx, Material* pMaterial);
+
+ private:
+ /* 0x00 (base) */
+ /* 0x14 */ void** mpFileResAry;
+ /* 0x18 */ AnimationLink* mAnimLinkAry;
+ /* 0x1C */ u16 mAnimLinkNum;
+ }; // size = 0x20
+
+ namespace detail {
+ AnimationLink* FindAnimationLink(AnimationLinkList* animList, AnimTransform* animTrans);
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/arcResourceAccessor.h b/src/revolution/homebuttonLib/nw4hbm/lyt/arcResourceAccessor.h
new file mode 100644
index 0000000000..b3e3d43e43
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/arcResourceAccessor.h
@@ -0,0 +1,98 @@
+#ifndef NW4HBM_LYT_ARC_RESOURCE_ACCESSOR_H
+#define NW4HBM_LYT_ARC_RESOURCE_ACCESSOR_H
+
+#include "revolution/types.h"
+
+#include "revolution/arc.h"
+
+#include "../ut/Font.h"
+#include "../ut/LinkList.h"
+
+#include "resourceAccessor.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ static const int RESOURCE_NAME_MAX = 128;
+
+ class FontRefLink {
+ public:
+ FontRefLink();
+
+ void Set(const char* name, ut::Font* pFont);
+
+ const char* GetFontName() const { return mFontName; }
+ ut::Font* GetFont() const { return mpFont; }
+
+ /* 0x00 */ ut::LinkListNode mLink;
+
+ protected:
+ /* 0x08 */ char mFontName[RESOURCE_NAME_MAX];
+ /* 0x88 */ ut::Font* mpFont;
+ };
+ typedef ut::LinkList<FontRefLink, offsetof(FontRefLink, mLink)> FontRefLinkList;
+
+ class ArcResourceLink {
+ public:
+ ArcResourceLink() {}
+
+ bool Set(void* archiveStart, const char* resRootDirectory);
+
+ char* GetResRootDir() { return mResRootDir; }
+ ARCHandle* GetArcHandle() { return &mArcHandle; }
+
+ /* 0x00 */ ut::LinkListNode mLink;
+
+ protected:
+ /* 0x08 */ ARCHandle mArcHandle;
+
+ /* 0x24 */ char mResRootDir[RESOURCE_NAME_MAX];
+ };
+ typedef ut::LinkList<ArcResourceLink, offsetof(ArcResourceLink, mLink)> ArcResourceLinkList;
+
+ class ArcResourceAccessor : public ResourceAccessor {
+ public:
+ ArcResourceAccessor();
+
+ /* 0x08 */ virtual ~ArcResourceAccessor() {}
+ /* 0x0C */ virtual void* GetResource(u32 resType, const char* name, u32* pSize = NULL);
+ /* 0x10 */ virtual ut::Font* GetFont(const char* name);
+
+ bool Attach(void* archiveStart, const char* resourceRootDirectory);
+
+ bool IsAttached(void) { return this->mArcBuf != NULL; }
+
+ private:
+ /* 0x00 (base) */
+ /* 0x04 */ ARCHandle mArcHandle;
+ /* 0x20 */ void* mArcBuf;
+ /* 0x24 */ FontRefLinkList mFontList;
+ /* 0x30 */ char mResRootDir[RESOURCE_NAME_MAX];
+ };
+
+ class MultiArcResourceAccessor : public ResourceAccessor {
+ public:
+ MultiArcResourceAccessor();
+
+ /* 0x08 */ virtual ~MultiArcResourceAccessor();
+ /* 0x0C */ virtual void* GetResource(u32 resType, const char* name, u32* pSize = NULL);
+ /* 0x10 */ virtual ut::Font* GetFont(const char* name);
+
+ void Attach(ArcResourceLink* pLink);
+ void DetachAll() { reinterpret_cast<ut::detail::LinkListImpl*>(&mArcList)->Clear(); }
+ void RegistFont(FontRefLink* pLink);
+
+ protected:
+ /* 0x00 (base) */
+ /* 0x04 */ ArcResourceLinkList mArcList;
+ /* 0x10 */ FontRefLinkList mFontList;
+ };
+
+ namespace detail {
+ ut::Font* FindFont(FontRefLinkList* pFontRefList, const char* name);
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/bounding.h b/src/revolution/homebuttonLib/nw4hbm/lyt/bounding.h
new file mode 100644
index 0000000000..86257168c7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/bounding.h
@@ -0,0 +1,21 @@
+#ifndef NW4HBM_LYT_BOUNDING_H
+#define NW4HBM_LYT_BOUNDING_H
+
+#include "pane.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class Bounding : public Pane {
+ public:
+ Bounding(const res::Bounding* pBlock, const ResBlockSet& resBlockSet);
+
+ /* 0x08 */ virtual ~Bounding();
+ /* 0x0C */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x18 */ virtual void DrawSelf(const DrawInfo& drawInfo);
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/common.h b/src/revolution/homebuttonLib/nw4hbm/lyt/common.h
new file mode 100644
index 0000000000..4f235556ef
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/common.h
@@ -0,0 +1,108 @@
+#ifndef NW4HBM_LYT_COMMON_H
+#define NW4HBM_LYT_COMMON_H
+
+#include "revolution/tpl.h"
+#include "revolution/types.h"
+
+#include "../ut/Color.h"
+
+#include "animation.h"
+#include "resources.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace lyt {
+ namespace detail {
+
+ typedef math::VEC2 TexCoords[4];
+
+ class TexCoordAry {
+ public:
+ TexCoordAry();
+
+ u8 GetSize() const { return mNum; }
+ const TexCoords* GetArray() const { return mpData; }
+
+ void SetSize(u8 num);
+
+ bool IsEmpty() const { return mCap == 0; }
+
+ void Reserve(u8 num);
+
+ void Free();
+ void Copy(const void* pResTexCoord, u8 texCoordNum);
+
+ void SetCoord(u32 idx, const math::VEC2* vec);
+ void GetCoord(u32 idx, math::VEC2* vec) const;
+
+ private:
+ /* 0x00 */ u8 mCap;
+ /* 0x01 */ u8 mNum;
+ /* 0x04 */ TexCoords* mpData;
+ };
+
+ bool EqualsPaneName(const char* name1, const char* name2);
+ bool EqualsMaterialName(const char* name1, const char* name2);
+
+ bool TestFileHeader(const res::BinaryFileHeader& fileHeader);
+ bool TestFileHeader(const res::BinaryFileHeader& fileHeader, u32 testSig);
+
+ bool IsModulateVertexColor(ut::Color* vtxColors, u8 glbAlpha);
+
+ ut::Color MultipleAlpha(const ut::Color col, u8 alpha);
+ void MultipleAlpha(ut::Color* dst, const ut::Color* src, u8 alpha);
+
+ void SetVertexFormat(bool bModulate, u8 texCoordNum);
+
+ void DrawQuad(const math::VEC2& basePt, const Size& size, u8 texCoordNum,
+ const TexCoords* texCoords, const ut::Color* vtxColors);
+ void DrawQuad(const math::VEC2& basePt, const Size& size, u8 texCoordNum,
+ const TexCoords* texCoords, const ut::Color* vtxColors, u8 alpha);
+
+ void DrawLine(const math::VEC2& pos, const Size& size, ut::Color color);
+
+ void InitGXTexObjFromTPL(GXTexObj* to, TPLPalette* pal, u32 id);
+
+ inline s32 GetSignatureInt(const char* sig) {
+ return *reinterpret_cast<const s32*>(sig);
+ }
+
+ inline const char* GetStrTableStr(const void* pStrTable, int index) {
+ const u32* offsets = static_cast<const u32*>(pStrTable);
+ const char* stringPool = static_cast<const char*>(pStrTable);
+
+ return &stringPool[offsets[index]];
+ }
+
+ inline u8 GetVtxColorElement(const ut::Color* cols, u32 idx) {
+ NW4HBM_ASSERT(199, idx < ANIMTARGET_VERTEXCOLOR_MAX);
+ return reinterpret_cast<const u8*>(cols + idx / 4)[idx % 4];
+ }
+
+ inline void SetVtxColorElement(ut::Color* cols, u32 idx, u8 value) {
+ NW4HBM_ASSERT(212, idx < ANIMTARGET_VERTEXCOLOR_MAX);
+ reinterpret_cast<u8*>(cols + idx / 4)[idx % 4] = value;
+ }
+
+ inline u8 GetHorizontalPosition(u8 var) {
+ return var % HORIZONTALPOSITION_MAX;
+ }
+
+ inline u8 GetVerticalPosition(u8 var) {
+ return var / VERTICALPOSITION_MAX;
+ }
+
+ inline void SetHorizontalPosition(u8* pVar, u8 newVal) {
+ *pVar = GetVerticalPosition(*pVar) * HORIZONTALPOSITION_MAX + newVal;
+ }
+
+ inline void SetVerticalPosition(u8* pVar, u8 newVal) {
+ *pVar = newVal * VERTICALPOSITION_MAX + GetHorizontalPosition(*pVar);
+ }
+
+ } // namespace detail
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/drawInfo.h b/src/revolution/homebuttonLib/nw4hbm/lyt/drawInfo.h
new file mode 100644
index 0000000000..3c3cbf12b6
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/drawInfo.h
@@ -0,0 +1,67 @@
+#ifndef NW4HBM_LYT_DRAW_INFO_H
+#define NW4HBM_LYT_DRAW_INFO_H
+
+#include <revolution/gx.h>
+
+#include "../math/types.h"
+
+#include "../ut/Rect.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class DrawInfo {
+ public:
+ DrawInfo();
+
+ /* 0x08 */ virtual ~DrawInfo();
+
+ void SetViewRect(const ut::Rect& rect) { mViewRect = rect; }
+
+ const math::MTX34& GetViewMtx() const { return mViewMtx; }
+ void SetViewMtx(const math::MTX34& value) { mViewMtx = value; }
+
+ const math::VEC2& GetLocationAdjustScale() const { return mLocationAdjustScale; }
+ void SetLocationAdjustScale(const math::VEC2& scale) { mLocationAdjustScale = scale; }
+
+ bool IsMultipleViewMtxOnDraw() const { return mFlag.mulViewDraw; }
+ void SetMultipleViewMtxOnDraw(bool bEnable) { mFlag.mulViewDraw = bEnable; }
+
+ bool IsInfluencedAlpha() const { return mFlag.influencedAlpha; }
+ void SetInfluencedAlpha(bool bEnable) { mFlag.influencedAlpha = bEnable; }
+
+ bool IsLocationAdjust() const { return mFlag.locationAdjust; }
+ void SetLocationAdjust(bool bEnable) { mFlag.locationAdjust = bEnable; }
+
+ bool IsInvisiblePaneCalculateMtx() const { return mFlag.invisiblePaneCalculateMtx; }
+ void SetInvisiblePaneCalculateMtx(bool bEnable) {
+ mFlag.invisiblePaneCalculateMtx = bEnable;
+ }
+
+ bool IsDebugDrawMode() const { return mFlag.debugDrawMode; }
+ void SetDebugDrawMode(bool bEnable) { mFlag.debugDrawMode = bEnable; }
+
+ bool IsYAxisUp() const { return mViewRect.bottom - mViewRect.top < 0.0f; }
+
+ f32 GetGlobalAlpha() const { return mGlobalAlpha; }
+ void SetGlobalAlpha(f32 alpha) { mGlobalAlpha = alpha; }
+
+ protected:
+ /* 0x00 (vtable) */
+ /* 0x04 */ math::MTX34 mViewMtx;
+ /* 0x34 */ ut::Rect mViewRect;
+ /* 0x44 */ math::VEC2 mLocationAdjustScale;
+ /* 0x4C */ f32 mGlobalAlpha;
+ /* 0x50 */ struct {
+ u8 mulViewDraw : 1; // 10000000
+ u8 influencedAlpha : 1; // 01000000
+ u8 locationAdjust : 1; // 00100000
+ u8 invisiblePaneCalculateMtx : 1; // 00010000
+ u8 debugDrawMode : 1; // 00001000
+ } mFlag;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/group.h b/src/revolution/homebuttonLib/nw4hbm/lyt/group.h
new file mode 100644
index 0000000000..b48ab0a99a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/group.h
@@ -0,0 +1,65 @@
+#ifndef NW4HBM_LYT_GROUP_H
+#define NW4HBM_LYT_GROUP_H
+
+#include <revolution/types.h>
+
+#include "pane.h"
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ namespace detail {
+ typedef struct PaneLink {
+ ut::LinkListNode mLink;
+
+ /* 0x08 */ Pane* mTarget;
+ } PaneLink;
+ } // namespace detail
+ typedef ut::LinkList<detail::PaneLink, offsetof(detail::PaneLink, mLink)> PaneLinkList;
+
+ class Group {
+ public:
+ Group();
+ Group(const res::Group* pResGroup, Pane* pRootPane);
+
+ /* 0x08 */ virtual ~Group();
+
+ const char* GetName() const { return mName; }
+ bool IsUserAllocated() const { return mbUserAllocated; }
+
+ PaneLinkList& GetPaneList() { return mPaneLinkList; };
+
+ void Init();
+ void AppendPane(Pane* pane);
+
+ /* 0x00 (vtable) */
+ /* 0x04 */ ut::LinkListNode mLink;
+
+ protected:
+ /* 0x0C */ PaneLinkList mPaneLinkList;
+ /* 0x18 */ char mName[16];
+ /* 0x29 */ bool mbUserAllocated;
+ /* 0x2A */ u8 mPadding[2];
+ };
+ typedef ut::LinkList<Group, offsetof(Group, mLink)> GroupList;
+
+ class GroupContainer {
+ public:
+ GroupContainer() {}
+ ~GroupContainer();
+
+ GroupList& GetGroupList() { return mGroupList; }
+
+ void AppendGroup(Group* pGroup);
+ Group* FindGroupByName(const char* findName);
+
+ protected:
+ /* 0x00 */ GroupList mGroupList;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/layout.h b/src/revolution/homebuttonLib/nw4hbm/lyt/layout.h
new file mode 100644
index 0000000000..4d44d70d48
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/layout.h
@@ -0,0 +1,68 @@
+#ifndef NW4HBM_LYT_LAYOUT_H
+#define NW4HBM_LYT_LAYOUT_H
+
+#include <revolution/mem.h>
+#include <revolution/types.h>
+
+#include "animation.h"
+#include "drawInfo.h"
+#include "group.h"
+#include "lyt_types.h"
+#include "resourceAccessor.h"
+
+
+#include "../ut/LinkList.h"
+#include "../ut/TagProcessorBase.h"
+#include "../ut/WideTagProcessor.h"
+
+#include "macros.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class Layout {
+ public:
+ Layout();
+
+ /* 0x08 */ virtual ~Layout();
+ /* 0x0C */ virtual bool Build(const void* lytResBuf, ResourceAccessor* pResAcsr);
+ /* 0x10 */ virtual AnimTransform* CreateAnimTransform(const void* anmResBuf,
+ ResourceAccessor* pResAcsr);
+ /* 0x14 */ virtual void BindAnimation(AnimTransform* animTrans);
+ /* 0x18 */ virtual void UnbindAnimation(AnimTransform* animTrans);
+ /* 0x1C */ virtual void UnbindAllAnimation();
+ /* 0x20 */ virtual void SetAnimationEnable(AnimTransform* animTrans,
+ bool bEnable = true);
+ /* 0x24 */ virtual void CalculateMtx(const DrawInfo& drawInfo);
+ /* 0x28 */ virtual void Draw(const DrawInfo& drawInfo);
+ /* 0x2C */ virtual void Animate(u32 option = 0);
+ /* 0x30 */ virtual void SetTagProcessor(ut::WideTagProcessor* pTagProcessor) NO_INLINE;
+
+ const ut::Rect GetLayoutRect() const;
+ Pane* GetRootPane() const { return mpRootPane; }
+ GroupContainer* GetGroupContainer() const { return mpGroupContainer; }
+
+ static MEMAllocator* GetAllocator() { return mspAllocator; }
+ static void SetAllocator(MEMAllocator* allocator) { mspAllocator = allocator; }
+
+ static void* AllocMemory(u32 size) { return MEMAllocFromAllocator(mspAllocator, size); }
+ static void FreeMemory(void* ptr) { MEMFreeToAllocator(mspAllocator, ptr); }
+
+ static Pane* BuildPaneObj(s32 kind, const void* dataPtr,
+ const ResBlockSet& resBlockSet) NO_INLINE;
+
+ private:
+ /* 0x00 (vtable) */
+ /* 0x04 */ AnimTransformList mAnimTransList;
+ /* 0x10 */ Pane* mpRootPane;
+ /* 0x14 */ GroupContainer* mpGroupContainer;
+ /* 0x18 */ Size mLayoutSize;
+ /* 0x20 */ u8 mOriginType;
+
+ static MEMAllocator* mspAllocator;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp
new file mode 100644
index 0000000000..a940cd5483
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_animation.cpp
@@ -0,0 +1,466 @@
+#include "animation.h"
+
+#include "common.h"
+#include "layout.h"
+#include "pane.h"
+
+#include "new.h"
+#include <cstring.h>
+
+namespace {
+ // pretend this is nw4hbm::lyt
+ using namespace nw4hbm;
+ using namespace nw4hbm::lyt;
+
+ inline bool RIsSame(const f32 a, const f32 b, const f32 tolerance) {
+ f32 c = a - b;
+
+ return -tolerance < c && c < tolerance;
+ }
+
+ u16 GetStepCurveValue(f32 frame, const res::StepKey* keyArray, u32 keySize);
+ f32 GetHermiteCurveValue(f32 frame, const res::HermiteKey* keyArray, u32 keySize);
+
+ void AnimatePainSRT(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+ void AnimateVisibility(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+ void AnimateVertexColor(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+ void AnimateMaterialColor(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+ void AnimateTextureSRT(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+ void AnimateTexturePattern(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame, void** tpls);
+ void AnimateIndTexSRT(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame);
+} // namespace
+
+namespace {
+
+ u16 GetStepCurveValue(f32 frame, const res::StepKey* keyArray, u32 keySize) {
+ if (keySize == 1 || frame <= keyArray[0].frame) {
+ return keyArray[0].value;
+ }
+
+ if (frame >= keyArray[keySize - 1].frame) {
+ return keyArray[keySize - 1].value;
+ }
+
+ int ikeyL = 0;
+ int ikeyR = keySize - 1;
+
+ while (ikeyL != ikeyR - 1 && ikeyL != ikeyR) {
+ int ikeyCenter = (ikeyL + ikeyR) / 2;
+ const res::StepKey& centerKey = keyArray[ikeyCenter];
+
+ if (frame < centerKey.frame) {
+ ikeyR = ikeyCenter;
+ } else {
+ ikeyL = ikeyCenter;
+ }
+ }
+
+ if (RIsSame(frame, keyArray[ikeyR].frame, 0.001f)) {
+ return keyArray[ikeyR].value;
+ } else {
+ return keyArray[ikeyL].value;
+ }
+ }
+
+ f32 GetHermiteCurveValue(f32 frame, const res::HermiteKey* keyArray, u32 keySize) {
+ if (keySize == 1 || frame <= keyArray[0].frame) {
+ return keyArray[0].value;
+ }
+
+ if (frame >= keyArray[keySize - 1].frame) {
+ return keyArray[keySize - 1].value;
+ }
+
+ int ikeyL = 0;
+ int ikeyR = keySize - 1;
+
+ while (ikeyL != ikeyR - 1 && ikeyL != ikeyR) {
+ int ikeyCenter = (ikeyL + ikeyR) / 2;
+
+ if (frame <= keyArray[ikeyCenter].frame) {
+ ikeyR = ikeyCenter;
+ } else {
+ ikeyL = ikeyCenter;
+ }
+ }
+
+ const res::HermiteKey& key0 = keyArray[ikeyL];
+ const res::HermiteKey& key1 = keyArray[ikeyR];
+
+ if (RIsSame(frame, key1.frame, 0.001f)) {
+ if (ikeyR < keySize - 1 && keyArray[ikeyR + 1].frame == key1.frame) {
+ return keyArray[ikeyR + 1].value;
+ } else {
+ return key1.value;
+ }
+ }
+
+ f32 t1 = frame - key0.frame;
+ f32 t2 = 1.0f / (key1.frame - key0.frame);
+ f32 v0 = key0.value;
+ f32 v1 = key1.value;
+ f32 s0 = key0.slope;
+ f32 s1 = key1.slope;
+
+ f32 t1t1t2 = t1 * t1 * t2;
+ f32 t1t1t2t2 = t1t1t2 * t2;
+ f32 t1t1t1t2t2 = t1 * t1t1t2t2;
+ f32 t1t1t1t2t2t2 = t1t1t1t2t2 * t2;
+
+ // Does anyone know what this means? Because I don't
+
+ // clang-format off
+ return v0 * (( 2.0f * t1t1t1t2t2t2) - (3.0f * t1t1t2t2) + 1.0f)
+ + v1 * ((-2.0f * t1t1t1t2t2t2) + (3.0f * t1t1t2t2) )
+ + s0 * (( t1t1t1t2t2 ) - (2.0f * t1t1t2 ) + t1 )
+ + s1 * (( t1t1t1t2t2 ) - ( t1t1t2 ) );
+ // clang-format on
+ }
+
+ void AnimatePainSRT(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(197, pAnimTarget->target < ANIMTARGET_PANE_MAX);
+ NW4HBM_ASSERT(198, pAnimTarget->curveType == ANIMCURVE_HERMITE);
+
+ const res::HermiteKey* keys =
+ detail::ConvertOffsToPtr<res::HermiteKey>(pAnimTarget, pAnimTarget->keysOffset);
+
+ pPane->SetSRTElement(pAnimTarget->target,
+ GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum));
+ }
+ }
+
+ void AnimateVisibility(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(217, pAnimTarget->target < ANIMTARGET_PANE_MAX);
+ NW4HBM_ASSERT(218, pAnimTarget->curveType == ANIMCURVE_STEP);
+
+ const res::StepKey* keys =
+ detail::ConvertOffsToPtr<res::StepKey>(pAnimTarget, pAnimTarget->keysOffset);
+
+ pPane->SetVisible(GetStepCurveValue(frame, keys, pAnimTarget->keyNum) != 0);
+ }
+ }
+
+ void AnimateVertexColor(Pane* pPane, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(237, pAnimTarget->target < ANIMTARGET_PANE_COLOR_MAX);
+ NW4HBM_ASSERT(238, pAnimTarget->curveType == ANIMCURVE_HERMITE);
+
+ const res::HermiteKey* keys =
+ detail::ConvertOffsToPtr<res::HermiteKey>(pAnimTarget, pAnimTarget->keysOffset);
+
+ f32 value = GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum);
+ value += 0.5f;
+
+ u8 u8Val;
+ OSf32tou8(&value, &u8Val);
+
+ // What
+ pPane->SetColorElement(pAnimTarget->target, *static_cast<u8*>(&u8Val));
+ }
+ }
+
+ inline void AnimateMaterialColor(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(262, pAnimTarget->target < ANIMTARGET_MATCOLOR_MAX);
+ NW4HBM_ASSERT(263, pAnimTarget->curveType == ANIMCURVE_HERMITE);
+
+ const res::HermiteKey* keys =
+ detail::ConvertOffsToPtr<res::HermiteKey>(pAnimTarget, pAnimTarget->keysOffset);
+
+ f32 value = GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum);
+ value += 0.5f;
+
+ s16 s16Val;
+ OSf32tos16(&value, &s16Val);
+ s16Val = ut::Min<s16>(ut::Max<s16>(s16Val, -1024), 1023);
+
+ pMaterial->SetColorElement(pAnimTarget->target, s16Val);
+ }
+ }
+
+ void AnimateTextureSRT(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(287, pAnimTarget->id < TexMtxMax);
+
+ if (pAnimTarget->id < pMaterial->GetTexSRTCap()) {
+ NW4HBM_ASSERT(290, pAnimTarget->target < ANIMTARGET_TEXSRT_MAX);
+ NW4HBM_ASSERT(291, pAnimTarget->curveType == ANIMCURVE_HERMITE);
+
+ const res::HermiteKey* keys =
+ detail::ConvertOffsToPtr<res::HermiteKey>(pAnimTarget, pAnimTarget->keysOffset);
+ pMaterial->SetTexSRTElement(pAnimTarget->id, pAnimTarget->target,
+ GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum));
+ }
+ }
+ }
+
+ void AnimateTexturePattern(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame, void** tpls) {
+ for (int j = 0; j < pAnimInfo->num; j++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[j]);
+
+ NW4HBM_ASSERT(311, pAnimTarget->id < GX_MAX_TEXMAP);
+
+ if (pAnimTarget->id < pMaterial->GetTextureNum()) {
+ NW4HBM_ASSERT(314, pAnimTarget->curveType == ANIMCURVE_STEP);
+
+ if (!pAnimTarget->target) {
+ const res::StepKey* keys = detail::ConvertOffsToPtr<res::StepKey>(
+ pAnimTarget, pAnimTarget->keysOffset);
+ u16 fileIdx = GetStepCurveValue(frame, keys, pAnimTarget->keyNum);
+ pMaterial->SetTextureNoWrap(pAnimTarget->id,
+ static_cast<TPLPalette*>(tpls[fileIdx]));
+ }
+ }
+ }
+ }
+
+ void AnimateIndTexSRT(Material* pMaterial, const res::AnimationInfo* pAnimInfo,
+ const u32* animTargetOffsets, f32 frame) {
+ for (int i = 0; i < pAnimInfo->num; i++) {
+ const res::AnimationTarget* pAnimTarget =
+ detail::ConvertOffsToPtr<res::AnimationTarget>(pAnimInfo, animTargetOffsets[i]);
+
+ NW4HBM_ASSERT(337, pAnimTarget->id < IndTexMtxMax);
+
+ if (pAnimTarget->id < pMaterial->GetIndTexSRTCap()) {
+ NW4HBM_ASSERT(340, pAnimTarget->target < ANIMTARGET_TEXSRT_MAX);
+ NW4HBM_ASSERT(341, pAnimTarget->curveType == ANIMCURVE_HERMITE);
+
+ const res::HermiteKey* keys =
+ detail::ConvertOffsToPtr<res::HermiteKey>(pAnimTarget, pAnimTarget->keysOffset);
+ pMaterial->SetIndTexSRTElement(
+ pAnimTarget->id, pAnimTarget->target,
+ GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum));
+ }
+ }
+ }
+
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ AnimTransform::AnimTransform() : mLink(), mpRes(NULL), mFrame(0.0f) {}
+
+ AnimTransform::~AnimTransform() {}
+
+ u16 AnimTransform::GetFrameSize() const {
+ return mpRes->frameSize;
+ }
+
+ AnimTransformBasic::AnimTransformBasic()
+ : mpFileResAry(NULL), mAnimLinkAry(NULL), mAnimLinkNum(0) {}
+
+ AnimTransformBasic::~AnimTransformBasic() {
+ if (mAnimLinkAry) {
+ Layout::FreeMemory(mAnimLinkAry);
+ }
+
+ if (mpFileResAry) {
+ Layout::FreeMemory(mpFileResAry);
+ }
+ }
+
+ void AnimTransformBasic::SetResource(const res::AnimationBlock* pRes,
+ ResourceAccessor* pResAccessor) {
+ NW4HBM_ASSERT(422, mpFileResAry == 0);
+ NW4HBM_ASSERT(423, mAnimLinkAry == 0);
+
+ mpRes = pRes;
+ mpFileResAry = NULL;
+
+ if (pRes->fileNum) {
+ mpFileResAry =
+ static_cast<void**>(Layout::AllocMemory(sizeof(*mpFileResAry) * pRes->fileNum));
+
+ if (mpFileResAry) {
+ const u32* fileNameOffsets =
+ detail::ConvertOffsToPtr<u32>(mpRes, sizeof(*mpRes));
+
+ for (int i = 0; i < mpRes->fileNum; i++) {
+ mpFileResAry[i] = pResAccessor->GetResource(
+ 'timg', detail::GetStrTableStr(fileNameOffsets, i), 0);
+ }
+ }
+ }
+
+ mAnimLinkAry = static_cast<AnimationLink*>(
+ Layout::AllocMemory(sizeof(*mAnimLinkAry) * pRes->animContNum));
+
+ if (mAnimLinkAry) {
+ mAnimLinkNum = pRes->animContNum;
+ std::memset(mAnimLinkAry, 0, sizeof(*mAnimLinkAry) * pRes->animContNum);
+
+ for (u16 i = 0; i < pRes->animContNum; i++) {
+ new (&mAnimLinkAry[i]) AnimationLink();
+ }
+ }
+ }
+
+ void AnimTransformBasic::Bind(Pane* pPane, bool bRecursive) {
+ const u32* animContOffsets =
+ detail::ConvertOffsToPtr<u32>(mpRes, mpRes->animContOffsetsOffset);
+
+ for (u16 i = 0; i < mpRes->animContNum; i++) {
+ const res::AnimationContent* pAnimCont =
+ detail::ConvertOffsToPtr<res::AnimationContent>(mpRes, animContOffsets[i]);
+
+ if (pAnimCont->type == res::AnimationContent::ACType_Pane) {
+ Pane* pFindPane = pPane->FindPaneByName(pAnimCont->name, bRecursive);
+
+ if (pFindPane) {
+ mAnimLinkAry[i].SetAnimTransform(this, i);
+ pFindPane->AddAnimationLink(&mAnimLinkAry[i]);
+ }
+ } else {
+ Material* pFindMat = pPane->FindMaterialByName(pAnimCont->name, bRecursive);
+
+ if (pFindMat) {
+ mAnimLinkAry[i].SetAnimTransform(this, i);
+ pFindMat->AddAnimationLink(&mAnimLinkAry[i]);
+ }
+ }
+ }
+ }
+
+ void AnimTransformBasic::Bind(Material* pMaterial) {
+ const u32* animContOffsets =
+ detail::ConvertOffsToPtr<u32>(mpRes, mpRes->animContOffsetsOffset);
+
+ for (u16 i = 0; i < mpRes->animContNum; i++) {
+ const res::AnimationContent* pAnimCont =
+ detail::ConvertOffsToPtr<res::AnimationContent>(mpRes, animContOffsets[i]);
+
+ if (pAnimCont->type != res::AnimationContent::ACType_Material) {
+ continue;
+ }
+
+ if (detail::EqualsMaterialName(pMaterial->GetName(), pAnimCont->name)) {
+ mAnimLinkAry[i].SetAnimTransform(this, i);
+ pMaterial->AddAnimationLink(&mAnimLinkAry[i]);
+ }
+ }
+ }
+
+ void AnimTransformBasic::Animate(u32 idx, Pane* pPane) {
+ u32 animContOffsets =
+ detail::ConvertOffsToPtr<u32>(mpRes, mpRes->animContOffsetsOffset)[idx];
+
+ const res::AnimationContent* pAnimCont =
+ detail::ConvertOffsToPtr<res::AnimationContent>(mpRes, animContOffsets);
+
+ const u32* animInfoOffsets =
+ detail::ConvertOffsToPtr<u32>(pAnimCont, sizeof(*pAnimCont));
+
+ for (int i = 0; i < pAnimCont->num; i++) {
+ const res::AnimationInfo* pAnimInfo =
+ detail::ConvertOffsToPtr<res::AnimationInfo>(pAnimCont, animInfoOffsets[i]);
+
+ const u32* animTargetOffsets =
+ detail::ConvertOffsToPtr<u32>(pAnimInfo, sizeof(*pAnimInfo));
+
+ switch (pAnimInfo->kind) {
+ case res::AnimationInfo::ANIM_INFO_PANE_PAIN_SRT:
+ AnimatePainSRT(pPane, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+
+ case res::AnimationInfo::ANIM_INFO_PANE_VISIBILITY:
+ AnimateVisibility(pPane, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+
+ case res::AnimationInfo::ANIM_INFO_PANE_VERTEX_COLOR:
+ AnimateVertexColor(pPane, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+ }
+ }
+ }
+
+ void AnimTransformBasic::Animate(u32 idx, Material* pMaterial) {
+ u32 animContOffsets =
+ detail::ConvertOffsToPtr<u32>(mpRes, mpRes->animContOffsetsOffset)[idx];
+
+ const res::AnimationContent* pAnimCont =
+ detail::ConvertOffsToPtr<res::AnimationContent>(mpRes, animContOffsets);
+
+ const u32* animInfoOffsets =
+ detail::ConvertOffsToPtr<u32>(pAnimCont, sizeof(*pAnimCont));
+
+ for (int i = 0; i < pAnimCont->num; i++) {
+ const res::AnimationInfo* pAnimInfo =
+ detail::ConvertOffsToPtr<res::AnimationInfo>(pAnimCont, animInfoOffsets[i]);
+
+ const u32* animTargetOffsets =
+ detail::ConvertOffsToPtr<u32>(pAnimInfo, sizeof(*pAnimInfo));
+
+ switch (pAnimInfo->kind) {
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_COLOR:
+ AnimateMaterialColor(pMaterial, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_TEXTURE_SRT:
+ AnimateTextureSRT(pMaterial, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_TEXTURE_PATTERN:
+ if (mpFileResAry) {
+ AnimateTexturePattern(pMaterial, pAnimInfo, animTargetOffsets, mFrame,
+ mpFileResAry);
+ }
+
+ break;
+
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_IND_TEX_SRT:
+ AnimateIndTexSRT(pMaterial, pAnimInfo, animTargetOffsets, mFrame);
+ break;
+ }
+ }
+ }
+
+ AnimationLink* detail::FindAnimationLink(AnimationLinkList* pAnimList,
+ AnimTransform* pAnimTrans) {
+ NW4HBM_ASSERT_CHECK_NULL(559, pAnimList);
+ NW4HBM_ASSERT_CHECK_NULL(560, pAnimTrans);
+ for (AnimationLinkList::Iterator it = pAnimList->GetBeginIter();
+ it != pAnimList->GetEndIter(); it++)
+ {
+ if (pAnimTrans == it->GetAnimTransform()) {
+ return &(*it);
+ }
+ }
+
+ return NULL;
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp
new file mode 100644
index 0000000000..554ba0d985
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_arcResourceAccessor.cpp
@@ -0,0 +1,197 @@
+#include "arcResourceAccessor.h"
+
+#include <revolution/arc.h>
+
+#include "string.h"
+
+namespace {
+
+ s32 FindNameResource(ARCHandle* pArcHandle, const char* resName) NO_INLINE {
+ s32 entryNum = -1;
+
+ ARCDir dir;
+ BOOL bSuccess = ARCOpenDir(pArcHandle, ".", &dir);
+ NW4HBM_ASSERT(48, bSuccess);
+
+ ARCDirEntry dirEntry;
+
+ while (ARCReadDir(&dir, &dirEntry)) {
+ if (dirEntry.isDir != 0) {
+ bSuccess = ARCChangeDir(pArcHandle, dirEntry.name);
+ NW4HBM_ASSERT(57, bSuccess);
+
+ entryNum = FindNameResource(pArcHandle, resName);
+ bSuccess = ARCChangeDir(pArcHandle, "..");
+ NW4HBM_ASSERT(60, bSuccess);
+
+ if (entryNum != -1) {
+ break;
+ }
+ } else if (stricmp(resName, dirEntry.name) == 0) {
+ entryNum = dirEntry.entryNum;
+ break;
+ }
+ }
+
+ bSuccess = ARCCloseDir(&dir);
+ NW4HBM_ASSERT(77, bSuccess);
+ return entryNum;
+ }
+
+ void* GetResourceSub(ARCHandle* pArcHandle, const char* resRootDir, u32 resType,
+ const char* name, u32* pSize) {
+ s32 entryNum = -1;
+
+ if (ARCConvertPathToEntrynum(pArcHandle, resRootDir) != -1 &&
+ ARCChangeDir(pArcHandle, resRootDir))
+ {
+ if (!resType) {
+ entryNum = FindNameResource(pArcHandle, name);
+ } else {
+ char resTypeStr[5];
+ resTypeStr[0] = resType >> 24;
+ resTypeStr[1] = resType >> 16;
+ resTypeStr[2] = resType >> 8;
+ resTypeStr[3] = resType;
+ resTypeStr[4] = '\0';
+
+ if (ARCConvertPathToEntrynum(pArcHandle, resTypeStr) != -1 &&
+ ARCChangeDir(pArcHandle, resTypeStr))
+ {
+ entryNum = ARCConvertPathToEntrynum(pArcHandle, name);
+ BOOL bSuccess = ARCChangeDir(pArcHandle, "..");
+ NW4HBM_ASSERT(117, bSuccess);
+ }
+ }
+
+ BOOL bSuccess = ARCChangeDir(pArcHandle, "..");
+ NW4HBM_ASSERT(123, bSuccess);
+ }
+
+ if (entryNum != -1) {
+ ARCFileInfo arcFileInfo;
+ BOOL bSuccess = ARCFastOpen(pArcHandle, entryNum, &arcFileInfo);
+ NW4HBM_ASSERT(131, bSuccess);
+
+ void* resPtr = ARCGetStartAddrInMem(&arcFileInfo);
+
+ if (pSize) {
+ *pSize = ARCGetLength(&arcFileInfo);
+ }
+
+ ARCClose(&arcFileInfo);
+
+ return resPtr;
+ }
+
+ return NULL;
+ }
+} // namespace
+
+namespace nw4hbm {
+ namespace lyt {
+ ut::Font* detail::FindFont(FontRefLinkList* pFontRefList, const char* name) {
+ for (FontRefLinkList::Iterator it = pFontRefList->GetBeginIter();
+ it != pFontRefList->GetEndIter(); it++)
+ {
+ if (strcmp(name, it->GetFontName()) == 0) {
+ return it->GetFont();
+ }
+ }
+ return NULL;
+ }
+
+ FontRefLink::FontRefLink() : mpFont(NULL) {}
+
+ void FontRefLink::Set(const char* name, ut::Font* pFont) {
+ strcpy(mFontName, name);
+ mpFont = pFont;
+ }
+
+ ArcResourceAccessor::ArcResourceAccessor() : mArcBuf(NULL) {}
+
+ void dummyString() {
+ OSReport("NW4HBM:Failed assertion std::strlen(name) < FONTNAMEBUF_MAX");
+ }
+
+ bool ArcResourceAccessor::Attach(void* archiveStart, const char* resourceRootDirectory) {
+ // clang-format off
+ NW4HBM_ASSERT(220, ! IsAttached());
+ NW4HBM_ASSERT_CHECK_NULL(221, archiveStart);
+ NW4HBM_ASSERT_CHECK_NULL(222, resourceRootDirectory);
+ // clang-format on
+
+ BOOL bSuccess = ARCInitHandle(archiveStart, &mArcHandle);
+
+ if (!bSuccess) {
+ return false;
+ }
+
+ mArcBuf = archiveStart;
+
+ strncpy(mResRootDir, resourceRootDirectory, ARRAY_SIZE(mResRootDir) - 1);
+ mResRootDir[ARRAY_SIZE(mResRootDir) - 1] = '\0';
+
+ return true;
+ }
+
+ void dummyString2() {
+ OSReport("NW4HBM:Failed assertion IsAttached()");
+ OSReport("NW4HBM:Pointer must not be NULL (pLink)");
+ }
+
+ void* ArcResourceAccessor::GetResource(u32 resType, const char* name, u32* pSize) {
+ return GetResourceSub(&mArcHandle, mResRootDir, resType, name, pSize);
+ }
+
+ bool ArcResourceLink::Set(void* archiveStart, const char* resRootDirectory) {
+ BOOL bSuccess = ARCInitHandle(archiveStart, &mArcHandle);
+
+ if (!bSuccess) {
+ return false;
+ }
+
+ strncpy(mResRootDir, resRootDirectory, ARRAY_SIZE(mResRootDir) - 1);
+ mResRootDir[ARRAY_SIZE(mResRootDir) - 1] = '\0';
+
+ return true;
+ }
+
+ ut::Font* ArcResourceAccessor::GetFont(const char* name) {
+ return detail::FindFont(&mFontList, name);
+ }
+
+ MultiArcResourceAccessor::MultiArcResourceAccessor() {}
+
+ MultiArcResourceAccessor::~MultiArcResourceAccessor() {
+ DetachAll();
+ }
+
+ void MultiArcResourceAccessor::Attach(ArcResourceLink* pLink) {
+ mArcList.PushBack(pLink);
+ }
+
+ void* MultiArcResourceAccessor::GetResource(u32 resType, const char* name, u32* pSize) {
+ for (ArcResourceLinkList::Iterator it = mArcList.GetBeginIter();
+ it != mArcList.GetEndIter(); it++)
+ {
+ ARCHandle* pArcHandle = it->GetArcHandle();
+ if (void* resPtr =
+ GetResourceSub(pArcHandle, it->GetResRootDir(), resType, name, pSize))
+ {
+ return resPtr;
+ }
+ }
+ return NULL;
+ }
+
+ void MultiArcResourceAccessor::RegistFont(FontRefLink* pLink) {
+ mFontList.PushBack(pLink);
+ }
+
+ ut::Font* MultiArcResourceAccessor::GetFont(const char* name) {
+ return detail::FindFont(&mFontList, name);
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp
new file mode 100644
index 0000000000..8546dc182b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_bounding.cpp
@@ -0,0 +1,24 @@
+#include "bounding.h"
+
+#include "common.h"
+#include "drawInfo.h"
+#include "pane.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(Bounding, Pane);
+
+ Bounding::Bounding(const res::Bounding* pBlock, const ResBlockSet&) : Pane(pBlock) {}
+
+ Bounding::~Bounding() {}
+
+ void Bounding::DrawSelf(const DrawInfo& drawInfo) {
+ if (drawInfo.IsDebugDrawMode()) {
+ LoadMtx(drawInfo);
+ detail::DrawLine(GetVtxPos(), mSize, ut::Color(0x00ff00ff));
+ }
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp
new file mode 100644
index 0000000000..7b87676dce
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_common.cpp
@@ -0,0 +1,279 @@
+#include "common.h"
+
+#include "layout.h"
+
+#include <cstring.h>
+
+namespace nw4hbm {
+ namespace lyt {
+ namespace detail {
+
+ // sizeof(Pane::mName) == 16
+ bool EqualsPaneName(const char* name1, const char* name2) {
+ return std::strncmp(name1, name2, 16) == 0;
+ }
+
+ // sizeof(Material::mName) == 20
+ bool EqualsMaterialName(const char* name1, const char* name2) {
+ return std::strncmp(name1, name2, 20) == 0;
+ }
+
+ // U+FEFF * BYTE ORDER MARK
+ bool TestFileHeader(const res::BinaryFileHeader& fileHeader) {
+ return fileHeader.byteOrder == 0xFEFF && fileHeader.version == 8;
+ }
+
+ bool TestFileHeader(const res::BinaryFileHeader& fileHeader, u32 testSig) {
+ return static_cast<u32>(GetSignatureInt(fileHeader.signature)) == testSig &&
+ TestFileHeader(fileHeader);
+ }
+
+ TexCoordAry::TexCoordAry() : mCap(0), mNum(0), mpData(NULL) {}
+
+ void TexCoordAry::Free() {
+ if (mpData) {
+ Layout::FreeMemory(mpData);
+ mpData = NULL;
+
+ mCap = 0;
+ mNum = 0;
+ }
+ }
+
+ void TexCoordAry::Reserve(u8 num) {
+ NW4HBM_ASSERT(93, num <= GX_MAX_TEXMAP);
+ if (mCap < num) {
+ Free();
+ mpData = static_cast<TexCoords*>(Layout::AllocMemory(sizeof(*mpData) * num));
+
+ if (mpData) {
+ mCap = num;
+ }
+ }
+ }
+
+ void TexCoordAry::SetSize(u8 num) {
+ if (!mpData) {
+ return;
+ }
+
+ if (num > mCap) {
+ return;
+ }
+
+ // clang-format off
+ static TexCoords texCoords =
+ {
+ math::VEC2(0.0f, 0.0f),
+ math::VEC2(1.0f, 0.0f),
+ math::VEC2(0.0f, 1.0f),
+ math::VEC2(1.0f, 1.0f)
+ };
+ // clang-format on
+
+ for (int j = mNum; j < num; j++) {
+ for (int i = 0; i < (int)ARRAY_SIZE(mpData[j]); i++) {
+ mpData[j][i] = texCoords[i];
+ }
+ }
+
+ mNum = num;
+ }
+
+ void TexCoordAry::Copy(const void* pResTexCoord, u8 texCoordNum) {
+ NW4HBM_ASSERT(161, texCoordNum <= mCap);
+ mNum = ut::Max(mNum, texCoordNum);
+ const TexCoords* src = static_cast<const TexCoords*>(pResTexCoord);
+
+ for (int j = 0; j < texCoordNum; j++) {
+ for (int i = 0; i < (int)ARRAY_SIZE(mpData[j]); i++) {
+ mpData[j][i] = src[j][i];
+ }
+ }
+ }
+
+ bool IsModulateVertexColor(ut::Color* vtxColors, u8 glbAlpha) {
+ if (glbAlpha != 0xff) {
+ return true;
+ }
+
+ if (vtxColors) {
+ if (vtxColors[0] != 0xffffffff || vtxColors[1] != 0xffffffff ||
+ vtxColors[2] != 0xffffffff || vtxColors[3] != 0xffffffff)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ut::Color MultipleAlpha(const ut::Color col, u8 alpha) {
+ ut::Color ret = col;
+
+ if (alpha != 0xff) {
+ ret.a = col.a * alpha / 255;
+ }
+
+ return ret;
+ }
+
+ void MultipleAlpha(ut::Color* dst, const ut::Color* src, u8 alpha) {
+ for (int i = 0; i < 4; i++) {
+ dst[i] = MultipleAlpha(src[i], alpha);
+ }
+ }
+
+ void SetVertexFormat(bool bModulate, u8 texCoordNum) {
+ GXClearVtxDesc();
+
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+
+ if (bModulate) {
+ GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
+ }
+
+ for (int i = 0; i < texCoordNum; i++) {
+ GXSetVtxDesc(static_cast<GXAttr>(GX_VA_TEX0 + i), GX_DIRECT);
+ }
+
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_F32, 0);
+
+ if (bModulate) {
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
+ }
+
+ for (int i = 0; i < texCoordNum; i++) {
+ GXSetVtxAttrFmt(GX_VTXFMT0, static_cast<GXAttr>(GX_VA_TEX0 + i), GX_CLR_RGBA,
+ GX_F32, 0);
+ }
+ }
+
+ static void __deadstrip1();
+ static void __deadstrip1() {
+ // Force instantiation of GXEnd here on debug build
+ GXEnd();
+ }
+
+ void DrawQuad(const math::VEC2& basePt, const Size& size, u8 texCoordNum,
+ const TexCoords* texCoords, const ut::Color* vtxColors) {
+ // start at top left, go clockwise
+
+ // clang-format off
+ GXBegin(GX_QUADS, GX_VTXFMT0, 4);
+
+ GXPosition2f32(basePt.x, basePt.y);
+ if (vtxColors)
+ GXColor1u32(vtxColors[0]);
+ for (int i = 0; i < texCoordNum; i++)
+ GXTexCoord2f32(texCoords[i][0].x, texCoords[i][0].y);
+
+ GXPosition2f32(basePt.x + size.width, basePt.y);
+ if (vtxColors)
+ GXColor1u32(vtxColors[1]);
+ for (int i = 0; i < texCoordNum; i++)
+ GXTexCoord2f32(texCoords[i][1].x, texCoords[i][1].y);
+
+ GXPosition2f32(basePt.x + size.width, basePt.y + size.height);
+ if (vtxColors)
+ GXColor1u32(vtxColors[3]);
+ for (int i = 0; i < texCoordNum; i++)
+ GXTexCoord2f32(texCoords[i][3].x, texCoords[i][3].y);
+
+ GXPosition2f32(basePt.x, basePt.y + size.height);
+ if (vtxColors)
+ GXColor1u32(vtxColors[2]);
+ for (int i = 0; i < texCoordNum; i++)
+ GXTexCoord2f32(texCoords[i][2].x, texCoords[i][2].y);
+
+ GXEnd();
+ // clang-format on
+ }
+
+ void DrawQuad(const math::VEC2& basePt, const Size& size, u8 texCoordNum,
+ const TexCoords* texCoords, const ut::Color* vtxColors, u8 alpha) {
+ ut::Color wkVtxColors[4];
+
+ if (vtxColors) {
+ MultipleAlpha(wkVtxColors, vtxColors, alpha);
+ }
+
+ DrawQuad(basePt, size, texCoordNum, texCoords, vtxColors ? wkVtxColors : NULL);
+ }
+
+ void DrawLine(const math::VEC2& pos, const Size& size, ut::Color color) {
+ GXClearVtxDesc();
+
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
+
+ GXSetNumChans(1);
+ GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+
+ GXSetChanMatColor(GX_COLOR0A0, color);
+ GXSetNumTexGens(0);
+ GXSetNumTevStages(1);
+ GXSetNumIndStages(0);
+
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+ GXSetTevOp(GX_TEVSTAGE0, GX_BLEND);
+ GXSetTevDirect(GX_TEVSTAGE0);
+
+ GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE,
+ GX_CH_ALPHA);
+
+ GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
+ GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
+
+ GXSetLineWidth(6, GX_TO_ZERO);
+
+ // start at top left, go clockwise
+
+ // clang-format off
+ GXBegin(GX_LINESTRIP, GX_VTXFMT0, 5);
+ GXPosition2f32(pos.x , pos.y );
+ GXPosition2f32(pos.x + size.width, pos.y );
+ GXPosition2f32(pos.x + size.width, pos.y + size.height);
+ GXPosition2f32(pos.x , pos.y + size.height);
+ GXPosition2f32(pos.x , pos.y );
+ GXEnd();
+ // clang-format on
+ }
+
+ void InitGXTexObjFromTPL(GXTexObj* to, TPLPalette* pal, u32 id) {
+ // Is there some sort of macro for this
+ if (pal->descriptorArray < (TPLDescriptor*)0x80000000) { // ?
+ TPLBind(pal);
+ }
+
+ TPLDescriptor* tdp = TPLGet(pal, id);
+
+ GXBool mipMap = tdp->textureHeader->minLOD != tdp->textureHeader->maxLOD ? GX_TRUE : GX_FALSE;
+
+ if (tdp->CLUTHeader) {
+ // NOTE: explicit recast of mipMap necessary for debug
+ GXInitTexObjCI(
+ to, tdp->textureHeader->data, tdp->textureHeader->width, tdp->textureHeader->height,
+ static_cast<GXCITexFmt>(tdp->textureHeader->format), tdp->textureHeader->wrapS,
+ tdp->textureHeader->wrapT, static_cast<GXBool>(mipMap), 0);
+
+ GXInitTexObjUserData(to, tdp->CLUTHeader);
+ } else {
+ // NOTE: explicit recast of mipMap necessary for debug
+ GXInitTexObj(
+ to, tdp->textureHeader->data, tdp->textureHeader->width, tdp->textureHeader->height,
+ static_cast<GXTexFmt>(tdp->textureHeader->format), tdp->textureHeader->wrapS,
+ tdp->textureHeader->wrapT, static_cast<GXBool>(mipMap));
+ }
+
+ GXInitTexObjLOD(to, tdp->textureHeader->minFilter, tdp->textureHeader->magFilter,
+ tdp->textureHeader->minLOD, tdp->textureHeader->maxLOD,
+ tdp->textureHeader->LODBias, false, tdp->textureHeader->edgeLODEnable,
+ GX_ANISO_1);
+ }
+
+ } // namespace detail
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp
new file mode 100644
index 0000000000..a9e8932053
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_drawInfo.cpp
@@ -0,0 +1,17 @@
+#include "drawInfo.h"
+
+#include <cstring.h>
+
+namespace nw4hbm {
+ namespace lyt {
+
+ DrawInfo::DrawInfo() : mLocationAdjustScale(1.0f, 1.0f), mGlobalAlpha(1.0f) {
+ std::memset(&mFlag, 0, sizeof(mFlag));
+
+ math::MTX34Identity(&mViewMtx);
+ }
+
+ DrawInfo::~DrawInfo() {}
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp
new file mode 100644
index 0000000000..727498c328
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_group.cpp
@@ -0,0 +1,86 @@
+#include "group.h"
+
+#include "common.h"
+#include "layout.h"
+
+#include <cstring.h>
+#include <new.h>
+
+
+namespace nw4hbm {
+ namespace lyt {
+
+ Group::Group() {}
+
+ Group::Group(const res::Group* pResGroup, Pane* pRootPane) {
+ Init();
+ std::memcpy(mName, pResGroup->name, sizeof(mName));
+
+ const char* paneName = detail::ConvertOffsToPtr<char>(pResGroup, sizeof(*pResGroup));
+
+ for (int i = 0; i < pResGroup->paneNum; i++) {
+ Pane* pFindPane =
+ pRootPane->FindPaneByName(paneName + (int)sizeof(pResGroup)->name * i, true);
+
+ if (pFindPane) {
+ AppendPane(pFindPane);
+ }
+ }
+ }
+
+ void Group::Init() {
+ mbUserAllocated = false;
+ }
+
+ Group::~Group() {
+ for (PaneLinkList::Iterator it = mPaneLinkList.GetBeginIter();
+ it != mPaneLinkList.GetEndIter();)
+ {
+ PaneLinkList::Iterator currIt = it++;
+
+ mPaneLinkList.Erase(currIt);
+ Layout::FreeMemory(&*currIt);
+ }
+ }
+
+ void Group::AppendPane(Pane* pPane) {
+ if (void* pMem = Layout::AllocMemory(sizeof(detail::PaneLink))) {
+ detail::PaneLink* pPaneLink = new (pMem) detail::PaneLink();
+
+ pPaneLink->mTarget = pPane;
+ mPaneLinkList.PushBack(pPaneLink);
+ }
+ }
+
+ GroupContainer::~GroupContainer() {
+ for (GroupList::Iterator it = mGroupList.GetBeginIter(); it != mGroupList.GetEndIter();)
+ {
+ GroupList::Iterator currIt = it++;
+
+ mGroupList.Erase(currIt);
+
+ if (!currIt->IsUserAllocated()) {
+ currIt->~Group();
+ Layout::FreeMemory(&*currIt);
+ }
+ }
+ }
+
+ void GroupContainer::AppendGroup(Group* pGroup) {
+ mGroupList.PushBack(pGroup);
+ }
+
+ Group* GroupContainer::FindGroupByName(const char* findName) {
+ for (GroupList::Iterator it = mGroupList.GetBeginIter(); it != mGroupList.GetEndIter();
+ it++)
+ {
+ if (detail::EqualsPaneName(it->GetName(), findName)) {
+ return &(*it);
+ }
+ }
+
+ return NULL;
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp
new file mode 100644
index 0000000000..1fe41d34e9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_layout.cpp
@@ -0,0 +1,368 @@
+#include "layout.h"
+
+#include "bounding.h"
+#include "picture.h"
+#include "textBox.h"
+#include "window.h"
+
+#include <new.h>
+
+#define CONVERT_OFFSET_TO_PTR(type_, ptr_, offset_) \
+ reinterpret_cast<type_*>(reinterpret_cast<u32>(ptr_) + offset_)
+
+namespace {
+ // pretend this is nw4hbm::lyt
+ using namespace nw4hbm;
+ using namespace nw4hbm::lyt;
+
+ void SetTagProcessorImpl(Pane* pPane, ut::TagProcessorBase<wchar_t>* pTagProcesssor);
+
+ // wait until these guys hear about c++11
+ template <class T>
+ T* CreateObject() {
+ void* pMem = Layout::AllocMemory(sizeof(T));
+
+ if (pMem) {
+ return new (pMem) T();
+ } else {
+ NW4R_DB_WARNING(47, false, "can't alloc memory.");
+ return NULL;
+ }
+ }
+
+ template <class T, typename Param1>
+ T* CreateObject(Param1 p1) {
+ void* pMem = Layout::AllocMemory(sizeof(T));
+
+ if (pMem) {
+ return new (pMem) T(p1);
+ } else {
+ return NULL;
+ }
+ }
+
+ template <class T, typename Param1, typename Param2>
+ T* CreateObject(Param1 p1, Param2 p2) {
+ void* pMem = Layout::AllocMemory(sizeof(T));
+
+ if (pMem) {
+ return new (pMem) T(p1, p2);
+ } else {
+ return NULL;
+ }
+ }
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ MEMAllocator* Layout::mspAllocator;
+ } // namespace lyt
+} // namespace nw4hbm
+
+namespace {
+
+ void SetTagProcessorImpl(Pane* pPane, ut::TagProcessorBase<wchar_t>* pTagProcessor) {
+ if (TextBox* pTextBox = ut::DynamicCast<TextBox*>(pPane)) {
+ pTextBox->SetTagProcessor(pTagProcessor);
+ }
+
+ for (PaneList::Iterator it = pPane->GetChildList().GetBeginIter();
+ it != pPane->GetChildList().GetEndIter(); ++it)
+ {
+ SetTagProcessorImpl(&(*it), pTagProcessor);
+ }
+ }
+
+} // anonymous namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ Layout::Layout()
+ : mpRootPane(NULL), mpGroupContainer(NULL), mLayoutSize(0.0f, 0.0f), mOriginType(0) {}
+
+ Layout::~Layout() {
+ if (mpGroupContainer) {
+ mpGroupContainer->~GroupContainer();
+ FreeMemory(mpGroupContainer);
+ }
+
+ if (mpRootPane && !mpRootPane->IsUserAllocated()) {
+ mpRootPane->~Pane();
+ FreeMemory(mpRootPane);
+ }
+
+ for (AnimTransformList::Iterator it = mAnimTransList.GetBeginIter();
+ it != mAnimTransList.GetEndIter();)
+ {
+ AnimTransformList::Iterator currIt = it++;
+
+ mAnimTransList.Erase(currIt);
+ currIt->~AnimTransform();
+ FreeMemory(&*currIt);
+ }
+ }
+
+ bool Layout::Build(const void* lytResBuf, ResourceAccessor* pResAcsr) {
+ NW4HBM_ASSERT_CHECK_NULL(171, mspAllocator);
+ NW4HBM_ASSERT_CHECK_NULL(172, lytResBuf);
+
+ const res::BinaryFileHeader* fileHead =
+ static_cast<const res::BinaryFileHeader*>(lytResBuf);
+
+ if (!detail::TestFileHeader(*fileHead, res::FILE_HEADER_SIGNATURE_LAYOUT)) {
+ return false;
+ }
+
+ if (fileHead->version != 8) {
+ NW4R_DB_ASSERTMSG(187, false, "Version check faild ('%d.%d' must be '%d.%d').",
+ (fileHead->version >> 8) & 0xFF, fileHead->version & 0xFF, 0, 8);
+ }
+
+ ResBlockSet resBlockSet = {};
+ resBlockSet.pResAccessor = pResAcsr;
+
+ Pane* pParentPane = NULL;
+ Pane* pLastPane = NULL;
+ bool bReadRootGroup = false;
+ int groupNestLevel = 0;
+ void* dataPtr = CONVERT_OFFSET_TO_PTR(void, lytResBuf, fileHead->headerSize);
+
+ for (int i = 0; i < fileHead->dataBlocks; i++) {
+ res::DataBlockHeader* pDataBlockHead = static_cast<res::DataBlockHeader*>(dataPtr);
+
+ switch (detail::GetSignatureInt(pDataBlockHead->kind)) {
+ case 'lyt1': {
+ res::Layout* pResLyt = static_cast<res::Layout*>(dataPtr);
+
+ mOriginType = pResLyt->originType != 0; // ?
+ mLayoutSize = pResLyt->layoutSize;
+ }
+
+ break;
+
+ case 'txl1':
+ resBlockSet.pTextureList = static_cast<res::TextureList*>(dataPtr);
+ break;
+
+ case 'fnl1':
+ resBlockSet.pFontList = static_cast<res::FontList*>(dataPtr);
+ break;
+
+ case 'mat1':
+ resBlockSet.pMaterialList = static_cast<res::MaterialList*>(dataPtr);
+ break;
+
+ case 'pan1':
+ case 'bnd1':
+ case 'pic1':
+ case 'txt1':
+ case 'wnd1':
+ if (Pane* pPane = BuildPaneObj(detail::GetSignatureInt(pDataBlockHead->kind),
+ dataPtr, resBlockSet))
+ {
+ if (!mpRootPane) {
+ mpRootPane = pPane;
+ }
+
+ if (pParentPane) {
+ pParentPane->AppendChild(pPane);
+ }
+
+ pLastPane = pPane;
+ }
+
+ break;
+
+ case 'pas1': // pane start?
+ NW4HBM_ASSERT_CHECK_NULL(249, pLastPane);
+ pParentPane = pLastPane;
+ break;
+
+ case 'pae1': // pane end?
+ pLastPane = pParentPane;
+ pParentPane = pLastPane->GetParent();
+ break;
+
+ case 'grp1':
+ if (!bReadRootGroup) {
+ bReadRootGroup = true;
+ mpGroupContainer = CreateObject<GroupContainer>();
+ } else if (mpGroupContainer && groupNestLevel == 1) {
+ if (Group* pGroup = CreateObject<Group>(
+ reinterpret_cast<const res::Group*>(pDataBlockHead), mpRootPane))
+ {
+ mpGroupContainer->AppendGroup(pGroup);
+ }
+ }
+ break;
+
+ case 'grs1': // group start?
+ groupNestLevel++;
+ break;
+
+ case 'gre1': // group end?
+ groupNestLevel--;
+ break;
+ }
+
+ dataPtr = CONVERT_OFFSET_TO_PTR(void, dataPtr, pDataBlockHead->size);
+ }
+
+ return true;
+ }
+
+ AnimTransform* Layout::CreateAnimTransform(const void* anmResBuf,
+ ResourceAccessor* pResAcsr) {
+ NW4HBM_ASSERT_CHECK_NULL(295, mspAllocator);
+ NW4HBM_ASSERT_CHECK_NULL(296, anmResBuf);
+
+ const res::BinaryFileHeader* pFileHead =
+ static_cast<const res::BinaryFileHeader*>(anmResBuf);
+
+ if (!detail::TestFileHeader(*pFileHead)) {
+ return NULL;
+ }
+
+ if (pFileHead->version != 8) {
+ NW4R_DB_ASSERTMSG(311, false, "Version check faild ('%d.%d' must be '%d.%d').",
+ (pFileHead->version >> 8) & 0xFF, pFileHead->version & 0xFF, 0,
+ 8);
+ }
+
+ const res::AnimationBlock* pInfoBlock = NULL;
+ const res::DataBlockHeader* pDataBlockHead =
+ detail::ConvertOffsToPtr<res::DataBlockHeader>(pFileHead, pFileHead->headerSize);
+
+ AnimTransform* ret = NULL;
+
+ for (int i = 0; i < pFileHead->dataBlocks; i++) {
+ switch (detail::GetSignatureInt(pDataBlockHead->kind)) {
+ case 'pai1': // painting? idk
+ NW4HBM_ASSERT(321, ret == 0);
+
+ switch (detail::GetSignatureInt(pFileHead->signature)) {
+ case 'RLAN':
+ case res::AnimationInfo::ANIM_INFO_PANE_PAIN_SRT:
+ case res::AnimationInfo::ANIM_INFO_PANE_VISIBILITY:
+ case res::AnimationInfo::ANIM_INFO_PANE_VERTEX_COLOR:
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_COLOR:
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_TEXTURE_SRT:
+ case res::AnimationInfo::ANIM_INFO_MATERIAL_TEXTURE_PATTERN:
+ if (AnimTransformBasic* pAnimTrans = CreateObject<AnimTransformBasic>()) {
+ pInfoBlock =
+ reinterpret_cast<const res::AnimationBlock*>(pDataBlockHead);
+
+ pAnimTrans->SetResource(pInfoBlock, pResAcsr);
+ ret = pAnimTrans;
+ }
+ }
+
+ if (ret) {
+ mAnimTransList.PushBack(ret);
+ }
+ }
+
+ pDataBlockHead = detail::ConvertOffsToPtr<res::DataBlockHeader>(
+ pDataBlockHead, pDataBlockHead->size);
+ }
+
+ return ret;
+ }
+
+ void Layout::BindAnimation(AnimTransform* pAnimTrans) {
+ if (mpRootPane) {
+ mpRootPane->BindAnimation(pAnimTrans, true);
+ }
+ }
+
+ void Layout::UnbindAnimation(AnimTransform* pAnimTrans) {
+ if (mpRootPane) {
+ mpRootPane->UnbindAnimation(pAnimTrans, true);
+ }
+ }
+
+ void Layout::UnbindAllAnimation() {
+ UnbindAnimation(NULL);
+ }
+
+ void Layout::SetAnimationEnable(AnimTransform* pAnimTrans, bool bEnable) {
+ if (mpRootPane) {
+ mpRootPane->SetAnimationEnable(pAnimTrans, bEnable, true);
+ }
+ }
+
+ void Layout::CalculateMtx(const DrawInfo& drawInfo) {
+ if (mpRootPane) {
+ mpRootPane->CalculateMtx(drawInfo);
+ }
+ }
+
+ void Layout::Draw(const DrawInfo& drawInfo) {
+ if (mpRootPane) {
+ mpRootPane->Draw(drawInfo);
+ }
+ }
+
+ void Layout::Animate(u32 option) {
+ if (mpRootPane) {
+ mpRootPane->Animate(option);
+ }
+ }
+
+ const ut::Rect Layout::GetLayoutRect() const {
+ if (mOriginType == 1) {
+ return ut::Rect(-mLayoutSize.width / 2.0f, mLayoutSize.height / 2.0f,
+ mLayoutSize.width / 2.0f, -mLayoutSize.height / 2.0f);
+ } else {
+ return ut::Rect(0.0f, 0.0f, mLayoutSize.width, mLayoutSize.height);
+ }
+ }
+
+ void Layout::SetTagProcessor(ut::TagProcessorBase<wchar_t>* pTagProcessor) {
+ SetTagProcessorImpl(mpRootPane, pTagProcessor);
+ }
+
+ Pane* Layout::BuildPaneObj(s32 kind, const void* dataPtr, const ResBlockSet& resBlockSet) {
+ switch (kind) {
+ case res::OBJECT_SIGNATURE_PANE: {
+ const res::Pane* pResPane = static_cast<const res::Pane*>(dataPtr);
+
+ return CreateObject<Pane>(pResPane);
+ }
+
+ case res::OBJECT_SIGNATURE_PICTURE: {
+ const res::Picture* pResPic = static_cast<const res::Picture*>(dataPtr);
+
+ return CreateObject<Picture>(pResPic, resBlockSet);
+ }
+
+ case res::OBJECT_SIGNATURE_TEXT_BOX: {
+ // block?
+ const res::TextBox* pBlock = static_cast<const res::TextBox*>(dataPtr);
+
+ return CreateObject<TextBox>(pBlock, resBlockSet);
+ }
+
+ case res::OBJECT_SIGNATURE_WINDOW: {
+ // block?
+ const res::Window* pBlock = static_cast<const res::Window*>(dataPtr);
+
+ return CreateObject<Window>(pBlock, resBlockSet);
+ }
+
+ case res::OBJECT_SIGNATURE_BOUNDING: {
+ const res::Bounding* pResBounding = static_cast<const res::Bounding*>(dataPtr);
+
+ return CreateObject<Bounding>(pResBounding, resBlockSet);
+ }
+
+ default:
+ NW4R_DB_ASSERTMSG(503, false, "unknown data type");
+ return NULL;
+ }
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp
new file mode 100644
index 0000000000..ce5f620b29
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_material.cpp
@@ -0,0 +1,1198 @@
+#include "common.h"
+#include "layout.h"
+#include "material.h"
+
+#include "../math/triangular.h"
+
+#include <revolution/tpl.h>
+
+#include <cstring.h>
+
+static const GXColorS10 DefaultBlackColor = {0, 0, 0, 0};
+
+static inline void SetDefaultWhiteColor(GXColorS10* p) {
+ p->r = 255;
+ p->g = 255;
+ p->b = 255;
+ p->a = 255;
+}
+
+static inline bool IsDefaultWhiteColor(GXColorS10* p) {
+ return p->r == 255 && p->g == 255 && p->b == 255 && p->a == 255;
+}
+
+namespace {
+ using namespace nw4hbm;
+ using namespace nw4hbm::lyt;
+
+ bool operator==(const GXColorS10& a, const GXColorS10& b) {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+ }
+
+ bool operator!=(const GXColorS10& a, const GXColorS10& b) {
+ return !(a == b);
+ }
+
+ inline u32 GetTexMtx(u32 texMtxIdx) {
+ return texMtxIdx * 3 + GX_TEXMTX0;
+ }
+
+ inline u32 GetTexMtxIdx(u32 texMtx) {
+ return (texMtx - GX_TEXMTX0) / 3;
+ }
+
+ void CalcTextureMtx(math::MTX34* pMtx, const TexSRT& texSRT) {
+ math::VEC2 center(0.5f, 0.5f);
+
+ f32 cosR = math::CosDeg(texSRT.rotate);
+ f32 sinR = math::SinDeg(texSRT.rotate);
+
+ f32 a0 = cosR * texSRT.scale.x, a1 = -sinR * texSRT.scale.y;
+ pMtx->mtx[0][0] = a0;
+ pMtx->mtx[0][1] = a1;
+ pMtx->mtx[0][2] = 0.0f;
+ pMtx->mtx[0][3] = texSRT.translate.x + center.x + a0 * -center.x + a1 * -center.y;
+
+ a0 = sinR * texSRT.scale.x;
+ a1 = cosR * texSRT.scale.y;
+ pMtx->mtx[1][0] = a0;
+ pMtx->mtx[1][1] = a1;
+ pMtx->mtx[1][2] = 0.0f;
+ pMtx->mtx[1][3] = texSRT.translate.y + center.y + a0 * -center.x + a1 * -center.y;
+
+ pMtx->mtx[2][0] = 0.0f;
+ pMtx->mtx[2][1] = 0.0f;
+ pMtx->mtx[2][2] = 1.0f;
+ pMtx->mtx[2][3] = 0.0f;
+ }
+
+ void CalcIndTexMtx(Mtx23 mtx, const TexSRT& texSRT) {
+ f32 cosR = math::CosDeg(texSRT.rotate);
+ f32 sinR = math::SinDeg(texSRT.rotate);
+
+ mtx[0][0] = cosR * texSRT.scale.x;
+ mtx[0][1] = -sinR * texSRT.scale.y;
+ mtx[0][2] = texSRT.translate.x;
+
+ mtx[1][0] = sinR * texSRT.scale.x;
+ mtx[1][1] = cosR * texSRT.scale.y;
+ mtx[1][2] = texSRT.translate.y;
+ }
+
+ void SetColorComponentValue(ut::Color* pCol, u32 compIdx, s16 value) {
+ const u8 u8Val = ut::Min<s16>(ut::Max<s16>(value, 0), 0xFF);
+
+ switch (compIdx) {
+ case 0: {
+ pCol->r = u8Val;
+ break;
+ }
+ case 1: {
+ pCol->g = u8Val;
+ break;
+ }
+ case 2: {
+ pCol->b = u8Val;
+ break;
+ }
+ case 3: {
+ pCol->a = u8Val;
+ break;
+ }
+ }
+ }
+
+ void SetIndTexMtx(GXIndTexMtxID id, const Mtx23 mtx) {
+ f32 m00, m01, m02;
+ f32 m10, m11, m12;
+
+ f32 a00, a01, a02;
+ f32 a10, a11, a12;
+
+ s8 scaleExp = 0;
+
+ m00 = mtx[0][0];
+ m01 = mtx[0][1];
+ m02 = mtx[0][2];
+ m10 = mtx[1][0];
+ m11 = mtx[1][1];
+ m12 = mtx[1][2];
+
+ a00 = math::FAbs(m00);
+ a01 = math::FAbs(m01);
+ a02 = math::FAbs(m02);
+ a10 = math::FAbs(m10);
+ a11 = math::FAbs(m11);
+ a12 = math::FAbs(m12);
+
+ if (a00 >= 1.0f || a01 >= 1.0f || a02 >= 1.0f || a10 >= 1.0f || a11 >= 1.0f || a12 >= 1.0f)
+ {
+ do {
+ if (scaleExp >= 46) {
+ break;
+ }
+
+ scaleExp++;
+
+ m00 /= 2.0f;
+ m01 /= 2.0f;
+ m02 /= 2.0f;
+ m10 /= 2.0f;
+ m11 /= 2.0f;
+ m12 /= 2.0f;
+
+ a00 /= 2.0f;
+ a01 /= 2.0f;
+ a02 /= 2.0f;
+ a10 /= 2.0f;
+ a11 /= 2.0f;
+ a12 /= 2.0f;
+
+ } while (a00 >= 1.0f || a01 >= 1.0f || a02 >= 1.0f || a10 >= 1.0f || a11 >= 1.0f ||
+ a12 >= 1.0f);
+ } else if (a00 < 0.5f && a01 < 0.5f && a02 < 0.5f && a10 < 0.5f && a11 < 0.5f && a12 < 0.5f)
+ {
+ do {
+ scaleExp--;
+
+ m00 *= 2.0f;
+ m01 *= 2.0f;
+ m02 *= 2.0f;
+ m10 *= 2.0f;
+ m11 *= 2.0f;
+ m12 *= 2.0f;
+
+ a00 *= 2.0f;
+ a01 *= 2.0f;
+ a02 *= 2.0f;
+ a10 *= 2.0f;
+ a11 *= 2.0f;
+ a12 *= 2.0f;
+ } while (a00 < 0.5f && a01 < 0.5f && a02 < 0.5f && a10 < 0.5f && a11 < 0.5f &&
+ a12 < 0.5f && scaleExp > -17);
+ }
+
+ Mtx23 outMtx = {{m00, m01, m02}, {m10, m11, m12}};
+
+ GXSetIndTexMtx(id, outMtx, scaleExp);
+ }
+
+ void InitTexSRT(TexSRT* texSRTs, u32 num) {
+ for (u32 i = 0; i < num; i++) {
+ texSRTs[i].translate = math::VEC2(0.0f, 0.0f);
+ texSRTs[i].rotate = 0.0f;
+ texSRTs[i].scale = math::VEC2(1.0f, 1.0f);
+ }
+ }
+
+ u32 CalcOffsetTexSRTAry(const detail::BitGXNums& bitGXNums) {
+ return (sizeof(res::TexMap) * 8) * bitGXNums.texMap;
+ }
+
+ u32 CalcOffsetTexCoordGenAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetTexSRTAry(bitGXNums) + sizeof(TexSRT) * bitGXNums.texSRT;
+ }
+
+ u32 CalcOffsetChanCtrlAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetTexCoordGenAry(bitGXNums) + sizeof(TexCoordGen) * bitGXNums.texCoordGen;
+ }
+
+ u32 CalcOffsetMatColAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetChanCtrlAry(bitGXNums) + sizeof(ChanCtrl) * bitGXNums.chanCtrl;
+ }
+
+ u32 CalcOffsetTevSwapAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetMatColAry(bitGXNums) + sizeof(ut::Color) * bitGXNums.matCol;
+ }
+
+ u32 CalcOffsetGetAlphaCompare(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetTevSwapAry(bitGXNums) + (sizeof(TevSwapMode) + 3) * bitGXNums.tevSwap;
+ }
+
+ u32 CalcOffsetBlendMode(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetGetAlphaCompare(bitGXNums) + sizeof(AlphaCompare) * bitGXNums.alpComp;
+ }
+
+ u32 CalcOffsetIndirectStageAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetBlendMode(bitGXNums) + sizeof(BlendMode) * bitGXNums.blendMode;
+ }
+
+ u32 CalcOffsetIndTexSRTAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetIndirectStageAry(bitGXNums) + sizeof(IndirectStage) * bitGXNums.indStage;
+ }
+
+ u32 CalcOffsetTevStageAry(const detail::BitGXNums& bitGXNums) {
+ return CalcOffsetIndTexSRTAry(bitGXNums) + (sizeof(TevStage) + 4) * bitGXNums.indSRT;
+ }
+
+ void CopyGXTexObj(GXTexObj* pDst, const GXTexObj* pSrc) {
+ *pDst = *pSrc;
+ GXInitTexObjUserData(pDst, NULL);
+ }
+} // namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ Material::Material() {
+ Init();
+ memset(mName, 0, sizeof(mName));
+ }
+
+ Material::Material(const res::Material* pRes, const ResBlockSet& resBlockSet)
+ : mAnimList() {
+ Init();
+ SetName(pRes->name);
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mTevCols); i++) {
+ mTevCols[i] = pRes->tevCols[i];
+ }
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mTevKCols); i++) {
+ mTevKCols[i] = pRes->tevKCols[i];
+ }
+
+ u32 resOffs = sizeof(res::Material);
+
+ const res::TexMap* const pResTexMap =
+ detail::ConvertOffsToPtr<res::TexMap>(pRes, resOffs);
+ resOffs += pRes->resNum.GetTexMapNum() * sizeof(res::TexMap);
+
+ const TexSRT* const resTexSRTs = detail::ConvertOffsToPtr<TexSRT>(pRes, resOffs);
+ resOffs += pRes->resNum.GetTexSRTNum() * sizeof(TexSRT);
+
+ const TexCoordGen* const resTexCoordGens =
+ detail::ConvertOffsToPtr<TexCoordGen>(pRes, resOffs);
+ resOffs += pRes->resNum.GetTexCoordGenNum() * sizeof(TexCoordGen);
+
+ u8 texMapNum = ut::Min<u8>(pRes->resNum.GetTexMapNum(), GX_MAX_TEXMAP);
+ u8 texSRTNum = ut::Min<u8>(pRes->resNum.GetTexSRTNum(), MAX_TEX_SRT);
+ u8 texCoordGenNum = ut::Min<u8>(pRes->resNum.GetTexCoordGenNum(), GX_MAX_TEXCOORD);
+
+ bool allocChanCtrl = pRes->resNum.GetChanCtrlNum() != 0;
+ bool allocMatCol = pRes->resNum.GetMatColNum() != 0;
+ bool allocTevSwap = pRes->resNum.HasTevSwapTable();
+ bool allocAlpComp = pRes->resNum.HasAlphaCompare();
+ bool allocBlendMode = pRes->resNum.HasBlendMode();
+
+ u8 indTexSRTNum = ut::Min<u8>(pRes->resNum.GetIndTexSRTNum(), MAX_IND_SRT);
+ u8 indStageNum = ut::Min<u8>(pRes->resNum.GetIndTexStageNum(), GX_MAX_INDTEXSTAGE);
+ u8 tevStageNum = ut::Min<u8>(pRes->resNum.GetTevStageNum(), GX_MAX_TEVSTAGE);
+
+ ReserveGXMem(texMapNum, texSRTNum, texCoordGenNum, tevStageNum, allocTevSwap,
+ indStageNum, indTexSRTNum, allocChanCtrl, allocMatCol, allocAlpComp,
+ allocBlendMode);
+
+ if (mpGXMem != NULL) {
+ SetTextureNum(texMapNum);
+ if (texMapNum) {
+ NW4HBM_ASSERT_CHECK_NULL(469, resBlockSet.pTextureList);
+
+ const res::Texture* const textures = detail::ConvertOffsToPtr<res::Texture>(
+ resBlockSet.pTextureList, sizeof(*resBlockSet.pTextureList));
+
+ GXTexObj* const texMaps = GetTexMapAry();
+
+ for (u8 i = 0; i < mGXMemNum.texMap; i++) {
+ NW4HBM_ASSERT(475, pResTexMap[i].texIdx < resBlockSet.pTextureList->texNum);
+
+ const char* fileName = detail::ConvertOffsToPtr<char>(
+ textures, textures[pResTexMap[i].texIdx].nameStrOffset);
+
+ void* pTplRes = resBlockSet.pResAccessor->GetResource(RESOURCE_TYPE_TEXTURE,
+ fileName, NULL);
+
+ SetTexture(i, static_cast<TPLPalette*>(pTplRes));
+ GXInitTexObjWrapMode(&texMaps[i],
+ static_cast<GXTexWrapMode>(pResTexMap[i].wrapS),
+ static_cast<GXTexWrapMode>(pResTexMap[i].wrapT));
+ }
+ }
+
+ TexSRT* texSRTs = GetTexSRTAry();
+ NW4HBM_ASSERT(486, texSRTNum <= mGXMemNum.texSRT);
+
+ for (int i = 0; i < texSRTNum; i++) {
+ texSRTs[i].translate = resTexSRTs[i].translate;
+ texSRTs[i].rotate = resTexSRTs[i].rotate;
+ texSRTs[i].scale = resTexSRTs[i].scale;
+ }
+
+ TexCoordGen* texCoordGens = GetTexCoordGenAry();
+ SetTexCoordGenNum(texCoordGenNum);
+ for (int i = 0; i < mGXMemNum.texCoordGen; i++) {
+ texCoordGens[i] = resTexCoordGens[i];
+ }
+
+ if (allocChanCtrl) {
+ NW4HBM_ASSERT(504, IsChanCtrlCap());
+ const ChanCtrl* pResChanCtrl =
+ detail::ConvertOffsToPtr<ChanCtrl>(pRes, resOffs);
+ *GetChanCtrlAry() = *pResChanCtrl;
+ resOffs += sizeof(ChanCtrl);
+ }
+
+ if (allocMatCol) {
+ NW4HBM_ASSERT(512, IsMatColorCap());
+ const ut::Color* pResMatCol =
+ detail::ConvertOffsToPtr<ut::Color>(pRes, resOffs);
+ *GetMatColAry() = *pResMatCol;
+ resOffs += sizeof(ut::Color);
+ }
+
+ if (allocTevSwap) {
+ NW4HBM_ASSERT(520, IsTevSwapCap());
+ const TevSwapMode* pResTevSwap =
+ detail::ConvertOffsToPtr<TevSwapMode>(pRes, resOffs);
+ TevSwapMode* tevSwaps = GetTevSwapAry();
+ for (int i = 0; i < GX_MAX_TEVSWAP; i++) {
+ tevSwaps[i] = pResTevSwap[i];
+ }
+ resOffs += GX_MAX_TEVSWAP * sizeof(TevSwapMode);
+ }
+
+ if (indTexSRTNum) {
+ NW4HBM_ASSERT(532, indTexSRTNum <= mGXMemNum.indSRT);
+ TexSRT* indTexSRTs = GetIndTexSRTAry();
+ const TexSRT* pResIndMtx = detail::ConvertOffsToPtr<TexSRT>(pRes, resOffs);
+ for (int i = 0; i < indTexSRTNum; i++) {
+ indTexSRTs[i] = pResIndMtx[i];
+ }
+ }
+ resOffs += sizeof(TexSRT) * pRes->resNum.GetIndTexSRTNum();
+
+ if (indStageNum) {
+ SetIndStageNum(indStageNum);
+
+ IndirectStage* indirectStages = GetIndirectStageAry();
+ const IndirectStage* pResIndStg =
+ detail::ConvertOffsToPtr<IndirectStage>(pRes, resOffs);
+ for (int i = 0; i < indStageNum; i++) {
+ indirectStages[i] = pResIndStg[i];
+ }
+ }
+ resOffs += sizeof(IndirectStage) * pRes->resNum.GetIndTexStageNum();
+
+ if (tevStageNum) {
+ SetTevStageNum(tevStageNum);
+
+ TevStage* tevStages = GetTevStageAry();
+ const TevStage* pResTevStg = detail::ConvertOffsToPtr<TevStage>(pRes, resOffs);
+ for (int i = 0; i < tevStageNum; i++) {
+ tevStages[i] = pResTevStg[i];
+ }
+ }
+ resOffs += sizeof(TevStage) * pRes->resNum.GetTevStageNum();
+
+ if (allocAlpComp) {
+ NW4HBM_ASSERT(568, IsAlphaCompareCap());
+ const AlphaCompare* pResAlphaCompare =
+ detail::ConvertOffsToPtr<AlphaCompare>(pRes, resOffs);
+ *GetAlphaComparePtr() = *pResAlphaCompare;
+ resOffs += sizeof(AlphaCompare);
+ }
+
+ if (allocBlendMode) {
+ NW4HBM_ASSERT(576, IsBlendModeCap());
+ const BlendMode* pResBlendMode =
+ detail::ConvertOffsToPtr<BlendMode>(pRes, resOffs);
+ *GetBlendModePtr() = *pResBlendMode;
+ resOffs += sizeof(BlendMode);
+ }
+ }
+ }
+
+ void Material::Init() {
+ mTevCols[TEVCOLOR_REG0] = DefaultBlackColor;
+ SetDefaultWhiteColor(&mTevCols[TEVCOLOR_REG1]);
+ SetDefaultWhiteColor(&mTevCols[TEVCOLOR_REG2]);
+
+ InitBitGXNums(&mGXMemCap);
+ InitBitGXNums(&mGXMemNum);
+
+ mbUserAllocated = false;
+ mpGXMem = NULL;
+ }
+
+ Material::~Material() {
+ UnbindAllAnimation();
+
+ if (mpGXMem != NULL) {
+ Layout::FreeMemory(mpGXMem);
+ mpGXMem = NULL;
+ }
+ }
+
+ void Material::InitBitGXNums(detail::BitGXNums* ptr) {
+ ptr->texMap = 0;
+ ptr->texSRT = 0;
+ ptr->texCoordGen = 0;
+ ptr->indSRT = 0;
+ ptr->indStage = 0;
+ ptr->tevSwap = false;
+ ptr->tevStage = 0;
+ ptr->chanCtrl = false;
+ ptr->matCol = false;
+ ptr->alpComp = false;
+ ptr->blendMode = 0;
+ }
+
+ void Material::ReserveGXMem(u8 texMapNum, u8 texSRTNum, u8 texCoordGenNum, u8 tevStageNum,
+ bool allocTevSwap, u8 indStageNum, u8 indSRTNum,
+ bool allocChanCtrl, bool allocMatCol, bool allocAlpComp,
+ bool allocBlendMode) {
+ NW4HBM_ASSERT(661, texMapNum <= GX_MAX_TEXMAP);
+ NW4HBM_ASSERT(662, texSRTNum <= TexMtxMax);
+ NW4HBM_ASSERT(663, texCoordGenNum <= GX_MAX_TEXCOORD);
+ NW4HBM_ASSERT(664, tevStageNum <= GX_MAX_TEVSTAGE);
+ NW4HBM_ASSERT(665, indStageNum <= GX_MAX_INDTEXSTAGE);
+ NW4HBM_ASSERT(666, indSRTNum <= IndTexMtxMax);
+
+ int tevSwapNum = allocTevSwap ? 1 : 0;
+ int chanCtrlNum = allocChanCtrl ? 1 : 0;
+ int matColNum = allocMatCol ? 1 : 0;
+ int alpCompNum = allocAlpComp ? 1 : 0;
+ int blendModeNum = allocBlendMode ? 1 : 0;
+
+ if (mGXMemCap.texMap >= texMapNum && mGXMemCap.texSRT >= texSRTNum &&
+ mGXMemCap.texCoordGen >= texCoordGenNum && mGXMemCap.tevStage >= tevStageNum &&
+ mGXMemCap.tevSwap >= tevSwapNum && mGXMemCap.indStage >= indStageNum &&
+ mGXMemCap.indSRT >= indSRTNum && mGXMemCap.chanCtrl >= chanCtrlNum &&
+ mGXMemCap.matCol >= matColNum && mGXMemCap.alpComp >= alpCompNum &&
+ mGXMemCap.blendMode >= blendModeNum)
+ {
+ return;
+ }
+
+ if (mpGXMem != NULL) {
+ Layout::FreeMemory(mpGXMem);
+ mpGXMem = NULL;
+ InitBitGXNums(&mGXMemCap);
+ InitBitGXNums(&mGXMemNum);
+ }
+
+ mpGXMem = Layout::AllocMemory(
+ 4 * (matColNum + chanCtrlNum + texCoordGenNum + tevSwapNum + alpCompNum +
+ blendModeNum + indStageNum) +
+ ((int)sizeof(TexSRT) * texSRTNum) + ((int)sizeof(GXTexObj) * texMapNum) +
+ ((int)sizeof(TexSRT) * indSRTNum) + ((int)sizeof(TevStage) * tevStageNum));
+
+ if (mpGXMem == NULL) {
+ return;
+ }
+
+ mGXMemCap.texMap = texMapNum;
+ mGXMemCap.texSRT = texSRTNum;
+ mGXMemCap.texCoordGen = texCoordGenNum;
+ mGXMemCap.indSRT = indSRTNum;
+ mGXMemCap.indStage = indStageNum;
+ mGXMemCap.tevSwap = tevSwapNum;
+ mGXMemCap.tevStage = tevStageNum;
+ mGXMemCap.chanCtrl = chanCtrlNum;
+ mGXMemCap.matCol = matColNum;
+ mGXMemCap.alpComp = alpCompNum;
+ mGXMemCap.blendMode = blendModeNum;
+
+ mGXMemNum.texSRT = mGXMemCap.texSRT;
+ InitTexSRT(GetTexSRTAry(), mGXMemNum.texSRT);
+
+ mGXMemNum.indSRT = mGXMemCap.indSRT;
+ InitTexSRT(GetIndTexSRTAry(), mGXMemNum.indSRT);
+
+ mGXMemNum.chanCtrl = mGXMemCap.chanCtrl;
+ if (mGXMemNum.chanCtrl) {
+ *GetChanCtrlAry() = ChanCtrl();
+ }
+
+ mGXMemNum.matCol = mGXMemCap.matCol;
+ if (mGXMemNum.matCol) {
+ *GetMatColAry() = ut::Color::WHITE;
+ }
+
+ mGXMemNum.tevSwap = mGXMemCap.tevSwap;
+ if (mGXMemNum.tevSwap) {
+ TevSwapMode* tevSwaps = GetTevSwapAry();
+ tevSwaps[GX_TEV_SWAP0].Set(GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
+ tevSwaps[GX_TEV_SWAP1].Set(GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
+ tevSwaps[GX_TEV_SWAP2].Set(GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA);
+ tevSwaps[GX_TEV_SWAP3].Set(GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA);
+ }
+
+ mGXMemNum.alpComp = mGXMemCap.alpComp;
+ if (mGXMemNum.alpComp) {
+ *GetAlphaComparePtr() = AlphaCompare();
+ }
+
+ mGXMemNum.blendMode = mGXMemCap.blendMode;
+ if (mGXMemNum.blendMode) {
+ *GetBlendModePtr() = BlendMode();
+ }
+ }
+
+ const GXTexObj* Material::GetTexMapAry() const {
+ return detail::ConvertOffsToPtr<GXTexObj>(mpGXMem, 0);
+ }
+
+ GXTexObj* Material::GetTexMapAry() {
+ return detail::ConvertOffsToPtr<GXTexObj>(mpGXMem, 0);
+ }
+
+ const TexSRT* Material::GetTexSRTAry() const {
+ return detail::ConvertOffsToPtr<TexSRT>(mpGXMem, CalcOffsetTexSRTAry(mGXMemCap));
+ }
+
+ TexSRT* Material::GetTexSRTAry() {
+ return detail::ConvertOffsToPtr<TexSRT>(mpGXMem, CalcOffsetTexSRTAry(mGXMemCap));
+ }
+
+ const TexCoordGen* Material::GetTexCoordGenAry() const {
+ return detail::ConvertOffsToPtr<TexCoordGen>(mpGXMem,
+ CalcOffsetTexCoordGenAry(mGXMemCap));
+ }
+
+ TexCoordGen* Material::GetTexCoordGenAry() {
+ return detail::ConvertOffsToPtr<TexCoordGen>(mpGXMem,
+ CalcOffsetTexCoordGenAry(mGXMemCap));
+ }
+
+ const ChanCtrl* Material::GetChanCtrlAry() const {
+ return detail::ConvertOffsToPtr<ChanCtrl>(mpGXMem, CalcOffsetChanCtrlAry(mGXMemCap));
+ }
+
+ ChanCtrl* Material::GetChanCtrlAry() {
+ return detail::ConvertOffsToPtr<ChanCtrl>(mpGXMem, CalcOffsetChanCtrlAry(mGXMemCap));
+ }
+
+ const ut::Color* Material::GetMatColAry() const {
+ return detail::ConvertOffsToPtr<ut::Color>(mpGXMem, CalcOffsetMatColAry(mGXMemCap));
+ }
+
+ ut::Color* Material::GetMatColAry() {
+ return detail::ConvertOffsToPtr<ut::Color>(mpGXMem, CalcOffsetMatColAry(mGXMemCap));
+ }
+
+ const TevSwapMode* Material::GetTevSwapAry() const {
+ return detail::ConvertOffsToPtr<TevSwapMode>(mpGXMem, CalcOffsetTevSwapAry(mGXMemCap));
+ }
+
+ TevSwapMode* Material::GetTevSwapAry() {
+ return detail::ConvertOffsToPtr<TevSwapMode>(mpGXMem, CalcOffsetTevSwapAry(mGXMemCap));
+ }
+
+ const AlphaCompare* Material::GetAlphaComparePtr() const {
+ return detail::ConvertOffsToPtr<AlphaCompare>(mpGXMem,
+ CalcOffsetGetAlphaCompare(mGXMemCap));
+ }
+
+ AlphaCompare* Material::GetAlphaComparePtr() {
+ return detail::ConvertOffsToPtr<AlphaCompare>(mpGXMem,
+ CalcOffsetGetAlphaCompare(mGXMemCap));
+ }
+
+ const BlendMode* Material::GetBlendModePtr() const {
+ return detail::ConvertOffsToPtr<BlendMode>(mpGXMem, CalcOffsetBlendMode(mGXMemCap));
+ }
+
+ BlendMode* Material::GetBlendModePtr() {
+ return detail::ConvertOffsToPtr<BlendMode>(mpGXMem, CalcOffsetBlendMode(mGXMemCap));
+ }
+
+ const IndirectStage* Material::GetIndirectStageAry() const {
+ return detail::ConvertOffsToPtr<IndirectStage>(mpGXMem,
+ CalcOffsetIndirectStageAry(mGXMemCap));
+ }
+
+ IndirectStage* Material::GetIndirectStageAry() {
+ return detail::ConvertOffsToPtr<IndirectStage>(mpGXMem,
+ CalcOffsetIndirectStageAry(mGXMemCap));
+ }
+
+ const TexSRT* Material::GetIndTexSRTAry() const {
+ return detail::ConvertOffsToPtr<TexSRT>(mpGXMem, CalcOffsetIndTexSRTAry(mGXMemCap));
+ }
+
+ TexSRT* Material::GetIndTexSRTAry() {
+ return detail::ConvertOffsToPtr<TexSRT>(mpGXMem, CalcOffsetIndTexSRTAry(mGXMemCap));
+ }
+
+ const TevStage* Material::GetTevStageAry() const {
+ return detail::ConvertOffsToPtr<TevStage>(mpGXMem, CalcOffsetTevStageAry(mGXMemCap));
+ }
+
+ TevStage* Material::GetTevStageAry() {
+ return detail::ConvertOffsToPtr<TevStage>(mpGXMem, CalcOffsetTevStageAry(mGXMemCap));
+ }
+
+ void Material::SetName(const char* name) {
+ std::strncpy(mName, name, ARRAY_SIZE(mName));
+ }
+
+ void Material::SetTextureNum(u8 num) {
+ NW4HBM_ASSERT(907, num <= mGXMemCap.texMap);
+
+ if (num) {
+ NW4HBM_ASSERT_CHECK_NULL(910, mpGXMem);
+ GXTexObj* texMaps = GetTexMapAry();
+
+ for (u32 i = mGXMemNum.texMap; i < num; i++) {
+ memset(&texMaps[i], 0, sizeof(*texMaps));
+ }
+
+ mGXMemNum.texMap = num;
+ }
+ }
+
+ void Material::SetTexCoordGenNum(u8 num) {
+ NW4HBM_ASSERT(924, num <= mGXMemCap.texCoordGen);
+
+ if (num) {
+ NW4HBM_ASSERT_CHECK_NULL(927, mpGXMem);
+ TexCoordGen* const texCoordGens = GetTexCoordGenAry();
+
+ for (u32 i = mGXMemNum.texCoordGen; i < num; i++) {
+ texCoordGens[i] = TexCoordGen();
+ }
+
+ mGXMemNum.texCoordGen = num;
+ }
+ }
+
+ void Material::SetTevStageNum(u8 num) {
+ NW4HBM_ASSERT(941, num <= mGXMemCap.tevStage);
+
+ if (num) {
+ NW4HBM_ASSERT_CHECK_NULL(944, mpGXMem);
+ TevStage* tevStages = GetTevStageAry();
+
+ for (u32 i = mGXMemNum.tevStage; i < num; i++) {
+ tevStages[i] = TevStage();
+ }
+
+ mGXMemNum.tevStage = num;
+ }
+ }
+
+ void Material::SetIndStageNum(u8 num) {
+ NW4HBM_ASSERT(958, num <= mGXMemCap.indStage);
+
+ if (num) {
+ NW4HBM_ASSERT_CHECK_NULL(961, mpGXMem);
+ IndirectStage* const indStages = GetIndirectStageAry();
+
+ for (u32 i = mGXMemNum.indStage; i < num; i++) {
+ indStages[i] = IndirectStage();
+ }
+
+ mGXMemNum.indStage = num;
+ }
+ }
+
+ void Material::GetTexture(GXTexObj* pTexObj, u8 texMapIdx) const {
+ NW4HBM_ASSERT_CHECK_NULL(988, pTexObj);
+ NW4HBM_ASSERT(989, texMapIdx < mGXMemNum.texMap);
+ CopyGXTexObj(pTexObj, &GetTexMapAry()[texMapIdx]);
+ }
+
+ void Material::SetTexture(u8 texMapIdx, TPLPalette* pTplRes) {
+ NW4HBM_ASSERT(1010, texMapIdx < mGXMemNum.texMap);
+ GXTexObj* pDstTexObj = &GetTexMapAry()[texMapIdx];
+ detail::InitGXTexObjFromTPL(pDstTexObj, pTplRes, 0);
+ }
+
+ void Material::SetTextureNoWrap(u8 texMapIdx, TPLPalette* pTplRes) {
+ NW4HBM_ASSERT(1033, texMapIdx < mGXMemNum.texMap);
+ GXTexObj* pDstTexObj = &GetTexMapAry()[texMapIdx];
+ GXTexWrapMode wrapS = GXGetTexObjWrapS(pDstTexObj);
+ GXTexWrapMode wrapT = GXGetTexObjWrapT(pDstTexObj);
+ detail::InitGXTexObjFromTPL(pDstTexObj, pTplRes, 0);
+ GXInitTexObjWrapMode(pDstTexObj, wrapS, wrapT);
+ }
+
+ void Material::SetTexture(u8 texMapIdx, const GXTexObj& texObj) {
+ NW4HBM_ASSERT(1061, texMapIdx < mGXMemNum.texMap);
+ GXTexObj* pDstTexObj = &GetTexMapAry()[texMapIdx];
+ CopyGXTexObj(pDstTexObj, &texObj);
+ }
+
+ void Material::SetColorElement(u32 colorType, s16 value) {
+ switch (colorType) {
+ case ANIMTARGET_MATCOLOR_MATR:
+ case ANIMTARGET_MATCOLOR_MATG:
+ case ANIMTARGET_MATCOLOR_MATB:
+ case ANIMTARGET_MATCOLOR_MATA: {
+ if (mGXMemNum.matCol >= 1) {
+ ut::Color* matCols = GetMatColAry();
+ SetColorComponentValue(matCols, colorType & 3, value);
+ }
+
+ break;
+ }
+ case ANIMTARGET_MATCOLOR_TEV0R:
+ case ANIMTARGET_MATCOLOR_TEV0G:
+ case ANIMTARGET_MATCOLOR_TEV0B:
+ case ANIMTARGET_MATCOLOR_TEV0A:
+ case ANIMTARGET_MATCOLOR_TEV1R:
+ case ANIMTARGET_MATCOLOR_TEV1G:
+ case ANIMTARGET_MATCOLOR_TEV1B:
+ case ANIMTARGET_MATCOLOR_TEV1A:
+ case ANIMTARGET_MATCOLOR_TEV2R:
+ case ANIMTARGET_MATCOLOR_TEV2G:
+ case ANIMTARGET_MATCOLOR_TEV2B:
+ case ANIMTARGET_MATCOLOR_TEV2A: {
+ u32 regIdx = (colorType - 4) / 4;
+ switch ((colorType - 4) % 4) {
+ case 0: {
+ mTevCols[regIdx].r = value;
+ break;
+ }
+ case 1: {
+ mTevCols[regIdx].g = value;
+ break;
+ }
+ case 2: {
+ mTevCols[regIdx].b = value;
+ break;
+ }
+ case 3: {
+ mTevCols[regIdx].a = value;
+ break;
+ }
+ }
+ break;
+ }
+ case ANIMTARGET_MATCOLOR_TEVK0R:
+ case ANIMTARGET_MATCOLOR_TEVK0G:
+ case ANIMTARGET_MATCOLOR_TEVK0B:
+ case ANIMTARGET_MATCOLOR_TEVK0A:
+ case ANIMTARGET_MATCOLOR_TEVK1R:
+ case ANIMTARGET_MATCOLOR_TEVK1G:
+ case ANIMTARGET_MATCOLOR_TEVK1B:
+ case ANIMTARGET_MATCOLOR_TEVK1A:
+ case ANIMTARGET_MATCOLOR_TEVK2R:
+ case ANIMTARGET_MATCOLOR_TEVK2G:
+ case ANIMTARGET_MATCOLOR_TEVK2B:
+ case ANIMTARGET_MATCOLOR_TEVK2A:
+ case ANIMTARGET_MATCOLOR_TEVK3R:
+ case ANIMTARGET_MATCOLOR_TEVK3G:
+ case ANIMTARGET_MATCOLOR_TEVK3B:
+ case ANIMTARGET_MATCOLOR_TEVK3A: {
+ u32 regIdx = (colorType - 16) / 4;
+ SetColorComponentValue(&mTevKCols[regIdx], (colorType - 16) & 3, value);
+ break;
+ }
+ }
+ }
+
+ bool Material::SetupGX(bool bModVtxCol, u8 alpha) {
+ static GXTevKColorSel kColSels[GX_MAX_TEXMAP] = {
+ GX_TEV_KCSEL_K3_A, GX_TEV_KCSEL_K3_B, GX_TEV_KCSEL_K3_G, GX_TEV_KCSEL_K3_R,
+ GX_TEV_KCSEL_K2_A, GX_TEV_KCSEL_K2_B, GX_TEV_KCSEL_K2_G, GX_TEV_KCSEL_K2_R,
+ };
+
+ static GXTevKAlphaSel kAlpSels[GX_MAX_TEXMAP] = {
+ GX_TEV_KASEL_K3_A, GX_TEV_KASEL_K3_B, GX_TEV_KASEL_K3_G, GX_TEV_KASEL_K3_R,
+ GX_TEV_KASEL_K2_A, GX_TEV_KASEL_K2_B, GX_TEV_KASEL_K2_G, GX_TEV_KASEL_K2_R,
+ };
+
+ bool bUseVtxCol = true;
+ bool bUseMatCol = false;
+
+ GXSetNumChans(1);
+
+ if (IsChanCtrlCap()) {
+ ChanCtrl* const chanCtrls = GetChanCtrlAry();
+
+ GXSetChanCtrl(GX_COLOR0, false, GX_SRC_REG, chanCtrls->GetColorSrc(), GX_LIGHT_NULL,
+ GX_DF_NONE, GX_AF_NONE);
+ GXSetChanCtrl(GX_ALPHA0, false, GX_SRC_REG, chanCtrls->GetAlphaSrc(), GX_LIGHT_NULL,
+ GX_DF_NONE, GX_AF_NONE);
+
+ bUseVtxCol = chanCtrls->GetColorSrc() == GX_SRC_VTX ||
+ chanCtrls->GetAlphaSrc() == GX_SRC_VTX;
+ bUseMatCol = chanCtrls->GetColorSrc() == GX_SRC_REG ||
+ chanCtrls->GetAlphaSrc() == GX_SRC_REG;
+ } else {
+ GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+ }
+
+ bool bUseRasStage = false;
+
+ if (bUseVtxCol) {
+ bUseRasStage = bUseRasStage || bModVtxCol;
+ }
+
+ if (bUseMatCol) {
+ ut::Color matCol(ut::Color::WHITE);
+
+ if (IsMatColorCap()) {
+ matCol = *GetMatColAry();
+ }
+
+ matCol = detail::MultipleAlpha(matCol, alpha);
+ GXSetChanMatColor(GX_COLOR0A0, matCol);
+
+ bUseRasStage = bUseRasStage || matCol != ut::Color::WHITE;
+ }
+
+ bool bSetTexMtx = false;
+ bool bUseTexMtx[MAX_TEX_SRT];
+
+ for (int i = 0; i < (int)ARRAY_SIZE(bUseTexMtx); i++) {
+ bUseTexMtx[i] = false;
+ }
+
+ GXSetNumTexGens(mGXMemNum.texCoordGen);
+
+ if (mGXMemNum.texCoordGen) {
+ TexCoordGen* texCoordGens = GetTexCoordGenAry();
+
+ for (int i = 0; i < mGXMemNum.texCoordGen; i++) {
+ NW4HBM_ASSERT(1288, texCoordGens[i].GetTexGenType() != GX_TG_MTX3x4);
+ u32 texMtx = texCoordGens[i].GetTexMtx();
+
+ if (texCoordGens[i].GetTexGenType() == GX_TG_MTX2x4) {
+ NW4HBM_ASSERT(1294, texMtx == GX_IDENTITY ||
+ GetTexMtxIdx(texMtx) < mGXMemNum.texSRT);
+
+ if (texMtx != GX_IDENTITY) {
+ bUseTexMtx[GetTexMtxIdx(texMtx)] = true;
+ bSetTexMtx = true;
+ }
+ }
+
+ GXSetTexCoordGen(static_cast<GXTexCoordID>(i), texCoordGens[i].GetTexGenType(),
+ texCoordGens[i].GetTexGenSrc(), texMtx);
+ }
+ }
+
+ if (bSetTexMtx) {
+ TexSRT* const texSRTs = GetTexSRTAry();
+ for (u8 i = 0; i < mGXMemNum.texSRT; i++) {
+ if (bUseTexMtx[i]) {
+ math::MTX34 texMtx;
+ CalcTextureMtx(&texMtx, texSRTs[i]);
+ GXLoadTexMtxImm(texMtx, GetTexMtx(i), GX_MTX2x4);
+ }
+ }
+ }
+
+ if (mGXMemNum.texMap) {
+ u32 tlutName = GX_TLUT0;
+ GXTexObj* texMaps = GetTexMapAry();
+
+ for (int i = 0; i < mGXMemNum.texMap; i++) {
+ GXTexObj texMap = texMaps[i];
+ int texFmt = GXGetTexObjFmt(&texMap);
+
+ if (texFmt == GX_TF_C4 || texFmt == GX_TF_C8) {
+ TPLClutHeader* pClutHeader =
+ static_cast<TPLClutHeader*>(GXGetTexObjUserData(&texMap));
+
+ if (pClutHeader) {
+ GXTlutObj tlutObj;
+
+ GXInitTlutObj(&tlutObj, pClutHeader->data, pClutHeader->format,
+ pClutHeader->numEntries);
+ GXLoadTlut(&tlutObj, tlutName);
+ GXInitTexObjTlut(&texMap, tlutName);
+
+ tlutName++;
+ }
+ }
+
+ GXLoadTexObj(&texMap, (GXTexMapID)i);
+ }
+ }
+
+ GXSetTevColorS10(GX_TEVREG0, mTevCols[TEVCOLOR_REG0]);
+ GXSetTevColorS10(GX_TEVREG1, mTevCols[TEVCOLOR_REG1]);
+ GXSetTevColorS10(GX_TEVREG2, mTevCols[TEVCOLOR_REG2]);
+
+ GXSetTevKColor(GX_KCOLOR0, mTevKCols[GX_KCOLOR0]);
+ GXSetTevKColor(GX_KCOLOR1, mTevKCols[GX_KCOLOR1]);
+ GXSetTevKColor(GX_KCOLOR2, mTevKCols[GX_KCOLOR2]);
+ GXSetTevKColor(GX_KCOLOR3, mTevKCols[GX_KCOLOR3]);
+
+ if (IsTevSwapCap()) {
+ TevSwapMode* tevSwaps = GetTevSwapAry();
+ for (int i = 0; i < GX_MAX_TEVSWAP; i++) {
+ GXSetTevSwapModeTable(static_cast<GXTevSwapSel>(i), tevSwaps[i].GetR(),
+ tevSwaps[i].GetG(), tevSwaps[i].GetB(),
+ tevSwaps[i].GetA());
+ }
+ } else {
+ GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE,
+ GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN,
+ GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE,
+ GX_CH_ALPHA);
+ }
+
+ bool bSetIndTexMtx = false;
+ bool bUseIndTexMtx[MAX_IND_SRT];
+
+ for (int i = 0; i < (int)ARRAY_SIZE(bUseIndTexMtx); i++) {
+ bUseIndTexMtx[i] = false;
+ }
+
+ if (mGXMemNum.tevStage) {
+ GXSetNumTevStages(mGXMemNum.tevStage);
+
+ TevStage* tevStages = GetTevStageAry();
+ for (int i = 0; i < mGXMemNum.tevStage; i++) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(i);
+
+ GXSetTevOrder(tevStage, tevStages[i].GetTexCoordGen(), tevStages[i].GetTexMap(),
+ tevStages[i].GetColorChan());
+
+ GXSetTevSwapMode(tevStage, tevStages[i].GetRasSwapSel(),
+ tevStages[i].GetTexSwapSel());
+
+ GXSetTevColorIn(tevStage, tevStages[i].GetColorInA(),
+ tevStages[i].GetColorInB(), tevStages[i].GetColorInC(),
+ tevStages[i].GetColorInD());
+
+ GXSetTevColorOp(tevStage, tevStages[i].GetColorOp(),
+ tevStages[i].GetColorBias(), tevStages[i].GetColorScale(),
+ tevStages[i].IsColorClamp(), tevStages[i].GetColorOutReg());
+
+ GXSetTevKColorSel(tevStage, tevStages[i].GetKColorSel());
+
+ GXSetTevAlphaIn(tevStage, tevStages[i].GetAlphaInA(),
+ tevStages[i].GetAlphaInB(), tevStages[i].GetAlphaInC(),
+ tevStages[i].GetAlphaInD());
+
+ GXSetTevAlphaOp(tevStage, tevStages[i].GetAlphaOp(),
+ tevStages[i].GetAlphaBias(), tevStages[i].GetAlphaScale(),
+ tevStages[i].IsAlphaClamp(), tevStages[i].GetAlphaOutReg());
+
+ GXSetTevKAlphaSel(tevStage, tevStages[i].GetKAlphaSel());
+
+ GXIndTexMtxID indMtxSel = tevStages[i].GetIndMtxSel();
+ GXSetTevIndirect(tevStage, tevStages[i].GetIndStage(),
+ tevStages[i].GetIndFormat(), tevStages[i].GetIndBiasSel(),
+ indMtxSel, tevStages[i].GetIndWrapS(),
+ tevStages[i].GetIndWrapT(), tevStages[i].IsIndAddPrev(),
+ tevStages[i].IsIndUtcLod(), tevStages[i].GetIndAlphaSel());
+
+ if (GX_ITM_0 <= indMtxSel && indMtxSel <= GX_ITM_2) {
+ bUseIndTexMtx[indMtxSel - 1] = true;
+ bSetIndTexMtx = true;
+ }
+ }
+
+ bUseRasStage = true;
+ } else {
+ u8 tevStageID = GX_TEVSTAGE0;
+
+ if (mGXMemNum.texMap == GX_TEXMAP0) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(tevStageID);
+
+ GXSetTevOrder(tevStage, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+ GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_C1, GX_CC_RASC, GX_CC_ZERO);
+ GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_A1, GX_CA_RASA, GX_CA_ZERO);
+
+ tevStageID++;
+ bUseRasStage = true;
+ } else {
+ if (mGXMemNum.texMap == GX_TEXMAP1) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(tevStageID);
+
+ GXSetTevOrder(tevStage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
+ GXSetTevColorIn(tevStage, GX_CC_C0, GX_CC_C1, GX_CC_TEXC, GX_CC_ZERO);
+ GXSetTevAlphaIn(tevStage, GX_CA_A0, GX_CA_A1, GX_CA_TEXA, GX_CA_ZERO);
+
+ tevStageID++;
+ } else {
+ if (mGXMemNum.texMap == GX_TEXMAP2) {
+ GXTevStageID tevStage;
+
+ tevStage = static_cast<GXTevStageID>(tevStageID);
+ GXSetTevOrder(tevStage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
+ GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
+ GX_CC_TEXC);
+ GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
+ GX_CA_TEXA);
+
+ tevStageID++;
+
+ tevStage = static_cast<GXTevStageID>(tevStageID);
+ GXSetTevOrder(tevStage, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL);
+ GXSetTevColorIn(tevStage, GX_CC_TEXC, GX_CC_CPREV, GX_CC_KONST,
+ GX_CC_ZERO);
+ GXSetTevAlphaIn(tevStage, GX_CA_TEXA, GX_CA_APREV, GX_CA_KONST,
+ GX_CA_ZERO);
+
+ GXSetTevKColorSel(tevStage, kColSels[GX_TEXMAP0]);
+ GXSetTevKAlphaSel(tevStage, kAlpSels[GX_TEXMAP0]);
+
+ tevStageID++;
+ } else {
+ for (int i = 0; i < mGXMemNum.texMap; i++) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(tevStageID);
+
+ GXSetTevOrder(tevStage, static_cast<GXTexCoordID>(i),
+ static_cast<GXTexMapID>(i), GX_COLOR_NULL);
+
+ GXTevColorArg colDIn = i == GX_TEXMAP0 ? GX_CC_ZERO : GX_CC_CPREV;
+ GXTevAlphaArg alpDIn = i == GX_TEXMAP0 ? GX_CA_ZERO : GX_CA_APREV;
+
+ GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST,
+ colDIn);
+ GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_TEXA, GX_CA_KONST,
+ alpDIn);
+
+ GXSetTevKColorSel(tevStage, kColSels[i]);
+ GXSetTevKAlphaSel(tevStage, kAlpSels[i]);
+
+ tevStageID++;
+ }
+ }
+
+ if (mTevCols[TEVCOLOR_REG0] != DefaultBlackColor ||
+ !IsDefaultWhiteColor(&mTevCols[TEVCOLOR_REG1]))
+ {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(tevStageID);
+
+ GXSetTevOrder(tevStage, GX_TEXCOORD_NULL, GX_TEXMAP_NULL,
+ GX_COLOR_NULL);
+ GXSetTevColorIn(tevStage, GX_CC_C0, GX_CC_C1, GX_CC_CPREV, GX_CC_ZERO);
+ GXSetTevAlphaIn(tevStage, GX_CA_A0, GX_CA_A1, GX_CA_APREV, GX_CA_ZERO);
+
+ tevStageID++;
+ }
+ }
+
+ if (bUseRasStage) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(tevStageID);
+
+ GXSetTevOrder(tevStage, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+ GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, GX_CC_ZERO);
+ GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_APREV, GX_CA_RASA, GX_CA_ZERO);
+
+ tevStageID++;
+ }
+ }
+
+ const u8 tevStageNum = tevStageID;
+ for (u8 id = 0; id < tevStageNum; id++) {
+ GXTevStageID tevStage = static_cast<GXTevStageID>(id);
+
+ GXSetTevColorOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GXSetTevAlphaOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+
+ GXSetTevDirect(tevStage);
+ GXSetTevSwapMode(tevStage, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ }
+
+ GXSetNumTevStages(tevStageNum);
+ }
+
+ if (bSetIndTexMtx) {
+ TexSRT* indTexSRTs = GetIndTexSRTAry();
+
+ for (int i = 0; i < mGXMemNum.indSRT; i++) {
+ if (bUseIndTexMtx[i]) {
+ Mtx23 mtx;
+ CalcIndTexMtx(mtx, indTexSRTs[i]);
+ SetIndTexMtx(static_cast<GXIndTexMtxID>(i + 1), mtx);
+ }
+ }
+ }
+
+ GXSetNumIndStages(mGXMemNum.indStage);
+
+ if (mGXMemNum.indStage) {
+ IndirectStage* indirectStages = GetIndirectStageAry();
+
+ for (int i = 0; i < mGXMemNum.indStage; i++) {
+ GXIndTexStageID indStage = static_cast<GXIndTexStageID>(i);
+
+ GXSetIndTexOrder(indStage, indirectStages[i].GetTexCoordGen(),
+ indirectStages[i].GetTexMap());
+ GXSetIndTexCoordScale(indStage, indirectStages[i].GetScaleS(),
+ indirectStages[i].GetScaleT());
+ }
+ }
+
+ if (IsAlphaCompareCap()) {
+ AlphaCompare* pAlpComp = GetAlphaComparePtr();
+
+ GXSetAlphaCompare(pAlpComp->GetComp0(), pAlpComp->GetRef0(), pAlpComp->GetOp(),
+ pAlpComp->GetComp1(), pAlpComp->GetRef1());
+ } else {
+ GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
+ }
+
+ if (IsBlendModeCap()) {
+ BlendMode* pBlendMode = GetBlendModePtr();
+ GXSetBlendMode(pBlendMode->GetType(), pBlendMode->GetSrcFactor(),
+ pBlendMode->GetDstFactor(), pBlendMode->GetOp());
+ } else {
+ GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
+ }
+
+ return bUseRasStage && bUseVtxCol;
+ }
+
+ void Material::BindAnimation(AnimTransform* animTrans) {
+ animTrans->Bind(this);
+ }
+
+ void Material::UnbindAnimation(AnimTransform* animTrans) {
+ for (AnimationLinkList::Iterator it = mAnimList.GetBeginIter();
+ it != mAnimList.GetEndIter();)
+ {
+ AnimationLinkList::Iterator currIt = it++;
+
+ if (animTrans == NULL || currIt->GetAnimTransform() == animTrans) {
+ mAnimList.Erase(currIt);
+ currIt->Reset();
+ }
+ }
+ }
+
+ void Material::UnbindAllAnimation() {
+ UnbindAnimation(NULL);
+ }
+
+ void Material::Animate() {
+ for (AnimationLinkList::Iterator it = mAnimList.GetBeginIter();
+ it != mAnimList.GetEndIter(); it++)
+ {
+ if (it->IsEnable()) {
+ AnimTransform* animTrans = it->GetAnimTransform();
+ animTrans->Animate(it->GetIndex(), this);
+ }
+ }
+ }
+
+ void Material::AddAnimationLink(AnimationLink* animationLink) {
+ mAnimList.PushBack(animationLink);
+ }
+
+ AnimationLink* Material::FindAnimationLink(AnimTransform* animTrans) {
+ return detail::FindAnimationLink(&mAnimList, animTrans);
+ }
+
+ void Material::SetAnimationEnable(AnimTransform* animTrans, bool bEnable) {
+ if (AnimationLink* animLink = FindAnimationLink(animTrans)) {
+ animLink->SetEnable(bEnable);
+ }
+ }
+
+ Size detail::GetTextureSize(Material* pMaterial, u8 texMapIdx) {
+ if (texMapIdx >= pMaterial->GetTextureNum()) {
+ return Size(0.0f, 0.0f);
+ }
+
+ GXTexObj texObj;
+ pMaterial->GetTexture(&texObj, texMapIdx);
+
+ return Size(GXGetTexObjWidth(&texObj), GXGetTexObjHeight(&texObj));
+ }
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp
new file mode 100644
index 0000000000..68d086334d
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_pane.cpp
@@ -0,0 +1,495 @@
+#include "pane.h"
+
+#include "common.h"
+#include "layout.h"
+
+#include <cstring.h>
+
+namespace {
+ // pretend this is nw4hbm::lyt
+ using namespace nw4hbm;
+ using namespace nw4hbm::lyt;
+
+ void ReverseYAxis(math::MTX34* pMtx);
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+ NW4HBM_UT_GET_RUNTIME_TYPEINFO(Pane);
+ }
+} // namespace nw4hbm
+
+namespace {
+
+ void ReverseYAxis(math::MTX34* pMtx) {
+ pMtx->m[0][1] = -pMtx->m[0][1];
+ pMtx->m[1][1] = -pMtx->m[1][1];
+ pMtx->m[2][1] = -pMtx->m[2][1];
+ }
+
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ Pane::Pane() {
+ Init();
+
+ mBasePosition = 4;
+ memset(mName, 0, sizeof(mName));
+ memset(mUserData, 0, sizeof(mUserData));
+
+ mTranslate = math::VEC3(0.0f, 0.0f, 0.0f);
+ mRotate = math::VEC3(0.0f, 0.0f, 0.0f);
+ mScale = math::VEC2(1.0f, 1.0f);
+ mSize = Size(0.0f, 0.0f);
+ mAlpha = ut::Color(255);
+ mGlbAlpha = mAlpha;
+ mFlag = 0;
+
+ SetVisible(true);
+ }
+
+ Pane::Pane(const res::Pane* pBlock) {
+ Init();
+
+ mBasePosition = pBlock->basePosition;
+ SetName(pBlock->name);
+ SetUserData(pBlock->userData);
+
+ mTranslate = pBlock->translate;
+ mRotate = pBlock->rotate;
+ mScale = pBlock->scale;
+ mSize = pBlock->size;
+ mAlpha = pBlock->alpha;
+ mGlbAlpha = mAlpha;
+ mFlag = pBlock->flag;
+ }
+
+ void Pane::Init() {
+ mpParent = NULL;
+ mpMaterial = NULL;
+ mbUserAllocated = false;
+ }
+
+ Pane::~Pane() {
+ for (PaneList::Iterator it = mChildList.GetBeginIter(); it != mChildList.GetEndIter();)
+ {
+ PaneList::Iterator currIt = it++;
+ mChildList.Erase(currIt);
+
+ if (!currIt->IsUserAllocated()) {
+ currIt->~Pane();
+ Layout::FreeMemory(&*currIt);
+ }
+ }
+
+ UnbindAnimationSelf(NULL);
+
+ if (mpMaterial && !mpMaterial->IsUserAllocated()) {
+ mpMaterial->~Material();
+ Layout::FreeMemory(mpMaterial);
+ }
+ }
+
+ void Pane::SetName(const char* name) {
+ std::strncpy(mName, name, sizeof(mName));
+ }
+
+ void Pane::SetUserData(const char* userData) {
+ std::strncpy(mUserData, userData, sizeof(mUserData));
+ }
+
+ void Pane::AppendChild(Pane* pChild) {
+ InsertChild(mChildList.GetEndIter(), pChild);
+ }
+
+ // it requires a type that wasn't used before to generate the string and avoid having it
+ // stripped
+ /* typedef nw4hbm::ut::LinkList<void**, 0> DummyLinkList;
+ DECOMP_FORCE_CLASS_METHOD(DummyLinkList, GetNodeFromPointer(NULL));
+ typedef nw4hbm::ut::LinkList<void***, 0> DummyLinkList2;
+ DECOMP_FORCE_CLASS_METHOD(DummyLinkList2, GetNodeFromPointer(NULL)); */
+
+ void Pane::InsertChild(PaneList::Iterator next, Pane* pChild) {
+ NW4HBM_ASSERT_CHECK_NULL(253, pChild);
+ NW4HBM_ASSERT(254, pChild->mpParent == 0);
+ mChildList.Insert(next, pChild);
+ pChild->mpParent = this;
+ }
+
+ void dummyString() {
+ OSReport("NW4HBM:Pointer must not be NULL (pNext)");
+ OSReport("NW4HBM:Failed assertion pNext->mpParent == this");
+ OSReport("NW4HBM:Failed assertion pChild->mpParent == this");
+ }
+
+#pragma ppc_iro_level 0
+
+ const ut::Rect Pane::GetPaneRect(const DrawInfo& drawInfo) const {
+ ut::Rect ret;
+ math::VEC2 basePt = GetVtxPos();
+
+ ret.left = basePt.x;
+ ret.top = basePt.y;
+ ret.right = ret.left + mSize.width;
+ ret.bottom = ret.top + mSize.height;
+
+ if (drawInfo.IsYAxisUp()) {
+ ret.top = -ret.top;
+ ret.bottom = -ret.bottom;
+ }
+
+ return ret;
+ }
+
+#pragma ppc_iro_level reset
+
+ ut::Color Pane::GetVtxColor(u32) const {
+ return ut::Color(0xFFFFFFFF);
+ }
+
+ void Pane::SetVtxColor(u32, ut::Color) { /* ... */ }
+
+ u8 Pane::GetColorElement(u32 idx) const {
+ NW4HBM_ASSERT(319, idx < ANIMTARGET_PANE_COLOR_MAX);
+ switch (idx) {
+ case 16:
+ return mAlpha;
+
+ default:
+ return GetVtxColorElement(idx);
+ }
+ }
+
+ // it requires a type that wasn't used before to generate the string and avoid having it
+ // stripped
+ /* typedef nw4hbm::ut::LinkList<void****, 0> DummyLinkList3;
+ DECOMP_FORCE_CLASS_METHOD(DummyLinkList3, GetNodeFromPointer(NULL)); */
+
+ void Pane::SetColorElement(u32 idx, u8 value) {
+ NW4HBM_ASSERT(334, idx < ANIMTARGET_PANE_COLOR_MAX);
+ switch (idx) {
+ case 16:
+ mAlpha = value;
+ break;
+
+ default:
+ SetVtxColorElement(idx, value);
+ break;
+ }
+ }
+
+ u8 Pane::GetVtxColorElement(u32) const {
+ return 0xff;
+ }
+
+ void Pane::SetVtxColorElement(u32, u8) { /* ... */ }
+
+ Pane* Pane::FindPaneByName(const char* findName, bool bRecursive) {
+ if (detail::EqualsPaneName(mName, findName)) {
+ return this;
+ }
+
+ if (bRecursive) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ if (Pane* pane = it->FindPaneByName(findName, true)) {
+ return pane;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ Material* Pane::FindMaterialByName(const char* findName, bool bRecursive) {
+ if (mpMaterial && detail::EqualsMaterialName(mpMaterial->GetName(), findName)) {
+ return mpMaterial;
+ }
+
+ if (bRecursive) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ if (Material* pMaterial = it->FindMaterialByName(findName, true)) {
+ return pMaterial;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ void Pane::CalculateMtx(const DrawInfo& drawInfo) {
+ // this doesnt even get referenced, an entirely new constant is generated lol
+ const f32 invAlpha = 1.0f / 255.0f;
+
+ if (!detail::TestBit(mFlag, 0) && !drawInfo.IsInvisiblePaneCalculateMtx()) {
+ return;
+ }
+
+ {
+ math::MTX34 mtx1;
+ math::MTX34 mtx2;
+ math::MTX34 rotateMtx;
+
+ {
+ math::VEC2 scale(mScale);
+
+ if (drawInfo.IsLocationAdjust() && detail::TestBit(mFlag, 2)) {
+ scale.x *= drawInfo.GetLocationAdjustScale().x;
+ scale.y *= drawInfo.GetLocationAdjustScale().y;
+ }
+
+ PSMTXScale(mtx2, scale.x, scale.y, 1.0f);
+ }
+
+ PSMTXRotRad(rotateMtx, 'x', DEG_TO_RAD(mRotate.x));
+ PSMTXConcat(rotateMtx, mtx2, mtx1);
+
+ PSMTXRotRad(rotateMtx, 'y', DEG_TO_RAD(mRotate.y));
+ PSMTXConcat(rotateMtx, mtx1, mtx2);
+
+ PSMTXRotRad(rotateMtx, 'z', DEG_TO_RAD(mRotate.z));
+ PSMTXConcat(rotateMtx, mtx2, mtx1);
+
+ PSMTXTransApply(mtx1, mMtx, mTranslate.x, mTranslate.y, mTranslate.z);
+ }
+
+ if (mpParent) {
+ math::MTX34Mult(&mGlbMtx, &mpParent->mGlbMtx, &mMtx);
+ } else if (drawInfo.IsMultipleViewMtxOnDraw()) {
+ mGlbMtx = mMtx;
+ } else {
+ math::MTX34Mult(&mGlbMtx, &drawInfo.GetViewMtx(), &mMtx);
+ }
+
+ if (drawInfo.IsInfluencedAlpha() && mpParent) {
+ mGlbAlpha = mAlpha * drawInfo.GetGlobalAlpha();
+ } else {
+ mGlbAlpha = mAlpha;
+ }
+
+ // cr = const ref?
+ f32 crGlobalAlpha = drawInfo.GetGlobalAlpha();
+ bool bCrInfluenced = drawInfo.IsInfluencedAlpha();
+
+ bool bModDrawInfo = detail::TestBit(mFlag, 1) && mAlpha != 0xff;
+
+ if (bModDrawInfo) {
+ // mt = mutable, probably
+ DrawInfo& mtDrawInfo = const_cast<DrawInfo&>(drawInfo);
+
+ mtDrawInfo.SetGlobalAlpha(crGlobalAlpha * mAlpha * invAlpha);
+ mtDrawInfo.SetInfluencedAlpha(true);
+ }
+
+ CalculateMtxChild(drawInfo);
+
+ // restore changed values if applicable
+ if (bModDrawInfo) {
+ DrawInfo& mtDrawInfo = const_cast<DrawInfo&>(drawInfo);
+
+ mtDrawInfo.SetGlobalAlpha(crGlobalAlpha);
+ mtDrawInfo.SetInfluencedAlpha(bCrInfluenced);
+ }
+ }
+
+ void Pane::CalculateMtxChild(const DrawInfo& drawInfo) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter(); it != mChildList.GetEndIter();
+ it++)
+ {
+ it->CalculateMtx(drawInfo);
+ }
+ }
+
+ void Pane::Draw(const DrawInfo& drawInfo) {
+ if (detail::TestBit(mFlag, 0)) {
+ DrawSelf(drawInfo);
+
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ it->Draw(drawInfo);
+ }
+ }
+ }
+
+ void Pane::DrawSelf(const DrawInfo& drawInfo) {
+ if (mpParent && drawInfo.IsDebugDrawMode()) {
+ LoadMtx(drawInfo);
+ detail::DrawLine(GetVtxPos(), mSize, 0x00ff00ff); // green
+ }
+ }
+
+ void Pane::Animate(u32 option) {
+ AnimateSelf(option);
+
+ if (detail::TestBit(mFlag, 0) || !(option & 1)) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ it->Animate(option);
+ }
+ }
+ }
+
+ void Pane::AnimateSelf(u32 option) {
+ for (AnimationLinkList::Iterator it = mAnimList.GetBeginIter();
+ it != mAnimList.GetEndIter(); it++)
+ {
+ if (it->IsEnable()) {
+ AnimTransform* animTrans = it->GetAnimTransform();
+
+ animTrans->Animate(it->GetIndex(), this);
+ }
+ }
+
+ if ((detail::TestBit(mFlag, 0) || !(option & 1)) && mpMaterial) {
+ mpMaterial->Animate();
+ }
+ }
+
+ void Pane::BindAnimation(AnimTransform* pAnimTrans, bool bRecursive) {
+ NW4HBM_ASSERT_CHECK_NULL(596, pAnimTrans);
+ pAnimTrans->Bind(this, bRecursive);
+ }
+
+ void Pane::UnbindAnimation(AnimTransform* pAnimTrans, bool bRecursive) {
+ UnbindAnimationSelf(pAnimTrans);
+
+ if (bRecursive) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ it->UnbindAnimation(pAnimTrans, bRecursive);
+ }
+ }
+ }
+
+ void Pane::UnbindAllAnimation(bool bRecursive) {
+ UnbindAnimation(NULL, bRecursive);
+ }
+
+ void Pane::UnbindAnimationSelf(AnimTransform* pAnimTrans) {
+ if (mpMaterial) {
+ mpMaterial->UnbindAnimation(pAnimTrans);
+ }
+
+ for (AnimationLinkList::Iterator it = mAnimList.GetBeginIter();
+ it != mAnimList.GetEndIter();)
+ {
+ AnimationLinkList::Iterator currIt = it++;
+
+ if (pAnimTrans == NULL || currIt->GetAnimTransform() == pAnimTrans) {
+ mAnimList.Erase(currIt);
+ currIt->Reset();
+ }
+ }
+ }
+
+ void Pane::AddAnimationLink(AnimationLink* pAnimationLink) {
+ NW4HBM_ASSERT_CHECK_NULL(649, pAnimationLink);
+ mAnimList.PushBack(pAnimationLink);
+ }
+
+ AnimationLink* Pane::FindAnimationLink(AnimTransform* pAnimTrans) {
+ if (AnimationLink* ret = detail::FindAnimationLink(&mAnimList, pAnimTrans)) {
+ return ret;
+ }
+
+ if (mpMaterial) {
+ if (AnimationLink* ret = mpMaterial->FindAnimationLink(pAnimTrans)) {
+ return ret;
+ }
+ }
+
+ return NULL;
+ }
+
+ void Pane::SetAnimationEnable(AnimTransform* pAnimTrans, bool bEnable, bool bRecursive) {
+ if (AnimationLink* pAnimLink = detail::FindAnimationLink(&mAnimList, pAnimTrans)) {
+ pAnimLink->SetEnable(bEnable);
+ }
+
+ if (mpMaterial) {
+ mpMaterial->SetAnimationEnable(pAnimTrans, bEnable);
+ }
+
+ if (bRecursive) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ it->SetAnimationEnable(pAnimTrans, bEnable, bRecursive);
+ }
+ }
+ }
+
+ void Pane::LoadMtx(const DrawInfo& drawInfo) {
+ math::MTX34 mtx;
+ MtxPtr mtxPtr = NULL;
+
+ if (drawInfo.IsMultipleViewMtxOnDraw()) {
+ math::MTX34Mult(&mtx, &drawInfo.GetViewMtx(), &mGlbMtx);
+
+ if (drawInfo.IsYAxisUp()) {
+ ReverseYAxis(&mtx);
+ }
+
+ mtxPtr = mtx;
+ } else if (drawInfo.IsYAxisUp()) {
+ math::MTX34Copy(&mtx, &mGlbMtx);
+ ReverseYAxis(&mtx);
+ mtxPtr = mtx;
+ } else {
+ mtxPtr = mGlbMtx;
+ }
+
+ GXLoadPosMtxImm(mtxPtr, 0);
+ GXSetCurrentMtx(0);
+ }
+
+ math::VEC2 Pane::GetVtxPos() const {
+ math::VEC2 basePt(0.0f, 0.0f);
+
+ switch (mBasePosition % 3) {
+ default:
+ case 0:
+ basePt.x = 0.0f;
+ break;
+
+ case 1:
+ basePt.x = -mSize.width / 2.0f;
+ break;
+
+ case 2:
+ basePt.x = -mSize.width;
+ break;
+ }
+
+ switch (mBasePosition / 3) {
+ default:
+ case 0:
+ basePt.y = 0.0f;
+ break;
+
+ case 1:
+ basePt.y = -mSize.height / 2.0f;
+ break;
+
+ case 2:
+ basePt.y = -mSize.height;
+ break;
+ }
+
+ return basePt;
+ }
+
+ Material* Pane::GetMaterial() const {
+ return mpMaterial;
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp
new file mode 100644
index 0000000000..65d262e033
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_picture.cpp
@@ -0,0 +1,130 @@
+#include "picture.h"
+
+#include "layout.h"
+
+#include "new.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(Picture, Pane);
+
+ Picture::Picture(const res::Picture* pResPic, const ResBlockSet& resBlockSet)
+ : Pane(pResPic) {
+ u8 texCoordNum = ut::Min<u8>(pResPic->texCoordNum, 8);
+
+ Init(texCoordNum);
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mVtxColors); i++) {
+ mVtxColors[i] = pResPic->vtxCols[i];
+ }
+
+ if (texCoordNum && !mTexCoordAry.IsEmpty()) {
+ mTexCoordAry.Copy(&pResPic[1], texCoordNum);
+ }
+
+ if (Material* pMemMaterial =
+ static_cast<Material*>(Layout::AllocMemory(sizeof(*pMemMaterial))))
+ {
+ NW4HBM_ASSERT_CHECK_NULL(149, resBlockSet.pMaterialList);
+ const u32* matOffsTbl =
+ detail::ConvertOffsToPtr<u32>(resBlockSet.pMaterialList, 0xc);
+ const res::Material* pResMaterial = detail::ConvertOffsToPtr<res::Material>(
+ resBlockSet.pMaterialList, matOffsTbl[pResPic->materialIdx]);
+
+ mpMaterial = new (pMemMaterial) Material(pResMaterial, resBlockSet);
+ }
+ }
+
+ void Picture::Init(u8 texNum) {
+ if (texNum) {
+ ReserveTexCoord(texNum);
+ }
+ }
+
+ Picture::~Picture() {
+ if (mpMaterial && !mpMaterial->IsUserAllocated()) {
+ mpMaterial->~Material();
+ Layout::FreeMemory(mpMaterial);
+ mpMaterial = NULL;
+ }
+
+ mTexCoordAry.Free();
+ }
+
+ void Picture::Append(TPLPalette* pTplRes) {
+ GXTexObj texObj;
+ detail::InitGXTexObjFromTPL(&texObj, pTplRes, 0);
+
+ Append(texObj);
+ }
+
+ void Picture::Append(const GXTexObj& texObj) {
+ if (mpMaterial->GetTextureNum() >= mpMaterial->GetTextureCap() ||
+ mpMaterial->GetTextureNum() >= mpMaterial->GetTexCoordGenCap())
+ {
+ NW4R_DB_WARNING(
+ 192, false,
+ "mpMaterial->GetTextureNum(%d) is large. mpMaterial->GetTextureCap(%d), "
+ "mpMaterial->GetTexCoordGenCap(%d)\n",
+ mpMaterial->GetTextureNum(), mpMaterial->GetTextureCap(),
+ mpMaterial->GetTexCoordGenCap());
+ return;
+ }
+
+ u8 texIdx = mpMaterial->GetTextureNum();
+ mpMaterial->SetTextureNum(texIdx + 1);
+ mpMaterial->SetTexture(texIdx, texObj);
+ mpMaterial->SetTexCoordGenNum(mpMaterial->GetTextureNum());
+ mpMaterial->SetTexCoordGen(texIdx, TexCoordGen());
+
+ SetTexCoordNum(mpMaterial->GetTextureNum());
+
+ if (mSize == Size(0.0f, 0.0f) && mpMaterial->GetTextureNum() == 1) {
+ mSize = detail::GetTextureSize(mpMaterial, 0);
+ }
+ }
+
+ void Picture::ReserveTexCoord(u8 num) {
+ mTexCoordAry.Reserve(num);
+ }
+
+ void Picture::SetTexCoordNum(u8 num) {
+ mTexCoordAry.SetSize(num);
+ }
+
+ ut::Color Picture::GetVtxColor(u32 idx) const {
+ NW4HBM_ASSERT(251, idx < VERTEXCOLOR_MAX);
+ return mVtxColors[idx];
+ }
+
+ void Picture::SetVtxColor(u32 idx, ut::Color value) {
+ NW4HBM_ASSERT(262, idx < VERTEXCOLOR_MAX);
+ mVtxColors[idx] = value;
+ }
+
+ u8 Picture::GetVtxColorElement(u32 idx) const {
+ return detail::GetVtxColorElement(mVtxColors, idx);
+ }
+
+ void Picture::SetVtxColorElement(u32 idx, u8 value) {
+ detail::SetVtxColorElement(mVtxColors, idx, value);
+ }
+
+ void Picture::DrawSelf(const DrawInfo& drawInfo) {
+ if (!mpMaterial) {
+ return;
+ }
+
+ LoadMtx(drawInfo);
+
+ bool bUseVtxCol = mpMaterial->SetupGX(
+ detail::IsModulateVertexColor(mVtxColors, mGlbAlpha), mGlbAlpha);
+ nw4hbm::lyt::detail::SetVertexFormat(bUseVtxCol, mTexCoordAry.GetSize());
+
+ detail::DrawQuad(GetVtxPos(), mSize, mTexCoordAry.GetSize(), mTexCoordAry.GetArray(),
+ bUseVtxCol ? mVtxColors : NULL, mGlbAlpha);
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp
new file mode 100644
index 0000000000..f421d7210a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_resourceAccessor.cpp
@@ -0,0 +1,15 @@
+#include "resourceAccessor.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ ResourceAccessor::~ResourceAccessor() {}
+
+ ResourceAccessor::ResourceAccessor() {}
+
+ ut::Font* ResourceAccessor::GetFont(const char*) {
+ return NULL;
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp
new file mode 100644
index 0000000000..1dc5fea683
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_textBox.cpp
@@ -0,0 +1,558 @@
+#include "../ut/CharStrmReader.h"
+#include "../ut/Font.h"
+#include "../ut/ResFont.h"
+
+#include "common.h"
+#include "layout.h"
+#include "material.h"
+#include "pane.h"
+#include "textBox.h"
+
+#include "new.h"
+
+namespace nw4hbm {
+ namespace lyt {
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(TextBox, Pane);
+ }
+} // namespace nw4hbm
+
+namespace {
+ using namespace nw4hbm;
+
+ u8 ClampColor(s16 colVal);
+
+ ut::Color GetColor(const GXColorS10& src) {
+ GXColor dst;
+
+ dst.r = ClampColor(src.r);
+ dst.g = ClampColor(src.g);
+ dst.b = ClampColor(src.b);
+ dst.a = ClampColor(src.a);
+
+ return dst;
+ }
+
+ u8 ClampColor(s16 value) {
+ return value < 0 ? 0 : (value > 255 ? 255 : value);
+ }
+
+ template <typename T>
+ int CalcLineRectImpl(ut::Rect* pRect, ut::TextWriterBase<T>* pTextWriter, const T* str,
+ int length, f32 maxWidth, bool* pbOver) {
+ NW4HBM_ASSERT_VALID_PTR(71, pTextWriter);
+ NW4HBM_ASSERT_VALID_PTR(72, pRect);
+ NW4HBM_ASSERT_VALID_PTR(73, str);
+ NW4R_ASSERT_MIN(74, length, 0);
+ ut::PrintContext<T> context = {pTextWriter, str, 0.0f, 0.0f, 0};
+ const ut::Font* font = pTextWriter->GetFont();
+
+ f32 x = 0.0f;
+ bool bCharSpace = false;
+
+ NW4HBM_ASSERT_VALID_PTR(83, font);
+ ut::CharStrmReader reader = font->GetCharStrmReader();
+ const T* prStrPos = static_cast<const T*>(reader.GetCurrentPos());
+
+ pRect->left = 0.0f;
+ pRect->right = 0.0f;
+ pRect->top = ut::Min(0.0f, pTextWriter->GetLineHeight());
+ pRect->bottom = ut::Max(0.0f, pTextWriter->GetLineHeight());
+
+ *pbOver = false;
+
+ reader.Set(str);
+
+ ut::Rect prMaxRect = *pRect;
+
+ for (u16 code = reader.Next();
+ static_cast<const T*>(reader.GetCurrentPos()) - str <= length;
+ prStrPos = static_cast<const T*>(reader.GetCurrentPos()), code = reader.Next(),
+ prMaxRect = *pRect)
+ {
+ if ((int)code < L' ') {
+ ut::Operation operation;
+ ut::Rect rect(x, 0.0f, 0.0f, 0.0f);
+
+ context.str = static_cast<const T*>(reader.GetCurrentPos());
+ context.flags = 0;
+ context.flags |= bCharSpace ? 0 : 1;
+
+ pTextWriter->SetCursorX(x);
+
+ operation = pTextWriter->GetTagProcessor().CalcRect(&rect, code, &context);
+
+ NW4HBM_ASSERT_VALID_PTR(123, context.str);
+ reader.Set(context.str);
+
+ pRect->left = ut::Min(pRect->left, rect.left);
+ pRect->top = ut::Min(pRect->top, rect.top);
+ pRect->right = ut::Max(pRect->right, rect.right);
+ pRect->bottom = ut::Max(pRect->bottom, rect.bottom);
+
+ x = pTextWriter->GetCursorX();
+
+ if (pRect->GetWidth() > maxWidth) {
+ *pbOver = true;
+ break;
+ }
+
+ if (operation == ut::OPERATION_END_DRAW) {
+ return length;
+ } else if (operation == ut::OPERATION_NO_CHAR_SPACE) {
+ bCharSpace = false;
+ } else if (operation == ut::OPERATION_CHAR_SPACE) {
+ bCharSpace = true;
+ } else if (operation == ut::OPERATION_NEXT_LINE) {
+ goto end_draw;
+ }
+ } else {
+ if (bCharSpace) {
+ x += pTextWriter->GetCharSpace();
+ }
+
+ bCharSpace = true;
+
+ if (pTextWriter->IsWidthFixed()) {
+ x += pTextWriter->GetFixedWidth();
+ } else {
+ x += pTextWriter->GetFont()->GetCharWidth(code) * pTextWriter->GetScaleH();
+ }
+
+ pRect->left = ut::Min(pRect->left, x);
+ pRect->right = ut::Max(pRect->right, x);
+
+ if (pRect->GetWidth() > maxWidth) {
+ *pbOver = true;
+ goto end_draw;
+ }
+ }
+ }
+
+ end_draw:
+ if (*pbOver && prStrPos) {
+ *pRect = prMaxRect;
+ return prStrPos - str;
+ } else {
+ return static_cast<const T*>(reader.GetCurrentPos()) - str;
+ }
+ }
+
+ template <typename T>
+ void CalcStringRectImpl(ut::Rect* pRect, ut::TextWriterBase<T>* pTextWriter, const T* str,
+ int length, f32 maxWidth) {
+ NW4HBM_ASSERT_VALID_PTR(218, pTextWriter);
+ NW4HBM_ASSERT_VALID_PTR(219, pRect);
+ NW4HBM_ASSERT_VALID_PTR(220, str);
+ NW4R_ASSERT_MIN(221, length, 0);
+ int remain = length;
+ const T* pos = str;
+
+ pRect->left = 0.0f;
+ pRect->right = 0.0f;
+ pRect->top = 0.0f;
+ pRect->bottom = 0.0f;
+ pTextWriter->SetCursor(0.0f, 0.0f);
+
+ do {
+ ut::Rect rect;
+ bool bOver;
+ int read = CalcLineRectImpl(&rect, pTextWriter, pos, remain, maxWidth, &bOver);
+
+ if (bOver) {
+ CalcLineRectImpl(&rect, pTextWriter, L"\n", 1, maxWidth, &bOver);
+ }
+
+ pos += read;
+ remain -= read;
+
+ pRect->left = ut::Min(pRect->left, rect.left);
+ pRect->top = ut::Min(pRect->top, rect.top);
+ pRect->right = ut::Max(pRect->right, rect.right);
+ pRect->bottom = ut::Max(pRect->bottom, rect.bottom);
+ } while (remain > 0);
+ }
+
+ template <typename T>
+ int CalcLineStrNum(f32* pWidth, ut::TextWriterBase<T>* pTextWriter, const T* str, int length,
+ f32 maxWidth, bool* pbOver) {
+ NW4HBM_ASSERT_VALID_PTR(275, pTextWriter);
+ NW4HBM_ASSERT_VALID_PTR(276, str);
+ NW4R_ASSERT_MIN(277, length, 0);
+
+ ut::Rect rect;
+ ut::TextWriterBase<T> myCopy = *pTextWriter;
+ myCopy.SetCursor(0.0f, 0.0f);
+
+ int ret = CalcLineRectImpl(&rect, &myCopy, str, length, maxWidth, pbOver);
+
+ *pWidth = rect.GetWidth();
+ return ret;
+ }
+
+ template <typename T>
+ void CalcStringRect(ut::Rect* pRect, ut::TextWriterBase<T>* pTextWriter, const T* str,
+ int length, f32 maxWidth) {
+ NW4HBM_ASSERT_VALID_PTR(311, pTextWriter);
+ NW4HBM_ASSERT_VALID_PTR(312, pRect);
+ NW4HBM_ASSERT_VALID_PTR(313, str);
+ NW4R_ASSERT_MIN(314, length, 0);
+ ut::TextWriterBase<T> myCopy = *pTextWriter;
+ CalcStringRectImpl(pRect, &myCopy, str, length, maxWidth);
+ }
+
+} // namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ TextBox::TextBox(u16 allocStrLen, const wchar_t* str, const ut::Font* pFont) : Pane() {
+ Init(allocStrLen);
+ SetString(str);
+ SetFont(pFont);
+ }
+
+ TextBox::TextBox(const res::TextBox* pBlock, const ResBlockSet& resBlockSet)
+ : Pane(pBlock) {
+ u16 allocStrBufLen = pBlock->textBufBytes / sizeof(wchar_t);
+ if (allocStrBufLen) {
+ allocStrBufLen = allocStrBufLen - 1;
+ }
+
+ Init(allocStrBufLen);
+
+ if (pBlock->textStrBytes >= 2 && mTextBuf != NULL) {
+ const wchar_t* pBlockText =
+ detail::ConvertOffsToPtr<wchar_t>(pBlock, pBlock->textStrOffset);
+ const u16 resStrLen = pBlock->textStrBytes / sizeof(wchar_t) - 1;
+
+ SetString(pBlockText, 0, resStrLen);
+ }
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mTextColors); i++) {
+ mTextColors[i] = pBlock->textCols[i];
+ }
+
+ mFontSize = pBlock->fontSize;
+ mTextPosition = pBlock->textPosition;
+ mCharSpace = pBlock->charSpace;
+ mLineSpace = pBlock->lineSpace;
+
+ NW4HBM_ASSERT_CHECK_NULL(395, resBlockSet.pFontList);
+ NW4HBM_ASSERT(396, pBlock->fontIdx < resBlockSet.pFontList->fontNum);
+
+ const res::Font* fonts = detail::ConvertOffsToPtr<res::Font>(
+ resBlockSet.pFontList, sizeof(*resBlockSet.pFontList));
+
+ const char* fontName =
+ detail::ConvertOffsToPtr<char>(fonts, fonts[pBlock->fontIdx].nameStrOffset);
+
+ if (ut::Font* pFont = resBlockSet.pResAccessor->GetFont(fontName)) {
+ mpFont = pFont;
+ } else if (void* fontRes =
+ resBlockSet.pResAccessor->GetResource('font', fontName, NULL))
+ {
+ if (void* pMemFont = Layout::AllocMemory(sizeof(ut::ResFont))) {
+ ut::ResFont* pResFont = new (pMemFont) ut::ResFont();
+ bool bSuccess = pResFont->SetResource(fontRes);
+
+ if (!bSuccess) {
+ NW4R_DB_ASSERTMSG(410, false, "Fail to load ResFont.");
+ }
+
+ mpFont = pResFont;
+ mTextBoxFlag.allocFont = true;
+ }
+ }
+
+ if (void* pMemMaterial = Layout::AllocMemory(sizeof(Material))) {
+ NW4HBM_ASSERT_CHECK_NULL(420, resBlockSet.pMaterialList);
+
+ const u32* matOffsTbl = detail::ConvertOffsToPtr<u32>(
+ resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList));
+
+ const res::Material* pResMaterial = detail::ConvertOffsToPtr<res::Material>(
+ resBlockSet.pMaterialList, matOffsTbl[pBlock->materialIdx]);
+
+ mpMaterial = new (pMemMaterial) Material(pResMaterial, resBlockSet);
+ }
+ }
+
+ void TextBox::Init(u16 allocStrLen) {
+ mTextBuf = NULL;
+ mTextBufBytes = 0;
+ mTextLen = 0;
+
+ mpFont = NULL;
+ mFontSize = Size(0.0f, 0.0f);
+
+ SetTextPositionH(HORIZONTALPOSITION_CENTER);
+ SetTextPositionV(VERTICALPOSITION_CENTER);
+
+ mLineSpace = 0.0f;
+ mCharSpace = 0.0f;
+ mpTagProcessor = NULL;
+
+ memset(&mTextBoxFlag, 0, sizeof(mTextBoxFlag));
+
+ if (allocStrLen != 0) {
+ AllocStringBuffer(allocStrLen);
+ }
+ }
+
+ TextBox::~TextBox() {
+ SetFont(NULL);
+
+ if (mpMaterial != NULL && !mpMaterial->IsUserAllocated()) {
+ mpMaterial->~Material();
+ Layout::FreeMemory(mpMaterial);
+ mpMaterial = NULL;
+ }
+
+ FreeStringBuffer();
+ }
+
+ ut::Color TextBox::GetVtxColor(u32 idx) const {
+ NW4HBM_ASSERT(467, idx < VERTEXCOLOR_MAX);
+ return GetTextColor(idx / 2);
+ }
+
+ void TextBox::SetVtxColor(u32 idx, ut::Color value) {
+ NW4HBM_ASSERT(478, idx < VERTEXCOLOR_MAX);
+ SetTextColor(idx / 2, value);
+ }
+
+ u8 TextBox::GetVtxColorElement(u32 idx) const {
+ NW4HBM_ASSERT(486, idx < ANIMTARGET_VERTEXCOLOR_MAX);
+ return reinterpret_cast<const u8*>(mTextColors + idx / 8)[idx % 4];
+ }
+
+ void TextBox::SetVtxColorElement(u32 idx, u8 value) {
+ NW4HBM_ASSERT(494, idx < ANIMTARGET_VERTEXCOLOR_MAX);
+ reinterpret_cast<u8*>(mTextColors + idx / 8)[idx % 4] = value;
+ }
+
+ const ut::Rect TextBox::GetTextDrawRect(const DrawInfo& drawInfo) const {
+ ut::WideTextWriter writer;
+
+ writer.SetFont(*mpFont);
+ writer.SetFontSize(mFontSize.width, mFontSize.height);
+ writer.SetLineSpace(mLineSpace);
+ writer.SetCharSpace(mCharSpace);
+
+ if (mpTagProcessor) {
+ writer.SetTagProcessor(mpTagProcessor);
+ }
+
+ ut::Rect rect = GetTextDrawRect(&writer);
+
+ if (drawInfo.IsYAxisUp()) {
+ rect.top = -rect.top;
+ rect.bottom = -rect.bottom;
+ }
+
+ return rect;
+ }
+
+ void TextBox::DrawSelf(const DrawInfo& drawInfo) {
+ if (mTextBuf == NULL || mpFont == NULL || mpMaterial == NULL) {
+ return;
+ }
+
+ LoadMtx(drawInfo);
+
+ ut::WideTextWriter writer;
+ writer.SetFont(*mpFont);
+ writer.SetFontSize(mFontSize.width, mFontSize.height);
+ writer.SetLineSpace(mLineSpace);
+ writer.SetCharSpace(mCharSpace);
+
+ ut::Color topCol = detail::MultipleAlpha(mTextColors[TEXTCOLOR_TOP], mGlbAlpha);
+ ut::Color btmCol = detail::MultipleAlpha(mTextColors[TEXTCOLOR_BOTTOM], mGlbAlpha);
+ writer.SetGradationMode(topCol != btmCol ? ut::CharWriter::GRADMODE_V :
+ ut::CharWriter::GRADMODE_NONE);
+
+ writer.SetTextColor(topCol, btmCol);
+
+ ut::Color minCol = GetColor(mpMaterial->GetTevColor(TEVCOLOR_REG0));
+ ut::Color maxCol = GetColor(mpMaterial->GetTevColor(TEVCOLOR_REG1));
+ writer.SetColorMapping(minCol, maxCol);
+
+ if (mpTagProcessor) {
+ writer.SetTagProcessor(mpTagProcessor);
+ }
+
+ writer.SetupGX();
+
+ ut::Rect textRect = GetTextDrawRect(&writer);
+
+ f32 hMag = GetTextMagH();
+ wchar_t* strPos = mTextBuf;
+ f32 textWidth = textRect.GetWidth();
+
+ writer.SetCursor(textRect.left, textRect.top);
+
+ for (int remain = mTextLen; remain > 0;) {
+ f32 lineWidth;
+ bool bOver;
+ int lineStrNum =
+ CalcLineStrNum(&lineWidth, &writer, strPos, remain, mSize.width, &bOver);
+ f32 textPosX = hMag * (textWidth - lineWidth);
+
+ writer.SetCursorX(textRect.left + textPosX);
+ writer.Print(strPos, lineStrNum);
+
+ if (bOver) {
+ writer.Print(L"\n");
+ }
+
+ strPos += lineStrNum;
+ remain -= lineStrNum;
+ }
+ }
+
+ u16 TextBox::GetStringBufferLength() const {
+ if (mTextBufBytes == 0) {
+ return 0;
+ } else {
+ NW4HBM_ASSERT(605, mTextBufBytes >= sizeof(wchar_t));
+ }
+
+ return mTextBufBytes / sizeof(wchar_t) - 1;
+ }
+
+ void TextBox::AllocStringBuffer(u16 minLen) {
+ if (minLen == 0) {
+ return;
+ }
+
+ u16 allocBytes = sizeof(wchar_t) * (minLen + 1);
+ if (allocBytes <= mTextBufBytes) {
+ return;
+ }
+
+ FreeStringBuffer();
+
+ mTextBuf = static_cast<wchar_t*>(Layout::AllocMemory(allocBytes));
+ if (mTextBuf != NULL) {
+ mTextBufBytes = allocBytes;
+ }
+ }
+
+ void TextBox::FreeStringBuffer() {
+ if (mTextBuf != NULL) {
+ Layout::FreeMemory(mTextBuf);
+ mTextBuf = NULL;
+ mTextBufBytes = 0;
+ }
+ }
+
+ u16 TextBox::SetString(const wchar_t* str, u16 dstIdx) {
+ return SetString(str, dstIdx, std::wcslen(str));
+ }
+
+ u16 TextBox::SetString(const wchar_t* str, u16 dstIdx, u16 strLen) {
+ if (mTextBuf == NULL) {
+ NW4R_DB_WARNING(708, false, "mTextBuf is NULL.\n");
+ return 0;
+ }
+
+ u16 bufLen = GetStringBufferLength();
+ if (dstIdx >= bufLen) {
+ NW4R_DB_WARNING(716, false, "dstIdx is out of range.\n");
+ return 0;
+ }
+
+ const u16 cpLen = ut::Min<u16>(strLen, bufLen - dstIdx);
+
+ if (cpLen < strLen) {
+ NW4R_DB_WARNING(721, false, "%d character(s) droped.\n", strLen - cpLen);
+ }
+
+ std::memcpy(&mTextBuf[dstIdx], str, sizeof(wchar_t) * cpLen);
+
+ mTextLen = dstIdx + cpLen;
+ mTextBuf[mTextLen] = L'\0';
+
+ return cpLen;
+ }
+
+ const ut::Font* TextBox::GetFont() const {
+ return mpFont;
+ }
+
+ void TextBox::SetFont(const ut::Font* pFont) {
+ if (mTextBoxFlag.allocFont) {
+ NW4HBM_ASSERT_CHECK_NULL(775, mpFont);
+ mpFont->~Font();
+ Layout::FreeMemory(const_cast<ut::Font*>(mpFont));
+ mTextBoxFlag.allocFont = false;
+ }
+
+ mpFont = pFont;
+
+ if (mpFont) {
+ SetFontSize(Size(mpFont->GetWidth(), mpFont->GetHeight()));
+ } else {
+ SetFontSize(Size(0.0f, 0.0f));
+ }
+ }
+
+ const ut::Rect TextBox::GetTextDrawRect(ut::WideTextWriter* pWriter) const {
+ ut::Rect textRect;
+
+ pWriter->SetCursor(0.0f, 0.0f);
+ CalcStringRect(&textRect, pWriter, mTextBuf, mTextLen, mSize.width);
+
+ math::VEC2 basePt = Pane::GetVtxPos();
+
+ textRect.MoveTo(basePt.x + (mSize.width - textRect.GetWidth()) * GetTextMagH(),
+ basePt.y + (mSize.height - textRect.GetHeight()) * GetTextMagV());
+
+ return textRect;
+ }
+
+ f32 TextBox::GetTextMagH() const {
+ f32 hMag = 0.0f;
+
+ switch (GetTextPositionH()) {
+ default:
+ case HORIZONTALPOSITION_LEFT: {
+ hMag = 0.0f;
+ break;
+ }
+ case HORIZONTALPOSITION_CENTER: {
+ hMag = 0.5f;
+ break;
+ }
+ case HORIZONTALPOSITION_RIGHT: {
+ hMag = 1.0f;
+ break;
+ }
+ }
+
+ return hMag;
+ }
+
+ f32 TextBox::GetTextMagV() const {
+ f32 vMag = 0.0f;
+
+ switch (GetTextPositionV()) {
+ default:
+ case HORIZONTALPOSITION_LEFT: {
+ vMag = 0.0f;
+ break;
+ }
+ case HORIZONTALPOSITION_CENTER: {
+ vMag = 0.5f;
+ break;
+ }
+ case HORIZONTALPOSITION_RIGHT: {
+ vMag = 1.0f;
+ break;
+ }
+ }
+
+ return vMag;
+ }
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_types.h b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_types.h
new file mode 100644
index 0000000000..effcb1f1f8
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_types.h
@@ -0,0 +1,449 @@
+#ifndef NW4HBM_LYT_TYPES_H
+#define NW4HBM_LYT_TYPES_H
+
+#include "revolution/types.h"
+
+#include <revolution/gx.h>
+
+#include "../math/types.h"
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace lyt {
+ static const u32 RESOURCE_TYPE_ANIMATION = 'anim';
+ static const u32 RESOURCE_TYPE_LAYOUT = 'blyt';
+ static const u32 RESOURCE_TYPE_FONT = 'font';
+ static const u32 RESOURCE_TYPE_TEXTURE = 'timg';
+
+ enum {
+ VERTEXCOLOR_LT = 0,
+ VERTEXCOLOR_RT,
+ VERTEXCOLOR_LB,
+ VERTEXCOLOR_RB,
+
+ VERTEXCOLOR_MAX
+ };
+
+ enum {
+ TEVCOLOR_REG0 = 0,
+ TEVCOLOR_REG1,
+ TEVCOLOR_REG2,
+
+ TEVCOLOR_MAX
+ };
+
+ enum {
+ HORIZONTALPOSITION_LEFT = 0,
+ HORIZONTALPOSITION_CENTER,
+ HORIZONTALPOSITION_RIGHT,
+ HORIZONTALPOSITION_MAX
+ };
+ enum {
+ VERTICALPOSITION_TOP = 0,
+ VERTICALPOSITION_CENTER,
+ VERTICALPOSITION_BOTTOM,
+ VERTICALPOSITION_MAX
+ };
+
+ enum {
+ ORIGINTYPE_TOPLEFT = 0,
+ ORIGINTYPE_CENTER,
+
+ ORIGINTYPE_MAX
+ };
+
+ enum {
+ TEXTCOLOR_TOP = 0,
+ TEXTCOLOR_BOTTOM,
+
+ TEXTCOLOR_MAX
+ };
+
+ enum {
+ WINDOWFRAME_LT = 0,
+ WINDOWFRAME_RT,
+ WINDOWFRAME_LB,
+ WINDOWFRAME_RB,
+
+ WINDOWFRAME_L,
+ WINDOWFRAME_R,
+ WINDOWFRAME_T,
+ WINDOWFRAME_B,
+
+ WINDOWFRAME_MAX
+ };
+
+ enum {
+ TEXTUREFLIP_NONE,
+ TEXTUREFLIP_H,
+ TEXTUREFLIP_V,
+ TEXTUREFLIP_90,
+ TEXTUREFLIP_180,
+ TEXTUREFLIP_270,
+
+ TEXTUREFLIP_MAX
+ };
+
+ enum {
+ FLIPINDEX_X,
+ FLIPINDEX_Y,
+
+ FLIPINDEX_MAX
+ };
+
+ namespace detail {
+ template <typename T>
+ const T* ConvertOffsToPtr(const void* baseAddress, unsigned offset) {
+ return reinterpret_cast<const T*>(reinterpret_cast<unsigned>(baseAddress) + offset);
+ }
+
+ template <typename T>
+ T* ConvertOffsToPtr(void* baseAddress, unsigned offset) {
+ return reinterpret_cast<T*>(reinterpret_cast<unsigned>(baseAddress) + offset);
+ }
+
+ template <typename T>
+ inline void SetBit(T* pBits, int pos, bool val) {
+ const T mask = (T)(~(1 << pos));
+ *pBits &= mask;
+ *pBits |= (val ? 1 : 0) << pos;
+ }
+
+ template <typename T>
+ inline bool TestBit(T bits, int pos) {
+ const T mask = (T)(1 << pos);
+ return 0 != (bits & mask);
+ }
+
+ template <typename T>
+ T GetBits(T bits, int pos, int len) {
+ T mask = static_cast<T>(~(-1 << len));
+ return static_cast<T>((bits >> pos) & mask);
+ }
+ } // namespace detail
+
+ typedef struct Size {
+ Size() : width(), height() {}
+
+ Size(f32 aWidth, f32 aHeight) : width(aWidth), height(aHeight) {}
+
+ Size(const Size& other) : width(other.width), height(other.height) {}
+
+ friend bool operator==(const Size& a, const Size& b) {
+ return a.width == b.width && a.height == b.height;
+ }
+
+ /* 0x00 */ f32 width;
+ /* 0x04 */ f32 height;
+ } Size; // size = 0x08
+
+ typedef struct TexSRT {
+ /* 0x00 */ math::VEC2 translate;
+ /* 0x08 */ f32 rotate;
+ /* 0x0C */ math::VEC2 scale;
+ } TexSRT;
+
+ class TexCoordGen {
+ public:
+ TexCoordGen() : reserve(0) { Set(GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); }
+
+ u32 GetTexMtx() const { return texMtx; }
+
+ GXTexGenType GetTexGenType() const { return static_cast<GXTexGenType>(texGenType); }
+ GXTexGenSrc GetTexGenSrc() const { return static_cast<GXTexGenSrc>(texGenSrc); }
+
+ void Set(GXTexGenType aTexGenType, GXTexGenSrc aTexGenSrc, u32 aTexMtx) {
+ texGenType = aTexGenType;
+ texGenSrc = aTexGenSrc;
+ texMtx = aTexMtx;
+ }
+
+ private:
+ /* 0x00 */ u8 texGenType;
+ /* 0x01 */ u8 texGenSrc;
+ /* 0x02 */ u8 texMtx;
+ /* 0x03 */ u8 reserve;
+ };
+
+ class IndirectStage {
+ public:
+ IndirectStage() { Set(GX_TEXCOORD0, GX_TEXMAP0, GX_ITS_1, GX_ITS_1); }
+
+ GXTexCoordID GetTexCoordGen() const { return static_cast<GXTexCoordID>(texCoordGen); }
+
+ GXTexMapID GetTexMap() const { return static_cast<GXTexMapID>(texMap); }
+
+ GXIndTexScale GetScaleS() const { return static_cast<GXIndTexScale>(scaleS); }
+ GXIndTexScale GetScaleT() const { return static_cast<GXIndTexScale>(scaleT); }
+
+ void Set(GXTexCoordID aTexCoordGen, GXTexMapID aTexMap, GXIndTexScale aScaleS,
+ GXIndTexScale aScaleT) {
+ texCoordGen = aTexCoordGen;
+
+ texMap = aTexMap;
+ scaleS = aScaleS;
+ scaleT = aScaleT;
+ }
+
+ private:
+ /* 0x00 */ u8 texCoordGen;
+ /* 0x01 */ u8 texMap;
+ /* 0x02 */ u8 scaleS;
+ /* 0x03 */ u8 scaleT;
+ };
+
+ class TevSwapMode {
+ public:
+ GXTevColorChan GetR() const { return static_cast<GXTevColorChan>((swap) & 0x03); }
+ GXTevColorChan GetG() const { return static_cast<GXTevColorChan>((swap >> 2) & 0x03); }
+ GXTevColorChan GetB() const { return static_cast<GXTevColorChan>((swap >> 4) & 0x03); }
+ GXTevColorChan GetA() const { return static_cast<GXTevColorChan>((swap >> 6) & 0x03); }
+
+ void Set(GXTevColorChan r, GXTevColorChan g, GXTevColorChan b, GXTevColorChan a) {
+ swap = r | g << 2 | b << 4 | a << 6;
+ }
+
+ private:
+ /* 0x00 */ u8 swap;
+ };
+
+ class TevStageInOp {
+ public:
+ u8 GetA() const { return ab & 0x0F; }
+ u8 GetB() const { return (ab >> 4) & 0x0F; }
+ u8 GetC() const { return cd & 0x0F; }
+ u8 GetD() const { return (cd >> 4) & 0x0F; }
+
+ u8 GetScale() const { return (op >> 6) & 0x03; }
+ u8 GetBias() const { return (op >> 4) & 0x03; }
+ u8 GetOp() const { return op & 0x0F; }
+
+ u8 GetKSel() const { return (cl >> 3) & 0x1F; }
+ u8 GetOutReg() const { return (cl >> 1) & 0x03; }
+ bool IsClamp() const { return static_cast<bool>(cl & 0x01); }
+
+ void SetIn(u8 a, u8 b, u8 c, u8 d) {
+ ab = a | b << 4;
+ cd = c | d << 4;
+ }
+
+ void SetOp(u8 aOp, u8 bias, u8 scale, bool clamp, u8 outReg, u8 kSel) {
+ op = aOp | bias << 4 | scale << 6;
+ cl = (clamp ? 1 : 0) | outReg << 1 | kSel << 3;
+ }
+
+ private:
+ /* 0x00 */ u8 ab;
+ /* 0x01 */ u8 cd;
+ /* 0x02 */ u8 op;
+ /* 0x03 */ u8 cl;
+ };
+
+ class TevStage {
+ public:
+ TevStage() {
+ SetOrder(GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ SetColorIn(GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC);
+ SetAlphaIn(GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA);
+ SetColorOp(GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV,
+ GX_TEV_KCSEL_K0);
+ SetAlphaOp(GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV,
+ GX_TEV_KASEL_K0_R);
+ SetIndirect(GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF,
+ GX_ITW_OFF, false, false, GX_ITBA_OFF);
+ }
+
+ GXTexCoordID GetTexCoordGen() const { return static_cast<GXTexCoordID>(texCoordGen); }
+ GXChannelID GetColorChan() const { return static_cast<GXChannelID>(colChan); }
+
+ GXTexMapID GetTexMap() const {
+ return static_cast<GXTexMapID>((swapSel & 1) << 8 | texMap);
+ }
+
+ GXTevSwapSel GetTexSwapSel() const {
+ return static_cast<GXTevSwapSel>((swapSel >> 3) & 0x03);
+ }
+ GXTevSwapSel GetRasSwapSel() const {
+ return static_cast<GXTevSwapSel>((swapSel >> 1) & 0x03);
+ }
+
+ GXTevColorArg GetColorInA() const { return static_cast<GXTevColorArg>(colIn.GetA()); }
+ GXTevColorArg GetColorInB() const { return static_cast<GXTevColorArg>(colIn.GetB()); }
+ GXTevColorArg GetColorInC() const { return static_cast<GXTevColorArg>(colIn.GetC()); }
+ GXTevColorArg GetColorInD() const { return static_cast<GXTevColorArg>(colIn.GetD()); }
+
+ GXTevOp GetColorOp() const { return static_cast<GXTevOp>(colIn.GetOp()); }
+ GXTevBias GetColorBias() const { return static_cast<GXTevBias>(colIn.GetBias()); }
+ GXTevScale GetColorScale() const { return static_cast<GXTevScale>(colIn.GetScale()); }
+
+ bool IsColorClamp() const { return colIn.IsClamp(); }
+
+ GXTevRegID GetColorOutReg() const { return static_cast<GXTevRegID>(colIn.GetOutReg()); }
+
+ GXTevKColorSel GetKColorSel() const {
+ return static_cast<GXTevKColorSel>(colIn.GetKSel());
+ }
+
+ GXTevAlphaArg GetAlphaInA() const { return static_cast<GXTevAlphaArg>(alpIn.GetA()); }
+ GXTevAlphaArg GetAlphaInB() const { return static_cast<GXTevAlphaArg>(alpIn.GetB()); }
+ GXTevAlphaArg GetAlphaInC() const { return static_cast<GXTevAlphaArg>(alpIn.GetC()); }
+ GXTevAlphaArg GetAlphaInD() const { return static_cast<GXTevAlphaArg>(alpIn.GetD()); }
+
+ GXTevOp GetAlphaOp() const { return static_cast<GXTevOp>(alpIn.GetOp()); }
+ GXTevBias GetAlphaBias() const { return static_cast<GXTevBias>(alpIn.GetBias()); }
+ GXTevScale GetAlphaScale() const { return static_cast<GXTevScale>(alpIn.GetScale()); }
+
+ bool IsAlphaClamp() const { return alpIn.IsClamp(); }
+
+ GXTevRegID GetAlphaOutReg() const { return static_cast<GXTevRegID>(alpIn.GetOutReg()); }
+
+ GXTevKAlphaSel GetKAlphaSel() const {
+ return static_cast<GXTevKAlphaSel>(alpIn.GetKSel());
+ }
+
+ GXIndTexStageID GetIndStage() const { return static_cast<GXIndTexStageID>(indStage); }
+ GXIndTexMtxID GetIndMtxSel() const {
+ return static_cast<GXIndTexMtxID>((indBiMt >> 3) & 0x0F);
+ }
+ GXIndTexBiasSel GetIndBiasSel() const {
+ return static_cast<GXIndTexBiasSel>(indBiMt & 0x07);
+ }
+
+ GXIndTexWrap GetIndWrapS() const { return static_cast<GXIndTexWrap>(indWrap & 0x07); }
+ GXIndTexWrap GetIndWrapT() const {
+ return static_cast<GXIndTexWrap>((indWrap >> 3) & 0x07);
+ }
+
+ GXIndTexAlphaSel GetIndAlphaSel() const {
+ return static_cast<GXIndTexAlphaSel>((indFoAdUtAl >> 4) & 0x03);
+ }
+
+ bool IsIndUtcLod() const { return static_cast<bool>((indFoAdUtAl >> 3) & 0x01); }
+ bool IsIndAddPrev() const { return static_cast<bool>((indFoAdUtAl >> 2) & 0x01); }
+
+ GXIndTexFormat GetIndFormat() const {
+ return static_cast<GXIndTexFormat>(indFoAdUtAl & 0x03);
+ }
+
+ void SetOrder(GXTexCoordID aTexCoordGen, GXTexMapID aTexMap, GXChannelID aColChan,
+ GXTevSwapSel rasSel, GXTevSwapSel texSel) {
+ texCoordGen = aTexCoordGen;
+ colChan = aColChan;
+ texMap = aTexMap;
+ swapSel = aTexMap >> 8 | rasSel << 1 | texSel << 3;
+ }
+
+ void SetColorIn(GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d) {
+ colIn.SetIn(a, b, c, d);
+ }
+
+ void SetColorOp(GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp,
+ GXTevRegID outReg, GXTevKColorSel kSel) {
+ colIn.SetOp(op, bias, scale, clamp, outReg, kSel);
+ }
+
+ void SetAlphaIn(GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d) {
+ alpIn.SetIn(a, b, c, d);
+ }
+
+ void SetAlphaOp(GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp,
+ GXTevRegID outReg, GXTevKAlphaSel kSel) {
+ alpIn.SetOp(op, bias, scale, clamp, outReg, kSel);
+ }
+
+ void SetIndirect(GXIndTexStageID stage, GXIndTexFormat format, GXIndTexBiasSel biasSel,
+ GXIndTexMtxID mtxSel, GXIndTexWrap wrapS, GXIndTexWrap wrapT,
+ bool addPrev, bool utcLod, GXIndTexAlphaSel alphaSel) {
+ indStage = stage;
+ indBiMt = biasSel | mtxSel << 3;
+ indWrap = wrapS | wrapT << 3;
+ indFoAdUtAl =
+ format | (addPrev ? 1 : 0) << 2 | (utcLod ? 1 : 0) << 3 | alphaSel << 4;
+ }
+
+ private:
+ /* 0x00 */ u8 texCoordGen;
+ /* 0x01 */ u8 colChan;
+ /* 0x02 */ u8 texMap;
+ /* 0x03 */ u8 swapSel;
+ /* 0x04 */ TevStageInOp colIn;
+ /* 0x08 */ TevStageInOp alpIn;
+ /* 0x0C */ u8 indStage;
+ /* 0x0D */ u8 indBiMt;
+ /* 0x0E */ u8 indWrap;
+ /* 0x0F */ u8 indFoAdUtAl;
+ };
+
+ class ChanCtrl {
+ public:
+ ChanCtrl() : reserve1(0), reserve2(0) { Set(GX_SRC_VTX, GX_SRC_VTX); }
+
+ GXColorSrc GetColorSrc() const { return static_cast<GXColorSrc>(matSrcCol); }
+ GXColorSrc GetAlphaSrc() const { return static_cast<GXColorSrc>(matSrcAlp); }
+
+ void Set(GXColorSrc colSrc, GXColorSrc alpSrc) {
+ matSrcCol = colSrc;
+ matSrcAlp = alpSrc;
+ }
+
+ private:
+ /* 0x00 */ u8 matSrcCol;
+ /* 0x01 */ u8 matSrcAlp;
+ /* 0x02 */ u8 reserve1;
+ /* 0x03 */ u8 reserve2;
+ };
+
+ class AlphaCompare {
+ public:
+ AlphaCompare() { Set(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); }
+
+ GXCompare GetComp0() const { return static_cast<GXCompare>(comp & 0x0F); }
+ GXCompare GetComp1() const { return static_cast<GXCompare>((comp >> 4) & 0x0F); }
+
+ GXAlphaOp GetOp() const { return static_cast<GXAlphaOp>(op); }
+
+ u8 GetRef0() const { return ref0; }
+ u8 GetRef1() const { return ref1; }
+
+ void Set(GXCompare aComp0, u8 aRef0, GXAlphaOp aOp, GXCompare aComp1, u8 aRef1) {
+ comp = aComp0 | aComp1 << 4;
+ op = aOp;
+ ref0 = aRef0;
+ ref1 = aRef1;
+ }
+
+ private:
+ /* 0x00 */ u8 comp;
+ /* 0x01 */ u8 op;
+ /* 0x02 */ u8 ref0;
+ /* 0x03 */ u8 ref1;
+ };
+
+ class BlendMode {
+ public:
+ BlendMode() { Set(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); }
+
+ GXBlendMode GetType() const { return static_cast<GXBlendMode>(type); }
+ GXBlendFactor GetSrcFactor() const { return static_cast<GXBlendFactor>(srcFactor); }
+ GXBlendFactor GetDstFactor() const { return static_cast<GXBlendFactor>(dstFactor); }
+ GXLogicOp GetOp() const { return static_cast<GXLogicOp>(op); }
+
+ void Set(GXBlendMode aType, GXBlendFactor aSrcFactor, GXBlendFactor aDstFactor,
+ GXLogicOp aOp) {
+ type = aType;
+ srcFactor = aSrcFactor;
+ dstFactor = aDstFactor;
+ op = aOp;
+ }
+
+ private:
+ /* 0x00 */ u8 type;
+ /* 0x01 */ u8 srcFactor;
+ /* 0x02 */ u8 dstFactor;
+ /* 0x03 */ u8 op;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp
new file mode 100644
index 0000000000..bc909c15eb
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/lyt_window.cpp
@@ -0,0 +1,568 @@
+#include "window.h"
+
+#include "layout.h"
+
+#include "new.h"
+
+struct TextureFlipInfo {
+ /* 0x00 */ u8 coords[4][2];
+ /* 0x08 */ u8 idx[2];
+}; // size = 0x0A
+
+namespace {
+ // pretend this is nw4hbm::lyt
+ using namespace nw4hbm;
+ using namespace nw4hbm::lyt;
+
+ // NOTE the misspelling of GetTextureFlipInfo
+ TextureFlipInfo& GetTexutreFlipInfo(u8 textureFlip);
+
+ void GetLTFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize);
+ void GetLTTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip);
+ void GetRTFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize);
+ void GetRTTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip);
+ void GetLBFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize);
+ void GetLBTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip);
+ void GetRBFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize);
+ void GetRBTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip);
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(Window, Pane);
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+//! TODO clean up
+
+namespace {
+
+ TextureFlipInfo& GetTexutreFlipInfo(u8 textureFlip) {
+ // clang-format off
+ static TextureFlipInfo flipInfos[] = // 0 1 2 3
+ { // in order of LT RT LB RB
+ {{{0, 0}, {1, 0}, {0, 1}, {1, 1}}, {0, 1}}, // 0 1 2 3 no flip
+ {{{1, 0}, {0, 0}, {1, 1}, {0, 1}}, {0, 1}}, // 1 0 3 2 horizontal flip
+ {{{0, 1}, {1, 1}, {0, 0}, {1, 0}}, {0, 1}}, // 2 3 0 1 vertical flip
+ {{{0, 1}, {0, 0}, {1, 1}, {1, 0}}, {1, 0}}, // 2 0 3 1, index flip cw 90 deg
+ {{{1, 1}, {0, 1}, {1, 0}, {0, 0}}, {0, 1}}, // 3 2 1 0 cw 180 deg
+ {{{1, 0}, {1, 1}, {0, 0}, {0, 1}}, {1, 0}} // 1 3 0 2, index flip cw 270 deg (ccw 90 deg)
+ };
+ // clang-format on
+
+ NW4HBM_ASSERT(50, textureFlip < TEXTUREFLIP_MAX);
+ return flipInfos[textureFlip];
+ }
+
+ void GetLTFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize) {
+ *pPt = basePt;
+ pSize->width = winSize.width - frameSize.r;
+ pSize->height = frameSize.t;
+ }
+
+ void GetLTTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip) {
+ TextureFlipInfo& flipInfo = GetTexutreFlipInfo(textureFlip);
+ int ix = flipInfo.idx[FLIPINDEX_X];
+ int iy = flipInfo.idx[FLIPINDEX_Y];
+
+ const math::VEC2 tSz(texSize.width, texSize.height);
+
+ texCds[VERTEXCOLOR_LT][ix] = texCds[VERTEXCOLOR_LB][ix] =
+ flipInfo.coords[VERTEXCOLOR_LT][ix];
+
+ texCds[VERTEXCOLOR_LT][iy] = texCds[VERTEXCOLOR_RT][iy] =
+ flipInfo.coords[VERTEXCOLOR_LT][iy];
+
+ texCds[VERTEXCOLOR_RB][ix] = texCds[VERTEXCOLOR_RT][ix] =
+ flipInfo.coords[VERTEXCOLOR_LT][ix] +
+ polSize.width /
+ ((flipInfo.coords[VERTEXCOLOR_RT][ix] - flipInfo.coords[VERTEXCOLOR_LT][ix]) *
+ tSz[ix]);
+
+ texCds[VERTEXCOLOR_RB][iy] = texCds[VERTEXCOLOR_LB][iy] =
+ flipInfo.coords[VERTEXCOLOR_LT][iy] +
+ polSize.height /
+ ((flipInfo.coords[VERTEXCOLOR_LB][iy] - flipInfo.coords[VERTEXCOLOR_LT][iy]) *
+ tSz[iy]);
+ }
+
+ void GetRTFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize) {
+ *pPt = math::VEC2(basePt.x + winSize.width - frameSize.r, basePt.y);
+ pSize->width = frameSize.r;
+ pSize->height = winSize.height - frameSize.b;
+ }
+
+ void GetRTTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip) {
+ TextureFlipInfo& flipInfo = GetTexutreFlipInfo(textureFlip);
+ int ix = flipInfo.idx[FLIPINDEX_X];
+ int iy = flipInfo.idx[FLIPINDEX_Y];
+
+ const math::VEC2 tSz(texSize.width, texSize.height);
+
+ texCds[VERTEXCOLOR_RT][ix] = texCds[VERTEXCOLOR_RB][ix] =
+ flipInfo.coords[VERTEXCOLOR_RT][ix];
+
+ texCds[VERTEXCOLOR_RT][iy] = texCds[VERTEXCOLOR_LT][iy] =
+ flipInfo.coords[VERTEXCOLOR_RT][iy];
+
+ texCds[VERTEXCOLOR_LB][ix] = texCds[VERTEXCOLOR_LT][ix] =
+ flipInfo.coords[VERTEXCOLOR_RT][ix] +
+ polSize.width /
+ ((flipInfo.coords[VERTEXCOLOR_LT][ix] - flipInfo.coords[VERTEXCOLOR_RT][ix]) *
+ tSz[ix]);
+
+ texCds[VERTEXCOLOR_LB][iy] = texCds[VERTEXCOLOR_RB][iy] =
+ flipInfo.coords[VERTEXCOLOR_RT][iy] +
+ polSize.height /
+ ((flipInfo.coords[VERTEXCOLOR_RB][iy] - flipInfo.coords[VERTEXCOLOR_RT][iy]) *
+ tSz[iy]);
+ }
+
+ void GetLBFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize) {
+ *pPt = math::VEC2(basePt.x, basePt.y + frameSize.t);
+ pSize->width = frameSize.l;
+ pSize->height = winSize.height - frameSize.t;
+ }
+
+ void GetLBTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip) {
+ TextureFlipInfo& flipInfo = GetTexutreFlipInfo(textureFlip);
+ int ix = flipInfo.idx[FLIPINDEX_X];
+ int iy = flipInfo.idx[FLIPINDEX_Y];
+
+ const math::VEC2 tSz(texSize.width, texSize.height);
+
+ texCds[VERTEXCOLOR_LB][ix] = texCds[VERTEXCOLOR_LT][ix] =
+ flipInfo.coords[VERTEXCOLOR_LB][ix];
+
+ texCds[VERTEXCOLOR_LB][iy] = texCds[VERTEXCOLOR_RB][iy] =
+ flipInfo.coords[VERTEXCOLOR_LB][iy];
+
+ texCds[VERTEXCOLOR_RT][ix] = texCds[VERTEXCOLOR_RB][ix] =
+ flipInfo.coords[VERTEXCOLOR_LB][ix] +
+ polSize.width /
+ ((flipInfo.coords[VERTEXCOLOR_RB][ix] - flipInfo.coords[VERTEXCOLOR_LB][ix]) *
+ tSz[ix]);
+
+ texCds[VERTEXCOLOR_RT][iy] = texCds[VERTEXCOLOR_LT][iy] =
+ flipInfo.coords[VERTEXCOLOR_LB][iy] +
+ polSize.height /
+ ((flipInfo.coords[VERTEXCOLOR_LT][iy] - flipInfo.coords[VERTEXCOLOR_LB][iy]) *
+ tSz[iy]);
+ }
+
+ void GetRBFrameSize(math::VEC2* pPt, Size* pSize, const math::VEC2& basePt, const Size& winSize,
+ const WindowFrameSize& frameSize) {
+ *pPt = math::VEC2(basePt.x + frameSize.l, basePt.y + winSize.height - frameSize.b);
+ pSize->width = winSize.width - frameSize.l;
+ pSize->height = frameSize.b;
+ }
+
+ void GetRBTexCoord(math::VEC2* texCds, const Size& polSize, const Size& texSize,
+ u8 textureFlip) {
+ TextureFlipInfo& flipInfo = GetTexutreFlipInfo(textureFlip);
+ int ix = flipInfo.idx[FLIPINDEX_X];
+ int iy = flipInfo.idx[FLIPINDEX_Y];
+
+ const math::VEC2 tSz(texSize.width, texSize.height);
+
+ texCds[VERTEXCOLOR_RB][ix] = texCds[VERTEXCOLOR_RT][ix] =
+ flipInfo.coords[VERTEXCOLOR_RB][ix];
+
+ texCds[VERTEXCOLOR_RB][iy] = texCds[VERTEXCOLOR_LB][iy] =
+ flipInfo.coords[VERTEXCOLOR_RB][iy];
+
+ texCds[VERTEXCOLOR_LT][ix] = texCds[VERTEXCOLOR_LB][ix] =
+ flipInfo.coords[VERTEXCOLOR_RB][ix] +
+ polSize.width /
+ ((flipInfo.coords[VERTEXCOLOR_LB][ix] - flipInfo.coords[VERTEXCOLOR_RB][ix]) *
+ tSz[ix]);
+
+ texCds[VERTEXCOLOR_LT][iy] = texCds[VERTEXCOLOR_RT][iy] =
+ flipInfo.coords[VERTEXCOLOR_RB][iy] +
+ polSize.height /
+ ((flipInfo.coords[VERTEXCOLOR_RT][iy] - flipInfo.coords[VERTEXCOLOR_RB][iy]) *
+ tSz[iy]);
+ }
+
+} // unnamed namespace
+
+namespace nw4hbm {
+ namespace lyt {
+
+ Window::Window(const res::Window* pBlock, const ResBlockSet& resBlockSet) : Pane(pBlock) {
+ mContentInflation = pBlock->inflation;
+
+ NW4HBM_ASSERT_CHECK_NULL(193, resBlockSet.pMaterialList);
+ const u32* const matOffsTbl = detail::ConvertOffsToPtr<u32>(
+ resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList));
+
+ const res::WindowContent* pResContent =
+ detail::ConvertOffsToPtr<res::WindowContent>(pBlock, pBlock->contentOffset);
+
+ for (int i = 0; i < (int)ARRAY_SIZE(mContent.vtxColors); i++) {
+ mContent.vtxColors[i] = pResContent->vtxCols[i];
+ }
+
+ if (pResContent->texCoordNum) {
+ u8 texCoordNum = ut::Min<u8>(pResContent->texCoordNum, 8);
+ mContent.texCoordAry.Reserve(texCoordNum);
+
+ if (!mContent.texCoordAry.IsEmpty()) {
+ mContent.texCoordAry.Copy(&pResContent[1], texCoordNum);
+ }
+ }
+
+ if (void* pMemMaterial = Layout::AllocMemory(sizeof(Material))) {
+ const res::Material* pResMaterial = detail::ConvertOffsToPtr<res::Material>(
+ resBlockSet.pMaterialList, matOffsTbl[pResContent->materialIdx]);
+
+ mpMaterial = new (pMemMaterial) Material(pResMaterial, resBlockSet);
+ }
+
+ mFrameNum = 0;
+ mFrames = NULL;
+
+ if (pBlock->frameNum) {
+ if ((mFrames = static_cast<Frame*>(
+ Layout::AllocMemory(sizeof(*mFrames) * pBlock->frameNum))))
+ {
+ mFrameNum = pBlock->frameNum;
+ const u32* frameOffsetTable =
+ detail::ConvertOffsToPtr<u32>(pBlock, pBlock->frameOffsetTableOffset);
+
+ for (int i = 0; i < mFrameNum; i++) {
+ const res::WindowFrame* pResWindowFrame =
+ detail::ConvertOffsToPtr<res::WindowFrame>(pBlock, frameOffsetTable[i]);
+
+ mFrames[i].textureFlip = pResWindowFrame->textureFlip;
+ mFrames[i].pMaterial = NULL;
+
+ if (void* pMemMaterial = Layout::AllocMemory(sizeof(Material))) {
+ const res::Material* pResMaterial =
+ detail::ConvertOffsToPtr<res::Material>(
+ resBlockSet.pMaterialList,
+ matOffsTbl[pResWindowFrame->materialIdx]);
+
+ mFrames[i].pMaterial =
+ new (pMemMaterial) Material(pResMaterial, resBlockSet);
+ }
+ }
+ }
+ }
+ }
+
+ Window::~Window() {
+ if (mFrames) {
+ for (int i = 0; i < mFrameNum; i++) {
+ mFrames[i].pMaterial->~Material();
+ Layout::FreeMemory(mFrames[i].pMaterial);
+ }
+
+ Layout::FreeMemory(mFrames);
+ }
+
+ if (mpMaterial && !mpMaterial->IsUserAllocated()) {
+ mpMaterial->~Material();
+ Layout::FreeMemory(mpMaterial);
+ mpMaterial = NULL;
+ }
+
+ mContent.texCoordAry.Free();
+ }
+
+ Material* Window::FindMaterialByName(const char* findName, bool bRecursive) {
+ if (mpMaterial && detail::EqualsMaterialName(mpMaterial->GetName(), findName)) {
+ return mpMaterial;
+ }
+
+ for (int i = 0; i < mFrameNum; i++) {
+ if (detail::EqualsMaterialName(mFrames[i].pMaterial->GetName(), findName)) {
+ return mFrames[i].pMaterial;
+ }
+ }
+
+ if (bRecursive) {
+ for (PaneList::Iterator it = mChildList.GetBeginIter();
+ it != mChildList.GetEndIter(); it++)
+ {
+ Material* pMat = it->FindMaterialByName(findName, true);
+ if (pMat) {
+ return pMat;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ AnimationLink* Window::FindAnimationLink(AnimTransform* pAnimTrans) {
+ if (AnimationLink* ret = Pane::FindAnimationLink(pAnimTrans)) {
+ return ret;
+ }
+
+ for (int i = 0; i < mFrameNum; i++) {
+ if (AnimationLink* ret = mFrames[i].pMaterial->FindAnimationLink(pAnimTrans)) {
+ return ret;
+ }
+ }
+
+ return NULL;
+ }
+
+ void Window::SetAnimationEnable(AnimTransform* pAnimTrans, bool bEnable, bool bRecursive) {
+ for (int i = 0; i < mFrameNum; i++) {
+ mFrames[i].pMaterial->SetAnimationEnable(pAnimTrans, bEnable);
+ }
+
+ Pane::SetAnimationEnable(pAnimTrans, bEnable, bRecursive);
+ }
+
+ ut::Color Window::GetVtxColor(u32 idx) const {
+ NW4HBM_ASSERT(360, idx < VERTEXCOLOR_MAX);
+ return mContent.vtxColors[idx];
+ }
+
+ void Window::SetVtxColor(u32 idx, ut::Color value) {
+ NW4HBM_ASSERT(371, idx < VERTEXCOLOR_MAX);
+ mContent.vtxColors[idx] = value;
+ }
+
+ u8 Window::GetVtxColorElement(u32 idx) const {
+ return detail::GetVtxColorElement(mContent.vtxColors, idx);
+ }
+
+ void Window::SetVtxColorElement(u32 idx, u8 value) {
+ detail::SetVtxColorElement(mContent.vtxColors, idx, value);
+ }
+
+ void Window::DrawSelf(const DrawInfo& drawInfo) {
+ LoadMtx(drawInfo);
+ WindowFrameSize frameSize = GetFrameSize(mFrameNum, mFrames);
+
+ math::VEC2 basePt = GetVtxPos();
+
+ DrawContent(basePt, frameSize, mGlbAlpha);
+
+ switch (mFrameNum) {
+ case 1:
+ DrawFrame(basePt, *mFrames, frameSize, mGlbAlpha);
+ break;
+
+ case 4:
+ DrawFrame4(basePt, mFrames, frameSize, mGlbAlpha);
+ break;
+
+ case 8:
+ DrawFrame8(basePt, mFrames, frameSize, mGlbAlpha);
+ break;
+ }
+ }
+
+ void Window::AnimateSelf(u32 option) {
+ Pane::AnimateSelf(option);
+
+ if (detail::TestBit<>(mFlag, 0) || !(option & 1)) {
+ for (int i = 0; i < mFrameNum; i++) {
+ mFrames[i].pMaterial->Animate();
+ }
+ }
+ }
+
+ void Window::UnbindAnimationSelf(AnimTransform* pAnimTrans) {
+ for (int i = 0; i < mFrameNum; i++) {
+ mFrames[i].pMaterial->UnbindAnimation(pAnimTrans);
+ }
+
+ Pane::UnbindAnimationSelf(pAnimTrans);
+ }
+
+ void Window::DrawContent(const math::VEC2& basePt, const WindowFrameSize& frameSize,
+ u8 alpha) {
+ bool bUseVtxCol = mpMaterial->SetupGX(
+ detail::IsModulateVertexColor(mContent.vtxColors, alpha), alpha);
+
+ detail::SetVertexFormat(bUseVtxCol, mContent.texCoordAry.GetSize());
+
+ // clang-format off
+ detail::DrawQuad(
+ math::VEC2(basePt.x + frameSize.l - mContentInflation.l,
+ basePt.y + frameSize.t - mContentInflation.t),
+ Size(mSize.width - frameSize.l + mContentInflation.l
+ - frameSize.r + mContentInflation.r,
+ mSize.height - frameSize.t + mContentInflation.t
+ - frameSize.b + mContentInflation.b),
+ mContent.texCoordAry.GetSize(),
+ mContent.texCoordAry.GetArray(),
+ bUseVtxCol ? mContent.vtxColors : NULL,
+ alpha
+ );
+ // clang-format on
+ }
+
+ void Window::DrawFrame(const math::VEC2& basePt, const Frame& frame,
+ const WindowFrameSize& frameSize, u8 alpha) {
+ bool bUseVtxCol =
+ frame.pMaterial->SetupGX(detail::IsModulateVertexColor(NULL, alpha), alpha);
+ detail::SetVertexFormat(bUseVtxCol, GX_TEXMAP1);
+
+ const Size texSize = detail::GetTextureSize(frame.pMaterial, GX_TEXMAP0);
+ const ut::Color vtxColors[VERTEXCOLOR_MAX];
+
+ detail::TexCoords texCds[1];
+
+ math::VEC2 polPt;
+ Size polSize;
+
+#define DRAW_QUAD_FOR_FRAME_1(corner_, frameIdx_) \
+ { \
+ Get##corner_##FrameSize(&polPt, &polSize, basePt, mSize, frameSize); \
+ Get##corner_##TexCoord(*texCds, polSize, texSize, frameIdx_); \
+ detail::DrawQuad(polPt, polSize, GX_TEXMAP1, texCds, bUseVtxCol ? vtxColors : NULL, \
+ alpha); \
+ }
+
+ DRAW_QUAD_FOR_FRAME_1(LT, TEXTUREFLIP_NONE);
+ DRAW_QUAD_FOR_FRAME_1(RT, TEXTUREFLIP_H);
+ DRAW_QUAD_FOR_FRAME_1(RB, TEXTUREFLIP_180);
+ DRAW_QUAD_FOR_FRAME_1(LB, TEXTUREFLIP_V);
+
+#undef DRAW_QUAD_FOR_FRAME_1
+ }
+
+ void Window::DrawFrame4(const math::VEC2& basePt, const Frame* frames,
+ const WindowFrameSize& frameSize, u8 alpha) {
+ ut::Color vtxColors[4];
+ detail::TexCoords texCds[1];
+ math::VEC2 polPt;
+ Size polSize;
+ bool bModVtxCol = detail::IsModulateVertexColor(NULL, alpha);
+
+#define DRAW_FRAME_4_QUAD_(corner_, frameIdx_) \
+ do { \
+ bool bUseVtxCol = frames[frameIdx_].pMaterial->SetupGX(bModVtxCol, alpha); \
+ \
+ Get##corner_##FrameSize(&polPt, &polSize, basePt, mSize, frameSize); \
+ Get##corner_##TexCoord(*texCds, polSize, \
+ detail::GetTextureSize(frames[frameIdx_].pMaterial, 0), \
+ frames[frameIdx_].textureFlip); \
+ \
+ detail::SetVertexFormat(bUseVtxCol, 1); \
+ \
+ detail::DrawQuad(polPt, polSize, 1, texCds, bUseVtxCol ? vtxColors : NULL, alpha); \
+ } while (0)
+
+ DRAW_FRAME_4_QUAD_(LT, 0);
+ DRAW_FRAME_4_QUAD_(RT, 1);
+ DRAW_FRAME_4_QUAD_(RB, 3);
+ DRAW_FRAME_4_QUAD_(LB, 2);
+
+#undef DRAW_FRAME_4_QUAD_
+ }
+
+ void Window::DrawFrame8(const math::VEC2& basePt, const Frame* frames,
+ const WindowFrameSize& frameSize, u8 alpha) {
+ ut::Color vtxColors[4];
+ detail::TexCoords texCds[1];
+ Size polSize;
+ bool bModVtxCol = detail::IsModulateVertexColor(NULL, alpha);
+
+#define DRAW_FRAME_8_QUAD_(corner_, frameIdx_, polSizeInit_, basePtInit_) \
+ do { \
+ bool bUseVtxCol = frames[frameIdx_].pMaterial->SetupGX(bModVtxCol, alpha); \
+ polSize = Size polSizeInit_; \
+ \
+ Get##corner_##TexCoord(*texCds, polSize, \
+ detail::GetTextureSize(frames[frameIdx_].pMaterial, 0), \
+ frames[frameIdx_].textureFlip); \
+ \
+ detail::SetVertexFormat(bUseVtxCol, 1); \
+ \
+ detail::DrawQuad(VEC_CTOR_ basePtInit_, polSize, 1, texCds, bUseVtxCol ? vtxColors : NULL, \
+ alpha); \
+ } while (0)
+
+#define VEC_CTOR_ // avoid copy construction specifically for this first call
+ DRAW_FRAME_8_QUAD_(LT, 0, (frameSize.l, frameSize.t), basePt);
+#undef VEC_CTOR_
+
+#define VEC_CTOR_ math::VEC2
+
+ DRAW_FRAME_8_QUAD_(LT, 6, (mSize.width - frameSize.l - frameSize.r, frameSize.t),
+ (basePt.x + frameSize.l, basePt.y));
+ DRAW_FRAME_8_QUAD_(RT, 1, (frameSize.r, frameSize.t),
+ (basePt.x + mSize.width - frameSize.r, basePt.y));
+ DRAW_FRAME_8_QUAD_(RT, 5, (frameSize.r, mSize.height - frameSize.t - frameSize.b),
+ (basePt.x + mSize.width - frameSize.r, basePt.y + frameSize.t));
+ DRAW_FRAME_8_QUAD_(
+ RB, 3, (frameSize.r, frameSize.b),
+ (basePt.x + mSize.width - frameSize.r, basePt.y + mSize.height - frameSize.b));
+ DRAW_FRAME_8_QUAD_(RB, 7, (mSize.width - frameSize.l - frameSize.r, frameSize.b),
+ (basePt.x + frameSize.l, basePt.y + mSize.height - frameSize.b));
+ DRAW_FRAME_8_QUAD_(LB, 2, (frameSize.l, frameSize.b),
+ (basePt.x, basePt.y + mSize.height - frameSize.b));
+ DRAW_FRAME_8_QUAD_(LB, 4, (frameSize.l, mSize.height - frameSize.t - frameSize.b),
+ (basePt.x, basePt.y + frameSize.t));
+
+#undef VEC_CTOR_
+#undef DRAW_FRAME_8_QUAD_
+ }
+
+ WindowFrameSize Window::GetFrameSize(u8 frameNum, const Frame* frames) {
+ WindowFrameSize ret = {};
+
+ switch (frameNum) {
+ case 1: {
+ Size texSize = detail::GetTextureSize(frames->pMaterial, 0);
+ ret.l = texSize.width;
+ ret.t = texSize.height;
+
+ ret.r = texSize.width;
+ ret.b = texSize.height;
+ } break;
+
+ case 4:
+ case 8: {
+ Size texSize = detail::GetTextureSize(frames[0].pMaterial, 0);
+ ret.l = texSize.width;
+ ret.t = texSize.height;
+
+ texSize = detail::GetTextureSize(frames[3].pMaterial, 0);
+ ret.r = texSize.width;
+ ret.b = texSize.height;
+ } break;
+ }
+
+ return ret;
+ }
+
+ Material* Window::GetFrameMaterial(u32 frameIdx) const {
+ NW4HBM_ASSERT(658, frameIdx < WINDOWFRAME_MAX);
+ if (frameIdx >= mFrameNum) {
+ return NULL;
+ }
+
+ return mFrames[frameIdx].pMaterial;
+ }
+
+ Material* Window::GetContentMaterial() const {
+ return GetMaterial();
+ }
+
+ } // namespace lyt
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/material.h b/src/revolution/homebuttonLib/nw4hbm/lyt/material.h
new file mode 100644
index 0000000000..045dd261bf
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/material.h
@@ -0,0 +1,154 @@
+#ifndef NW4HBM_LYT_MATERIAL_H
+#define NW4HBM_LYT_MATERIAL_H
+
+#include <revolution/types.h>
+#include <revolution/tpl.h>
+#include <revolution/gx.h>
+
+#include "animation.h"
+#include "lyt_types.h"
+#include "resources.h"
+
+#include "../ut/Color.h"
+
+namespace nw4hbm {
+ namespace lyt {
+ class Material;
+
+ namespace detail {
+ typedef struct BitGXNums {
+ u32 texMap : 4; // 11110000000000000000000000000000
+ u32 texSRT : 4; // 00001111000000000000000000000000
+ u32 texCoordGen : 4; // 00000000111100000000000000000000
+ u32 indSRT : 2; // 00000000000011000000000000000000
+ u32 indStage : 3; // 00000000000000111000000000000000
+ u32 tevSwap : 1; // 00000000000000000100000000000000
+ u32 tevStage : 5; // 00000000000000000011111000000000
+ u32 chanCtrl : 1; // 00000000000000000000000100000000
+ u32 matCol : 1; // 00000000000000000000000010000000
+ u32 alpComp : 1; // 00000000000000000000000001000000
+ u32 blendMode : 1; // 00000000000000000000000000100000
+ } BitGXNums;
+ Size GetTextureSize(Material* pMaterial, u8 texMapIdx);
+ } // namespace detail
+
+ class Material {
+ public:
+ Material();
+ Material(const res::Material* pRes, const ResBlockSet& resBlockSet);
+
+ /* 0x08 */ virtual ~Material();
+ /* 0x0C */ virtual bool SetupGX(bool bModVtxCol, u8 alpha);
+ /* 0x10 */ virtual void BindAnimation(AnimTransform* animTrans);
+ /* 0x14 */ virtual void UnbindAnimation(AnimTransform* animTrans);
+ /* 0x18 */ virtual void UnbindAllAnimation();
+ /* 0x1C */ virtual void Animate();
+ /* 0x20 */ virtual AnimationLink* FindAnimationLink(AnimTransform* animTrans);
+ /* 0x24 */ virtual void SetAnimationEnable(AnimTransform* animTrans, bool bEnable);
+
+ const char* GetName() const { return mName; }
+ GXColorS10 GetTevColor(u32 idx) const { return mTevCols[idx]; }
+
+ u8 GetTextureCap() const { return mGXMemCap.texMap; }
+
+ u8 GetTexSRTCap() const { return mGXMemCap.texSRT; }
+
+ u8 GetTexCoordGenCap() const { return mGXMemCap.texCoordGen; }
+
+ u8 GetIndTexSRTCap() const { return mGXMemCap.indSRT; }
+
+ bool IsTevSwapCap() const { return static_cast<bool>(mGXMemCap.tevSwap); }
+ bool IsBlendModeCap() const { return static_cast<bool>(mGXMemCap.blendMode); }
+ bool IsAlphaCompareCap() const { return static_cast<bool>(mGXMemCap.alpComp); }
+
+ bool IsMatColorCap() const { return static_cast<bool>(mGXMemCap.matCol); }
+ bool IsChanCtrlCap() const { return static_cast<bool>(mGXMemCap.chanCtrl); }
+
+ u8 GetTextureNum() const { return mGXMemNum.texMap; }
+
+ bool IsUserAllocated() const { return mbUserAllocated; }
+
+ void SetName(const char* name);
+
+ const GXTexObj* GetTexMapAry() const;
+ const TexSRT* GetTexSRTAry() const;
+ const TexCoordGen* GetTexCoordGenAry() const;
+ const ChanCtrl* GetChanCtrlAry() const;
+ const ut::Color* GetMatColAry() const;
+ const TevSwapMode* GetTevSwapAry() const;
+ const AlphaCompare* GetAlphaComparePtr() const;
+ const BlendMode* GetBlendModePtr() const;
+ const IndirectStage* GetIndirectStageAry() const;
+ const TexSRT* GetIndTexSRTAry() const;
+ const TevStage* GetTevStageAry() const;
+
+ GXTexObj* GetTexMapAry();
+ TexSRT* GetTexSRTAry();
+ TexCoordGen* GetTexCoordGenAry();
+ ChanCtrl* GetChanCtrlAry();
+ ut::Color* GetMatColAry();
+ TevSwapMode* GetTevSwapAry();
+ AlphaCompare* GetAlphaComparePtr();
+ BlendMode* GetBlendModePtr();
+ IndirectStage* GetIndirectStageAry();
+ TexSRT* GetIndTexSRTAry();
+ TevStage* GetTevStageAry();
+
+ void GetTexture(GXTexObj* pTexObj, u8 texMapIdx) const;
+
+ void SetTextureNum(u8 num);
+ void SetTexCoordGenNum(u8 num);
+ void SetIndStageNum(u8 num);
+ void SetTevStageNum(u8 num);
+
+ void SetTexture(u8 texMapIdx, const GXTexObj& texObj);
+ void SetTexture(u8 texMapIdx, TPLPalette* pTplRes);
+ void SetTextureNoWrap(u8 texMapIdx, TPLPalette* pTplRes);
+
+ void SetColorElement(u32 colorType, s16 value);
+
+ void SetTexCoordGen(u32 idx, TexCoordGen value) {
+ NW4HBM_ASSERT(180, idx < mGXMemNum.texCoordGen);
+ GetTexCoordGenAry()[idx] = value;
+ }
+
+ void SetTexSRTElement(u32 texSRTIdx, u32 eleIdx, f32 value) {
+ NW4HBM_ASSERT(293, texSRTIdx < mGXMemNum.texSRT);
+ f32* srtAry = reinterpret_cast<f32*>(&GetTexSRTAry()[texSRTIdx]);
+ srtAry[eleIdx] = value;
+ }
+
+ void SetIndTexSRTElement(u32 texSRTIdx, u32 eleIdx, f32 value) {
+ NW4HBM_ASSERT(309, texSRTIdx < mGXMemNum.indSRT);
+ f32* srtAry = reinterpret_cast<f32*>(&GetIndTexSRTAry()[texSRTIdx]);
+ srtAry[eleIdx] = value;
+ }
+
+ void Init();
+ void InitBitGXNums(detail::BitGXNums* ptr);
+
+ void ReserveGXMem(u8 texMapNum, u8 texSRTNum, u8 texCoordGenNum, u8 tevStageNum,
+ bool allocTevSwap, u8 indStageNum, u8 indSRTNum, bool allocChanCtrl,
+ bool allocMatCol, bool allocAlpComp, bool allocBlendMode);
+
+ void AddAnimationLink(AnimationLink* animationLink);
+
+ private:
+ static const int MAX_TEX_SRT = (GX_TEXMTX9 - GX_TEXMTX0) / 3 + 1;
+ static const int MAX_IND_SRT = (GX_ITM_2 - GX_ITM_0) + 1;
+
+ /* 0x00 (vtable) */
+ /* 0x04 */ char mName[20];
+ /* 0x18 */ AnimationLinkList mAnimList;
+ /* 0x24 */ GXColorS10 mTevCols[TEVCOLOR_MAX];
+ /* 0x3C */ ut::Color mTevKCols[GX_MAX_KCOLOR];
+ /* 0x4C */ detail::BitGXNums mGXMemCap;
+ /* 0x50 */ detail::BitGXNums mGXMemNum;
+ /* 0x54 */ bool mbUserAllocated;
+ /* 0x58 */ void* mpGXMem;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/pane.h b/src/revolution/homebuttonLib/nw4hbm/lyt/pane.h
new file mode 100644
index 0000000000..a8106c2ac9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/pane.h
@@ -0,0 +1,175 @@
+#ifndef NW4HBM_LYT_PANE_H
+#define NW4HBM_LYT_PANE_H
+
+#include "revolution/types.h"
+
+#include "../ut/Color.h"
+#include "../ut/LinkList.h"
+#include "../ut/Rect.h"
+#include "../ut/RuntimeTypeInfo.h"
+
+#include "../db/assert.h"
+
+#include "../math/types.h"
+
+#include "animation.h"
+#include "drawInfo.h"
+#include "lyt_types.h"
+#include "material.h"
+
+
+namespace nw4hbm {
+ namespace lyt {
+ class Pane;
+
+ namespace detail {
+ class PaneBase {
+ public:
+ inline PaneBase() : mLink() {}
+
+ /* 0x08 */ virtual ~PaneBase() {}
+
+ /* 0x00 (vtable) */
+ /* 0x04 */ ut::LinkListNode mLink;
+ };
+ } // namespace detail
+ typedef ut::LinkList<Pane, offsetof(detail::PaneBase, mLink)> PaneList;
+
+ enum {
+ /* 1 */ ANIMOPTION_SKIP_INVISIBLE = (1 << 0),
+ };
+
+ class Pane : detail::PaneBase {
+ private:
+ enum {
+ /* 0 */ BIT_VISIBLE = 0,
+ /* 1 */ BIT_INFLUENCED_ALPHA,
+ /* 2 */ BIT_LOCATION_ADJUST
+ };
+
+ public:
+ Pane();
+ Pane(const res::Pane* pBlock);
+
+ /* 0x08 */ virtual ~Pane();
+ /* 0x0C */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x10 */ virtual void CalculateMtx(const DrawInfo& drawInfo);
+ /* 0x14 */ virtual void Draw(const DrawInfo& drawInfo);
+ /* 0x18 */ virtual void DrawSelf(const DrawInfo& drawInfo);
+ /* 0x1C */ virtual void Animate(u32 option = 0);
+ /* 0x20 */ virtual void AnimateSelf(u32 option = 0);
+ /* 0x24 */ virtual ut::Color GetVtxColor(u32 idx) const;
+ /* 0x28 */ virtual void SetVtxColor(u32 idx, ut::Color valuw);
+ /* 0x2C */ virtual u8 GetColorElement(u32 idx) const;
+ /* 0x30 */ virtual void SetColorElement(u32 idx, u8 color);
+ /* 0x34 */ virtual u8 GetVtxColorElement(u32 idx) const;
+ /* 0x38 */ virtual void SetVtxColorElement(u32 idx, u8 element);
+ /* 0x3C */ virtual Pane* FindPaneByName(const char* findName, bool bRecursive = true);
+ /* 0x40 */ virtual Material* FindMaterialByName(const char* findName,
+ bool bRecursive = true);
+ /* 0x44 */ virtual void BindAnimation(AnimTransform* animTrans, bool bRecursive = true);
+ /* 0x48 */ virtual void UnbindAnimation(AnimTransform* animTrans,
+ bool bRecursive = true);
+ /* 0x4C */ virtual void UnbindAllAnimation(bool bRecursive = true);
+ /* 0x50 */ virtual void UnbindAnimationSelf(AnimTransform* animTrans);
+ /* 0x54 */ virtual AnimationLink* FindAnimationLink(AnimTransform* animTrans);
+ /* 0x58 */ virtual void SetAnimationEnable(AnimTransform* animTrans, bool bEnable,
+ bool bRecursive = true);
+ /* 0x5C */ virtual Material* GetMaterial() const;
+ /* 0x60 */ virtual void LoadMtx(const DrawInfo& drawInfo);
+
+ Pane* GetParent() const { return mpParent; }
+ PaneList& GetChildList() { return mChildList; }
+
+ const math::VEC3& GetTranslate() { return mTranslate; }
+ void SetTranslate(const math::VEC3& translate) { mTranslate = translate; }
+ void SetTranslate(const math::VEC2& translate) {
+ SetTranslate(math::VEC3(translate.x, translate.y, 0.0f));
+ }
+
+ const math::VEC3& GetRotate() const { return mRotate; }
+ void SetRotate(const math::VEC3& rotate) { mRotate = rotate; }
+
+ const math::VEC2& GetScale() const { return mScale; }
+ void SetScale(const math::VEC2& scale) { mScale = scale; }
+
+ const Size& GetSize() const { return mSize; }
+ void SetSize(const Size& size) { mSize = size; }
+
+ bool IsVisible() { return detail::TestBit(mFlag, BIT_VISIBLE); };
+ void SetVisible(bool visible) { detail::SetBit(&mFlag, BIT_VISIBLE, visible); };
+
+ bool IsInfluencedAlpha() { return detail::TestBit(mFlag, BIT_INFLUENCED_ALPHA); };
+ void SetInfluencedAlpha(bool visible) {
+ detail::SetBit(&mFlag, BIT_INFLUENCED_ALPHA, visible);
+ };
+
+ bool IsLocationAdjust() { return detail::TestBit(mFlag, BIT_LOCATION_ADJUST); };
+ void SetLocationAdjust(bool visible) {
+ detail::SetBit(&mFlag, BIT_LOCATION_ADJUST, visible);
+ };
+
+ const math::MTX34& GetGlobalMtx() const { return mGlbMtx; }
+ void SetGlobalMtx(const math::MTX34& mtx) { mGlbMtx = mtx; }
+
+ const math::MTX34& GetMtx() const { return mMtx; }
+ void SetMtx(const math::MTX34& mtx) { mMtx = mtx; }
+
+ u8 GetAlpha() { return mAlpha; }
+ void SetAlpha(u8 alpha) { mAlpha = alpha; }
+
+ const char* GetName() const { return mName; }
+
+ void SetSRTElement(u32 idx, f32 value) {
+ NW4HBM_ASSERT(250, idx < ANIMTARGET_PANE_MAX);
+ reinterpret_cast<f32*>(&mTranslate)[idx] = value;
+ }
+
+ bool IsUserAllocated() const { return mbUserAllocated; }
+
+ const ut::Rect GetPaneRect(const DrawInfo& drawInfo) const;
+
+ math::VEC2 GetVtxPos() const;
+
+ void SetName(const char* name);
+ void SetUserData(const char* userData);
+
+ void Init();
+
+ void InsertChild(PaneList::Iterator next, Pane* pChild);
+ void InsertChild(Pane* pNext, Pane* pChild);
+
+ void PrependChild(Pane* pChild);
+ void AppendChild(Pane* pChild);
+
+ void RemoveChild(Pane* pChild);
+
+ void CalculateMtxChild(const DrawInfo& drawInfo);
+
+ void AddAnimationLink(AnimationLink* animationLink);
+
+ protected:
+ /* 0x00 (base) */
+ /* 0x0C */ Pane* mpParent;
+ /* 0x10 */ PaneList mChildList;
+ /* 0x1C */ AnimationLinkList mAnimList;
+ /* 0x28 */ Material* mpMaterial;
+ /* 0x2C */ math::VEC3 mTranslate;
+ /* 0x38 */ math::VEC3 mRotate;
+ /* 0x44 */ math::VEC2 mScale;
+ /* 0x4C */ Size mSize;
+ /* 0x54 */ math::MTX34 mMtx;
+ /* 0x84 */ math::MTX34 mGlbMtx;
+ /* 0xB4 */ char mName[16];
+ /* 0xC4 */ char mUserData[8];
+ /* 0xCC */ u8 mBasePosition;
+ /* 0xCD */ u8 mAlpha;
+ /* 0xCE */ u8 mGlbAlpha;
+ /* 0xCF */ u8 mFlag;
+ /* 0xD0 */ bool mbUserAllocated;
+ }; // size = 0xD4
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/picture.h b/src/revolution/homebuttonLib/nw4hbm/lyt/picture.h
new file mode 100644
index 0000000000..4a77129ea7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/picture.h
@@ -0,0 +1,44 @@
+#ifndef NW4HBM_LYT_PICTURE_H
+#define NW4HBM_LYT_PICTURE_H
+
+#include "pane.h"
+
+#include "common.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class Picture : public Pane {
+ public:
+ Picture(u8 num);
+ Picture(const res::Picture* pResPic, const ResBlockSet& resBlockSet);
+
+ /* 0x08 */ virtual ~Picture();
+ /* 0x0C */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x18 */ virtual void DrawSelf(const DrawInfo& drawInfo);
+ /* 0x24 */ virtual ut::Color GetVtxColor(u32 idx) const;
+ /* 0x28 */ virtual void SetVtxColor(u32 idx, ut::Color value);
+ /* 0x34 */ virtual u8 GetVtxColorElement(u32 idx) const;
+ /* 0x38 */ virtual void SetVtxColorElement(u32 idx, u8 value);
+ /* 0x64 */ virtual void Append(TPLPalette* pTplRes);
+ /* 0x68 */ virtual void Append(const GXTexObj& texObj);
+
+ void SetTexCoordNum(u8 num);
+ u8 GetTexCoordNum() const;
+
+ void GetTexCoord(u32 idx, math::VEC2* coords) const;
+ void SetTexCoord(u32 idx, const math::VEC2* coords);
+
+ void Init(u8 texNum);
+ void ReserveTexCoord(u8 num);
+
+ private:
+ /* 0x00 (base) */
+ /* 0xD4 */ ut::Color mVtxColors[VERTEXCOLOR_MAX] ATTRIBUTE_ALIGN(4);
+ /* 0xE4 */ detail::TexCoordAry mTexCoordAry;
+ };
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/resourceAccessor.h b/src/revolution/homebuttonLib/nw4hbm/lyt/resourceAccessor.h
new file mode 100644
index 0000000000..11bd30d2d7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/resourceAccessor.h
@@ -0,0 +1,27 @@
+#ifndef NW4HBM_LYT_RESOURCE_ACCESSOR_H
+#define NW4HBM_LYT_RESOURCE_ACCESSOR_H
+
+#include "revolution/types.h"
+
+namespace nw4hbm {
+
+ namespace ut {
+ class Font;
+ }
+
+ namespace lyt {
+ class ResourceAccessor {
+ public:
+ ResourceAccessor();
+
+ /* 0x08 */ virtual ~ResourceAccessor();
+ /* 0x0C */ virtual void* GetResource(u32 resType, const char* name, u32* pSize) = 0;
+ /* 0x10 */ virtual ut::Font* GetFont(const char* name);
+
+ /* 0x00 (vtable) */
+ }; // size = 0x04
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/resources.h b/src/revolution/homebuttonLib/nw4hbm/lyt/resources.h
new file mode 100644
index 0000000000..deb388161c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/resources.h
@@ -0,0 +1,292 @@
+#ifndef NW4HBM_LYT_RESOURCES_H
+#define NW4HBM_LYT_RESOURCES_H
+
+#include <revolution/gx.h>
+#include <revolution/types.h>
+
+#include "../ut/binaryFileFormat.h"
+
+#include "lyt_types.h"
+#include "resourceAccessor.h"
+
+
+namespace nw4hbm {
+ namespace lyt {
+
+ typedef struct InflationLRTB {
+ /* 0x00 */ f32 l;
+ /* 0x04 */ f32 r;
+ /* 0x08 */ f32 t;
+ /* 0x0C */ f32 b;
+ } InflationLRTB;
+
+ typedef struct WindowFrameSize {
+ /* 0x00 */ f32 l;
+ /* 0x04 */ f32 r;
+ /* 0x08 */ f32 t;
+ /* 0x0C */ f32 b;
+ } WindowFrameSize;
+
+ class MaterialResourceNum {
+ public:
+ u8 GetTexMapNum() const { return detail::GetBits<>(bits, 0, 4); }
+ u8 GetTexSRTNum() const { return detail::GetBits<>(bits, 4, 4); }
+ u8 GetTexCoordGenNum() const { return detail::GetBits<>(bits, 8, 4); }
+ bool HasTevSwapTable() const { return detail::TestBit<>(bits, 12); }
+ u8 GetIndTexSRTNum() const { return detail::GetBits<>(bits, 13, 2); }
+ u8 GetIndTexStageNum() const { return detail::GetBits<>(bits, 15, 3); }
+ u8 GetTevStageNum() const { return detail::GetBits<>(bits, 18, 5); }
+ bool HasAlphaCompare() const { return detail::TestBit<>(bits, 23); }
+ bool HasBlendMode() const { return detail::TestBit<>(bits, 24); }
+ u8 GetChanCtrlNum() const { return detail::GetBits<>(bits, 25, 1); }
+ u8 GetMatColNum() const { return detail::GetBits<>(bits, 27, 1); }
+
+ private:
+ /* 0x00 */ u32 bits;
+ }; // size = 0x04
+
+ namespace res {
+
+ /*** COMMON ***/
+
+ typedef struct BinaryFileHeader {
+ /* 0x00 */ char signature[4];
+ /* 0x04 */ u16 byteOrder;
+ /* 0x06 */ u16 version;
+ /* 0x08 */ u32 fileSize;
+ /* 0x0C */ u16 headerSize;
+ /* 0x0E */ u16 dataBlocks;
+ } BinaryFileHeader; // size = 0x10
+
+ typedef struct DataBlockHeader {
+ /* 0x00 */ char kind[4];
+ /* 0x04 */ u32 size;
+ } DataBlockHeader; // size = 0x08
+
+ /*** ANIMATION ***/
+
+ typedef struct StepKey {
+ /* 0x00 */ f32 frame;
+ /* 0x04 */ u16 value;
+ /* 0x06 */ u16 padding;
+ } StepKey; // size = 0x08
+
+ typedef struct HermiteKey {
+ /* 0x00 */ f32 frame;
+ /* 0x04 */ f32 value;
+ /* 0x08 */ f32 slope;
+ } HermiteKey; // size = 0x0C
+
+ typedef struct AnimationInfo {
+ public:
+ /* 0x00 */ u32 kind;
+ /* 0x04 */ u8 num;
+ /* 0x05 */ u8 padding[3];
+
+ public:
+ static const u32 ANIM_INFO_PANE_PAIN_SRT = 'RLPA';
+ static const u32 ANIM_INFO_PANE_VERTEX_COLOR = 'RLVC';
+ static const u32 ANIM_INFO_PANE_VISIBILITY = 'RLVI';
+
+ static const u32 ANIM_INFO_MATERIAL_COLOR = 'RLMC';
+ static const u32 ANIM_INFO_MATERIAL_TEXTURE_PATTERN = 'RLTP';
+ static const u32 ANIM_INFO_MATERIAL_TEXTURE_SRT = 'RLTS';
+ static const u32 ANIM_INFO_MATERIAL_IND_TEX_SRT = 'RLIM';
+ } AnimationInfo;
+
+ typedef struct AnimationTarget {
+ /* 0x00 */ u8 id;
+ /* 0x01 */ u8 target;
+ /* 0x02 */ u8 curveType;
+ /* 0x03 */ u8 padding1;
+ /* 0x04 */ u16 keyNum;
+ /* 0x06 */ u8 padding2[2];
+ /* 0x08 */ u32 keysOffset;
+ } AnimationTarget; // size = 0x10
+
+ typedef struct AnimationBlock {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u16 frameSize;
+ /* 0x0A */ u8 loop;
+ /* 0x0B */ u8 padding1;
+ /* 0x0C */ u16 fileNum;
+ /* 0x0E */ u16 animContNum;
+ /* 0x10 */ u32 animContOffsetsOffset;
+ } AnimationBlock; // size = 0x14
+
+ typedef struct AnimationContent {
+ public:
+ enum {
+ /* 0 */ ACType_Pane = 0,
+ /* 1 */ ACType_Material,
+ /* 2 */ ACType_Max
+ };
+
+ public:
+ /* 0x00 */ char name[20];
+ /* 0x14 */ u8 num;
+ /* 0x15 */ u8 type;
+ /* 0x16 */ u8 padding[2];
+ } AnimationContent; // size = 0x18
+
+ /*** MATERIAL ***/
+
+ typedef struct Texture {
+ /* 0x00 */ u32 nameStrOffset;
+ /* 0x04 */ u8 type;
+ /* 0x05 */ u8 padding[3];
+ } Texture; // size = 0x08
+
+ typedef struct Material {
+ /* 0x00 */ char name[20];
+ /* 0x14 */ GXColorS10 tevCols[TEVCOLOR_MAX];
+ /* 0x2C */ GXColor tevKCols[GX_MAX_KCOLOR];
+ /* 0x3C */ MaterialResourceNum resNum;
+ } Material; // size = 0x40
+
+ typedef struct TexMap {
+ /* 0x00 */ u16 texIdx;
+ /* 0x02 */ u8 wrapS;
+ /* 0x03 */ u8 wrapT;
+ } TexMap; // size = 0x04
+
+ /*** PANES ***/
+
+ static const u32 FILE_HEADER_SIGNATURE_ANIMATION = 'RLAN';
+ static const u32 FILE_HEADER_SIGNATURE_LAYOUT = 'RLYT';
+
+ static const u32 OBJECT_SIGNATURE_LAYOUT = 'lyt1';
+ static const u32 OBJECT_SIGNATURE_FONT_LIST = 'fnl1';
+ static const u32 OBJECT_SIGNATURE_MATERIAL_LIST = 'mat1';
+ static const u32 OBJECT_SIGNATURE_TEXTURE_LIST = 'txl1';
+ static const u32 OBJECT_SIGNATURE_PANE = 'pan1';
+ static const u32 OBJECT_SIGNATURE_PANE_CHILD_START = 'pas1';
+ static const u32 OBJECT_SIGNATURE_PANE_CHILD_END = 'pae1';
+ static const u32 OBJECT_SIGNATURE_PICTURE = 'pic1';
+ static const u32 OBJECT_SIGNATURE_BOUNDING = 'bnd1';
+ static const u32 OBJECT_SIGNATURE_WINDOW = 'wnd1';
+ static const u32 OBJECT_SIGNATURE_TEXT_BOX = 'txt1';
+ static const u32 OBJECT_SIGNATURE_GROUP = 'grp1';
+ static const u32 OBJECT_SIGNATURE_GROUP_CHILD_START = 'grs1';
+ static const u32 OBJECT_SIGNATURE_GROUP_CHILD_END = 'gre1';
+ static const u32 OBJECT_SIGNATURE_PANE_ANIM = 'pai1';
+
+ typedef struct Pane {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u8 flag;
+ /* 0x09 */ u8 basePosition;
+ /* 0x0A */ u8 alpha;
+ /* 0x0B */ u8 padding;
+ /* 0x0C */ char name[16];
+ /* 0x1C */ char userData[8];
+ /* 0x24 */ math::VEC3 translate;
+ /* 0x30 */ math::VEC3 rotate;
+ /* 0x3C */ math::VEC2 scale;
+ /* 0x44 */ Size size;
+ } Pane; // size = 0x4C
+
+ typedef struct Bounding : Pane {
+ // (empty)
+ } Bounding;
+
+ typedef struct Picture : public Pane {
+ /* 0x4C */ u32 vtxCols[4];
+ /* 0x5C */ u16 materialIdx;
+ /* 0x5E */ u8 texCoordNum;
+ /* 0x5F */ u8 padding[1];
+ } Picture;
+
+ typedef struct Font {
+ /* 0x00 */ u32 nameStrOffset;
+ /* 0x04 */ u8 type;
+ u8 padding[3];
+ } Font;
+
+ typedef struct TextBox : public Pane {
+ /* 0x4C */ u16 textBufBytes;
+ /* 0x4E */ u16 textStrBytes;
+ /* 0x50 */ u16 materialIdx;
+ /* 0x52 */ u16 fontIdx;
+ /* 0x54 */ u8 textPosition;
+ u8 padding[3];
+ /* 0x58 */ u32 textStrOffset;
+ /* 0x5C */ u32 textCols[TEXTCOLOR_MAX];
+ /* 0x64 */ Size fontSize;
+ /* 0x6C */ f32 charSpace;
+ /* 0x70 */ f32 lineSpace;
+ } TextBox;
+
+ typedef struct WindowFrame {
+ /* 0x00 */ u16 materialIdx;
+ /* 0x02 */ u8 textureFlip;
+ /* 0x03 */ u8 padding1;
+ } WindowFrame;
+
+ typedef struct WindowContent {
+ /* 0x00 */ u32 vtxCols[VERTEXCOLOR_MAX];
+ /* 0x10 */ u16 materialIdx;
+ /* 0x12 */ u8 texCoordNum;
+ /* 0x13 */ u8 padding[1];
+ } WindowContent;
+
+ typedef struct Window : public Pane {
+ /* 0x4C */ InflationLRTB inflation;
+ /* 0x5C */ u8 frameNum;
+ /* 0x5D */ u8 padding1;
+ /* 0x5E */ u8 padding2;
+ /* 0x5F */ u8 padding3;
+ /* 0x60 */ u32 contentOffset;
+ /* 0x64 */ u32 frameOffsetTableOffset;
+ } Window;
+
+ /*** GROUP ***/
+
+ typedef struct Group {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ char name[16];
+ /* 0x18 */ u16 paneNum;
+ /* 0x19 */ u8 padding[2];
+ } Group;
+
+ /*** LAYOUT ***/
+
+ typedef struct Layout {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u8 originType;
+ /* 0x09 */ u8 padding[3];
+ /* 0x0C */ Size layoutSize;
+ } Layout;
+
+ } // namespace res
+
+ namespace res {
+ typedef struct TextureList {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u16 texNum;
+ u8 padding[2];
+ } TextureList;
+
+ typedef struct FontList {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u16 fontNum;
+ u8 padding[2];
+ } FontList;
+
+ typedef struct MaterialList {
+ /* 0x00 */ DataBlockHeader blockHeader;
+ /* 0x08 */ u16 materialNum;
+ u8 padding[2];
+ } MaterialList;
+ } // namespace res
+
+ typedef struct ResBlockSet {
+ /* 0x00 */ const res::TextureList* pTextureList;
+ /* 0x04 */ const res::FontList* pFontList;
+ /* 0x08 */ const res::MaterialList* pMaterialList;
+ /* 0x0C */ ResourceAccessor* pResAccessor;
+ } ResBlockSet;
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/textBox.h b/src/revolution/homebuttonLib/nw4hbm/lyt/textBox.h
new file mode 100644
index 0000000000..37baa0f255
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/textBox.h
@@ -0,0 +1,86 @@
+#ifndef NW4HBM_LYT_TEXTBOX_H
+#define NW4HBM_LYT_TEXTBOX_H
+
+#include "common.h"
+#include "pane.h"
+
+#include "../ut/RuntimeTypeInfo.h"
+#include "../ut/TagProcessorBase.h"
+#include "../ut/WideTagProcessor.h"
+#include "../ut/WideTextWriter.h"
+
+namespace nw4hbm {
+namespace lyt {
+
+class TextBox : public Pane {
+public:
+ TextBox(u16 allocStrLen, const wchar_t* str, const ut::Font* pFont);
+ TextBox(const res::TextBox* pBlock, const ResBlockSet& resBlockSet);
+
+ /* 0x08 */ virtual ~TextBox();
+ /* 0x0C */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x18 */ virtual void DrawSelf(const DrawInfo& drawInfo);
+ /* 0x24 */ virtual ut::Color GetVtxColor(u32 idx) const;
+ /* 0x28 */ virtual void SetVtxColor(u32 idx, ut::Color value);
+ /* 0x34 */ virtual u8 GetVtxColorElement(u32 idx) const;
+ /* 0x38 */ virtual void SetVtxColorElement(u32 idx, u8 value);
+ /* 0x64 */ virtual void AllocStringBuffer(u16 size);
+ /* 0x68 */ virtual void FreeStringBuffer();
+ /* 0x6C */ virtual u16 SetString(const wchar_t* str, u16 dstIdx = 0);
+ /* 0x70 */ virtual u16 SetString(const wchar_t* str, u16 dstIdx, u16 strLen);
+
+ const Size& GetFontSize() const { return mFontSize; }
+
+ void SetFontSize(const Size& fontSize) { mFontSize = fontSize; }
+ void SetTagProcessor(ut::WideTagProcessor* pTagProcessor) { mpTagProcessor = pTagProcessor; }
+
+ u16 GetStringBufferLength() const;
+
+ f32 GetTextMagH() const;
+ f32 GetTextMagV() const;
+
+ u8 GetTextPositionH() const { return detail::GetHorizontalPosition(mTextPosition); }
+ u8 GetTextPositionV() const { return detail::GetVerticalPosition(mTextPosition); }
+
+ const ut::Color GetTextColor(u32 type) const {
+ NW4HBM_ASSERT(95, type < TEXTCOLOR_MAX);
+ return mTextColors[type];
+ }
+
+ void SetTextColor(u32 type, ut::Color value) {
+ NW4HBM_ASSERT(96, type < TEXTCOLOR_MAX);
+ mTextColors[type] = value;
+ }
+
+ void SetTextPositionH(u8 pos) { detail::SetHorizontalPosition(&mTextPosition, pos); }
+ void SetTextPositionV(u8 pos) { detail::SetVerticalPosition(&mTextPosition, pos); }
+
+ const ut::Font* GetFont() const;
+ void SetFont(const ut::Font* pFont);
+
+ void Init(u16 allocStrLen);
+
+ const ut::Rect GetTextDrawRect(const DrawInfo& drawInfo) const;
+ const ut::Rect GetTextDrawRect(ut::WideTextWriter* pWriter) const;
+
+private:
+ /* 0x00 (base) */
+ /* 0xD4 */ wchar_t* mTextBuf;
+ /* 0xD8 */ ut::Color mTextColors[TEXTCOLOR_MAX];
+ /* 0xE0 */ const ut::Font* mpFont;
+ /* 0xE4 */ Size mFontSize;
+ /* 0xEC */ f32 mLineSpace;
+ /* 0xF0 */ f32 mCharSpace;
+ /* 0xF4 */ ut::WideTagProcessor* mpTagProcessor;
+ /* 0xF8 */ u16 mTextBufBytes;
+ /* 0xFA */ u16 mTextLen;
+ /* 0xFC */ u8 mTextPosition;
+
+ /* 0xFD */ struct {
+ u8 allocFont : 1;
+ } mTextBoxFlag;
+};
+} // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/lyt/window.h b/src/revolution/homebuttonLib/nw4hbm/lyt/window.h
new file mode 100644
index 0000000000..daea776ae4
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/lyt/window.h
@@ -0,0 +1,63 @@
+#ifndef NW4HBM_LYT_WINDOW_H
+#define NW4HBM_LYT_WINDOW_H
+
+#include "common.h"
+#include "pane.h"
+#include "resources.h"
+
+namespace nw4hbm {
+ namespace lyt {
+
+ class Window : public Pane {
+ private:
+ typedef struct Content {
+ /* 0x00 */ ut::Color vtxColors[4];
+ /* 0x10 */ detail::TexCoordAry texCoordAry;
+ } Content;
+
+ typedef struct Frame {
+ /* 0x00 */ u8 textureFlip;
+ /* 0x04 */ Material* pMaterial;
+ } Frame;
+
+ public:
+ Window(const res::Window* pBlock, const ResBlockSet& resBlockSet);
+
+ /* 0x08 */ virtual ~Window();
+ /* 0x0C */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x18 */ virtual void DrawSelf(const DrawInfo& drawInfo);
+ /* 0x20 */ virtual void AnimateSelf(u32 option);
+ /* 0x24 */ virtual ut::Color GetVtxColor(u32 idx) const;
+ /* 0x28 */ virtual void SetVtxColor(u32 idx, ut::Color value);
+ /* 0x34 */ virtual u8 GetVtxColorElement(u32 idx) const;
+ /* 0x38 */ virtual void SetVtxColorElement(u32 idx, u8 value);
+ /* 0x40 */ virtual Material* FindMaterialByName(const char* findName, bool bRecursive);
+ /* 0x50 */ virtual void UnbindAnimationSelf(AnimTransform* animTrans);
+ /* 0x54 */ virtual AnimationLink* FindAnimationLink(AnimTransform* animTrans);
+ /* 0x58 */ virtual void SetAnimationEnable(AnimTransform* animTrans, bool bEnable,
+ bool bRecursive);
+ /* 0x64 */ virtual Material* GetContentMaterial() const;
+ /* 0x68 */ virtual Material* GetFrameMaterial(u32 frameIdx) const;
+ /* 0x6C */ virtual void DrawContent(const math::VEC2& basePt,
+ const WindowFrameSize& frameSize, u8 alpha);
+ /* 0x70 */ virtual void DrawFrame(const math::VEC2& basePt, const Frame& frame,
+ const WindowFrameSize& frameSize, u8 alpha);
+ /* 0x74 */ virtual void DrawFrame4(const math::VEC2& basePt, const Frame* frames,
+ const WindowFrameSize& frameSize, u8 alpha);
+ /* 0x78 */ virtual void DrawFrame8(const math::VEC2& basePt, const Frame* frames,
+ const WindowFrameSize& frameSize, u8 alpha);
+
+ WindowFrameSize GetFrameSize(u8 frameNum, const Frame* frames);
+
+ private:
+ /* 0x00 (base) */
+ /* 0x0D4 */ InflationLRTB mContentInflation;
+ /* 0x0E4 */ Content mContent;
+ /* 0x0FC */ Frame* mFrames;
+ /* 0x100 */ u8 mFrameNum;
+ }; // size = 0x104
+
+ } // namespace lyt
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/macros.h b/src/revolution/homebuttonLib/nw4hbm/macros.h
new file mode 100644
index 0000000000..715a819430
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/macros.h
@@ -0,0 +1,22 @@
+#ifndef NW4HBM_MACROS_H
+#define NW4HBM_MACROS_H
+
+// Doesn't exist in line info, but still helpful to have
+
+#if defined(CHAR_BIT)
+#define CHAR_BIT_ CHAR_BIT
+#else
+#define CHAR_BIT_ 8 // most common; default
+#endif
+
+// offset_ is in chars
+#define NW4HBM_BYTE_(byte_, offset_) (static_cast<unsigned char>(byte_) << CHAR_BIT_ * (offset_))
+
+// File versions and FourChars
+
+#define NW4HBM_FILE_VERSION(major_, minor_) (NW4HBM_BYTE_(major_, 1) | NW4HBM_BYTE_(minor_, 0))
+
+#define NW4HBM_FOUR_CHAR(a_, b_, c_, d_) \
+ (NW4HBM_BYTE_(a_, 3) | NW4HBM_BYTE_(b_, 2) | NW4HBM_BYTE_(c_, 1) | NW4HBM_BYTE_(d_, 0))
+
+#endif // NW4HBM_MACROS_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/arithmetic.h b/src/revolution/homebuttonLib/nw4hbm/math/arithmetic.h
new file mode 100644
index 0000000000..edec2fc1e5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/arithmetic.h
@@ -0,0 +1,164 @@
+#ifndef NW4HBM_MATH_ARITHMETIC_H
+#define NW4HBM_MATH_ARITHMETIC_H
+
+#include <math.h>
+#include <cmath.h>
+
+#include <revolution/os.h>
+#include <revolution/types.h>
+
+
+namespace nw4hbm {
+ namespace math {
+ f32 FrSqrt(f32 x);
+
+ f32 Hermite(f32 p1, f32 t1, f32 p2, f32 t2, f32 s);
+ f32 Bezier(f32 p1, f32 p2, f32 p3, f32 p4, f32 s);
+ f32 CatmullRom(f32 p0, f32 p1, f32 p2, f32 p3, f32 s);
+
+ u32 CntBit1(u32 x);
+ u32 CntBit1(u32 const* first, u32 const* last);
+ u32 DistBit(u32 const* first1, u32 const* last1, u32 const* first2);
+ u32 RevBit(u32 x);
+
+ int IExp(int x, u32 n);
+ u32 ILog10(u32 x);
+
+ inline u32 F32AsU32(f32 x) {
+ return *reinterpret_cast<u32*>(&x);
+ }
+
+ inline f32 U32AsF32(u32 x) {
+ return *reinterpret_cast<f32*>(&x);
+ }
+
+ inline s32 FGetExpPart(f32 f) {
+ s32 s = (F32AsU32(f) >> 23) & 0xff;
+
+ return s - 127;
+ }
+
+ inline f32 FGetMantPart(f32 f) {
+ u32 u = (F32AsU32(f) & 0x807fffff) | 0x3f800000;
+
+ return U32AsF32(u);
+ }
+
+ inline f32 FSelect(register f32 cond, register f32 ifPos, register f32 ifNeg) {
+ register f32 ret;
+
+#if defined(__MWERKS__)
+ asm { fsel ret, cond, ifPos, ifNeg }
+ ;
+#else
+#pragma unused(cond, ifPos, ifNeg)
+ ret = 0;
+#endif
+
+ return ret;
+ }
+
+ inline f32 FAbs(register f32 x) {
+ register f32 ret;
+
+#if defined(__MWERKS__)
+ asm { fabs ret, x }
+#else
+#pragma unused(x)
+ ret = 0;
+#endif
+
+ return ret;
+ }
+
+ inline f32 FNAbs(register f32 x) {
+ register f32 ret;
+
+#if defined(__MWERKS__)
+ asm { fnabs ret, x }
+#else
+#pragma unused(x)
+ ret = 0;
+#endif
+
+ return ret;
+ }
+
+ inline f32 AcosRad(f32 x) {
+ return std::acos(x);
+ }
+
+ inline f32 FCopySign(f32 abs, f32 sign) {
+ f32 pos = FAbs(abs);
+ f32 neg = FNAbs(abs);
+
+ return FSelect(sign, pos, neg);
+ }
+
+ inline s16 F32ToS16(f32 x) {
+ s16 rval;
+
+ OSf32tos16(&x, &rval);
+
+ return rval;
+ }
+
+ inline u16 F32ToU16(f32 x) {
+ u16 rval;
+
+ OSf32tou16(&x, &rval);
+
+ return rval;
+ }
+
+ inline f32 U16ToF32(u16 x) {
+ f32 rval;
+
+ OSu16tof32(&x, &rval);
+
+ return rval;
+ }
+
+ inline f32 S16ToF32(s16 x) {
+ f32 rval;
+
+ OSs16tof32(&x, &rval);
+
+ return rval;
+ }
+
+ inline f32 FSqrt(f32 x) {
+ return x <= 0.0f ? 0.0f : x * FrSqrt(x);
+ }
+
+ inline f32 FCbrt(f32 x) {
+ return std::pow(x, 1.0f / 3.0f);
+ }
+
+ inline u32 CntLz(register u32 x) {
+ register u32 result;
+
+#if defined(__MWERKS__)
+ asm { cntlzw result, x }
+#else
+#pragma unused(x)
+ result = 0;
+#endif
+
+ return result;
+ }
+
+ inline u32 DistBit(u32 x, u32 y) {
+ return CntBit1(x ^ y);
+ }
+
+ namespace detail {
+ f32 FExp(f32 x);
+ f32 FLog(f32 x);
+
+ u32 CntLz_(u32 x);
+ } // namespace detail
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_ARITHMETIC_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/constants.h b/src/revolution/homebuttonLib/nw4hbm/math/constants.h
new file mode 100644
index 0000000000..07d3460c77
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/constants.h
@@ -0,0 +1,25 @@
+#ifndef NW4HBM_MATH_CONSTANTS_H
+#define NW4HBM_MATH_CONSTANTS_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace math {
+ static f32 const Ln2 = 0.69314718056f;
+ static f32 const Pi = 3.141592653589f;
+ static f32 const Tau = 2.0f * Pi;
+
+ namespace convert {
+ static f32 const Deg2Rad = Pi / 180.0f;
+ // Rad2Deg
+
+ static f32 const Deg2FIdx = 256.0f / 360.0f;
+ static f32 const FIdx2Deg = 360.0f / 256.0f;
+
+ static f32 const Rad2FIdx = 256.0f / Tau;
+ static f32 const FIdx2Rad = Tau / 256.0f;
+ } // namespace convert
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_CONSTANTS_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/equation.h b/src/revolution/homebuttonLib/nw4hbm/math/equation.h
new file mode 100644
index 0000000000..2e82f79c91
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/equation.h
@@ -0,0 +1,14 @@
+#ifndef NW4HBM_MATH_EQUATION_H
+#define NW4HBM_MATH_EQUATION_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace math {
+ int SolveEquation2(f32* root, f32 a, f32 b, f32 c);
+ int SolveEquation3(f32* root, f32 a, f32 b, f32 c, f32 d);
+ int SolveEquation4(f32* root, f32 a, f32 b, f32 c, f32 d, f32 e);
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_EQUATION_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/geometry.h b/src/revolution/homebuttonLib/nw4hbm/math/geometry.h
new file mode 100644
index 0000000000..aefeb0736e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/geometry.h
@@ -0,0 +1,171 @@
+#ifndef NW4HBM_MATH_GEOMETRY_H
+#define NW4HBM_MATH_GEOMETRY_H
+
+#include <revolution/types.h>
+
+#include "types.h"
+
+namespace nw4hbm {
+ namespace math {
+ enum IntersectionResult {
+ INTERSECTION_NONE = 0,
+ INTERSECTION_1 = 1,
+ INTERSECTION_2 = 2,
+
+ INTERSECTION_LINE3_ON_PLANE = INTERSECTION_2,
+ INTERSECTION_RAY3_ON_PLANE = INTERSECTION_2,
+ INTERSECTION_SEGMENT3_ON_PLANE = INTERSECTION_2,
+
+ INTERSECTION_OUTSIDE = 0,
+ INTERSECTION_INSIDE = 1,
+ INTERSECTION_INTERSECT = 2
+ };
+
+ struct SEGMENT3 {
+ VEC3 P0; // size 0x0c, offset 0x00
+ VEC3 P1; // size 0x0c, offset 0x0c
+ }; // size 0x18
+
+ struct RAY3 {
+ VEC3 P; // size 0x0c, offset 0x00
+ VEC3 d; // size 0x0c, offset 0x0c
+ }; // size 0x18
+
+ struct LINE3 {
+ // methods
+ public:
+ // cdtors
+ LINE3() {}
+ LINE3(VEC3 const& Pt, VEC3 const& dir, bool isNormalized) : P(Pt), d(dir) {
+ if (!isNormalized)
+ Normalize();
+ }
+
+ // methods
+ void Normalize() { VEC3Normalize(&d, &d); }
+
+ void Set(SEGMENT3 const* S) {
+ P = S->P0;
+ VEC3Sub(&d, &S->P1, &S->P0);
+
+ Normalize();
+ }
+
+ // members
+ public:
+ VEC3 P; // size 0x0c, offset 0x00
+ VEC3 d; // size 0x0c, offset 0x0c
+ }; // size 0x18
+
+ struct CAPSULE {
+ SEGMENT3 S; // size 0x18, offset 0x00
+ f32 r; // size 0x04, offset 0x18
+ }; // size 0x1c
+
+ struct PLANE {
+ // methods
+ public:
+ // methods
+ void Set(VEC3 const* P0, VEC3 const* P1, VEC3 const* P2);
+
+ f32 Test(VEC3 const& P) const { return VEC3Dot(&N, &P) + d; }
+
+ // members
+ public:
+ VEC3 N; // size 0x0c, offset 0x00
+ f32 d; // size 0x04, offset 0x0c
+ }; // size 0x10
+
+ struct AABB {
+ // methods
+ public:
+ // cdtors
+ AABB() {}
+
+ // methods
+ void Normalize();
+ void Set(VEC3 const* arrayPoint, unsigned numPoints);
+ void Set(AABB const* box, MTX34 const* M);
+
+ // members
+ public:
+ VEC3 Pmin; // size 0x0c, offset 0x00
+ VEC3 Pmax; // size 0x0c, offset 0x0c
+ }; // size 0x18
+
+ struct SPHERE {
+ // methods
+ public:
+ // methods
+ void Set(VEC3 const* arrayPoint, unsigned numPoints);
+
+ // members
+ public:
+ VEC3 C; // size 0x0c, offset 0x00
+ f32 r; // size 0x04, offset 0x0c
+ }; // size 0x10
+
+ class FRUSTUM {
+ // methods
+ public:
+ // methods
+ void Set(f32 fovy, f32 aspect, f32 n, f32 f, MTX34 const& camera);
+ void Set(f32 top, f32 bottom, f32 left, f32 right, f32 n, f32 f, MTX34 const& camera);
+
+ bool IntersectSphere(SPHERE const* S) const;
+ bool IntersectAABB(AABB const* B) const;
+ IntersectionResult IntersectAABB_Ex(AABB const* B) const;
+
+ // members
+ public:
+ MTX34 cam; // size 0x30, offset 0x00
+ PLANE leftPlane; // size 0x10, offset 0x30
+ PLANE rightPlane; // size 0x10, offset 0x40
+ PLANE topPlane; // size 0x10, offset 0x50
+ PLANE bottomPlane; // size 0x10, offset 0x60
+ f32 near; // size 0x04, offset 0x70
+ f32 far; // size 0x04, offset 0x74
+ AABB box; // size 0x18, offset 0x78
+ PLANE planes[6]; // size 0x60, offset 0x90
+ }; // size 0xf0
+
+ f32 DistSqPoint3ToLine3(VEC3 const* P, LINE3 const* L, f32* t);
+ f32 DistSqPoint3ToRay3(VEC3 const* P, RAY3 const* R, f32* t);
+ f32 DistSqPoint3ToSegment3(VEC3 const* P, SEGMENT3 const* S, f32* t);
+ f32 DistSqPoint3ToPlane(VEC3 const* P, PLANE const* J, VEC3* Q);
+ f32 DistSqSphereToPlane(SPHERE const* S, PLANE const* J);
+ f32 DistSqPoint3ToPolyline3(VEC3 const* P, VEC3 const* vertices, unsigned nVertices);
+ f32 DistSqLine3ToLine3(LINE3 const* L0, LINE3 const* L1, f32* s, f32* t);
+ f32 DistSqSegment3ToSegment3(SEGMENT3 const* S1, SEGMENT3 const* S2, f32* s, f32* t);
+ f32 DistSqLine3ToRay3(LINE3 const* L, RAY3 const* R, f32* s, f32* t);
+ f32 DistSqLine3ToSegment3(LINE3 const* L0, SEGMENT3 const* S, f32* s, f32* t);
+ f32 DistSqRay3ToRay3(RAY3 const* R0, RAY3 const* R1, f32* s, f32* t);
+ f32 DistSqRay3ToSegment3(RAY3 const* R0, SEGMENT3 const* S, f32* s, f32* t);
+
+ IntersectionResult IntersectionLine3Plane(LINE3 const* L, PLANE const* J, f32* t, VEC3* I);
+ IntersectionResult IntersectionRay3Plane(RAY3 const* R, PLANE const* J, f32* t, VEC3* I);
+ IntersectionResult IntersectionSegment3Plane(SEGMENT3 const* S, PLANE const* J, f32* t,
+ VEC3* I);
+ IntersectionResult IntersectionLine3Sphere(LINE3 const* L, SPHERE const* sphere, f32* t0,
+ f32* t1);
+ IntersectionResult IntersectionRay3Sphere(RAY3 const* R, SPHERE const* sphere, f32* t0,
+ f32* t1);
+ bool IntersectionRay3Sphere(RAY3 const* R, SPHERE const* sphere);
+ IntersectionResult IntersectionSegment3Sphere(SEGMENT3 const* S, SPHERE const* sphere,
+ f32* t0, f32* t1);
+
+ bool IntersectionRay3AABB(RAY3 const* R, AABB const* box, f32* t);
+ bool IntersectionAABB(AABB const* a, AABB const* b);
+ bool IntersectionSphereAABB(SPHERE const* sphere, AABB const* aabb);
+ bool IntersectionSphere(SPHERE const* s0, SPHERE const* s1);
+ bool IntersectionCapsule(CAPSULE const* C0, CAPSULE const* C1);
+ bool IntersectionRay3Capsule(RAY3 const* R, CAPSULE const* C);
+ bool IntersectionLine3Capsule(LINE3 const* L, CAPSULE const* C);
+ bool IntersectionPlaneCapsule(PLANE const* J, CAPSULE const* C);
+
+ SPHERE* MergeSphere(SPHERE* s2, SPHERE const* s0, SPHERE const* s1);
+ AABB* MergeAABB(AABB* a2, AABB const* a0, AABB const* a1);
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_GEOMETRY_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp b/src/revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp
new file mode 100644
index 0000000000..b3655c49ca
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/math_triangular.cpp
@@ -0,0 +1,433 @@
+#include "arithmetic.h"
+#include <revolution/types.h>
+
+typedef struct {
+ /* 0x00 */ f32 sin_val;
+ /* 0x04 */ f32 cos_val;
+ /* 0x08 */ f32 sin_delta;
+ /* 0x0C */ f32 cos_delta;
+} SinCosSample; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ f32 atan_val;
+ /* 0x04 */ f32 atan_delta;
+} ArcTanSample; // size = 0x08
+
+namespace nw4hbm {
+ namespace math {
+ namespace {
+ f32 AtanFIdx_(f32 x);
+
+ static SinCosSample sSinCosTbl[] = {
+ {0.0f, 1.0f, 0.024541f, -3.01e-4f},
+ {0.024541f, 0.999699f, 0.024526f, -9.03e-4f},
+ {0.049068f, 0.998795f, 0.024497f, -0.001505f},
+ {0.073565f, 0.99729f, 0.024453f, -0.002106f},
+ {0.098017f, 0.995185f, 0.024394f, -0.002705f},
+ {0.122411f, 0.99248f, 0.02432f, -0.003303f},
+ {0.14673f, 0.989177f, 0.024231f, -0.003899f},
+ {0.170962f, 0.985278f, 0.024128f, -0.004492f},
+ {0.19509f, 0.980785f, 0.024011f, -0.005083f},
+ {0.219101f, 0.975702f, 0.023879f, -0.005671f},
+ {0.24298f, 0.970031f, 0.023733f, -0.006255f},
+ {0.266713f, 0.963776f, 0.023572f, -0.006836f},
+ {0.290285f, 0.95694f, 0.023397f, -0.007412f},
+ {0.313682f, 0.949528f, 0.023208f, -0.007984f},
+ {0.33689f, 0.941544f, 0.023005f, -0.008551f},
+ {0.359895f, 0.932993f, 0.022788f, -0.009113f},
+ {0.382683f, 0.92388f, 0.022558f, -0.00967f},
+ {0.405241f, 0.91421f, 0.022314f, -0.01022f},
+ {0.427555f, 0.903989f, 0.022056f, -0.010765f},
+ {0.449611f, 0.893224f, 0.021785f, -0.011303f},
+ {0.471397f, 0.881921f, 0.021501f, -0.011834f},
+ {0.492898f, 0.870087f, 0.021205f, -0.012358f},
+ {0.514103f, 0.857729f, 0.020895f, -0.012875f},
+ {0.534998f, 0.844854f, 0.020573f, -0.013384f},
+ {0.55557f, 0.83147f, 0.020238f, -0.013885f},
+ {0.575808f, 0.817585f, 0.019891f, -0.014377f},
+ {0.595699f, 0.803208f, 0.019532f, -0.014861f},
+ {0.615232f, 0.788346f, 0.019162f, -0.015336f},
+ {0.634393f, 0.77301f, 0.01878f, -0.015802f},
+ {0.653173f, 0.757209f, 0.018386f, -0.016258f},
+ {0.671559f, 0.740951f, 0.017982f, -0.016704f},
+ {0.689541f, 0.724247f, 0.017566f, -0.01714f},
+ {0.707107f, 0.707107f, 0.01714f, -0.017566f},
+ {0.724247f, 0.689541f, 0.016704f, -0.017982f},
+ {0.740951f, 0.671559f, 0.016258f, -0.018386f},
+ {0.757209f, 0.653173f, 0.015802f, -0.01878f},
+ {0.77301f, 0.634393f, 0.015336f, -0.019162f},
+ {0.788346f, 0.615232f, 0.014861f, -0.019532f},
+ {0.803208f, 0.595699f, 0.014377f, -0.019891f},
+ {0.817585f, 0.575808f, 0.013885f, -0.020238f},
+ {0.83147f, 0.55557f, 0.013384f, -0.020573f},
+ {0.844854f, 0.534998f, 0.012875f, -0.020895f},
+ {0.857729f, 0.514103f, 0.012358f, -0.021205f},
+ {0.870087f, 0.492898f, 0.011834f, -0.021501f},
+ {0.881921f, 0.471397f, 0.011303f, -0.021785f},
+ {0.893224f, 0.449611f, 0.010765f, -0.022056f},
+ {0.903989f, 0.427555f, 0.01022f, -0.022314f},
+ {0.91421f, 0.405241f, 0.00967f, -0.022558f},
+ {0.92388f, 0.382683f, 0.009113f, -0.022788f},
+ {0.932993f, 0.359895f, 0.008551f, -0.023005f},
+ {0.941544f, 0.33689f, 0.007984f, -0.023208f},
+ {0.949528f, 0.313682f, 0.007412f, -0.023397f},
+ {0.95694f, 0.290285f, 0.006836f, -0.023572f},
+ {0.963776f, 0.266713f, 0.006255f, -0.023733f},
+ {0.970031f, 0.24298f, 0.005671f, -0.023879f},
+ {0.975702f, 0.219101f, 0.005083f, -0.024011f},
+ {0.980785f, 0.19509f, 0.004492f, -0.024128f},
+ {0.985278f, 0.170962f, 0.003899f, -0.024231f},
+ {0.989177f, 0.14673f, 0.003303f, -0.02432f},
+ {0.99248f, 0.122411f, 0.002705f, -0.024394f},
+ {0.995185f, 0.098017f, 0.002106f, -0.024453f},
+ {0.99729f, 0.073565f, 0.001505f, -0.024497f},
+ {0.998795f, 0.049068f, 9.03e-4f, -0.024526f},
+ {0.999699f, 0.024541f, 3.01e-4f, -0.024541f},
+ {1.0f, 0.0f, -3.01e-4f, -0.024541f},
+ {0.999699f, -0.024541f, -9.03e-4f, -0.024526f},
+ {0.998795f, -0.049068f, -0.001505f, -0.024497f},
+ {0.99729f, -0.073565f, -0.002106f, -0.024453f},
+ {0.995185f, -0.098017f, -0.002705f, -0.024394f},
+ {0.99248f, -0.122411f, -0.003303f, -0.02432f},
+ {0.989177f, -0.14673f, -0.003899f, -0.024231f},
+ {0.985278f, -0.170962f, -0.004492f, -0.024128f},
+ {0.980785f, -0.19509f, -0.005083f, -0.024011f},
+ {0.975702f, -0.219101f, -0.005671f, -0.023879f},
+ {0.970031f, -0.24298f, -0.006255f, -0.023733f},
+ {0.963776f, -0.266713f, -0.006836f, -0.023572f},
+ {0.95694f, -0.290285f, -0.007412f, -0.023397f},
+ {0.949528f, -0.313682f, -0.007984f, -0.023208f},
+ {0.941544f, -0.33689f, -0.008551f, -0.023005f},
+ {0.932993f, -0.359895f, -0.009113f, -0.022788f},
+ {0.92388f, -0.382683f, -0.00967f, -0.022558f},
+ {0.91421f, -0.405241f, -0.01022f, -0.022314f},
+ {0.903989f, -0.427555f, -0.010765f, -0.022056f},
+ {0.893224f, -0.449611f, -0.011303f, -0.021785f},
+ {0.881921f, -0.471397f, -0.011834f, -0.021501f},
+ {0.870087f, -0.492898f, -0.012358f, -0.021205f},
+ {0.857729f, -0.514103f, -0.012875f, -0.020895f},
+ {0.844854f, -0.534998f, -0.013384f, -0.020573f},
+ {0.83147f, -0.55557f, -0.013885f, -0.020238f},
+ {0.817585f, -0.575808f, -0.014377f, -0.019891f},
+ {0.803208f, -0.595699f, -0.014861f, -0.019532f},
+ {0.788346f, -0.615232f, -0.015336f, -0.019162f},
+ {0.77301f, -0.634393f, -0.015802f, -0.01878f},
+ {0.757209f, -0.653173f, -0.016258f, -0.018386f},
+ {0.740951f, -0.671559f, -0.016704f, -0.017982f},
+ {0.724247f, -0.689541f, -0.01714f, -0.017566f},
+ {0.707107f, -0.707107f, -0.017566f, -0.01714f},
+ {0.689541f, -0.724247f, -0.017982f, -0.016704f},
+ {0.671559f, -0.740951f, -0.018386f, -0.016258f},
+ {0.653173f, -0.757209f, -0.01878f, -0.015802f},
+ {0.634393f, -0.77301f, -0.019162f, -0.015336f},
+ {0.615232f, -0.788346f, -0.019532f, -0.014861f},
+ {0.595699f, -0.803208f, -0.019891f, -0.014377f},
+ {0.575808f, -0.817585f, -0.020238f, -0.013885f},
+ {0.55557f, -0.83147f, -0.020573f, -0.013384f},
+ {0.534998f, -0.844854f, -0.020895f, -0.012875f},
+ {0.514103f, -0.857729f, -0.021205f, -0.012358f},
+ {0.492898f, -0.870087f, -0.021501f, -0.011834f},
+ {0.471397f, -0.881921f, -0.021785f, -0.011303f},
+ {0.449611f, -0.893224f, -0.022056f, -0.010765f},
+ {0.427555f, -0.903989f, -0.022314f, -0.01022f},
+ {0.405241f, -0.91421f, -0.022558f, -0.00967f},
+ {0.382683f, -0.92388f, -0.022788f, -0.009113f},
+ {0.359895f, -0.932993f, -0.023005f, -0.008551f},
+ {0.33689f, -0.941544f, -0.023208f, -0.007984f},
+ {0.313682f, -0.949528f, -0.023397f, -0.007412f},
+ {0.290285f, -0.95694f, -0.023572f, -0.006836f},
+ {0.266713f, -0.963776f, -0.023733f, -0.006255f},
+ {0.24298f, -0.970031f, -0.023879f, -0.005671f},
+ {0.219101f, -0.975702f, -0.024011f, -0.005083f},
+ {0.19509f, -0.980785f, -0.024128f, -0.004492f},
+ {0.170962f, -0.985278f, -0.024231f, -0.003899f},
+ {0.14673f, -0.989177f, -0.02432f, -0.003303f},
+ {0.122411f, -0.99248f, -0.024394f, -0.002705f},
+ {0.098017f, -0.995185f, -0.024453f, -0.002106f},
+ {0.073565f, -0.99729f, -0.024497f, -0.001505f},
+ {0.049068f, -0.998795f, -0.024526f, -9.03e-4f},
+ {0.024541f, -0.999699f, -0.024541f, -3.01e-4f},
+ {0.0f, -1.0f, -0.024541f, 3.01e-4f},
+ {-0.024541f, -0.999699f, -0.024526f, 9.03e-4f},
+ {-0.049068f, -0.998795f, -0.024497f, 0.001505f},
+ {-0.073565f, -0.99729f, -0.024453f, 0.002106f},
+ {-0.098017f, -0.995185f, -0.024394f, 0.002705f},
+ {-0.122411f, -0.99248f, -0.02432f, 0.003303f},
+ {-0.14673f, -0.989177f, -0.024231f, 0.003899f},
+ {-0.170962f, -0.985278f, -0.024128f, 0.004492f},
+ {-0.19509f, -0.980785f, -0.024011f, 0.005083f},
+ {-0.219101f, -0.975702f, -0.023879f, 0.005671f},
+ {-0.24298f, -0.970031f, -0.023733f, 0.006255f},
+ {-0.266713f, -0.963776f, -0.023572f, 0.006836f},
+ {-0.290285f, -0.95694f, -0.023397f, 0.007412f},
+ {-0.313682f, -0.949528f, -0.023208f, 0.007984f},
+ {-0.33689f, -0.941544f, -0.023005f, 0.008551f},
+ {-0.359895f, -0.932993f, -0.022788f, 0.009113f},
+ {-0.382683f, -0.92388f, -0.022558f, 0.00967f},
+ {-0.405241f, -0.91421f, -0.022314f, 0.01022f},
+ {-0.427555f, -0.903989f, -0.022056f, 0.010765f},
+ {-0.449611f, -0.893224f, -0.021785f, 0.011303f},
+ {-0.471397f, -0.881921f, -0.021501f, 0.011834f},
+ {-0.492898f, -0.870087f, -0.021205f, 0.012358f},
+ {-0.514103f, -0.857729f, -0.020895f, 0.012875f},
+ {-0.534998f, -0.844854f, -0.020573f, 0.013384f},
+ {-0.55557f, -0.83147f, -0.020238f, 0.013885f},
+ {-0.575808f, -0.817585f, -0.019891f, 0.014377f},
+ {-0.595699f, -0.803208f, -0.019532f, 0.014861f},
+ {-0.615232f, -0.788346f, -0.019162f, 0.015336f},
+ {-0.634393f, -0.77301f, -0.01878f, 0.015802f},
+ {-0.653173f, -0.757209f, -0.018386f, 0.016258f},
+ {-0.671559f, -0.740951f, -0.017982f, 0.016704f},
+ {-0.689541f, -0.724247f, -0.017566f, 0.01714f},
+ {-0.707107f, -0.707107f, -0.01714f, 0.017566f},
+ {-0.724247f, -0.689541f, -0.016704f, 0.017982f},
+ {-0.740951f, -0.671559f, -0.016258f, 0.018386f},
+ {-0.757209f, -0.653173f, -0.015802f, 0.01878f},
+ {-0.77301f, -0.634393f, -0.015336f, 0.019162f},
+ {-0.788346f, -0.615232f, -0.014861f, 0.019532f},
+ {-0.803208f, -0.595699f, -0.014377f, 0.019891f},
+ {-0.817585f, -0.575808f, -0.013885f, 0.020238f},
+ {-0.83147f, -0.55557f, -0.013384f, 0.020573f},
+ {-0.844854f, -0.534998f, -0.012875f, 0.020895f},
+ {-0.857729f, -0.514103f, -0.012358f, 0.021205f},
+ {-0.870087f, -0.492898f, -0.011834f, 0.021501f},
+ {-0.881921f, -0.471397f, -0.011303f, 0.021785f},
+ {-0.893224f, -0.449611f, -0.010765f, 0.022056f},
+ {-0.903989f, -0.427555f, -0.01022f, 0.022314f},
+ {-0.91421f, -0.405241f, -0.00967f, 0.022558f},
+ {-0.92388f, -0.382683f, -0.009113f, 0.022788f},
+ {-0.932993f, -0.359895f, -0.008551f, 0.023005f},
+ {-0.941544f, -0.33689f, -0.007984f, 0.023208f},
+ {-0.949528f, -0.313682f, -0.007412f, 0.023397f},
+ {-0.95694f, -0.290285f, -0.006836f, 0.023572f},
+ {-0.963776f, -0.266713f, -0.006255f, 0.023733f},
+ {-0.970031f, -0.24298f, -0.005671f, 0.023879f},
+ {-0.975702f, -0.219101f, -0.005083f, 0.024011f},
+ {-0.980785f, -0.19509f, -0.004492f, 0.024128f},
+ {-0.985278f, -0.170962f, -0.003899f, 0.024231f},
+ {-0.989177f, -0.14673f, -0.003303f, 0.02432f},
+ {-0.99248f, -0.122411f, -0.002705f, 0.024394f},
+ {-0.995185f, -0.098017f, -0.002106f, 0.024453f},
+ {-0.99729f, -0.073565f, -0.001505f, 0.024497f},
+ {-0.998795f, -0.049068f, -9.03e-4f, 0.024526f},
+ {-0.999699f, -0.024541f, -3.01e-4f, 0.024541f},
+ {-1.0f, -0.0f, 3.01e-4f, 0.024541f},
+ {-0.999699f, 0.024541f, 9.03e-4f, 0.024526f},
+ {-0.998795f, 0.049068f, 0.001505f, 0.024497f},
+ {-0.99729f, 0.073565f, 0.002106f, 0.024453f},
+ {-0.995185f, 0.098017f, 0.002705f, 0.024394f},
+ {-0.99248f, 0.122411f, 0.003303f, 0.02432f},
+ {-0.989177f, 0.14673f, 0.003899f, 0.024231f},
+ {-0.985278f, 0.170962f, 0.004492f, 0.024128f},
+ {-0.980785f, 0.19509f, 0.005083f, 0.024011f},
+ {-0.975702f, 0.219101f, 0.005671f, 0.023879f},
+ {-0.970031f, 0.24298f, 0.006255f, 0.023733f},
+ {-0.963776f, 0.266713f, 0.006836f, 0.023572f},
+ {-0.95694f, 0.290285f, 0.007412f, 0.023397f},
+ {-0.949528f, 0.313682f, 0.007984f, 0.023208f},
+ {-0.941544f, 0.33689f, 0.008551f, 0.023005f},
+ {-0.932993f, 0.359895f, 0.009113f, 0.022788f},
+ {-0.92388f, 0.382683f, 0.00967f, 0.022558f},
+ {-0.91421f, 0.405241f, 0.01022f, 0.022314f},
+ {-0.903989f, 0.427555f, 0.010765f, 0.022056f},
+ {-0.893224f, 0.449611f, 0.011303f, 0.021785f},
+ {-0.881921f, 0.471397f, 0.011834f, 0.021501f},
+ {-0.870087f, 0.492898f, 0.012358f, 0.021205f},
+ {-0.857729f, 0.514103f, 0.012875f, 0.020895f},
+ {-0.844854f, 0.534998f, 0.013384f, 0.020573f},
+ {-0.83147f, 0.55557f, 0.013885f, 0.020238f},
+ {-0.817585f, 0.575808f, 0.014377f, 0.019891f},
+ {-0.803208f, 0.595699f, 0.014861f, 0.019532f},
+ {-0.788346f, 0.615232f, 0.015336f, 0.019162f},
+ {-0.77301f, 0.634393f, 0.015802f, 0.01878f},
+ {-0.757209f, 0.653173f, 0.016258f, 0.018386f},
+ {-0.740951f, 0.671559f, 0.016704f, 0.017982f},
+ {-0.724247f, 0.689541f, 0.01714f, 0.017566f},
+ {-0.707107f, 0.707107f, 0.017566f, 0.01714f},
+ {-0.689541f, 0.724247f, 0.017982f, 0.016704f},
+ {-0.671559f, 0.740951f, 0.018386f, 0.016258f},
+ {-0.653173f, 0.757209f, 0.01878f, 0.015802f},
+ {-0.634393f, 0.77301f, 0.019162f, 0.015336f},
+ {-0.615232f, 0.788346f, 0.019532f, 0.014861f},
+ {-0.595699f, 0.803208f, 0.019891f, 0.014377f},
+ {-0.575808f, 0.817585f, 0.020238f, 0.013885f},
+ {-0.55557f, 0.83147f, 0.020573f, 0.013384f},
+ {-0.534998f, 0.844854f, 0.020895f, 0.012875f},
+ {-0.514103f, 0.857729f, 0.021205f, 0.012358f},
+ {-0.492898f, 0.870087f, 0.021501f, 0.011834f},
+ {-0.471397f, 0.881921f, 0.021785f, 0.011303f},
+ {-0.449611f, 0.893224f, 0.022056f, 0.010765f},
+ {-0.427555f, 0.903989f, 0.022314f, 0.01022f},
+ {-0.405241f, 0.91421f, 0.022558f, 0.00967f},
+ {-0.382683f, 0.92388f, 0.022788f, 0.009113f},
+ {-0.359895f, 0.932993f, 0.023005f, 0.008551f},
+ {-0.33689f, 0.941544f, 0.023208f, 0.007984f},
+ {-0.313682f, 0.949528f, 0.023397f, 0.007412f},
+ {-0.290285f, 0.95694f, 0.023572f, 0.006836f},
+ {-0.266713f, 0.963776f, 0.023733f, 0.006255f},
+ {-0.24298f, 0.970031f, 0.023879f, 0.005671f},
+ {-0.219101f, 0.975702f, 0.024011f, 0.005083f},
+ {-0.19509f, 0.980785f, 0.024128f, 0.004492f},
+ {-0.170962f, 0.985278f, 0.024231f, 0.003899f},
+ {-0.14673f, 0.989177f, 0.02432f, 0.003303f},
+ {-0.122411f, 0.99248f, 0.024394f, 0.002705f},
+ {-0.098017f, 0.995185f, 0.024453f, 0.002106f},
+ {-0.073565f, 0.99729f, 0.024497f, 0.001505f},
+ {-0.049068f, 0.998795f, 0.024526f, 9.03e-4f},
+ {-0.024541f, 0.999699f, 0.024541f, 3.01e-4f},
+ {-0.0f, 1.0f, 0.024541f, -3.01e-4f},
+ };
+
+ static ArcTanSample sArcTanTbl[] = {
+ {0.0f, 1.2728254f}, {1.2728254f, 1.2703458f}, {2.5431712f, 1.2654155f},
+ {3.8085866f, 1.2580916f}, {5.0666785f, 1.2484571f}, {6.3151355f, 1.2366195f},
+ {7.551755f, 1.2227072f}, {8.774462f, 1.2068666f}, {9.981329f, 1.1892582f},
+ {11.170587f, 1.1700529f}, {12.34064f, 1.149428f}, {13.4900675f, 1.1275644f},
+ {14.617632f, 1.1046423f}, {15.722275f, 1.0808387f}, {16.803114f, 1.0563251f},
+ {17.859438f, 1.0312649f}, {18.890703f, 1.005812f}, {19.896515f, 0.98010963f},
+ {20.876625f, 0.9542891f}, {21.830914f, 0.9284698f}, {22.759384f, 0.90275896f},
+ {23.662142f, 0.87725157f}, {24.539394f, 0.8520309f}, {25.391424f, 0.8271689f},
+ {26.218594f, 0.802727f}, {27.02132f, 0.77875656f}, {27.800077f, 0.7553001f},
+ {28.555378f, 0.7323915f}, {29.28777f, 0.7100574f}, {29.997826f, 0.6883175f},
+ {30.686144f, 0.66718566f}, {31.353329f, 0.6466705f}, {32.0f, 0.62677616f},
+ };
+
+ } // namespace
+
+ namespace {
+
+ f32 AtanFIdx_(f32 x) {
+ u16 idx;
+ f32 val;
+ f32 r;
+
+ x *= 32.0f;
+
+ idx = F32ToU16(x);
+ r = x - U16ToF32(idx);
+ val = sArcTanTbl[idx].atan_val + r * sArcTanTbl[idx].atan_delta;
+
+ return val;
+ }
+
+ } // unnamed namespace
+
+ f32 SinFIdx(f32 fidx) {
+ f32 abs_fidx = FAbs(fidx); // hm
+ f32 val;
+ u16 idx;
+ f32 r;
+
+ while (abs_fidx >= 65536.0f) {
+ abs_fidx -= 65536.0f;
+ }
+
+ idx = F32ToU16(abs_fidx);
+ r = abs_fidx - U16ToF32(idx);
+
+ idx &= 0xff;
+ val = sSinCosTbl[idx].sin_val + r * sSinCosTbl[idx].sin_delta;
+
+ return fidx < 0.0f ? -val : val;
+ }
+
+ f32 CosFIdx(f32 fidx) {
+ u16 idx;
+ f32 r;
+
+ fidx = FAbs(fidx);
+ while (fidx >= 65536.0f) {
+ fidx -= 65536.0f;
+ }
+
+ idx = F32ToU16(fidx);
+ r = fidx - U16ToF32(idx);
+
+ idx &= 0xff;
+
+ return sSinCosTbl[idx].cos_val + r * sSinCosTbl[idx].cos_delta;
+ }
+
+ extern void __deadstrip1();
+ extern void __deadstrip1() {
+ (void)32.0f;
+ (void)64.0f;
+ }
+
+ f32 Atan2FIdx(f32 y, f32 x) {
+ f32 a;
+ f32 b;
+ f32 c;
+ bool minus;
+
+ if (x == 0.0f && y == 0.0f) {
+ return 0.0f;
+ }
+
+ if (x >= 0.0f) {
+ if (y >= 0.0f) {
+ if (x >= y) {
+ a = x;
+ b = y;
+ c = 0.0f;
+
+ minus = false;
+ } else {
+ a = y;
+ b = x;
+ c = 64.0f;
+
+ minus = true;
+ }
+ } else {
+ if (x >= -y) {
+ a = x;
+ b = -y;
+ c = 0.0f;
+
+ minus = true;
+ } else {
+ a = -y;
+ b = x;
+ c = -64.0f;
+
+ minus = false;
+ }
+ }
+ } else {
+ if (y >= 0.0f) {
+ if (-x >= y) {
+ a = -x;
+ b = y;
+ c = 128.0f;
+
+ minus = true;
+ } else {
+ a = y;
+ b = -x;
+ c = 64.0f;
+
+ minus = false;
+ }
+ } else {
+ if (-x >= -y) {
+ a = -x;
+ b = -y;
+ c = -128.0f;
+
+ minus = false;
+ } else {
+ a = -y;
+ b = -x;
+ c = -64.0f;
+
+ minus = true;
+ }
+ }
+ }
+
+ return minus ? c - AtanFIdx_(b / a) : c + AtanFIdx_(b / a);
+ }
+
+ } // namespace math
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/triangular.h b/src/revolution/homebuttonLib/nw4hbm/math/triangular.h
new file mode 100644
index 0000000000..3a96e81205
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/triangular.h
@@ -0,0 +1,47 @@
+#ifndef NW4HBM_MATH_TRIANGULAR_H
+#define NW4HBM_MATH_TRIANGULAR_H
+
+#include <math.h>
+#include <cmath.h>
+
+#include <revolution/types.h>
+
+#include "constants.h"
+
+namespace nw4hbm {
+ namespace math {
+ f32 SinFIdx(f32 fidx);
+ f32 CosFIdx(f32 fidx);
+ void SinCosFIdx(f32* s, f32* c, f32 fidx);
+
+ f32 AtanFIdx(f32 x);
+ f32 Atan2FIdx(f32 y, f32 x);
+
+ inline f32 TanFIdx(f32 fidx) {
+ // They were just like "Ah fuck it we already got too many tables" haha lol
+ return std::tanf(fidx * convert::FIdx2Rad);
+ }
+
+ inline f32 CosRad(f32 rad) {
+ return CosFIdx(rad * convert::Rad2FIdx);
+ }
+
+ inline f32 SinDeg(f32 deg) {
+ return SinFIdx(deg * convert::Deg2FIdx);
+ }
+
+ inline f32 CosDeg(f32 deg) {
+ return CosFIdx(deg * convert::Deg2FIdx);
+ }
+
+ inline f32 TanDeg(f32 deg) {
+ return TanFIdx(deg * convert::Deg2FIdx);
+ }
+
+ inline f32 Atan2Deg(f32 y, f32 x) {
+ return Atan2FIdx(y, x) * convert::FIdx2Deg;
+ }
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_TRIANGULAR_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/math/types.h b/src/revolution/homebuttonLib/nw4hbm/math/types.h
new file mode 100644
index 0000000000..7db18aa542
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/math/types.h
@@ -0,0 +1,523 @@
+#ifndef NW4HBM_MATH_TYPES_H
+#define NW4HBM_MATH_TYPES_H
+
+#include <revolution/types.h>
+#include <revolution/mtx.h>
+
+namespace nw4hbm {
+ namespace math {
+ struct _VEC2 {
+ f32 x; // size 0x04, offset 0x00
+ f32 y; // size 0x04, offset 0x04
+ }; // size 0x08
+
+ struct _VEC3 {
+ f32 x; // size 0x04, offset 0x00
+ f32 y; // size 0x04, offset 0x04
+ f32 z; // size 0x04, offset 0x08
+ }; // size 0x0c
+
+ struct _VEC4 {
+ f32 x; // size 0x04, offset 0x00
+ f32 y; // size 0x04, offset 0x04
+ f32 z; // size 0x04, offset 0x08
+ f32 w; // size 0x04, offset 0x0c
+ }; // size 0x10
+
+ struct _QUAT {
+ f32 x; // size 0x04, offset 0x00
+ f32 y; // size 0x04, offset 0x04
+ f32 z; // size 0x04, offset 0x08
+ f32 w; // size 0x04, offset 0x0c
+ }; // size 0x10
+
+ struct _MTX33 {
+ union {
+ struct {
+ f32 _00; // size 0x04, offset 0x00
+ f32 _01; // size 0x04, offset 0x04
+ f32 _02; // size 0x04, offset 0x08
+ f32 _10; // size 0x04, offset 0x0c
+ f32 _11; // size 0x04, offset 0x10
+ f32 _12; // size 0x04, offset 0x14
+ f32 _20; // size 0x04, offset 0x18
+ f32 _21; // size 0x04, offset 0x1c
+ f32 _22; // size 0x04, offset 0x20
+ }; // size 0x24
+
+ f32 m[3][3]; // size 0x24
+ f32 a[3 * 3]; // size 0x24
+ }; // size 0x24, offset 0x00
+ }; // size 0x24
+
+ struct _MTX34 {
+ union {
+ struct {
+ f32 _00; // size 0x04, offset 0x00
+ f32 _01; // size 0x04, offset 0x04
+ f32 _02; // size 0x04, offset 0x08
+ f32 _03; // size 0x04, offset 0x0c
+ f32 _10; // size 0x04, offset 0x10
+ f32 _11; // size 0x04, offset 0x14
+ f32 _12; // size 0x04, offset 0x18
+ f32 _13; // size 0x04, offset 0x1c
+ f32 _20; // size 0x04, offset 0x20
+ f32 _21; // size 0x04, offset 0x24
+ f32 _22; // size 0x04, offset 0x28
+ f32 _23; // size 0x04, offset 0x2c
+ }; // size 0x30
+
+ f32 m[3][4]; // size 0x30
+ f32 a[3 * 4]; // size 0x30
+ Mtx mtx; // size 0x30
+ }; // size 0x30, offset 0x00
+ }; // size 0x30
+
+ struct _MTX44 {
+ union {
+ struct {
+ f32 _00; // size 0x04, offset 0x00
+ f32 _01; // size 0x04, offset 0x04
+ f32 _02; // size 0x04, offset 0x08
+ f32 _03; // size 0x04, offset 0x0c
+ f32 _10; // size 0x04, offset 0x10
+ f32 _11; // size 0x04, offset 0x14
+ f32 _12; // size 0x04, offset 0x18
+ f32 _13; // size 0x04, offset 0x1c
+ f32 _20; // size 0x04, offset 0x20
+ f32 _21; // size 0x04, offset 0x24
+ f32 _22; // size 0x04, offset 0x28
+ f32 _23; // size 0x04, offset 0x2c
+ f32 _30; // size 0x04, offset 0x30
+ f32 _31; // size 0x04, offset 0x34
+ f32 _32; // size 0x04, offset 0x38
+ f32 _33; // size 0x04, offset 0x3c
+ }; // size 0x40
+
+ f32 m[4][4]; // size 0x40
+ f32 a[4 * 4]; // size 0x40
+ Mtx44 mtx; // size 0x40
+ }; // size 0x40, offset 0x00
+ }; // size 0x40
+
+ struct VEC2 : public _VEC2 {
+ // methods
+ public:
+ // cdtors
+ VEC2() {}
+ VEC2(f32 fx, f32 fy) {
+ x = fx;
+ y = fy;
+ }
+
+ // operators
+ operator f32*() { return reinterpret_cast<f32*>(this); }
+ operator f32 const*() const { return reinterpret_cast<f32 const*>(this); }
+
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _VEC2 */ // size 0x08, offset 0x00
+ }; // size 0x08
+
+ struct VEC3 : public _VEC3 {
+ // methods
+ public:
+ // cdtors
+ VEC3() {}
+ VEC3(f32 fx, f32 fy, f32 fz) {
+ x = fx;
+ y = fy;
+ z = fz;
+ }
+
+ // operators
+ VEC3 operator+(VEC3 const& rhs) const;
+ VEC3 operator-(VEC3 const& rhs) const;
+
+ VEC3 operator*(f32 f) const;
+
+ operator VecPtr() { return reinterpret_cast<VecPtr>(this); }
+ operator CVecPtr() const { return (CVecPtr)(this); }
+
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _VEC3 */ // size 0x0c, offset 0x00
+ }; // size 0x0c
+
+ struct VEC4 : public _VEC4 {
+ // methods
+ public:
+ // cdtors
+ VEC4() {}
+
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _VEC4 */ // size 0x10, offset 0x00
+ }; // size 0x10
+
+ struct QUAT : public _QUAT {
+ // methods
+ public:
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _QUAT */ // size 0x10, offset 0x00
+ }; // size 0x10
+
+ struct MTX33 : public _MTX33 {
+ // methods
+ public:
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _MTX33 */ // size 0x24, offset 0x00
+ }; // size 0x24
+
+ struct MTX34 : public _MTX34 {
+ // methods
+ public:
+ // cdtors
+ MTX34() {}
+
+ // operators
+ operator MtxPtr() { return mtx; }
+ operator CMtxP() const { return mtx; }
+
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _MTX34 */ // size 0x30, offset 0x00
+ }; // size 0x30
+
+ struct MTX44 : public _MTX44 {
+ // methods
+ public:
+ // methods
+ void Report(bool bNewline, char const* name) const;
+
+ // members
+ public:
+ /* base _MTX44 */ // size 0x24, offset 0x00
+ }; // size 0x24
+
+ inline VEC3* VEC3Add(register VEC3* pOut, register VEC3 const* p1,
+ register VEC3 const* p2) {
+ register f32 a, b, c;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ // xy
+ psq_l a, 0(p1), FALSE, qr0
+ psq_l b, 0(p2), FALSE, qr0
+
+ ps_add c, a, b
+ psq_st c, 0(pOut), FALSE, qr0
+
+ // z-
+ psq_l a, 8(p1), TRUE, qr0
+ psq_l b, 8(p2), TRUE, qr0
+
+ ps_add c, a, b
+ psq_st c, 8(pOut), TRUE, qr0
+
+#undef qr0
+ }
+#else
+#pragma unused(p1, p2, a, b, c)
+#endif
+
+ return pOut;
+ }
+
+ inline VEC3* VEC3Sub(register VEC3* pOut, register VEC3 const* p1,
+ register VEC3 const* p2) {
+ register f32 a, b, c;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ // xy
+ psq_l a, 0(p1), FALSE, qr0
+ psq_l b, 0(p2), FALSE, qr0
+
+ ps_sub c, a, b
+ psq_st c, 0(pOut), FALSE, qr0
+
+ // z-
+ psq_l a, 8(p1), TRUE, qr0
+ psq_l b, 8(p2), TRUE, qr0
+
+ ps_sub c, a, b
+ psq_st c, 8(pOut), TRUE, qr0
+
+#undef qr0
+ }
+#else
+#pragma unused(p1, p2, a, b, c)
+#endif
+
+ return pOut;
+ }
+
+ inline VEC3* VEC3Scale(register VEC3* pOut, register VEC3 const* p, register f32 scale) {
+ register f32 a, b;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ // xy
+ psq_l a, 0(p), FALSE, qr0
+ ps_muls0 b, a, scale
+ psq_st b, 0(pOut), FALSE, qr0
+
+ // z-
+ psq_l a, 8(p), TRUE, qr0
+ ps_muls0 b, a, scale
+ psq_st b, 8(pOut), TRUE, qr0
+
+#undef qr0
+ }
+#else
+#pragma unused(p, scale, a, b)
+#endif
+
+ return pOut;
+ }
+
+ inline VEC3* VEC3Lerp(register VEC3* pOut, register VEC3 const* p1, register VEC3 const* p2,
+ register f32 t) {
+ register f32 a, b, c;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ // xy
+ psq_l a, 0(p1), FALSE, qr0
+ psq_l b, 0(p2), FALSE, qr0
+
+ ps_sub c, b, a
+ ps_madds0 c, c, t, a
+ psq_st c, 0(pOut), FALSE, qr0
+
+ // z-
+ psq_l a, 8(p1), TRUE, qr0
+ psq_l b, 8(p2), TRUE, qr0
+
+ ps_sub c, b, a
+ ps_madds0 c, c, t, a
+ psq_st c, 8(pOut), TRUE, qr0
+
+#undef qr0
+ }
+#else
+#pragma unused(p1, p2, t, a, b, c)
+#endif
+
+ return pOut;
+ }
+
+ inline f32 VEC3Dot(register VEC3 const* p1, register VEC3 const* p2) {
+ register f32 _v1, _v2, _v3, _v4, _v5;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ // yz
+ psq_l _v2, 4(p1), FALSE, qr0
+ psq_l _v3, 4(p2), FALSE, qr0
+
+ ps_mul _v2, _v2, _v3
+
+ // x-
+ psq_l _v5, 0(p1), TRUE, qr0
+ psq_l _v4, 0(p2), TRUE, qr0
+
+ ps_madd _v3, _v5, _v4, _v2
+ ps_sum0 _v1, _v3, _v2, _v2
+
+#undef qr0
+ }
+#else
+#pragma unused(p1, p2, _v2, _v3, _v4, _v5)
+ _v1 = 0;
+#endif
+
+ return _v1;
+ }
+
+ inline f32 VEC3LenSq(register VEC3 const* p) {
+ register f32 vxy, vzz, sqmag;
+
+#if defined(__MWERKS__)
+ asm
+ {
+#define qr0 0
+
+ psq_l vxy, 0(p), FALSE, qr0
+ ps_mul vxy, vxy, vxy
+
+ lfs vzz, 8(p)
+ ps_madd sqmag, vzz, vzz, vxy
+ ps_sum0 sqmag, sqmag, vxy, vxy
+
+#undef qr0
+ }
+#else
+#pragma unused(p, vxy, vzz)
+ sqmag = 0;
+#endif
+
+ return sqmag;
+ }
+
+ inline VEC3* VEC3Cross(VEC3* pOut, VEC3 const* p1, VEC3 const* p2) {
+ PSVECCrossProduct(*p1, *p2, *pOut);
+
+ return pOut;
+ }
+
+ inline VEC3* VEC3Normalize(VEC3* pOut, VEC3 const* p) {
+ PSVECNormalize(*p, *pOut);
+
+ return pOut;
+ }
+
+ inline f32 VEC3DistSq(VEC3 const* p1, VEC3 const* p2) {
+ return PSVECSquareDistance(*p1, *p2);
+ }
+
+ // Line info puts these operators outside of the class body
+ inline VEC3 VEC3::operator+(VEC3 const& rhs) const {
+ VEC3 tmp;
+ VEC3Add(&tmp, this, &rhs);
+
+ return tmp;
+ }
+
+ inline VEC3 VEC3::operator-(VEC3 const& rhs) const {
+ VEC3 tmp;
+ VEC3Sub(&tmp, this, &rhs);
+
+ return tmp;
+ }
+
+ inline VEC3 VEC3::operator*(f32 f) const {
+ VEC3 tmp;
+ VEC3Scale(&tmp, this, f);
+
+ return tmp;
+ }
+
+ inline MTX34* MTX34Mult(MTX34* pOut, MTX34 const* p1, MTX34 const* p2) {
+ PSMTXConcat(*p1, *p2, *pOut);
+
+ return pOut;
+ }
+
+ inline MTX34* MTX34Copy(MTX34* pOut, const MTX34* p) {
+ PSMTXCopy(*p, *pOut);
+
+ return pOut;
+ }
+
+ inline MTX34* MTX34Identity(MTX34* pOut) {
+ PSMTXIdentity(*pOut);
+
+ return pOut;
+ }
+
+ inline u32 MTX34Inv(MTX34* pOut, MTX34 const* p) {
+ return PSMTXInverse(*p, *pOut);
+ }
+
+ inline VEC3* VEC3TransformCoord(VEC3* pOut, MTX34 const* pM, VEC3 const* pV) {
+ PSMTXMultVec(*pM, *pV, *pOut);
+
+ return pOut;
+ }
+
+ VEC2* VEC2Maximize(VEC2* pOut, VEC2 const* p1, VEC2 const* p2);
+ VEC2* VEC2Minimize(VEC2* pOut, VEC2 const* p1, VEC2 const* p2);
+ VEC2* VEC2Normalize(VEC2* pOut, VEC2 const* p);
+
+ VEC3* VEC3Maximize(VEC3* pOut, VEC3 const* p1, VEC3 const* p2);
+ VEC3* VEC3Minimize(VEC3* pOut, VEC3 const* p1, VEC3 const* p2);
+
+ VEC4* VEC4Add(VEC4* pOut, VEC4 const* p1, VEC4 const* p2);
+ VEC4* VEC4Sub(VEC4* pOut, VEC4 const* p1, VEC4 const* p2);
+ VEC4* VEC4Scale(VEC4* pOut, VEC4 const* p, f32 scale);
+ VEC4* VEC4Lerp(VEC4* pOut, VEC4 const* p1, VEC4 const* p2, f32 t);
+ f32 VEC4Dot(VEC4 const* p1, VEC4 const* p2);
+ f32 VEC4LenSq(VEC4 const* p);
+ f32 VEC4Len(VEC4 const* p);
+ VEC4* VEC4Normalize(VEC4* pOut, VEC4 const* p);
+ f32 VEC4DistSq(VEC4 const* p1, VEC4 const* p2);
+ VEC4* VEC4Maximize(VEC4* pOut, VEC4 const* p1, VEC4 const* p2);
+ VEC4* VEC4Minimize(VEC4* pOut, VEC4 const* p1, VEC4 const* p2);
+
+ MTX33* MTX33Copy(MTX33* pOut, MTX33 const* p);
+ MTX33* MTX33Zero(MTX33* pOut);
+ MTX33* MTX33Identity(MTX33* pOut);
+
+ MTX33* MTX34ToMTX33(MTX33* pOut, MTX34 const* pM);
+
+ MTX33* MTX33MAdd(MTX33* pOut, f32 t, MTX33 const* p1, MTX33 const* p2);
+
+ u32 MTX34InvTranspose(MTX33* inv, MTX34 const* src);
+ MTX34* MTX34Zero(MTX34* pOut);
+ bool MTX34IsIdentity(MTX34 const* p);
+ MTX34* MTX34Add(MTX34* pOut, MTX34 const* p1, MTX34 const* p2);
+ MTX34* MTX34Sub(MTX34* pOut, MTX34 const* p1, MTX34 const* p2);
+ MTX34* MTX34Mult(MTX34* pOut, MTX34 const* p, f32 f);
+ MTX34* MTX34Scale(MTX34* pOut, MTX34 const* pM, VEC3 const* pS);
+ MTX34* MTX34Trans(MTX34* pOut, MTX34 const* pM, VEC3 const* pT);
+ MTX34* MTX34MAdd(MTX34* pOut, f32 t, MTX34 const* p1, MTX34 const* p2);
+ MTX34* MTX34RotAxisFIdx(MTX34* pOut, VEC3 const* pAxis, f32 fIdx);
+ MTX34* MTX34RotXYZFIdx(MTX34* pOut, f32 fIdxX, f32 fIdxY, f32 fIdxZ);
+ MTX34* MTX34RotXYZTransFIdx(MTX34* pOut, f32 fIdxX, f32 fIdxY, f32 fIdxZ, VEC3 const* pT);
+
+ VEC3* VEC3TransformNormal(VEC3* pOut, MTX34 const* pM, VEC3 const* pV);
+ VEC3* VEC3TransformNormalArray(VEC3* pOut, MTX34 const* pM, VEC3 const* pV, u32 count);
+
+ MTX44* MTX44Zero(MTX44* pOut);
+ MTX44* MTX44Identity(MTX44* pOut);
+ MTX44* MTX44Copy(MTX44* pOut, MTX44 const* p);
+ bool MTX44IsIdentity(MTX44 const* p);
+
+ VEC4* VEC3Transform(VEC4* pOut, MTX44 const* pM, VEC3 const* pV);
+ VEC3* VEC3TransformCoord(VEC3* pOut, MTX44 const* pM, VEC3 const* pV);
+ VEC4* VEC3TransformArray(VEC4* pOut, MTX44 const* pM, VEC3 const* pV, u32 count);
+ VEC3* VEC3TransformCoordArray(VEC3* pOut, MTX44 const* pM, VEC3 const* pV, u32 count);
+
+ VEC4* VEC4Transform(VEC4* pOut, MTX44 const* pM, VEC4 const* pV);
+ VEC4* VEC4TransformArray(VEC4* pOut, MTX44 const* pM, VEC4 const* pV, u32 count);
+ } // namespace math
+} // namespace nw4hbm
+
+#endif // NW4HBM_MATH_TYPES_H
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/AxManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/AxManager.h
new file mode 100644
index 0000000000..947d941342
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/AxManager.h
@@ -0,0 +1,125 @@
+#ifndef NW4HBM_SND_AX_MANAGER_H
+#define NW4HBM_SND_AX_MANAGER_H
+
+#include "FxBase.h"
+
+#include "AxVoice.h"
+#include "MoveValue.h"
+
+#include <revolution/ai.h>
+#include <revolution/ax.h>
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class AxManager {
+ public:
+ typedef struct CallbackListNode {
+ /* 0x00 */ ut::LinkListNode link;
+ /* 0x08 */ AXCallback callback;
+ } CallbackListNode;
+
+ typedef ut::LinkList<CallbackListNode, offsetof(CallbackListNode, link)>
+ CallbackList;
+
+ static AxManager& GetInstance();
+
+ void Init();
+ void Shutdown();
+ void Update();
+
+ bool CheckInit() { return mInitialized; }
+
+ bool IsDiskError() const { return mDiskErrorFlag; }
+ bool IsHomeButtonMenu() const { return mHomeButtonMuteFlag; }
+
+ bool IsResetReady() const { return mResetReadyCounter == 0; }
+
+ f32 GetOutputVolume() const;
+ void* GetZeroBufferAddress();
+
+ void RegisterCallback(CallbackListNode* node, AXCallback callback);
+ void UnregisterCallback(CallbackListNode* node);
+
+ void SetOutputMode(OutputMode mode);
+ OutputMode GetOutputMode();
+
+ void UpdateAllVoicesPriority();
+ void UpdateAllVoices();
+ void UpdateAllVoicesSync(u32 syncFlag);
+
+ f32 GetMasterVolume() const { return mMasterVolume.GetValue(); }
+ void SetMasterVolume(f32 volume, int frame);
+
+ bool AppendEffect(AuxBus bus, FxBase* fx);
+ void ClearEffect(AuxBus bus, int frame);
+ void ShutdownEffect(AuxBus bus);
+
+ FxList& GetEffectList(AuxBus bus) {
+ NW4R_ASSERT_MINMAX(189, bus, AUX_A, AUX_BUS_NUM);
+ return mFxList[bus];
+ }
+
+ void AppendVoiceList(AxVoice* voice);
+ void RemoveVoiceList(AxVoice* voice);
+
+ AxVoice* AllocVoice(int channels, int voices, int priority,
+ AxVoice::AxVoiceCallback callback, void* callbackData);
+ void FreeVoice(AxVoice* voice);
+
+ void ChangeVoicePriority(AxVoice* voice);
+ void LockUpdateVoicePriority();
+ void UnlockUpdateVoicePriority();
+
+ int DropLowestPriorityVoice(int priority);
+
+ AxVoiceList& GetVoiceList() { return mPrioVoiceList; }
+
+ private:
+ static const u8 AUX_CALLBACK_WAIT_FRAME = 6;
+
+ static const int FX_SAMPLE_RATE = AX_SAMPLE_RATE;
+ static const SampleFormat FX_SAMPLE_FORMAT = SAMPLE_FORMAT_PCM_S32;
+ static const int FX_BUFFER_SIZE = AX_FRAME_SIZE;
+
+ static const int ZERO_BUFFER_SIZE = 256;
+
+ AxManager();
+
+ static void AxCallbackFunc();
+ static void AuxCallbackFunc(void* chans, void* context);
+
+ /* 0x0000 */ OutputMode mOutputMode;
+ /* 0x0004 */ void* mZeroBufferAddress;
+ /* 0x0008 */ CallbackList mCallbackList;
+ /* 0x0014 */ AxVoiceList mPrioVoiceList;
+ /* 0x0020 */ AxVoiceList mFreeVoiceList;
+ /* 0x002C */ AxVoice mVoices[AX_MAX_VOICES];
+ /* 0xA22C */ AXCallback mNextAxRegisterCallback;
+ /* 0xA230 */ bool mInitialized;
+ /* 0xA231 */ bool mUpdateVoicePrioFlag;
+ /* 0xA232 */ bool mHomeButtonMuteFlag;
+ /* 0xA233 */ bool mDiskErrorFlag;
+ /* 0xA234 */ MoveValue<f32, int> mHomeButtonMenuVolume;
+ /* 0xA244 */ MoveValue<f32, int> mMasterVolume;
+ /* 0xA254 */ MoveValue<f32, int> mVolumeForReset;
+ /* 0xA264 */ AIDCallback mOldAidCallback;
+ /* 0xA268 */ vs32 mResetReadyCounter;
+ /* 0xA26C */ MoveValue<f32, int> mAuxFadeVolume[AUX_BUS_NUM];
+ /* 0xA29C */ MoveValue<f32, int> mAuxUserVolume[AUX_BUS_NUM];
+ /* 0xA2CC */ FxList mFxList[AUX_BUS_NUM];
+ /* 0xA2F0 */ AXCallback mAuxCallback[AUX_BUS_NUM];
+ /* 0xA2FC */ void* mAuxCallbackContext[AUX_BUS_NUM];
+ /* 0xA308 */ u8 mAuxCallbackWaitCounter[AUX_BUS_NUM];
+
+ static u8 sZeroBuffer[ZERO_BUFFER_SIZE];
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/AxVoice.h b/src/revolution/homebuttonLib/nw4hbm/snd/AxVoice.h
new file mode 100644
index 0000000000..3ac1890723
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/AxVoice.h
@@ -0,0 +1,321 @@
+#ifndef NW4HBM_SND_AX_VOICE_H
+#define NW4HBM_SND_AX_VOICE_H
+
+#include <revolution/ax.h>
+#include <revolution/wpad.h>
+#include "DisposeCallbackManager.h"
+#include "limits.h"
+#include "snd_global.h"
+#include "snd_types.h"
+
+#include "../ut/LinkList.h"
+
+#include "global.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ inline int CalcAxvpbDelta(u16 init, u16 target) {
+ return (target - init) / AX_SAMPLES_PER_FRAME;
+ }
+
+ inline u16 CalcMixVolume(f32 volume) {
+ return ut::Min<u32>(USHRT_MAX, AX_MAX_VOLUME * volume);
+ }
+
+ class AxVoiceParamBlock {
+ public:
+ AxVoiceParamBlock();
+
+ operator AXVPB*() { return mVpb; }
+
+ bool IsAvailable() const { return mVpb != NULL; }
+
+ bool IsRun() const { return IsAvailable() && mVpb->pb.state == AX_VOICE_RUN; }
+
+ u32 GetCurrentAddress() const {
+ if (!IsAvailable()) {
+ return 0;
+ }
+
+ return (mVpb->pb.addr.currentAddressHi << 16) + mVpb->pb.addr.currentAddressLo;
+ }
+
+ u32 GetLoopAddress() const {
+ if (!IsAvailable()) {
+ return 0;
+ }
+
+ return (mVpb->pb.addr.loopAddressHi << 16) + mVpb->pb.addr.loopAddressLo;
+ }
+
+ u32 GetEndAddress() const {
+ if (!IsAvailable()) {
+ return 0;
+ }
+
+ return (mVpb->pb.addr.endAddressHi << 16) + mVpb->pb.addr.endAddressLo;
+ }
+
+ void SetVoiceAddr(const AXPBADDR& rAddr) {
+ if (IsAvailable()) {
+ // AXSetVoiceAddr doesn't actually modify the object
+ AXSetVoiceAddr(mVpb, const_cast<AXPBADDR*>(&rAddr));
+ }
+ }
+
+ void SetVoicePriority(u32 priority) {
+ if (IsAvailable()) {
+ AXSetVoicePriority(mVpb, priority);
+ }
+ }
+
+ void SetVoiceStateRun() {
+ if (IsAvailable()) {
+ AXSetVoiceState(mVpb, AX_VOICE_RUN);
+ }
+ }
+
+ void SetVoiceStateStop() {
+ if (IsRun()) {
+ AXSetVoiceState(mVpb, AX_VOICE_STOP);
+ }
+ }
+
+ void Sync();
+ bool IsRmtIirEnable() const;
+
+ void Set(AXVPB* pVpb);
+ void Clear();
+
+ void SetVoiceType(u16 type);
+ void SetVoiceVe(u16 volume, u16 initVolume);
+ void SetVoiceMix(const AXPBMIX& rMix, bool syncNow);
+ void SetVoiceLoop(u16 loop);
+ void SetVoiceLoopAddr(u32 addr);
+ void SetVoiceEndAddr(u32 addr);
+ void SetVoiceAdpcm(const AXPBADPCM& rAdpcm);
+ void SetVoiceSrcType(u32 type);
+ void SetVoiceSrc(const AXPBSRC& rSrc);
+ void SetVoiceSrcRatio(f32 ratio);
+ void SetVoiceAdpcmLoop(const AXPBADPCMLOOP& rLoop);
+ void SetVoiceLpf(const AXPBLPF& rLpf);
+ void SetVoiceLpfCoefs(u16 a0, u16 b0);
+ void SetVoiceRmtOn(u16 on);
+ void SetVoiceRmtMix(const AXPBRMTMIX& rMix);
+ void SetVoiceRmtIIR(const AXPBRMTIIR& rIir);
+ void SetVoiceRmtIIRCoefs(u16 type, ...);
+ void UpdateDelta();
+
+ private:
+ static const u16 DEFAULT_VOLUME = AX_MAX_VOLUME;
+
+ private:
+ /* 0x00 */ AXVPB* mVpb;
+ /* 0x04 */ u32 mSync;
+ /* 0x08 */ volatile AXPBVE mPrevVeSetting;
+ /* 0x0C */ bool mFirstVeUpdateFlag;
+ /* 0x0E */ u16 mVolume;
+ };
+
+ class WaveData;
+ class AxVoice : public DisposeCallback {
+ friend class AxManager;
+
+ public:
+ typedef enum CallbackStatus {
+ CALLBACK_STATUS_FINISH_WAVE = 0,
+ CALLBACK_STATUS_INVALIDATE_WAVE,
+ CALLBACK_STATUS_DROP_VOICE,
+ CALLBACK_STATUS_DROP_DSP
+ } CallbackStatus;
+
+ typedef enum VoiceSyncFlag {
+ SYNC_AX_SRC_INITIAL = (1 << 0),
+ SYNC_AX_VOICE = (1 << 1),
+ SYNC_AX_SRC = (1 << 3),
+ SYNC_AX_VE = (1 << 4),
+ SYNC_AX_MIX = (1 << 5),
+ SYNC_AX_LPF = (1 << 6),
+ SYNC_AX_REMOTE = (1 << 7),
+ SYNC_AX_BIQUAD = (1 << 8),
+ } VoiceSyncFlag;
+
+ typedef enum Format {
+ FORMAT_PCM16 = 10,
+ FORMAT_PCM8 = 25,
+ FORMAT_ADPCM = 0
+ } Format;
+
+ typedef enum VoiceType { VOICE_TYPE_NORMAL = 0, VOICE_TYPE_STREAM } VoiceType;
+
+ typedef enum SrcType {
+ SRC_NONE = 0,
+ SRC_LINEAR,
+ SRC_4TAP_8K,
+ SRC_4TAP_12K,
+ SRC_4TAP_16K,
+ SRC_4TAP_AUTO
+ } SrcType;
+
+ typedef void (*AxVoiceCallback)(AxVoice* drovoice, CallbackStatus status,
+ void* callbackArg);
+
+ AxVoice();
+
+ /* 0x08 */ virtual ~AxVoice();
+ /* 0x0C */ virtual void InvalidateData(const void* start, const void* end) {}
+ /* 0x10 */ virtual void InvalidateWaveData(const void* start, const void* end);
+
+ void Setup(const WaveData& waveParam);
+ void Update();
+ void Free();
+
+ void Start();
+ void Stop();
+
+ void Pause(bool flag);
+
+ void SetVolume(f32 volume);
+ void SetVeVolume(f32 targetVolume, f32 initVolume);
+ void SetMainOutVolume(f32 volume);
+ void SetRemoteOutVolume(int remoteIndex, f32 volume);
+
+ void SetPitch(f32 pitch);
+ void SetPan(f32 pan);
+ void SetPan2(f32 pan);
+ void SetSurroundPan(f32 surroundPan);
+ void SetSurroundPan2(f32 surroundPan);
+
+ void SetLpfFreq(f32 lpfFreq);
+
+ void SetOutputLine(int lineFlag);
+
+ void SetMainSend(f32 send);
+ void SetFxSend(AuxBus bus, f32 send);
+ void SetRemoteSend(int remoteIndex, f32 send);
+ void SetRemoteFxSend(int remoteIndex, f32 send);
+
+ void SetPriority(int priority);
+ int GetPriority() const { return mPriority; }
+
+ u32 GetCurrentPlayingDspAddress() const;
+ u32 GetLoopStartDspAddress() const;
+ u32 GetLoopEndDspAddress() const;
+
+ u32 GetCurrentPlayingSample() const;
+
+ void SetAdpcmLoop(int channelIndex, Format format, const AdpcmLoopParam* param);
+ void SetBaseAddress(int channelIndex, const void* baseAddress);
+
+ bool IsPlayFinished() const;
+ bool IsRun();
+
+ void SetAxAddr(int channelIndex, bool loopFlag, Format format, const void* waveAddr,
+ u32 loopStart, u32 loopEnd);
+
+ void SetLoopStart(int channelIndex, const void* baseAddress, u32 samples);
+ void SetLoopEnd(int channelIndex, const void* baseAddress, u32 samples);
+ void SetLoopFlag(bool loopFlag);
+
+ void StopAtPoint(int channelIndex, const void* baseAddress, u32 samples);
+ void SetVoiceType(VoiceType type);
+
+ void UpdateAxSrc(bool initialUpdate);
+ void SetAxSrcType(SrcType type);
+
+ void SetAxAdpcm(int channelIndex, Format format, const AdpcmParam* param);
+ void SetAxAdpcmLoop(int channelIndex, Format format,
+ const AdpcmLoopParam* param) NO_INLINE;
+
+ bool UpdateAxVe();
+ void UpdateAxMix();
+ void UpdateAxLpf();
+
+ void InitParam(int channelCount, int voiceOutCount, AxVoiceCallback callback,
+ void* callbackData);
+ bool Acquire(int channelCount, int voiceOutCount, int priority,
+ AxVoiceCallback callback, void* callbackData);
+
+ int GetAxVoiceCount() const { return mChannelCount * mVoiceOutCount; }
+
+ static u32 GetDspAddressBySample(const void* baseAddress, u32 samples,
+ Format format);
+ static u32 GetSampleByDspAddress(const void* baseAddress, u32 addr, Format format);
+ static u32 GetSampleByByte(u32 byte, Format format);
+
+ template <typename T>
+ void SetAxParam(void (*func)(AXVPB*, T), T param) {
+ for (int i = 0; i < mChannelCount; i++) {
+ for (int j = 0; j < mVoiceOutCount; j++) {
+ if (mVpb[i][j] != NULL) {
+ func(mVpb[i][j], param);
+ }
+ }
+ }
+ }
+
+ Format GetFormat() { return mFormat; }
+
+ static const int VOICES_MAX = 4;
+ static const int PRIORITY_MAX = 255;
+
+ protected:
+ static void VoiceCallback(void* callbackData);
+
+ void TransformDpl2Pan(f32* outPan, f32* outSurroundPan, f32 inPan,
+ f32 inSurroundPan);
+
+ void CalcAXPBMIX(int channelIndex, int voiceIndex, AXPBMIX* mix);
+ void CalcAXPBRMTMIX(int channelIndex, int voiceIndex, AXPBRMTMIX* mix);
+
+ /* 0x000 (base) */
+ /* 0x00C */ AXVPB* mVpb[CHANNEL_MAX][VOICES_MAX];
+ /* 0x02C */ VoiceChannelParam mVoiceChannelParam[CHANNEL_MAX];
+ /* 0x094 */ SoundParam mVoiceOutParam[VOICES_MAX];
+ /* 0x104 */ s32 mChannelCount;
+ /* 0x108 */ s32 mVoiceOutCount;
+ /* 0x10C */ AxVoiceCallback mCallback;
+ /* 0x110 */ void* mCallbackData;
+ /* 0x114 */ int mSampleRate;
+ /* 0x118 */ Format mFormat;
+ /* 0x11C */ bool mActiveFlag;
+ /* 0x11D */ bool mStartFlag;
+ /* 0x11E */ bool mStartedFlag;
+ /* 0x11F */ bool mPauseFlag;
+ /* 0x120 */ bool mPausingFlag;
+ /* 0x121 */ bool mFirstVeUpdateFlag;
+ /* 0x122 */ bool mHomeButtonMuteFlag;
+ /* 0x123 */ u8 mSyncFlag;
+ /* 0x124 */ int mPriority;
+ /* 0x128 */ f32 mPan;
+ /* 0x12C */ f32 mSurroundPan;
+ /* 0x130 */ f32 mPan2;
+ /* 0x134 */ f32 mSurroundPan2;
+ /* 0x138 */ f32 mLpfFreq;
+ /* 0x13C */ int mOutputLineFlag;
+ /* 0x140 */ f32 mMainOutVolume;
+ /* 0x144 */ f32 mMainSend;
+ /* 0x148 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x154 */ f32 mRemoteOutVolume[VOICES_MAX];
+ /* 0x164 */ f32 mRemoteSend[VOICES_MAX];
+ /* 0x174 */ f32 mRemoteFxSend[VOICES_MAX];
+ /* 0x184 */ f32 mPitch;
+ /* 0x188 */ f32 mVolume;
+ /* 0x18C */ f32 mVolumePrev[VOICES_MAX];
+ /* 0x19C */ f32 mVeInitVolume;
+ /* 0x1A0 */ f32 mVeTargetVolume;
+ /* 0x1A4 */ u16 mGainPrev;
+
+ public:
+ /* 0x1A8 */ ut::LinkListNode mLinkNode;
+ };
+
+ typedef ut::LinkList<AxVoice, offsetof(AxVoice, mLinkNode)> AxVoiceList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/Bank.h b/src/revolution/homebuttonLib/nw4hbm/snd/Bank.h
new file mode 100644
index 0000000000..029ede17a5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/Bank.h
@@ -0,0 +1,36 @@
+#ifndef NW4HBM_SND_BANK_H
+#define NW4HBM_SND_BANK_H
+
+#include <revolution/types.h>
+
+#include "BankFile.h"
+#include "NoteOnCallback.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class Channel;
+
+ class Bank {
+ public:
+ explicit Bank(const void* bankData);
+ ~Bank();
+
+ Channel* NoteOn(const NoteOnInfo& noteOnInfo) const;
+
+ void SetWaveDataAddress(const void* waveData) {
+ NW4HBM_ASSERT_CHECK_NULL(47, waveData);
+ mWaveDataAddress = waveData;
+ }
+
+ private:
+ /* 0x00 */ BankFileReader mBankReader;
+ /* 0x0C */ const void* mWaveDataAddress;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/BankFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/BankFile.h
new file mode 100644
index 0000000000..756f3eadd5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/BankFile.h
@@ -0,0 +1,122 @@
+#ifndef NW4HBM_SND_BANK_FILE_H
+#define NW4HBM_SND_BANK_FILE_H
+
+#include <revolution/types.h>
+
+#include "Util.h"
+#include "WaveFile.h"
+
+#include "../ut/binaryFileFormat.h"
+
+#include <revolution/hbm.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ inline u8 ReadByte(const void* address) {
+ return *static_cast<const u8*>(address);
+ }
+
+ namespace BankFile {
+
+ static const u32 SIGNATURE_FILE = 'RBNK';
+ static const u32 SIGNATURE_DATA_BLOCK = 'DATA';
+ static const u32 SIGNATURE_WAVE_BLOCK = 'WAVE';
+
+ typedef struct InstParam {
+ /* 0x00 */ s32 waveIndex;
+ /* 0x04 */ u8 attack;
+ /* 0x05 */ u8 decay;
+ /* 0x06 */ u8 sustain;
+ /* 0x07 */ u8 release;
+ /* 0x08 */ u16 hold;
+ /* 0x0A */ u16 padding;
+ /* 0x0C */ u8 originalKey;
+ /* 0x0D */ u8 volume;
+ /* 0x0E */ u8 pan;
+ /* 0x0F */ u8 padding2;
+ /* 0x10 */ f32 tune;
+ /* 0x14 */ Util::DataRef<void> lfoTableRef;
+ /* 0x1C */ Util::DataRef<void> graphEnvTablevRef;
+ /* 0x24 */ Util::DataRef<void> randomizerTableRef;
+ /* 0x2C */ u32 reserved;
+ } InstParam;
+
+ typedef struct RangeTable {
+ /* 0x00 */ u8 tableSize;
+ /* 0x01 */ u8 key[];
+ } RangeTable;
+
+ typedef struct IndexTable IndexTable;
+
+ typedef Util::DataRef<void, InstParam, RangeTable, IndexTable> DataRegion;
+ typedef Util::DataRef<WaveFile::WaveInfo> WaveRegion;
+
+ struct IndexTable {
+ /* 0x00 */ u8 min;
+ /* 0x01 */ u8 max;
+ /* 0x02 */ u16 reserved;
+ /* 0x04 */ DataRegion ref[];
+ };
+
+ typedef struct Header {
+ /* 0x00 */ ut::BinaryFileHeader fileHeader;
+ /* 0x10 */ u32 dataBlockOffset;
+ /* 0x14 */ u32 dataBlockSize;
+ /* 0x18 */ u32 waveBlockOffset;
+ /* 0x1C */ u32 waveBlockSize;
+ } Header;
+
+ typedef struct DataBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ Util::Table<DataRegion> instTable;
+ } DataBlock;
+
+ typedef struct WaveBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ Util::Table<WaveRegion> waveInfoTable;
+ } WaveBlock;
+
+ } // namespace BankFile
+
+ typedef struct InstInfo {
+ /* 0x00 */ s32 waveIndex;
+ /* 0x04 */ u8 attack;
+ /* 0x05 */ u8 decay;
+ /* 0x06 */ u8 sustain;
+ /* 0x07 */ u8 release;
+ /* 0x08 */ u8 originalKey;
+ /* 0x09 */ u8 pan;
+ /* 0x0A */ u8 volume;
+ /* 0x0C */ f32 tune;
+ } InstInfo;
+
+ class BankFileReader {
+ public:
+ static const int FILE_VERSION = NW4HBM_VERSION(1, 1);
+
+ public:
+ explicit BankFileReader(const void* bankData);
+
+ bool IsValidFileHeader(const void* bankData);
+
+ bool ReadInstInfo(InstInfo* instInfo, int prgNo, int key, int velocity) const;
+ bool ReadWaveParam(WaveData* waveParam, int waveIndex,
+ const void* waveDataAddress) const;
+
+ private:
+ const BankFile::DataRegion* GetReferenceToSubRegion(const BankFile::DataRegion* ref,
+ int splitKey) const;
+
+ private:
+ /* 0x00 */ const BankFile::Header* mHeader;
+ /* 0x04 */ const BankFile::DataBlock* mDataBlock;
+ /* 0x08 */ const BankFile::WaveBlock* mWaveBlock;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/BasicPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/BasicPlayer.h
new file mode 100644
index 0000000000..e06dfc16b9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/BasicPlayer.h
@@ -0,0 +1,73 @@
+#ifndef NW4HBM_SND_BASIC_PLAYER_H
+#define NW4HBM_SND_BASIC_PLAYER_H
+
+#include <revolution/types.h>
+#include "snd_types.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ namespace {
+
+ class BasicPlayer {
+ public:
+ BasicPlayer() : mId(-1) {}
+
+ /* 0x08 */ virtual ~BasicPlayer()
+#ifdef MAKE_DTOR_ZERO
+ = 0
+#endif
+ {};
+
+ /* 0x0C */ virtual bool Start() = 0;
+ /* 0x10 */ virtual void Stop() = 0;
+ /* 0x14 */ virtual void Pause(bool flag) = 0;
+ /* 0x18 */ virtual bool IsActive() const = 0;
+ /* 0x1C */ virtual bool IsPrepared() const = 0;
+ /* 0x20 */ virtual bool IsStarted() const = 0;
+ /* 0x24 */ virtual bool IsPause() const = 0;
+ /* 0x28 */ virtual void SetVolume(f32 volume) = 0;
+ /* 0x2C */ virtual void SetPitch(f32 pitch) = 0;
+ /* 0x30 */ virtual void SetPan(f32 pan) = 0;
+ /* 0x34 */ virtual void SetSurroundPan(f32 surroundPan) = 0;
+ /* 0x38 */ virtual void SetPan2(f32 pan2) = 0;
+ /* 0x3C */ virtual void SetSurroundPan2(f32 surroundPan2) = 0;
+ /* 0x40 */ virtual void SetLpfFreq(f32 lpfFreq) = 0;
+ /* 0x44 */ virtual f32 GetVolume() const = 0;
+ /* 0x48 */ virtual f32 GetPitch() const = 0;
+ /* 0x4C */ virtual f32 GetPan() const = 0;
+ /* 0x50 */ virtual f32 GetSurroundPan() const = 0;
+ /* 0x54 */ virtual f32 GetPan2() const = 0;
+ /* 0x58 */ virtual f32 GetSurroundPan2() const = 0;
+ /* 0x5C */ virtual f32 GetLpfFreq() const = 0;
+ /* 0x60 */ virtual void SetOutputLine(int lineFlag) = 0;
+ /* 0x64 */ virtual void SetMainOutVolume(f32 volume) = 0;
+ /* 0x68 */ virtual void SetMainSend(f32 send) = 0;
+ /* 0x6C */ virtual void SetFxSend(AuxBus bus, f32 send) = 0;
+ /* 0x70 */ virtual void SetRemoteOutVolume(int remoteIndex, f32 volume) = 0;
+ /* 0x74 */ virtual void SetRemoteSend(int remoteIndex, f32 send) = 0;
+ /* 0x78 */ virtual void SetRemoteFxSend(int remoteIndex, f32 send) = 0;
+ /* 0x7C */ virtual int GetOutputLine() const = 0;
+ /* 0x80 */ virtual f32 GetMainOutVolume() const = 0;
+ /* 0x84 */ virtual f32 GetMainSend() const = 0;
+ /* 0x88 */ virtual f32 GetFxSend(AuxBus bus) const = 0;
+ /* 0x8C */ virtual f32 GetRemoteOutVolume(int remoteIndex) const = 0;
+ /* 0x90 */ virtual f32 GetRemoteSend(int remoteIndex) const = 0;
+ /* 0x94 */ virtual f32 GetRemoteFxSend(int remoteIndex) const = 0;
+
+ u32 GetId() const { return mId; }
+ void SetId(u32 id) { mId = id; }
+
+ private:
+ /* 0x04 */ u32 mId;
+ };
+
+ } // namespace
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/BasicSound.h b/src/revolution/homebuttonLib/nw4hbm/snd/BasicSound.h
new file mode 100644
index 0000000000..6c7cd4273c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/BasicSound.h
@@ -0,0 +1,214 @@
+#ifndef NW4HBM_SND_BASIC_SOUND_H
+#define NW4HBM_SND_BASIC_SOUND_H
+
+#include <revolution/types.h>
+#include <revolution/wpad.h>
+
+#include "MoveValue.h"
+#include "snd_global.h"
+#include "snd_types.h"
+
+#include "../ut/LinkList.h"
+#include "../ut/RuntimeTypeInfo.h"
+#include "../ut/inlines.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ class SoundHandle;
+ class SoundPlayer;
+
+ namespace detail {
+ namespace {
+ class BasicPlayer;
+ }
+ class ExternalSoundPlayer;
+ class PlayerHeap;
+ } // namespace detail
+
+ namespace detail {
+ class BasicSound {
+ public:
+ typedef struct AmbientParamUpdateCallback {
+ typedef enum ParamUpdateFlags {
+ /* 1 */ PARAM_UPDATE_VOLUME = (1 << 0),
+ /* 2 */ PARAM_UPDATE_PAN = (1 << 1),
+ /* 4 */ PARAM_UPDATE_SURROUND_PAN = (1 << 2),
+ /* 8 */ PARAM_UPDATE_PRIORITY = (1 << 3),
+ } ParamUpdateFlags;
+
+ /* 0x0C */ virtual void detail_Update(SoundParam* param, u32 id,
+ BasicSound* sound, const void* arg,
+ u32 flags) = 0;
+ } AmbientParamUpdateCallback;
+
+ typedef struct AmbientArgUpdateCallback {
+ /* 0x0C */ virtual void detail_Update(void* arg, const BasicSound* sound) = 0;
+ } AmbientArgUpdateCallback;
+
+ typedef struct AmbientArgAllocaterCallback {
+ /* 0x0C */ virtual void* detail_AllocAmbientArg(u32 size) = 0;
+ /* 0x10 */ virtual void detail_FreeAmbientArg(void* arg,
+ const BasicSound* sound) = 0;
+ } AmbientArgAllocaterCallback;
+
+ typedef struct AmbientArgInfo {
+ /* 0x00 */ AmbientParamUpdateCallback* paramUpdateCallback;
+ /* 0x04 */ AmbientArgUpdateCallback* argUpdateCallback;
+ /* 0x08 */ AmbientArgAllocaterCallback* argAllocaterCallback;
+ /* 0x0C */ void* arg;
+ /* 0x10 */ u32 argSize;
+ } AmbientArgInfo;
+
+ static const u32 INVALID_ID = 0xFFFFFFFF;
+ static const int PRIORITY_MAX = 127;
+
+ public:
+ BasicSound();
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~BasicSound() {}
+ /* 0x10 */ virtual void Update();
+ /* 0x14 */ virtual void StartPrepared();
+ /* 0x18 */ virtual void Stop(int frames = 0);
+ /* 0x1C */ virtual void Pause(bool flag, int frames);
+ /* 0x20 */ virtual void SetAutoStopCounter(int count);
+ /* 0x24 */ virtual void FadeIn(int frames);
+ /* 0x28 */ virtual void Shutdown();
+ /* 0x2C */ virtual bool IsPrepared() const;
+ /* 0x30 */ virtual bool IsPause() const;
+ /* 0x34 */ virtual void SetInitialVolume(f32 volume);
+ /* 0x38 */ virtual void SetVolume(f32 volume, int frames);
+ /* 0x3C */ virtual void SetPitch(f32 pitch);
+ /* 0x40 */ virtual void SetPan(f32 pan);
+ /* 0x44 */ virtual void SetSurroundPan(f32 pan);
+ /* 0x48 */ virtual void SetLpfFreq(f32 freq);
+ /* 0x4C */ virtual void SetPlayerPriority(int priority);
+ /* 0x50 */ virtual bool IsAttachedTempSpecialHandle() = 0;
+ /* 0x54 */ virtual void DetachTempSpecialHandle() = 0;
+ /* 0x58 */ virtual void InitParam();
+ /* 0x5C */ virtual BasicPlayer& GetBasicPlayer() = 0;
+ /* 0x60 */ virtual const BasicPlayer& GetBasicPlayer() const = 0;
+
+ PlayerHeap* GetPlayerHeap() { return mHeap; }
+ void SetPlayerHeap(PlayerHeap* heap) { mHeap = heap; }
+
+ bool IsAttachedGeneralHandle();
+ void DetachGeneralHandle();
+
+ bool IsAttachedTempGeneralHandle();
+ void DetachTempGeneralHandle();
+
+ SoundPlayer* GetSoundPlayer() { return mSoundPlayer; }
+ void SetSoundPlayer(SoundPlayer* player) { mSoundPlayer = player; }
+
+ ExternalSoundPlayer* GetExternalSoundPlayer() { return mExtSoundPlayer; }
+ void SetExternalSoundPlayer(ExternalSoundPlayer* extPlayer) {
+ mExtSoundPlayer = extPlayer;
+ }
+
+ AmbientParamUpdateCallback* GetAmbientParamUpdateCallback() {
+ return mAmbientParamUpdateCallback;
+ }
+ AmbientArgUpdateCallback* GetAmbientArgUpdateCallback() {
+ return mAmbientArgUpdateCallback;
+ }
+ void ClearAmbientArgUpdateCallback() { mAmbientArgUpdateCallback = NULL; }
+
+ AmbientArgAllocaterCallback* GetAmbientArgAllocaterCallback() {
+ return mAmbientArgAllocaterCallback;
+ }
+
+ void* GetAmbientArg() { return mAmbientArg; }
+ SoundParam& GetAmbientParam() { return mAmbientParam; }
+
+ void SetAmbientParamCallback(AmbientParamUpdateCallback* paramUpdateCallback,
+ AmbientArgUpdateCallback* argUpdateCallback,
+ AmbientArgAllocaterCallback* argAllocaterCallback,
+ void* callbackArg);
+
+ void SetPriority(int priority) { mPriority = priority; }
+
+ u32 GetId() const { return mId; }
+ void SetId(u32 id);
+
+ f32 GetMoveVolume() { return mExtMoveVolume.GetValue(); }
+
+ f32 GetInitialVolume() const;
+ f32 GetPan() const;
+ f32 GetSurroundPan() const;
+ f32 GetPitch() const;
+ f32 GetVolume() const;
+
+ void SetOutputLine(int flag);
+ bool IsEnabledOutputLine() const;
+ int GetOutputLine() const;
+
+ f32 GetMainOutVolume() const;
+ void SetMainOutVolume(f32 volume);
+
+ f32 GetRemoteOutVolume(int remote) const;
+ void SetRemoteOutVolume(int remote, f32 volume);
+
+ void SetFxSend(AuxBus bus, f32 send);
+
+ int CalcCurrentPlayerPriority() const {
+ return ut::Clamp(mPriority + mAmbientParam.priority, (s32)0, (s32)PRIORITY_MAX);
+ }
+
+ private:
+ /* 0x04 */ PlayerHeap* mHeap;
+ /* 0x08 */ SoundHandle* mGeneralHandle;
+ /* 0x0C */ SoundHandle* mTempGeneralHandle;
+ /* 0x10 */ SoundPlayer* mSoundPlayer;
+ /* 0x14 */ ExternalSoundPlayer* mExtSoundPlayer;
+ /* 0x18 */ AmbientParamUpdateCallback* mAmbientParamUpdateCallback;
+ /* 0x1C */ AmbientArgUpdateCallback* mAmbientArgUpdateCallback;
+ /* 0x20 */ AmbientArgAllocaterCallback* mAmbientArgAllocaterCallback;
+ /* 0x24 */ void* mAmbientArg;
+ /* 0x28 */ SoundParam mAmbientParam;
+ /* 0x44 */ MoveValue<f32, int> mFadeVolume;
+ /* 0x54 */ MoveValue<f32, int> mPauseFadeVolume;
+ /* 0x64 */ bool mStartFlag;
+ /* 0x65 */ bool mStartedFlag;
+ /* 0x66 */ bool mAutoStopFlag;
+ /* 0x67 */ bool mPauseFlag;
+ /* 0x68 */ bool mPauseFadeFlag;
+ /* 0x69 */ bool mFadeOutFlag;
+ /* 0x6C */ int mAutoStopCounter;
+ /* 0x70 */ u32 mUpdateCounter;
+ /* 0x74 */ u8 mPriority;
+ /* 0x78 */ u32 mId;
+ /* 0x7C */ MoveValue<f32, int> mExtMoveVolume;
+ /* 0x8C */ f32 mInitVolume;
+ /* 0x90 */ f32 mExtPan;
+ /* 0x94 */ f32 mExtSurroundPan;
+ /* 0x98 */ f32 mExtPitch;
+ /* 0x9C */ bool mOutputLineFlagEnable;
+ /* 0xA0 */ int mOutputLineFlag;
+ /* 0xA4 */ f32 mMainOutVolume;
+ /* 0xA8 */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+
+ public:
+ /* 0xB8 */ ut::LinkListNode mPriorityLink;
+ /* 0xC0 */ ut::LinkListNode mSoundPlayerPlayLink;
+ /* 0xC8 */ ut::LinkListNode mSoundPlayerPriorityLink;
+ /* 0xD0 */ ut::LinkListNode mExtSoundPlayerPlayLink;
+
+ friend class SoundHandle;
+ };
+
+ typedef ut::LinkList<BasicSound, offsetof(BasicSound, mPriorityLink)>
+ BasicSoundPrioList;
+ typedef ut::LinkList<BasicSound, offsetof(BasicSound, mSoundPlayerPlayLink)>
+ BasicSoundPlayerPlayList;
+ typedef ut::LinkList<BasicSound, offsetof(BasicSound, mSoundPlayerPriorityLink)>
+ BasicSoundPlayerPrioList;
+ typedef ut::LinkList<BasicSound, offsetof(BasicSound, mExtSoundPlayerPlayLink)>
+ BasicSoundExtPlayList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/Channel.h b/src/revolution/homebuttonLib/nw4hbm/snd/Channel.h
new file mode 100644
index 0000000000..4d842a64f7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/Channel.h
@@ -0,0 +1,185 @@
+#ifndef NW4HBM_SND_CHANNEL_H
+#define NW4HBM_SND_CHANNEL_H
+
+#include <limits.h> // USHRT_MAX
+
+#include <revolution/types.h>
+
+#include "EnvGenerator.h"
+#include "InstancePool.h"
+#include "Lfo.h"
+#include "MoveValue.h"
+#include "WaveFile.h"
+#include "snd_global.h"
+#include "snd_types.h"
+
+#include "../ut/LinkList.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class Channel : public LinkedInstance {
+ public:
+ typedef enum ChannelCallbackStatus {
+ CALLBACK_STATUS_STOPPED = 0,
+ CALLBACK_STATUS_DROP,
+ CALLBACK_STATUS_FINISH,
+ CALLBACK_STATUS_CANCEL
+ } ChannelCallbackStatus;
+
+ typedef void (*ChannelCallback)(Channel* dropChannel, ChannelCallbackStatus status,
+ u32 callbackArg);
+
+ typedef enum LfoTarget {
+ LFO_TARGET_PITCH = 0,
+ LFO_TARGET_VOLUME,
+ LFO_TARGET_PAN
+ } LfoTarget;
+
+ public:
+ Channel();
+ ~Channel();
+
+ void InitParam(ChannelCallback callback, u32 callbackArg);
+ void Update(bool doPeriodicProc);
+ void Start(const WaveData& waveData, s32 length);
+ void Release();
+ void Stop();
+
+ void SetAttack(int attack) { mEnvelope.SetAttack(attack); }
+ void SetDecay(int decay) { mEnvelope.SetDecay(decay); }
+ void SetSustain(int sustain) { mEnvelope.SetSustain(sustain); }
+
+ void SetRelease(int release) { mEnvelope.SetRelease(release); }
+ bool IsRelease() const {
+ return mEnvelope.GetStatus() == EnvGenerator::STATUS_RELEASE;
+ }
+
+ void SetLfoParam(const LfoParam& rParam) { mLfo.SetParam(rParam); }
+ void SetLfoTarget(LfoTarget target) { mLfoTarget = target; }
+
+ void Pause(bool pause) {
+ mPauseFlag = pause;
+ mVoice->Pause(pause);
+ }
+
+ bool IsPause() const { return mPauseFlag != false; }
+ bool IsActive() const { return mActiveFlag; }
+ bool IsAutoUpdateSweep() const { return mAutoSweep; }
+
+ void SetUserVolume(f32 volume) { mUserVolume = volume; }
+ void SetUserPitch(f32 pitch) { mUserPitch = pitch; }
+ void SetUserPitchRatio(f32 ratio) { mUserPitchRatio = ratio; }
+ void SetUserPan(f32 pan) { mUserPan = pan; }
+ void SetUserSurroundPan(f32 pan) { mUserSurroundPan = pan; }
+ void SetUserPan2(f32 pan2) { mUserPan2 = pan2; }
+ void SetUserSurroundPan2(f32 pan2) { mUserSurroundPan2 = pan2; }
+ void SetUserLpfFreq(f32 freq) { mUserLpfFreq = freq; }
+
+ void SetOutputLine(int flag) { mOutputLineFlag = flag; }
+
+ void SetMainOutVolume(f32 volume) { mMainOutVolume = volume; }
+ void SetMainSend(f32 send) { mMainSend = send; }
+ void SetFxSend(AuxBus bus, f32 send) { mFxSend[bus] = send; }
+
+ void SetSilence(bool silence, int fadeTimes) {
+ NW4R_ASSERT_MINMAXLT(124, fadeTimes, 0, 0xFFFF);
+ mSilenceVolume.SetTarget(silence ? 0 : SILENCE_VOLUME_MAX, fadeTimes);
+ }
+
+ void SetRemoteOutVolume(int remoteIndex, f32 volume) {
+ NW4R_ASSERT_MINMAX(165, remoteIndex, 0, 4);
+ mRemoteOutVolume[remoteIndex] = volume;
+ }
+
+ void SetRemoteSend(int remoteIndex, f32 send) {
+ NW4R_ASSERT_MINMAX(170, remoteIndex, 0, 4);
+ mRemoteSend[remoteIndex] = send;
+ }
+
+ void SetRemoteFxSend(int remoteIndex, f32 send) {
+ NW4R_ASSERT_MINMAX(175, remoteIndex, 0, 4);
+ mRemoteFxSend[remoteIndex] = send;
+ }
+
+ void UpdateSweep(int count);
+ void SetSweepParam(f32 pitch, int time, bool autoUpdate);
+ f32 GetSweepValue() const;
+
+ void SetInitVolume(f32 volume) { mInitVolume = volume; }
+ void SetInitPan(f32 pan) { mInitPan = pan; }
+ void SetInitSurroundPan(f32 pan) { mInitSurroundPan = pan; }
+ void SetTune(f32 tune) { mTune = tune; }
+
+ void SetKey(int key) { mKey = key; }
+ void SetOriginalKey(int key) { mOriginalKey = key; }
+
+ s32 GetLength() const { return mLength; }
+ void SetLength(s32 length) { mLength = length; }
+
+ Channel* GetNextTrackChannel() const { return mNextLink; }
+ void SetNextTrackChannel(Channel* channel) { mNextLink = channel; }
+
+ static void UpdateAllChannel();
+ static Channel* AllocChannel(int channels, int voices, int priority,
+ ChannelCallback pCallback, u32 callbackArg);
+ static void FreeChannel(Channel* channel);
+
+ static void VoiceCallback(AxVoice* voice, AxVoice::CallbackStatus status,
+ void* arg);
+
+ private:
+ static const u8 SILENCE_VOLUME_MAX = 255;
+
+ static const int KEY_INIT = 60;
+ static const int ORIGINAL_KEY_INIT = 60;
+
+ static const int PRIORITY_RELEASE = 1;
+
+ /* 0x08 */ EnvGenerator mEnvelope;
+ /* 0x20 */ Lfo mLfo;
+ /* 0x38 */ u8 mLfoTarget;
+ /* 0x39 */ bool mPauseFlag;
+ /* 0x3A */ bool mActiveFlag;
+ /* 0x3B */ bool mAllocFlag;
+ /* 0x3C */ bool mAutoSweep;
+ /* 0x40 */ f32 mUserVolume;
+ /* 0x44 */ f32 mUserPitchRatio;
+ /* 0x48 */ f32 mUserPan;
+ /* 0x4C */ f32 mUserSurroundPan;
+ /* 0x50 */ f32 mUserPan2;
+ /* 0x54 */ f32 mUserSurroundPan2;
+ /* 0x58 */ f32 mUserLpfFreq;
+ /* 0x5C */ int mOutputLineFlag;
+ /* 0x60 */ f32 mMainOutVolume;
+ /* 0x64 */ f32 mMainSend;
+ /* 0x68 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x74 */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ /* 0x84 */ f32 mRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x94 */ f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0xA4 */ f32 mUserPitch;
+ /* 0xA8 */ f32 mSweepPitch;
+ /* 0xAC */ s32 mSweepCounter;
+ /* 0xB0 */ s32 mSweepLength;
+ /* 0xB4 */ f32 mInitVolume;
+ /* 0xB8 */ f32 mInitPan;
+ /* 0xBC */ f32 mInitSurroundPan;
+ /* 0xC0 */ f32 mTune;
+ /* 0xC4 */ MoveValue<u8, u16> mSilenceVolume;
+ /* 0xCC */ int mKey;
+ /* 0xD0 */ int mOriginalKey;
+ /* 0xD4 */ s32 mLength;
+ /* 0xD8 */ ChannelCallback mCallback;
+ /* 0xDC */ u32 mCallbackData;
+ /* 0xE0 */ AxVoice* mVoice;
+ /* 0xE4 */ Channel* mNextLink;
+
+ static Channel mChannel[AX_MAX_VOICES + 1];
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/ChannelManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/ChannelManager.h
new file mode 100644
index 0000000000..d842d29050
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/ChannelManager.h
@@ -0,0 +1,23 @@
+#ifndef NW4HBM_SND_CHANNEL_MANAGER_H
+#define NW4HBM_SND_CHANNEL_MANAGER_H
+
+#include "Channel.h"
+#include "InstanceManager.h"
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class ChannelManager : public InstanceManager<Channel, 0> {
+ public:
+ static ChannelManager& GetInstance() {
+ static ChannelManager instance;
+ return instance;
+ }
+ ChannelManager() {}
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallback.h b/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallback.h
new file mode 100644
index 0000000000..8129ab0720
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallback.h
@@ -0,0 +1,40 @@
+#ifndef NW4HBM_SND_DISPOSE_CALLBACK_H
+#define NW4HBM_SND_DISPOSE_CALLBACK_H
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ // we want the vtable to get a unique name but function parameters need not to be unique
+ // so we use a dummy class for that and cast to `DisposeCallback` when needed
+ class DisposeCallbackBase {};
+
+ namespace {
+
+ class DisposeCallback : public DisposeCallbackBase {
+ public:
+ /* 0x00 */ ut::LinkListNode mDisposeLink;
+
+ /* 0x08 */ virtual ~DisposeCallback()
+#ifdef MAKE_DTOR_ZERO
+ = 0
+#endif
+ {};
+
+ /* 0x0C */ virtual void InvalidateData(const void* start, const void* end) = 0;
+ /* 0x10 */ virtual void InvalidateWaveData(const void* start,
+ const void* end) = 0;
+ };
+
+ } // namespace
+
+ typedef ut::LinkList<DisposeCallback, offsetof(DisposeCallback, mDisposeLink)>
+ DisposeCallbackList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallbackManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallbackManager.h
new file mode 100644
index 0000000000..38e117366f
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/DisposeCallbackManager.h
@@ -0,0 +1,28 @@
+#ifndef NW4HBM_SND_DISPOSE_CALLBACK_MANAGER_H
+#define NW4HBM_SND_DISPOSE_CALLBACK_MANAGER_H
+
+#include "DisposeCallback.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class DisposeCallbackManager {
+ public:
+ static DisposeCallbackManager& GetInstance();
+
+ void RegisterDisposeCallback(DisposeCallbackBase* callback);
+ void UnregisterDisposeCallback(DisposeCallbackBase* callback);
+
+ static void Dispose(void* mem, u32 size, void* arg);
+ static void DisposeWave(void* mem, u32 size, void* arg);
+
+ private:
+ DisposeCallbackManager();
+
+ /* 0x00 */ DisposeCallbackList mCallbackList;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/DvdSoundArchive.h b/src/revolution/homebuttonLib/nw4hbm/snd/DvdSoundArchive.h
new file mode 100644
index 0000000000..1f446754fa
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/DvdSoundArchive.h
@@ -0,0 +1,56 @@
+#ifndef NW4HBM_SND_DVD_SOUND_ARCHIVE_H
+#define NW4HBM_SND_DVD_SOUND_ARCHIVE_H
+
+#include "snd_types.h"
+
+#include "SoundArchive.h"
+#include "SoundArchiveFile.h"
+
+#include "../ut/FileStream.h"
+
+#include <revolution/dvd.h>
+
+namespace nw4hbm {
+ namespace snd {
+ class DvdSoundArchive : public SoundArchive {
+ private:
+ class DvdFileStream;
+
+ public:
+ DvdSoundArchive();
+
+ /* 0x08 */ virtual ~DvdSoundArchive();
+ /* 0x0C */ virtual const void* detail_GetFileAddress(u32 id) const { return NULL; }
+ /* 0x10 */ virtual const void* detail_GetWaveDataFileAddress(u32 id) const {
+ return NULL;
+ }
+ /* 0x14 */ virtual int detail_GetRequiredStreamBufferSize() const;
+ /* 0x18 */ virtual ut::FileStream* OpenStream(void* buffer, int size, u32 offset,
+ u32 length) const;
+ /* 0x1C */ virtual ut::FileStream* OpenExtStream(void* buffer, int size,
+ const char* extPath, u32 offset,
+ u32 length) const;
+
+ bool Open(s32 entrynum);
+ bool Open(const char* path);
+
+ void Close();
+
+ bool LoadHeader(void* buffer, u32 size);
+ bool LoadLabelStringData(void* buffer, u32 size);
+
+ u32 GetHeaderSize() const { return mFileReader.GetInfoChunkSize(); }
+ u32 GetLabelStringDataSize() const { return mFileReader.GetLabelStringChunkSize(); }
+
+ private:
+ bool LoadFileHeader();
+
+ private:
+ /* 0x108 */ detail::SoundArchiveFileReader mFileReader;
+ /* 0x14C */ DVDFileInfo mFileInfo;
+ /* 0x188 */ bool mOpen;
+ };
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/EnvGenerator.h b/src/revolution/homebuttonLib/nw4hbm/snd/EnvGenerator.h
new file mode 100644
index 0000000000..e9bb96a306
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/EnvGenerator.h
@@ -0,0 +1,60 @@
+#ifndef NW4HBM_SND_ENV_GENERATOR_H
+#define NW4HBM_SND_ENV_GENERATOR_H
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class EnvGenerator {
+ public:
+ typedef enum Status {
+ STATUS_ATTACK = 0,
+ STATUS_DECAY,
+ STATUS_SUSTAIN,
+ STATUS_RELEASE
+ } Status;
+
+ EnvGenerator();
+
+ void Init();
+ void Reset();
+ f32 GetValue() const;
+ void Update(int msec);
+
+ Status GetStatus() const { return mStatus; }
+ void SetStatus(Status status) { mStatus = status; }
+
+ void SetAttack(int attack);
+ void SetDecay(int decay);
+ void SetSustain(int sustain);
+ void SetRelease(int release);
+
+ private:
+ static const int DECIBEL_SQUARE_TABLE_SIZE = 128;
+
+ static const vf32 VOLUME_INIT;
+ static const int ATTACK_INIT = 127;
+ static const int DECAY_INIT = 127;
+ static const int SUSTAIN_INIT = 127;
+ static const int RELEASE_INIT = 127;
+
+ private:
+ f32 CalcRelease(int release);
+ int CalcDecibelSquare(int scale);
+
+ /* 0x00 */ Status mStatus;
+ /* 0x04 */ f32 mValue;
+ /* 0x08 */ f32 mDecay;
+ /* 0x0C */ f32 mRelease;
+ /* 0x10 */ f32 mAttack;
+ /* 0x14 */ u8 mSustain;
+ /* 0x16 */ u16 padding;
+
+ static const s16 DecibelSquareTable[DECIBEL_SQUARE_TABLE_SIZE];
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/ExternalSoundPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/ExternalSoundPlayer.h
new file mode 100644
index 0000000000..80685ee08e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/ExternalSoundPlayer.h
@@ -0,0 +1,66 @@
+#ifndef NW4HBM_SND_EXTERNAL_SOUND_PLAYER_H
+#define NW4HBM_SND_EXTERNAL_SOUND_PLAYER_H
+
+#include <revolution/types.h>
+
+#include "BasicSound.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class ExternalSoundPlayer {
+ public:
+ ExternalSoundPlayer();
+ ~ExternalSoundPlayer();
+
+ int GetPlayableSoundCount() const { return mPlayableCount; }
+ void SetPlayableSoundCount(int count);
+
+ int GetPlayingSoundCount() const { return mSoundList.GetSize(); }
+
+ f32 detail_GetVolume() const { return mVolume; }
+ BasicSound* GetLowestPrioritySound();
+
+ void InsertSoundList(BasicSound* sound);
+ void RemoveSoundList(BasicSound* sound);
+
+ template <typename TForEachFunc>
+ TForEachFunc ForEachSound(TForEachFunc func, bool reverse) {
+ if (reverse) {
+ BasicSoundExtPlayList::RevIterator it = mSoundList.GetBeginReverseIter();
+
+ while (it != mSoundList.GetEndReverseIter()) {
+ BasicSoundExtPlayList::RevIterator curr = it;
+
+ SoundHandle handle;
+ handle.detail_AttachSoundAsTempHandle(&*curr);
+ func(handle);
+
+ if (handle.IsAttachedSound()) {
+ it++;
+ }
+ }
+ } else {
+ for (BasicSoundExtPlayList::Iterator itr = mSoundList.GetBeginIter();
+ itr != mSoundList.GetEndIter();)
+ {
+ BasicSoundExtPlayList::Iterator curr = itr++;
+ SoundHandle handle;
+ handle.detail_AttachSoundAsTempHandle(&*curr);
+ func(handle);
+ }
+ }
+
+ return func;
+ }
+
+ private:
+ /* 0x00 */ BasicSoundExtPlayList mSoundList;
+ /* 0x0C */ u16 mPlayableCount;
+ /* 0x10 */ f32 mVolume;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/FrameHeap.h b/src/revolution/homebuttonLib/nw4hbm/snd/FrameHeap.h
new file mode 100644
index 0000000000..1184a3284e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/FrameHeap.h
@@ -0,0 +1,75 @@
+#ifndef NW4HBM_SND_FRAME_HEAP_H
+#define NW4HBM_SND_FRAME_HEAP_H
+
+#include <revolution/types.h>
+
+#include "../ut/LinkList.h"
+
+#include <revolution/mem.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class FrameHeap {
+ public:
+ typedef void (*FreeCallback)(void* buffer, u32 size, void* callbackArg);
+
+ FrameHeap();
+ ~FrameHeap();
+
+ bool Create(void* base, u32 size);
+ void Destroy();
+ void Clear();
+ void* Alloc(u32 size, FreeCallback callback, void* callbackArg);
+
+ int SaveState();
+ void LoadState(int id);
+
+ int GetCurrentLevel() const;
+ u32 GetFreeSize() const;
+
+ bool IsValid() const { return mHandle != NULL; }
+
+ private:
+ typedef struct Block {
+ /* 0x00 */ ut::LinkListNode mLink;
+ /* 0x08 */ u32 mSize;
+ /* 0x0C */ FreeCallback mCallback;
+ /* 0x10 */ void* mCallbackArg;
+
+ Block(u32 size, FreeCallback callback, void* callbackArg)
+ : mSize(size), mCallback(callback), mCallbackArg(callbackArg) {}
+
+ ~Block() {
+ if (mCallback != NULL) {
+ mCallback(GetBufferAddr(), mSize, mCallbackArg);
+ }
+ }
+
+ void* GetBufferAddr() { return ut::AddOffsetToPtr(this, BLOCK_BUFFER_SIZE); }
+ } Block;
+ typedef ut::LinkList<Block, offsetof(Block, mLink)> BlockList;
+
+ typedef struct Section {
+ /* 0x00 */ ut::LinkListNode mLink;
+ /* 0x08 */ BlockList mBlockList;
+
+ ~Section();
+ void AppendBlock(Block* block) { mBlockList.PushBack(block); }
+ } Section;
+ typedef ut::LinkList<Section, offsetof(Section, mLink)> SectionList;
+
+ bool NewSection();
+ void ClearSection();
+
+ static const int BLOCK_BUFFER_SIZE = OSRoundUp32B(sizeof(Block));
+ static const int HEAP_ALIGN = 32;
+
+ /* 0x00 */ MEMHeapHandle mHandle;
+ /* 0x04 */ SectionList mSectionList;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/FxBase.h b/src/revolution/homebuttonLib/nw4hbm/snd/FxBase.h
new file mode 100644
index 0000000000..3adf414e62
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/FxBase.h
@@ -0,0 +1,31 @@
+#ifndef NW4HBM_SND_FX_BASE_H
+#define NW4HBM_SND_FX_BASE_H
+
+#include <revolution/types.h>
+
+#include "snd_global.h"
+#include "snd_types.h"
+
+#include "../ut/inlines.h"
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+namespace snd {
+class FxBase : ut::NonCopyable {
+public:
+ /* 0x08 */ virtual ~FxBase() {}
+
+ /* 0x0C */ virtual bool StartUp() { return true; }
+ /* 0x10 */ virtual void Shutdown() {}
+ /* 0x14 */ virtual void UpdateBuffer(int channels, void** buffer, u32 size, SampleFormat format, f32 sampleRate,
+ OutputMode mode) {}
+
+public:
+ /* 0x04 */ ut::LinkListNode mFxLink;
+};
+
+typedef ut::LinkList<FxBase, offsetof(FxBase, mFxLink)> FxList;
+} // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/InstanceManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/InstanceManager.h
new file mode 100644
index 0000000000..ba3cac711c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/InstanceManager.h
@@ -0,0 +1,53 @@
+#ifndef NW4HBM_INSTANCEMANAGER_H
+#define NW4HBM_INSTANCEMANAGER_H
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ template <typename T, int Ofs>
+ class InstanceManager {
+ public:
+ typedef typename ut::LinkList<T, Ofs>::Iterator Iterator;
+ void Append(T* obj) {
+ NW4HBM_ASSERT_CHECK_NULL(67, obj);
+ mFreeList.PushBack(obj);
+ }
+
+ void Remove(T* obj) {
+ NW4HBM_ASSERT_CHECK_NULL(84, obj);
+ mFreeList.Erase(obj);
+ }
+
+ T* Alloc() {
+ if (mFreeList.IsEmpty()) {
+ return NULL;
+ } else {
+ T& obj = mFreeList.GetFront();
+ mFreeList.PopFront();
+ mActiveList.PushBack(&obj);
+ return &obj;
+ }
+ }
+ void Free(T* obj) {
+ NW4HBM_ASSERT_CHECK_NULL(119, obj);
+
+ if (!mActiveList.IsEmpty()) {
+ mActiveList.Erase(obj);
+ mFreeList.PushBack(obj);
+ }
+ }
+
+ Iterator GetBeginIter() { return mActiveList.GetBeginIter(); }
+ Iterator GetEndIter() { return mActiveList.GetEndIter(); }
+
+ private:
+ /* 0x00 */ ut::LinkList<T, Ofs> mFreeList;
+ /* 0x0C */ ut::LinkList<T, Ofs> mActiveList;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/InstancePool.h b/src/revolution/homebuttonLib/nw4hbm/snd/InstancePool.h
new file mode 100644
index 0000000000..9a74c22f39
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/InstancePool.h
@@ -0,0 +1,69 @@
+#ifndef NW4HBM_SND_INSTANCE_POOL_H
+#define NW4HBM_SND_INSTANCE_POOL_H
+
+#include <new.h>
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class PoolImpl {
+ public:
+ PoolImpl() : mNext(NULL) {}
+
+ protected:
+ u32 CreateImpl(void* buffer, u32 size, u32 objSize);
+ void DestroyImpl(void* buffer, u32 size);
+ int CountImpl() const;
+
+ void* AllocImpl();
+ void FreeImpl(void* ptr);
+
+ private:
+ /* 0x00 */ PoolImpl* mNext;
+ };
+
+ template <typename T>
+ class InstancePool : private PoolImpl {
+ public:
+ u32 Create(void* buffer, u32 size) { return CreateImpl(buffer, size, sizeof(T)); }
+
+ void Destroy(void* buffer, u32 size) { DestroyImpl(buffer, size); }
+
+ int Count() const { return CountImpl(); }
+
+ T* Alloc() {
+ void* ptr = AllocImpl();
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ return new (ptr) T;
+ }
+
+ void Free(T* obj) {
+ if (obj != NULL) {
+ obj->~T();
+ FreeImpl(obj);
+ }
+ }
+ };
+
+ template <typename T>
+ class MemoryPool : private PoolImpl {
+ public:
+ u32 Create(void* buffer, u32 size) { return CreateImpl(buffer, size, sizeof(T)); }
+
+ void Destroy(void* buffer, u32 size) { DestroyImpl(buffer, size); }
+
+ int Count() const { return CountImpl(); }
+
+ T* Alloc() { return static_cast<T*>(AllocImpl()); }
+
+ void Free(T* obj) { FreeImpl(obj); }
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/Lfo.h b/src/revolution/homebuttonLib/nw4hbm/snd/Lfo.h
new file mode 100644
index 0000000000..9ba21d6e64
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/Lfo.h
@@ -0,0 +1,48 @@
+#ifndef NW4HBM_SND_LFO_H
+#define NW4HBM_SND_LFO_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ typedef struct LfoParam {
+ LfoParam() { Init(); }
+
+ void Init();
+
+ /* 0x00 */ f32 depth;
+ /* 0x04 */ f32 speed;
+ /* 0x08 */ u32 delay;
+ /* 0x0C */ u8 range;
+ /* 0x0D */ u8 padding[3];
+ } LfoParam;
+
+ class Lfo {
+ public:
+ Lfo() : mDelayCounter(0), mCounter(0.0f) {}
+
+ LfoParam& GetParam() { return mParam; }
+ void SetParam(const LfoParam& rParam) { mParam = rParam; }
+
+ void Reset();
+ void Update(int msec);
+
+ f32 GetValue() const;
+
+ private:
+ static const int TABLE_SIZE = 32;
+
+ static s8 GetSinIdx(int idx);
+
+ /* 0x00 */ LfoParam mParam;
+ /* 0x10 */ u32 mDelayCounter;
+ /* 0x14 */ f32 mCounter;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MemorySoundArchive.h b/src/revolution/homebuttonLib/nw4hbm/snd/MemorySoundArchive.h
new file mode 100644
index 0000000000..3591f0c356
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MemorySoundArchive.h
@@ -0,0 +1,39 @@
+#ifndef NW4HBM_SND_MEMORY_SOUND_ARCHIVE_H
+#define NW4HBM_SND_MEMORY_SOUND_ARCHIVE_H
+
+#include <revolution/types.h>
+#include "../ut/FileStream.h"
+#include "SoundArchive.h"
+#include "SoundArchiveFile.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ class MemorySoundArchive : public SoundArchive {
+ private:
+ class MemoryFileStream;
+
+ public:
+ MemorySoundArchive();
+
+ /* 0x08 */ virtual ~MemorySoundArchive();
+ /* 0x0C */ virtual const void* detail_GetFileAddress(u32 id) const;
+ /* 0x10 */ virtual const void* detail_GetWaveDataFileAddress(u32 id) const;
+ /* 0x14 */ virtual int detail_GetRequiredStreamBufferSize() const;
+ /* 0x18 */ virtual ut::FileStream* OpenStream(void* buffer, int size, u32 offset,
+ u32 length) const;
+ /* 0x1C */ virtual ut::FileStream* OpenExtStream(void* buffer, int size,
+ const char* extPath, u32 offset,
+ u32 length) const;
+
+ bool Setup(const void* buffer);
+ void Shutdown();
+
+ private:
+ /* 0x108 */ const void* mData;
+ /* 0x10C */ detail::SoundArchiveFileReader mFileReader;
+ };
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqPlayer.h
new file mode 100644
index 0000000000..d14573b913
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqPlayer.h
@@ -0,0 +1,20 @@
+#ifndef NW4HBM_SND_MIDI_SEQ_PLAYER_H
+#define NW4HBM_SND_MIDI_SEQ_PLAYER_H
+
+#include "SeqPlayer.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ // No midi for you
+ class MidiSeqPlayer : public SeqPlayer {
+ MidiSeqPlayer();
+ virtual ~MidiSeqPlayer() {}
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqTrack.h b/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqTrack.h
new file mode 100644
index 0000000000..6f1cee39c9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MidiSeqTrack.h
@@ -0,0 +1,20 @@
+#ifndef NW4HBM_SND_MIDI_SEQ_TRACK_H
+#define NW4HBM_SND_MIDI_SEQ_TRACK_H
+
+#include "SeqTrack.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ // No midi for you
+ class MidiSeqTrack : public SeqTrack {
+ MidiSeqTrack();
+ virtual ~MidiSeqTrack() {}
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MmlParser.h b/src/revolution/homebuttonLib/nw4hbm/snd/MmlParser.h
new file mode 100644
index 0000000000..07fc1b5c73
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MmlParser.h
@@ -0,0 +1,155 @@
+#ifndef NW4HBM_SND_MML_PARSER_H
+#define NW4HBM_SND_MML_PARSER_H
+
+#include <revolution/types.h>
+#include "SeqTrack.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class MmlSeqTrack;
+ class SeqPlayer;
+
+ class MmlParser {
+ public:
+ typedef enum SeqArgType {
+ SEQ_ARG_U8 = 0,
+ SEQ_ARG_S16,
+ SEQ_ARG_VMIDI,
+ SEQ_ARG_RANDOM,
+ SEQ_ARG_VARIABLE
+ } SeqArgType;
+
+ static const int CALL_STACK_MAX_DEPTH = 3;
+
+ public:
+ /* 0x08 */ virtual void CommandProc(MmlSeqTrack* track, u32 command,
+ s32 commandArg1, s32 commandArg2) const;
+ /* 0x0C */ virtual Channel* NoteOnCommandProc(MmlSeqTrack* track, int key,
+ int velocity, s32 length,
+ bool tieFlag) const;
+
+ ParseResult Parse(MmlSeqTrack* track, bool doNoteOn) const;
+
+ vs16* GetVariablePtr(SeqPlayer* player, SeqTrack* track, int varNo) const;
+
+ static void EnablePrintVar(bool enable) { mPrintVarEnabledFlag = enable; }
+
+ private:
+ typedef enum MmlSeqData {
+ MML_CMD_MIN = 0x80, // <80 -> MML note, not a command
+
+ MML_WAIT = 0x80, // 0x80
+ MML_SET_PRGNO, // 0x81
+ MML_OPENTRACK = 0x88, // 0x88
+ MML_JUMP, // 0x89
+ MML_CALL, // 0x8A
+
+ MML_RNDPARAM = 0xA0, // 0xA0
+ MML_INDPARAM, // 0xA1
+ MML_EXECIF, // 0xA2
+
+ MML_SET_TIMEBASE = 0xB0, // 0xB0 (unused by this version)
+
+ MML_SET_PAN = 0xC0, // 0xC0
+ MML_SET_TRACK_VOLUME, // 0xC1
+ MML_SET_PLAYER_VOLUME, // 0xC2
+ MML_SET_TRANSPOSE, // 0xC3
+ MML_SET_PITCHBEND, // 0xC4
+ MML_SET_BENDRANGE, // 0xC5
+ MML_SET_PRIORITY, // 0xC6
+ MML_SET_NOTEWAIT, // 0xC7
+ MML_SET_TIE, // 0xC8
+ MML_SET_PORTAMENTO, // 0xC9
+ MML_SET_LFODEPTH, // 0xCA
+ MML_SET_LFOSPEED, // 0xCB
+ MML_SET_LFOTARGET, // 0xCC
+ MML_SET_LFORANGE, // 0xCD
+ MML_SET_PORTASPEED, // 0xCE
+ MML_SET_PORTATIME, // 0xCF
+
+ MML_SET_ATTACK = 0xD0, // 0xD0
+ MML_SET_DECAY, // 0xD1
+ MML_SET_SUSTAIN, // 0xD2
+ MML_SET_RELEASE, // 0xD3
+ MML_LOOP_START, // 0xD4
+ MML_SET_TRACK_VOLUME2, // 0xD5
+ MML_PRINTVAR, // 0xD6
+ MML_SET_SURROUNDPAN, // 0xD7
+ MML_SET_LPFFREQ, // 0xD8
+ MML_SET_FXSEND_A, // 0xD9
+ MML_SET_FXSEND_B, // 0xDA
+ MML_SET_MAINSEND, // 0xDB
+ MML_SET_INITPAN, // 0xDC
+ MML_SET_MUTE, // 0xDD
+ MML_SET_FXSEND_C, // 0xDE
+ MML_SET_DAMPER, // 0xDF (unused by this version)
+
+ MML_SET_LFODELAY = 0xE0, // 0xE0
+ MML_SET_TEMPO, // 0xE1
+ MML_CMD_E2h, // 0xE2
+ MML_SET_SWEEPPITCH, // 0xE3
+
+ MML_LOOP_END = 0xFC, // 0xFC
+ MML_RET, // 0xFD
+ MML_ALLOCTRACK, // 0xFE
+ MML_EOF, // 0xFF
+
+ MML_CMD_MASK = 0x80,
+ MML_CMD_SET_MASK = 0xF0,
+
+ MML_CMD_MAX = 0xFF, // >FF -> EX command
+
+ // (MML_EX_COMMAND << 8) | Command
+ MML_EX_COMMAND = 0xF0,
+
+ // MML EX arithmetic command set
+ MML_EX_ARITHMETIC = 0x80, // 0xF0 0x8X
+ MML_EX_SET = 0x80, // 0xF0 0x80
+ MML_EX_APL, // 0xF0 0x81
+ MML_EX_AMI, // 0xF0 0x82
+ MML_EX_AMU, // 0xF0 0x83
+ MML_EX_ADV, // 0xF0 0x84
+ MML_EX_ALS, // 0xF0 0x85
+ MML_EX_RND, // 0xF0 0x86
+ MML_EX_AAD, // 0xF0 0x87
+ MML_EX_AOR, // 0xF0 0x88
+ MML_EX_AER, // 0xF0 0x89
+ MML_EX_ACO, // 0xF0 0x8A
+ MML_EX_AMD, // 0xF0 0x8B
+
+ // MML EX logic command set
+ MML_EX_LOGIC = 0x90, // 0xF0 0x9X
+ MML_EX_EQ = 0x90, // 0xF0 0x90
+ MML_EX_GE, // 0xF0 0x91
+ MML_EX_GT, // 0xF0 0x92
+ MML_EX_LE, // 0xF0 0x93
+ MML_EX_LT, // 0xF0 0x94
+ MML_EX_NE, // 0xF0 0x95
+
+ // MML EX userproc callback
+ MML_EX_USERPROC = 0xE0, // 0xF0 0xE0
+
+ MML_EX_CMD_MAX = 0xFFFF, // >FFFF -> Invalid command
+ } MmlSeqData;
+
+ private:
+ u8 ReadByte(const u8** data) const { return *(*data)++; }
+
+ u16 Read16(const u8** data) const;
+ u32 Read24(const u8** data) const;
+ s32 ReadVar(const u8** data) const;
+ s32 ReadArg(const u8** data, SeqPlayer* player, SeqTrack* track,
+ SeqArgType type) const;
+
+ private:
+ static bool mPrintVarEnabledFlag;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrack.h b/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrack.h
new file mode 100644
index 0000000000..6b3c627875
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrack.h
@@ -0,0 +1,38 @@
+#ifndef NW4HBM_SND_MML_SEQ_TRACK_H
+#define NW4HBM_SND_MML_SEQ_TRACK_H
+
+#include "MmlParser.h"
+#include "SeqTrack.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class MmlSeqTrack : public SeqTrack {
+ public:
+ typedef struct MmlParserParam {
+ /* 0x00 */ bool cmpFlag;
+ /* 0x01 */ bool noteWaitFlag;
+ /* 0x02 */ bool tieFlag;
+ /* 0x03 */ u8 loopCount[MmlParser::CALL_STACK_MAX_DEPTH];
+ /* 0x06 */ u8 callStackDepth;
+ /* 0x08 */ const u8* callStack[MmlParser::CALL_STACK_MAX_DEPTH];
+ } MmlParserParam;
+
+ public:
+ MmlSeqTrack();
+
+ /* 0x0C */ virtual ParseResult Parse(bool doNoteOn);
+
+ void SetMmlParser(const MmlParser* pParser) { mParser = pParser; }
+ MmlParserParam& GetMmlParserParam() { return mMmlParserParam; }
+
+ private:
+ /* 0xC0 */ const MmlParser* mParser;
+ /* 0xC4 */ MmlParserParam mMmlParserParam;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrackAllocator.h b/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrackAllocator.h
new file mode 100644
index 0000000000..51da49e549
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MmlSeqTrackAllocator.h
@@ -0,0 +1,37 @@
+#ifndef NW4HBM_SND_MML_SEQ_TRACK_ALLOCATOR_H
+#define NW4HBM_SND_MML_SEQ_TRACK_ALLOCATOR_H
+
+#include <revolution/types.h>
+
+#include "InstancePool.h"
+#include "SeqTrackAllocator.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class MmlParser;
+ class MmlSeqTrack;
+ class SeqPlayer;
+ class SeqTrack;
+
+ class MmlSeqTrackAllocator : public SeqTrackAllocator {
+ public:
+ explicit MmlSeqTrackAllocator(MmlParser* parser) : mParser(parser) {}
+
+ /* 0x0C */ virtual SeqTrack* AllocTrack(SeqPlayer* player);
+ /* 0x10 */ virtual void FreeTrack(SeqTrack* track);
+
+ u32 Create(void* buffer, u32 size);
+ void Destroy(void* buffer, u32 size);
+
+ private:
+ /* 0x04 */ MmlParser* mParser;
+ /* 0x08 */ InstancePool<MmlSeqTrack> mTrackPool;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/MoveValue.h b/src/revolution/homebuttonLib/nw4hbm/snd/MoveValue.h
new file mode 100644
index 0000000000..edfc0f74cf
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/MoveValue.h
@@ -0,0 +1,56 @@
+#ifndef NW4HBM_SND_MOVE_VALUE_H
+#define NW4HBM_SND_MOVE_VALUE_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ template <typename TValue, typename TTime>
+ class MoveValue {
+ public:
+ MoveValue()
+ : mOrigin(TValue()), mTarget(TValue()), mFrame(TTime()), mCounter(TTime()) {}
+
+ void InitValue(TValue t1) {
+ mOrigin = t1;
+ mTarget = t1;
+ mFrame = 0;
+ mCounter = 0;
+ }
+
+ bool IsFinished() const { return mCounter >= mFrame; }
+
+ TValue GetValue() const {
+ if (IsFinished()) {
+ return mTarget;
+ }
+ return mOrigin + mCounter * (mTarget - mOrigin) / mFrame;
+ }
+
+ void Update() {
+ if (mCounter < mFrame) {
+ mCounter++;
+ }
+ }
+
+ void SetTarget(TValue target, TTime frame) {
+ mOrigin = GetValue();
+ mTarget = target;
+ mFrame = frame;
+ mCounter = 0;
+ }
+
+ private:
+ /* 0x00 */ TValue mOrigin;
+ /* 0x04 */ TValue mTarget;
+ /* 0x08 */ TTime mFrame;
+ /* 0x0C */ TTime mCounter;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/NandSoundArchive.h b/src/revolution/homebuttonLib/nw4hbm/snd/NandSoundArchive.h
new file mode 100644
index 0000000000..79aee565ca
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/NandSoundArchive.h
@@ -0,0 +1,52 @@
+#ifndef NW4HBM_SND_NAND_SOUND_ARCHIVE_H
+#define NW4HBM_SND_NAND_SOUND_ARCHIVE_H
+
+#include <revolution/nand.h>
+#include "../ut/FileStream.h"
+#include "SoundArchive.h"
+#include "SoundArchiveFile.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ class NandSoundArchive : public SoundArchive {
+ private:
+ class NandFileStream;
+
+ public:
+ NandSoundArchive();
+
+ /* 0x08 */ virtual ~NandSoundArchive();
+ /* 0x0C */ virtual const void* detail_GetFileAddress(u32 id) const { return NULL; }
+ /* 0x10 */ virtual const void* detail_GetWaveDataFileAddress(u32 id) const {
+ return NULL;
+ }
+ /* 0x14 */ virtual int detail_GetRequiredStreamBufferSize() const;
+ /* 0x18 */ virtual ut::FileStream* OpenStream(void* buffer, int size, u32 offset,
+ u32 length) const;
+ /* 0x1C */ virtual ut::FileStream* OpenExtStream(void* buffer, int size,
+ const char* extPath, u32 offset,
+ u32 length) const;
+
+ bool Open(const char* path);
+ void Close();
+
+ bool LoadHeader(void* buffer, u32 size);
+ bool LoadLabelStringData(void* buffer, u32 size);
+
+ u32 GetHeaderSize() const { return mFileReader.GetInfoChunkSize(); }
+ u32 GetLabelStringDataSize() const { return mFileReader.GetLabelStringChunkSize(); }
+
+ private:
+ bool LoadFileHeader() NO_INLINE;
+
+ private:
+ /* 0x108 */ detail::SoundArchiveFileReader mFileReader;
+ /* 0x14C */ NANDFileInfo mFileInfo;
+ /* 0x1D8 */ bool mOpen;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/NoteOnCallback.h b/src/revolution/homebuttonLib/nw4hbm/snd/NoteOnCallback.h
new file mode 100644
index 0000000000..3b2f4f663b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/NoteOnCallback.h
@@ -0,0 +1,36 @@
+#ifndef NW4HBM_SND_NOTE_ON_CALLBACK_H
+#define NW4HBM_SND_NOTE_ON_CALLBACK_H
+
+#include <revolution/types.h>
+
+#include "Channel.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ typedef struct NoteOnInfo {
+ /* 0x00 */ int prgNo;
+ /* 0x04 */ int key;
+ /* 0x08 */ int velocity;
+ /* 0x0C */ int length;
+ /* 0x10 */ int initPan;
+ /* 0x14 */ int priority;
+ /* 0x18 */ int voiceOutCount;
+ /* 0x1C */ Channel::ChannelCallback channelCallback;
+ /* 0x20 */ u32 channelCallbackData;
+ };
+
+ class SeqPlayer;
+ class NoteOnCallback {
+ public:
+ /* 0x08 */ virtual ~NoteOnCallback() {}
+ /* 0x0C */ virtual Channel* NoteOn(SeqPlayer* player, int bankNo,
+ const NoteOnInfo& noteOnInfo) = 0;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/PlayerHeap.h b/src/revolution/homebuttonLib/nw4hbm/snd/PlayerHeap.h
new file mode 100644
index 0000000000..62c183463e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/PlayerHeap.h
@@ -0,0 +1,43 @@
+#ifndef NW4HBM_SND_PLAYER_HEAP_H
+#define NW4HBM_SND_PLAYER_HEAP_H
+
+#include "SoundHeap.h"
+#include "SoundMemoryAllocatable.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ namespace detail {
+ class BasicSound;
+ }
+
+ class SoundPlayer;
+
+ } // namespace snd
+} // namespace nw4hbm
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class PlayerHeap : public SoundHeap {
+ public:
+ PlayerHeap() : mSound(NULL), mPlayer(NULL) {}
+ /* 0x08 */ virtual ~PlayerHeap() {}
+
+ void SetSound(BasicSound* sound) { mSound = sound; }
+ void SetSoundPlayer(SoundPlayer* player) { mPlayer = player; }
+
+ public:
+ /* 0x2C */ ut::LinkListNode mLink;
+
+ private:
+ /* 0x34 */ BasicSound* mSound;
+ /* 0x38 */ SoundPlayer* mPlayer;
+ };
+ typedef ut::LinkList<PlayerHeap, offsetof(PlayerHeap, mLink)> PlayerHeapList;
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeaker.h b/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeaker.h
new file mode 100644
index 0000000000..7fb8ccedac
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeaker.h
@@ -0,0 +1,72 @@
+#ifndef NW4HBM_SND_REMOTE_SPEAKER_H
+#define NW4HBM_SND_REMOTE_SPEAKER_H
+
+#include <revolution/os.h>
+#include <revolution/wenc.h>
+#include <revolution/wpad.h>
+
+namespace nw4hbm {
+ namespace snd {
+
+ class RemoteSpeaker {
+ public:
+ static const int SAMPLES_PER_AUDIO_PACKET = 40;
+
+ RemoteSpeaker();
+
+ void InitParam();
+ void ClearParam();
+
+ bool Setup(WPADCallback pCallback);
+ void Update(const s16* axRemoteSamples);
+
+ bool IsAvailable() const { return mRemoteInitFlag; }
+ void SetChannelIndex(int index) { mChannelIndex = index; }
+
+ private:
+ typedef enum SpeakerState {
+ STATE_INVALID,
+ STATE_EXEC_SPEAKER_ON,
+ STATE_SPEAKER_ON,
+ STATE_EXEC_SPEAKER_PLAY,
+ STATE_SPEAKER_PLAY,
+ STATE_EXEC_SPEAKER_OFF,
+ STATE_SPEAKER_OFF
+ } SpeakerState;
+
+ typedef enum SpeakerCommand {
+ COMMAND_NONE,
+ COMMAND_SPEAKER_ON,
+ COMMAND_SPEAKER_PLAY,
+ COMMAND_SPEAKER_OFF
+ } SpeakerCommand;
+
+ bool IsAllSampleZero(const s16* axRemoteSamples);
+
+ static const int SAMPLES_PER_ENCODED_PACKET = (SAMPLES_PER_AUDIO_PACKET + 1) / 2;
+ static const int CONTINUOUS_PLAY_INTERVAL_MINUTES = 8;
+
+ static void ContinueAlarmHandler(OSAlarm* alarm, OSContext* context);
+ static void IntervalAlarmHandler(OSAlarm* alarm, OSContext* context);
+
+ /* 0x00 */ bool mInitFlag;
+ /* 0x01 */ bool mRemoteInitFlag;
+ /* 0x02 */ bool mPlayFlag;
+ /* 0x03 */ bool mEnableFlag;
+ /* 0x04 */ bool mSetupBusyFlag;
+ /* 0x05 */ bool mFirstEncodeFlag;
+ /* 0x06 */ bool mForceResumeFlag;
+ /* 0x07 */ bool mContinueFlag;
+ /* 0x08 */ bool mIntervalFlag;
+ /* 0x09 */ WENCInfo mEncodeInfo;
+ /* 0x2C */ int mChannelIndex;
+ /* 0x30 */ WPADCallback mSetupCallback;
+ /* 0x38 */ OSAlarm mContinueAlarm;
+ /* 0x68 */ OSAlarm mInvervalAlarm;
+ /* 0x98 */ OSTime mContinueBeginTime;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeakerManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeakerManager.h
new file mode 100644
index 0000000000..ffb7d4679e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/RemoteSpeakerManager.h
@@ -0,0 +1,42 @@
+#ifndef NW4HBM_SND_REMOTE_SPEAKER_MANAGER_H
+#define NW4HBM_SND_REMOTE_SPEAKER_MANAGER_H
+
+#include <revolution/os.h>
+#include <revolution/wpad.h>
+#include "RemoteSpeaker.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class RemoteSpeakerManager {
+ public:
+ static RemoteSpeakerManager& GetInstance();
+
+ RemoteSpeaker& GetRemoteSpeaker(int idx);
+
+ void Setup();
+ void Shutdown();
+
+ private:
+ static const int SPEAKER_ALARM_HZ = 150;
+ static const int SPEAKER_ALARM_PERIOD_NSEC =
+ static_cast<int>(1.0f / SPEAKER_ALARM_HZ * 1000 * 1000 * 1000);
+
+ private:
+ RemoteSpeakerManager();
+
+ static void RemoteSpeakerAlarmProc(OSAlarm* alarm, OSContext* context);
+
+ private:
+ /* 0x00 */ bool mInitialized;
+ /* 0x08 */ OSAlarm mRemoteSpeakerAlarm;
+ /* 0x38 */ RemoteSpeaker mSpeaker[WPAD_MAX_CONTROLLERS];
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqFile.h
new file mode 100644
index 0000000000..e146a4d73a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqFile.h
@@ -0,0 +1,52 @@
+#ifndef NW4HBM_SND_SEQ_FILE_H
+#define NW4HBM_SND_SEQ_FILE_H
+
+#include <revolution/types.h>
+
+#include "Util.h"
+
+#include "../ut/binaryFileFormat.h"
+
+#include <revolution/hbm.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ namespace SeqFile {
+ typedef struct Header {
+ /* 0x00 */ ut::BinaryFileHeader fileHeader;
+ /* 0x10 */ u32 dataBlockOffset;
+ /* 0x14 */ u32 dataBlockSize;
+ /* 0x18 */ u32 labelBlockOffset;
+ /* 0x1C */ u32 labelBlockSize;
+ } Header;
+
+ typedef struct DataBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ u32 baseOffset;
+ } DataBlock;
+
+ static const u32 SIGNATURE_DATA_BLOCK = 'DATA';
+ static const u32 SIGNATURE_FILE = 'RSEQ';
+ static const int FILE_VERSION = NW4HBM_VERSION(1, 0);
+ static const int SUPPORTED_FILE_VERSION = NW4HBM_VERSION(1, 1);
+ } // namespace SeqFile
+
+ class SeqFileReader {
+ public:
+ explicit SeqFileReader(const void* seqData);
+
+ bool IsValidFileHeader(const void* seqData);
+
+ const void* GetBaseAddress() const;
+
+ private:
+ /* 0x00 */ const SeqFile::Header* mHeader;
+ /* 0x04 */ const SeqFile::DataBlock* mDataBlock;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqPlayer.h
new file mode 100644
index 0000000000..b4a769b113
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqPlayer.h
@@ -0,0 +1,199 @@
+#ifndef NW4HBM_SND_SEQ_PLAYER_H
+#define NW4HBM_SND_SEQ_PLAYER_H
+
+#include <revolution/types.h>
+
+#include "BasicPlayer.h"
+
+//! TODO: find a way to remove this hack
+#define MAKE_DTOR_ZERO
+#include "DisposeCallbackManager.h"
+#undef MAKE_DTOR_ZERO
+
+#include "SoundThread.h"
+
+#include "../ut/Lock.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class Channel;
+ struct NoteOnInfo;
+ class NoteOnCallback;
+ class SeqTrack;
+ class SeqTrackAllocator;
+
+ class SeqPlayer : public BasicPlayer, public DisposeCallback {
+ public:
+ typedef struct ParserPlayerParam {
+ /* 0x00 */ u8 volume;
+ /* 0x01 */ u8 priority;
+ /* 0x02 */ u16 tempo;
+
+ /* 0x04 */ NoteOnCallback* callback;
+ } ParserPlayerParam;
+
+ typedef enum OffsetType { OFFSET_TYPE_TICK = 0, OFFSET_TYPE_MILLISEC } OffsetType;
+
+ typedef enum SetupResult {
+ SETUP_SUCCESS = 0,
+ SETUP_ERR_CANNOT_ALLOCATE_TRACK,
+ SETUP_ERR_UNKNOWN
+ } SetupResult;
+
+ static const int LOCAL_VARIABLE_NUM = 16;
+ static const int GLOBAL_VARIABLE_NUM = 16;
+ static const int VARIABLE_NUM = LOCAL_VARIABLE_NUM + GLOBAL_VARIABLE_NUM;
+
+ static const int TRACK_NUM = 16;
+
+ SeqPlayer();
+
+ /* 0x08 */ virtual ~SeqPlayer();
+ /* 0x0C */ virtual bool Start();
+ /* 0x10 */ virtual void Stop();
+ /* 0x14 */ virtual void Pause(bool flag);
+ /* 0x18 */ virtual bool IsActive() const { return mActiveFlag; }
+ /* 0x1C */ virtual bool IsPrepared() const { return mPreparedFlag; };
+ /* 0x20 */ virtual bool IsStarted() const { return mStartedFlag; };
+ /* 0x24 */ virtual bool IsPause() const { return mPauseFlag; };
+ /* 0x28 */ virtual void SetVolume(f32 volume);
+ /* 0x2C */ virtual void SetPitch(f32 pitch);
+ /* 0x30 */ virtual void SetPan(f32 pan);
+ /* 0x34 */ virtual void SetSurroundPan(f32 surroundPan);
+ /* 0x38 */ virtual void SetPan2(f32 pan2);
+ /* 0x3C */ virtual void SetSurroundPan2(f32 surroundPan2);
+ /* 0x40 */ virtual void SetLpfFreq(f32 lpfFreq);
+ /* 0x44 */ virtual f32 GetVolume() const { return mExtVolume; };
+ /* 0x48 */ virtual f32 GetPitch() const { return mExtPitch; };
+ /* 0x4C */ virtual f32 GetPan() const { return mExtPan; };
+ /* 0x50 */ virtual f32 GetSurroundPan() const { return mExtSurroundPan; };
+ /* 0x54 */ virtual f32 GetPan2() const { return mExtPan2; };
+ /* 0x58 */ virtual f32 GetSurroundPan2() const { return mExtSurroundPan2; };
+ /* 0x5C */ virtual f32 GetLpfFreq() const { return mExtLpfFreq; };
+ /* 0x60 */ virtual void SetOutputLine(int lineFlag);
+ /* 0x64 */ virtual void SetMainOutVolume(f32 volume);
+ /* 0x68 */ virtual void SetMainSend(f32 send);
+ /* 0x6C */ virtual void SetFxSend(AuxBus bus, f32 send);
+ /* 0x70 */ virtual void SetRemoteOutVolume(int remoteIndex, f32 volume);
+ /* 0x74 */ virtual void SetRemoteSend(int remoteIndex, f32 send);
+ /* 0x78 */ virtual void SetRemoteFxSend(int remoteIndex, f32 send);
+ /* 0x7C */ virtual int GetOutputLine() const;
+ /* 0x80 */ virtual f32 GetMainOutVolume() const;
+ /* 0x84 */ virtual f32 GetMainSend() const;
+ /* 0x88 */ virtual f32 GetFxSend(AuxBus bus) const;
+ /* 0x8C */ virtual f32 GetRemoteOutVolume(int remoteIndex) const;
+ /* 0x90 */ virtual f32 GetRemoteSend(int remoteIndex) const;
+ /* 0x94 */ virtual f32 GetRemoteFxSend(int remoteIndex) const;
+ /* 0x50 */ virtual void InvalidateData(const void* start, const void* end);
+ /* 0x54 */ virtual void InvalidateWaveData(const void* start, const void* end) {}
+ /* 0x58 */ virtual void ChannelCallback(Channel* channel);
+
+ void InitParam(int voices, NoteOnCallback* callback);
+
+ SetupResult Setup(SeqTrackAllocator* allocator, u32 allocTrackFlags, int voices,
+ NoteOnCallback* callback);
+ void SetSeqData(const void* base, s32 offset);
+
+ void Skip(OffsetType type, int offset);
+
+ void SetTempoRatio(f32 tempo);
+ void SetChannelPriority(int priority);
+ void SetReleasePriorityFix(bool flag);
+
+ void SetLocalVariable(int trackNo, s16 value);
+ static void SetGlobalVariable(int trackNo, s16 value);
+
+ void SetTrackVolume(u32 trackFlags, f32 volume);
+ void SetTrackPitch(u32 trackFlags, f32 pitch);
+
+ SeqTrack* GetPlayerTrack(int trackNo);
+ vs16* GetVariablePtr(int varNo);
+ void Update();
+
+ Channel* NoteOn(int bankNo, const NoteOnInfo& noteOnInfo);
+
+ static void UpdateAllPlayers();
+ static void StopAllPlayers();
+
+ template <typename T>
+ void SetTrackParam(u32 trackFlags, void (SeqTrack::*setter)(T), T param) {
+ ut::AutoInterruptLock lock;
+
+ for (int i = 0; i < TRACK_NUM && trackFlags != 0; trackFlags >>= 1, i++) {
+ if (trackFlags & 1) {
+ SeqTrack* track = GetPlayerTrack(i);
+
+ if (track != NULL) {
+ (track->*setter)(param);
+ }
+ }
+ }
+ }
+
+ f32 GetPanRange() const { return mPanRange; }
+ int GetVoiceOutCount() const { return mVoiceOutCount; }
+ ParserPlayerParam& GetParserPlayerParam() { return mParserParam; }
+
+ private:
+ static const int DEFAULT_TEMPO = 120;
+ static const int DEFAULT_PRIORITY = 64;
+ static const int DEFAULT_VARIABLE_VALUE = -1;
+
+ static const int MAX_SKIP_TICK_PER_FRAME = 768;
+
+ void CloseTrack(int trackNo);
+ void SetPlayerTrack(int trackNo, SeqTrack* track);
+
+ void FinishPlayer();
+ void UpdateChannelParam();
+ int UpdateTempoCounter();
+ int ParseNextTick(bool doNoteOn);
+
+ void UpdateTick(int msec);
+ void SkipTick();
+
+ static void InitGlobalVariable();
+
+ /* 0x14 */ bool mHomeButtonMenuFlag;
+ /* 0x15 */ u8 mActiveFlag;
+ /* 0x16 */ u8 mPreparedFlag;
+ /* 0x17 */ u8 mStartedFlag;
+ /* 0x18 */ u8 mPauseFlag;
+ /* 0x19 */ bool mSkipFlag;
+ /* 0x1C */ f32 mExtVolume;
+ /* 0x20 */ f32 mExtPan;
+ /* 0x24 */ f32 mExtSurroundPan;
+ /* 0x28 */ f32 mPanRange;
+ /* 0x2C */ f32 mExtPan2;
+ /* 0x30 */ f32 mExtSurroundPan2;
+ /* 0x34 */ f32 mExtPitch;
+ /* 0x38 */ f32 mExtLpfFreq;
+ /* 0x3C */ int mOutputLineFlag;
+ /* 0x40 */ f32 mMainOutVolume;
+ /* 0x44 */ f32 mMainSend;
+ /* 0x48 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x54 */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ /* 0x64 */ f32 mRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x74 */ f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0x84 */ f32 mTempoRatio;
+ /* 0x88 */ u16 mTempoCounter;
+ /* 0x8C */ s32 mVoiceOutCount;
+ /* 0x90 */ ParserPlayerParam mParserParam;
+ /* 0x98 */ SeqTrackAllocator* mSeqTrackAllocator;
+ /* 0x9C */ SeqTrack* mTracks[TRACK_NUM];
+ /* 0xDC */ vs16 mLocalVariable[TRACK_NUM];
+ /* 0xFC */ u32 mTickCounter;
+
+ static vs16 mGlobalVariable[LOCAL_VARIABLE_NUM];
+
+ public:
+ /* 0x100 */ ut::LinkListNode mPlayerLink;
+ };
+ typedef ut::LinkList<SeqPlayer, offsetof(SeqPlayer, mPlayerLink)> SeqPlayerList;
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqSound.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqSound.h
new file mode 100644
index 0000000000..68d25ea3e4
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqSound.h
@@ -0,0 +1,83 @@
+#ifndef NW4HBM_SND_SEQ_SOUND_H
+#define NW4HBM_SND_SEQ_SOUND_H
+
+#include <revolution/types.h>
+
+#include "debug.h"
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ class SeqSoundHandle;
+
+ namespace detail {
+ class NoteOnCallback;
+ class SeqTrackAllocator;
+ class SeqPlayer;
+ class BasicSound;
+ template <typename T>
+ class SoundInstanceManager;
+ } // namespace detail
+
+ namespace detail {
+ class SeqSound : public BasicSound {
+ friend class SeqSoundHandle;
+
+ public:
+ typedef void (*NotifyAsyncEndCallback)(bool result, const void* seqBase,
+ s32 seqOffset, void* userData);
+
+ class SeqLoadCallback {
+ public:
+ typedef enum Result {
+ /* 0 */ RESULT_SUCCESS = 0,
+ /* 1 */ RESULT_FAILED,
+ /* 2 */ RESULT_CANCELED,
+ /* 3 */ RESULT_ASYNC,
+ /* 4 */ RESULT_RETRY
+ } Result;
+
+ /* 0x08 */ virtual ~SeqLoadCallback() {}
+ /* 0x0C */ virtual Result LoadData(SeqSound::NotifyAsyncEndCallback callback,
+ void* callbackArg, u32 userData) const = 0;
+ /* 0x10 */ virtual void CancelLoading(u32 userData) const = 0;
+ };
+
+ explicit SeqSound(SoundInstanceManager<SeqSound>* nanager);
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x28 */ virtual void Shutdown();
+ /* 0x4C */ virtual void SetPlayerPriority(int priority);
+ /* 0x50 */ virtual bool IsAttachedTempSpecialHandle();
+ /* 0x54 */ virtual void DetachTempSpecialHandle();
+ /* 0x58 */ virtual void InitParam();
+ /* 0x68 */ virtual BasicPlayer& GetBasicPlayer() { return mSeqPlayer; }
+ /* 0x6C */ virtual const BasicPlayer& GetBasicPlayer() const { return mSeqPlayer; }
+
+ SeqPlayer::SetupResult Setup(SeqTrackAllocator* trackAllocator, u32 allocTrackFlags,
+ int voices, NoteOnCallback* callback);
+
+ void Prepare(const void* seqBase, s32 seqOffset);
+ void Prepare(const SeqLoadCallback* callback, u32 callbackData);
+
+ void SetChannelPriority(int priority);
+
+ private:
+ static void NotifyLoadAsyncEndSeqData(bool result, const void* seqBase,
+ s32 seqOffset, void* userData);
+
+ /* 0x0D8 */ SeqPlayer mSeqPlayer;
+ /* 0x1F4 */ SeqSoundHandle* mTempSpecialHandle;
+ /* 0x1F8 */ SoundInstanceManager<SeqSound>* mManager;
+ /* 0x1E8 */ bool mLoadingFlag;
+ /* 0x1EC */ const SeqLoadCallback* mCallback;
+ /* 0x1F0 */ u32 mCallbackData;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqSoundHandle.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqSoundHandle.h
new file mode 100644
index 0000000000..488e5a3cf6
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqSoundHandle.h
@@ -0,0 +1,27 @@
+#ifndef NW4HBM_SND_SEQ_SOUND_HANDLE_H
+#define NW4HBM_SND_SEQ_SOUND_HANDLE_H
+
+#include "snd_types.h"
+
+#include "BasicSound.h"
+#include "SeqPlayer.h"
+#include "SeqSound.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class SeqSoundHandle : private ut::NonCopyable {
+ public:
+ ~SeqSoundHandle() { DetachSound(); }
+
+ void DetachSound();
+
+ bool IsAttachedSound() const { return mSound != NULL; }
+
+ private:
+ /* 0x00 */ detail::SeqSound* mSound;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrack.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrack.h
new file mode 100644
index 0000000000..f9b4b0b32b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrack.h
@@ -0,0 +1,146 @@
+#ifndef NW4HBM_SND_SEQ_TRACK_H
+#define NW4HBM_SND_SEQ_TRACK_H
+
+#include <revolution/types.h>
+
+#include "Channel.h"
+#include "Lfo.h"
+#include "MoveValue.h"
+#include "snd_global.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ typedef enum SeqMute { MUTE_OFF = 0, MUTE_NO_STOP, MUTE_RELEASE, MUTE_STOP } SeqMute;
+
+ typedef enum ParseResult { PARSE_RESULT_CONTINUE = 0, PARSE_RESULT_FINISH } ParseResult;
+
+ namespace detail {
+ class SeqPlayer;
+ class SeqTrack {
+ public:
+ static const int VARIABLE_NUM = 16;
+ static const int PRGNO_MAX = 0xFFFF;
+
+ typedef struct ParserTrackParam {
+ /* 0x00 */ const u8* baseAddr;
+ /* 0x04 */ const u8* currentAddr;
+ /* 0x08 */ s32 wait;
+ /* 0x0C */ u8 muteFlag;
+ /* 0x0D */ u8 silenceFlag;
+ /* 0x0E */ u8 noteFinishWait;
+ /* 0x0F */ u8 portaFlag;
+ /* 0x10 */ int bankNo;
+ /* 0x14 */ int prgNo;
+ /* 0x18 */ LfoParam lfoParam;
+ /* 0x28 */ u8 lfoTarget;
+ /* 0x2C */ f32 sweepPitch;
+ /* 0x30 */ u8 volume;
+ /* 0x31 */ u8 volume2;
+ /* 0x32 */ s8 pitchBend;
+ /* 0x33 */ u8 bendRange;
+ /* 0x34 */ s8 pan;
+ /* 0x35 */ s8 initPan;
+ /* 0x36 */ s8 surroundPan;
+ /* 0x37 */ s8 transpose;
+ /* 0x38 */ u8 priority;
+ /* 0x39 */ u8 portaKey;
+ /* 0x3A */ u8 portaTime;
+ /* 0x3B */ u8 attack;
+ /* 0x3C */ u8 decay;
+ /* 0x3D */ u8 sustain;
+ /* 0x3E */ u8 release;
+ /* 0x3F */ u8 mainSend;
+ /* 0x40 */ u8 fxSend[AUX_BUS_NUM];
+ /* 0x43 */ u8 lpfFreq;
+ } ParserTrackParam;
+
+ public:
+ SeqTrack() : mSeqPlayer(NULL) { InitParam(); }
+
+ /* 0x08 */ virtual ~SeqTrack() {}
+ /* 0x0C */ virtual ParseResult Parse(bool doNoteOn) = 0;
+
+ void SetPlayerTrackNo(int playerTrackNo);
+ u8 GetPlayerTrackNo() const { return mPlayerTrackNo; }
+
+ void InitParam();
+ void SetSeqData(const void* base, s32 offset);
+
+ void Open();
+ void Close();
+
+ void UpdateChannelLength();
+ void UpdateChannelRelease(Channel* channel);
+
+ int ParseNextTick(bool doNoteOn);
+
+ void StopAllChannel();
+ void ReleaseAllChannel(int release) NO_INLINE;
+ void PauseAllChannel(bool flag);
+ void AddChannel(Channel* channel);
+ void UpdateChannelParam();
+ void FreeAllChannel();
+
+ void SetMute(SeqMute mute);
+ void SetVolume(f32 volume);
+ void SetPitch(f32 pitch);
+
+ void SetSilence(bool silence, int fadeTime);
+ void SetPan(f32 param1);
+ void SetSurroundPan(f32 param1);
+ void SetLpfFreq(f32 param1);
+ void SetBiquadFilter(int param1, f32 param2);
+ void SetPanRange(f32 param1);
+ void SetModDepth(f32 param1);
+ void SetModSpeed(f32 param1);
+ void SetMainSend(f32 param1);
+ void SetFxSend(AuxBus bus, f32 param2);
+ void SetRemoteSend(s32 remoteIndex, f32 param2);
+ void SetRemoteFxSend(s32 remoteIndex, f32 param2);
+
+ ParserTrackParam& GetParserTrackParam() { return mParserTrackParam; }
+
+ vs16* GetVariablePtr(int varNo);
+
+ SeqPlayer* GetSeqPlayer() { return mSeqPlayer; }
+ void SetSeqPlayer(SeqPlayer* seqPlayer) { mSeqPlayer = seqPlayer; }
+
+ Channel* GetLastChannel() const { return mChannelList; }
+
+ Channel* NoteOn(int key, int velocity, s32 portatime, bool tie);
+
+ private:
+ static const int DEFAULT_PRIORITY = 64;
+ static const int DEFAULT_BENDRANGE = 2;
+ static const int DEFAULT_PORTA_KEY = 60;
+ static const int DEFAULT_VARIABLE_VALUE = -1;
+
+ private:
+ static void ChannelCallbackFunc(Channel* dropChannel,
+ Channel::ChannelCallbackStatus status,
+ u32 userData);
+
+ private:
+ /* 0x04 */ u8 mPlayerTrackNo;
+ /* 0x08 */ f32 mExtVolume;
+ /* 0x0C */ f32 mExtPitch;
+ /* 0x10 */ f32 mExtPan;
+ /* 0x14 */ f32 mExtSurroundPan;
+ /* 0x18 */ f32 mPanRange;
+ /* 0x1C */ f32 mExtLpfFreq;
+ /* 0x20 */ f32 mExtMainSend;
+ /* 0x24 */ f32 mExtFxSend[AUX_BUS_NUM];
+ /* 0x30 */ f32 mExtRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x40 */ f32 mExtRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0x50 */ ParserTrackParam mParserTrackParam;
+ /* 0x94 */ vs16 mTrackVariable[VARIABLE_NUM];
+ /* 0xB4 */ SeqPlayer* mSeqPlayer;
+ /* 0xB8 */ Channel* mChannelList;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrackAllocator.h b/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrackAllocator.h
new file mode 100644
index 0000000000..3aacfac0a7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SeqTrackAllocator.h
@@ -0,0 +1,22 @@
+#ifndef NW4HBM_SND_SEQ_TRACK_ALLOCATOR_H
+#define NW4HBM_SND_SEQ_TRACK_ALLOCATOR_H
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class SeqPlayer;
+ class SeqTrack;
+
+ class SeqTrackAllocator {
+ public:
+ /* 0x08 */ virtual ~SeqTrackAllocator() {}
+ /* 0x0C */ virtual SeqTrack* AllocTrack(SeqPlayer* player) = 0;
+ /* 0x10 */ virtual void FreeTrack(SeqTrack* track) = 0;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchive.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchive.h
new file mode 100644
index 0000000000..b61b9e26cb
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchive.h
@@ -0,0 +1,169 @@
+#ifndef NW4HBM_SND_SOUND_ARCHIVE_H
+#define NW4HBM_SND_SOUND_ARCHIVE_H
+
+#include <revolution/types.h>
+#include "../ut/FileStream.h"
+#include "snd_global.h"
+#include "snd_types.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class SoundArchiveFileReader;
+ }
+
+ typedef enum SoundType {
+ SOUND_TYPE_INVALID = 0,
+ SOUND_TYPE_SEQ,
+ SOUND_TYPE_STRM,
+ SOUND_TYPE_WAVE
+ } SoundType;
+
+ class SoundArchive {
+ public:
+ typedef struct SoundInfo {
+ /* 0x00 */ u32 fileId;
+ /* 0x04 */ u32 playerId;
+ /* 0x08 */ int playerPriority;
+ /* 0x0C */ int volume;
+ } SoundInfo;
+
+ typedef struct SeqSoundInfo {
+ /* 0x00 */ u32 dataOffset;
+ /* 0x04 */ u32 bankId;
+ /* 0x08 */ u32 allocTrack;
+ /* 0x0C */ int channelPriority;
+ } SeqSoundInfo;
+
+ typedef struct StrmSoundInfo {
+ /* Nothing here to see */
+ } StrmSoundInfo;
+
+ typedef struct WaveSoundInfo {
+ /* 0x00 */ int subNo;
+ /* 0x04 */ int channelPriority;
+ } WaveSoundInfo;
+
+ typedef struct Sound3DParam {
+ /* 0x00 */ u32 flags;
+ /* 0x04 */ u8 decayCurve;
+ /* 0x05 */ u8 decayRatio;
+ } Sound3DParam;
+
+ typedef struct BankInfo {
+ /* 0x0 */ u32 fileId;
+ } BankInfo;
+
+ typedef struct PlayerInfo {
+ /* 0x00 */ int playableSoundCount;
+ /* 0x04 */ u32 heapSize;
+ } PlayerInfo;
+
+ typedef struct GroupInfo {
+ /* 0x00 */ u32 itemCount;
+ /* 0x04 */ const char* extFilePath;
+ /* 0x08 */ u32 offset;
+ /* 0x0C */ u32 size;
+ /* 0x10 */ u32 waveDataOffset;
+ /* 0x14 */ u32 waveDataSize;
+ } GroupInfo;
+
+ typedef struct GroupItemInfo {
+ /* 0x00 */ u32 fileId;
+ /* 0x04 */ u32 offset;
+ /* 0x08 */ u32 size;
+ /* 0x0C */ u32 waveDataOffset;
+ /* 0x10 */ u32 waveDataSize;
+ } GroupItemInfo;
+
+ typedef struct FileInfo {
+ /* 0x00 */ u32 fileSize;
+ /* 0x04 */ u32 waveDataFileSize;
+ /* 0x08 */ const char* extFilePath;
+ /* 0x0C */ u32 filePosCount;
+ } FileInfo;
+
+ typedef struct FilePos {
+ /* 0x00 */ u32 groupId;
+ /* 0x04 */ u32 index;
+ } FilePos;
+
+ typedef struct SoundArchivePlayerInfo {
+ /* 0x00 */ int seqSoundCount;
+ /* 0x04 */ int seqTrackCount;
+ /* 0x08 */ int strmSoundCount;
+ /* 0x0C */ int strmTrackCount;
+ /* 0x10 */ int strmChannelCount;
+ /* 0x14 */ int waveSoundCount;
+ /* 0x18 */ int waveTrackCount;
+ } SoundArchivePlayerInfo;
+
+ static const u32 INVALID_ID = 0xFFFFFFFF;
+
+ public:
+ SoundArchive();
+ /* 0x08 */ virtual ~SoundArchive();
+ /* 0x0C */ virtual const void* detail_GetFileAddress(u32 id) const = 0;
+ /* 0x10 */ virtual const void* detail_GetWaveDataFileAddress(u32 id) const = 0;
+ /* 0x14 */ virtual int detail_GetRequiredStreamBufferSize() const = 0;
+ /* 0x18 */ virtual ut::FileStream* OpenStream(void* buffer, int bufferSize, u32 offset,
+ u32 length) const = 0;
+ /* 0x1C */ virtual ut::FileStream* OpenExtStream(void* buffer, int bufferSize,
+ const char* extPath, u32 offset,
+ u32 length) const = 0;
+
+ bool IsAvailable() const;
+
+ void Setup(detail::SoundArchiveFileReader* fileReader);
+ void Shutdown();
+
+ u32 GetPlayerCount() const;
+ u32 GetGroupCount() const;
+
+ const char* GetSoundLabelString(u32 id) const;
+ u32 ConvertLabelStringToSoundId(const char* label) const;
+ u32 ConvertLabelStringToPlayerId(const char* label) const;
+ u32 ConvertLabelStringToGroupId(const char* label) const;
+
+ u32 GetSoundUserParam(u32 id) const;
+ SoundType GetSoundType(u32 id) const;
+
+ bool ReadSoundInfo(u32 id, SoundInfo* info) const;
+ bool detail_ReadSeqSoundInfo(u32 id, SeqSoundInfo* info) const;
+ bool detail_ReadStrmSoundInfo(u32 id, StrmSoundInfo* info) const;
+ bool detail_ReadWaveSoundInfo(u32 id, WaveSoundInfo* info) const;
+
+ bool ReadPlayerInfo(u32 id, PlayerInfo* info) const;
+ bool ReadSoundArchivePlayerInfo(SoundArchivePlayerInfo* info) const;
+
+ bool detail_ReadBankInfo(u32 id, BankInfo* info) const;
+ bool detail_ReadGroupInfo(u32 id, GroupInfo* info) const;
+ bool detail_ReadGroupItemInfo(u32 groupId, u32 itemId, GroupItemInfo* info) const;
+
+ bool detail_ReadFileInfo(u32 id, FileInfo* info) const;
+ bool detail_ReadFilePos(u32 fileId, u32 posId, FilePos* pos) const;
+
+ ut::FileStream* detail_OpenFileStream(u32 id, void* buffer, int bufferSize) const;
+ ut::FileStream* detail_OpenGroupStream(u32 id, void* buffer, int bufferSize) const;
+ ut::FileStream* detail_OpenGroupWaveDataStream(u32 id, void* buffer,
+ int bufferSize) const;
+
+ void SetExternalFileRoot(const char* extFileRoot);
+
+ protected:
+ static const int FILE_PATH_MAX = 256;
+
+ private:
+ ut::FileStream* OpenExtStreamImpl(void* buffer, int bufferSize, const char* extPath,
+ u32 offset, u32 size) const;
+
+ private:
+ /* 0x04 */ detail::SoundArchiveFileReader* mFileReader;
+ /* 0x08 */ char mExtFileRoot[FILE_PATH_MAX];
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveFile.h
new file mode 100644
index 0000000000..a2e77c1aa9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveFile.h
@@ -0,0 +1,289 @@
+#ifndef NW4HBM_SND_SOUND_ARCHIVE_FILE_H
+#define NW4HBM_SND_SOUND_ARCHIVE_FILE_H
+
+#include <revolution/types.h>
+
+#include "SoundArchive.h"
+#include "Util.h"
+
+#include "../ut/binaryFileFormat.h"
+#include "../ut/inlines.h"
+
+#include <revolution/hbm.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ namespace SoundArchiveFile {
+
+ static const u32 SIGNATURE_FILE = 'RSAR';
+ static const u32 SIGNATURE_INFO_BLOCK = 'INFO';
+ static const u32 SIGNATURE_SYMB_BLOCK = 'SYMB';
+ static const int FILE_VERSION = NW4HBM_VERSION(1, 1);
+
+ typedef struct StringTreeNode {
+ /* 0x00 */ u16 flags;
+ /* 0x02 */ u16 bit;
+ /* 0x04 */ u32 leftIdx;
+ /* 0x08 */ u32 rightIdx;
+ /* 0x0C */ u32 strIdx;
+ /* 0x10 */ u32 id;
+ } StringTreeNode;
+
+ typedef struct StringTree {
+ /* 0x00 */ u32 rootIdx;
+ /* 0x04 */ Util::Table<StringTreeNode> nodeTable;
+ } StringTree;
+
+ typedef struct StringTable {
+ /* 0x00 */ Util::Table<u32> offsetTable;
+ } StringTable;
+
+ typedef struct StringChunk {
+ /* 0x00 */ u32 tableOffset;
+ /* 0x04 */ u32 soundTreeOffset;
+ /* 0x08 */ u32 playerTreeOffset;
+ /* 0x0C */ u32 groupTreeOffset;
+ /* 0x10 */ u32 bankTreeOffset;
+ } StringChunk;
+
+ typedef struct StringBlock {
+ union {
+ StringTable stringTable;
+ StringChunk stringChunk;
+ /* 0x00 */ };
+ } StringBlock;
+
+ typedef struct SymbolBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ StringBlock stringBlock;
+ } SymbolBlock;
+
+ typedef struct SeqSoundInfo {
+ /* 0x00 */ u32 dataOffset;
+ /* 0x04 */ u32 bankId;
+ /* 0x08 */ u32 allocTrack;
+ /* 0x0C */ u8 channelPriority;
+ /* 0x0D */ u8 releasePriorityFix;
+ } SeqSoundInfo;
+
+ typedef struct StrmSoundInfo {
+ /* Nothing to see here. */
+ } StrmSoundInfo;
+
+ typedef struct WaveSoundInfo {
+ /* 0x00 */ s32 subNo;
+ /* 0x04 */ u32 allocTrack;
+ /* 0x08 */ u8 channelPriority;
+ /* 0x09 */ u8 releasePriorityFix;
+ } WaveSoundInfo;
+
+ typedef Util::DataRef<void, SeqSoundInfo, StrmSoundInfo, WaveSoundInfo>
+ SoundInfoOffset;
+
+ typedef struct Sound3DParam {
+ /* 0x00 */ u32 flags;
+ /* 0x04 */ u8 decayCurve;
+ /* 0x05 */ u8 decayRatio;
+ } Sound3DParam;
+
+ typedef struct SoundCommonInfo {
+ /* 0x00 */ u32 stringId;
+ /* 0x04 */ u32 fileId;
+ /* 0x08 */ u32 playerId;
+
+ /* 0x0C */ Util::DataRef<Sound3DParam> param3dRef;
+ /* 0x14 */ u8 volume;
+ /* 0x15 */ u8 playerPriority;
+ /* 0x16 */ u8 soundType;
+ /* 0x17 */ u8 remoteFilter;
+ /* 0x18 */ SoundInfoOffset soundInfoRef;
+
+ /* 0x20 */ u32 userParam[2];
+
+ /* 0x28 */ u8 panMode;
+ /* 0x29 */ u8 panCurve;
+ } SoundCommonInfo;
+
+ typedef Util::DataRef<SoundCommonInfo> SoundCommonInfoRef;
+ typedef Util::Table<SoundCommonInfoRef> SoundCommonTable;
+
+ typedef struct BankInfo {
+ /* 0x00 */ u32 stringId;
+ /* 0x04 */ u32 fileId;
+ } BankInfo;
+ typedef Util::DataRef<BankInfo> BankInfoRef;
+ typedef Util::Table<BankInfoRef> BankTable;
+
+ typedef struct PlayerInfo {
+ /* 0x00 */ u32 stringId;
+ /* 0x04 */ u8 playableSoundCount;
+ /* 0x08 */ u32 heapSize;
+ } PlayerInfo;
+ typedef Util::DataRef<PlayerInfo> PlayerInfoRef;
+ typedef Util::Table<PlayerInfoRef> PlayerTable;
+
+ typedef Util::DataRef<SoundArchive::FilePos> SoundArchiveRef;
+ typedef Util::Table<SoundArchiveRef> FilePosTable;
+
+ typedef struct FileInfo {
+ /* 0x00 */ u32 fileSize;
+ /* 0x04 */ u32 waveDataSize;
+ /* 0x08 */ s32 entryNum;
+ /* 0x0C */ Util::DataRef<char> extFilePathRef;
+ /* 0x14 */ Util::DataRef<FilePosTable> filePosTableRef;
+ } FileInfo;
+ typedef Util::DataRef<FileInfo> FileInfoRef;
+ typedef Util::Table<FileInfoRef> FileTable;
+
+ typedef struct GroupItemInfo {
+ /* 0x0 */ u32 fileId;
+ /* 0x4 */ u32 offset;
+ /* 0x8 */ u32 size;
+ /* 0xC */ u32 waveDataOffset;
+ /* 0x10 */ u32 waveDataSize;
+ } GroupItemInfo;
+ typedef Util::DataRef<GroupItemInfo> GroupItemInfoRef;
+ typedef Util::Table<GroupItemInfoRef> GroupItemTable;
+
+ typedef struct GroupInfo {
+ /* 0x00 */ u32 stringId;
+ /* 0x04 */ s32 entryNum;
+ /* 0x08 */ Util::DataRef<char> extFilePathRef;
+ /* 0x10 */ u32 offset;
+ /* 0x14 */ u32 size;
+ /* 0x18 */ u32 waveDataOffset;
+ /* 0x1C */ u32 waveDataSize;
+ /* 0x20 */ Util::DataRef<GroupItemTable> itemTableRef;
+ } GroupInfo;
+ typedef Util::DataRef<GroupInfo> GroupInfoRef;
+ typedef Util::Table<GroupInfoRef> GroupTable;
+
+ typedef struct SoundArchivePlayerInfo {
+ /* 0x00 */ u16 seqSoundCount;
+ /* 0x02 */ u16 seqTrackCount;
+ /* 0x04 */ u16 strmSoundCount;
+ /* 0x06 */ u16 strmTrackCount;
+ /* 0x08 */ u16 strmChannelCount;
+ /* 0x0A */ u16 waveSoundCount;
+ /* 0x0C */ u16 waveTrackCount;
+ } SoundArchivePlayerInfo;
+
+ typedef struct Info {
+ /* 0x00 */ Util::DataRef<SoundCommonTable> soundTableRef;
+ /* 0x08 */ Util::DataRef<BankTable> bankTableRef;
+ /* 0x10 */ Util::DataRef<PlayerTable> playerTableRef;
+ /* 0x18 */ Util::DataRef<FileTable> fileTableRef;
+ /* 0x20 */ Util::DataRef<GroupTable> groupTableRef;
+ /* 0x28 */ Util::DataRef<SoundArchivePlayerInfo> soundArchivePlayerInfoRef;
+ } Info;
+
+ typedef struct InfoBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ Info info;
+ } InfoBlock;
+
+ typedef struct Header {
+ /* 0x00 */ ut::BinaryFileHeader fileHeader;
+ /* 0x10 */ u32 symbolDataOffset;
+ /* 0x14 */ u32 symbolDataSize;
+ /* 0x18 */ u32 infoOffset;
+ /* 0x1C */ u32 infoSize;
+ /* 0x20 */ u32 fileImageOffset;
+ /* 0x24 */ u32 fileImageSize;
+ } Header;
+
+ static const int HEADER_AREA_SIZE = OSRoundUp32B(sizeof(Header)) + 40;
+ } // namespace SoundArchiveFile
+
+ class SoundArchiveFileReader {
+ public:
+ SoundArchiveFileReader();
+
+ void Init(const void* soundArchiveData);
+ bool IsValidFileHeader(const void* soundArchiveData);
+
+ void SetStringChunk(const void* stringChunk, u32 size);
+ void SetInfoChunk(const void* infoChunk, u32 size);
+
+ SoundType GetSoundType(u32 id) const;
+
+ bool ReadSoundInfo(u32 id, SoundArchive::SoundInfo* info) const;
+ bool ReadSound3DParam(u32 id, SoundArchive::Sound3DParam* param) const;
+ bool ReadSeqSoundInfo(u32 id, SoundArchive::SeqSoundInfo* info) const;
+ bool ReadStrmSoundInfo(u32 id, SoundArchive::StrmSoundInfo* info) const;
+ bool ReadWaveSoundInfo(u32 id, SoundArchive::WaveSoundInfo* info) const;
+ bool ReadBankInfo(u32 id, SoundArchive::BankInfo* info) const;
+ bool ReadPlayerInfo(u32 id, SoundArchive::PlayerInfo* info) const;
+ bool ReadGroupInfo(u32 id, SoundArchive::GroupInfo* info) const;
+ bool ReadGroupItemInfo(u32 groupId, u32 itemId,
+ SoundArchive::GroupItemInfo* info) const;
+ bool ReadSoundArchivePlayerInfo(SoundArchive::SoundArchivePlayerInfo* info) const;
+
+ u32 GetSoundStringId(u32 id) const;
+ u32 GetPlayerCount() const;
+ u32 GetGroupCount() const;
+ u32 GetFileCount() const;
+
+ const char* GetSoundLabelString(u32 id) const;
+ u32 GetSoundUserParam(u32 id) const;
+
+ bool ReadFileInfo(u32 id, SoundArchive::FileInfo* info) const;
+ bool ReadFilePos(u32 fileId, u32 id, SoundArchive::FilePos* filePos) const;
+
+ const char* GetString(u32 id) const;
+
+ u32 ConvertLabelStringToSoundId(const char* label) const {
+ return ConvertLabelStringToId(mStringTreeSound, label);
+ }
+ u32 ConvertLabelStringToPlayerId(const char* label) const {
+ return ConvertLabelStringToId(mStringTreePlayer, label);
+ }
+ u32 ConvertLabelStringToGroupId(const char* label) const {
+ return ConvertLabelStringToId(mStringTreeGroup, label);
+ }
+
+ u16 GetVersion() const { return mHeader.fileHeader.version; }
+ u32 GetLabelStringChunkOffset() const { return mHeader.symbolDataOffset; }
+ u32 GetLabelStringChunkSize() const { return mHeader.symbolDataSize; }
+ u32 GetInfoChunkOffset() const { return mHeader.infoOffset; }
+
+ u32 GetInfoChunkSize() const { return mHeader.infoSize; }
+
+ private:
+ u32 ConvertLabelStringToId(const SoundArchiveFile::StringTree* tree,
+ const char* label) const;
+
+ const SoundArchiveFile::SoundCommonInfo* impl_GetSoundInfo(u32 id) const;
+ SoundArchiveFile::SoundInfoOffset impl_GetSoundInfoOffset(u32 id) const NO_INLINE;
+
+ const SoundArchiveFile::SeqSoundInfo* impl_GetSeqSoundInfo(u32 id) const;
+ const SoundArchiveFile::StrmSoundInfo* impl_GetStrmSoundInfo(u32 id) const;
+ const SoundArchiveFile::WaveSoundInfo* impl_GetWaveSoundInfo(u32 id) const;
+
+ const SoundArchiveFile::BankInfo* impl_GetBankInfo(u32 id) const;
+ const SoundArchiveFile::PlayerInfo* impl_GetPlayerInfo(u32 id) const;
+ const SoundArchiveFile::GroupInfo* impl_GetGroupInfo(u32 id) const;
+
+ const void* GetPtrConst(const void* base, u32 offset) const {
+ if (offset == 0) {
+ return NULL;
+ }
+ return ut::AddOffsetToPtr(base, offset);
+ }
+
+ private:
+ /* 0x00 */ SoundArchiveFile::Header mHeader;
+ /* 0x28 */ const SoundArchiveFile::Info* mInfo;
+ /* 0x2C */ const void* mStringBase;
+ /* 0x30 */ const SoundArchiveFile::StringTable* mStringTable;
+ /* 0x34 */ const SoundArchiveFile::StringTree* mStringTreeSound;
+ /* 0x38 */ const SoundArchiveFile::StringTree* mStringTreePlayer;
+ /* 0x3C */ const SoundArchiveFile::StringTree* mStringTreeGroup;
+ /* 0x40 */ const SoundArchiveFile::StringTree* mStringTreeBank;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveLoader.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveLoader.h
new file mode 100644
index 0000000000..e9c8ee4dd2
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchiveLoader.h
@@ -0,0 +1,59 @@
+#ifndef NW4HBM_SND_SOUND_ARCHIVE_LOADER_H
+#define NW4HBM_SND_SOUND_ARCHIVE_LOADER_H
+
+#include <revolution/os.h>
+#include "../ut/FileStream.h"
+#include "snd_types.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ class SoundArchive;
+ class SoundMemoryAllocatable;
+
+ namespace detail {
+
+ class FileStreamHandle {
+ public:
+ FileStreamHandle(ut::FileStream* pStream) : mStream(pStream) {}
+
+ ~FileStreamHandle() {
+ if (mStream != NULL) {
+ mStream->Close();
+ }
+ }
+
+ ut::FileStream* GetFileStream() { return mStream; }
+
+ ut::FileStream* operator->() { return mStream; }
+
+ operator bool() const { return mStream; }
+
+ private:
+ /* 0x00 */ ut::FileStream* mStream;
+ };
+
+ class SoundArchiveLoader {
+ public:
+ explicit SoundArchiveLoader(const SoundArchive& soundArchive);
+ ~SoundArchiveLoader();
+
+ void* LoadGroup(u32 id, SoundMemoryAllocatable* allocater, void** waveBuffer,
+ u32 blockSize);
+ s32 ReadFile(u32 id, void* dst, s32 size, s32 offset);
+ void* LoadFile(u32 id, SoundMemoryAllocatable* allocater);
+
+ void Cancel();
+
+ private:
+ /* 0x000 */ mutable OSMutex mMutex;
+ /* 0x018 */ const SoundArchive& mArc;
+ /* 0x01C */ u8 mStreamArea[STREAM_BUFFER_SIZE];
+ /* 0x21C */ ut::FileStream* mStream;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchivePlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchivePlayer.h
new file mode 100644
index 0000000000..dad7434de5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundArchivePlayer.h
@@ -0,0 +1,285 @@
+#ifndef NW4HBM_SND_SOUND_ARCHIVE_PLAYER_H
+#define NW4HBM_SND_SOUND_ARCHIVE_PLAYER_H
+
+// the order is important to get vtables in the right order in `snd_SoundArchivePlayer.cpp`
+// clang-format off
+#include "BasicSound.h"
+#include "PlayerHeap.h"
+
+#define MAKE_DTOR_ZERO
+#include "DisposeCallback.h"
+#undef MAKE_DTOR_ZERO
+
+#include "AxVoice.h"
+#include "SoundStartable.h"
+#include "MmlSeqTrackAllocator.h"
+#include "SoundArchive.h"
+#include "SoundInstanceManager.h"
+#include "MmlParser.h"
+#include "SeqPlayer.h"
+#include "SeqSound.h"
+#include "StrmSound.h"
+#include "NoteOnCallback.h"
+#include "Task.h"
+#include "WaveSound.h"
+// clang-format on
+
+#include "../db/assert.h"
+#include "global.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class SoundMemoryAllocatable;
+ class SoundPlayer;
+
+ namespace detail {
+ class SeqTrackAllocator;
+ class SoundArchiveLoader;
+ } // namespace detail
+
+ class SoundArchivePlayer_FileManager {
+ public:
+ /* 0x8 */ virtual const void* GetFileAddress(u32 id) = 0;
+ /* 0x8 */ virtual const void* GetFileWaveDataAddress(u32 id) = 0;
+ };
+
+ class SoundArchivePlayer : public detail::DisposeCallback, public SoundStartable {
+ public:
+ SoundArchivePlayer();
+
+ /* 0x08 */ virtual ~SoundArchivePlayer();
+ /* 0x0C */ virtual void InvalidateData(const void* start, const void* end);
+ /* 0x10 */ virtual void InvalidateWaveData(const void* start, const void* end);
+ /* 0x28 */ virtual StartResult detail_SetupSound(
+ SoundHandle* handle, u32 id, detail::BasicSound::AmbientArgInfo* ambientArgInfoInfo,
+ detail::ExternalSoundPlayer* extPlayer, bool hold, const StartInfo* startInfo);
+ /* 0x2C */ virtual u32 detail_ConvertLabelStringToSoundId(const char* label) {
+ NW4HBM_ASSERT_CHECK_NULL(355, mSoundArchive);
+ return mSoundArchive->ConvertLabelStringToSoundId(label);
+ }
+
+ bool IsAvailable() const;
+
+ bool Setup(const SoundArchive* arc, void* buffer, u32 mramBufferSize, void* strmBuffer,
+ u32 strmBufferSize);
+
+ void Shutdown();
+
+ u32 GetRequiredMemSize(const SoundArchive* arc);
+ u32 GetRequiredStrmBufferSize(const SoundArchive* arc);
+
+ void Update();
+
+ const SoundArchive& GetSoundArchive() const;
+
+ SoundPlayer& GetSoundPlayer(u32 playerId);
+ SoundPlayer& GetSoundPlayer(int playerId) {
+ return GetSoundPlayer(static_cast<u32>(playerId));
+ }
+
+ const void* detail_GetFileAddress(u32 id) const;
+ const void* detail_GetFileWaveDataAddress(u32 id) const;
+
+ const void* GetGroupAddress(u32 groupId) const;
+ void SetGroupAddress(u32 groupId, const void* addr);
+
+ const void* GetGroupWaveDataAddress(u32 groupId) const;
+ void SetGroupWaveDataAddress(u32 groupId, const void* addr);
+
+ bool LoadGroup(u32 id, SoundMemoryAllocatable* allocatable, u32 loadBlockSize);
+ bool LoadGroup(const char* label, SoundMemoryAllocatable* allocatable,
+ u32 loadBlockSize);
+
+ bool LoadGroup(int id, SoundMemoryAllocatable* allocatable, u32 loadBlockSize) {
+ return LoadGroup(static_cast<u32>(id), allocatable, loadBlockSize);
+ }
+ bool LoadGroup(u32 id, SoundMemoryAllocatable* allocatable,
+ unsigned int loadBlockSize) {
+ return LoadGroup(static_cast<u32>(id), allocatable, loadBlockSize);
+ }
+
+ u32 GetSoundPlayerCount() const { return mSoundPlayerCount; }
+
+ u32 GetFreeSeqSoundCount() const { return mSeqSoundInstanceManager.GetFreeCount(); }
+ u32 GetFreeStrmSoundCount() const { return mStrmSoundInstanceManager.GetFreeCount(); }
+ u32 GetFreeWaveSoundCount() const { return mWaveSoundInstanceManager.GetFreeCount(); }
+
+ private:
+ class SeqLoadCallback : public detail::SeqSound::SeqLoadCallback {
+ public:
+ SeqLoadCallback(const SoundArchivePlayer& player);
+
+ virtual Result LoadData(detail::SeqSound::NotifyAsyncEndCallback callback,
+ void* callbackArg, u32 userData) const;
+ virtual void CancelLoading(u32 userData) const;
+
+ private:
+ /* 0x04 */ const SoundArchivePlayer& mSoundArchivePlayer;
+ /* 0x08 */ mutable OSMutex mMutex;
+ };
+
+ class SeqNoteOnCallback : public detail::NoteOnCallback {
+ public:
+ SeqNoteOnCallback(const SoundArchivePlayer& player) : mSoundArchivePlayer(player) {}
+
+ virtual detail::Channel* NoteOn(detail::SeqPlayer* seqPlayer, int bankNo,
+ const detail::NoteOnInfo& noteOnInfo);
+
+ private:
+ /* 0x04 */ const SoundArchivePlayer& mSoundArchivePlayer;
+ };
+ friend class SoundArchivePlayer::SeqNoteOnCallback;
+
+ class StrmCallback : public detail::StrmPlayer::StrmCallback {
+ public:
+ StrmCallback(const SoundArchivePlayer& player);
+
+ virtual Result
+ LoadHeader(detail::StrmPlayer::NotifyLoadHeaderAsyncEndCallback callback,
+ void* callbackData, u32 userId, u32 userData) const;
+ virtual Result LoadStream(void* mramAddr, u32 size, s32 offset, int numChannels,
+ u32 blockSize, s32 blockHeaderOffset,
+ bool needUpdateAdpcmLoop,
+ detail::StrmPlayer::LoadCommand& callback, u32 userId,
+ u32 userData) const;
+ virtual void CancelLoading(u32 userId, u32 userData) const;
+
+ private:
+ /* 0x04 */ const SoundArchivePlayer& mSoundArchivePlayer;
+ /* 0x08 */ mutable OSMutex mMutex;
+ };
+
+ class WsdCallback : public detail::WsdTrack::WsdCallback {
+ public:
+ WsdCallback(const SoundArchivePlayer& player) : mSoundArchivePlayer(player) {}
+
+ virtual bool GetWaveSoundData(detail::WaveSoundInfo* soundInfo,
+ detail::WaveSoundNoteInfo* noteInfo,
+ detail::WaveData* waveData, const void* waveSoundData,
+ int index, int noteIndex, u32 userData) const;
+
+ private:
+ /* 0x04 */ const SoundArchivePlayer& mSoundArchivePlayer;
+ };
+
+ class SeqLoadTask : public detail::Task {
+ public:
+ SeqLoadTask(detail::SeqSound::NotifyAsyncEndCallback callback, void* callbackArg,
+ const SoundArchive& arc, u32 fileId, u32 dataOffset, SoundHeap& heap,
+ u32 taskId, OSMutex& mutex);
+
+ virtual void Execute();
+ virtual void Cancel();
+
+ private:
+ /* 0x10 */ detail::SoundArchiveLoader* mLoader;
+ /* 0x14 */ const SoundArchive& mSoundArchive;
+ /* 0x18 */ u32 mFileId;
+ /* 0x1C */ u32 mDataOffset;
+ /* 0x20 */ SoundHeap& mHeap;
+ /* 0x24 */ detail::SeqSound::NotifyAsyncEndCallback mCallback;
+ /* 0x28 */ void* mCallbackData;
+ /* 0x2C */ OSMutex& mMutex;
+ };
+
+ class StrmHeaderLoadTask : public detail::Task {
+ public:
+ StrmHeaderLoadTask(detail::StrmPlayer::NotifyLoadHeaderAsyncEndCallback callback,
+ void* callbackData, const SoundArchive& arc, u32 fileId,
+ u32 taskId, OSMutex& mutex);
+
+ virtual void Execute();
+ virtual void Cancel();
+
+ private:
+ /* 0x10 */ ut::FileStream* mStream;
+ /* 0x14 */ const SoundArchive& mSoundArchive;
+ /* 0x18 */ u32 mFileId;
+ /* 0x1C */ detail::StrmPlayer::NotifyLoadHeaderAsyncEndCallback mCallback;
+ /* 0x24 */ void* mCallbackData;
+ /* 0x28 */ OSMutex& mMutex;
+ };
+
+ class StrmDataLoadTask : public detail::Task {
+ public:
+ StrmDataLoadTask(void* addr, u32 size, s32 offset, int numChannels, u32 blockSize,
+ s32 blockHeaderOffset, bool needUpdateAdpcmLoop,
+ detail::StrmPlayer::LoadCommand& callback, const SoundArchive& arc,
+ u32 fileId, u32 taskId, OSMutex& mutex) NO_INLINE;
+ virtual void Execute();
+ virtual void Cancel();
+
+ private:
+ /* 0x10 */ detail::StrmPlayer::LoadCommand* mCallback;
+
+ /* 0x14 */ ut::FileStream* mStream;
+ /* 0x18 */ const SoundArchive& mSoundArchive;
+ /* 0x1C */ u32 mFileId;
+ /* 0x20 */ void* mAddr;
+ /* 0x24 */ u32 mSize;
+ /* 0x28 */ s32 mOffset;
+ /* 0x2C */ s32 mNumChannels;
+ /* 0x30 */ u32 mBlockSize;
+ /* 0x34 */ s32 mBlockHeaderOffset;
+ /* 0x38 */ bool mNeedUpdateAdpcmLoop;
+ /* 0x3C */ OSMutex& mMutex;
+ };
+
+ bool SetupMram(const SoundArchive* arc, void* buffer, u32 size);
+
+ detail::PlayerHeap* CreatePlayerHeap(void* buffer, u32 bufferSize);
+
+ bool SetupSoundPlayer(const SoundArchive* arc, void** buffer, void* end);
+
+ bool CreateGroupAddressTable(const SoundArchive* arc, void** buffer, void* end);
+
+ bool SetupSeqSound(const SoundArchive* arc, int sounds, void** buffer, void* end);
+ bool SetupWaveSound(const SoundArchive* arc, int sounds, void** buffer, void* end);
+ bool SetupStrmSound(const SoundArchive* arc, int sounds, void** buffer, void* end);
+ bool SetupSeqTrack(const SoundArchive* arc, int tracks, void** buffer, void* end);
+ bool SetupStrmBuffer(const SoundArchive* arc, void* buffer, u32 bufferSize);
+
+ StartResult PrepareSeqImpl(detail::SeqSound* sound,
+ const SoundArchive::SoundInfo* soundInfo,
+ const SoundArchive::SeqSoundInfo* seqSoundInfo, int voices);
+ StartResult PrepareStrmImpl(detail::StrmSound* sound,
+ const SoundArchive::SoundInfo* soundInfo,
+ const SoundArchive::StrmSoundInfo* strmSoundInfo,
+ SoundStartable::StartInfo::StartOffsetType startOffsetType,
+ int startOffset, int voices);
+
+ StartResult PrepareWaveSoundImpl(detail::WaveSound* sound,
+ const SoundArchive::SoundInfo* soundInfo,
+ const SoundArchive::WaveSoundInfo* waveSoundInfo,
+ int voices);
+
+ typedef struct Group {
+ /* 0x0 */ const void* address;
+ /* 0x4 */ const void* waveDataAddress;
+ } Group;
+ typedef detail::Util::Table<Group> GroupTable;
+
+ /* 0x10 */ const SoundArchive* mSoundArchive;
+ /* 0x14 */ GroupTable* mGroupTable;
+ /* 0x18 */ SeqLoadCallback mSeqLoadCallback;
+ /* 0x38 */ SeqNoteOnCallback mSeqCallback;
+ /* 0x40 */ WsdCallback mWsdCallback;
+ /* 0x48 */ StrmCallback mStrmCallback;
+ /* 0x68 */ detail::SeqTrackAllocator* mSeqTrackAllocator;
+ /* 0x6C */ SoundArchivePlayer_FileManager* mFileManager;
+ /* 0x70 */ u32 mSoundPlayerCount;
+ /* 0x74 */ SoundPlayer* mSoundPlayers;
+ /* 0x78 */ detail::SoundInstanceManager<detail::SeqSound> mSeqSoundInstanceManager;
+ /* 0x88 */ detail::SoundInstanceManager<detail::StrmSound> mStrmSoundInstanceManager;
+ /* 0x98 */ detail::SoundInstanceManager<detail::WaveSound> mWaveSoundInstanceManager;
+ /* 0xA8 */ detail::StrmBufferPool mStrmBufferPool;
+ /* 0xC0 */ detail::MmlParser mMmlParser;
+ /* 0xC4 */ detail::MmlSeqTrackAllocator mMmlSeqTrackAllocator;
+ /* 0xD0 */ void* mSetupBufferAddress;
+ /* 0xD4 */ u32 mSetupBufferSize;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundHandle.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundHandle.h
new file mode 100644
index 0000000000..baeba5dbdf
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundHandle.h
@@ -0,0 +1,105 @@
+#ifndef NW4HBM_SND_SOUND_HANDLE_H
+#define NW4HBM_SND_SOUND_HANDLE_H
+
+#include <revolution/types.h>
+
+#include "BasicSound.h"
+
+#include "../ut/inlines.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class SoundHandle : private ut::NonCopyable {
+ public:
+ SoundHandle() : mSound(NULL) {}
+ ~SoundHandle() { DetachSound(); }
+
+ void detail_AttachSound(detail::BasicSound* sound);
+ void detail_AttachSoundAsTempHandle(detail::BasicSound* sound);
+
+ bool IsAttachedSound() const { return mSound != NULL; }
+
+ detail::BasicSound* detail_GetAttachedSound() { return mSound; }
+
+ void DetachSound();
+
+ bool IsPrepared() const {
+ if (IsAttachedSound()) {
+ return mSound->IsPrepared();
+ }
+
+ return false;
+ }
+
+ u32 GetId() const {
+ if (IsAttachedSound()) {
+ return mSound->GetId();
+ }
+
+ return -1;
+ }
+
+ void StartPrepared() {
+ if (IsAttachedSound()) {
+ mSound->StartPrepared();
+ }
+ }
+
+ void Stop(int frames) {
+ if (IsAttachedSound()) {
+ mSound->Stop(frames);
+ }
+ }
+
+ void Pause(bool flag, int frames) {
+ if (IsAttachedSound()) {
+ mSound->Pause(flag, frames);
+ }
+ }
+
+ void SetVolume(f32 volume, int frames) {
+ if (IsAttachedSound()) {
+ mSound->SetVolume(volume, frames);
+ }
+ }
+ void SetPan(f32 pan) {
+ if (IsAttachedSound()) {
+ mSound->SetPan(pan);
+ }
+ }
+ void SetPitch(f32 pitch) {
+ if (IsAttachedSound()) {
+ mSound->SetPitch(pitch);
+ }
+ }
+
+ void SetOutputLine(int flag) {
+ if (IsAttachedSound()) {
+ mSound->SetOutputLine(flag);
+ }
+ }
+
+ void SetMainOutVolume(f32 volume) {
+ if (IsAttachedSound()) {
+ mSound->SetMainOutVolume(volume);
+ }
+ }
+ void SetRemoteOutVolume(int remote, f32 volume) {
+ if (IsAttachedSound()) {
+ mSound->SetRemoteOutVolume(remote, volume);
+ }
+ }
+
+ void SetFxSend(AuxBus bus, f32 send) {
+ if (IsAttachedSound()) {
+ mSound->SetFxSend(bus, send);
+ }
+ }
+
+ private:
+ /* 0x00 */ detail::BasicSound* mSound;
+ };
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundHeap.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundHeap.h
new file mode 100644
index 0000000000..a8665e4644
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundHeap.h
@@ -0,0 +1,57 @@
+#ifndef NW4HBM_SND_SOUND_HEAP_H
+#define NW4HBM_SND_SOUND_HEAP_H
+
+#include <revolution/types.h>
+
+#include "FrameHeap.h"
+#include "SoundMemoryAllocatable.h"
+
+#include "../ut/Lock.h"
+
+#include <revolution/os.h>
+
+namespace nw4hbm {
+ namespace snd {
+
+ class SoundHeap : public SoundMemoryAllocatable {
+ public:
+ SoundHeap();
+ /* 0x08 */ virtual ~SoundHeap();
+
+ /* 0x0C */ virtual void* Alloc(u32 size);
+ void* Alloc(u32 size, detail::FrameHeap::FreeCallback callback, void* callbackArg);
+
+ bool Create(void* base, u32 size);
+ void Destroy();
+
+ void Clear() {
+ ut::AutoMutexLock lock(mMutex);
+ mFrameHeap.Clear();
+ }
+
+ int SaveState();
+ void LoadState(int id);
+
+ bool IsValid() { return mFrameHeap.IsValid(); }
+
+ int GetCurrentLevel() const {
+ ut::detail::AutoLock<OSMutex> lock(mMutex);
+ return mFrameHeap.GetCurrentLevel();
+ }
+
+ u32 GetFreeSize() const {
+ ut::detail::AutoLock<OSMutex> lock(mMutex);
+ return mFrameHeap.GetFreeSize();
+ }
+
+ private:
+ static void DisposeCallbackFunc(void* buffer, u32 size, void* callbackArg);
+
+ /* 0x00 */ mutable OSMutex mMutex;
+ /* 0x1C */ detail::FrameHeap mFrameHeap;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundInstanceManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundInstanceManager.h
new file mode 100644
index 0000000000..5af6bb4dda
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundInstanceManager.h
@@ -0,0 +1,141 @@
+#ifndef NW4HBM_SND_SOUND_INSTANCE_MANAGER_H
+#define NW4HBM_SND_SOUND_INSTANCE_MANAGER_H
+
+#include <new.h>
+
+#include <revolution/types.h>
+
+#include "InstancePool.h"
+
+#include "../ut/Lock.h"
+#include "../ut/inlines.h"
+
+#include <revolution/os.h>
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ template <typename T>
+ class SoundInstanceManager {
+ public:
+ u32 Create(void* buffer, u32 size) {
+ NW4HBM_ASSERT_CHECK_NULL(59, buffer);
+ return mPool.Create(buffer, size);
+ }
+
+ void Destroy(void* buffer, u32 size) {
+ NW4HBM_ASSERT_CHECK_NULL(76, buffer);
+ mPool.Destroy(buffer, size);
+ }
+
+ T* Alloc(int priority) {
+ NW4R_ASSERT_MINMAXLT(92, priority, 0, 127);
+ ut::AutoInterruptLock lock;
+ T* sound;
+
+ void* ptr = mPool.Alloc();
+
+ if (ptr != NULL) {
+ sound = new (ptr) T(this);
+ } else {
+ if (mPriorityList.IsEmpty()) {
+ return NULL;
+ }
+ sound = &mPriorityList.GetFront();
+
+ if (sound == NULL) {
+ return NULL;
+ }
+
+ if (priority < sound->CalcCurrentPlayerPriority()) {
+ return NULL;
+ }
+ sound->Stop();
+
+ ptr = mPool.Alloc();
+ NW4HBM_ASSERT_CHECK_NULL(114, ptr);
+ sound = new (ptr) T(this);
+ }
+
+ InsertPriorityList(sound, priority);
+ return sound;
+ }
+
+ void Free(T* sound) {
+ NW4HBM_ASSERT_CHECK_NULL(134, sound);
+ ut::AutoInterruptLock lock;
+
+ if (mPriorityList.IsEmpty()) {
+ return;
+ }
+
+ RemovePriorityList(sound);
+ sound->~T();
+ mPool.Free(sound);
+ }
+
+ u32 GetActiveCount() const { return mPriorityList.GetSize(); }
+
+ u32 GetFreeCount() const { return mPool.Count(); }
+
+ T* GetLowestPrioritySound() {
+ if (mPriorityList.IsEmpty()) {
+ return NULL;
+ }
+
+ return static_cast<T*>(&mPriorityList.GetFront());
+ }
+
+ void InsertPriorityList(T* sound, int priority) {
+ TPrioList::Iterator it = mPriorityList.GetBeginIter();
+
+ for (; it != mPriorityList.GetEndIter(); it++) {
+ if (priority < it->CalcCurrentPlayerPriority()) {
+ break;
+ }
+ }
+
+ mPriorityList.Insert(it, sound);
+ }
+
+ void RemovePriorityList(T* sound) { mPriorityList.Erase(sound); }
+
+ void SortPriorityList() {
+ TPrioList listsByPrio[T::PRIORITY_MAX + 1];
+
+ while (!mPriorityList.IsEmpty()) {
+ T& rSound = mPriorityList.GetFront();
+ mPriorityList.PopFront();
+ listsByPrio[rSound.CalcCurrentPlayerPriority()].PushBack(&rSound);
+ }
+
+ for (int i = 0; i < T::PRIORITY_MAX + 1; i++) {
+ while (!listsByPrio[i].IsEmpty()) {
+ T& rSound = listsByPrio[i].GetFront();
+ listsByPrio[i].PopFront();
+ mPriorityList.PushBack(&rSound);
+ }
+ }
+ }
+
+ void UpdatePriority(T* sound, int priority) {
+ RemovePriorityList(sound);
+ InsertPriorityList(sound, priority);
+ }
+
+ private:
+ typedef ut::LinkList<T, offsetof(T, mPriorityLink)> TPrioList;
+
+ private:
+ /* 0x00 */ MemoryPool<T> mPool;
+ /* 0x04 */ TPrioList mPriorityList;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundMemoryAllocatable.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundMemoryAllocatable.h
new file mode 100644
index 0000000000..6cf45202d8
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundMemoryAllocatable.h
@@ -0,0 +1,16 @@
+#ifndef NW4HBM_SND_SOUND_MEMORY_ALLOCATABLE_H
+#define NW4HBM_SND_SOUND_MEMORY_ALLOCATABLE_H
+
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class SoundMemoryAllocatable {
+ public:
+ /* 0x08 */ virtual ~SoundMemoryAllocatable() {}
+ /* 0x0C */ virtual void* Alloc(u32 size) = 0;
+ };
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundPlayer.h
new file mode 100644
index 0000000000..c8629f66d5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundPlayer.h
@@ -0,0 +1,111 @@
+#ifndef NW4HBM_SND_SOUND_PLAYER_H
+#define NW4HBM_SND_SOUND_PLAYER_H
+
+#include <revolution/types.h>
+
+#include "BasicSound.h"
+#include "PlayerHeap.h"
+#include "snd_global.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class ExternalSoundPlayer;
+ class SeqSound;
+ template <typename T>
+ class SoundInstanceManager;
+ class StrmSound;
+ class WaveSound;
+ } // namespace detail
+
+ class SoundPlayer {
+ public:
+ SoundPlayer();
+ ~SoundPlayer();
+
+ void InitParam();
+ void Update();
+
+ void StopAllSound(int frames);
+ void PauseAllSound(bool flag, int frames);
+
+ void SetVolume(f32 volume);
+
+ int detail_GetOutputLine() const;
+ bool detail_IsEnabledOutputLine() const;
+
+ f32 detail_GetRemoteOutVolume(int remoteIndex) const;
+
+ void detail_InsertSoundList(detail::BasicSound* sound);
+ void detail_RemoveSoundList(detail::BasicSound* sound);
+
+ void detail_InsertPriorityList(detail::BasicSound* sound);
+ void detail_RemovePriorityList(detail::BasicSound* sound);
+
+ void detail_SortPriorityList();
+
+ detail::SeqSound*
+ detail_AllocSeqSound(int priority, int startPriority,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer, u32 id,
+ detail::SoundInstanceManager<detail::SeqSound>* manager);
+
+ detail::StrmSound*
+ detail_AllocStrmSound(int priority, int startPriority,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer, u32 id,
+ detail::SoundInstanceManager<detail::StrmSound>* manager);
+
+ detail::WaveSound*
+ detail_AllocWaveSound(int priority, int startPriority,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer, u32 id,
+ detail::SoundInstanceManager<detail::WaveSound>* manager);
+
+ int CalcPriorityReduction(detail::BasicSound::AmbientArgInfo* ambientArgInfo, u32 id);
+
+ void InitAmbientArg(detail::BasicSound* sound,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo);
+
+ void SetPlayableSoundCount(int count);
+ void detail_SetPlayableSoundLimit(int limit);
+
+ bool CheckPlayableSoundCount(int startPriority, detail::ExternalSoundPlayer* extPlayer);
+
+ void detail_AppendPlayerHeap(detail::PlayerHeap* pHeap);
+ void detail_AppendPlayerHeap2(detail::PlayerHeap* pHeap);
+ detail::PlayerHeap* detail_AllocPlayerHeap(detail::BasicSound* sound);
+ void detail_FreePlayerHeap(detail::BasicSound* sound);
+
+ bool detail_AppendSound(detail::BasicSound* sound);
+
+ int GetPlayingSoundCount() const { return mSoundList.GetSize(); }
+ int GetPlayableSoundCount() const { return mPlayableCount; }
+
+ f32 GetVolume() const { return mVolume; }
+
+ detail::BasicSound* detail_GetLowestPrioritySound() {
+ //! @bug UB when the list is empty
+ return &mPriorityList.GetFront();
+ }
+
+ f32 detail_GetMainOutVolume() const { return mMainOutVolume; }
+
+ private:
+ /* 0x00 */ detail::BasicSoundPlayerPlayList mSoundList;
+ /* 0x0C */ detail::BasicSoundPlayerPrioList mPriorityList;
+ /* 0x18 */ detail::PlayerHeapList mHeapList;
+ /* 0x24 */ u16 mPlayableCount;
+ /* 0x26 */ u16 mPlayableLimit;
+ /* 0x28 */ f32 mVolume;
+ /* 0x2C */ bool mOutputLineFlagEnable;
+ /* 0x2D */ bool mUsePlayerHeap;
+ /* 0x30 */ int mOutputLineFlag;
+ /* 0x34 */ f32 mMainOutVolume;
+ /* 0x38 */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundStartable.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundStartable.h
new file mode 100644
index 0000000000..b30f736501
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundStartable.h
@@ -0,0 +1,112 @@
+#ifndef NW4HBM_SND_SOUND_STARTABLE_H
+#define NW4HBM_SND_SOUND_STARTABLE_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace snd {
+ class SoundHandle;
+
+ namespace detail {
+ class ExternalSoundPlayer;
+ }
+
+ class SoundStartable {
+ public:
+ typedef enum StartResult {
+ START_SUCCESS = 0,
+ START_ERR_LOW_PRIORITY,
+ START_ERR_INVALID_LABEL_STRING,
+ START_ERR_INVALID_SOUNDID,
+ START_ERR_NOT_DATA_LOADED,
+ START_ERR_NOT_ENOUGH_PLAYER_HEAP,
+ START_ERR_CANNOT_OPEN_FILE,
+ START_ERR_NOT_AVAILABLE,
+ START_ERR_CANNOT_ALLOCATE_TRACK,
+ START_ERR_NOT_ENOUGH_INSTANCE,
+ START_ERR_INVALID_PARAMETER,
+ START_ERR_INVALID_SEQ_START_LOCATION_LABEL,
+
+ START_ERR_USER = 128,
+ START_ERR_UNKNOWN = 255,
+ } StartResult;
+
+ typedef struct StartInfo {
+ typedef enum EnableFlag {
+ ENABLE_START_OFFSET = (1 << 0),
+ ENABLE_PLAYER_ID = (1 << 1),
+ ENABLE_PLAYER_PRIORITY = (1 << 2)
+ } EnableFlag;
+
+ typedef enum StartOffsetType {
+ START_OFFSET_TYPE_SAMPLE = 0,
+ START_OFFSET_TYPE_MILLISEC,
+ } StartOffsetType;
+
+ /* 0x00 */ StartOffsetType startOffsetType;
+ /* 0x04 */ int startOffset;
+ /* 0x08 */ u32 playerId;
+ /* 0x0C */ int playerPriority;
+ /* 0x10 */ int voiceOutCount;
+ } StartInfo;
+
+ public:
+ /* 0x08 */ virtual ~SoundStartable() {}
+ /* 0x0C */ virtual StartResult
+ detail_SetupSound(SoundHandle* soundHandle, u32 id,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer, bool hold,
+ const StartInfo* startInfo) = 0;
+ /* 0x10 */ virtual u32 detail_ConvertLabelStringToSoundId(const char* label) = 0;
+
+ bool StartSound(SoundHandle* soundHandle, u32 id) {
+ return detail_StartSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool StartSound(SoundHandle* soundHandle, unsigned int id) {
+ return detail_StartSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool StartSound(SoundHandle* soundHandle, int id) {
+ return detail_StartSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+
+ bool HoldSound(SoundHandle* soundHandle, u32 id) {
+ return detail_HoldSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool HoldSound(SoundHandle* soundHandle, unsigned int id) {
+ return detail_HoldSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool HoldSound(SoundHandle* soundHandle, int id) {
+ return detail_HoldSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+
+ bool PrepareSound(SoundHandle* soundHandle, u32 id) {
+ return detail_PrepareSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool PrepareSound(SoundHandle* soundHandle, unsigned int id) {
+ return detail_PrepareSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+ bool PrepareSound(SoundHandle* soundHandle, int id) {
+ return detail_PrepareSound(soundHandle, id, NULL, NULL, NULL) == START_SUCCESS;
+ }
+
+ private:
+ StartResult detail_StartSound(SoundHandle* soundHandle, u32 id,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer,
+ const StartInfo* startInfo);
+
+ StartResult detail_HoldSound(SoundHandle* soundHandle, u32 id,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer,
+ const StartInfo* startInfo);
+
+ StartResult detail_PrepareSound(SoundHandle* soundHandle, u32 id,
+ detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer,
+ const StartInfo* startInfo);
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundSystem.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundSystem.h
new file mode 100644
index 0000000000..c611d049e9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundSystem.h
@@ -0,0 +1,50 @@
+#ifndef NW4HBM_SND_SOUND_SYSTEM_H
+#define NW4HBM_SND_SOUND_SYSTEM_H
+
+#include <revolution/types.h>
+
+#include "AxManager.h"
+#include "RemoteSpeakerManager.h"
+#include "TaskThread.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ class FxBase;
+ class SoundSystem {
+ public:
+ static const int DEFAULT_DVD_THREAD_PRIORITY = 3;
+ static const int DEFAULT_SOUND_THREAD_PRIORITY = 4;
+
+ static void InitSoundSystem(s32 soundThreadPrio = DEFAULT_SOUND_THREAD_PRIORITY,
+ s32 dvdThreadPriority = DEFAULT_DVD_THREAD_PRIORITY);
+
+ static void ShutdownSoundSystem();
+
+ static void SetOutputMode(OutputMode mode) {
+ detail::AxManager::GetInstance().SetOutputMode(mode);
+ }
+
+ static f32 GetMasterVolume() {
+ return detail::AxManager::GetInstance().GetMasterVolume();
+ }
+ static void SetMasterVolume(f32 volume, int frame) {
+ detail::AxManager::GetInstance().SetMasterVolume(volume, frame);
+ }
+
+ static RemoteSpeaker& GetRemoteSpeaker(int idx) {
+ return detail::RemoteSpeakerManager::GetInstance().GetRemoteSpeaker(idx);
+ }
+
+ static void AppendEffect(AuxBus bus, FxBase* pFx) {
+ detail::AxManager::GetInstance().AppendEffect(bus, pFx);
+ }
+ static void ClearEffect(AuxBus bus, int frame) {
+ detail::AxManager::GetInstance().ClearEffect(bus, frame);
+ }
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/SoundThread.h b/src/revolution/homebuttonLib/nw4hbm/snd/SoundThread.h
new file mode 100644
index 0000000000..57fe78af43
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/SoundThread.h
@@ -0,0 +1,76 @@
+#ifndef NW4HBM_SND_SOUND_THREAD_H
+#define NW4HBM_SND_SOUND_THREAD_H
+
+#include <revolution/types.h>
+
+#include "AxManager.h"
+
+#include "../ut/LinkList.h"
+#include "../ut/inlines.h"
+
+#include <revolution/os/OSMessage.h>
+#include <revolution/os/OSMutex.h>
+#include <revolution/os/OSThread.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class SoundThread {
+ public:
+ class Callback {
+ public:
+ /* 0x00 */ ut::LinkListNode mLink;
+
+ /* 0x08 */ virtual ~Callback() {}
+ /* 0x0C */ virtual void UpdatePlayer() {}
+ /* 0x10 */ virtual void EndSoundFrame() {}
+ };
+ typedef ut::LinkList<Callback, offsetof(Callback, mLink)> CallbackList;
+
+ static SoundThread& GetInstance();
+
+ bool Create(s32 priority);
+ BOOL Shutdown();
+
+ OSMutex& GetSoundMutex() { return mMutex; }
+
+ private:
+ typedef enum ThreadMessage {
+ MSG_NONE = 0,
+ MSG_AX_CALLBACK,
+ MSG_SHUTDOWN,
+ } ThreadMessage;
+
+ static const int MSG_QUEUE_CAPACITY = 8;
+
+ SoundThread() : mCreateFlag(false) {}
+
+ ~SoundThread() {}
+
+ void SoundThreadProc();
+
+ static void AxCallback();
+ static void* SoundThreadFunc(void* arg);
+
+ void Lock() { OSLockMutex(&mMutex); }
+ void Unlock() { OSUnlockMutex(&mMutex); }
+
+ private:
+ /* 0x0000 */ OSThread mThread;
+ /* 0x0318 */ u64 mThreadStack[THREAD_STACK_SIZE];
+ /* 0x2318 */ OSThreadQueue mThreadQueue;
+ /* 0x2320 */ mutable OSMutex mMutex;
+ /* 0x2338 */ OSMessageQueue mMsgQueue;
+ /* 0x2358 */ OSMessage mMsgBuffer[MSG_QUEUE_CAPACITY];
+ /* 0x2378 */ AxManager::CallbackListNode mAxCallbackNode;
+ /* 0x2384 */ CallbackList mCallbackList;
+ /* 0x2390 */ u32 mProcessTick;
+ /* 0x2394 */ bool mCreateFlag;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/StrmChannel.h b/src/revolution/homebuttonLib/nw4hbm/snd/StrmChannel.h
new file mode 100644
index 0000000000..ae184aeab7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/StrmChannel.h
@@ -0,0 +1,42 @@
+#ifndef NW4HBM_SND_STRM_CHANNEL_H
+#define NW4HBM_SND_STRM_CHANNEL_H
+
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ typedef struct StrmChannel {
+ /* 0x00 */ void* mBuffer;
+ /* 0x04 */ u32 mBufferSize;
+ /* 0x08 */ AdpcmInfo mAdpcmInfo;
+ } StrmChannel;
+
+ class StrmBufferPool {
+ public:
+ void Setup(void* base, u32 size, int count);
+ void Shutdown();
+
+ void* Alloc();
+ void Free(void* buffer);
+
+ u32 GetBlockSize() const { return mBlockSize; }
+
+ private:
+ static const int BLOCK_MAX = 32;
+ static const int BITS_PER_BYTE = 8;
+
+ /* 0x00 */ void* mBuffer;
+ /* 0x04 */ u32 mBufferSize;
+ /* 0x08 */ u32 mBlockSize;
+ /* 0x0C */ int mBlockCount;
+ /* 0x10 */ int mAllocCount;
+ /* 0x14 */ u8 mAllocFlags[BLOCK_MAX / BITS_PER_BYTE];
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/StrmFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/StrmFile.h
new file mode 100644
index 0000000000..ef51a225e4
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/StrmFile.h
@@ -0,0 +1,156 @@
+#ifndef NW4HBM_SND_STRM_FILE_H
+#define NW4HBM_SND_STRM_FILE_H
+
+#include <revolution/os.h>
+#include <revolution/types.h>
+
+#include <revolution/hbm.h>
+
+#include "Util.h"
+#include "snd_global.h"
+
+#include "../ut/FileStream.h"
+#include "../ut/binaryFileFormat.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ namespace StrmFile {
+
+ static const u32 SIGNATURE_FILE = 'RSTM';
+ static const u32 SIGNATURE_HEAD_BLOCK = 'HEAD';
+ static const int FILE_VERSION = NW4HBM_VERSION(1, 0);
+
+ typedef struct StrmDataInfo {
+ /* 0x00 */ u8 format;
+ /* 0x01 */ u8 loopFlag;
+ /* 0x02 */ u8 numChannels;
+ /* 0x03 */ u8 sampleRate24;
+ /* 0x04 */ u16 sampleRate;
+ /* 0x06 */ u16 blockHeaderOffset;
+ /* 0x08 */ u32 loopStart;
+ /* 0x0C */ u32 loopEnd;
+ /* 0x10 */ u32 dataOffset;
+ /* 0x14 */ u32 numBlocks;
+ /* 0x18 */ u32 blockSize;
+ /* 0x1C */ u32 blockSamples;
+ /* 0x20 */ u32 lastBlockSize;
+ /* 0x24 */ u32 lastBlockSamples;
+ /* 0x28 */ u32 lastBlockPaddedSize;
+ /* 0x2C */ u32 adpcmDataInterval;
+ /* 0x30 */ u32 adpcmDataSize;
+ } StrmDataInfo;
+
+ typedef struct TrackInfo {
+ /* 0x00 */ u8 channelCount;
+ /* 0x01 */ u8 channelIndexTable[];
+ } TrackInfo;
+
+ typedef struct TrackTable {
+ /* 0x00 */ u8 trackCount;
+ /* 0x01 */ u8 trackDataType;
+ /* 0x04 */ Util::DataRef<TrackInfo> refTrackHeader[];
+ } TrackTable;
+
+ typedef struct ChannelInfo {
+ /* 0x00 */ Util::DataRef<AdpcmInfo> refAdpcmInfo;
+ } ChannelInfo;
+
+ typedef struct ChannelTable {
+ /* 0x00 */ u8 channelCount;
+ /* 0x04 */ Util::DataRef<ChannelInfo> refChannelHeader[];
+ } ChannelTable;
+
+ typedef struct Header {
+ /* 0x00 */ ut::BinaryFileHeader fileHeader;
+ /* 0x10 */ u32 headBlockOffset;
+ /* 0x14 */ u32 headBlockSize;
+ /* 0x18 */ u32 adpcBlockOffset;
+ /* 0x1C */ u32 adpcBlockSize;
+ /* 0x20 */ u32 dataBlockOffset;
+ /* 0x24 */ u32 dataBlockSize;
+ } Header;
+
+ typedef struct HeadBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ Util::DataRef<StrmDataInfo> refDataHeader;
+ /* 0x10 */ Util::DataRef<TrackTable> refTrackTable;
+ /* 0x18 */ Util::DataRef<ChannelTable> refChannelTable;
+ } HeadBlock;
+ } // namespace StrmFile
+
+ typedef struct StrmInfo {
+ /* 0x00 */ u8 format;
+ /* 0x01 */ u8 loopFlag;
+ /* 0x02 */ u8 numChannels;
+ /* 0x04 */ int sampleRate;
+ /* 0x08 */ u16 blockHeaderOffset;
+ /* 0x0C */ u32 loopStart;
+ /* 0x10 */ u32 loopEnd;
+ /* 0x14 */ u32 dataOffset;
+ /* 0x18 */ u32 numBlocks;
+ /* 0x1C */ u32 blockSize;
+ /* 0x20 */ u32 blockSamples;
+ /* 0x24 */ u32 lastBlockSize;
+ /* 0x28 */ u32 lastBlockSamples;
+ /* 0x2C */ u32 lastBlockPaddedSize;
+ /* 0x30 */ u32 adpcmDataInterval;
+ /* 0x34 */ u32 adpcmDataSize;
+ } StrmInfo;
+
+ class StrmFileReader {
+ public:
+ StrmFileReader();
+
+ bool IsAvailable() const { return mHeader != NULL; }
+
+ bool IsValidFileHeader(const void* buffer);
+ void Setup(const void* buffer);
+
+ bool ReadStrmInfo(StrmInfo* strmInfo) const;
+ bool ReadAdpcmInfo(AdpcmInfo* adpcmInfo, int channels) const;
+
+ u32 GetAdpcBlockOffset() const {
+ if (IsAvailable()) {
+ return mHeader->adpcBlockOffset;
+ }
+ return 0;
+ }
+
+ private:
+ /* 0x00 */ const StrmFile::Header* mHeader;
+ /* 0x04 */ const StrmFile::HeadBlock* mHeadBlock;
+ };
+
+ class StrmFileLoader {
+ public:
+ explicit StrmFileLoader(ut::FileStream& rFileStream) : mStream(rFileStream) {}
+
+ bool LoadFileHeader(void* buffer, u32 size);
+ bool ReadAdpcBlockData(u16* yn1, u16* yn2, int block, int channels);
+
+ bool ReadStrmInfo(StrmInfo* strmInfo) const {
+ if (!mReader.IsAvailable()) {
+ return false;
+ }
+ return mReader.ReadStrmInfo(strmInfo);
+ }
+
+ bool ReadAdpcmInfo(AdpcmInfo* adpcmInfo, int channel) const {
+ if (!mReader.IsAvailable()) {
+ return false;
+ }
+ return mReader.ReadAdpcmInfo(adpcmInfo, channel);
+ }
+
+ private:
+ static const int HEADER_ALIGNED_SIZE = OSRoundUp32B(sizeof(StrmFile::Header));
+
+ /* 0x00 */ ut::FileStream& mStream;
+ /* 0x04 */ StrmFileReader mReader;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/StrmPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/StrmPlayer.h
new file mode 100644
index 0000000000..eae08ba82d
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/StrmPlayer.h
@@ -0,0 +1,240 @@
+#ifndef NW4HBM_SND_STRM_PLAYER_H
+#define NW4HBM_SND_STRM_PLAYER_H
+
+#include "snd_types.h"
+
+#include "AxVoice.h"
+
+//! TODO: find a way to remove this hack
+#define MAKE_DTOR_ZERO
+#include "BasicPlayer.h"
+#undef MAKE_DTOR_ZERO
+
+#include "InstancePool.h"
+#include "StrmChannel.h"
+#include "StrmFile.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class StrmPlayer : public BasicPlayer {
+ public:
+ typedef enum StartOffsetType {
+ START_OFFSET_TYPE_SAMPLE = 0,
+ START_OFFSET_TYPE_MILLISEC
+ } StartOffsetType;
+
+ typedef struct StrmHeader {
+ //! TODO: Why 8 if the player only supports 2???
+ static const int STRM_CHANNEL_MAX = 8;
+
+ /* 0x00 */ StrmInfo strmInfo;
+ /* 0x38 */ AdpcmInfo adpcmInfo[STRM_CHANNEL_MAX];
+ } StrmHeader;
+
+ //! TODO: How is this calculated?
+ static const int LOAD_BUFFER_SIZE = 0x4000 + 32;
+ class LoadCommand {
+ friend class StrmPlayer;
+
+ public:
+ virtual void NotifyAsyncEnd(bool result);
+
+ void SetAdpcmLoopContext(int channelNum, u16* predScale);
+ void* GetBuffer(int channelNum);
+
+ private:
+ typedef enum Status {
+ STATE_SETUP = 0,
+ STATE_INTERVAL,
+ } Status;
+
+ /* 0x04 */ StrmPlayer* mPlayer;
+ /* 0x08 */ Status mStatus;
+ /* 0x0C */ s32 mStreamBlockIndex;
+ /* 0x10 */ s32 mBufferBlockIndex;
+
+ public:
+ /* 0x14 */ ut::LinkListNode mLinkNode;
+
+ static u8 mMramBuf[LOAD_BUFFER_SIZE] ATTRIBUTE_ALIGN(32);
+ };
+ typedef ut::LinkList<LoadCommand, offsetof(LoadCommand, mLinkNode)> LoadCommandList;
+
+ typedef void (*NotifyLoadHeaderAsyncEndCallback)(bool, const StrmHeader*, void*);
+ class StrmCallback {
+ public:
+ typedef enum Result {
+ RESULT_SUCCESS = 0,
+ RESULT_FAILED,
+ RESULT_CANCELED,
+ RESULT_ASYNC,
+ RESULT_RETRY
+ } Result;
+
+ virtual ~StrmCallback() {}
+
+ virtual Result LoadHeader(StrmPlayer::NotifyLoadHeaderAsyncEndCallback callback,
+ void* callbackData, u32 userId,
+ u32 userData) const = 0;
+ virtual Result LoadStream(void* mramAddr, u32 size, s32 offset, int numChannels,
+ u32 blockSize, s32 blockHeaderOffset,
+ bool needUpdateAdpcmLoop,
+ StrmPlayer::LoadCommand& callback, u32 userId,
+ u32 userData) const = 0;
+
+ virtual void CancelLoading(u32 userId, u32 userData) const = 0;
+ };
+
+ public:
+ StrmPlayer();
+
+ /* 0x0C */ virtual bool Start();
+ /* 0x10 */ virtual void Stop();
+ /* 0x14 */ virtual void Pause(bool flag);
+ /* 0x18 */ virtual bool IsActive() const { return mActiveFlag; }
+ /* 0x1C */ virtual bool IsPrepared() const { return mPreparedFlag; };
+ /* 0x20 */ virtual bool IsStarted() const { return mStartedFlag; };
+ /* 0x24 */ virtual bool IsPause() const { return mPauseFlag; };
+ /* 0x28 */ virtual void SetVolume(f32 volume) { mExtVolume = volume; }
+ /* 0x2C */ virtual void SetPitch(f32 pitch) { mExtPitch = pitch; }
+ /* 0x30 */ virtual void SetPan(f32 pan) { mExtPan = pan; }
+ /* 0x34 */ virtual void SetSurroundPan(f32 surroundPan) {
+ mExtSurroundPan = surroundPan;
+ }
+ /* 0x38 */ virtual void SetPan2(f32 pan2) { mExtPan2 = pan2; }
+ /* 0x3C */ virtual void SetSurroundPan2(f32 surroundPan2) {
+ mExtSurroundPan2 = surroundPan2;
+ }
+ /* 0x40 */ virtual void SetLpfFreq(f32 lpfFreq) { mExtLpfFreq = lpfFreq; }
+ /* 0x44 */ virtual f32 GetVolume() const { return mExtVolume; };
+ /* 0x48 */ virtual f32 GetPitch() const { return mExtPitch; };
+ /* 0x4C */ virtual f32 GetPan() const { return mExtPan; };
+ /* 0x50 */ virtual f32 GetSurroundPan() const { return mExtSurroundPan; };
+ /* 0x54 */ virtual f32 GetPan2() const { return mExtPan2; };
+ /* 0x58 */ virtual f32 GetSurroundPan2() const { return mExtSurroundPan2; };
+ /* 0x5C */ virtual f32 GetLpfFreq() const { return mExtLpfFreq; };
+ /* 0x60 */ virtual void SetOutputLine(int lineFlag);
+ /* 0x64 */ virtual void SetMainOutVolume(f32 volume);
+ /* 0x68 */ virtual void SetMainSend(f32 send);
+ /* 0x6C */ virtual void SetFxSend(AuxBus bus, f32 send);
+ /* 0x70 */ virtual void SetRemoteOutVolume(int remoteIndex, f32 volume);
+ /* 0x74 */ virtual void SetRemoteSend(int remoteIndex, f32 send);
+ /* 0x78 */ virtual void SetRemoteFxSend(int remoteIndex, f32 send);
+ /* 0x7C */ virtual int GetOutputLine() const;
+ /* 0x80 */ virtual f32 GetMainOutVolume() const;
+ /* 0x84 */ virtual f32 GetMainSend() const;
+ /* 0x88 */ virtual f32 GetFxSend(AuxBus bus) const;
+ /* 0x8C */ virtual f32 GetRemoteOutVolume(int remoteIndex) const;
+ /* 0x90 */ virtual f32 GetRemoteSend(int remoteIndex) const;
+ /* 0x94 */ virtual f32 GetRemoteFxSend(int remoteIndex) const;
+
+ void Setup(const StrmHeader* header);
+ bool SetupPlayer(const StrmHeader* header);
+ void Shutdown();
+
+ bool Prepare(StrmBufferPool* bufferPool, StartOffsetType startOffsetType,
+ s32 startOffset, int voices, StrmCallback* callback, u32 callbackData);
+
+ void InitParam(int voices);
+ void ForceStop();
+
+ void Update();
+
+ static void UpdateAllPlayers();
+ static void StopAllPlayers();
+ static void UpdateBufferAllPlayers();
+
+ private:
+ static const int DATA_BLOCK_COUNT_MIN = 4;
+ static const int DATA_BLOCK_COUNT_MAX = 32;
+ static const int DATA_BLOCK_SIZE_MAX = 0x2000;
+
+ bool AllocChannels(int channels, int voices);
+ void FreeChannels();
+
+ void UpdateBuffer();
+ void UpdateLoopAddress(u32 startSample, u32 endSample);
+ void UpdatePlayingBlockIndex();
+ void UpdateDataLoopAddress(s32 endBlock);
+ void SetLoopEndToZeroBuffer(int endBlock);
+ void UpdateLoadingBlockIndex(LoadCommand::Status status);
+ void UpdatePauseStatus();
+
+ int CalcLoadingBufferBlockCount() const;
+ bool CalcStartOffset(s32* blockIndex, u32* blockOffset, s32* loopCount);
+
+ static void VoiceCallbackFunc(AxVoice* voice, AxVoice::CallbackStatus status,
+ void* pCallbackArg);
+
+ static void NotifyStrmHeaderAsyncEndCallback(bool result, const StrmHeader* header,
+ void* userData);
+
+ public:
+ /* 0x008 */ ut::LinkListNode mPlayerLink;
+
+ private:
+ /* 0x010 */ StrmInfo mStrmInfo;
+ /* 0x048 */ u8 mActiveFlag;
+ /* 0x049 */ u8 mStartedFlag;
+ /* 0x04A */ u8 mPreparedFlag;
+ /* 0x04B */ u8 mPauseFlag;
+ /* 0x04C */ u8 mDiskErrorFlag;
+ /* 0x04D */ u8 mPauseStatus;
+ /* 0x04E */ u8 mLoadWaitFlag;
+ /* 0x04F */ u8 mNoRealtimeLoadFlag;
+ /* 0x050 */ u8 mSkipUpdateAdpcmLoop;
+ /* 0x051 */ u8 mValidAdpcmLoop;
+ /* 0x052 */ u8 mPlayFinishFlag;
+ /* 0x053 */ u8 mLoadFinishFlag;
+ /* 0x054 */ s32 mLoopCounter;
+ /* 0x058 */ int mPrepareCounter;
+ /* 0x05C */ int mChangeNumBlocks;
+ /* 0x060 */ int mDataBlockSize;
+ /* 0x064 */ int mBufferBlockCount;
+ /* 0x068 */ int mBufferBlockCountBase;
+ /* 0x06C */ int mLoadingBufferBlockCount;
+ /* 0x070 */ int mLoadingBufferBlockIndex;
+ /* 0x074 */ int mLoadingDataBlockIndex;
+ /* 0x078 */ int mPlayingBufferBlockCount;
+ /* 0x07C */ int mPlayingBufferBlockIndex;
+ /* 0x080 */ int mPlayingDataBlockIndex;
+ /* 0x084 */ int mLoopStartBlockIndex;
+ /* 0x088 */ int mLastBlockIndex;
+ /* 0x08C */ StartOffsetType mStartOffsetType;
+ /* 0x090 */ int mStartOffset;
+ /* 0x094 */ LoadCommandList mFreeLoadCommandList;
+ /* 0x0A0 */ LoadCommandList mFillBufferCommandList;
+ /* 0x0AC */ LoadCommand mLoadCoammndArray[32];
+ /* 0x42C */ StrmBufferPool* mBufferPool;
+ /* 0x430 */ StrmCallback* mCallback;
+ /* 0x434 */ u32 mCallbackData;
+ /* 0x438 */ AxVoice* mVoice;
+ /* 0x43C */ f32 mExtVolume;
+ /* 0x440 */ f32 mExtPan;
+ /* 0x444 */ f32 mExtSurroundPan;
+ /* 0x448 */ f32 mExtPan2;
+ /* 0x44C */ f32 mExtSurroundPan2;
+ /* 0x450 */ f32 mExtPitch;
+ /* 0x454 */ f32 mExtLpfFreq;
+ /* 0x458 */ int mOutputLineFlag;
+ /* 0x45C */ f32 mMainOutVolume;
+ /* 0x460 */ f32 mMainSend;
+ /* 0x464 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x470 */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ /* 0x480 */ f32 mRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x490 */ f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0x4A0 */ s32 mChannelCount;
+ /* 0x4A4 */ s32 mVoiceOutCount;
+ /* 0x4A8 */ StrmChannel mChannels[CHANNEL_MAX];
+ /* 0x518 */ u16 mAdpcmPredScale[CHANNEL_MAX];
+ };
+
+ typedef ut::LinkList<StrmPlayer, offsetof(StrmPlayer, mPlayerLink)> StrmPlayerList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/StrmSound.h b/src/revolution/homebuttonLib/nw4hbm/snd/StrmSound.h
new file mode 100644
index 0000000000..537d1f8d06
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/StrmSound.h
@@ -0,0 +1,59 @@
+#ifndef NW4HBM_SND_STRM_SOUND_H
+#define NW4HBM_SND_STRM_SOUND_H
+
+#include <revolution/types.h>
+
+#include "BasicSound.h"
+#include "MoveValue.h"
+#include "StrmPlayer.h"
+#include "debug.h"
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class StrmSoundHandle;
+ namespace detail {
+ template <typename T>
+ class SoundInstanceManager;
+ }
+
+ namespace detail {
+
+ class StrmSound : public BasicSound {
+ friend class StrmSoundHandle;
+
+ public:
+ explicit StrmSound(SoundInstanceManager<StrmSound>* manager);
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x28 */ virtual void Shutdown();
+ /* 0x4C */ virtual void SetPlayerPriority(int priority);
+ /* 0x5C */ virtual bool IsAttachedTempSpecialHandle();
+ /* 0x60 */ virtual void DetachTempSpecialHandle();
+ /* 0x68 */ virtual BasicPlayer& GetBasicPlayer() { return mStrmPlayer; }
+ /* 0x6C */ virtual const BasicPlayer& GetBasicPlayer() const { return mStrmPlayer; }
+
+ void Setup(const StrmPlayer::StrmHeader* header);
+
+ bool Prepare(StrmBufferPool* bufferPool,
+ StrmPlayer::StartOffsetType startOffsetType, s32 startOffset,
+ int voices, StrmPlayer::StrmCallback* callback, u32 callbackData);
+
+ void SetChannelPriority(int priority);
+
+ private:
+ /* 0x0D8 */ StrmPlayer mStrmPlayer;
+ /* 0x1F4 */ StrmSoundHandle* mTempSpecialHandle;
+ /* 0x1F8 */ SoundInstanceManager<StrmSound>* mManager;
+ /* 0x5FC */ u8 mNumChannels;
+ /* 0x600 */ void* mBuffer[CHANNEL_MAX];
+ /* 0x608 */ u32 mBufSize;
+ /* 0x60C */ u8 mNumBufBlocks;
+ /* 0x610 */ int mAllocChannelCount;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/StrmSoundHandle.h b/src/revolution/homebuttonLib/nw4hbm/snd/StrmSoundHandle.h
new file mode 100644
index 0000000000..b7737b74a5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/StrmSoundHandle.h
@@ -0,0 +1,27 @@
+#ifndef NW4HBM_SND_STRM_SOUND_HANDLE_H
+#define NW4HBM_SND_STRM_SOUND_HANDLE_H
+
+#include <revolution/types.h>
+#include "../ut/inlines.h"
+#include "StrmSound.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+
+ class StrmSoundHandle : private ut::NonCopyable {
+ public:
+ ~StrmSoundHandle() { DetachSound(); }
+
+ void DetachSound();
+
+ bool IsAttachedSound() const { return mSound != NULL; }
+
+ private:
+ /* 0x00 */ detail::StrmSound* mSound;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/Task.h b/src/revolution/homebuttonLib/nw4hbm/snd/Task.h
new file mode 100644
index 0000000000..66850a6ae0
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/Task.h
@@ -0,0 +1,37 @@
+#ifndef NW4HBM_SND_TASK_H
+#define NW4HBM_SND_TASK_H
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class Task : private ut::NonCopyable {
+ friend class TaskManager;
+
+ public:
+ Task() : mTaskId(0) {}
+
+ Task(u32 taskId) : mTaskId(taskId) {}
+
+ /* 0x08 */ virtual ~Task() {}
+ /* 0x0C */ virtual void Execute() = 0;
+ /* 0x10 */ virtual void Cancel() = 0;
+
+ u32 GetTaskId() const { return mTaskId; }
+
+ public:
+ /* 0x04 */ ut::LinkListNode mTaskLink;
+
+ private:
+ /* 0x0C */ u32 mTaskId;
+ };
+
+ typedef ut::LinkList<Task, offsetof(Task, mTaskLink)> TaskList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/TaskManager.h b/src/revolution/homebuttonLib/nw4hbm/snd/TaskManager.h
new file mode 100644
index 0000000000..a74a025aab
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/TaskManager.h
@@ -0,0 +1,53 @@
+#ifndef NW4HBM_SND_TASK_MANAGER_H
+#define NW4HBM_SND_TASK_MANAGER_H
+
+#include "Task.h"
+
+#include <revolution/mem.h>
+
+#define TASK_NUM 128
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class TaskManager {
+ public:
+ typedef enum TaskPriority {
+ PRIORITY_LOW = 0,
+ PRIORITY_MIDDLE,
+ PRIORITY_HIGH,
+ PRIORITY_MAX
+ } TaskPriority;
+
+ public:
+ static TaskManager& GetInstance();
+
+ void AppendTask(Task* task, TaskPriority priority);
+ Task* PopTask(TaskPriority priority);
+
+ void Execute();
+ bool ExecuteSingle() NO_INLINE;
+
+ void CancelByTaskId(u32 taskId);
+
+ u32 GetTaskBufferSize();
+ void* Alloc();
+ void Free(void* ptr);
+
+ static u8 mTaskArea[0x2000 + 0x44];
+
+ private:
+ TaskManager();
+
+ /* 0x00 */ OSMutex mMutex;
+ /* 0x18 */ MEMHeapHandle mHeapHandle;
+ /* 0x1C */ Task* mCurrentTask;
+ /* 0x20 */ TaskList mTaskList[PRIORITY_MAX];
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/TaskThread.h b/src/revolution/homebuttonLib/nw4hbm/snd/TaskThread.h
new file mode 100644
index 0000000000..11b1db9f40
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/TaskThread.h
@@ -0,0 +1,47 @@
+#ifndef NW4HBM_SND_TASK_THREAD_H
+#define NW4HBM_SND_TASK_THREAD_H
+
+#include <revolution/os.h>
+#include "snd_types.h"
+
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class TaskThread {
+ public:
+ TaskThread() : mCreateFlag(false) {}
+
+ bool Create(s32 priority);
+ BOOL Destroy();
+
+ void SendWakeupMessage();
+
+ void ThreadProc();
+
+ static void* ThreadFunc(void* arg);
+
+ static TaskThread& GetInstance();
+
+ enum {
+ MSG_NONE = 0,
+ MSG_EXECUTE,
+ MSG_DONE,
+ };
+
+ private:
+ static const int MSG_QUEUE_CAPACITY = 8;
+
+ /* 0x0000 */ OSThread mThread;
+ /* 0x0318 */ u64 mThreadStack[THREAD_STACK_SIZE];
+ /* 0x2318 */ OSThreadQueue mThreadQueue;
+ /* 0x2320 */ OSMessageQueue mMsgQueue;
+ /* 0x2340 */ OSMessage mMsgBuffer[MSG_QUEUE_CAPACITY];
+ /* 0x2360 */ bool mCreateFlag;
+ /* 0x2361 */ u8 mPadding[3];
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/Util.h b/src/revolution/homebuttonLib/nw4hbm/snd/Util.h
new file mode 100644
index 0000000000..10823e909e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/Util.h
@@ -0,0 +1,118 @@
+#ifndef NW4HBM_SND_UTIL_H
+#define NW4HBM_SND_UTIL_H
+
+#include <revolution/types.h>
+
+#include "../db/assert.h"
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class Util {
+ public:
+ static const int SEMITONE_MAX = 12;
+ static const int MICROTONE_MAX = 256;
+
+ static const int VOLUME_MIN = static_cast<int>(10 * VOLUME_MIN_DB); // -90.4db
+ static const int VOLUME_MAX = static_cast<int>(10 * VOLUME_MAX_DB); // +6.0db
+
+ public:
+ typedef enum RefType {
+ REFTYPE_ADDRESS,
+ REFTYPE_OFFSET,
+ } RefType;
+
+ typedef enum DataType {
+ DATATYPE_T0,
+ DATATYPE_T1,
+ DATATYPE_T2,
+ DATATYPE_T3,
+ DATATYPE_INVALID
+ } DataType;
+
+ template <typename T0, typename T1 = void, typename T2 = void, typename T3 = void>
+ struct DataRef {
+ /* 0x00 */ u8 refType;
+ /* 0x01 */ u8 dataType;
+ /* 0x02 */ u16 reserved;
+ /* 0x04 */ u32 value;
+ };
+
+ template <typename T0, typename T1, typename T2, typename T3>
+ static inline const T0* GetDataRefAddress0(const DataRef<T0, T1, T2, T3>& ref,
+ const void* base) {
+ NW4HBM_ASSERT(111, ref.dataType == 0);
+ return static_cast<const T0*>(
+ GetDataRefAddressImpl(static_cast<RefType>(ref.refType), ref.value, base));
+ }
+
+ template <typename T0, typename T1, typename T2, typename T3>
+ static inline const T1* GetDataRefAddress1(const DataRef<T0, T1, T2, T3>& ref,
+ const void* base) {
+ NW4HBM_ASSERT(112, ref.dataType == 1);
+ return static_cast<const T1*>(
+ GetDataRefAddressImpl(static_cast<RefType>(ref.refType), ref.value, base));
+ }
+
+ template <typename T0, typename T1, typename T2, typename T3>
+ static inline const T2* GetDataRefAddress2(const DataRef<T0, T1, T2, T3>& ref,
+ const void* base) {
+ NW4HBM_ASSERT(113, ref.dataType == 2);
+ return static_cast<const T2*>(
+ GetDataRefAddressImpl(static_cast<RefType>(ref.refType), ref.value, base));
+ }
+
+ template <typename T0, typename T1, typename T2, typename T3>
+ static inline const T3* GetDataRefAddress3(const DataRef<T0, T1, T2, T3>& ref,
+ const void* base) {
+ NW4HBM_ASSERT(114, ref.dataType == 3);
+ return static_cast<const T3*>(
+ GetDataRefAddressImpl(static_cast<RefType>(ref.refType), ref.value, base));
+ }
+
+ template <typename T>
+ struct Table {
+ /* 0x00 */ u32 count;
+ /* 0x04 */ T items[1];
+ };
+
+ static inline u16 ReadBigEndian(u16 x) { return x; }
+ static inline u32 ReadBigEndian(u32 x) { return x; }
+
+ static f32 CalcPitchRatio(int pitch);
+ static f32 CalcVolumeRatio(f32 db);
+ static f32 CalcPanRatio(f32 pan);
+ static f32 CalcSurroundPanRatio(f32 surroundPan);
+ static int CalcLpfFreq(f32 scale);
+
+ static u16 CalcRandom();
+
+ private:
+ static const void* GetDataRefAddressImpl(RefType type, u32 value, const void* base);
+
+ private:
+ // Chromatic scale (tbl[idx] / tbl[idx-1] == ~1.06)
+ static const f32 NoteTable[SEMITONE_MAX];
+ // Each note contains 256 microtones
+ static const f32 PitchTable[MICROTONE_MAX];
+
+ // Table index is the millibel / hundredth-of-a-decibel
+ // dB(idx) = VOLUME_MIN_DB + (idx / 10)
+ // tbl[idx] = 10 ** (dB(idx) / 20)
+ static const int VOLUME_TABLE_SIZE = VOLUME_RANGE_MB + 1;
+ static const f32 Decibel2RatioTable[VOLUME_TABLE_SIZE];
+
+ // 1/256 step
+ static const int PAN_TABLE_MIN = 0;
+ static const int PAN_TABLE_MAX = 256;
+ static const int PAN_TABLE_CENTER = PAN_TABLE_MAX / 2;
+ static const int PAN_TABLE_SIZE = PAN_TABLE_MAX + 1;
+ // tbl[idx] = sqrt(1 - (idx / PAN_TABLE_MAX))
+ static const f32 Pan2RatioTable[PAN_TABLE_SIZE];
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WaveFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/WaveFile.h
new file mode 100644
index 0000000000..60b334686b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WaveFile.h
@@ -0,0 +1,81 @@
+#ifndef NW4HBM_SND_WAVE_FILE_H
+#define NW4HBM_SND_WAVE_FILE_H
+
+#include "AxVoice.h"
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ namespace WaveFile {
+ typedef enum Format { FORMAT_PCM8 = 0, FORMAT_PCM16, FORMAT_ADPCM } Format;
+
+ typedef struct WaveInfo {
+ /* 0x00 */ u8 format;
+ /* 0x01 */ u8 loopFlag;
+ /* 0x02 */ u8 numChannels;
+ /* 0x03 */ u8 sampleRate24;
+ /* 0x04 */ u16 sampleRate;
+ /* 0x06 */ u16 padding2;
+ /* 0x08 */ u32 loopStart;
+ /* 0x0C */ u32 loopEnd;
+ /* 0x10 */ u32 channelInfoTableOffset;
+ /* 0x14 */ u32 dataOffset;
+ /* 0x18 */ u32 reserved;
+ } WaveInfo;
+
+ typedef struct WaveChannelInfo {
+ /* 0x00 */ u32 channelDataOffset;
+ /* 0x04 */ u32 adpcmOffset;
+ /* 0x08 */ u32 volumeFrontLeft;
+ /* 0x0C */ u32 volumeFrontRight;
+ /* 0x10 */ u32 volumeRearLeft;
+ /* 0x14 */ u32 volumeRearRight;
+ /* 0x18 */ u32 reserved;
+ } WaveChannelInfo;
+ } // namespace WaveFile
+
+ typedef struct ChannelParam {
+ /* 0x00 */ void* dataAddr;
+ /* 0x04 */ u32 volumeFrontLeft;
+ /* 0x08 */ u32 volumeFrontRight;
+ /* 0x0C */ u32 volumeRearLeft;
+ /* 0x10 */ u32 volumeRearRight;
+ /* 0x14 */ AdpcmInfo adpcmInfo;
+ } ChannelParam;
+
+ typedef struct WaveData {
+ /* 0x00 */ u8 format;
+ /* 0x01 */ u8 loopFlag;
+ /* 0x02 */ u8 numChannels;
+ /* 0x04 */ int sampleRate;
+ /* 0x08 */ u32 loopStart;
+ /* 0x0C */ u32 loopEnd;
+ /* 0x10 */ ChannelParam channelParam[CHANNEL_MAX];
+ } WaveData;
+
+ class WaveFileReader {
+ public:
+ explicit WaveFileReader(const WaveFile::WaveInfo* waveInfo);
+
+ bool ReadWaveParam(WaveData* waveData, const void* waveAddr) const;
+ static AxVoice::Format WaveFormatToAxFormat(u32 format);
+
+ private:
+ /* 0x00 */ const WaveFile::WaveInfo* mWaveInfo;
+ };
+
+ inline AxVoice::Format WaveFormatToAxFormat(u32 format) {
+ if (format == WaveFile::FORMAT_PCM16) {
+ return AxVoice::FORMAT_PCM16;
+ }
+ if (format == WaveFile::FORMAT_PCM8) {
+ return AxVoice::FORMAT_PCM8;
+ }
+ return AxVoice::FORMAT_ADPCM;
+ }
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WavePlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/WavePlayer.h
new file mode 100644
index 0000000000..23110908b0
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WavePlayer.h
@@ -0,0 +1,127 @@
+#ifndef NW4HBM_SND_WAVE_PLAYER_H
+#define NW4HBM_SND_WAVE_PLAYER_H
+
+#include "snd_types.h"
+
+#include "AxVoice.h"
+
+#include "../ut/LinkList.h"
+
+#include <revolution/wpad.h>
+
+namespace nw4hbm {
+ namespace snd {
+
+ namespace detail {
+ class SoundThread;
+ }
+
+ class WavePlayer {
+ public:
+ friend class detail::SoundThread;
+ typedef struct WaveBufferInfo {
+ int channelCount;
+ void* bufferAddress[CHANNEL_MAX];
+ u32 bufferSize;
+ } WaveBufferInfo;
+
+ class WavePacket {
+ WavePacket();
+ virtual ~WavePacket() {}
+
+ private:
+ /* 0x04 */ WaveBufferInfo mWaveBuffer;
+ /* 0x14 */ bool mAppendFlag;
+
+ public:
+ /* 0x18 */ ut::LinkListNode mLinkNode;
+
+ friend class WavePlayer;
+ };
+ typedef ut::LinkList<WavePacket, offsetof(WavePacket, mLinkNode)> WavePacketList;
+
+ typedef enum WavePacketCallbackStatus {
+ WAVE_PACKET_CALLBACK_STATUS_FINISH = 0,
+ WAVE_PACKET_CALLBACK_STATUS_CANCEL,
+ } WavePacketCallbackStatus;
+
+ typedef void (*WavePacketCallback)(WavePacketCallbackStatus, WavePlayer*, WavePacket*,
+ void*);
+
+ typedef struct SetupParam {
+ /* 0x00 */ int channelCount;
+ /* 0x04 */ SampleFormat sampleFormat;
+ /* 0x08 */ int sampleRate;
+ /* 0x0C */ f32 pitchMax;
+ /* 0x10 */ int voiceCount;
+ } SetupParam;
+
+ WavePlayer();
+ virtual ~WavePlayer();
+
+ void InitParam();
+ bool Setup(const SetupParam& setupParam, WavePacketCallback callback,
+ void* callbackArg);
+
+ bool AppendWavePacket(WavePacket* packet);
+
+ void Start();
+ void Stop();
+ void Pause(bool flag);
+
+ void Shutdown();
+
+ void StartVoice();
+ void StopVoice();
+
+ bool IsNextWavePacket() const;
+ void SetNextWavePacket();
+ void UpdateWavePacket();
+
+ private:
+ void detail_Update();
+ void detail_UpdateBuffer();
+
+ static void detail_UpdateAllPlayers();
+ static void detail_UpdateBufferAllPlayers();
+ static void detail_StopAllPlayers();
+
+ static void VoiceCallbackFunc(detail::AxVoice* voice,
+ detail::AxVoice::CallbackStatus status, void* arg);
+
+ /* 0x04 */ WavePacketList mWavePacketList;
+ /* 0x10 */ int mChannelCount;
+ /* 0x14 */ f32 mPitchMax;
+ /* 0x18 */ detail::AxVoice* mVoice;
+ /* 0x1C */ bool mStartFlag;
+ /* 0x1D */ bool mVoiceStartFlag;
+ /* 0x1E */ bool mLoopSetFlag;
+ /* 0x1F */ bool mPauseFlag;
+ /* 0x20 */ SampleFormat mSampleFormat;
+ /* 0x24 */ int mSampleRate;
+ /* 0x28 */ s64 mPlaySampleCount;
+ /* 0x30 */ f32 mVolume;
+ /* 0x34 */ f32 mPan;
+ /* 0x38 */ f32 mSurroundPan;
+ /* 0x3C */ f32 mPitch;
+ /* 0x40 */ f32 mLpfFreq;
+ /* 0x44 */ int mOutputLineFlag;
+ /* 0x48 */ f32 mMainOutVolume;
+ /* 0x4C */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ /* 0x5C */ f32 mMainSend;
+ /* 0x60 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x6C */ f32 mRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x7C */ f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0x8C */ WavePacketCallback mCallback;
+ /* 0x90 */ void* mCallbackArg;
+
+ public:
+ /* 0x94 */ ut::LinkListNode mPlayerLink;
+ };
+
+ typedef ut::LinkList<WavePlayer, offsetof(WavePlayer, mPlayerLink)> WavePlayerList;
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WaveSound.h b/src/revolution/homebuttonLib/nw4hbm/snd/WaveSound.h
new file mode 100644
index 0000000000..17a4c5a252
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WaveSound.h
@@ -0,0 +1,49 @@
+#ifndef NW4HBM_SND_WAVE_SOUND_H
+#define NW4HBM_SND_WAVE_SOUND_H
+
+#include <revolution/types.h>
+
+#include "BasicSound.h"
+#include "WsdPlayer.h"
+#include "debug.h"
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class WaveSoundHandle;
+ namespace detail {
+ template <typename T>
+ class SoundInstanceManager;
+
+ class WaveSound : public BasicSound {
+ friend class WaveSoundHandle;
+
+ public:
+ explicit WaveSound(SoundInstanceManager<WaveSound>* manager);
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x28 */ virtual void Shutdown();
+ /* 0x4C */ virtual void SetPlayerPriority(int priority);
+ /* 0x5C */ virtual bool IsAttachedTempSpecialHandle();
+ /* 0x60 */ virtual void DetachTempSpecialHandle();
+ /* 0x68 */ virtual BasicPlayer& GetBasicPlayer() { return mWsdPlayer; }
+ /* 0x6C */ virtual const BasicPlayer& GetBasicPlayer() const { return mWsdPlayer; }
+
+ bool Prepare(const void* waveSoundBase, s32 waveSoundOffset, int voices,
+ const WsdTrack::WsdCallback* callback, u32 callbackData);
+
+ void SetChannelPriority(int priority);
+ void SetReleasePriorityFix(bool flag);
+
+ private:
+ /* 0x0D8 */ WsdPlayer mWsdPlayer;
+ /* 0x1B0 */ WaveSoundHandle* mTempSpecialHandle;
+ /* 0x1B4 */ SoundInstanceManager<WaveSound>* mManager;
+ };
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WaveSoundHandle.h b/src/revolution/homebuttonLib/nw4hbm/snd/WaveSoundHandle.h
new file mode 100644
index 0000000000..ba0a8bcd3a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WaveSoundHandle.h
@@ -0,0 +1,25 @@
+#ifndef NW4HBM_SND_WAVE_SOUND_HANDLE_H
+#define NW4HBM_SND_WAVE_SOUND_HANDLE_H
+
+#include "WaveSound.h"
+#include "snd_types.h"
+
+namespace nw4hbm {
+ namespace snd {
+
+ class WaveSoundHandle : private ut::NonCopyable {
+ public:
+ ~WaveSoundHandle() { DetachSound(); }
+
+ void DetachSound();
+
+ bool IsAttachedSound() const { return mSound != NULL; }
+
+ private:
+ /* 0x00 */ detail::WaveSound* mSound;
+ };
+
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WsdFile.h b/src/revolution/homebuttonLib/nw4hbm/snd/WsdFile.h
new file mode 100644
index 0000000000..8554e5cf8e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WsdFile.h
@@ -0,0 +1,143 @@
+#ifndef NW4HBM_SND_WSD_FILE_H
+#define NW4HBM_SND_WSD_FILE_H
+
+#include <revolution/types.h>
+
+#include "Util.h"
+#include "WaveFile.h"
+#include "snd_types.h"
+
+#include "../ut/binaryFileFormat.h"
+
+#include <revolution/hbm.h>
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ namespace WsdFile {
+
+ static const u32 SIGNATURE_FILE = 'RWSD';
+ static const u32 SIGNATURE_DATA_BLOCK = 'DATA';
+ static const u32 SIGNATURE_WAVE_BLOCK = 'WAVE';
+ static const int FILE_VERSION = NW4HBM_VERSION(1, 2);
+
+ typedef struct WsdInfo {
+ /* 0x00 */ f32 pitch;
+ /* 0x04 */ u8 pan;
+ /* 0x05 */ u8 surroundPan;
+ /* 0x06 */ u8 fxSendA;
+ /* 0x07 */ u8 fxSendB;
+ /* 0x08 */ u8 fxSendC;
+ /* 0x09 */ u8 mainSend;
+ /* 0x0A */ u8 padding[2];
+ /* 0x0C */ Util::DataRef<void> graphEnvTablevRef;
+ /* 0x14 */ Util::DataRef<void> randomizerTableRef;
+ /* 0x1C */ u32 reserved;
+ } WsdInfo;
+
+ typedef struct TrackInfo {
+ // Nothing
+ } TrackInfo;
+
+ typedef struct NoteInfo {
+ /* 0x00 */ s32 waveIndex;
+ /* 0x04 */ u8 attack;
+ /* 0x05 */ u8 decay;
+ /* 0x06 */ u8 sustain;
+ /* 0x07 */ u8 release;
+ /* 0x08 */ u16 hold;
+ /* 0x0A */ u16 padding;
+ /* 0x0C */ u8 originalKey;
+ /* 0x0D */ u8 volume;
+ /* 0x0E */ u8 pan;
+ /* 0x0F */ u8 surroundPan;
+ /* 0x10 */ f32 pitch;
+ /* 0x14 */ Util::DataRef<void> lfoTableRef;
+ /* 0x1C */ Util::DataRef<void> graphEnvTablevRef;
+ /* 0x24 */ Util::DataRef<void> randomizerTableRef;
+ /* 0x2C */ u32 reserved;
+ } NoteInfo;
+
+ typedef Util::DataRef<TrackInfo> TrackInfoRef;
+ typedef Util::Table<TrackInfoRef> TrackTable;
+
+ typedef Util::DataRef<NoteInfo> NoteInfoRef;
+ typedef Util::Table<NoteInfoRef> NoteTable;
+
+ typedef struct Wsd {
+ /* 0x00 */ Util::DataRef<WsdInfo> refWsdInfo;
+ /* 0x08 */ Util::DataRef<TrackTable> refTrackTable;
+ /* 0x10 */ Util::DataRef<NoteTable> refNoteTable;
+ } Wsd;
+
+ typedef struct Header {
+ /* 0x00 */ ut::BinaryFileHeader fileHeader;
+ /* 0x10 */ u32 dataBlockOffset;
+ /* 0x14 */ u32 dataBlockSize;
+ /* 0x18 */ u32 waveBlockOffset;
+ /* 0x1C */ u32 waveBlockSize;
+ } Header;
+
+ typedef struct DataBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ u32 wsdCount;
+ /* 0x0C */ Util::DataRef<Wsd> refWsd[];
+ } DataBlock;
+
+ typedef struct WaveBlock {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ u32 waveCount;
+ /* 0x0C */ u32 offsetTable[];
+ } WaveBlock;
+
+ // <= NW4HBM_VERSION(1, 0)
+ typedef struct WaveBlockOld {
+ /* 0x00 */ ut::BinaryBlockHeader blockHeader;
+ /* 0x08 */ u32 offsetTable[];
+ } WaveBlockOld;
+ } // namespace WsdFile
+
+ typedef struct WaveSoundInfo {
+ /* 0x00 */ f32 pitch;
+ /* 0x04 */ u8 pan;
+ /* 0x05 */ u8 surroundPan;
+ /* 0x06 */ u8 fxSendA;
+ /* 0x07 */ u8 fxSendB;
+ /* 0x08 */ u8 fxSendC;
+ /* 0x09 */ u8 mainSend;
+ } WaveSoundInfo;
+
+ typedef struct WaveSoundNoteInfo {
+ /* 0x00 */ s32 waveIndex;
+ /* 0x04 */ u8 attack;
+ /* 0x06 */ u8 decay;
+ /* 0x07 */ u8 sustain;
+ /* 0x08 */ u8 release;
+ /* 0x09 */ u8 originalKey;
+ /* 0x0A */ u8 pan;
+ /* 0x0B */ u8 surroundPan;
+ /* 0x0C */ u8 volume;
+ /* 0x10 */ f32 pitch;
+ } WaveSoundNoteInfo;
+
+ class WsdFileReader {
+ public:
+ explicit WsdFileReader(const void* waveData);
+
+ bool IsValidFileHeader(const void* waveData);
+
+ bool ReadWaveSoundInfo(WaveSoundInfo* soundInfo, int id) const;
+ bool ReadWaveSoundNoteInfo(WaveSoundNoteInfo* soundNoteInfo, int id,
+ int note) const;
+ bool ReadWaveParam(int id, WaveData* waveData, const void* waveAddr) const;
+
+ private:
+ /* 0x00 */ const WsdFile::Header* mHeader;
+ /* 0x04 */ const WsdFile::DataBlock* mDataBlock;
+ /* 0x08 */ const WsdFile::WaveBlock* mWaveBlock;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WsdPlayer.h b/src/revolution/homebuttonLib/nw4hbm/snd/WsdPlayer.h
new file mode 100644
index 0000000000..aa2a8892ff
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WsdPlayer.h
@@ -0,0 +1,116 @@
+#ifndef NW4HBM_SND_WSD_PLAYER_H
+#define NW4HBM_SND_WSD_PLAYER_H
+
+#include <revolution/types.h>
+
+#include "BasicPlayer.h"
+#include "WsdTrack.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+
+ class WsdPlayer : public BasicPlayer, public DisposeCallback {
+ public:
+ WsdPlayer();
+
+ void InitParam(int voices, const WsdTrack::WsdCallback* callback, u32 callbackData);
+
+ bool Prepare(const void* waveSoundBase, int index, int voices,
+ const WsdTrack::WsdCallback* callback, u32 callbackData);
+
+ /* 0x0C */ virtual bool Start();
+ /* 0x10 */ virtual void Stop();
+ /* 0x14 */ virtual void Pause(bool flag);
+ /* 0x18 */ virtual bool IsActive() const { return mActiveFlag; }
+ /* 0x20 */ virtual bool IsStarted() const { return mStartedFlag; };
+ /* 0x1C */ virtual bool IsPrepared() const { return mPreparedFlag; };
+ /* 0x24 */ virtual bool IsPause() const { return mPauseFlag; };
+ /* 0x28 */ virtual void SetVolume(f32 volume);
+ /* 0x2C */ virtual void SetPitch(f32 pitch);
+ /* 0x30 */ virtual void SetPan(f32 pan);
+ /* 0x34 */ virtual void SetSurroundPan(f32 surroundPan);
+ /* 0x38 */ virtual void SetPan2(f32 pan2);
+ /* 0x3C */ virtual void SetSurroundPan2(f32 surroundPan2);
+ /* 0x40 */ virtual void SetLpfFreq(f32 lpfFreq);
+ /* 0x44 */ virtual f32 GetVolume() const { return mExtVolume; };
+ /* 0x48 */ virtual f32 GetPitch() const { return mExtPitch; };
+ /* 0x4C */ virtual f32 GetPan() const { return mExtPan; };
+ /* 0x50 */ virtual f32 GetSurroundPan() const { return mExtSurroundPan; };
+ /* 0x54 */ virtual f32 GetPan2() const { return mExtPan2; };
+ /* 0x58 */ virtual f32 GetSurroundPan2() const { return mExtSurroundPan2; };
+ /* 0x5C */ virtual f32 GetLpfFreq() const { return mExtLpfFreq; };
+ /* 0x60 */ virtual void SetOutputLine(int lineFlag);
+ /* 0x64 */ virtual void SetMainOutVolume(f32 volume);
+ /* 0x68 */ virtual void SetMainSend(f32 send);
+ /* 0x6C */ virtual void SetFxSend(AuxBus bus, f32 send);
+ /* 0x70 */ virtual void SetRemoteOutVolume(int remoteIndex, f32 volume);
+ /* 0x74 */ virtual void SetRemoteSend(int remoteIndex, f32 send);
+ /* 0x78 */ virtual void SetRemoteFxSend(int remoteIndex, f32 send);
+ /* 0x7C */ virtual int GetOutputLine() const;
+ /* 0x80 */ virtual f32 GetMainOutVolume() const;
+ /* 0x84 */ virtual f32 GetMainSend() const;
+ /* 0x88 */ virtual f32 GetFxSend(AuxBus bus) const;
+ /* 0x8C */ virtual f32 GetRemoteOutVolume(int remoteIndex) const;
+ /* 0x90 */ virtual f32 GetRemoteSend(int remoteIndex) const;
+ /* 0x94 */ virtual f32 GetRemoteFxSend(int remoteIndex) const;
+
+ void SetChannelPriority(int prio);
+ u8 GetChannelPriority() { return mPriority; }
+
+ f32 GetPanRange() { return mPanRange; }
+
+ int GetVoiceOutCount() { return mVoiceOutCount; }
+
+ void FinishPlayer();
+
+ BOOL ParseNextTick(bool doNoteOn);
+
+ void UpdateChannel();
+ void Update();
+
+ static void UpdateAllPlayers();
+ static void StopAllPlayers();
+
+ virtual void InvalidateData(const void* start, const void* end);
+ virtual void InvalidateWaveData(const void* start, const void* end) {}
+
+ /* 0x14 */ ut::LinkListNode mPlayerLink;
+
+ private:
+ /* 0x1C */ bool mHomeButtonMenuFlag;
+ /* 0x1D */ u8 mActiveFlag;
+ /* 0x1E */ u8 mPreparedFlag;
+ /* 0x1F */ u8 mStartedFlag;
+ /* 0x20 */ u8 mPauseFlag;
+ /* 0x21 */ u8 mSkipFlag;
+ /* 0x24 */ f32 mExtVolume;
+ /* 0x28 */ f32 mExtPan;
+ /* 0x2C */ f32 mExtSurroundPan;
+ /* 0x30 */ f32 mPanRange;
+ /* 0x34 */ f32 mExtPan2;
+ /* 0x38 */ f32 mExtSurroundPan2;
+ /* 0x3C */ f32 mExtPitch;
+ /* 0x40 */ f32 mExtLpfFreq;
+ /* 0x44 */ int mOutputLineFlag;
+ /* 0x48 */ f32 mMainOutVolume;
+ /* 0x4C */ f32 mMainSend;
+ /* 0x50 */ f32 mFxSend[AUX_BUS_NUM];
+ /* 0x5C */ f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS];
+ /* 0x6C */ f32 mRemoteSend[WPAD_MAX_CONTROLLERS];
+ /* 0x7C */ f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS];
+ /* 0x8C */ int mVoiceOutCount;
+ /* 0x90 */ u8 mPriority;
+ /* 0x94 */ const WsdTrack::WsdCallback* mCallback;
+ /* 0x98 */ u32 mCallbackData;
+ /* 0x9C */ WsdTrack mTrack;
+ /* 0xD0 */ u32 mTickCounter;
+ };
+
+ typedef ut::LinkList<WsdPlayer, offsetof(WsdPlayer, mPlayerLink)> WsdPlayerList;
+
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/WsdTrack.h b/src/revolution/homebuttonLib/nw4hbm/snd/WsdTrack.h
new file mode 100644
index 0000000000..94a5846ca2
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/WsdTrack.h
@@ -0,0 +1,69 @@
+#ifndef NW4HBM_SND_WSD_TRACK_H
+#define NW4HBM_SND_WSD_TRACK_H
+
+#include "snd_types.h"
+
+#include "Channel.h"
+#include "Lfo.h"
+#include "WsdFile.h"
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ class WsdPlayer;
+ class WsdTrack {
+ public:
+ class WsdCallback {
+ public:
+ /* 0x08 */ virtual ~WsdCallback() = 0 {}
+
+ /* 0x0C */ virtual bool GetWaveSoundData(WaveSoundInfo* info,
+ WaveSoundNoteInfo* noteInfo,
+ WaveData* waveData,
+ const void* waveSoundData, int index,
+ int noteIndex, u32 userData) const = 0;
+ };
+
+ typedef enum StartOffsetType {
+ START_OFFSET_TYPE_SAMPLE = 0,
+ START_OFFSET_TYPE_MILLISEC
+ } StartOffsetType;
+
+ WsdTrack() : mWsdPlayer(NULL) {}
+
+ void Init(WsdPlayer* player);
+
+ void Start(const void* wsdData, int index);
+ void Close();
+
+ void UpdateChannel();
+ void ReleaseAllChannel(int release) NO_INLINE;
+ void PauseAllChannel(bool flag);
+ void FreeAllChannel();
+ void AddChannel(Channel* channel);
+
+ int Parse(const WsdCallback* callback, u32 callbackData, bool doNoteOn);
+ int ParseNextTick(const WsdCallback* callback, u32 callbackData, bool doNoteOn);
+
+ static void ChannelCallbackFunc(Channel* dropChannel,
+ Channel::ChannelCallbackStatus status,
+ u32 userData);
+
+ const void* GetWsdDataAddress() const { return mWsdData; }
+
+ private:
+ /* 0x00 */ const void* mWsdData;
+ /* 0x04 */ int mIndex;
+ /* 0x08 */ u32 mCounter;
+ /* 0x0C */ LfoParam mLfoParam;
+ /* 0x1C */ u8 mBendRange;
+ /* 0x1D */ u8 mPriority;
+ /* 0x20 */ WaveSoundInfo mWaveSoundInfo;
+ /* 0x2C */ WsdPlayer* mWsdPlayer;
+ /* 0x30 */ Channel* mChannelList;
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/debug.h b/src/revolution/homebuttonLib/nw4hbm/snd/debug.h
new file mode 100644
index 0000000000..ec3e43277b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/debug.h
@@ -0,0 +1,37 @@
+#ifndef NW4HBM_SND_DEBUG_H
+#define NW4HBM_SND_DEBUG_H
+
+namespace nw4hbm {
+ namespace snd {
+
+ enum DebugWarningFlag {
+ DEBUG_WARNING_NOT_ENOUGH_INSTANCE,
+ DEBUG_WARNING_NOT_ENOUGH_SEQSOUND,
+ DEBUG_WARNING_NOT_ENOUGH_STRMSOUND,
+ DEBUG_WARNING_NOT_ENOUGH_WAVESOUND,
+ DEBUG_WARNING_NOT_ENOUGH_SEQTRACK,
+ DEBUG_WARNING_NOT_ENOUGH_STRMCHANNEL,
+ };
+
+ namespace detail {
+
+ enum DebugSoundType {
+ DEBUG_SOUND_TYPE_SEQSOUND,
+ DEBUG_SOUND_TYPE_STRMSOUND,
+ DEBUG_SOUND_TYPE_WAVESOUND,
+ };
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+namespace nw4hbm {
+ namespace snd {
+ namespace detail {
+ bool Debug_GetWarningFlag(DebugWarningFlag warning);
+ DebugWarningFlag Debug_GetDebugWarningFlagFromSoundType(DebugSoundType type);
+ char const* Debug_GetSoundTypeString(DebugSoundType type);
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp
new file mode 100644
index 0000000000..f4a52fefc9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundArchivePlayer.cpp
@@ -0,0 +1,14 @@
+
+#include "SoundArchivePlayer.h"
+#include "SoundPlayer.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ SoundPlayer& SoundArchivePlayer::GetSoundPlayer(u32 playerId) {
+ NW4R_ASSERT_MINMAX(690, playerId, 0, mSoundPlayerCount);
+ return mSoundPlayers[playerId];
+ }
+ }
+}
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp
new file mode 100644
index 0000000000..82b5c7e44a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundHandle.cpp
@@ -0,0 +1,24 @@
+#include "BasicSound.h"
+#include "SoundHandle.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ void SoundHandle::DetachSound() {
+ if (IsAttachedSound()) {
+ if (mSound->mGeneralHandle == this) {
+ mSound->mGeneralHandle = NULL;
+ }
+
+ if (mSound->mTempGeneralHandle == this) {
+ mSound->mTempGeneralHandle = NULL;
+ }
+ }
+
+ if (mSound) {
+ mSound = NULL;
+ }
+ }
+ } // namespace snd
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp
new file mode 100644
index 0000000000..1e9e355558
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundPlayer.cpp
@@ -0,0 +1,21 @@
+#include "SoundPlayer.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ void SoundPlayer::StopAllSound(int frames) {
+ for (detail::BasicSoundPlayerPlayList::Iterator it = mSoundList.GetBeginIter();
+ it != mSoundList.GetEndIter();)
+ {
+ detail::BasicSoundPlayerPlayList::Iterator currIt = it++;
+ currIt->Stop(frames);
+ }
+ }
+
+ void SoundPlayer::SetVolume(f32 volume) {
+ NW4HBM_ASSERT(153, volume >= 0.0f);
+ mVolume = volume;
+ }
+ } // namespace snd
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp
new file mode 100644
index 0000000000..9315e8a0e5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_SoundStartable.cpp
@@ -0,0 +1,22 @@
+#include "SoundHandle.h"
+#include "SoundStartable.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace snd {
+ SoundStartable::StartResult SoundStartable::detail_StartSound(
+ SoundHandle* soundHandle, u32 id, detail::BasicSound::AmbientArgInfo* ambientArgInfo,
+ detail::ExternalSoundPlayer* extPlayer, const StartInfo* pStartInfo) {
+ StartResult result =
+ detail_SetupSound(soundHandle, id, ambientArgInfo, extPlayer, false, pStartInfo);
+
+ if (result != START_SUCCESS) {
+ return result;
+ }
+
+ soundHandle->StartPrepared();
+ return START_SUCCESS;
+ }
+ } // namespace snd
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_global.h b/src/revolution/homebuttonLib/nw4hbm/snd/snd_global.h
new file mode 100644
index 0000000000..6873c2b4a1
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_global.h
@@ -0,0 +1,45 @@
+#ifndef NW4HBM_SND_GLOBAL_H
+#define NW4HBM_SND_GLOBAL_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace snd {
+
+ enum PanMode {
+ PAN_MODE_DUAL,
+ PAN_MODE_BALANCE,
+ };
+
+ enum PanCurve {
+ PAN_CURVE_SQRT,
+ PAN_CURVE_SQRT_0DB,
+ PAN_CURVE_SQRT_0DB_CLAMP,
+
+ PAN_CURVE_SINCOS,
+ PAN_CURVE_SINCOS_0DB,
+ PAN_CURVE_SINCOS_0DB_CLAMP,
+
+ PAN_CURVE_LINEAR,
+ PAN_CURVE_LINEAR_0DB,
+ PAN_CURVE_LINEAR_0DB_CLAMP,
+ };
+
+ struct VoiceOutParam {
+ public:
+ VoiceOutParam()
+ : volume(1.0f), pitch(1.0f), pan(0.0f), surroundPan(0.0f), fxSend(0.0f), lpf(0.0f) {
+ }
+
+ public:
+ /* 0x00 */ f32 volume;
+ /* 0x04 */ f32 pitch;
+ /* 0x08 */ f32 pan;
+ /* 0x0C */ f32 surroundPan;
+ /* 0x10 */ f32 fxSend;
+ /* 0x14 */ f32 lpf;
+ }; // size = 0x18
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/snd/snd_types.h b/src/revolution/homebuttonLib/nw4hbm/snd/snd_types.h
new file mode 100644
index 0000000000..f167089375
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/snd/snd_types.h
@@ -0,0 +1,121 @@
+#ifndef NW4HBM_SND_TYPES_H
+#define NW4HBM_SND_TYPES_H
+
+#include <revolution/types.h>
+
+#include "../ut/LinkList.h"
+
+namespace nw4hbm {
+ namespace snd {
+ class LinkedInstance {
+ public:
+ /* 0x00 */ ut::LinkListNode mInstanceLink;
+ };
+ template <typename T, int Ofs>
+ class InstanceManager {
+ public:
+ typedef typename ut::LinkList<T, Ofs>::Iterator Iterator;
+ void Append(T* obj) { mFreeList.PushBack(obj); }
+ void Remove(T* obj) { mFreeList.Erase(obj); }
+ T* Alloc() {
+ if (mFreeList.IsEmpty()) {
+ return NULL;
+ } else {
+ T& obj = mFreeList.GetFront();
+ mFreeList.PopFront();
+ mActiveList.PushBack(&obj);
+ return &obj;
+ }
+ }
+ void Free(T* obj) {
+ if (!mActiveList.IsEmpty()) {
+ mActiveList.Erase(obj);
+ mFreeList.PushBack(obj);
+ }
+ }
+
+ Iterator GetBeginIter() { return mActiveList.GetBeginIter(); }
+ Iterator GetEndIter() { return mActiveList.GetEndIter(); }
+
+ private:
+ /* 0x00 */ ut::LinkList<T, Ofs> mFreeList;
+ /* 0x0C */ ut::LinkList<T, Ofs> mActiveList;
+ };
+
+ static const int THREAD_STACK_SIZE = 1024;
+ static const int STREAM_BUFFER_SIZE = 512;
+
+ static const int CHANNEL_MAX = 2;
+
+ static const int VOICE_MAX = 4;
+
+ static const int PRIORITY_MAX = 255;
+
+ static const int REMOTE_FILTER_MAX = 127;
+
+ static const f32 VOLUME_MIN_DB = -90.4f;
+ static const f32 VOLUME_MAX_DB = 6.0f;
+ static const f32 VOLUME_RANGE_DB = -(VOLUME_MIN_DB - VOLUME_MAX_DB);
+ static const int VOLUME_RANGE_MB = static_cast<int>(10 * VOLUME_RANGE_DB);
+
+ typedef enum OutputLineFlag {
+ OUTPUT_LINE_MAIN = (1 << 0),
+ OUTPUT_LINE_REMOTE_N = (1 << 1),
+ } OutputLineFlag;
+
+ typedef enum OutputMode {
+ OUTPUT_MODE_STEREO = 0,
+ OUTPUT_MODE_SURROUND,
+ OUTPUT_MODE_DPL2,
+ OUTPUT_MODE_MONO
+ } OutputMode;
+
+ typedef enum AuxBus { AUX_A = 0, AUX_B, AUX_C, AUX_BUS_NUM } AuxBus;
+
+ typedef enum SampleFormat {
+ SAMPLE_FORMAT_PCM_S32 = 0,
+ SAMPLE_FORMAT_PCM_S16,
+ SAMPLE_FORMAT_PCM_S8,
+ SAMPLE_FORMAT_DSP_ADPCM
+ } SampleFormat;
+
+ typedef struct SoundParam {
+ /* 0x00 */ f32 volume;
+ /* 0x04 */ f32 pitch;
+ /* 0x08 */ f32 pan;
+ /* 0x0C */ f32 surroundPan;
+ /* 0x10 */ f32 fxSend;
+ /* 0x14 */ f32 lpf;
+ /* 0x18 */ s32 priority;
+ } SoundParam;
+
+ namespace detail {
+ typedef struct AdpcmParam {
+ /* 0x00 */ u16 coef[16];
+ /* 0x20 */ u16 gain;
+ /* 0x22 */ u16 pred_scale;
+ /* 0x24 */ u16 yn1;
+ /* 0x26 */ u16 yn2;
+ } AdpcmParam;
+
+ typedef struct AdpcmLoopParam {
+ /* 0x00 */ u16 loop_pred_scale;
+ /* 0x02 */ u16 loop_yn1;
+ /* 0x04 */ u16 loop_yn2;
+ } AdpcmLoopParam;
+
+ typedef struct AdpcmInfo {
+ /* 0x08 */ AdpcmParam adpcm;
+ /* 0x28 */ AdpcmLoopParam adpcmloop;
+ /* 0x2E */ u16 padding;
+ } AdpcmInfo;
+
+ typedef struct VoiceChannelParam {
+ /* 0x00 */ void* waveData;
+ /* 0x04 */ AdpcmInfo adpcmInfo;
+ } VoiceChannelParam;
+ } // namespace detail
+ } // namespace snd
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/CharStrmReader.h b/src/revolution/homebuttonLib/nw4hbm/ut/CharStrmReader.h
new file mode 100644
index 0000000000..6c66ad82c7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/CharStrmReader.h
@@ -0,0 +1,71 @@
+#ifndef NW4HBM_UT_CHAR_STREAM_READER_H
+#define NW4HBM_UT_CHAR_STREAM_READER_H
+
+// required to fix data order in `lyt_textBox.cpp`
+#include "../lyt/textBox.h"
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+ class CharStrmReader {
+ public:
+ typedef u16 (CharStrmReader::*ReadFunc)();
+
+ CharStrmReader(ReadFunc func) : mCharStrm(NULL), mReadFunc(func) {}
+
+ void Set(const char* stream) {
+ NW4HBM_ASSERT_VALID_PTR(49, this);
+ NW4HBM_ASSERT_VALID_PTR(50, stream);
+ NW4HBM_ASSERT(53, mReadFunc == ReadNextCharUTF8 || mReadFunc == ReadNextCharCP1252 || mReadFunc == ReadNextCharSJIS);
+ mCharStrm = stream;
+ }
+
+ void Set(const wchar_t* stream) {
+ NW4HBM_ASSERT_VALID_PTR(59, this);
+ NW4HBM_ASSERT_ALIGN2(60, stream);
+ NW4HBM_ASSERT_VALID_PTR(61, stream);
+ NW4HBM_ASSERT(62, mReadFunc == ReadNextCharUTF16);
+ mCharStrm = stream;
+ }
+
+ const void* GetCurrentPos() const {
+ NW4HBM_ASSERT_VALID_PTR(68, this);
+ return mCharStrm;
+ }
+
+ u16 Next() {
+ NW4HBM_ASSERT_VALID_PTR(74, this);
+ return (this->*mReadFunc)();
+ }
+
+ u16 ReadNextCharUTF8();
+ u16 ReadNextCharUTF16();
+ u16 ReadNextCharCP1252();
+ u16 ReadNextCharSJIS();
+
+ template <typename T>
+ T GetChar() const {
+ const T* const charStrm = static_cast<const T* const>(mCharStrm);
+ return charStrm[0];
+ }
+
+ template <typename T>
+ T GetChar(int offset) const {
+ const T* const charStrm = static_cast<const T* const>(mCharStrm);
+ return charStrm[offset];
+ }
+
+ template <typename T>
+ void StepStrm(int step) {
+ const T*& charStrm = *reinterpret_cast<const T**>(&mCharStrm);
+ charStrm += step;
+ }
+
+ /* 0x00 */ const void* mCharStrm;
+ /* 0x04 */ const ReadFunc mReadFunc;
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/CharWriter.h b/src/revolution/homebuttonLib/nw4hbm/ut/CharWriter.h
new file mode 100644
index 0000000000..adfad2b37c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/CharWriter.h
@@ -0,0 +1,160 @@
+#ifndef NW4HBM_UT_CHAR_WRITER_H
+#define NW4HBM_UT_CHAR_WRITER_H
+
+#include <revolution/types.h>
+
+#include "Color.h"
+#include "fontResources.h"
+
+#include "../math/types.h"
+
+#include <revolution/gx.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ class Font;
+
+ class CharWriter {
+ public:
+ typedef enum GradationMode {
+ /* 0 */ GRADMODE_NONE = 0,
+ /* 1 */ GRADMODE_H,
+ /* 2 */ GRADMODE_V,
+ /* 3 */ NUM_OF_GRADMODE
+ } GradationMode;
+
+ private:
+ typedef struct ColorMapping {
+ /* 0x00 */ Color min;
+ /* 0x04 */ Color max;
+ } ColorMapping;
+
+ typedef struct VertexColor {
+ /* 0x00 */ Color lu, ru;
+ /* 0x08 */ Color ld, rd;
+ } VertexColor;
+
+ typedef struct TextureFilter {
+ public:
+ bool operator!=(const TextureFilter& rhs) const {
+ return atSmall != rhs.atSmall || atLarge != rhs.atLarge;
+ }
+
+ /* 0x00 */ GXTexFilter atSmall;
+ /* 0x04 */ GXTexFilter atLarge;
+ } TextureFilter;
+
+ typedef struct TextColor {
+ /* 0x00 */ Color start;
+ /* 0x04 */ Color end;
+ /* 0x08 */ GradationMode gradationMode;
+ } TextColor;
+
+ typedef struct LoadingTexture {
+ public:
+ bool operator!=(const LoadingTexture& rhs) const {
+ return slot != rhs.slot || texture != rhs.texture || filter != rhs.filter;
+ }
+
+ void Reset() {
+ slot = GX_TEXMAP_NULL;
+ texture = NULL;
+ }
+
+ /* 0x00 */ GXTexMapID slot;
+ /* 0x04 */ void* texture;
+ /* 0x08 */ TextureFilter filter;
+ } LoadingTexture;
+
+ public:
+ CharWriter();
+ ~CharWriter();
+
+ const Font* GetFont() const;
+
+ f32 GetScaleH() const;
+ f32 GetScaleV() const;
+
+ f32 GetCursorX() const;
+ f32 GetCursorY() const;
+
+ void SetFont(const Font& font);
+ void SetColorMapping(Color min, Color max);
+
+ void SetScale(f32 hScale, f32 vScale);
+ void SetScale(f32 scale);
+
+ void SetCursor(f32 x, f32 y);
+ void SetCursor(f32 x, f32 y, f32 z);
+ void SetCursorX(f32 x);
+ void SetCursorY(f32 y);
+
+ f32 GetFontWidth() const;
+ f32 GetFontHeight() const;
+ f32 GetFontAscent() const;
+
+ bool IsWidthFixed() const;
+ void EnableFixedWidth(bool flag);
+
+ f32 GetFixedWidth() const;
+ void SetFixedWidth(f32 width);
+
+ void SetGradationMode(GradationMode mode);
+
+ void SetTextColor(Color color);
+ void SetTextColor(Color start, Color end);
+ Color GetTextColor() const;
+
+ void SetFontSize(f32 width, f32 height);
+ void SetFontSize(f32 height);
+
+ void SetupGX();
+
+ void ResetColorMapping();
+ void ResetTextureCache();
+
+ void EnableLinearFilter(bool atSmall, bool atLarge);
+
+ f32 Print(u16 code);
+
+ void MoveCursorX(f32 dx);
+ void MoveCursorY(f32 dy);
+
+ void PrintGlyph(f32 x, f32 y, f32 z, const Glyph& glyph);
+
+ void LoadTexture(const Glyph& glyph, GXTexMapID slot);
+
+ void UpdateVertexColor();
+
+ private:
+ static void SetupVertexFormat();
+
+ static void SetupGXDefault();
+ static void SetupGXWithColorMapping(Color min, Color max);
+ static void SetupGXForI();
+ static void SetupGXForRGBA();
+
+ /* 0x00 */ ColorMapping mColorMapping;
+ /* 0x08 */ VertexColor mVertexColor;
+ /* 0x18 */ TextColor mTextColor;
+ /* 0x24 */ math::VEC2 mScale;
+ /* 0x2C */ math::VEC3 mCursorPos;
+ /* 0x38 */ TextureFilter mFilter;
+ /* 0x40 */ u8 padding_[2];
+ /* 0x42 */ u8 mAlpha;
+ /* 0x43 */ bool mIsWidthFixed;
+ /* 0x44 */ f32 mFixedWidth;
+ /* 0x48 */ const Font* mFont;
+
+ static const u32 DEFAULT_COLOR_MAPPING_MIN = 0x00000000;
+ static const u32 DEFAULT_COLOR_MAPPING_MAX = 0xFFFFFFFF;
+
+ private:
+ static LoadingTexture mLoadingTexture;
+ }; // size = 0x4C
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/Color.h b/src/revolution/homebuttonLib/nw4hbm/ut/Color.h
new file mode 100644
index 0000000000..a71235add0
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/Color.h
@@ -0,0 +1,69 @@
+#ifndef NW4HBM_UT_COLOR_H
+#define NW4HBM_UT_COLOR_H
+
+#include <revolution/types.h>
+#include <revolution/gx.h>
+
+
+namespace nw4hbm {
+ namespace ut {
+ typedef struct Color : public GXColor {
+ static const u32 RED = 0xFF0000FF;
+ static const u32 GREEN = 0x00FF00FF;
+ static const u32 BLUE = 0x0000FFFF;
+
+ static const u32 CYAN = 0x00FFFFFF;
+ static const u32 MAGENTA = 0xFF00FFFF;
+ static const u32 YELLOW = 0xFFFF00FF;
+
+ static const u32 BLACK = 0x000000FF;
+ static const u32 GRAY = 0x808080FF;
+ static const u32 WHITE = 0xFFFFFFFF;
+
+ static const u32 NOCOLOR = 0x00000000;
+ static const u32 MAXCOLOR = 0xFFFFFFFF;
+
+ // Constructor
+
+ Color() { *this = WHITE; }
+ Color(u32 color) { *this = color; }
+ Color(const GXColor& color) { *this = color; }
+ Color(int red, int green, int blue, int alpha) { Set(red, green, blue, alpha); }
+
+ // Left out destructor
+
+ ~Color() {}
+
+ // Operators
+
+ Color& operator=(u32 color) {
+ ToU32ref() = color;
+ return *this;
+ }
+
+ Color& operator=(const GXColor& color) {
+ return *this = *reinterpret_cast<const u32*>(&color);
+ }
+
+ Color operator|(u32 color) const { return Color(ToU32() | color); }
+ Color operator&(u32 color) const { return Color(ToU32() & color); }
+
+ u32& ToU32ref() { return *reinterpret_cast<u32*>(this); }
+ const u32& ToU32ref() const { return *reinterpret_cast<const u32*>(this); }
+ u32 ToU32() const { return ToU32ref(); }
+
+ operator u32() const { return ToU32ref(); }
+
+ // Functions
+
+ void Set(int red, int green, int blue, int alpha) {
+ r = red;
+ g = green;
+ b = blue;
+ a = alpha;
+ }
+ } Color;
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/DvdFileStream.h b/src/revolution/homebuttonLib/nw4hbm/ut/DvdFileStream.h
new file mode 100644
index 0000000000..d1b5dacc9f
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/DvdFileStream.h
@@ -0,0 +1,73 @@
+#ifndef NW4HBM_UT_DVD_FILE_STREAM_H
+#define NW4HBM_UT_DVD_FILE_STREAM_H
+
+#include "FileStream.h"
+
+#include <revolution/dvd.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ class DvdFileStream : public FileStream {
+ public:
+ explicit DvdFileStream(s32 entrynum);
+ DvdFileStream(const DVDFileInfo* info, bool close);
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~DvdFileStream();
+ /* 0x10 */ virtual void Close();
+ /* 0x14 */ virtual s32 Read(void* pDst, u32 size);
+ /* 0x18 */ virtual bool ReadAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg);
+ /* 0x24 */ virtual bool IsBusy() const { return mIsBusy; }
+ /* 0x28 */ virtual bool CanAsync() const { return true; }
+ /* 0x2C */ virtual bool CanRead() const { return true; }
+ /* 0x30 */ virtual bool CanWrite() const { return false; }
+ /* 0x34 */ virtual u32 GetOffsetAlign() const { return 4; }
+ /* 0x38 */ virtual u32 GetSizeAlign() const { return 32; }
+ /* 0x3C */ virtual u32 GetBufferAlign() const { return 32; }
+ /* 0x40 */ virtual u32 GetSize() const { return mFilePosition.GetFileSize(); }
+ /* 0x44 */ virtual void Seek(s32 offset, u32 origin);
+ /* 0x48 */ virtual void Cancel();
+ /* 0x4C */ virtual bool CancelAsync(IOStreamCallback pCallback, void* pCallbackArg);
+ /* 0x50 */ virtual bool CanSeek() const { return true; }
+ /* 0x54 */ virtual bool CanCancel() const { return true; }
+ /* 0x58 */ virtual u32 Tell() const { return mFilePosition.Tell(); }
+ /* 0x5C */ virtual s32 Peek(void* pDst, u32 size);
+ /* 0x60 */ virtual bool PeekAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg);
+
+ bool Open(s32 entrynum);
+ bool Open(const DVDFileInfo* info, bool close);
+ void SetPriority(s32 priority) { mPriority = priority; }
+
+ private:
+ typedef struct DvdFileStreamInfo {
+ /* 0x00 */ DVDFileInfo dvdInfo;
+ /* 0x3C */ DvdFileStream* stream;
+ } DvdFileStreamInfo;
+
+ private:
+ static void DvdAsyncCallback_(s32 result, DVDFileInfo* info);
+ static void DvdCBAsyncCallback_(s32 result, DVDCommandBlock* pBlock);
+
+ void Initialize_();
+ u32 AdjustReadLength_(u32 len);
+
+ private:
+ /* 0x00 (base) */
+ /* 0x14 */ FilePosition mFilePosition;
+ /* 0x1C */ IOStreamCallback mCancelCallback;
+ /* 0x20 */ void* mCancelArg;
+ /* 0x24 */ volatile bool mIsCanceling;
+ /* 0x28 */ DvdFileStreamInfo mFileInfo;
+ /* 0x68 */ s32 mPriority;
+ /* 0x6C */ volatile bool mIsBusy;
+ /* 0x6D */ bool mCloseOnDestroyFlg;
+ /* 0x6E */ bool mCloseEnableFlg;
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/DvdLockedFileStream.h b/src/revolution/homebuttonLib/nw4hbm/ut/DvdLockedFileStream.h
new file mode 100644
index 0000000000..55135315ab
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/DvdLockedFileStream.h
@@ -0,0 +1,45 @@
+#ifndef NW4HBM_UT_DVD_LOCKED_FILE_STREAM_H
+#define NW4HBM_UT_DVD_LOCKED_FILE_STREAM_H
+
+#include "DvdFileStream.h"
+#include <revolution/os.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ class DvdLockedFileStream : public DvdFileStream {
+ public:
+ explicit DvdLockedFileStream(s32 entrynum);
+ DvdLockedFileStream(const DVDFileInfo* info, bool close);
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~DvdLockedFileStream();
+ /* 0x14 */ virtual s32 Read(void* pDst, u32 size);
+ /* 0x28 */ virtual bool CanAsync() const { return false; }
+
+ /* 0x18 */ virtual bool ReadAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ return false;
+ }
+
+ /* 0x5C */ virtual s32 Peek(void* pDst, u32 size);
+ /* 0x60 */ virtual bool PeekAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ return false;
+ }
+
+ private:
+ static void InitMutex_();
+
+ private:
+ /* 0x00 (base) */
+ /* 0x6F */ bool mCancelFlag;
+
+ static bool sInitialized;
+ static OSMutex sMutex;
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/FileStream.h b/src/revolution/homebuttonLib/nw4hbm/ut/FileStream.h
new file mode 100644
index 0000000000..63c6caec1a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/FileStream.h
@@ -0,0 +1,46 @@
+#ifndef NW4HBM_UT_FILE_STREAM_H
+#define NW4HBM_UT_FILE_STREAM_H
+
+#include "IOStream.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ class FileStream : public IOStream {
+ public:
+ FileStream() {}
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~FileStream() {}
+ /* 0x40 */ virtual u32 GetSize() const = 0;
+ /* 0x44 */ virtual void Seek(s32 offset, u32 origin);
+ /* 0x48 */ virtual void Cancel();
+ /* 0x4C */ virtual bool CancelAsync(IOStreamCallback pCallback, void* pCallbackArg);
+ /* 0x50 */ virtual bool CanSeek() const = 0;
+ /* 0x54 */ virtual bool CanCancel() const = 0;
+ /* 0x58 */ virtual u32 Tell() const = 0;
+
+ protected:
+ class FilePosition {
+ public:
+ FilePosition() : mFileSize(0), mPosition(0) {}
+
+ u32 GetFileSize() const { return mFileSize; }
+ void SetFileSize(u32 size) { mFileSize = size; }
+
+ u32 Tell() const { return mPosition; }
+
+ u32 Skip(s32 offset);
+ u32 Append(s32 offset);
+ void Seek(s32 offset, u32 origin);
+
+ private:
+ /* 0x00 */ u32 mFileSize;
+ /* 0x04 */ u32 mPosition;
+ }; // size = 0x08
+ }; // size = 0x14
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/Font.h b/src/revolution/homebuttonLib/nw4hbm/ut/Font.h
new file mode 100644
index 0000000000..1aa49f20bf
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/Font.h
@@ -0,0 +1,85 @@
+#ifndef NW4HBM_UT_FONT_H
+#define NW4HBM_UT_FONT_H
+
+#include <revolution/gx.h>
+#include <revolution/types.h>
+
+#include "CharStrmReader.h"
+#include "fontResources.h"
+
+#include "macros.h"
+#include "global.h"
+
+#define FONT_TYPE_NNGCTEXTURE 1
+#define GLYPH_INDEX_NOT_FOUND 0xFFFF
+
+namespace nw4hbm {
+ namespace ut {
+
+ typedef enum FontMapMethod {
+ /* 0 */ FONT_MAPMETHOD_DIRECT = 0,
+ /* 1 */ FONT_MAPMETHOD_TABLE,
+ /* 2 */ FONT_MAPMETHOD_SCAN,
+ } FontMapMethod;
+
+ typedef enum FontEncoding {
+ /* 0 */ FONT_ENCODING_UTF8 = 0,
+ /* 1 */ FONT_ENCODING_UTF16,
+ /* 2 */ FONT_ENCODING_SJIS,
+ /* 3 */ FONT_ENCODING_CP1252,
+ /* 4 */ NUM_OF_FONT_ENCODING
+ } FontEncoding;
+
+ class Font {
+ public:
+ typedef enum Type {
+ /* 0xFFFF */ INVALID_CHARACTER_CODE = 0xFFFF,
+ /* 0 */ TYPE_NULL = 0,
+ /* 1 */ TYPE_ROM,
+ /* 2 */ TYPE_RESOURCE,
+ } Type;
+
+ public:
+ Font() : mReaderFunc(&CharStrmReader::ReadNextCharCP1252) {}
+
+ /* 0x08 */ virtual ~Font() {}
+ /* 0x0C */ virtual int GetWidth() const = 0;
+ /* 0x10 */ virtual int GetHeight() const = 0;
+ /* 0x14 */ virtual int GetAscent() const = 0;
+ /* 0x18 */ virtual int GetDescent() const = 0;
+ /* 0x1C */ virtual int GetBaselinePos() const = 0;
+ /* 0x20 */ virtual int GetCellHeight() const = 0;
+ /* 0x24 */ virtual int GetCellWidth() const = 0;
+ /* 0x28 */ virtual int GetMaxCharWidth() const = 0;
+ /* 0x2C */ virtual Type GetType() const = 0;
+ /* 0x30 */ virtual GXTexFmt GetTextureFormat() const = 0;
+ /* 0x34 */ virtual int GetLineFeed() const = 0;
+ /* 0x38 */ virtual CharWidths GetDefaultCharWidths() const = 0;
+ /* 0x3C */ virtual void SetDefaultCharWidths(const CharWidths& widths) = 0;
+ /* 0x40 */ virtual bool SetAlternateChar(u16 c) = 0;
+ /* 0x44 */ virtual void SetLineFeed(int linefeed) = 0;
+ /* 0x48 */ virtual int GetCharWidth(u16 c) const = 0;
+ /* 0x4C */ virtual CharWidths GetCharWidths(u16 c) const = 0;
+ /* 0x50 */ virtual void GetGlyph(Glyph* glyph, u16 c) const = 0;
+ /* 0x54 */ virtual FontEncoding GetEncoding() const = 0;
+
+ void InitReaderFunc(FontEncoding encoding);
+
+ CharStrmReader GetCharStrmReader() const NO_INLINE {
+ //! TODO: required to make `ut_TextWriterBase.cpp` happy
+ #ifndef NO_THIS_ASSERT
+ NW4HBM_ASSERT_VALID_PTR(117, this);
+ #endif
+ CharStrmReader reader(mReaderFunc);
+ return reader;
+ }
+
+ private:
+ /* 0x00 (vtable) */
+ /* 0x04 */ CharStrmReader::ReadFunc mReaderFunc;
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/IOStream.h b/src/revolution/homebuttonLib/nw4hbm/ut/IOStream.h
new file mode 100644
index 0000000000..b4c804038c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/IOStream.h
@@ -0,0 +1,47 @@
+#ifndef NW4HBM_UT_IO_STREAM_H
+#define NW4HBM_UT_IO_STREAM_H
+
+#include <revolution/types.h>
+
+#include "RuntimeTypeInfo.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ class IOStream {
+ public:
+ typedef void (*IOStreamCallback)(s32 result, IOStream* pStream, void* pCallbackArg);
+
+ IOStream() : mAvailable(false), mCallback(NULL), mArg(NULL) {}
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~IOStream() {}
+ /* 0x10 */ virtual void Close() = 0;
+ /* 0x14 */ virtual s32 Read(void* pDst, u32 size);
+ /* 0x18 */ virtual bool ReadAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg);
+ /* 0x1C */ virtual void Write(const void* pSrc, u32 size);
+ /* 0x20 */ virtual bool WriteAsync(const void* pSrc, u32 size,
+ IOStreamCallback pCallback, void* pCallbackArg);
+ /* 0x24 */ virtual bool IsBusy() const;
+ /* 0x28 */ virtual bool CanAsync() const = 0;
+ /* 0x2C */ virtual bool CanRead() const = 0;
+ /* 0x30 */ virtual bool CanWrite() const = 0;
+ /* 0x34 */ virtual u32 GetOffsetAlign() const { return 1; }
+ /* 0x38 */ virtual u32 GetSizeAlign() const { return 1; }
+ /* 0x3C */ virtual u32 GetBufferAlign() const { return 1; }
+
+ bool IsAvailable() const { return mAvailable; }
+
+ protected:
+ /* 0x00 (vtable) */
+ /* 0x04 */ bool mAvailable;
+ /* 0x08 */ s32 mAsyncResult;
+ /* 0x0C */ IOStreamCallback mCallback;
+ /* 0x10 */ void* mArg;
+ }; // size = 0x14
+
+ }; // namespace ut
+}; // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/LinkList.h b/src/revolution/homebuttonLib/nw4hbm/ut/LinkList.h
new file mode 100644
index 0000000000..f6aa2fa059
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/LinkList.h
@@ -0,0 +1,378 @@
+#ifndef NW4HBM_UT_LINK_LIST_H
+#define NW4HBM_UT_LINK_LIST_H
+
+#include <revolution/types.h>
+
+#include "inlines.h"
+
+#include "../db/assert.h"
+#include "stddef.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ namespace detail {
+ class LinkListImpl;
+ }
+
+ /******************************************************************************
+ *
+ * Linked list node
+ *
+ ******************************************************************************/
+ class LinkListNode : private NonCopyable {
+ friend class detail::LinkListImpl;
+
+ public:
+ LinkListNode() : mNext(NULL), mPrev(NULL) {}
+
+ LinkListNode* GetNext() const { return mNext; }
+ LinkListNode* GetPrev() const { return mPrev; }
+
+ private:
+ /* 0x00 */ LinkListNode* mNext;
+ /* 0x04 */ LinkListNode* mPrev;
+ }; // size = 0x08
+
+ namespace detail {
+
+ /******************************************************************************
+ *
+ * Linked list implementation
+ *
+ ******************************************************************************/
+ class LinkListImpl : private NonCopyable {
+ public:
+ class ConstIterator;
+
+ /******************************************************************************
+ * Iterator implementation
+ ******************************************************************************/
+ class Iterator {
+ friend class LinkListImpl;
+ friend class ConstIterator;
+
+ public:
+ Iterator() : mPointer(NULL) {}
+ explicit Iterator(LinkListNode* pNode) : mPointer(pNode) {}
+
+ Iterator& operator++() {
+ mPointer = mPointer->GetNext();
+ return *this;
+ }
+
+ Iterator& operator--() {
+ mPointer = mPointer->GetPrev();
+ return *this;
+ }
+
+ LinkListNode* operator->() const { return mPointer; }
+
+ friend bool operator==(LinkListImpl::Iterator lhs, LinkListImpl::Iterator rhs) {
+ return lhs.mPointer == rhs.mPointer;
+ }
+
+ private:
+ /* 0x00 */ LinkListNode* mPointer;
+ }; // size = 0x04
+
+ /******************************************************************************
+ * Iterator implementation (const-view)
+ ******************************************************************************/
+ class ConstIterator {
+ friend class LinkListImpl;
+
+ public:
+ explicit ConstIterator(Iterator it) : mNode(it.mPointer) {}
+
+ ConstIterator& operator++() {
+ mNode = mNode->GetNext();
+ return *this;
+ }
+
+ ConstIterator& operator--() {
+ mNode = mNode->GetPrev();
+ return *this;
+ }
+
+ const LinkListNode* operator->() const { return mNode; }
+
+ friend bool operator==(LinkListImpl::ConstIterator lhs,
+ LinkListImpl::ConstIterator rhs) {
+ return lhs.mNode == rhs.mNode;
+ }
+
+ private:
+ /* 0x00 */ LinkListNode* mNode;
+ }; // size = 0x04
+
+ protected:
+ static Iterator GetIteratorFromPointer(LinkListNode* pNode) {
+ return Iterator(pNode);
+ }
+
+ LinkListImpl() { Initialize_(); }
+ ~LinkListImpl();
+
+ Iterator GetBeginIter() { return Iterator(mNode.GetNext()); }
+ Iterator GetEndIter() { return Iterator(&mNode); }
+
+ Iterator Insert(Iterator it, LinkListNode* pNode);
+
+ Iterator Erase(Iterator it);
+ Iterator Erase(LinkListNode* pNode);
+ Iterator Erase(Iterator begin, Iterator end);
+
+ public:
+ u32 GetSize() const { return mSize; }
+ bool IsEmpty() const { return mSize == 0; }
+
+ void PopFront() { Erase(GetBeginIter()); }
+ void PopBack() { Erase(--GetEndIter()); }
+
+ void Clear();
+ void SetPrev(LinkListNode* p, LinkListNode* pPrev);
+ void SetNext(LinkListNode* p, LinkListNode* pNext);
+
+ private:
+ void Initialize_() {
+ mSize = 0;
+ mNode.mNext = &mNode;
+ mNode.mPrev = &mNode;
+ }
+
+ private:
+ /* 0x00 */ u32 mSize;
+ /* 0x04 */ LinkListNode mNode;
+ }; // size = 0x0C
+
+ /******************************************************************************
+ *
+ * Reverse iterator
+ *
+ ******************************************************************************/
+ template <typename TIter>
+ class ReverseIterator {
+ public:
+ explicit ReverseIterator(TIter it) : mCurrent(it) {}
+
+ TIter GetBase() const { return mCurrent; }
+
+ ReverseIterator& operator++() {
+ --mCurrent;
+ return *this;
+ }
+
+ const typename TIter::TElem* operator->() const { return &this->operator*(); }
+
+ typename TIter::TElem& operator*() const {
+ TIter it = mCurrent;
+ return *--it;
+ }
+
+ friend bool operator==(const ReverseIterator& rLhs, const ReverseIterator& rRhs) {
+ return rLhs.mCurrent == rRhs.mCurrent;
+ }
+
+ friend bool operator!=(const ReverseIterator& rLhs, const ReverseIterator& rRhs) {
+ return !(rLhs.mCurrent == rRhs.mCurrent);
+ }
+
+ private:
+ /* 0x00 */ TIter mCurrent;
+ };
+
+ } // namespace detail
+
+ /******************************************************************************
+ *
+ * Templated linked list
+ *
+ ******************************************************************************/
+ template <typename T, int Ofs>
+ class LinkList : public detail::LinkListImpl {
+ public:
+ class ConstIterator;
+
+ /******************************************************************************
+ * Templated iterator
+ ******************************************************************************/
+ class Iterator {
+ friend class LinkList;
+ friend class ConstIterator;
+
+ public:
+ // Element type must be visible to ReverseIterator
+ typedef T TElem;
+
+ public:
+ Iterator() : mIterator(NULL) {}
+ explicit Iterator(LinkListImpl::Iterator it) : mIterator(it) {}
+
+ Iterator& operator++() {
+ ++mIterator;
+ return *this;
+ }
+
+ Iterator& operator--() {
+ --mIterator;
+ return *this;
+ }
+
+ Iterator operator++(int) {
+ Iterator ret = *this;
+ ++*this;
+ return ret;
+ }
+
+ T* operator->() const { return GetPointerFromNode(mIterator.operator->()); }
+
+ T& operator*() const {
+ T* p = this->operator->();
+ NW4HBM_ASSERT_CHECK_NULL(403, p);
+ return *p;
+ }
+
+ friend bool operator==(Iterator lhs, Iterator rhs) {
+ return lhs.mIterator == rhs.mIterator;
+ }
+
+ friend bool operator!=(Iterator lhs, Iterator rhs) { return !(lhs == rhs); }
+
+ private:
+ /* 0x00 */ LinkListImpl::Iterator mIterator;
+ };
+
+ /******************************************************************************
+ * Templated iterator (const-view)
+ ******************************************************************************/
+ class ConstIterator {
+ friend class LinkList;
+
+ public:
+ // Element type must be visible to ReverseIterator
+ typedef T TElem;
+
+ public:
+ explicit ConstIterator(LinkListImpl::Iterator it) : mIterator(it) {}
+ explicit ConstIterator(Iterator it) : mIterator(it.mIterator) {}
+
+ ConstIterator& operator++() {
+ ++mIterator;
+ return *this;
+ }
+
+ ConstIterator& operator--() {
+ --mIterator;
+ return *this;
+ }
+
+ ConstIterator operator++(int) {
+ ConstIterator ret = *this;
+ ++*this;
+ return ret;
+ }
+
+ const T* operator->() const { return GetPointerFromNode(mIterator.operator->()); }
+
+ const T& operator*() const {
+ const T* p = this->operator->();
+ NW4HBM_ASSERT_CHECK_NULL(447, p);
+ return *p;
+ }
+
+ friend bool operator==(ConstIterator lhs, ConstIterator rhs) {
+ return lhs.mIterator == rhs.mIterator;
+ }
+
+ friend bool operator!=(ConstIterator lhs, ConstIterator rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ /* 0x00 */ LinkListImpl::ConstIterator mIterator;
+ };
+
+ public:
+ // Shorthand names for reverse iterator types
+ typedef detail::ReverseIterator<Iterator> RevIterator;
+ typedef detail::ReverseIterator<ConstIterator> ConstRevIterator;
+
+ public:
+ LinkList() {}
+
+ Iterator GetBeginIter() { return Iterator(LinkListImpl::GetBeginIter()); }
+ ConstIterator GetBeginIter() const {
+ return ConstIterator(const_cast<LinkList*>(this)->GetBeginIter());
+ }
+ RevIterator GetBeginReverseIter() { return RevIterator(GetBeginIter()); }
+ ConstRevIterator GetBeginReverseIter() const {
+ return ConstRevIterator(GetBeginIter());
+ }
+
+ Iterator GetEndIter() { return Iterator(LinkListImpl::GetEndIter()); }
+ ConstIterator GetEndIter() const {
+ return ConstIterator(const_cast<LinkList*>(this)->GetEndIter());
+ }
+ RevIterator GetEndReverseIter() { return RevIterator(GetEndIter()); }
+ ConstRevIterator GetEndReverseIter() const { return ConstRevIterator(GetEndIter()); }
+
+ Iterator Insert(Iterator it, T* pElem) {
+ return Iterator(LinkListImpl::Insert(it.mIterator, GetNodeFromPointer(pElem)));
+ }
+
+ Iterator Erase(T* pElem) {
+ return Iterator(LinkListImpl::Erase(GetNodeFromPointer(pElem)));
+ }
+ Iterator Erase(Iterator it) { return Iterator(LinkListImpl::Erase(it.mIterator)); }
+
+ void PushBack(T* pElem) { Insert(GetEndIter(), pElem); }
+
+ T& GetFront() {
+ NW4HBM_ASSERT(497, !IsEmpty());
+ return *GetBeginIter();
+ }
+
+ const T& GetFront() const {
+ NW4HBM_ASSERT(502, !IsEmpty());
+ return *GetBeginIter();
+ }
+
+ T& GetBack() {
+ NW4HBM_ASSERT(507, !IsEmpty());
+ return *--GetEndIter();
+ }
+
+ const T& GetBack() const {
+ NW4HBM_ASSERT(512, !IsEmpty()); // assumed line number
+ return *--GetEndIter();
+ }
+
+ static Iterator GetIteratorFromPointer(T* pElem) {
+ return GetIteratorFromPointer(GetNodeFromPointer(pElem));
+ }
+
+ static Iterator GetIteratorFromPointer(LinkListNode* pNode) {
+ return Iterator(LinkListImpl::GetIteratorFromPointer(pNode));
+ }
+
+ static LinkListNode* GetNodeFromPointer(T* p) {
+ NW4HBM_ASSERT_CHECK_NULL(563, p);
+ return reinterpret_cast<LinkListNode*>(reinterpret_cast<char*>(p) + Ofs);
+ }
+
+ static T* GetPointerFromNode(LinkListNode* p) {
+ NW4HBM_ASSERT_CHECK_NULL(573, p);
+ return reinterpret_cast<T*>(reinterpret_cast<char*>(p) - Ofs);
+ }
+
+ static const T* GetPointerFromNode(const LinkListNode* p) {
+ NW4HBM_ASSERT_CHECK_NULL(578, p);
+ return reinterpret_cast<const T*>(reinterpret_cast<const char*>(p) - Ofs);
+ }
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/Lock.h b/src/revolution/homebuttonLib/nw4hbm/ut/Lock.h
new file mode 100644
index 0000000000..c9c2d8f458
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/Lock.h
@@ -0,0 +1,44 @@
+#ifndef NW4HBM_UT_LOCK_H
+#define NW4HBM_UT_LOCK_H
+
+#include "inlines.h"
+#include <revolution/os.h>
+
+namespace nw4hbm {
+ namespace ut {
+ namespace detail {
+
+ inline void Lock(OSMutex& rMutex) {
+ OSLockMutex(&rMutex);
+ }
+ inline void Unlock(OSMutex& rMutex) {
+ OSUnlockMutex(&rMutex);
+ }
+
+ template <typename T>
+ class AutoLock : private NonCopyable {
+ public:
+ explicit AutoLock(T& rLockObj) : mLockObj(rLockObj) { Lock(rLockObj); }
+ ~AutoLock() { Unlock(mLockObj); }
+
+ private:
+ /* 0x00 */ T& mLockObj;
+ }; // size = 0x04
+
+ } // namespace detail
+
+ typedef detail::AutoLock<OSMutex> AutoMutexLock;
+
+ class AutoInterruptLock : private NonCopyable {
+ public:
+ AutoInterruptLock() : mOldState(OSDisableInterrupts()) {}
+ ~AutoInterruptLock() { OSRestoreInterrupts(mOldState); }
+
+ private:
+ /* 0x00 */ BOOL mOldState;
+ }; // size = 0x04
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/NandFileStream.h b/src/revolution/homebuttonLib/nw4hbm/ut/NandFileStream.h
new file mode 100644
index 0000000000..93d9e885ae
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/NandFileStream.h
@@ -0,0 +1,71 @@
+#ifndef NW4HBM_UT_NAND_FILE_STREAM_H
+#define NW4HBM_UT_NAND_FILE_STREAM_H
+
+#include "FileStream.h"
+
+#include <revolution/nand.h>
+#include "global.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ class NandFileStream : public FileStream {
+ public:
+ NandFileStream(const char* path, u32 mode);
+ NandFileStream(const NANDFileInfo* info, u32 mode, bool enableClose);
+
+ bool Open(const char* path, u32 mode);
+ bool Open(const NANDFileInfo* info, u32 mode, bool enableClose) NO_INLINE;
+
+ /* 0x08 */ NW4HBM_UT_RUNTIME_TYPEINFO;
+ /* 0x0C */ virtual ~NandFileStream();
+ /* 0x10 */ virtual void Close();
+ /* 0x14 */ virtual s32 Read(void* pDst, u32 size);
+ /* 0x18 */ virtual bool ReadAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg);
+ /* 0x1C */ virtual void Write(const void* pSrc, u32 size);
+ /* 0x20 */ virtual bool WriteAsync(const void* pSrc, u32 size,
+ IOStreamCallback pCallback, void* pCallbackArg);
+ /* 0x44 */ virtual void Seek(s32 offset, u32 origin);
+
+ // the order is important
+ /* 0x24 */ virtual bool IsBusy() const { return mIsBusy; }
+ /* 0x58 */ virtual u32 Tell() const { return mFilePosition.Tell(); }
+ /* 0x40 */ virtual u32 GetSize() const { return mFilePosition.GetFileSize(); }
+ /* 0x28 */ virtual bool CanAsync() const { return true; }
+ /* 0x50 */ virtual bool CanSeek() const { return true; }
+ /* 0x2C */ virtual bool CanRead() const { return mCanRead; }
+ /* 0x30 */ virtual bool CanWrite() const { return mCanWrite; }
+ /* 0x54 */ virtual bool CanCancel() const { return false; }
+ /* 0x34 */ virtual u32 GetOffsetAlign() const { return 1; }
+ /* 0x38 */ virtual u32 GetSizeAlign() const { return 32; }
+ /* 0x3C */ virtual u32 GetBufferAlign() const { return 32; }
+
+ private:
+ typedef struct NandFileStreamInfo {
+ /* 0x000 */ NANDCommandBlock nandBlock;
+ /* 0x0B8 */ NANDFileInfo nandInfo;
+ /* 0x144 */ NandFileStream* stream;
+ } NandFileStreamInfo;
+
+ static void NandAsyncCallback_(s32 result, NANDCommandBlock* pBlock);
+
+ void Initialize_();
+ bool ReadAsyncImpl(void* buf, u32 length, IOStreamCallback pCallback,
+ void* pCallbackArg);
+ void ReadAsyncSetArgs(IOStreamCallback pCallback, void* pCallbackArg);
+
+ /* 0x000 (base) */
+ /* 0x014 */ FilePosition mFilePosition;
+ /* 0x01C */ NandFileStreamInfo mFileInfo;
+ /* 0x164 */ bool mCanRead;
+ /* 0x165 */ bool mCanWrite;
+ /* 0x166 */ volatile bool mIsBusy;
+ /* 0x167 */ bool mCloseOnDestroyFlg;
+ /* 0x168 */ bool mCloseEnableFlg;
+ }; // size = 0x16C
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/Rect.h b/src/revolution/homebuttonLib/nw4hbm/ut/Rect.h
new file mode 100644
index 0000000000..4dcc03dce9
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/Rect.h
@@ -0,0 +1,53 @@
+#ifndef NW4HBM_RECT_H
+#define NW4HBM_RECT_H
+
+#include <revolution/types.h>
+
+#include "../math/arithmetic.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ class Rect {
+ public:
+ Rect() : left(), top(), right(), bottom() {}
+ Rect(f32 l, f32 t, f32 r, f32 b) : left(l), top(t), right(r), bottom(b) {}
+
+ ~Rect() {}
+
+ void SetWidth(f32 width) { right = left + width; }
+ void SetHeight(f32 height) { bottom = top + height; }
+
+ f32 GetWidth() const { return right - left; }
+ f32 GetHeight() const { return bottom - top; }
+
+ void MoveTo(f32 x, f32 y) {
+ right = x + GetWidth();
+ left = x;
+
+ bottom = y + GetHeight();
+ top = y;
+ }
+
+ void Normalize() {
+ f32 l = left;
+ f32 t = top;
+ f32 r = right;
+ f32 b = bottom;
+
+ left = math::FSelect(r - l, l, r);
+ right = math::FSelect(r - l, r, l);
+ top = math::FSelect(b - t, t, b);
+ bottom = math::FSelect(b - t, b, t);
+ }
+
+ /* 0x00 */ f32 left;
+ /* 0x04 */ f32 top;
+ /* 0x08 */ f32 right;
+ /* 0x0C */ f32 bottom;
+ }; // size = 0x10
+
+ }; // namespace ut
+}; // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ResFont.h b/src/revolution/homebuttonLib/nw4hbm/ut/ResFont.h
new file mode 100644
index 0000000000..81dbf99f5d
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ResFont.h
@@ -0,0 +1,78 @@
+#ifndef NW4HBM_UT_RES_FONT_H
+#define NW4HBM_UT_RES_FONT_H
+
+#include <revolution/types.h>
+
+#include "Font.h"
+#include "binaryFileFormat.h"
+
+namespace nw4hbm {
+ namespace ut {
+ namespace detail {
+
+ class ResFontBase : public Font {
+ public:
+ ResFontBase();
+
+ /* 0x08 */ virtual ~ResFontBase();
+ /* 0x0C */ virtual int GetWidth() const;
+ /* 0x10 */ virtual int GetHeight() const;
+ /* 0x14 */ virtual int GetAscent() const;
+ /* 0x18 */ virtual int GetDescent() const;
+ /* 0x1C */ virtual int GetBaselinePos() const;
+ /* 0x20 */ virtual int GetCellHeight() const;
+ /* 0x24 */ virtual int GetCellWidth() const;
+ /* 0x28 */ virtual int GetMaxCharWidth() const;
+ /* 0x2C */ virtual Type GetType() const;
+ /* 0x30 */ virtual GXTexFmt GetTextureFormat() const;
+ /* 0x34 */ virtual int GetLineFeed() const;
+ /* 0x38 */ virtual CharWidths GetDefaultCharWidths() const;
+ /* 0x3C */ virtual void SetDefaultCharWidths(const CharWidths& widths);
+ /* 0x40 */ virtual bool SetAlternateChar(u16 c);
+ /* 0x44 */ virtual void SetLineFeed(int linefeed);
+ /* 0x48 */ virtual int GetCharWidth(u16 c) const;
+ /* 0x4C */ virtual CharWidths GetCharWidths(u16 c) const;
+ /* 0x50 */ virtual void GetGlyph(Glyph* glyph, u16 c) const;
+ /* 0x54 */ virtual FontEncoding GetEncoding() const;
+
+ void SetResourceBuffer(void* pUserBuffer, FontInformation* pFontInfo);
+
+ u16 GetGlyphIndex(u16 c) const;
+ const CharWidths& GetCharWidthsFromIndex(u16 index) const;
+ const CharWidths& GetCharWidthsFromIndex(const FontWidth* pWidth, u16 index) const;
+ void GetGlyphFromIndex(Glyph* glyph, u16 index) const;
+ u16 FindGlyphIndex(u16 c) const;
+ u16 FindGlyphIndex(const FontCodeMap* pMap, u16 c) const;
+
+ bool IsManaging(const void* ptr) const { return mResource == ptr; }
+
+ private:
+ /* 0x00 (base) */
+ /* 0x10 */ void* mResource;
+ /* 0x14 */ FontInformation* mFontInfo;
+ }; // size = 0x18
+
+ } // namespace detail
+
+ class ResFont : public detail::ResFontBase {
+ public:
+ ResFont();
+ virtual ~ResFont();
+
+ bool SetResource(void* brfnt);
+ static FontInformation* Rebuild(BinaryFileHeader* fileHeader);
+
+ private:
+ static const u32 SIGNATURE_FONT = 'RFNT'; /* Revolution FoNT */
+ static const u32 SIGNATURE_FONT_UNPACKED = 'RFNU'; /* Revolution FoNt Unpacked */
+ static const u32 SIGNATURE_FONT_INFO = 'FINF'; /* Font INFormation */
+ static const u32 SIGNATURE_TEX_GLYPH = 'TGLP'; /* Texture GLyPh */
+ static const u32 SIGNATURE_CHAR_WIDTH = 'CWDH'; /* Character WiDtH */
+ static const u32 SIGNATURE_CODE_MAP = 'CMAP'; /* Code MAP */
+ static const u32 SIGNATURE_GLGR = 'GLGR'; /* Something related to GLyPh? */
+ };
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/RuntimeTypeInfo.h b/src/revolution/homebuttonLib/nw4hbm/ut/RuntimeTypeInfo.h
new file mode 100644
index 0000000000..4d7f91327d
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/RuntimeTypeInfo.h
@@ -0,0 +1,56 @@
+#ifndef NW4HBM_UT_RUNTIME_TYPE_INFO_H
+#define NW4HBM_UT_RUNTIME_TYPE_INFO_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+#define NW4HBM_UT_RUNTIME_TYPEINFO \
+ virtual const nw4hbm::ut::detail::RuntimeTypeInfo* GetRuntimeTypeInfo() const { \
+ return &typeInfo; \
+ } \
+ static const nw4hbm::ut::detail::RuntimeTypeInfo typeInfo
+
+#define NW4HBM_UT_GET_RUNTIME_TYPEINFO(T) \
+ const nw4hbm::ut::detail::RuntimeTypeInfo T::typeInfo(NULL);
+
+#define NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(T, D) \
+ const nw4hbm::ut::detail::RuntimeTypeInfo T::typeInfo(&D::typeInfo);
+
+ namespace detail {
+ struct RuntimeTypeInfo {
+ explicit RuntimeTypeInfo(const RuntimeTypeInfo* base) : mParentTypeInfo(base) {}
+
+ bool IsDerivedFrom(const RuntimeTypeInfo* base) const {
+ for (const RuntimeTypeInfo* it = this; it != NULL; it = it->mParentTypeInfo) {
+ if (it == base) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* 0x00 */ const RuntimeTypeInfo* mParentTypeInfo;
+ }; // size = 0x04
+
+ template <typename T>
+ inline const RuntimeTypeInfo* GetTypeInfoFromPtr_(T* pPtr) {
+ return &pPtr->typeInfo;
+ }
+ } // namespace detail
+
+ template <typename TDerived, typename TBase>
+ inline TDerived DynamicCast(TBase* pPtr) {
+ const detail::RuntimeTypeInfo* pDerivedTypeInfo =
+ detail::GetTypeInfoFromPtr_(static_cast<TDerived>(NULL));
+ if (pPtr->GetRuntimeTypeInfo()->IsDerivedFrom(pDerivedTypeInfo)) {
+ return static_cast<TDerived>(pPtr);
+ }
+ return NULL;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessor.h b/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessor.h
new file mode 100644
index 0000000000..f36bbda0c4
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessor.h
@@ -0,0 +1,12 @@
+#ifndef NW4HBM_UT_TAG_PROCESSOR_H
+#define NW4HBM_UT_TAG_PROCESSOR_H
+
+#include "TagProcessorBase.h"
+
+namespace nw4hbm {
+ namespace ut {
+ typedef TagProcessorBase<char> TagProcessor;
+ }
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessorBase.h b/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessorBase.h
new file mode 100644
index 0000000000..827f88902c
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/TagProcessorBase.h
@@ -0,0 +1,49 @@
+#ifndef NW4HBM_UT_TAG_PROCESSOR_BASE_H
+#define NW4HBM_UT_TAG_PROCESSOR_BASE_H
+
+#include <revolution/types.h>
+
+#include "Rect.h"
+
+namespace nw4hbm {
+ namespace ut {
+ typedef enum Operation {
+ /* 0 */ OPERATION_DEFAULT = 0,
+ /* 1 */ OPERATION_NO_CHAR_SPACE,
+ /* 2 */ OPERATION_CHAR_SPACE,
+ /* 3 */ OPERATION_NEXT_LINE,
+ /* 4 */ OPERATION_END_DRAW,
+ /* 5 */ NUM_OF_OPERATION
+ } Operation;
+
+ template <typename>
+ class TextWriterBase;
+
+ template <typename T>
+ struct PrintContext {
+ /* 0x00 */ TextWriterBase<T>* writer;
+ /* 0x04 */ const T* str;
+ /* 0x08 */ f32 xOrigin;
+ /* 0x0C */ f32 yOrigin;
+ /* 0x10 */ u32 flags;
+ };
+
+ template <typename T>
+ class TagProcessorBase {
+ public:
+ TagProcessorBase();
+
+ /* 0x08 */ virtual ~TagProcessorBase();
+ /* 0x0C */ virtual Operation Process(u16 code, PrintContext<T>* context);
+ /* 0x10 */ virtual Operation CalcRect(Rect* pRect, u16 code, PrintContext<T>* context);
+
+ void ProcessLinefeed(PrintContext<T>* context);
+ void ProcessTab(PrintContext<T>* context);
+
+ /* 0x00 (vtable) */
+ }; // size = 0x04
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/TextWriter.h b/src/revolution/homebuttonLib/nw4hbm/ut/TextWriter.h
new file mode 100644
index 0000000000..d288acb03b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/TextWriter.h
@@ -0,0 +1,12 @@
+#ifndef NW4HBM_UT_TEXT_WRITER_H
+#define NW4HBM_UT_TEXT_WRITER_H
+
+#include "TextWriterBase.h"
+
+namespace nw4hbm {
+ namespace ut {
+ typedef TextWriterBase<char> TextWriter;
+ }
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/TextWriterBase.h b/src/revolution/homebuttonLib/nw4hbm/ut/TextWriterBase.h
new file mode 100644
index 0000000000..7d24112e84
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/TextWriterBase.h
@@ -0,0 +1,138 @@
+#ifndef NW4HBM_UT_TEXT_WRITER_BASE_H
+#define NW4HBM_UT_TEXT_WRITER_BASE_H
+
+#include <revolution/types.h>
+
+#include "CharWriter.h"
+#include "Rect.h"
+
+#include <cstring.h>
+#include <stdio.h>
+
+#include "TagProcessorBase.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ struct Rect;
+ template <typename>
+ class TagProcessorBase;
+
+ template <typename T>
+ class TextWriterBase : public CharWriter {
+ public:
+ enum DrawFlag {
+ // Align text lines
+ DRAWFLAG_ALIGN_TEXT_BASELINE = 0,
+ DRAWFLAG_ALIGN_TEXT_CENTER = (1 << 0),
+ DRAWFLAG_ALIGN_TEXT_RIGHT = (1 << 1),
+
+ // Align text block (horizontal)
+ DRAWFLAG_ALIGN_H_BASELINE = 0,
+ DRAWFLAG_ALIGN_H_CENTER = (1 << 4),
+ DRAWFLAG_ALIGN_H_RIGHT = (1 << 5),
+
+ // Align text block (vertical)
+ DRAWFLAG_ALIGN_V_BASELINE = 0,
+ DRAWFLAG_ALIGN_V_CENTER = (1 << 8),
+ DRAWFLAG_ALIGN_V_TOP = (1 << 9),
+
+ // Mask constants
+ DRAWFLAG_MASK_ALIGN_TEXT = DRAWFLAG_ALIGN_TEXT_BASELINE |
+ DRAWFLAG_ALIGN_TEXT_CENTER | DRAWFLAG_ALIGN_TEXT_RIGHT,
+
+ DRAWFLAG_MASK_ALIGN_H =
+ DRAWFLAG_ALIGN_H_BASELINE | DRAWFLAG_ALIGN_H_CENTER | DRAWFLAG_ALIGN_H_RIGHT,
+
+ DRAWFLAG_MASK_ALIGN_V =
+ DRAWFLAG_ALIGN_V_BASELINE | DRAWFLAG_ALIGN_V_CENTER | DRAWFLAG_ALIGN_V_TOP,
+ };
+
+ TextWriterBase();
+ ~TextWriterBase();
+
+ void SetLineHeight(f32 lineHeight);
+ f32 GetLineHeight() const;
+
+ void SetLineSpace(f32 lineSpace);
+ void SetCharSpace(f32 charSpace);
+
+ f32 GetLineSpace() const;
+ f32 GetCharSpace() const;
+
+ void SetTabWidth(int tabWidth);
+ int GetTabWidth() const;
+
+ void SetDrawFlag(u32 flags);
+ u32 GetDrawFlag() const;
+
+ void SetTagProcessor(TagProcessorBase<T>* tagProcessor);
+ void ResetTagProcessor();
+ TagProcessorBase<T>& GetTagProcessor() const;
+
+ f32 CalcFormatStringWidth(const T* format, ...) const;
+ f32 CalcFormatStringHeight(const T* format, ...) const;
+
+ void CalcFormatStringRect(Rect* pRect, const T* format, ...) const;
+ void CalcVStringRect(Rect* pRect, const T* format, va_list args) const;
+
+ f32 CalcStringWidth(const T* str, int length) const;
+ f32 CalcStringWidth(const T* str) const;
+
+ f32 CalcStringHeight(const T* str, int length) const;
+ f32 CalcStringHeight(const T* str) const;
+
+ void CalcStringRect(Rect* pRect, const T* str, int length) const;
+ void CalcStringRect(Rect* pRect, const T* str) const;
+
+ f32 Printf(const T* format, ...);
+ f32 VPrintf(const T* format, va_list args);
+ f32 Print(const T* str, int length);
+ f32 Print(const T* str);
+
+ static T* SetBuffer(T* buf, u32 size);
+ static T* SetBuffer(u32 size);
+
+ static u32 GetBufferSize();
+ static T* GetBuffer();
+
+ static int VSNPrintf(T* buffer, u32 count, const T* format, va_list arg) {
+ return sizeof(T) == sizeof(char) ?
+ std::vsnprintf((char*)buffer, count, (const char*)format, arg) :
+ std::vswprintf((wchar_t*)buffer, count, (const wchar_t*)format, arg);
+ }
+ static int StrLen(const T* str) {
+ return sizeof(T) == sizeof(char) ? std::strlen((const char*)str) :
+ std::wcslen((const wchar_t*)str);
+ }
+
+ void ut_TextWriterBase_unused1(Rect* pRect, const T* str, int length);
+
+ f32 CalcLineWidth(const T* str, int length);
+ int CalcLineRectImpl(Rect* pRect, const T* str, int length);
+ void CalcStringRectImpl(Rect* pRect, const T* str, int length);
+ f32 PrintImpl(const T* str, int length);
+ f32 AdjustCursor(f32* xOrigin, f32* yOrigin, const T* str, int length);
+
+ bool IsDrawFlagSet(u32 mask, u32 flag) const { return (mDrawFlag & mask) == flag; }
+
+ private:
+ /* 0x4C */ f32 mCharSpace;
+ /* 0x50 */ f32 mLineSpace;
+ /* 0x54 */ int mTabWidth;
+ /* 0x58 */ u32 mDrawFlag;
+ /* 0x5C */ TagProcessorBase<T>* mTagProcessor;
+
+ private:
+ static T* mFormatBuffer;
+ static u32 mFormatBufferSize;
+ static TagProcessorBase<T> mDefaultTagProcessor;
+ static const int DEFAULT_FORMAT_BUFFER_SIZE = 256;
+ static const u32 DRAWFLAG_MASK_ALL =
+ DRAWFLAG_MASK_ALIGN_TEXT | DRAWFLAG_MASK_ALIGN_H | DRAWFLAG_MASK_ALIGN_V;
+ }; // size = 0x60
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/WideTagProcessor.h b/src/revolution/homebuttonLib/nw4hbm/ut/WideTagProcessor.h
new file mode 100644
index 0000000000..9dc09390de
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/WideTagProcessor.h
@@ -0,0 +1,12 @@
+#ifndef NW4HBM_UT_WIDE_TAG_PROCESSOR_H
+#define NW4HBM_UT_WIDE_TAG_PROCESSOR_H
+
+#include "TagProcessorBase.h"
+
+namespace nw4hbm {
+namespace ut {
+typedef TagProcessorBase<wchar_t> WideTagProcessor;
+}
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/WideTextWriter.h b/src/revolution/homebuttonLib/nw4hbm/ut/WideTextWriter.h
new file mode 100644
index 0000000000..49e90fc582
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/WideTextWriter.h
@@ -0,0 +1,12 @@
+#ifndef NW4HBM_UT_WIDE_TEXT_WRITER_H
+#define NW4HBM_UT_WIDE_TEXT_WRITER_H
+
+#include "TextWriterBase.h"
+
+namespace nw4hbm {
+namespace ut {
+typedef TextWriterBase<wchar_t> WideTextWriter;
+}
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/binaryFileFormat.h b/src/revolution/homebuttonLib/nw4hbm/ut/binaryFileFormat.h
new file mode 100644
index 0000000000..409881607b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/binaryFileFormat.h
@@ -0,0 +1,29 @@
+#ifndef NW4HBM_UT_BINARY_FILE_FORMAT_H
+#define NW4HBM_UT_BINARY_FILE_FORMAT_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ typedef struct BinaryFileHeader {
+ /* 0x00 */ u32 signature;
+ /* 0x04 */ u16 byteOrder;
+ /* 0x06 */ u16 version;
+ /* 0x08 */ u32 fileSize;
+ /* 0x0C */ u16 headerSize;
+ /* 0x0E */ u16 dataBlocks;
+ } BinaryFileHeader;
+
+ typedef struct BinaryBlockHeader {
+ /* 0x00 */ u32 kind;
+ /* 0x04 */ u32 size;
+ } BinaryBlockHeader;
+
+ bool IsValidBinaryFile(const BinaryFileHeader* header, u32 signature, u16 version,
+ u16 minBlocks);
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/fontResources.h b/src/revolution/homebuttonLib/nw4hbm/ut/fontResources.h
new file mode 100644
index 0000000000..c408d6903e
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/fontResources.h
@@ -0,0 +1,77 @@
+#ifndef NW4HBM_UT_FONT_RESOURCES_H
+#define NW4HBM_UT_FONT_RESOURCES_H
+
+#include <revolution/types.h>
+
+#include <revolution/gx.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ typedef struct CharWidths {
+ /* 0x00 */ s8 left;
+ /* 0x01 */ u8 glyphWidth;
+ /* 0x02 */ s8 charWidth;
+ } CharWidths; // size = 0x04
+
+ typedef struct FontWidth {
+ /* 0x00 */ u16 indexBegin;
+ /* 0x02 */ u16 indexEnd;
+ /* 0x04 */ FontWidth* pNext;
+ /* 0x08 */ CharWidths widthTable[];
+ } FontWidth;
+
+ typedef struct Glyph {
+ /* 0x00 */ void* pTexture;
+ /* 0x04 */ CharWidths widths;
+ /* 0x07 */ u8 height;
+ /* 0x08 */ GXTexFmt texFormat;
+ /* 0x0C */ u16 texWidth;
+ /* 0x0E */ u16 texHeight;
+ /* 0x10 */ u16 cellX;
+ /* 0x12 */ u16 cellY;
+ } Glyph; // size = 0x14
+
+ typedef struct FontTextureGlyph {
+ /* 0x00 */ u8 cellWidth;
+ /* 0x01 */ u8 cellHeight;
+ /* 0x02 */ s8 baselinePos;
+ /* 0x03 */ u8 maxCharWidth;
+ /* 0x04 */ u32 sheetSize;
+ /* 0x08 */ u16 sheetNum;
+ /* 0x0A */ u16 sheetFormat;
+ /* 0x0C */ u16 sheetRow;
+ /* 0x0E */ u16 sheetLine;
+ /* 0x10 */ u16 sheetWidth;
+ /* 0x12 */ u16 sheetHeight;
+ /* 0x14 */ u8* sheetImage;
+ } FontTextureGlyph; // size = 0x18
+
+ typedef struct FontCodeMap {
+ /* 0x00 */ u16 ccodeBegin;
+ /* 0x02 */ u16 ccodeEnd;
+ /* 0x04 */ u16 mappingMethod;
+ /* 0x06 */ u16 reserved;
+ /* 0x08 */ FontCodeMap* pNext;
+ /* 0x0C */ u16 mapInfo[];
+ } FontCodeMap;
+
+ typedef struct FontInformation {
+ /* 0x00 */ u8 fontType;
+ /* 0x01 */ s8 linefeed;
+ /* 0x02 */ u16 alterCharIndex;
+ /* 0x04 */ CharWidths defaultWidth;
+ /* 0x07 */ u8 encoding;
+ /* 0x08 */ FontTextureGlyph* pGlyph;
+ /* 0x0C */ FontWidth* pWidth;
+ /* 0x10 */ FontCodeMap* pMap;
+ /* 0x14 */ u8 height;
+ /* 0x15 */ u8 width;
+ /* 0x16 */ u8 ascent;
+ /* 0x17 */ u8 padding_[1];
+ } FontInformation; // size = 0x18
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/inlines.h b/src/revolution/homebuttonLib/nw4hbm/ut/inlines.h
new file mode 100644
index 0000000000..2de4ea0c14
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/inlines.h
@@ -0,0 +1,92 @@
+#ifndef NW4HBM_UT_INLINE_FUNCTIONS_H
+#define NW4HBM_UT_INLINE_FUNCTIONS_H
+
+#include <revolution/types.h>
+
+namespace nw4hbm {
+ namespace ut {
+
+ class NonCopyable {
+ protected:
+ NonCopyable() {}
+ ~NonCopyable() {}
+
+ private:
+ NonCopyable(const NonCopyable&);
+ const NonCopyable& operator=(const NonCopyable&);
+ };
+
+ template <typename T>
+ inline T Min(T a, T b) {
+ return (a > b) ? b : a;
+ }
+ template <typename T>
+ inline T Max(T a, T b) {
+ return (a < b) ? b : a;
+ }
+ template <typename T>
+ inline T Clamp(T x, T low, T high) {
+ return (x > high) ? high : ((x < low) ? low : x);
+ }
+
+ template <typename T>
+ inline T Abs(T x) {
+ // Static cast needed to break abs optimization
+ return x < 0 ? static_cast<T>(-x) : static_cast<T>(x);
+ }
+
+ template <typename T>
+ inline T BitExtract(T bits, int pos, int len) {
+ T mask = (1 << len) - 1;
+ return (bits >> pos) & mask;
+ }
+
+ inline u32 GetIntPtr(const void* pPtr) {
+ return reinterpret_cast<u32>(pPtr);
+ }
+
+ template <typename T>
+ inline const void* AddOffsetToPtr(const void* base, T offset) {
+ return reinterpret_cast<const void*>(GetIntPtr(base) + offset);
+ }
+ template <typename T>
+ inline void* AddOffsetToPtr(void* base, T offset) {
+ return reinterpret_cast<void*>(GetIntPtr(base) + offset);
+ }
+
+ inline s32 GetOffsetFromPtr(const void* start, const void* end) {
+ return static_cast<s32>(GetIntPtr(end) - GetIntPtr(start));
+ }
+
+ inline int ComparePtr(const void* pPtr1, const void* pPtr2) {
+ return static_cast<int>(GetIntPtr(pPtr1) - GetIntPtr(pPtr2));
+ }
+
+ template <typename T>
+ inline T RoundUp(T t, u32 alignment) {
+ return (alignment + t - 1) & ~(alignment - 1);
+ }
+
+ template <typename T>
+ inline void* RoundUp(T* pPtr, u32 alignment) {
+ u32 value = reinterpret_cast<u32>(pPtr);
+ u32 rounded = (alignment + value - 1) & ~(alignment - 1);
+ return reinterpret_cast<void*>(rounded);
+ }
+
+ template <typename T>
+ inline T RoundDown(T t, u32 alignment) {
+ return t & ~(alignment - 1);
+ }
+
+ template <typename T>
+ inline void* RoundDown(T* pPtr, u32 alignment) {
+ u32 value = reinterpret_cast<u32>(pPtr);
+ u32 rounded = value & ~(alignment - 1);
+ return reinterpret_cast<void*>(rounded);
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/list.h b/src/revolution/homebuttonLib/nw4hbm/ut/list.h
new file mode 100644
index 0000000000..dcfc4a000b
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/list.h
@@ -0,0 +1,48 @@
+#ifndef NW4HBM_UT_LIST_H
+#define NW4HBM_UT_LIST_H
+
+#include <revolution/types.h>
+#include "../db/assert.h"
+
+
+namespace nw4hbm {
+ namespace ut {
+
+ typedef struct Link {
+ /* 0x00 */ void* prevObject;
+ /* 0x04 */ void* nextObject;
+ } Link; // size = 0x08
+
+ typedef struct List {
+ /* 0x00 */ void* headObject;
+ /* 0x04 */ void* tailObject;
+ /* 0x08 */ u16 numObjects;
+ /* 0x0A */ u16 offset;
+ } List; // size = 0x0C
+
+ void List_Init(List* list, u16 offset);
+ void List_Append(List* list, void* object);
+ void List_Prepend(List* list, void* object);
+ void List_Insert(List* list, void* target, void* object);
+ void List_Remove(List* list, void* object);
+
+ void* List_GetNext(const List* list, const void* object);
+ void* List_GetPrev(const List* list, const void* object);
+ void* List_GetNth(const List* list, u16 index);
+
+ static void* List_GetFirst(const List* list) {
+ return List_GetNext(list, NULL);
+ }
+ static void* List_GetLast(const List* list) {
+ return List_GetPrev(list, NULL);
+ }
+
+ inline u16 List_GetSize(const List* list) {
+ NW4R_ASSERT_CHECK_NULL(207, list);
+ return list->numObjects;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
+
+#endif
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp
new file mode 100644
index 0000000000..7357a04083
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharStrmReader.cpp
@@ -0,0 +1,79 @@
+#include "CharStrmReader.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ inline bool IsSJISLeadByte(u8 c) {
+ return (0x81 <= c && c < 0xA0) || 0xE0 <= c;
+ }
+
+ u16 CharStrmReader::ReadNextCharUTF8() {
+ NW4HBM_ASSERT_VALID_PTR(76, this);
+ NW4HBM_ASSERT_VALID_PTR(77, mCharStrm);
+ NW4HBM_ASSERT(79, (GetChar<u8>() & 0xC0) != 0x80);
+ u16 code;
+
+ if ((GetChar<u8>(0) & 0x80) == 0x00) {
+ // 1-byte UTF-8 sequence
+ code = GetChar<u8>(0);
+ StepStrm<u8>(1);
+ } else if ((GetChar<u8>(0) & 0xE0) == 0xC0) {
+ // 2-byte UTF-8 sequence
+ code = (GetChar<u8>(0) & 0x1f) << 6 | (GetChar<u8>(1) & 0x3f);
+ StepStrm<u8>(2);
+ } else {
+ // 3-byte UTF-8 sequence
+
+ NW4HBM_ASSERT(100, (GetChar<u8>() & 0xF0) == 0xE0);
+ /* technical ERRATUM: the mask of GetChar<u8>(0) should be 0x0f */
+ code = (GetChar<u8>(0) & 0x1f) << 12 | (GetChar<u8>(1) & 0x3f) << 6 |
+ (GetChar<u8>(2) & 0x3f);
+ StepStrm<u8>(3);
+ }
+
+ /* NOTE: 4-byte to 7-byte UTF-8 sequences usually encode code points outside
+ * of the BMP; I think sticking to the BMP is fine here.
+ */
+
+ return code;
+ }
+
+ u16 CharStrmReader::ReadNextCharUTF16() {
+ NW4HBM_ASSERT_VALID_PTR(129, this);
+ NW4HBM_ASSERT_VALID_PTR(130, mCharStrm);
+ NW4HBM_ASSERT_ALIGN2(131, mCharStrm);
+ u16 code = GetChar<u16>(0);
+ StepStrm<u16>(1);
+
+ return code;
+ }
+
+ u16 CharStrmReader::ReadNextCharCP1252() {
+ NW4HBM_ASSERT_VALID_PTR(155, this);
+ NW4HBM_ASSERT_VALID_PTR(156, mCharStrm);
+ u16 code = GetChar<u8>(0);
+ StepStrm<u8>(1);
+
+ return code;
+ }
+
+ u16 CharStrmReader::ReadNextCharSJIS() {
+ NW4HBM_ASSERT_VALID_PTR(180, this);
+ NW4HBM_ASSERT_VALID_PTR(181, mCharStrm);
+ u16 code;
+
+ if (IsSJISLeadByte(GetChar<u8>(0))) {
+ code = GetChar<u8>(0) << 8 | GetChar<u8>(1);
+ StepStrm<u8>(2);
+ } else {
+ code = GetChar<u8>(0);
+ StepStrm<u8>(1);
+ }
+
+ return code;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp
new file mode 100644
index 0000000000..20bd0919d7
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_CharWriter.cpp
@@ -0,0 +1,414 @@
+#include "CharWriter.h"
+
+#include "../db/assert.h"
+#include "Color.h"
+#include "Font.h"
+
+#include <revolution/gx.h>
+
+namespace nw4hbm {
+
+ static void SetupGXCommon();
+
+ void SetupGXCommon() {
+ static ut::Color fog(0x00000000);
+
+ GXSetFog(GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, fog);
+ GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
+ GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0);
+ GXSetNumChans(1);
+ GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+ GXSetChanCtrl(GX_COLOR1A1, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE,
+ GX_AF_NONE);
+ GXSetNumTexGens(1);
+ GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 60);
+ GXSetNumIndStages(0);
+ GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
+ }
+
+ namespace ut {
+
+ CharWriter::LoadingTexture CharWriter::mLoadingTexture;
+
+ CharWriter::CharWriter()
+ : mAlpha(0xff), mIsWidthFixed(false), mFixedWidth(0.0f), mFont(NULL) {
+ mLoadingTexture.Reset();
+ ResetColorMapping();
+ SetGradationMode(GRADMODE_NONE);
+ SetTextColor(0xffffffff);
+ SetScale(1.0f, 1.0f);
+ SetCursor(0.0f, 0.0f, 0.0f);
+ EnableLinearFilter(true, true);
+ }
+
+ CharWriter::~CharWriter() {}
+
+ void CharWriter::SetFont(const Font& font) {
+ // clang-format off
+ NW4HBM_ASSERT_VALID_PTR(133, this);
+ NW4HBM_ASSERT_VALID_PTR(134, & font);
+ mFont = &font;
+ // clang-format on
+ }
+
+ const Font* CharWriter::GetFont() const {
+ NW4HBM_ASSERT_VALID_PTR(151, this);
+ return mFont;
+ }
+
+ void CharWriter::SetupGX() {
+ NW4HBM_ASSERT_VALID_PTR(173, this);
+ ResetTextureCache();
+
+ if (mColorMapping.min != 0x00000000 || mColorMapping.max != 0xffffffff) {
+ SetupGXWithColorMapping(mColorMapping.min, mColorMapping.max);
+ } else if (mFont) {
+ GXTexFmt format = mFont->GetTextureFormat();
+ switch (format) {
+ case GX_TF_I4:
+ case GX_TF_I8:
+ SetupGXForI();
+ break;
+
+ case GX_TF_IA4:
+ case GX_TF_IA8:
+ SetupGXDefault();
+ break;
+
+ case GX_TF_RGB565:
+ case GX_TF_RGB5A3:
+ case GX_TF_RGBA8:
+ SetupGXForRGBA();
+ break;
+
+ default:
+ NW4R_DB_WARNING(207, false,
+ "CharWriter::SetupGX: Unknown font sheet format(=%d)", format);
+ SetupGXDefault();
+ break;
+ }
+ } else {
+ SetupGXDefault();
+ }
+ }
+
+ void CharWriter::SetColorMapping(Color min, Color max) {
+ NW4HBM_ASSERT_VALID_PTR(235, this);
+ mColorMapping.min = min;
+ mColorMapping.max = max;
+ }
+
+ void CharWriter::ResetColorMapping() {
+ NW4HBM_ASSERT_VALID_PTR(284, this);
+ SetColorMapping(0x00000000, 0xffffffff);
+ }
+
+ void CharWriter::ResetTextureCache() {
+ NW4HBM_ASSERT_VALID_PTR(300, this);
+ mLoadingTexture.Reset();
+ }
+
+ void CharWriter::SetGradationMode(GradationMode mode) {
+ NW4HBM_ASSERT_VALID_PTR(355, this);
+ NW4R_ASSERT_MINMAXLT(356, mode, 0, 2);
+ mTextColor.gradationMode = mode;
+ UpdateVertexColor();
+ }
+
+ void CharWriter::SetTextColor(Color color) {
+ NW4HBM_ASSERT_VALID_PTR(389, this);
+ mTextColor.start = color;
+ UpdateVertexColor();
+ }
+
+ void CharWriter::SetTextColor(Color start, Color end) {
+ NW4HBM_ASSERT_VALID_PTR(410, this);
+ mTextColor.start = start;
+ mTextColor.end = end;
+ UpdateVertexColor();
+ }
+
+ void CharWriter::SetScale(f32 hScale, f32 vScale) {
+ NW4HBM_ASSERT_VALID_PTR(487, this);
+ mScale.x = hScale;
+ mScale.y = vScale;
+ }
+
+ f32 CharWriter::GetScaleH() const {
+ NW4HBM_ASSERT_VALID_PTR(522, this);
+ return mScale.x;
+ }
+
+ f32 CharWriter::GetScaleV() const {
+ NW4HBM_ASSERT_VALID_PTR(538, this);
+ return mScale.y;
+ }
+
+ void CharWriter::SetFontSize(f32 width, f32 height) {
+ NW4HBM_ASSERT_VALID_PTR(559, this);
+ NW4HBM_ASSERT_VALID_PTR(560, mFont);
+ NW4R_ASSERT_MIN(561, mFont->GetWidth(), 1);
+ NW4R_ASSERT_MIN(562, mFont->GetHeight(), 1);
+ SetScale(width / mFont->GetWidth(), height / mFont->GetHeight());
+ }
+
+ f32 CharWriter::GetFontWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(601, this);
+ NW4HBM_ASSERT_VALID_PTR(602, mFont);
+ return mFont->GetWidth() * mScale.x;
+ }
+
+ f32 CharWriter::GetFontHeight() const {
+ NW4HBM_ASSERT_VALID_PTR(618, this);
+ NW4HBM_ASSERT_VALID_PTR(619, mFont);
+ return mFont->GetHeight() * mScale.y;
+ }
+
+ f32 CharWriter::GetFontAscent() const {
+ NW4HBM_ASSERT_VALID_PTR(635, this);
+ NW4HBM_ASSERT_VALID_PTR(636, mFont);
+ return mFont->GetAscent() * mScale.y;
+ }
+
+ void CharWriter::EnableLinearFilter(bool atSmall, bool atLarge) {
+ NW4HBM_ASSERT_VALID_PTR(681, this);
+ mFilter.atSmall = atSmall ? GX_LINEAR : GX_NEAR;
+ mFilter.atLarge = atLarge ? GX_LINEAR : GX_NEAR;
+ }
+
+ bool CharWriter::IsWidthFixed() const {
+ NW4HBM_ASSERT_VALID_PTR(738, this);
+ return mIsWidthFixed;
+ }
+
+ f32 CharWriter::GetFixedWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(769, this);
+ return mFixedWidth;
+ }
+
+ f32 CharWriter::Print(u16 code) {
+ NW4HBM_ASSERT_VALID_PTR(808, this);
+ NW4HBM_ASSERT_VALID_PTR(809, mFont);
+ NW4HBM_ASSERT(810, code != Font::INVALID_CHARACTER_CODE);
+ Glyph glyph;
+ f32 width;
+ f32 left;
+
+ mFont->GetGlyph(&glyph, code);
+
+ CharWidths& widths = glyph.widths;
+
+ if (mIsWidthFixed) {
+ f32 margin = (mFixedWidth - widths.charWidth * mScale.x) / 2.0f;
+
+ width = mFixedWidth;
+ left = margin + widths.left * mScale.x;
+ } else {
+ width = widths.charWidth * mScale.x;
+ left = widths.left * mScale.x;
+ }
+
+ PrintGlyph(mCursorPos.x + left, mCursorPos.y, mCursorPos.z, glyph);
+
+ mCursorPos.x += width;
+ return width;
+ }
+
+ void CharWriter::SetCursor(f32 x, f32 y) {
+ NW4HBM_ASSERT_VALID_PTR(879, this);
+ mCursorPos.x = x;
+ mCursorPos.y = y;
+ }
+
+ void CharWriter::SetCursor(f32 x, f32 y, f32 z) {
+ NW4HBM_ASSERT_VALID_PTR(902, this);
+ mCursorPos.x = x;
+ mCursorPos.y = y;
+ mCursorPos.z = z;
+ }
+
+ void CharWriter::SetCursorX(f32 x) {
+ NW4HBM_ASSERT_VALID_PTR(965, this);
+ mCursorPos.x = x;
+ }
+
+ void CharWriter::SetCursorY(f32 y) {
+ NW4HBM_ASSERT_VALID_PTR(981, this);
+ mCursorPos.y = y;
+ }
+
+ void CharWriter::MoveCursorX(f32 dx) {
+ NW4HBM_ASSERT_VALID_PTR(1013, this);
+ mCursorPos.x += dx;
+ }
+
+ void CharWriter::MoveCursorY(f32 dy) {
+ NW4HBM_ASSERT_VALID_PTR(1029, this);
+ mCursorPos.y += dy;
+ }
+
+ f32 CharWriter::GetCursorX() const {
+ NW4HBM_ASSERT_VALID_PTR(1061, this);
+ return mCursorPos.x;
+ }
+
+ f32 CharWriter::GetCursorY() const {
+ NW4HBM_ASSERT_VALID_PTR(1077, this);
+ return mCursorPos.y;
+ }
+
+ void CharWriter::PrintGlyph(f32 x, f32 y, f32 z, const Glyph& glyph) {
+ // clang-format off
+ NW4HBM_ASSERT_VALID_PTR(1127, this);
+ NW4HBM_ASSERT_VALID_PTR(1128, & glyph);
+ NW4R_ASSERT_MIN(1129, glyph.texWidth, 1);
+ NW4R_ASSERT_MIN(1130, glyph.texHeight, 1);
+ // clang-format on
+
+ f32 posLeft = x;
+ f32 posTop = y;
+ f32 posRight = posLeft + glyph.widths.glyphWidth * mScale.x;
+ f32 posBottom = posTop + glyph.height * mScale.y;
+ f32 posZ = z;
+ u16 texLeft = static_cast<u32>(glyph.cellX << 15) / glyph.texWidth;
+ u16 texTop = static_cast<u32>(glyph.cellY << 15) / glyph.texHeight;
+ u16 texRight =
+ static_cast<u32>((glyph.cellX + glyph.widths.glyphWidth) << 15) / glyph.texWidth;
+ u16 texBottom = static_cast<u32>((glyph.cellY + glyph.height) << 15) / glyph.texHeight;
+
+ LoadTexture(glyph, GX_TEXMAP0);
+
+ GXBegin(GX_QUADS, GX_VTXFMT0, 4);
+ GXPosition3f32(posLeft, posTop, posZ);
+ GXColor1u32(mVertexColor.lu);
+ GXTexCoord2u16(texLeft, texTop);
+
+ GXPosition3f32(posRight, posTop, posZ);
+ GXColor1u32(mVertexColor.ru);
+ GXTexCoord2u16(texRight, texTop);
+
+ GXPosition3f32(posRight, posBottom, posZ);
+ GXColor1u32(mVertexColor.rd);
+ GXTexCoord2u16(texRight, texBottom);
+
+ GXPosition3f32(posLeft, posBottom, posZ);
+ GXColor1u32(mVertexColor.ld);
+ GXTexCoord2u16(texLeft, texBottom);
+ GXEnd();
+ }
+
+ void CharWriter::LoadTexture(const Glyph& glyph, GXTexMapID slot) {
+ // clang-format off
+ NW4HBM_ASSERT_VALID_PTR(1192, this);
+ NW4HBM_ASSERT_VALID_PTR(1193, & glyph);
+ // clang-format on
+
+ LoadingTexture loadInfo;
+ loadInfo.slot = slot;
+ loadInfo.texture = glyph.pTexture;
+ loadInfo.filter = mFilter;
+
+ if (loadInfo != mLoadingTexture) {
+ GXTexObj tobj;
+
+ GXInitTexObj(&tobj, glyph.pTexture, glyph.texWidth, glyph.texHeight,
+ glyph.texFormat, GX_CLAMP, GX_CLAMP, 0);
+ GXInitTexObjLOD(&tobj, mFilter.atSmall, mFilter.atLarge, 0.0f, 0.0f, 0.0f, false,
+ false, GX_ANISO_1);
+ GXLoadTexObj(&tobj, slot);
+
+ mLoadingTexture = loadInfo;
+ }
+ }
+
+ void CharWriter::UpdateVertexColor() {
+ NW4HBM_ASSERT_VALID_PTR(1242, this);
+
+ mVertexColor.lu = mTextColor.start;
+ mVertexColor.ru =
+ mTextColor.gradationMode != GRADMODE_H ? mTextColor.start : mTextColor.end;
+ mVertexColor.ld =
+ mTextColor.gradationMode != GRADMODE_V ? mTextColor.start : mTextColor.end;
+ mVertexColor.rd =
+ mTextColor.gradationMode == GRADMODE_NONE ? mTextColor.start : mTextColor.end;
+
+ mVertexColor.lu.a = mVertexColor.lu.a * mAlpha / 255;
+ mVertexColor.ru.a = mVertexColor.ru.a * mAlpha / 255;
+ mVertexColor.ld.a = mVertexColor.ld.a * mAlpha / 255;
+ mVertexColor.rd.a = mVertexColor.rd.a * mAlpha / 255;
+ }
+
+ void CharWriter::SetupVertexFormat() {
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
+ GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 15);
+
+ GXClearVtxDesc();
+
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
+ GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+ }
+
+ void CharWriter::SetupGXDefault() {
+ SetupGXCommon();
+
+ GXSetNumTevStages(1);
+ GXSetTevDirect(GX_TEVSTAGE0);
+ GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
+ GXSetTevOp(GX_TEVSTAGE0, GX_MODULATE);
+
+ SetupVertexFormat();
+ }
+
+ void CharWriter::SetupGXWithColorMapping(Color min, Color max) {
+ SetupGXCommon();
+
+ GXSetNumTevStages(2);
+ GXSetTevDirect(GX_TEVSTAGE0);
+ GXSetTevDirect(GX_TEVSTAGE1);
+ GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
+
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
+ GXSetTevColor(GX_TEVREG0, min);
+ GXSetTevColor(GX_TEVREG1, max);
+
+ GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_C1, GX_CC_TEXC, GX_CC_ZERO);
+ GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_A1, GX_CA_TEXA, GX_CA_ZERO);
+ GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+
+ GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
+ GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, GX_CC_ZERO);
+ GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_APREV, GX_CA_RASA, GX_CA_ZERO);
+ GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+
+ SetupVertexFormat();
+ }
+
+ void CharWriter::SetupGXForI() {
+ SetupGXCommon();
+
+ GXSetNumTevStages(1);
+ GXSetTevDirect(GX_TEVSTAGE0);
+ GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
+ GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC);
+ GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO);
+ GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+
+ SetupVertexFormat();
+ }
+
+ void CharWriter::SetupGXForRGBA() {
+ SetupGXDefault();
+ }
+
+ } // namespace ut
+
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_DvdFileStream.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_DvdFileStream.cpp
new file mode 100644
index 0000000000..3d7141675a
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_DvdFileStream.cpp
@@ -0,0 +1,9 @@
+#include "DvdFileStream.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(DvdFileStream, FileStream);
+
+ }
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_FileStream.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_FileStream.cpp
new file mode 100644
index 0000000000..7949ba7a63
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_FileStream.cpp
@@ -0,0 +1,71 @@
+#include "FileStream.h"
+
+#include "../db/assert.h"
+#include "inlines.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(FileStream, IOStream);
+
+ void FileStream::Seek(s32 offset, u32 origin) {
+ NW4R_DB_ASSERTMSG(44, CanSeek(), "Stream don't support SEEK function\n");
+ }
+
+ void FileStream::Cancel() {
+ NW4R_DB_ASSERTMSG(60, CanCancel(), "Stream don't support CANCEL function\n");
+ }
+
+ bool FileStream::CancelAsync(IOStreamCallback pCallback, void* pCallbackArg) {
+ #pragma unused(pCallback)
+ #pragma unused(pCallbackArg)
+ NW4R_DB_ASSERTMSG(78, CanCancel(), "Stream don't support CANCEL function\n");
+ NW4R_DB_ASSERTMSG(79, CanAsync(), "Stream don't support ASYNC function\n");
+ return true;
+ }
+
+ u32 FileStream::FilePosition::Skip(s32 offset) {
+ if (offset != 0) {
+ s64 newOffset = mPosition + offset;
+ mPosition = Clamp<s64>(newOffset, 0, mFileSize);
+ }
+
+ return mPosition;
+ }
+
+ u32 FileStream::FilePosition::Append(s32 offset) {
+ s64 newOffset = mPosition + offset;
+
+ if (newOffset < 0) {
+ mPosition = 0;
+ } else {
+ mPosition = newOffset;
+ mFileSize = Max(mPosition, mFileSize);
+ }
+
+ return mPosition;
+ }
+
+ void FileStream::FilePosition::Seek(s32 offset, u32 origin) {
+ switch (origin) {
+ case SEEK_BEG: {
+ mPosition = 0;
+ break;
+ }
+
+ case SEEK_END: {
+ mPosition = mFileSize;
+ break;
+ }
+
+ case SEEK_CUR:
+ default: {
+ break;
+ }
+ }
+
+ Skip(offset);
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp
new file mode 100644
index 0000000000..deb0040be5
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_Font.cpp
@@ -0,0 +1,33 @@
+#include "Font.h"
+
+#include "../db/assert.h"
+#include "CharStrmReader.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ void Font::InitReaderFunc(FontEncoding encoding) {
+ NW4HBM_ASSERT_VALID_PTR(43, this);
+
+ switch (encoding) {
+ case FONT_ENCODING_UTF8:
+ mReaderFunc = &CharStrmReader::ReadNextCharUTF8;
+ break;
+
+ case FONT_ENCODING_UTF16:
+ mReaderFunc = &CharStrmReader::ReadNextCharUTF16;
+ break;
+
+ case FONT_ENCODING_SJIS:
+ mReaderFunc = &CharStrmReader::ReadNextCharSJIS;
+ break;
+
+ default:
+ case FONT_ENCODING_CP1252:
+ mReaderFunc = &CharStrmReader::ReadNextCharCP1252;
+ break;
+ }
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_IOStream.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_IOStream.cpp
new file mode 100644
index 0000000000..e0917511b8
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_IOStream.cpp
@@ -0,0 +1,53 @@
+#include "IOStream.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ NW4HBM_UT_GET_RUNTIME_TYPEINFO(IOStream);
+
+ s32 IOStream::Read(void* pDst, u32 size) {
+ #pragma unused(pDst)
+ #pragma unused(size)
+ #pragma unused(pCallback)
+ #pragma unused(pCallbackArg)
+ NW4R_DB_ASSERTMSG(41, CanRead(), "Stream don't support READ function\n");
+ return 0;
+ }
+
+ bool IOStream::ReadAsync(void* pDst, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ #pragma unused(pDst)
+ #pragma unused(size)
+ #pragma unused(pCallback)
+ #pragma unused(pCallbackArg)
+ NW4R_DB_ASSERTMSG(62, CanRead(), "Stream don't support READ function\n");
+ NW4R_DB_ASSERTMSG(63, CanAsync(), "Stream don't support ASYNC function\n");
+ return false;
+ }
+
+ void IOStream::Write(const void* pSrc, u32 size) {
+ #pragma unused(pSrc)
+ #pragma unused(size)
+ NW4R_DB_ASSERTMSG(82, CanWrite(), "Stream don't support WRITE function\n");
+ }
+
+ bool IOStream::WriteAsync(const void* pSrc, u32 size, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ #pragma unused(pSrc)
+ #pragma unused(size)
+ #pragma unused(pCallback)
+ #pragma unused(pCallbackArg)
+ NW4R_DB_ASSERTMSG(102, CanWrite(), "Stream don't support WRITE function\n");
+ NW4R_DB_ASSERTMSG(103, CanAsync(), "Stream don't support ASYNC function\n");
+ return false;
+ }
+
+ bool IOStream::IsBusy() const {
+ NW4R_DB_ASSERTMSG(142, CanAsync(), "Stream don't support ASYNC function\n");
+ return false;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp
new file mode 100644
index 0000000000..1a812db5ba
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_LinkList.cpp
@@ -0,0 +1,96 @@
+#include "LinkList.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+ namespace detail {
+
+ LinkListImpl::~LinkListImpl() {
+ Clear();
+ }
+
+ LinkListImpl::Iterator LinkListImpl::Erase(Iterator it) {
+ // clang-format off
+ NW4HBM_ASSERT(31, it.mPointer!=&mNode);
+ // clang-format on
+
+ Iterator itNext(it);
+ ++itNext;
+
+ return Erase(it, itNext);
+ }
+
+ LinkListImpl::Iterator LinkListImpl::Erase(LinkListImpl::Iterator itFirst,
+ LinkListImpl::Iterator itLast) {
+ LinkListNode *p = itFirst.mPointer, *pItLast = itLast.mPointer, *pNext;
+
+ while (p != pItLast) {
+ pNext = p->mNext;
+ Erase(p);
+ p = pNext;
+ }
+
+ return itLast;
+ }
+
+ void LinkListImpl::Clear() {
+ Erase(GetBeginIter(), GetEndIter());
+ }
+
+ LinkListImpl::Iterator LinkListImpl::Insert(Iterator it, LinkListNode* p) {
+ NW4HBM_ASSERT_CHECK_NULL(74, p);
+
+ LinkListNode* pIt = it.mPointer;
+ NW4HBM_ASSERT_CHECK_NULL(76, pIt);
+
+ LinkListNode* pItPrev = pIt->mPrev;
+ NW4HBM_ASSERT_CHECK_NULL(79, pItPrev);
+
+ NW4HBM_ASSERT(81, p->mNext == NULL);
+ NW4HBM_ASSERT(82, p->mPrev == NULL);
+ p->mNext = pIt;
+ p->mPrev = pItPrev;
+
+ pIt->mPrev = p;
+ pItPrev->mNext = p;
+
+ mSize++;
+
+ return (Iterator)p;
+ }
+
+ LinkListImpl::Iterator LinkListImpl::Erase(LinkListNode* p) {
+ // clang-format off
+ NW4HBM_ASSERT(96, !IsEmpty());
+ NW4HBM_ASSERT_CHECK_NULL(97, p);
+ NW4HBM_ASSERT(98, p!=&mNode);
+ // clang-format on
+
+ LinkListNode* pNext = p->mNext;
+ LinkListNode* pPrev = p->mPrev;
+
+ SetPrev(pPrev, pNext);
+ SetNext(pPrev, pNext);
+
+ mSize--;
+
+ p->mNext = NULL;
+ p->mPrev = NULL;
+
+ return (Iterator)pNext;
+ }
+
+ void LinkListImpl::SetPrev(LinkListNode* pPrev, LinkListNode* pNext) {
+ NW4HBM_ASSERT_CHECK_NULL(101, pNext);
+ pNext->mPrev = pPrev;
+ }
+
+ void LinkListImpl::SetNext(LinkListNode* pPrev, LinkListNode* pNext) {
+ NW4HBM_ASSERT_CHECK_NULL(103, pPrev);
+ pPrev->mNext = pNext;
+ }
+
+ } // namespace detail
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_NandFileStream.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_NandFileStream.cpp
new file mode 100644
index 0000000000..6eb7c95737
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_NandFileStream.cpp
@@ -0,0 +1,220 @@
+#include "NandFileStream.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ NW4HBM_UT_GET_DERIVED_RUNTIME_TYPEINFO(NandFileStream, FileStream);
+
+ void NandFileStream::NandAsyncCallback_(s32 result, NANDCommandBlock* commandBlock) {
+ NW4HBM_ASSERT_CHECK_NULL(44, commandBlock);
+ NandFileStream* p = reinterpret_cast<NandFileStreamInfo*>(commandBlock)->stream;
+
+ p->mIsBusy = false;
+ p->mAsyncResult = result;
+
+ if (p->mCallback != NULL) {
+ p->mCallback(result, p, p->mArg);
+ }
+ }
+
+ void NandFileStream::Initialize_() {
+ mCanRead = false;
+ mCanWrite = false;
+
+ mCloseOnDestroyFlg = false;
+ mCloseEnableFlg = false;
+
+ mAvailable = false;
+ mIsBusy = false;
+
+ mCallback = NULL;
+ mArg = NULL;
+ mAsyncResult = NAND_RESULT_OK;
+
+ mFileInfo.stream = this;
+ }
+
+ NandFileStream::NandFileStream(const char* path, u32 mode) {
+ NW4HBM_ASSERT_CHECK_NULL(113, path);
+ Initialize_();
+ Open(path, mode);
+ }
+
+ NandFileStream::NandFileStream(const NANDFileInfo* pInfo, u32 mode, bool enableClose) {
+ Initialize_();
+ Open(pInfo, mode, enableClose);
+ }
+
+ NandFileStream::~NandFileStream() {
+ if (mCloseOnDestroyFlg) {
+ Close();
+ }
+ }
+
+ bool NandFileStream::Open(const char* path, u32 mode) {
+ NW4HBM_ASSERT_CHECK_NULL(173, path);
+
+ if (mCloseOnDestroyFlg) {
+ Close();
+ }
+
+ mCanRead = mode & NAND_ACCESS_READ;
+ mCanWrite = mode & NAND_ACCESS_WRITE;
+
+ if (NANDOpen(path, &mFileInfo.nandInfo, mode) != NAND_RESULT_OK) {
+ return false;
+ }
+
+ if (mCanRead) {
+ u32 fileSize;
+
+ if (NANDGetLength(&mFileInfo.nandInfo, &fileSize) != NAND_RESULT_OK) {
+ NANDClose(&mFileInfo.nandInfo);
+ return false;
+ }
+
+ mFilePosition.SetFileSize(fileSize);
+ }
+
+ mFilePosition.Seek(0, SEEK_BEG);
+
+ mCloseOnDestroyFlg = true;
+ mCloseEnableFlg = true;
+ mAvailable = true;
+
+ return true;
+ }
+
+ bool NandFileStream::Open(const NANDFileInfo* pInfo, u32 mode, bool enableClose) {
+ if (mCloseOnDestroyFlg) {
+ Close();
+ }
+
+ mCanRead = mode & NAND_ACCESS_READ;
+ mCanWrite = mode & NAND_ACCESS_WRITE;
+
+ mFileInfo.nandInfo = *pInfo;
+
+ u32 fileSize;
+ if (NANDGetLength(&mFileInfo.nandInfo, &fileSize) != NAND_RESULT_OK) {
+ if (enableClose) {
+ NANDClose(&mFileInfo.nandInfo);
+ }
+
+ return false;
+ }
+
+ mFilePosition.SetFileSize(fileSize);
+ mFilePosition.Seek(0, SEEK_BEG);
+
+ mCloseOnDestroyFlg = false;
+ mCloseEnableFlg = enableClose;
+ mAvailable = true;
+
+ return true;
+ }
+
+ void NandFileStream::Close() {
+ if (mCloseEnableFlg && mAvailable) {
+ NW4R_DB_ASSERTMSG(264, NANDClose(&mFileInfo.nandInfo) == NAND_RESULT_OK,
+ "Can't Close NAND File. It still has been used\n");
+ mAvailable = false;
+ }
+ }
+
+ s32 NandFileStream::Read(void* buf, u32 length) {
+ NW4HBM_ASSERT_ALIGN32(284, buf);
+ NW4HBM_ASSERT_ALIGN32(285, length);
+ NW4R_DB_ASSERTMSG(286, this->IsAvailable() != 0, "NandFileStream is not opened");
+ NANDSeek(&mFileInfo.nandInfo, mFilePosition.Tell(), NAND_SEEK_BEG);
+
+ s32 result = NANDRead(&mFileInfo.nandInfo, buf, length);
+ mFilePosition.Skip(result);
+
+ return result;
+ }
+
+ bool NandFileStream::ReadAsync(void* buf, u32 length, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ NW4HBM_ASSERT_ALIGNED(313, buf, 32);
+ NW4HBM_ASSERT_ALIGNED(314, length, 32);
+ NW4R_DB_ASSERTMSG(315, this->IsAvailable() != 0, "NandFileStream is not opened");
+
+ return ReadAsyncImpl(buf, length, pCallback, pCallbackArg);
+ }
+
+ // fake? ReadAsync requires inlines
+ void NandFileStream::ReadAsyncSetArgs(IOStreamCallback pCallback, void* pCallbackArg) {
+ mCallback = pCallback;
+ mArg = pCallbackArg;
+ mIsBusy = true;
+ }
+
+ // fake? ReadAsync requires inlines
+ bool NandFileStream::ReadAsyncImpl(void* buf, u32 length, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ NW4HBM_ASSERT_ALIGN32(370, buf);
+ NW4HBM_ASSERT_ALIGN32(371, length);
+ NW4R_DB_ASSERTMSG(372, this->IsAvailable() != 0, "NandFileStream is not opened");
+
+ ReadAsyncSetArgs(pCallback, pCallbackArg);
+
+ NANDSeek(&mFileInfo.nandInfo, mFilePosition.Tell(), NAND_SEEK_BEG);
+
+ bool success = NANDReadAsync(&mFileInfo.nandInfo, buf, length, NandAsyncCallback_,
+ &mFileInfo.nandBlock) == NAND_RESULT_OK;
+
+ if (success) {
+ mFilePosition.Skip(length);
+ } else {
+ mIsBusy = false;
+ }
+
+ return success;
+ }
+
+ void NandFileStream::Write(const void* buf, u32 length) {
+ NW4HBM_ASSERT_ALIGN32(396, buf);
+ NW4HBM_ASSERT_ALIGN32(397, length);
+ NW4R_DB_ASSERTMSG(398, this->IsAvailable() != 0, "NandFileStream is not opened");
+
+ NANDSeek(&mFileInfo.nandInfo, mFilePosition.Tell(), NAND_SEEK_BEG);
+ s32 result = NANDWrite(&mFileInfo.nandInfo, buf, length);
+
+ //! @bug: Error code will be interpreted as a negative size
+ mFilePosition.Append(result);
+ }
+
+ bool NandFileStream::WriteAsync(const void* buf, u32 length, IOStreamCallback pCallback,
+ void* pCallbackArg) {
+ NW4HBM_ASSERT_ALIGN32(423, buf);
+ NW4HBM_ASSERT_ALIGN32(424, length);
+ NW4R_DB_ASSERTMSG(425, this->IsAvailable() != 0, "NandFileStream is not opened");
+
+ mCallback = pCallback;
+ mArg = pCallbackArg;
+ mIsBusy = true;
+
+ NANDSeek(&mFileInfo.nandInfo, mFilePosition.Tell(), NAND_SEEK_BEG);
+
+ s32 result = NANDWriteAsync(&mFileInfo.nandInfo, buf, length, NandAsyncCallback_,
+ &mFileInfo.nandBlock);
+
+ if (result == NAND_RESULT_OK) {
+ mFilePosition.Append(length);
+ } else {
+ mIsBusy = false;
+ }
+
+ return result == NAND_RESULT_OK;
+ }
+
+ void NandFileStream::Seek(s32 offset, u32 origin) {
+ NW4R_DB_ASSERTMSG(462, this->IsAvailable() != 0, "NandFileStream is not opened");
+ mFilePosition.Seek(offset, origin);
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp
new file mode 100644
index 0000000000..7b702f9dbc
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFont.cpp
@@ -0,0 +1,204 @@
+#include "ResFont.h"
+
+#include <revolution/hbm.h>
+#include "../db/assert.h"
+
+
+#define MAGIC_FONT 'RFNT' // Revolution Font
+#define MAGIC_FONT_UNPACKED 'RFNU' // Revolution Font, unpacked
+
+#define MAGIC_FONT_INFO 'FINF' // FontInformation
+#define MAGIC_FONT_TEX_GLYPH 'TGLP' // FontTextureGlyph
+#define MAGIC_FONT_CHAR_WIDTH 'CWDH' // Font[Char]Width
+#define MAGIC_FONT_CODE_MAP 'CMAP' // FontCodeMap
+#define MAGIC_FONT_GLGR 'GLGR' // Glyph Group?
+
+#define CONVERT_OFFSET_TO_PTR(type_, ptr_, offset_) \
+ reinterpret_cast<type_*>(reinterpret_cast<u32>(ptr_) + offset_)
+
+namespace nw4hbm {
+ namespace ut {
+
+ namespace {
+ template <typename T>
+ void ResolveOffset(T*& ptr, void* base) {
+ *reinterpret_cast<u32*>(&ptr) =
+ reinterpret_cast<u32>(base) + reinterpret_cast<u32>(ptr);
+ }
+ } // namespace
+
+ ResFont::ResFont() {}
+
+ ResFont::~ResFont() {}
+
+ bool ResFont::SetResource(void* brfnt) {
+ NW4HBM_ASSERT_VALID_PTR(97, this);
+ NW4HBM_ASSERT_VALID_PTR(98, brfnt);
+ NW4HBM_ASSERT_ALIGN32(99, brfnt);
+
+ FontInformation* pFontInfo = NULL;
+ BinaryFileHeader* fileHeader = static_cast<BinaryFileHeader*>(brfnt);
+
+ if (!IsManaging(NULL)) {
+ NW4R_DB_WARNING(107, false, "Font resource already atached.");
+ return false;
+ }
+
+ if (fileHeader->signature == MAGIC_FONT_UNPACKED) {
+ BinaryBlockHeader* blockHeader;
+ int nBlocks = 0;
+
+ blockHeader =
+ CONVERT_OFFSET_TO_PTR(BinaryBlockHeader, fileHeader, fileHeader->headerSize);
+
+ while (nBlocks < fileHeader->dataBlocks) {
+ NW4HBM_ASSERT_VALID_PTR(124, blockHeader);
+ if (blockHeader->kind == MAGIC_FONT_INFO) {
+ pFontInfo = CONVERT_OFFSET_TO_PTR(FontInformation, blockHeader,
+ sizeof(*blockHeader));
+ break;
+ }
+
+ blockHeader =
+ CONVERT_OFFSET_TO_PTR(BinaryBlockHeader, blockHeader, blockHeader->size);
+ nBlocks++;
+ }
+ } else {
+ if (fileHeader->version == NW4HBM_VERSION(1, 4)) {
+ if (!IsValidBinaryFile(fileHeader, MAGIC_FONT, NW4HBM_VERSION(1, 4), 2)) {
+ NW4R_DB_WARNING(150, false, "Invalid font resource.");
+ return false;
+ }
+ } else {
+ if (!IsValidBinaryFile(fileHeader, MAGIC_FONT, NW4HBM_VERSION(1, 2), 2)) {
+ NW4R_DB_WARNING(160, false, "Invalid font resource.");
+ return false;
+ }
+ }
+
+ pFontInfo = Rebuild(fileHeader);
+ }
+
+ if (!pFontInfo) {
+ return false;
+ }
+
+ SetResourceBuffer(brfnt, pFontInfo);
+ InitReaderFunc(GetEncoding());
+
+ return true;
+ }
+
+ void dummyString() {
+ OSReport("ResFont::RemoveResource(): Res font is not loaded.\n");
+ }
+
+ FontInformation* ResFont::Rebuild(BinaryFileHeader* fileHeader) {
+ BinaryBlockHeader* blockHeader;
+ FontInformation* info;
+ int nBlocks;
+
+ NW4HBM_ASSERT_VALID_PTR(218, fileHeader);
+ NW4HBM_ASSERT_ALIGN32(219, fileHeader);
+
+ info = NULL;
+ nBlocks = 0;
+ blockHeader =
+ CONVERT_OFFSET_TO_PTR(BinaryBlockHeader, fileHeader, fileHeader->headerSize);
+
+ while (nBlocks < fileHeader->dataBlocks) {
+ NW4HBM_ASSERT_VALID_PTR(230, blockHeader);
+
+ switch (blockHeader->kind) {
+ case MAGIC_FONT_INFO: {
+ NW4HBM_ASSERT(237, info == NULL);
+ info =
+ CONVERT_OFFSET_TO_PTR(FontInformation, blockHeader, sizeof(*blockHeader));
+
+ NW4HBM_ASSERT(243, info->fontType == FONT_TYPE_NNGCTEXTURE);
+ NW4HBM_ASSERT(244, info->alterCharIndex != GLYPH_INDEX_NOT_FOUND);
+
+ // no check
+ NW4HBM_ASSERT_CHECK_NULL(247, info->pGlyph);
+ ResolveOffset(info->pGlyph, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(249, info->pGlyph);
+
+ if (info->pWidth) {
+ ResolveOffset(info->pWidth, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(255, info->pWidth);
+ }
+
+ if (info->pMap) {
+ ResolveOffset(info->pMap, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(260, info->pMap);
+ }
+ } break;
+
+ case MAGIC_FONT_TEX_GLYPH: {
+ FontTextureGlyph* glyph =
+ CONVERT_OFFSET_TO_PTR(FontTextureGlyph, blockHeader, sizeof(*blockHeader));
+
+ NW4HBM_ASSERT_CHECK_NULL(274, glyph->sheetImage);
+ // no check
+ ResolveOffset(glyph->sheetImage, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(276, glyph->sheetImage);
+ NW4R_ASSERT_MIN(279, glyph->cellWidth, 1);
+ NW4R_ASSERT_MIN(280, glyph->cellHeight, 1);
+ NW4R_ASSERT_MINMAXLT(281, glyph->sheetSize, 0x200, 0x400000);
+ NW4R_ASSERT_MIN(282, glyph->sheetNum, 1);
+ NW4R_ASSERT_MIN(283, glyph->sheetRow, 1);
+ NW4R_ASSERT_MIN(284, glyph->sheetLine, 1);
+ NW4R_ASSERT_MINMAXLT(285, glyph->sheetWidth, 0x20, 0x400);
+ NW4R_ASSERT_MINMAXLT(286, glyph->sheetHeight, 0x20, 0x400);
+ } break;
+
+ case MAGIC_FONT_CHAR_WIDTH: {
+ FontWidth* width =
+ CONVERT_OFFSET_TO_PTR(FontWidth, blockHeader, sizeof(*blockHeader));
+
+ NW4HBM_ASSERT(298, width->indexBegin <= width->indexEnd);
+
+ if (width->pNext) {
+ ResolveOffset(width->pNext, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(303, width->pNext);
+ }
+ } break;
+
+ case MAGIC_FONT_CODE_MAP: {
+ FontCodeMap* map =
+ CONVERT_OFFSET_TO_PTR(FontCodeMap, blockHeader, sizeof(*blockHeader));
+
+ NW4HBM_ASSERT(316, map->ccodeBegin <= map->ccodeEnd);
+ NW4HBM_ASSERT(319, (map->mappingMethod == FONT_MAPMETHOD_DIRECT) ||
+ (map->mappingMethod == FONT_MAPMETHOD_TABLE) ||
+ (map->mappingMethod == FONT_MAPMETHOD_SCAN));
+
+ if (map->pNext) {
+ ResolveOffset(map->pNext, fileHeader);
+ NW4HBM_ASSERT_VALID_PTR(324, map->pNext);
+ }
+ } break;
+
+ case MAGIC_FONT_GLGR:
+ break;
+
+ default:
+ nw4hbm::db::detail::Panic(
+ __FILE__, 345, "The font has unknown block('%c%c%c%c').",
+ blockHeader->kind >> 24, (blockHeader->kind >> 16) & 0xFF,
+ (blockHeader->kind >> 8) & 0xFF, blockHeader->kind & 0xFF);
+ return NULL;
+ }
+
+ blockHeader =
+ CONVERT_OFFSET_TO_PTR(BinaryBlockHeader, blockHeader, blockHeader->size);
+ nBlocks++;
+ }
+
+ fileHeader->signature = MAGIC_FONT_UNPACKED;
+
+ return info;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp
new file mode 100644
index 0000000000..258ba47ed8
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_ResFontBase.cpp
@@ -0,0 +1,268 @@
+#include "ResFont.h"
+
+#include "../db/assert.h"
+
+struct CMapScanEntry {
+ /* 0x00 */ u16 ccode;
+ /* 0x02 */ u16 index;
+}; // size = 0x04
+
+struct CMapInfoScan {
+ /* 0x00 */ u16 num;
+ CMapScanEntry entries[]; // flexible, offset 0x02 (unit size 0x04)
+}; // size = 0x02
+
+namespace nw4hbm {
+ namespace ut {
+ namespace detail {
+
+ ResFontBase::ResFontBase() : mResource(NULL), mFontInfo(NULL) {}
+
+ ResFontBase::~ResFontBase() {}
+
+ void ResFontBase::SetResourceBuffer(void* pUserBuffer, FontInformation* pFontInfo) {
+ NW4HBM_ASSERT_VALID_PTR(79, pUserBuffer);
+ NW4HBM_ASSERT_VALID_PTR(80, pFontInfo);
+ NW4HBM_ASSERT(81, mResource == NULL);
+ NW4HBM_ASSERT(82, mFontInfo == NULL);
+ mResource = pUserBuffer;
+ mFontInfo = pFontInfo;
+ }
+
+ int ResFontBase::GetWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(128, this);
+ NW4HBM_ASSERT_VALID_PTR(129, mFontInfo);
+ return mFontInfo->width;
+ }
+
+ int ResFontBase::GetHeight() const {
+ NW4HBM_ASSERT_VALID_PTR(145, this);
+ NW4HBM_ASSERT_VALID_PTR(146, mFontInfo);
+ return mFontInfo->height;
+ }
+
+ int ResFontBase::GetAscent() const {
+ NW4HBM_ASSERT_VALID_PTR(162, this);
+ NW4HBM_ASSERT_VALID_PTR(163, mFontInfo);
+ return mFontInfo->ascent;
+ }
+
+ int ResFontBase::GetDescent() const {
+ NW4HBM_ASSERT_VALID_PTR(179, this);
+ NW4HBM_ASSERT_VALID_PTR(180, mFontInfo);
+ return mFontInfo->height - mFontInfo->ascent;
+ }
+
+ int ResFontBase::GetBaselinePos() const {
+ NW4HBM_ASSERT_VALID_PTR(196, this);
+ NW4HBM_ASSERT_VALID_PTR(197, mFontInfo);
+ return mFontInfo->pGlyph->baselinePos;
+ }
+
+ int ResFontBase::GetCellHeight() const {
+ NW4HBM_ASSERT_VALID_PTR(213, this);
+ NW4HBM_ASSERT_VALID_PTR(214, mFontInfo);
+ return mFontInfo->pGlyph->cellHeight;
+ }
+
+ int ResFontBase::GetCellWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(230, this);
+ NW4HBM_ASSERT_VALID_PTR(231, mFontInfo);
+ return mFontInfo->pGlyph->cellWidth;
+ }
+
+ int ResFontBase::GetMaxCharWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(247, this);
+ NW4HBM_ASSERT_VALID_PTR(248, mFontInfo);
+ return mFontInfo->pGlyph->maxCharWidth;
+ }
+
+ Font::Type ResFontBase::GetType() const {
+ return TYPE_RESOURCE;
+ }
+
+ GXTexFmt ResFontBase::GetTextureFormat() const {
+ NW4HBM_ASSERT_VALID_PTR(279, this);
+ NW4HBM_ASSERT_VALID_PTR(280, mFontInfo);
+ return static_cast<GXTexFmt>(mFontInfo->pGlyph->sheetFormat);
+ }
+
+ int ResFontBase::GetLineFeed() const {
+ NW4HBM_ASSERT_VALID_PTR(296, this);
+ NW4HBM_ASSERT_VALID_PTR(297, mFontInfo);
+ return mFontInfo->linefeed;
+ }
+
+ CharWidths ResFontBase::GetDefaultCharWidths() const {
+ NW4HBM_ASSERT_VALID_PTR(313, this);
+ NW4HBM_ASSERT_VALID_PTR(314, mFontInfo);
+ return mFontInfo->defaultWidth;
+ }
+
+ void ResFontBase::SetDefaultCharWidths(const CharWidths& widths) {
+ // clang-format off
+ NW4HBM_ASSERT_VALID_PTR(330, this);
+ NW4HBM_ASSERT_VALID_PTR(331, mFontInfo);
+ NW4HBM_ASSERT_VALID_PTR(332, & widths);
+ mFontInfo->defaultWidth = widths;
+ // clang-format on
+ }
+
+ bool ResFontBase::SetAlternateChar(u16 c) {
+ NW4HBM_ASSERT_VALID_PTR(350, this);
+ NW4HBM_ASSERT_VALID_PTR(351, mFontInfo);
+ u16 index = FindGlyphIndex(c);
+
+ if (index != GLYPH_INDEX_NOT_FOUND) {
+ mFontInfo->alterCharIndex = index;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void ResFontBase::SetLineFeed(int linefeed) {
+ NW4HBM_ASSERT_VALID_PTR(375, this);
+ NW4HBM_ASSERT_VALID_PTR(376, mFontInfo);
+ NW4R_ASSERT_MINMAXLT(377, linefeed, -128, 127);
+ mFontInfo->linefeed = linefeed;
+ }
+
+ int ResFontBase::GetCharWidth(u16 c) const {
+ return GetCharWidths(c).charWidth;
+ }
+
+ CharWidths ResFontBase::GetCharWidths(u16 c) const {
+ u16 index = GetGlyphIndex(c);
+
+ return GetCharWidthsFromIndex(index);
+ }
+
+ void ResFontBase::GetGlyph(Glyph* glyph, u16 c) const {
+ u16 index = GetGlyphIndex(c);
+
+ GetGlyphFromIndex(glyph, index);
+ }
+
+ FontEncoding ResFontBase::GetEncoding() const {
+ NW4HBM_ASSERT_VALID_PTR(456, this);
+ NW4HBM_ASSERT_VALID_PTR(457, mFontInfo);
+ return static_cast<FontEncoding>(mFontInfo->encoding);
+ }
+
+ u16 ResFontBase::GetGlyphIndex(u16 c) const {
+ NW4HBM_ASSERT_VALID_PTR(482, this);
+ NW4HBM_ASSERT_VALID_PTR(483, mFontInfo);
+ u16 index = FindGlyphIndex(c);
+
+ return index != GLYPH_INDEX_NOT_FOUND ? index : mFontInfo->alterCharIndex;
+ }
+
+ u16 ResFontBase::FindGlyphIndex(u16 c) const {
+ NW4HBM_ASSERT_VALID_PTR(502, this);
+ NW4HBM_ASSERT_VALID_PTR(503, mFontInfo);
+ FontCodeMap* pMap;
+ for (pMap = mFontInfo->pMap; pMap; pMap = pMap->pNext) {
+ if (pMap->ccodeBegin <= c && c <= pMap->ccodeEnd) {
+ return FindGlyphIndex(pMap, c);
+ }
+ }
+
+ return GLYPH_INDEX_NOT_FOUND;
+ }
+
+ u16 ResFontBase::FindGlyphIndex(const FontCodeMap* pMap, u16 c) const {
+ NW4HBM_ASSERT_VALID_PTR(539, this);
+ NW4HBM_ASSERT_VALID_PTR(540, pMap);
+ u16 index = GLYPH_INDEX_NOT_FOUND;
+
+ switch (pMap->mappingMethod) {
+ case FONT_MAPMETHOD_DIRECT: {
+ u16 offset = *pMap->mapInfo;
+
+ index = c - pMap->ccodeBegin + offset;
+ } break;
+
+ case FONT_MAPMETHOD_TABLE: {
+ int table_index = c - pMap->ccodeBegin;
+
+ index = pMap->mapInfo[table_index];
+ } break;
+
+ case FONT_MAPMETHOD_SCAN: {
+ const CMapInfoScan* scanInfo =
+ reinterpret_cast<const CMapInfoScan*>(pMap->mapInfo);
+
+ const CMapScanEntry* first = scanInfo->entries;
+ const CMapScanEntry* last = scanInfo->entries + (scanInfo->num - 1);
+
+ while (first <= last) {
+ const CMapScanEntry* mid = first + (last - first) / 2;
+
+ if (mid->ccode < c) {
+ first = mid + 1;
+ } else if (c < mid->ccode) {
+ last = mid - 1;
+ } else {
+ index = mid->index;
+ break;
+ }
+ }
+ } break;
+
+ default:
+ nw4hbm::db::detail::Panic(__FILE__, 597, "unknwon MAPMETHOD");
+ break;
+ }
+
+ return index;
+ }
+
+ const CharWidths& ResFontBase::GetCharWidthsFromIndex(u16 index) const {
+ const FontWidth* pWidth;
+
+ NW4HBM_ASSERT_VALID_PTR(615, this);
+ NW4HBM_ASSERT_VALID_PTR(616, mFontInfo);
+ for (pWidth = mFontInfo->pWidth; pWidth; pWidth = pWidth->pNext) {
+ if (pWidth->indexBegin <= index && index <= pWidth->indexEnd) {
+ return GetCharWidthsFromIndex(pWidth, index);
+ }
+ }
+
+ return mFontInfo->defaultWidth;
+ }
+
+ const CharWidths& ResFontBase::GetCharWidthsFromIndex(const FontWidth* pWidth,
+ u16 index) const {
+ NW4HBM_ASSERT_VALID_PTR(651, pWidth);
+ return pWidth->widthTable[index - pWidth->indexBegin];
+ }
+
+ void ResFontBase::GetGlyphFromIndex(Glyph* glyph, u16 index) const {
+ NW4HBM_ASSERT_VALID_PTR(671, this);
+ NW4HBM_ASSERT_VALID_PTR(672, mFontInfo);
+ FontTextureGlyph& tg = *mFontInfo->pGlyph;
+
+ u32 cellsInASheet = tg.sheetRow * tg.sheetLine;
+ u32 sheetNo = index / cellsInASheet;
+ u32 cellNo = index % cellsInASheet;
+ u32 cellUnitX = cellNo % tg.sheetRow;
+ u32 cellUnitY = cellNo / tg.sheetRow;
+ u32 cellPixelX = cellUnitX * (tg.cellWidth + 1);
+ u32 cellPixelY = cellUnitY * (tg.cellHeight + 1);
+ u32 offsetBytes = sheetNo * tg.sheetSize;
+ void* pSheet = tg.sheetImage + offsetBytes;
+
+ glyph->pTexture = pSheet;
+ glyph->widths = GetCharWidthsFromIndex(index);
+ glyph->height = static_cast<u8>(tg.cellHeight);
+ glyph->texFormat = static_cast<GXTexFmt>(tg.sheetFormat);
+ glyph->texWidth = static_cast<u16>(tg.sheetWidth);
+ glyph->texHeight = static_cast<u16>(tg.sheetHeight);
+ glyph->cellX = cellPixelX + 1;
+ glyph->cellY = cellPixelY + 1;
+ }
+
+ } // namespace detail
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp
new file mode 100644
index 0000000000..85009a5f02
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_TagProcessorBase.cpp
@@ -0,0 +1,109 @@
+#include "TagProcessorBase.h"
+
+#include "../db/assert.h"
+#include "TextWriter.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ template <typename T>
+ TagProcessorBase<T>::TagProcessorBase() {}
+
+ template <typename T>
+ TagProcessorBase<T>::~TagProcessorBase() {}
+
+ template <typename T>
+ Operation TagProcessorBase<T>::Process(u16 code, PrintContext<T>* context) {
+ NW4HBM_ASSERT(85, code < ' ');
+ NW4HBM_ASSERT_VALID_PTR(86, context);
+
+ switch (code) {
+ case '\n':
+ ProcessLinefeed(context);
+ return OPERATION_NEXT_LINE;
+
+ case '\t':
+ ProcessTab(context);
+ return OPERATION_NO_CHAR_SPACE;
+
+ default:
+ return OPERATION_DEFAULT;
+ }
+ }
+
+ template <typename T>
+ Operation TagProcessorBase<T>::CalcRect(Rect* pRect, u16 code, PrintContext<T>* context) {
+ NW4HBM_ASSERT_VALID_PTR(132, pRect);
+ NW4HBM_ASSERT(133, code < ' ');
+ NW4HBM_ASSERT_VALID_PTR(134, context);
+
+ switch (code) {
+ case '\n': {
+ TextWriterBase<T>& writer = *context->writer;
+
+ pRect->right = writer.GetCursorX();
+ pRect->top = writer.GetCursorY();
+
+ ProcessLinefeed(context);
+
+ pRect->left = writer.GetCursorX();
+ pRect->bottom = writer.GetCursorY() + context->writer->GetFontHeight();
+
+ pRect->Normalize();
+ }
+ return OPERATION_NEXT_LINE;
+
+ case '\t': {
+ TextWriterBase<T>& writer = *context->writer;
+
+ pRect->left = writer.GetCursorX();
+
+ ProcessTab(context);
+
+ pRect->right = writer.GetCursorX();
+ pRect->top = writer.GetCursorY();
+ pRect->bottom = pRect->top + writer.GetFontHeight();
+
+ pRect->Normalize();
+ }
+ return OPERATION_NO_CHAR_SPACE;
+
+ default:
+ return OPERATION_DEFAULT;
+ }
+ }
+
+ template <typename T>
+ void TagProcessorBase<T>::ProcessLinefeed(PrintContext<T>* context) {
+ NW4HBM_ASSERT_VALID_PTR(195, context);
+ TextWriterBase<T>& writer = *context->writer;
+
+ f32 x = context->xOrigin;
+ f32 y = writer.GetCursorY() + writer.GetLineHeight();
+
+ writer.SetCursor(x, y);
+ }
+
+ template <typename T>
+ void TagProcessorBase<T>::ProcessTab(PrintContext<T>* context) {
+ NW4HBM_ASSERT_VALID_PTR(211, context);
+ TextWriterBase<T>& writer = *context->writer;
+ int tabWidth = writer.GetTabWidth();
+
+ if (tabWidth > 0) {
+ f32 aCharWidth =
+ writer.IsWidthFixed() ? writer.GetFixedWidth() : writer.GetFontWidth();
+ f32 dx = writer.GetCursorX() - context->xOrigin;
+ f32 tabPixel = static_cast<f32>(tabWidth) * aCharWidth;
+ int numTab = static_cast<int>(dx / tabPixel) + 1;
+ f32 x = tabPixel * static_cast<f32>(numTab) + context->xOrigin;
+
+ writer.SetCursorX(x);
+ }
+ }
+
+ template class TagProcessorBase<char>;
+ template class TagProcessorBase<wchar_t>;
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp
new file mode 100644
index 0000000000..76a0f69951
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_TextWriterBase.cpp
@@ -0,0 +1,564 @@
+#include "TextWriterBase.h"
+
+#include "CharWriter.h"
+#include "inlines.h"
+
+//! TODO: remove this
+#define NO_THIS_ASSERT
+#include "Font.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ template <typename charT>
+ u32 TextWriterBase<charT>::mFormatBufferSize = 0x100;
+ template <typename charT>
+ charT* TextWriterBase<charT>::mFormatBuffer;
+ template <typename charT>
+ TagProcessorBase<charT> TextWriterBase<charT>::mDefaultTagProcessor;
+
+ template <typename charT>
+ TextWriterBase<charT>::TextWriterBase()
+ : mCharSpace(0.0f), mLineSpace(0.0f), mTabWidth(4), mDrawFlag(0),
+ mTagProcessor(&mDefaultTagProcessor) {}
+
+ template <typename charT>
+ TextWriterBase<charT>::~TextWriterBase() {}
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetLineHeight(f32 lineHeight) {
+ const Font* font = GetFont();
+ int linefeed = font ? font->GetLineFeed() : 0;
+
+ mLineSpace = lineHeight - linefeed * GetScaleV();
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::GetLineHeight() const {
+ NW4HBM_ASSERT_VALID_PTR(241, this);
+ const Font* font = GetFont();
+ int linefeed = font ? font->GetLineFeed() : 0;
+
+ return mLineSpace + linefeed * GetScaleV();
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetLineSpace(f32 lineSpace) {
+ NW4HBM_ASSERT_VALID_PTR(261, this);
+ mLineSpace = lineSpace;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetCharSpace(f32 charSpace) {
+ NW4HBM_ASSERT_VALID_PTR(278, this);
+ mCharSpace = charSpace;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::GetLineSpace() const {
+ return mLineSpace;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::GetCharSpace() const {
+ NW4HBM_ASSERT_VALID_PTR(312, this);
+ return mCharSpace;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetTabWidth(int tabWidth) {
+ mTabWidth = tabWidth;
+ }
+
+ template <typename charT>
+ int TextWriterBase<charT>::GetTabWidth() const {
+ NW4HBM_ASSERT_VALID_PTR(346, this);
+ return mTabWidth;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetDrawFlag(u32 flags) {
+ mDrawFlag = flags;
+ }
+
+ template <typename charT>
+ u32 TextWriterBase<charT>::GetDrawFlag() const {
+ return mDrawFlag;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::SetTagProcessor(TagProcessorBase<charT>* tagProcessor) {
+ NW4HBM_ASSERT_VALID_PTR(114, this);
+ NW4HBM_ASSERT_VALID_PTR(115, tagProcessor);
+ mTagProcessor = tagProcessor;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::ResetTagProcessor() {
+ mTagProcessor = &mDefaultTagProcessor;
+ }
+
+ template <typename charT>
+ TagProcessorBase<charT>& TextWriterBase<charT>::GetTagProcessor() const {
+ NW4HBM_ASSERT_VALID_PTR(151, this);
+ return *mTagProcessor;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcFormatStringWidth(const charT* format, ...) const {
+ Rect rect;
+ std::va_list vargs;
+
+ va_start(vargs, format);
+ CalcVStringRect(&rect, format, vargs);
+ va_end(vargs);
+
+ return rect.GetWidth();
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcFormatStringHeight(const charT* format, ...) const {
+ Rect rect;
+ std::va_list vargs;
+
+ va_start(vargs, format);
+ CalcVStringRect(&rect, format, vargs);
+ va_end(vargs);
+
+ return rect.GetHeight();
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::CalcFormatStringRect(Rect* pRect, const charT* format,
+ ...) const {
+ std::va_list vargs;
+
+ va_start(vargs, format);
+ CalcVStringRect(pRect, format, vargs);
+ va_end(vargs);
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::AdjustCursor(float* xOrigin, float* yOrigin, const charT* str,
+ int length) {
+ f32 textWidth = 0.0f;
+ f32 textHeight = 0.0f;
+
+ if (!IsDrawFlagSet(0x333, 0x300) && !IsDrawFlagSet(0x333, 0x0)) {
+ Rect textRect;
+
+ CalcStringRect(&textRect, str, length);
+ textWidth = textRect.left + textRect.right;
+ textHeight = textRect.top + textRect.bottom;
+ }
+
+ if (IsDrawFlagSet(0x30, 0x10)) {
+ *xOrigin -= textWidth / 2.0f;
+ } else if (IsDrawFlagSet(0x30, 0x20)) {
+ *xOrigin -= textWidth;
+ }
+
+ if (IsDrawFlagSet(0x300, 0x100)) {
+ *yOrigin -= textHeight / 2.0f;
+ } else if (IsDrawFlagSet(0x300, 0x200)) {
+ *yOrigin -= textHeight;
+ }
+
+ if (IsDrawFlagSet(0x3, 0x1)) {
+ f32 width = CalcLineWidth(str, length);
+ f32 offset = (textWidth - width) / 2.0f;
+ SetCursorX(*xOrigin + offset);
+ } else if (IsDrawFlagSet(0x3, 0x2)) {
+ f32 width = CalcLineWidth(str, length);
+ f32 offset = textWidth - width;
+ SetCursorX(*xOrigin + offset);
+ } else {
+ SetCursorX(*xOrigin);
+ }
+
+ if (IsDrawFlagSet(0x300, 0x300)) {
+ SetCursorY(*yOrigin);
+ } else {
+ SetCursorY(*yOrigin + GetFontAscent());
+ }
+
+ return textWidth;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::PrintImpl(const charT* str, int length) {
+ NW4HBM_ASSERT_VALID_PTR(1055, this);
+ NW4HBM_ASSERT_VALID_PTR(1056, str);
+ NW4HBM_ASSERT_VALID_PTR(1057, GetFont());
+ NW4R_ASSERT_MIN(1058, length, 0);
+ f32 xOrigin = GetCursorX();
+ f32 yOrigin = GetCursorY();
+ f32 orgCursorX = xOrigin;
+ f32 orgCursorY = yOrigin;
+ f32 xCursorAdj = 0.0f;
+ f32 yCursorAdj = 0.0f;
+ f32 textWidth = 0.0f;
+ bool bCharSpace = false;
+
+ textWidth = AdjustCursor(&xOrigin, &yOrigin, str, length);
+ xCursorAdj = orgCursorX - GetCursorX();
+ yCursorAdj = orgCursorY - GetCursorY();
+
+ PrintContext<charT> context = {this, str, xOrigin, yOrigin, 0};
+ CharStrmReader reader = GetFont()->GetCharStrmReader();
+ reader.Set(str);
+
+ u16 code = reader.Next();
+
+ while (static_cast<const charT*>(reader.GetCurrentPos()) - str <= length) {
+ if (code < ' ') { // C0 control characters under space
+ context.str = static_cast<const charT*>(reader.GetCurrentPos());
+ context.flags = 0;
+ context.flags |= bCharSpace ? FALSE : TRUE;
+
+ Operation operation = mTagProcessor->Process(code, &context);
+
+ if (operation == OPERATION_NEXT_LINE) {
+ NW4HBM_ASSERT_VALID_PTR(1097, context.str);
+
+ if (IsDrawFlagSet(0x3, 0x1)) {
+ int remain = length - (context.str - str);
+ f32 width = CalcLineWidth(context.str, remain);
+ f32 offset = (textWidth - width) / 2.0f;
+
+ SetCursorX(context.xOrigin + offset);
+ } else if (IsDrawFlagSet(0x3, 0x2)) {
+ int remain = length - (context.str - str);
+ f32 width = CalcLineWidth(context.str, remain);
+ f32 offset = textWidth - width;
+
+ SetCursorX(context.xOrigin + offset);
+ } else {
+ f32 width = GetCursorX() - context.xOrigin;
+
+ textWidth = Max(textWidth, width);
+ SetCursorX(context.xOrigin);
+ }
+ bCharSpace = false;
+ } else if (operation == OPERATION_NO_CHAR_SPACE) {
+ bCharSpace = false;
+ } else if (operation == OPERATION_CHAR_SPACE) {
+ bCharSpace = true;
+ } else if (operation == OPERATION_END_DRAW) {
+ break;
+ }
+
+ NW4HBM_ASSERT_VALID_PTR(1137, context.str);
+ reader.Set(context.str);
+ } else {
+ f32 baseY = GetCursorY();
+
+ if (bCharSpace) {
+ MoveCursorX(GetCharSpace());
+ }
+
+ bCharSpace = true;
+
+ {
+ const Font* pFont = GetFont();
+ f32 adj = -pFont->GetBaselinePos() * GetScaleV();
+
+ MoveCursorY(adj);
+ }
+
+ CharWriter::Print(code);
+ SetCursorY(baseY);
+ }
+
+ code = reader.Next();
+ }
+
+ if (IsDrawFlagSet(0x300, 0x100) || IsDrawFlagSet(0x300, 0x200)) {
+ SetCursorY(orgCursorY);
+ } else {
+ MoveCursorY(yCursorAdj);
+ }
+
+ return textWidth;
+ }
+
+ static void ut_TextWriterBase_dummy(void* buffer, int size) {
+ NW4HBM_ASSERT_VALID_PTR(0, buffer);
+ NW4R_ASSERT_MIN(0, size, 0);
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::CalcStringRectImpl(Rect* pRect, const charT* str, int length) {
+ NW4HBM_ASSERT_VALID_PTR(1010, this);
+ NW4HBM_ASSERT_VALID_PTR(1011, pRect);
+ NW4HBM_ASSERT_VALID_PTR(1012, str);
+ NW4R_ASSERT_MIN(1013, length, 0);
+ int remain = length;
+ const charT* pos = str;
+
+ pRect->left = 0.0;
+ pRect->right = 0.0;
+ pRect->top = 0.0;
+ pRect->bottom = 0.0;
+
+ SetCursor(0.0f, 0.0f);
+
+ do {
+ Rect rect;
+ int read = CalcLineRectImpl(&rect, pos, remain);
+
+ pos += read;
+ remain -= read;
+
+ pRect->left = Min(pRect->left, rect.left);
+ pRect->top = Min(pRect->top, rect.top);
+ pRect->right = Max(pRect->right, rect.right);
+ pRect->bottom = Max(pRect->bottom, rect.bottom);
+ } while (remain > 0);
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::ut_TextWriterBase_unused1(Rect* pRect, const charT* str,
+ int length) {
+ void* buffer = 0;
+ int size = 0;
+
+ NW4HBM_ASSERT_VALID_PTR(0, this);
+ NW4HBM_ASSERT_VALID_PTR(0, pRect);
+ NW4HBM_ASSERT_VALID_PTR(0, str);
+ NW4R_ASSERT_MIN(0, length, 0);
+ NW4HBM_ASSERT_VALID_PTR(0, buffer);
+ NW4R_ASSERT_MIN(0, size, 1);
+ }
+
+ template <typename charT>
+ int TextWriterBase<charT>::CalcLineRectImpl(Rect* pRect, const charT* str, int length) {
+ NW4HBM_ASSERT_VALID_PTR(893, this);
+ NW4HBM_ASSERT_VALID_PTR(894, pRect);
+ NW4HBM_ASSERT_VALID_PTR(895, str);
+ NW4R_ASSERT_MIN(896, length, 0);
+ PrintContext<charT> context = {this, str, 0.0f, 0.0f, 0};
+
+ const Font* font = GetFont();
+ f32 x = 0.0f;
+ bool bCharSpace = false;
+
+ NW4HBM_ASSERT_VALID_PTR(902, font);
+ CharStrmReader reader = font->GetCharStrmReader();
+
+ pRect->left = 0.0f;
+ pRect->right = 0.0f;
+ pRect->top = Min(0.0f, GetLineHeight());
+ pRect->bottom = Max(0.0f, GetLineHeight());
+
+ reader.Set(str);
+
+ for (u16 code = reader.Next();
+ static_cast<const charT*>(reader.GetCurrentPos()) - str <= length;
+ code = reader.Next())
+ {
+ if (code < ' ') {
+ Operation operation;
+ Rect rect(x, 0.0f, 0.0f, 0.0f);
+
+ context.str = static_cast<const charT*>(reader.GetCurrentPos());
+ context.flags = 0;
+ context.flags |= bCharSpace ? FALSE : TRUE;
+
+ SetCursorX(x);
+
+ operation = mTagProcessor->CalcRect(&rect, code, &context);
+
+ NW4HBM_ASSERT_VALID_PTR(936, context.str);
+ reader.Set(context.str);
+
+ pRect->left = Min(pRect->left, rect.left);
+ pRect->top = Min(pRect->top, rect.top);
+ pRect->right = Max(pRect->right, rect.right);
+ pRect->bottom = Max(pRect->bottom, rect.bottom);
+
+ x = GetCursorX();
+
+ if (operation == OPERATION_END_DRAW) {
+ return length;
+ } else if (operation == OPERATION_NO_CHAR_SPACE) {
+ bCharSpace = false;
+ } else if (operation == OPERATION_CHAR_SPACE) {
+ bCharSpace = true;
+ } else if (operation == OPERATION_NEXT_LINE) {
+ break;
+ }
+ } else {
+ if (bCharSpace) {
+ x += GetCharSpace();
+ }
+
+ bCharSpace = true;
+
+ if (IsWidthFixed()) {
+ x += GetFixedWidth();
+ } else {
+ x += GetFont()->GetCharWidth(code) * GetScaleH();
+ }
+
+ pRect->left = Min(pRect->left, x);
+ pRect->right = Max(pRect->right, x);
+ }
+ }
+
+ return static_cast<const charT*>(reader.GetCurrentPos()) - str;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcLineWidth(const charT* str, int length) {
+ NW4HBM_ASSERT_VALID_PTR(861, this);
+ NW4HBM_ASSERT_VALID_PTR(862, str);
+ NW4R_ASSERT_MIN(863, length, 0);
+ Rect rect;
+ TextWriterBase<charT> myCopy(*this);
+
+ myCopy.SetCursor(0.0f, 0.0f);
+ myCopy.CalcLineRectImpl(&rect, str, length);
+ return rect.GetWidth();
+ }
+
+ template <typename charT>
+ charT* TextWriterBase<charT>::GetBuffer() {
+ return mFormatBuffer;
+ }
+
+ template <typename charT>
+ u32 TextWriterBase<charT>::GetBufferSize() {
+ return mFormatBufferSize;
+ }
+
+ template <typename charT>
+ charT* TextWriterBase<charT>::SetBuffer(u32 size) {
+ charT* oldBuffer = mFormatBuffer;
+
+ mFormatBuffer = NULL;
+ mFormatBufferSize = size;
+
+ return oldBuffer;
+ }
+
+ template <typename charT>
+ charT* TextWriterBase<charT>::SetBuffer(charT* buffer, u32 size) {
+ charT* oldBuffer = mFormatBuffer;
+
+ mFormatBuffer = buffer;
+ mFormatBufferSize = size;
+
+ return oldBuffer;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::Print(const charT* str) {
+ NW4HBM_ASSERT_VALID_PTR(733, this);
+ NW4HBM_ASSERT_VALID_PTR(734, str);
+ return Print(str, StrLen(str));
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::Print(const charT* str, int length) {
+ NW4HBM_ASSERT_VALID_PTR(705, this);
+ NW4HBM_ASSERT_VALID_PTR(706, str);
+ NW4R_ASSERT_MIN(707, length, 0);
+ TextWriterBase<charT> myCopy(*this);
+
+ f32 width = myCopy.PrintImpl(str, length);
+ SetCursor(myCopy.GetCursorX(), myCopy.GetCursorY());
+
+ return width;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::VPrintf(const charT* format, std::va_list args) {
+ NW4HBM_ASSERT_VALID_PTR(678, this);
+ NW4HBM_ASSERT_VALID_PTR(679, format);
+
+ // i did not know about alloca before this TU so thank you kiwi
+ charT* buffer =
+ mFormatBuffer ? mFormatBuffer : static_cast<charT*>(__alloca(mFormatBufferSize));
+
+ int length = VSNPrintf(buffer, mFormatBufferSize, format, args);
+ f32 width = Print(buffer, length);
+ return width;
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::Printf(const charT* format, ...) {
+ NW4HBM_ASSERT_VALID_PTR(650, this);
+ NW4HBM_ASSERT_VALID_PTR(651, format);
+ std::va_list vargs;
+
+ va_start(vargs, format);
+ f32 width = VPrintf(format, vargs);
+ va_end(vlist);
+
+ return width;
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::CalcStringRect(Rect* pRect, const charT* str) const {
+ CalcStringRect(pRect, str, StrLen(str));
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::CalcStringRect(Rect* pRect, const charT* str,
+ int length) const {
+ NW4HBM_ASSERT_VALID_PTR(548, this);
+ NW4HBM_ASSERT_VALID_PTR(549, pRect);
+ NW4HBM_ASSERT_VALID_PTR(550, str);
+ NW4R_ASSERT_MIN(551, length, 0);
+ TextWriterBase<charT> myCopy(*this);
+
+ myCopy.CalcStringRectImpl(pRect, str, length);
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcStringHeight(const charT* str) const {
+ return CalcStringHeight(str, StrLen(str));
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcStringHeight(const charT* str, int length) const {
+ Rect rect;
+
+ CalcStringRect(&rect, str, length);
+ return rect.GetHeight();
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcStringWidth(const charT* str) const {
+ return CalcStringWidth(str, StrLen(str));
+ }
+
+ template <typename charT>
+ f32 TextWriterBase<charT>::CalcStringWidth(const charT* str, int length) const {
+ Rect rect;
+
+ CalcStringRect(&rect, str, length);
+ return rect.GetWidth();
+ }
+
+ template <typename charT>
+ void TextWriterBase<charT>::CalcVStringRect(Rect* pRect, const charT* format,
+ std::va_list args) const {
+ NW4HBM_ASSERT_VALID_PTR(0, this);
+ NW4HBM_ASSERT_VALID_PTR(0, format);
+ NW4HBM_ASSERT_VALID_PTR(0, pRect);
+ charT* buffer =
+ mFormatBuffer ? mFormatBuffer : static_cast<charT*>(__alloca(mFormatBufferSize));
+
+ int length = VSNPrintf(buffer, mFormatBufferSize, format, args);
+ CalcStringRect(pRect, buffer, length);
+ }
+
+ template class TextWriterBase<char>;
+ template class TextWriterBase<wchar_t>;
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp
new file mode 100644
index 0000000000..4bc3a7b181
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_binaryFileFormat.cpp
@@ -0,0 +1,63 @@
+#include "binaryFileFormat.h"
+
+#include "../db/assert.h"
+
+namespace nw4hbm {
+ namespace ut {
+
+ bool IsValidBinaryFile(const BinaryFileHeader* header, u32 signature, u16 version,
+ u16 minBlocks) {
+ NW4HBM_ASSERT_VALID_PTR(48, header);
+
+ if (header->signature != signature) {
+ s8 signature1 = (header->signature >> 24) & 0xFF;
+ s8 signature2 = (header->signature >> 16) & 0xFF;
+ s8 signature3 = (header->signature >> 8) & 0xFF;
+ s8 signature4 = (header->signature & 0xFF);
+
+ s8 signature5 = (signature >> 24) & 0xFF;
+ s8 signature6 = (signature >> 16) & 0xFF;
+ s8 signature7 = (signature >> 8) & 0xFF;
+ s8 signature8 = (signature & 0xFF);
+
+ NW4R_DB_WARNING(60, false,
+ "Signature check failed ('%c%c%c%c' must be '%c%c%c%c').",
+ signature1, signature2, signature3, signature4, signature5,
+ signature6, signature7, signature8);
+ return false;
+ }
+
+ // U+FEFF * BYTE ORDER MARK
+ if (header->byteOrder != 0xFEFF) {
+ NW4R_DB_WARNING(65, false, "Unsupported byte order.");
+ return false;
+ }
+
+ if (header->version != version) {
+ NW4R_DB_WARNING(75, false, "Version check faild ('%d.%d' must be '%d.%d').",
+ (header->version >> 8) & 0xFF, header->version & 0xFF,
+ (version >> 8) & 0xFF, version & 0xFF);
+ return false;
+ }
+
+ if (header->fileSize < sizeof(*header) + sizeof(BinaryBlockHeader) * minBlocks) {
+ NW4R_DB_WARNING(80, false, "Too small file size(=%d).", header->fileSize);
+ return false;
+ }
+
+ if (header->dataBlocks < minBlocks) {
+ NW4R_DB_WARNING(85, false, "Too small number of data blocks(=%d).",
+ header->dataBlocks);
+ return false;
+ }
+
+ return true;
+ }
+
+ // required to match .data
+ void dummy(const BinaryFileHeader* fileHeader) {
+ NW4HBM_ASSERT_VALID_PTR(0, fileHeader);
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp b/src/revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp
new file mode 100644
index 0000000000..a951a75eb2
--- /dev/null
+++ b/src/revolution/homebuttonLib/nw4hbm/ut/ut_list.cpp
@@ -0,0 +1,102 @@
+#include "list.h"
+
+#include "../db/assert.h"
+
+#define OBJ_TO_NODE(list_, object_) \
+ reinterpret_cast<Link*>(reinterpret_cast<u32>(object_) + (list_)->offset)
+
+namespace nw4hbm {
+ namespace ut {
+
+ void List_Init(List* list, u16 offset) {
+ NW4HBM_ASSERT_CHECK_NULL(41, list);
+ list->headObject = NULL;
+ list->tailObject = NULL;
+ list->numObjects = 0;
+ list->offset = offset;
+ }
+
+ static void SetFirstObject(List* list, void* object) {
+ NW4HBM_ASSERT_CHECK_NULL(64, list);
+ NW4HBM_ASSERT_CHECK_NULL(65, object);
+
+ Link* link = OBJ_TO_NODE(list, object);
+
+ link->nextObject = NULL;
+ link->prevObject = NULL;
+ list->headObject = object;
+ list->tailObject = object;
+ list->numObjects++;
+ }
+
+ void List_Append(List* list, void* object) {
+ NW4HBM_ASSERT_CHECK_NULL(89, list);
+ NW4HBM_ASSERT_CHECK_NULL(90, object);
+
+ if (list->headObject == NULL) {
+ SetFirstObject(list, object);
+ return;
+ }
+
+ Link* link = OBJ_TO_NODE(list, object);
+
+ link->prevObject = list->tailObject;
+ link->nextObject = NULL;
+
+ OBJ_TO_NODE(list, list->tailObject)->nextObject = object;
+
+ list->tailObject = object;
+ list->numObjects++;
+ }
+
+ void List_Remove(List* list, void* object) {
+ NW4HBM_ASSERT_CHECK_NULL(203, list);
+ NW4HBM_ASSERT_CHECK_NULL(204, object);
+
+ Link* link = OBJ_TO_NODE(list, object);
+
+ if (!link->prevObject) {
+ list->headObject = link->nextObject;
+ } else {
+ OBJ_TO_NODE(list, link->prevObject)->nextObject = link->nextObject;
+ }
+
+ if (!link->nextObject) {
+ list->tailObject = link->prevObject;
+ } else {
+ OBJ_TO_NODE(list, link->nextObject)->prevObject = link->prevObject;
+ }
+
+ link->prevObject = NULL;
+ link->nextObject = NULL;
+
+ list->numObjects--;
+ }
+
+ void* List_GetNext(const List* list, const void* object) {
+ NW4HBM_ASSERT_CHECK_NULL(245, list);
+
+ if (object == NULL) {
+ return list->headObject;
+ }
+
+ return OBJ_TO_NODE(list, object)->nextObject;
+ }
+
+ void* List_GetNth(const List* list, u16 index) {
+ int count = 0;
+ Link* object = NULL;
+
+ NW4HBM_ASSERT_CHECK_NULL(297, list);
+
+ for (; (object = static_cast<Link*>(List_GetNext(list, object))); count++) {
+ if (index == count) {
+ return object;
+ }
+ }
+
+ return NULL;
+ }
+
+ } // namespace ut
+} // namespace nw4hbm
diff --git a/src/revolution/homebuttonLib/sound/mix.cpp b/src/revolution/homebuttonLib/sound/mix.cpp
new file mode 100644
index 0000000000..36dcc452ae
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/mix.cpp
@@ -0,0 +1,727 @@
+#include "mix.h"
+
+
+#include <string.h>
+
+#include <revolution/types.h>
+
+// #include <revolution/ax.h>
+// #include <revolution/os.h>
+// #include <revolution/os/OSInterrupt.h>
+
+
+
+#define HBMMIX_MAX_CHANNELS 0x10
+#define HBMMIX_MAX_INDICES 0x60
+#define HBMMIX_MAX_KEYS 0x10
+
+
+enum HBMMIXChannelModeFlags
+{
+ // TODO: figure out what these represent
+ HBMMIX_FLAG_31 = 1u << 31,
+ HBMMIX_FLAG_30 = 1 << 30,
+ HBMMIX_FLAG_29 = 1 << 29,
+ HBMMIX_FLAG_28 = 1 << 28,
+};
+
+typedef struct HBMMIXChannel
+{
+ AXVPB *axvpb; // size 0x04, offset 0x00
+ HBMMIXSoundMode mode; // size 0x04, offset 0x04
+ int input; // size 0x04, offset 0x08
+ int auxA; // size 0x04, offset 0x0c
+ int pan; // size 0x04, offset 0x10
+ int fader; // size 0x04, offset 0x14
+ int l; // size 0x04, offset 0x18
+ int r; // size 0x04, offset 0x1c
+ int f; // size 0x04, offset 0x20
+ int b; // size 0x04, offset 0x24
+ int l1; // size 0x04, offset 0x28
+ int r1; // size 0x04, offset 0x2c
+ u16 v; // size 0x02, offset 0x30
+ u16 v1; // size 0x02, offset 0x32
+ u16 vL; // size 0x02, offset 0x34
+ u16 vL1; // size 0x02, offset 0x36
+ u16 vR; // size 0x02, offset 0x38
+ u16 vR1; // size 0x02, offset 0x3a
+ u16 vS; // size 0x02, offset 0x3c
+ u16 vS1; // size 0x02, offset 0x3e
+ u16 vAL; // size 0x02, offset 0x40
+ u16 vAL1; // size 0x02, offset 0x42
+ u16 vAR; // size 0x02, offset 0x44
+ u16 vAR1; // size 0x02, offset 0x46
+ u16 vAS; // size 0x02, offset 0x48
+ u16 vAS1; // size 0x02, offset 0x4a
+ u16 vBL; // size 0x02, offset 0x4c
+ u16 vBL1; // size 0x02, offset 0x4e
+ u16 vBR; // size 0x02, offset 0x50
+ u16 vBR1; // size 0x02, offset 0x52
+ u16 vBS; // size 0x02, offset 0x54
+ u16 vBS1; // size 0x02, offset 0x56
+ u16 vCL; // size 0x02, offset 0x58
+ u16 vCL1; // size 0x02, offset 0x5a
+ u16 vCR; // size 0x02, offset 0x5c
+ u16 vCR1; // size 0x02, offset 0x5e
+ u16 vCS; // size 0x02, offset 0x60
+ u16 vCS1; // size 0x02, offset 0x62
+} HBMMIXChannel; // size 0x64
+
+
+static void __HBMMIXSetPan(HBMMIXChannel *channel);
+static void __HBMMIXResetChannel(HBMMIXChannel *channel);
+static int __HBMMIXClampPan(int pan);
+static u16 __HBMMIXGetVolume(int db_);
+
+
+// .data
+static u16 __HBMMIXVolumeTable[964 + 1] =
+{
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12,
+ 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13,
+ 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15,
+ 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18,
+ 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20,
+ 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23,
+ 23, 23, 24, 24, 24, 24, 25, 25, 25, 26, 26,
+ 26, 26, 27, 27, 27, 28, 28, 28, 29, 29, 29,
+ 30, 30, 30, 31, 31, 32, 32, 32, 33, 33, 33,
+ 34, 34, 35, 35, 35, 36, 36, 37, 37, 38, 38,
+ 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
+ 44, 44, 45, 45, 46, 46, 47, 47, 48, 49, 49,
+ 50, 50, 51, 51, 52, 53, 53, 54, 55, 55, 56,
+ 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 63,
+ 64, 65, 66, 66, 67, 68, 69, 70, 70, 71, 72,
+ 73, 74, 75, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 106,
+ 107, 108, 109, 111, 112, 113, 114, 116, 117, 118, 120,
+ 121, 123, 124, 126, 127, 128, 130, 131, 133, 135, 136,
+ 138, 139, 141, 143, 144, 146, 148, 149, 151, 153, 155,
+ 156, 158, 160, 162, 164, 166, 168, 170, 171, 173, 175,
+ 178, 180, 182, 184, 186, 188, 190, 192, 195, 197, 199,
+ 202, 204, 206, 209, 211, 214, 216, 219, 221, 224, 226,
+ 229, 231, 234, 237, 240, 242, 245, 248, 251, 254, 257,
+ 260, 263, 266, 269, 272, 275, 278, 282, 285, 288, 292,
+ 295, 298, 302, 305, 309, 312, 316, 320, 323, 327, 331,
+ 335, 339, 343, 347, 351, 355, 359, 363, 367, 371, 376,
+ 380, 384, 389, 393, 398, 403, 407, 412, 417, 422, 427,
+ 431, 436, 442, 447, 452, 457, 462, 468, 473, 479, 484,
+ 490, 495, 501, 507, 513, 519, 525, 531, 537, 543, 550,
+ 556, 562, 569, 576, 582, 589, 596, 603, 610, 617, 624,
+ 631, 638, 646, 653, 661, 669, 676, 684, 692, 700, 708,
+ 716, 725, 733, 742, 750, 759, 768, 777, 786, 795, 804,
+ 813, 823, 832, 842, 852, 861, 871, 881, 892, 902, 912,
+ 923, 934, 945, 955, 967, 978, 989, 1001, 1012, 1024, 1036,
+ 1048, 1060, 1072, 1085, 1097, 1110, 1123, 1136, 1149, 1162, 1176,
+ 1189, 1203, 1217, 1231, 1245, 1260, 1274, 1289, 1304, 1319, 1334,
+ 1350, 1365, 1381, 1397, 1414, 1430, 1446, 1463, 1480, 1497, 1515,
+ 1532, 1550, 1568, 1586, 1604, 1623, 1642, 1661, 1680, 1700, 1719,
+ 1739, 1759, 1780, 1800, 1821, 1842, 1864, 1885, 1907, 1929, 1951,
+ 1974, 1997, 2020, 2043, 2067, 2091, 2115, 2140, 2164, 2190, 2215,
+ 2241, 2266, 2293, 2319, 2346, 2373, 2401, 2429, 2457, 2485, 2514,
+ 2543, 2573, 2602, 2632, 2663, 2694, 2725, 2757, 2789, 2821, 2853,
+ 2887, 2920, 2954, 2988, 3023, 3058, 3093, 3129, 3165, 3202, 3239,
+ 3276, 3314, 3353, 3391, 3431, 3470, 3511, 3551, 3592, 3634, 3676,
+ 3719, 3762, 3805, 3849, 3894, 3939, 3985, 4031, 4078, 4125, 4173,
+ 4221, 4270, 4319, 4369, 4420, 4471, 4523, 4575, 4628, 4682, 4736,
+ 4791, 4846, 4902, 4959, 5017, 5075, 5133, 5193, 5253, 5314, 5375,
+ 5438, 5501, 5564, 5629, 5694, 5760, 5827, 5894, 5962, 6031, 6101,
+ 6172, 6243, 6316, 6389, 6463, 6538, 6613, 6690, 6767, 6846, 6925,
+ 7005, 7086, 7168, 7251, 7335, 7420, 7506, 7593, 7681, 7770, 7860,
+ 7951, 8043, 8136, 8230, 8326, 8422, 8520, 8618, 8718, 8819, 8921,
+ 9025, 9129, 9235, 9342, 9450, 9559, 9670, 9782, 9895, 10010, 10126,
+ 10243, 10362, 10482, 10603, 10726, 10850, 10976, 11103, 11231, 11361, 11493,
+ 11626, 11761, 11897, 12035, 12174, 12315, 12458, 12602, 12748, 12895, 13045,
+ 13196, 13349, 13503, 13659, 13818, 13978, 14140, 14303, 14469, 14636, 14806,
+ 14977, 15151, 15326, 15504, 15683, 15865, 16049, 16234, 16422, 16613, 16805,
+ 17000, 17196, 17396, 17597, 17801, 18007, 18215, 18426, 18640, 18856, 19074,
+ 19295, 19518, 19744, 19973, 20204, 20438, 20675, 20914, 21156, 21401, 21649,
+ 21900, 22153, 22410, 22669, 22932, 23197, 23466, 23738, 24013, 24291, 24572,
+ 24857, 25144, 25436, 25730, 26028, 26329, 26634, 26943, 27255, 27570, 27890,
+ 28213, 28539, 28870, 29204, 29542, 29884, 30230, 30580, 30934, 31293, 31655,
+ 32022, 32392, 32767, 33147, 33531, 33919, 34312, 34709, 35111, 35518, 35929,
+ 36345, 36766, 37192, 37622, 38058, 38499, 38944, 39395, 39851, 40313, 40780,
+ 41252, 41730, 42213, 42702, 43196, 43696, 44202, 44714, 45232, 45756, 46286,
+ 46821, 47364, 47912, 48467, 49028, 49596, 50170, 50751, 51339, 51933, 52535,
+ 53143, 53758, 54381, 55011, 55648, 56292, 56944, 57603, 58270, 58945, 59627,
+ 60318, 61016, 61723, 62438, 63161, 63892, 64632, 65380
+};
+
+static int __HBMMIXPanTable[128] =
+{
+ 0, 0, -1, -1, -1, -2, -2, -2, -3, -3, -4, -4, -4,
+ -5, -5, -5, -6, -6, -7, -7, -7, -8, -8, -9, -9, -10,
+ -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15,
+ -16, -16, -17, -17, -18, -18, -19, -20, -20, -21, -21, -22, -22,
+ -23, -23, -24, -25, -25, -26, -26, -27, -28, -28, -29, -30, -30,
+ -31, -32, -33, -33, -34, -35, -36, -36, -37, -38, -39, -40, -40,
+ -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -54,
+ -55, -56, -57, -59, -60, -61, -63, -64, -66, -67, -69, -71, -72,
+ -74, -76, -78, -80, -83, -85, -87, -90, -93, -96, -99, -102, -106,
+ -110, -115, -120, -126, -133, -140, -150, -163, -180, -210, -904
+};
+
+static s16 __HBMMIX_DPL2_front[128] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3,
+ -3, -4, -4, -4, -5, -5, -6, -6, -6, -7, -7, -8,
+ -8, -9, -9, -10, -11, -11, -12, -12, -13, -14, -14, -15,
+ -16, -17, -17, -18, -19, -20, -21, -21, -22, -23, -24, -25,
+ -26, -27, -28, -29, -30, -31, -32, -34, -35, -36, -37, -38,
+ -40, -41, -42, -44, -45, -47, -48, -50, -52, -53, -55, -57,
+ -58, -60, -62, -64, -66, -68, -70, -73, -75, -77, -80, -82,
+ -85, -88, -90, -93, -96, -100, -103, -106, -110, -114, -118, -122,
+ -126, -131, -136, -141, -146, -152, -159, -166, -173, -181, -190, -201,
+ -212, -225, -241, -261, -286, -321, -381, -960
+};
+
+static s16 __HBMMIX_DPL2_rear[128] =
+{
+ -61, -61, -60, -59, -59, -58, -58, -57, -56, -56, -55, -55, -54, -53, -53,
+ -52, -52, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -46, -45, -45,
+ -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -38,
+ -37, -37, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -32, -31,
+ -31, -31, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26,
+ -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21,
+ -21, -20, -20, -20, -20, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17,
+ -17, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13,
+ -13, -13, -13, -12, -12, -12, -12, -11
+};
+
+// .bss
+static s8 sIndex2IdTable[HBMMIX_MAX_KEYS];
+static s8 sId2IndexTable[HBMMIX_MAX_INDICES];
+static HBMMIXSoundMode __HBMMIXSoundMode;
+static HBMMIXChannel *__HBMMIXChannel;
+static HBMMIXChannel __s_HBMMIXChannel[HBMMIX_MAX_CHANNELS];
+static BOOL __init;
+
+
+static void __HBMMIXSetPan(HBMMIXChannel *channel)
+{
+ int pan, span, ipan, ispan;
+
+ ASSERTLINE(211, (channel->pan <= 127) && (channel->pan >= 0));
+
+ pan = channel->pan;
+ ipan = 127 - pan;
+ span = 127;
+ ispan = 0;
+
+ if (__HBMMIXSoundMode == HBMMIX_SOUND_MODE_DPL2)
+ {
+ channel->l = __HBMMIX_DPL2_front[pan];
+ channel->r = __HBMMIX_DPL2_front[ipan];
+ channel->f = __HBMMIX_DPL2_front[ispan];
+ channel->b = __HBMMIX_DPL2_front[span];
+ channel->l1 = __HBMMIX_DPL2_rear[ipan];
+ channel->r1 = __HBMMIX_DPL2_rear[pan];
+ }
+ else
+ {
+ channel->l = __HBMMIXPanTable[pan];
+ channel->r = __HBMMIXPanTable[ipan];
+ channel->f = __HBMMIXPanTable[ispan];
+ channel->b = __HBMMIXPanTable[span];
+ channel->l1 = 0;
+ channel->r1 = 0;
+ }
+}
+
+static void __HBMMIXResetChannel(HBMMIXChannel *channel)
+{
+ channel->mode = HBMMIX_FLAG_30 | HBMMIX_FLAG_28;
+ channel->input = 0;
+ channel->auxA = -960;
+ channel->fader = 0;
+ channel->pan = 64;
+
+ // one big long chain assignment
+ channel->v =
+ channel->vL =
+ channel->vR =
+ channel->vS =
+ channel->vAL =
+ channel->vAR =
+ channel->vAS =
+ channel->vBL =
+ channel->vBR =
+ channel->vBS =
+ channel->vCL =
+ channel->vCR =
+ channel->vCS = 0;
+
+ __HBMMIXSetPan(channel);
+}
+
+static int __HBMMIXClampPan(int pan)
+{
+ if (pan < 0)
+ return 0;
+
+ if (pan > 127)
+ return 127;
+
+ return pan;
+}
+
+static u16 __HBMMIXGetVolume(int db_)
+{
+ int const db = db_; // presumably? i can't think of another reason
+
+ if (db <= -904)
+ return 0;
+
+ if (db >= 60)
+ return 65380;
+
+ return __HBMMIXVolumeTable[db + 904];
+}
+
+void HBMMIXInit(void)
+{
+ int i;
+
+ ASSERTLINE(332, AXIsInit());
+
+ if (AXIsInit() && !__init)
+ {
+ for (int i = 0; i < HBMMIX_MAX_KEYS; ++i)
+ sIndex2IdTable[i] = HBMMIX_KEY_NULL;
+
+ for (int i = 0; i < HBMMIX_MAX_INDICES; ++i)
+ sId2IndexTable[i] = HBMMIX_INDEX_NULL;
+
+ __HBMMIXChannel = __s_HBMMIXChannel;
+
+ // what
+ for (i = 0; i < HBMMIX_MAX_CHANNELS; ++i)
+ {
+ __HBMMIXChannel[i].axvpb = NULL;
+ __HBMMIXResetChannel(&__HBMMIXChannel[i]);
+ }
+
+ __HBMMIXSoundMode = HBMMIX_SOUND_MODE_STEREO;
+ __init = TRUE;
+ }
+}
+
+void HBMMIXQuit(void)
+{
+ __HBMMIXChannel = NULL;
+ __init = FALSE;
+}
+
+void HBMMIXSetSoundMode(HBMMIXSoundMode mode)
+{
+ ASSERTLINE(397, (mode == HBMMIX_SOUND_MODE_MONO)
+ || (mode == HBMMIX_SOUND_MODE_STEREO)
+ || (mode == HBMMIX_SOUND_MODE_DPL2));
+
+ __HBMMIXSoundMode = mode;
+}
+
+HBMMIXSoundMode HBMMIXGetSoundMode(void)
+{
+ return __HBMMIXSoundMode;
+}
+
+void HBMMIXInitChannel(AXVPB *axvpb, int input, int auxA, int pan, int fader)
+{
+ BOOL intrStatus; /* int old; */
+ HBMMIXChannel *c;
+ u32 mixerCtrl;
+ u16 *p;
+
+ ASSERTLINE(449, axvpb);
+
+ HBMMIXIndex index = HBMGetIndex(axvpb->index);
+ c = &__HBMMIXChannel[index];
+
+ c->axvpb = axvpb;
+ c->mode = 0;
+ c->input = input;
+ c->auxA = auxA;
+ c->pan = pan;
+ c->fader = fader;
+
+ __HBMMIXSetPan(c);
+ c->v = __HBMMIXGetVolume(input);
+
+ mixerCtrl = 0;
+
+ switch (__HBMMIXSoundMode)
+ {
+ case HBMMIX_SOUND_MODE_MONO:
+ c->vL = __HBMMIXGetVolume(c->fader + c->f );
+ c->vR = __HBMMIXGetVolume(c->fader + c->f );
+ c->vS = __HBMMIXGetVolume(c->fader + c->b - 30);
+ c->vAL = __HBMMIXGetVolume(c->fader + c->auxA + c->f );
+ c->vAR = __HBMMIXGetVolume(c->fader + c->auxA + c->f );
+ c->vAS = __HBMMIXGetVolume(c->fader + c->auxA + c->b - 30);
+ c->vBL = 0;
+ c->vBR = 0;
+ c->vBS = 0;
+ c->vCL = 0;
+ c->vCR = 0;
+ c->vCS = 0;
+ break;
+
+ case HBMMIX_SOUND_MODE_STEREO:
+ c->vL = __HBMMIXGetVolume(c->fader + c->l + c->f);
+ c->vR = __HBMMIXGetVolume(c->fader + c->r + c->f);
+ c->vS = __HBMMIXGetVolume(c->fader + c->b - 30);
+ c->vAL = __HBMMIXGetVolume(c->fader + c->auxA + c->l + c->f);
+ c->vAR = __HBMMIXGetVolume(c->fader + c->auxA + c->r + c->f);
+ c->vAS = __HBMMIXGetVolume(c->fader + c->auxA + c->b - 30);
+ c->vBL = 0;
+ c->vBR = 0;
+ c->vBS = 0;
+ c->vCL = 0;
+ c->vCR = 0;
+ c->vCS = 0;
+ break;
+
+ case HBMMIX_SOUND_MODE_DPL2:
+ c->vL = __HBMMIXGetVolume(c->fader + c->l + c->f);
+ c->vR = __HBMMIXGetVolume(c->fader + c->r + c->f);
+ c->vS = __HBMMIXGetVolume(c->fader + c->l1 + c->b);
+ c->vCL = __HBMMIXGetVolume(c->fader + c->r1 + c->b);
+ c->vAL = __HBMMIXGetVolume(c->fader + c->auxA + c->l + c->f);
+ c->vAR = __HBMMIXGetVolume(c->fader + c->auxA + c->r + c->f);
+ c->vAS = __HBMMIXGetVolume(c->fader + c->auxA + c->l1 + c->b);
+ c->vCR = __HBMMIXGetVolume(c->fader + c->auxA + c->r1 + c->b);
+ c->vBL = 0;
+ c->vBR = 0;
+ c->vBS = 0;
+ c->vCS = 0;
+
+ mixerCtrl |= HBMMIX_FLAG_31;
+ break;
+ }
+
+ intrStatus = OSDisableInterrupts();
+
+ axvpb->pb.ve.currentVolume = c->v;
+ axvpb->pb.ve.currentDelta = 0;
+
+ p = (u16 *)&axvpb->pb.mix;
+
+#define DO_THING_(src_, flag_) \
+ do \
+ { \
+ if ((*p++ = c->src_)) \
+ mixerCtrl |= AX_MIXER_CTRL_ ## flag_; \
+ \
+ *p++ = 0; \
+ } while (0)
+
+#define DO_THING_LR_(srcInfix_, flagInfix_) \
+ DO_THING_(v ## srcInfix_ ## L, flagInfix_ ## L); \
+ DO_THING_(v ## srcInfix_ ## R, flagInfix_ ## R)
+
+ DO_THING_LR_( , );
+ DO_THING_LR_(A, A_);
+ DO_THING_LR_(B, B_);
+ DO_THING_LR_(C, C_);
+
+#undef DO_THING_LR_
+
+#define DO_THING_S_(srcInfix_, flagInfix_) \
+ DO_THING_(v ## srcInfix_ ## S, flagInfix_ ## S)
+
+ DO_THING_S_( , );
+ DO_THING_S_(A, A_);
+ DO_THING_S_(B, B_);
+ DO_THING_S_(C, C_);
+
+#undef DO_THING_S_
+
+#undef DO_THING_
+
+ axvpb->pb.mixerCtrl = mixerCtrl;
+ axvpb->sync |= AX_VPB_SYNC_FLAG_MIXER_CTRL | AX_VPB_SYNC_FLAG_MIX
+ | AX_VPB_SYNC_FLAG_VE;
+
+ p = (u16 *)&axvpb->pb.rmtMix;
+ memset(p, 0, sizeof axvpb->pb.rmtMix);
+
+ axvpb->pb.rmtMixerCtrl = 0;
+ axvpb->sync |= AX_VPB_SYNC_FLAG_RMT_MIXER_CTRL | AX_VPB_SYNC_FLAG_RMT_MIX;
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+void HBMMIXReleaseChannel(AXVPB *axvpb)
+{
+ ASSERTLINE(590, axvpb);
+
+ int index = HBMGetIndex(axvpb->index);
+ __HBMMIXChannel[index].axvpb = NULL;
+}
+
+void HBMMIXSetInput(AXVPB *p, int dB)
+{
+ u32 index = HBMGetIndex(p->index);
+ HBMMIXChannel *channel = __HBMMIXChannel + index;
+
+ channel->input = dB;
+ channel->mode |= HBMMIX_FLAG_28;
+}
+
+void HBMMIXSetAuxA(AXVPB *p, int dB)
+{
+ u32 index = HBMGetIndex(p->index);
+ HBMMIXChannel *channel = __HBMMIXChannel + index;
+
+ channel->auxA = dB;
+ channel->mode |= HBMMIX_FLAG_30;
+}
+
+void HBMMIXSetPan(AXVPB *p, int pan)
+{
+ u32 index = HBMGetIndex(p->index);
+ HBMMIXChannel *channel = __HBMMIXChannel + index;
+
+ channel->pan = __HBMMIXClampPan(pan);
+ __HBMMIXSetPan(channel);
+ channel->mode |= HBMMIX_FLAG_30;
+}
+
+void HBMMIXSetFader(AXVPB *p, int dB)
+{
+ u32 index = HBMGetIndex(p->index);
+ HBMMIXChannel *channel = __HBMMIXChannel + index;
+
+ channel->fader = dB;
+ channel->mode |= HBMMIX_FLAG_30;
+}
+
+void HBMMIXUpdateSettings(void)
+{
+ int i;
+
+ if (!__init)
+ return;
+
+ for (i = 0; i < HBMMIX_MAX_CHANNELS; ++i)
+ {
+ int setNewMixLevel;
+ int setNewInputLevel;
+ HBMMIXChannel *c;
+ AXVPB *axvpb;
+
+ setNewInputLevel = 0;
+ setNewMixLevel = 0;
+ c = &__HBMMIXChannel[i];
+ axvpb = c->axvpb;
+
+ if (axvpb)
+ {
+ u32 mixerCtrl = 0;
+
+ if (c->mode & HBMMIX_FLAG_29)
+ {
+ c->v = c->v1;
+
+ c->mode &= ~HBMMIX_FLAG_29;
+ setNewInputLevel = TRUE;
+ }
+
+ if (c->mode & HBMMIX_FLAG_28)
+ {
+ c->v1 = __HBMMIXGetVolume(c->input);
+
+ c->mode &= ~HBMMIX_FLAG_28;
+ c->mode |= HBMMIX_FLAG_29;
+ setNewInputLevel = TRUE;
+ }
+
+ if (c->mode & HBMMIX_FLAG_31)
+ {
+ c->vL = c->vL1;
+ c->vR = c->vR1;
+ c->vS = c->vS1;
+ c->vAL = c->vAL1;
+ c->vAR = c->vAR1;
+ c->vAS = c->vAS1;
+ c->vBL = c->vBL1;
+ c->vBR = c->vBR1;
+ c->vBS = c->vBS1;
+ c->vCL = c->vCL1;
+ c->vCR = c->vCR1;
+ c->vCS = c->vCS1;
+
+ c->mode &= ~HBMMIX_FLAG_31;
+ setNewMixLevel = TRUE;
+ }
+
+ if (c->mode & HBMMIX_FLAG_30)
+ {
+ switch (__HBMMIXSoundMode)
+ {
+ case HBMMIX_SOUND_MODE_MONO:
+ c->vL1 = __HBMMIXGetVolume(c->fader + c->f );
+ c->vR1 = __HBMMIXGetVolume(c->fader + c->f );
+ c->vS1 = __HBMMIXGetVolume(c->fader + c->b - 30);
+ c->vAL1 = __HBMMIXGetVolume(c->fader + c->auxA + c->f );
+ c->vAR1 = __HBMMIXGetVolume(c->fader + c->auxA + c->f );
+ c->vAS1 = __HBMMIXGetVolume(c->fader + c->auxA + c->b - 30);
+ c->vBL1 = 0;
+ c->vBR1 = 0;
+ c->vBS1 = 0;
+ c->vCL1 = 0;
+ c->vCR1 = 0;
+ c->vCS1 = 0;
+ break;
+
+ case HBMMIX_SOUND_MODE_STEREO:
+ c->vL1 = __HBMMIXGetVolume(c->fader + c->l + c->f);
+ c->vR1 = __HBMMIXGetVolume(c->fader + c->r + c->f);
+ c->vS1 = __HBMMIXGetVolume(c->fader + c->b - 30);
+ c->vAL1 = __HBMMIXGetVolume(c->fader + c->auxA + c->l + c->f);
+ c->vAR1 = __HBMMIXGetVolume(c->fader + c->auxA + c->r + c->f);
+ c->vAS1 = __HBMMIXGetVolume(c->fader + c->auxA + c->b - 30);
+ c->vBL1 = 0;
+ c->vBR1 = 0;
+ c->vBS1 = 0;
+ c->vCL1 = 0;
+ c->vCR1 = 0;
+ c->vCS1 = 0;
+ break;
+
+ case HBMMIX_SOUND_MODE_DPL2:
+ c->vL1 = __HBMMIXGetVolume(c->fader + c->l + c->f);
+ c->vR1 = __HBMMIXGetVolume(c->fader + c->r + c->f);
+ c->vS1 = __HBMMIXGetVolume(c->fader + c->l1 + c->b);
+ c->vCL1 = __HBMMIXGetVolume(c->fader + c->r1 + c->b);
+ c->vAL1 = __HBMMIXGetVolume(c->fader + c->auxA + c->l + c->f);
+ c->vAR1 = __HBMMIXGetVolume(c->fader + c->auxA + c->r + c->f);
+ c->vAS1 = __HBMMIXGetVolume(c->fader + c->auxA + c->l1 + c->b);
+ c->vCR1 = __HBMMIXGetVolume(c->fader + c->auxA + c->r1 + c->b);
+ c->vBL1 = 0;
+ c->vBR1 = 0;
+ c->vBS1 = 0;
+ c->vCS1 = 0;
+
+ mixerCtrl |= HBMMIX_FLAG_31;
+ break;
+ }
+
+ c->mode &= ~HBMMIX_FLAG_30;
+ c->mode |= HBMMIX_FLAG_31;
+ setNewMixLevel = TRUE;
+ }
+
+ if (setNewInputLevel)
+ {
+ axvpb->pb.ve.currentVolume = c->v;
+ axvpb->pb.ve.currentDelta = (c->v1 - c->v) / 96;
+ axvpb->sync |= AX_VPB_SYNC_FLAG_VE;
+ }
+
+ if (setNewMixLevel)
+ {
+ u16 *p = (u16 *)&axvpb->pb.mix;
+
+#define DO_THING_(src_, flag1_, flag2_) \
+ do \
+ { \
+ if ((*p++ = c->src_)) \
+ mixerCtrl |= AX_MIXER_CTRL_ ## flag1_; \
+ \
+ if ((*p++ = (c->src_##1 - c->src_) / 96)) \
+ mixerCtrl |= AX_MIXER_CTRL_ ## flag2_; \
+ } while (0)
+
+#define DO_THING_LR_(srcInfix_, flagInfix_) \
+ DO_THING_(v ## srcInfix_ ## L, flagInfix_ ## L, flagInfix_ ## DELTA); \
+ DO_THING_(v ## srcInfix_ ## R, flagInfix_ ## R, flagInfix_ ## DELTA)
+
+ DO_THING_LR_( , );
+ DO_THING_LR_(A, A_);
+ DO_THING_LR_(B, B_);
+ DO_THING_LR_(C, C_);
+
+#undef DO_THING_LR_
+
+#define DO_THING_S_(srcInfix_, flagInfix_) \
+ DO_THING_(v ## srcInfix_ ## S, flagInfix_ ## S, flagInfix_ ## DELTA_S)
+
+ DO_THING_S_( , );
+ DO_THING_S_(A, A_);
+ DO_THING_S_(B, B_);
+ DO_THING_S_(C, C_);
+
+#undef DO_THING_S_
+
+#undef DO_THING_
+
+ axvpb->pb.mixerCtrl = mixerCtrl;
+ axvpb->sync |=
+ AX_VPB_SYNC_FLAG_MIXER_CTRL | AX_VPB_SYNC_FLAG_MIX;
+ }
+ }
+ }
+}
+
+HBMMIXIndex HBMAllocIndex(HBMMIXKey key)
+{
+ for (int i = 0; i < HBMMIX_MAX_KEYS; ++i)
+ {
+ if (sIndex2IdTable[i] < 0)
+ {
+ sIndex2IdTable[i] = key;
+ sId2IndexTable[key] = i;
+
+ return i;
+ }
+ }
+
+ return HBMMIX_INDEX_NULL;
+}
+
+HBMMIXIndex HBMGetIndex(HBMMIXKey key)
+{
+ return sId2IndexTable[key];
+}
+
+void HBMFreeIndex(HBMMIXIndex index)
+{
+ HBMMIXKey key = sIndex2IdTable[index];
+
+ sIndex2IdTable[index] = HBMMIX_KEY_NULL;
+ sId2IndexTable[key] = HBMMIX_INDEX_NULL;
+}
+
+void HBMFreeIndexByKey(HBMMIXKey key)
+{
+ HBMMIXIndex index = sId2IndexTable[key];
+
+ if (index >= 0)
+ {
+ sIndex2IdTable[index] = HBMMIX_KEY_NULL;
+ sId2IndexTable[key] = HBMMIX_INDEX_NULL;
+ }
+}
diff --git a/src/revolution/homebuttonLib/sound/mix.h b/src/revolution/homebuttonLib/sound/mix.h
new file mode 100644
index 0000000000..79665ca142
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/mix.h
@@ -0,0 +1,51 @@
+#ifndef RVL_SDK_HBMMIX_H
+#define RVL_SDK_HBMMIX_H
+
+#include <revolution/ax.h>
+#include <revolution/types.h>
+
+
+typedef int HBMMIXIndex;
+#define HBMMIX_INDEX_NULL (-1)
+
+typedef u32 HBMMIXKey;
+#define HBMMIX_KEY_NULL (-1)
+
+typedef u32 HBMMIXSoundMode;
+enum HBMMIXSoundMode_et {
+ HBMMIX_SOUND_MODE_MONO,
+ HBMMIX_SOUND_MODE_STEREO,
+ HBMMIX_SOUND_MODE_DPL2,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void HBMMIXInit(void);
+void HBMMIXQuit(void);
+
+void HBMMIXSetSoundMode(HBMMIXSoundMode mode);
+HBMMIXSoundMode HBMMIXGetSoundMode(void);
+
+void HBMMIXInitChannel(AXVPB* axvpb, int input, int auxA, int pan, int fader);
+void HBMMIXReleaseChannel(AXVPB* axvpb);
+
+void HBMMIXSetInput(AXVPB* p, int dB);
+void HBMMIXSetAuxA(AXVPB* p, int dB);
+void HBMMIXSetPan(AXVPB* p, int pan);
+void HBMMIXSetFader(AXVPB* p, int dB);
+
+void HBMMIXUpdateSettings(void);
+
+HBMMIXIndex HBMAllocIndex(HBMMIXKey key);
+HBMMIXIndex HBMGetIndex(HBMMIXKey key);
+
+void HBMFreeIndex(HBMMIXIndex index);
+void HBMFreeIndexByKey(HBMMIXKey key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RVL_SDK_HBMMIX_H
diff --git a/src/revolution/homebuttonLib/sound/seq.cpp b/src/revolution/homebuttonLib/sound/seq.cpp
new file mode 100644
index 0000000000..dde2d01960
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/seq.cpp
@@ -0,0 +1,532 @@
+#include "seq.h"
+
+
+#include <revolution/types.h>
+
+#include "syn.h"
+
+// #include <revolution/os.h>
+// #include <revolution/os/OSInterrupt.h>
+
+
+
+// As specified by General MIDI 1
+
+#define MIDI_TRACK_HEADER_MAGIC 'MThd'
+#define MIDI_TRACK_MAGIC 'MTrk'
+
+#define MIDI_FILE_FORMAT_0 0
+#define MIDI_FILE_FORMAT_1 1
+// #define MIDI_FILE_FORMAT_2 2
+
+#define MIDI_MESSAGE_CHANNEL_MODE_MESSAGE 0xB0
+#define MIDI_MESSAGE_SYSEX_BEGIN 0xF0
+#define MIDI_MESSAGE_SYSEX_END 0xF7
+#define MIDI_MESSAGE_META_EVENT 0xFF
+
+#define MIDI_CHANNEL_MODE_MESSAGE_ALL_NOTES_OFF 0x7B
+
+#define MIDI_META_EVENT_END_OF_TRACK 0x2F
+#define MIDI_META_EVENT_SET_TEMPO 0x51
+
+
+enum HBMSEQTRACKSTATE_et
+{
+ HBMSEQ_TRACK_STATE_0, // stopped?
+ HBMSEQ_TRACK_STATE_1, // playing?
+ HBMSEQ_TRACK_STATE_2, // never set to; only checked for
+};
+
+
+static void __HBMSEQPushSequenceList(HBMSEQSEQUENCE *sequence);
+static void __HBMSEQRemoveSequenceFromList(HBMSEQSEQUENCE *sequence);
+static u32 __HBMSEQGetIntTrack(HBMSEQTRACK *track);
+static void __HBMSEQHandleSysExEvent(HBMSEQTRACK *track);
+static void __HBMSEQSetTicksPerFrame(HBMSEQTRACK *track, f32 bps);
+static void __HBMSEQTempoMetaEvent(HBMSEQTRACK *track);
+static void __HBMSEQTrackEnd(HBMSEQTRACK *track);
+static void __HBMSEQHandleMetaEvent(HBMSEQTRACK *track);
+static void __HBMSEQHandleSynthEvent(HBMSYNSYNTH *synth, HBMSEQTRACK *track);
+static void __HBMSEQRunEvent(HBMSYNSYNTH *synth, HBMSEQTRACK *track);
+static void __HBMSEQInitTracks(HBMSEQSEQUENCE *sequence, u8 *read,
+ int tracks);
+static void __HBMSEQReadHeader(HBMSEQSEQUENCE *sequence, u8 *midiStream);
+
+
+// .data
+static u8 __HBMSEQMidiEventLength[128] =
+{
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+
+ 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+// .bss
+static HBMSEQSEQUENCE *__HBMSEQSequenceList;
+static BOOL __init;
+
+
+static void __HBMSEQPushSequenceList(HBMSEQSEQUENCE *sequence)
+{
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ if (__HBMSEQSequenceList)
+ sequence->next = __HBMSEQSequenceList;
+ else
+ sequence->next = NULL;
+
+ __HBMSEQSequenceList = sequence;
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+static void __HBMSEQRemoveSequenceFromList(HBMSEQSEQUENCE *sequence)
+{
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+ HBMSEQSEQUENCE *thisSequence = __HBMSEQSequenceList;
+
+ __HBMSEQSequenceList = NULL;
+
+ while (thisSequence)
+ {
+ HBMSEQSEQUENCE *next = thisSequence->next;
+
+ if (thisSequence != sequence)
+ __HBMSEQPushSequenceList(thisSequence);
+
+ thisSequence = next;
+ }
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+static u32 __HBMSEQGetIntTrack(HBMSEQTRACK *track)
+{
+ u32 value;
+
+ ASSERTLINE(106, track);
+
+ value = *track->current & 0x7f;
+
+ while (*track->current & 0x80)
+ {
+ ++track->current;
+
+ value = (value << 7) + (*track->current & 0x7f);
+ }
+
+ ++track->current;
+
+ return value;
+}
+
+static void __HBMSEQHandleSysExEvent(HBMSEQTRACK *track)
+{
+ u32 length;
+
+ ASSERTLINE(129, track);
+
+ length = __HBMSEQGetIntTrack(track);
+ track->current += length;
+}
+
+static void __HBMSEQSetTicksPerFrame(HBMSEQTRACK *track, f32 bps)
+{
+ HBMSEQSEQUENCE *sequence;
+
+ ASSERTLINE(143, track);
+
+ sequence = track->sequence;
+
+ track->beatsPerSec = bps;
+ track->ticksPerFrame =
+ 96.0f / (3.2e4f / bps / sequence->timeFormat) * 65536.0f;
+}
+
+static void __HBMSEQTempoMetaEvent(HBMSEQTRACK *track)
+{
+ u32 data;
+ f32 beatsPerSec;
+
+ // doubled statements
+ data = *track->current; ++track->current;
+ data = (data << 8) + *track->current; ++track->current;
+ data = (data << 8) + *track->current; ++track->current;
+
+ beatsPerSec = 1e6f / data;
+
+ __HBMSEQSetTicksPerFrame(track, beatsPerSec);
+}
+
+static void __HBMSEQTrackEnd(HBMSEQTRACK *track)
+{
+ HBMSEQSEQUENCE *sequence;
+
+ ASSERTLINE(185, track);
+
+ sequence = track->sequence;
+
+ --sequence->tracksRunning;
+ track->state = HBMSEQ_TRACK_STATE_0;
+
+ if (sequence->tracksRunning == 0)
+ sequence->end = TRUE;
+}
+
+static void __HBMSEQHandleMetaEvent(HBMSEQTRACK *track)
+{
+ u8 type;
+ u32 length;
+
+ ASSERTLINE(204, track);
+
+ // doubled statements
+ type = *track->current; ++track->current;
+ switch (type)
+ {
+ case MIDI_META_EVENT_END_OF_TRACK:
+ __HBMSEQTrackEnd(track);
+ break;
+
+ case MIDI_META_EVENT_SET_TEMPO:
+ length = __HBMSEQGetIntTrack(track); // assignment unnecessary?
+ __HBMSEQTempoMetaEvent(track);
+ break;
+
+ default:
+ length = __HBMSEQGetIntTrack(track);
+ track->current += length;
+ break;
+ }
+}
+
+static void __HBMSEQHandleSynthEvent(HBMSYNSYNTH *synth, HBMSEQTRACK *track)
+{
+ u8 ch[3];
+ u32 bytes;
+
+ bytes = __HBMSEQMidiEventLength[track->status - 128];
+ ch[0] = track->status;
+
+ switch (bytes)
+ {
+ case 0:
+ break;
+
+ case 1:
+ // doubled statements
+ ch[1] = *track->current; ++track->current;
+ break;
+
+ case 2:
+ // doubled statements
+ ch[1] = *track->current; ++track->current;
+ ch[2] = *track->current; ++track->current;
+ break;
+ }
+
+ HBMSYNMidiInput(synth, ch);
+}
+
+static void __HBMSEQRunEvent(HBMSYNSYNTH *synth, HBMSEQTRACK *track)
+{
+ u8 event;
+
+ ASSERTLINE(280, synth);
+ ASSERTLINE(281, track);
+
+ event = *track->current;
+
+ if (event >= 0x80)
+ {
+ track->status = event;
+ ++track->current;
+ }
+
+ switch (track->status)
+ {
+ case MIDI_MESSAGE_SYSEX_BEGIN:
+ case MIDI_MESSAGE_SYSEX_END:
+ __HBMSEQHandleSysExEvent(track);
+ break;
+
+ case MIDI_MESSAGE_META_EVENT:
+ __HBMSEQHandleMetaEvent(track);
+ break;
+
+ default:
+ __HBMSEQHandleSynthEvent(synth, track);
+ break;
+ }
+
+ if (track->current >= track->end)
+ __HBMSEQTrackEnd(track);
+}
+
+static void __HBMSEQInitTracks(HBMSEQSEQUENCE *sequence, u8 *read,
+ int tracks)
+{
+ int i = 0;
+ u8 *p = read;
+
+ while (tracks)
+ {
+ while (TRUE)
+ {
+ u32 chunk;
+ u32 bytes;
+
+ // doubled statements
+ chunk = *(u32 *)p; p += sizeof(u32);
+ bytes = *(u32 *)p; p += sizeof(u32);
+
+ if (chunk == MIDI_TRACK_MAGIC)
+ {
+ HBMSEQTRACK *track = sequence->track + i;
+
+ track->sequence = sequence;
+ track->start = p;
+ track->end = p + bytes;
+ track->current = p;
+ track->defaultTicksPerFrame =
+ 96.0f / (1.6e4f / sequence->timeFormat) * 65536.0f;
+ track->state = HBMSEQ_TRACK_STATE_0;
+
+ p += bytes;
+ break;
+ }
+
+ p += bytes;
+ }
+
+ --tracks;
+ ++i;
+ }
+}
+
+static void __HBMSEQReadHeader(HBMSEQSEQUENCE *sequence, u8 *midiStream)
+{
+ u8 *read = midiStream;
+ u32 bytes;
+ u32 fileType;
+
+ OSAssertMessage_Line(383, *(u32 *)read == MIDI_TRACK_HEADER_MAGIC,
+ "!!!midiStream is not a valid MIDI file\n!!!");
+ read += sizeof(u32);
+
+ // doubled statements
+ bytes = *(u32 *)read; read += sizeof(u32);
+ fileType = *(u16 *)read; read += sizeof(u16);
+ sequence->nTracks = *(u16 *)read; read += sizeof(u16);
+ sequence->timeFormat = *(s16 *)read; read += sizeof(s16);
+
+ OSAssertMessage_Line(398, sequence->timeFormat >= 0,
+ "!!!HBMSEQ does not support SMPTE time!!!\n");
+
+ // fileType, sequence->nTracks, and sequence->timeFormat, respectively
+ bytes -= sizeof(u16) + sizeof(u16) + sizeof(s16);
+ read += bytes;
+
+ switch (fileType)
+ {
+ case MIDI_FILE_FORMAT_0:
+ sequence->nTracks = 1;
+ __HBMSEQInitTracks(sequence, read, 1);
+ break;
+
+ case MIDI_FILE_FORMAT_1:
+ OSAssertMessage_Line(420, sequence->nTracks < HBMSEQ_MAX_TRACKS,
+ "exceeded HBMSEQ_MAX_TRACKS, please increase "
+ "HBMSEQ_MAX_TRACKS\n");
+
+ __HBMSEQInitTracks(sequence, read, sequence->nTracks);
+ break;
+
+ // case MIDI_FILE_FORMAT_2: // not supported
+ default:
+ OSAssertMessage_Line(428, FALSE, "!!!Invalid MIDI file type\n!!!");
+ }
+
+ sequence->tracksRunning = sequence->nTracks;
+}
+
+void HBMSEQInit(void)
+{
+ if (!__init)
+ {
+ __HBMSEQSequenceList = NULL;
+ __init = TRUE;
+ }
+}
+
+void HBMSEQQuit(void)
+{
+ __HBMSEQSequenceList = NULL;
+ __init = FALSE;
+}
+
+void HBMSEQRunAudioFrame(void)
+{
+ HBMSEQSEQUENCE *sequence = __HBMSEQSequenceList;
+
+ if (!__init)
+ return;
+
+ for (; sequence; sequence = sequence->next)
+ {
+ if (sequence->state == HBM_SEQ_STATE_1
+ || sequence->state == HBM_SEQ_STATE_2)
+ {
+ u32 i;
+
+ for (i = 0; i < sequence->nTracks; ++i)
+ {
+ HBMSEQTRACK *track = &sequence->track[i];
+
+ if (track->state == HBMSEQ_TRACK_STATE_1
+ || track->state == HBMSEQ_TRACK_STATE_2)
+ {
+ u32 ticks = track->ticksPerFrame;
+
+ if (track->delay > ticks)
+ {
+ track->delay -= ticks;
+ continue;
+ }
+
+ while (ticks >= track->delay)
+ {
+ ticks -= track->delay;
+
+ __HBMSEQRunEvent(&sequence->synth, track);
+
+ if (track->state == HBMSEQ_TRACK_STATE_0)
+ break;
+
+ track->delay = __HBMSEQGetIntTrack(track) << 16;
+ }
+
+ track->delay -= ticks;
+ }
+ }
+ }
+
+ if (sequence->end)
+ {
+ if (sequence->state == HBM_SEQ_STATE_2)
+ {
+ HBMSEQSetState(sequence, HBM_SEQ_STATE_0);
+ HBMSEQSetState(sequence, HBM_SEQ_STATE_2);
+ }
+ else
+ {
+ HBMSEQSetState(sequence, HBM_SEQ_STATE_0);
+ }
+ }
+ }
+}
+
+void HBMSEQAddSequence(HBMSEQSEQUENCE *sequence, u8 *midiStream,
+ u8 *wavetable, u8 *samples, u8 *zerobuffer)
+{
+ ASSERTLINE(551, sequence);
+ ASSERTLINE(552, midiStream);
+ ASSERTLINE(553, wavetable);
+ ASSERTLINE(554, samples);
+
+ HBMSYNInitSynth(&sequence->synth, wavetable, samples, zerobuffer);
+ sequence->state = HBM_SEQ_STATE_0;
+ __HBMSEQReadHeader(sequence, midiStream);
+ __HBMSEQPushSequenceList(sequence);
+}
+
+void HBMSEQRemoveSequence(HBMSEQSEQUENCE *sequence)
+{
+ ASSERTLINE(581, sequence);
+
+ __HBMSEQRemoveSequenceFromList(sequence);
+ HBMSYNQuitSynth(&sequence->synth);
+}
+
+void HBMSEQSetState(HBMSEQSEQUENCE *sequence, HBMSEQSTATE state)
+{
+ int i;
+
+ ASSERTLINE(598, sequence);
+
+ switch (state)
+ {
+ case HBM_SEQ_STATE_1:
+ case HBM_SEQ_STATE_2:
+ if (sequence->state == HBM_SEQ_STATE_0)
+ {
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ for (i = 0; i < sequence->nTracks; ++i)
+ {
+ HBMSEQTRACK *track = &sequence->track[i];
+
+ track->current = track->start;
+ track->ticksPerFrame = track->defaultTicksPerFrame;
+ track->delay = __HBMSEQGetIntTrack(track) << 16;
+ track->state = HBMSEQ_TRACK_STATE_1;
+ }
+
+ sequence->tracksRunning = sequence->nTracks;
+
+ OSRestoreInterrupts(intrStatus);
+ }
+
+ sequence->end = FALSE;
+ break;
+
+ case HBM_SEQ_STATE_0:
+ case HBM_SEQ_STATE_3:
+ for (i = 0; i < HBMSYN_NUM_MIDI_CHANNELS; ++i)
+ {
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ u8 ch[3];
+ ch[0] = MIDI_MESSAGE_CHANNEL_MODE_MESSAGE | i;
+ ch[1] = MIDI_CHANNEL_MODE_MESSAGE_ALL_NOTES_OFF;
+ ch[2] = 0; // v = 0 -> all notes off
+
+ HBMSYNMidiInput(&sequence->synth, ch);
+
+ OSRestoreInterrupts(intrStatus);
+ }
+
+ break;
+ }
+
+ sequence->state = state;
+}
+
+HBMSEQSTATE HBMSEQGetState(HBMSEQSEQUENCE *sequence)
+{
+ ASSERTLINE(666, sequence);
+
+ return sequence->state;
+}
+
+void HBMSEQSetVolume(HBMSEQSEQUENCE *sequence, s32 dB)
+{
+ ASSERTLINE(677, sequence);
+
+ HBMSYNSetMasterVolume(&sequence->synth, dB);
+}
+
+s32 HBMSEQGetVolume(HBMSEQSEQUENCE *sequence)
+{
+ ASSERTLINE(688, sequence);
+
+ return HBMSYNGetMasterVolume(&sequence->synth);
+}
diff --git a/src/revolution/homebuttonLib/sound/seq.h b/src/revolution/homebuttonLib/sound/seq.h
new file mode 100644
index 0000000000..00cceeb33f
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/seq.h
@@ -0,0 +1,70 @@
+#ifndef RVL_SDK_HBMSEQ_H
+#define RVL_SDK_HBMSEQ_H
+
+#include <revolution/types.h>
+
+#include "syn.h"
+
+// Modifiable; to what extent is unknown
+#define HBMSEQ_MAX_TRACKS 64
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef u32 HBMSEQSTATE;
+enum HBMSEQSTATE_et {
+ HBM_SEQ_STATE_0, // off?
+ HBM_SEQ_STATE_1, // on?
+ HBM_SEQ_STATE_2, // some sort of next?
+ HBM_SEQ_STATE_3, // paused?
+};
+
+// forward declarations
+typedef struct _HBMSEQSEQUENCE HBMSEQSEQUENCE;
+
+typedef struct _HBMSEQTRACK {
+ HBMSEQSEQUENCE* sequence; // size 0x04, offset 0x00
+ u8* start; // size 0x04, offset 0x04
+ u8* end; // size 0x04, offset 0x08
+ u8* current; // size 0x04, offset 0x0c
+ u8 status; // size 0x01, offset 0x10
+ /* 3 bytes padding */
+ f32 beatsPerSec; // size 0x04, offset 0x14
+ u32 defaultTicksPerFrame; // size 0x04, offset 0x18
+ u32 ticksPerFrame; // size 0x04, offset 0x1c
+ u32 delay; // size 0x04, offset 0x20
+ u32 state; // size 0x04, offset 0x24
+} HBMSEQTRACK; // size 0x28
+
+struct _HBMSEQSEQUENCE {
+ HBMSEQSEQUENCE* next; // size 0x0004, offset 0x0000
+ HBMSEQSTATE state; // size 0x0004, offset 0x0004
+ u16 nTracks; // size 0x0002, offset 0x0008
+ s16 timeFormat; // size 0x0002, offset 0x000a
+ u32 tracksRunning; // size 0x0004, offset 0x000c
+ u32 end; // size 0x0004, offset 0x0010
+ HBMSYNSYNTH synth; // size 0x2408, offset 0x0014
+ HBMSEQTRACK track[HBMSEQ_MAX_TRACKS]; // size 0x0a00, offset 0x241c
+}; // size 0x2e1c
+
+void HBMSEQInit(void);
+void HBMSEQQuit(void);
+
+void HBMSEQRunAudioFrame(void);
+
+void HBMSEQAddSequence(HBMSEQSEQUENCE* sequence, u8* midiStream, u8* wavetable, u8* samples,
+ u8* zerobuffer);
+void HBMSEQRemoveSequence(HBMSEQSEQUENCE* sequence);
+
+void HBMSEQSetState(HBMSEQSEQUENCE* sequence, HBMSEQSTATE state);
+HBMSEQSTATE HBMSEQGetState(HBMSEQSEQUENCE* sequence);
+
+void HBMSEQSetVolume(HBMSEQSEQUENCE* sequence, s32 dB);
+s32 HBMSEQGetVolume(HBMSEQSEQUENCE* sequence);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RVL_SDK_HBMSEQ_H
diff --git a/src/revolution/homebuttonLib/sound/syn.cpp b/src/revolution/homebuttonLib/sound/syn.cpp
new file mode 100644
index 0000000000..81f3cf93cd
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/syn.cpp
@@ -0,0 +1,224 @@
+#include "syn.h"
+
+
+#include <macros.h>
+
+#include <revolution/types.h>
+
+#include "synprivate.h"
+
+#include "mix.h"
+
+// #include <revolution/ax.h>
+// #include <revolution/os.h>
+// #include <revolution/os/OSAddress.h>
+// #include <revolution/os/OSInterrupt.h>
+
+
+
+#define HBMSYN_MAX_VOICES 0x10
+
+
+static void __HBMSYNAddSynthToList(HBMSYNSYNTH *synth);
+static void __HBMSYNRemoveSynthFromList(HBMSYNSYNTH *synth);
+
+
+// .bss
+static HBMSYNSYNTH *__HBMSYNSynthList;
+HBMSYNVOICE *__HBMSYNVoice;
+static HBMSYNVOICE __s_HBMSYNVoice[HBMSYN_MAX_VOICES];
+static int __init;
+
+
+static void __HBMSYNAddSynthToList(HBMSYNSYNTH *synth)
+{
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ if (__HBMSYNSynthList)
+ synth->next = __HBMSYNSynthList;
+ else
+ synth->next = NULL;
+
+ __HBMSYNSynthList = synth;
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+static void __HBMSYNRemoveSynthFromList(HBMSYNSYNTH *synth)
+{
+ HBMSYNSYNTH *tempHead, *tempTail;
+ HBMSYNSYNTH *tempSynth;
+
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ tempHead = NULL;
+ tempTail = NULL;
+
+ for (tempSynth = __HBMSYNSynthList; tempSynth; tempSynth = tempSynth->next)
+ {
+ if (tempSynth != synth)
+ {
+ if (tempHead)
+ {
+ tempTail->next = tempSynth;
+ tempTail = tempSynth;
+ }
+ else
+ {
+ tempTail = tempSynth;
+ tempHead = tempTail;
+ }
+ }
+ }
+
+ if (tempTail)
+ tempTail->next = NULL;
+
+ __HBMSYNSynthList = tempHead;
+
+ OSRestoreInterrupts(intrStatus);
+
+ (void)tempSynth; // TODO: Where would this extra use go?
+}
+
+void HBMSYNInit(void)
+{
+ int i;
+
+ ASSERTLINE(95, AXIsInit());
+
+ if (AXIsInit() && !__init)
+ {
+ __HBMSYNVoice = __s_HBMSYNVoice;
+
+ for (i = 0; i < HBMSYN_MAX_VOICES; ++i)
+ __HBMSYNVoice[i].synth = NULL;
+
+ __HBMSYNSynthList = NULL;
+ __init = TRUE;
+ }
+}
+
+void HBMSYNQuit(void)
+{
+ __HBMSYNVoice = NULL;
+ __init = FALSE;
+}
+
+void HBMSYNRunAudioFrame(void)
+{
+ int i;
+ HBMSYNSYNTH *synth;
+
+ if (__init)
+ {
+ for (i = 0; i < HBMSYN_MAX_VOICES; ++i)
+ __HBMSYNServiceVoice(i);
+
+ for (synth = __HBMSYNSynthList; synth; synth = synth->next)
+ __HBMSYNRunInputBufferEvents(synth);
+ }
+}
+
+void HBMSYNInitSynth(HBMSYNSYNTH *synth, u8 *wavetable, u8 *samples,
+ u8 *zerobuffer ATTR_UNUSED)
+{
+ u32 *p;
+ u32 mramBase;
+ u32 midiChannel, noteNumber;
+
+ ASSERTLINE(173, synth);
+ ASSERTLINE(174, wavetable);
+ ASSERTLINE(175, samples);
+
+ p = (u32 *)wavetable;
+ mramBase = (u32)OSCachedToPhysical(samples);
+
+ // doubled statements
+ synth->percussiveInst = POINTER_ADD_TYPE(WTINST *, wavetable, *p); ++p;
+ synth->melodicInst = POINTER_ADD_TYPE(WTINST *, wavetable, *p); ++p;
+ synth->region = POINTER_ADD_TYPE(WTREGION *, wavetable, *p); ++p;
+ synth->art = POINTER_ADD_TYPE(WTART *, wavetable, *p); ++p;
+ synth->sample = POINTER_ADD_TYPE(WTSAMPLE *, wavetable, *p); ++p;
+ synth->adpcm = POINTER_ADD_TYPE(WTADPCM *, wavetable, *p); ++p;
+
+ synth->samplesBaseWord = mramBase >> 1;
+ synth->samplesBaseByte = mramBase;
+ synth->samplesBaseNibble = mramBase << 1;
+ synth->masterVolume = 0;
+
+ __HBMSYNResetAllControllers(synth);
+
+ synth->inputPosition = *synth->input;
+ synth->inputCounter = 0;
+ synth->notes = 0;
+
+ for (midiChannel = 0; midiChannel < HBMSYN_NUM_MIDI_CHANNELS; ++midiChannel)
+ {
+ for (noteNumber = 0; noteNumber < HBMSYN_NUM_MIDI_NOTES; ++noteNumber)
+ synth->voice[midiChannel][noteNumber] = NULL;
+ }
+
+ __HBMSYNAddSynthToList(synth);
+}
+
+void HBMSYNQuitSynth(HBMSYNSYNTH *synth)
+{
+ int i;
+ BOOL intrStatus = OSDisableInterrupts(); /* int old; */
+
+ if (synth->notes)
+ {
+ for (i = 0; i < HBMSYN_MAX_VOICES; ++i)
+ {
+ HBMSYNVOICE *voice = __HBMSYNVoice + i;
+
+ if (voice->synth == synth)
+ {
+ HBMMIXReleaseChannel(voice->axvpb);
+ HBMFreeIndexByKey(voice->axvpb->index);
+ AXFreeVoice(voice->axvpb);
+ voice->synth = NULL;
+ }
+ }
+ }
+
+ __HBMSYNRemoveSynthFromList(synth);
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+void HBMSYNMidiInput(HBMSYNSYNTH *synth, u8 *input)
+{
+ u8 *src;
+
+ ASSERTLINE(254, synth);
+ ASSERTLINE(255, input);
+
+ src = input;
+
+ // tripled statements
+ *synth->inputPosition = *src; ++synth->inputPosition; ++src;
+ *synth->inputPosition = *src; ++synth->inputPosition; ++src;
+ *synth->inputPosition = *src; ++synth->inputPosition; ++src;
+
+ ++synth->inputCounter;
+
+ OSAssertMessage_Line(
+ 268, synth->inputCounter < HBMSYN_INPUT_BUFFER_SIZE,
+ "synth input buffer exceeded, increase HBMSYN_INPUT_BUFFER_SIZE");
+}
+
+void HBMSYNSetMasterVolume(HBMSYNSYNTH *synth, s32 dB)
+{
+ ASSERTLINE(277, synth);
+
+ synth->masterVolume = dB << 16;
+}
+
+s32 HBMSYNGetMasterVolume(HBMSYNSYNTH *synth)
+{
+ ASSERTLINE(288, synth);
+
+ return synth->masterVolume >> 16;
+}
diff --git a/src/revolution/homebuttonLib/sound/syn.h b/src/revolution/homebuttonLib/sound/syn.h
new file mode 100644
index 0000000000..2e8d29bcd2
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/syn.h
@@ -0,0 +1,121 @@
+#ifndef RVL_SDK_HBMSYN_H
+#define RVL_SDK_HBMSYN_H
+
+#include <revolution/types.h>
+
+#define HBMSYN_NUM_MIDI_CHANNELS 16
+#define HBMSYN_NUM_MIDI_NOTES 128
+
+// Modifiable; to what extent is unknown
+#define HBMSYN_INPUT_BUFFER_SIZE 256
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// context declarations
+typedef struct HBMSYNVOICE HBMSYNVOICE; // see synprivate.h
+
+// forward declarations
+typedef struct HBMSYNSYNTH HBMSYNSYNTH;
+
+typedef struct WTADPCM {
+ u16 a[8][2]; // size 0x10, offset 0x00
+ u16 gain; // size 0x02, offset 0x20
+ u16 pred_scale; // size 0x02, offset 0x22
+ u16 yn1; // size 0x02, offset 0x24
+ u16 yn2; // size 0x02, offset 0x26
+ u16 loop_pred_scale; // size 0x02, offset 0x28
+ u16 loop_yn1; // size 0x02, offset 0x2a
+ u16 loop_yn2; // size 0x02, offset 0x2c
+} WTADPCM; // size 0x2e
+
+typedef struct WTART {
+ s32 lfoFreq; // size 0x04, offset 0x00
+ s32 lfoDelay; // size 0x04, offset 0x04
+ s32 lfoAtten; // size 0x04, offset 0x08
+ s32 lfoPitch; // size 0x04, offset 0x0c
+ s32 lfoMod2Atten; // size 0x04, offset 0x10
+ s32 lfoMod2Pitch; // size 0x04, offset 0x14
+ s32 eg1Attack; // size 0x04, offset 0x18
+ s32 eg1Decay; // size 0x04, offset 0x1c
+ s32 eg1Sustain; // size 0x04, offset 0x20
+ s32 eg1Release; // size 0x04, offset 0x24
+ s32 eg1Vel2Attack; // size 0x04, offset 0x28
+ s32 eg1Key2Decay; // size 0x04, offset 0x2c
+ s32 eg2Attack; // size 0x04, offset 0x30
+ s32 eg2Decay; // size 0x04, offset 0x34
+ s32 eg2Sustain; // size 0x04, offset 0x38
+ s32 eg2Release; // size 0x04, offset 0x3c
+ s32 eg2Vel2Attack; // size 0x04, offset 0x40
+ s32 eg2Key2Decay; // size 0x04, offset 0x44
+ s32 eg2Pitch; // size 0x04, offset 0x48
+ s32 pan; // size 0x04, offset 0x4c
+} WTART; // size 0x50
+
+typedef struct WTINST {
+ u16 keyRegion[HBMSYN_NUM_MIDI_NOTES];
+} WTINST; // size 0x100
+
+typedef struct WTREGION {
+ u8 unityNote; // size 0x01, offset 0x00
+ u8 keyGroup; // size 0x01, offset 0x01
+ s16 fineTune; // size 0x02, offset 0x02
+ s32 attn; // size 0x04, offset 0x04
+ u32 loopStart; // size 0x04, offset 0x08
+ u32 loopLength; // size 0x04, offset 0x0c
+ u32 articulationIndex; // size 0x04, offset 0x10
+ u32 sampleIndex; // size 0x04, offset 0x14
+} WTREGION; // size 0x18
+
+typedef struct WTSAMPLE {
+ u16 format; // size 0x02, offset 0x00
+ u16 sampleRate; // size 0x02, offset 0x02
+ u32 offset; // size 0x04, offset 0x04
+ u32 length; // size 0x04, offset 0x08
+ u16 adpcmIndex; // size 0x02, offset 0x0c
+ /* 2 bytes padding */
+} WTSAMPLE; // size 0x10
+
+struct HBMSYNSYNTH {
+ HBMSYNSYNTH* next; // size 0x0004, offset 0x0000
+ WTINST* percussiveInst; // size 0x0004, offset 0x0004
+ WTINST* melodicInst; // size 0x0004, offset 0x0008
+ WTREGION* region; // size 0x0004, offset 0x000c
+ WTART* art; // size 0x0004, offset 0x0010
+ WTSAMPLE* sample; // size 0x0004, offset 0x0014
+ WTADPCM* adpcm; // size 0x0004, offset 0x0018
+ u32 samplesBaseWord; // size 0x0004, offset 0x001c
+ u32 samplesBaseByte; // size 0x0004, offset 0x0020
+ u32 samplesBaseNibble; // size 0x0004, offset 0x0024
+ WTINST* inst[HBMSYN_NUM_MIDI_CHANNELS]; // size 0x0040, offset 0x0028
+ s32 masterVolume; // size 0x0004, offset 0x0068
+ s32 volAttn[HBMSYN_NUM_MIDI_CHANNELS]; // size 0x0040, offset 0x006c
+ s32 auxAAttn[HBMSYN_NUM_MIDI_CHANNELS]; // size 0x0040, offset 0x00ac
+ u8 pan[HBMSYN_NUM_MIDI_CHANNELS]; // size 0x0010, offset 0x00ec
+ u8 input[HBMSYN_INPUT_BUFFER_SIZE][3]; // size 0x0300, offset 0x00fc
+ u8* inputPosition; // size 0x0004, offset 0x03fc
+ u32 inputCounter; // size 0x0004, offset 0x0400
+ u32 notes; // size 0x0004, offset 0x0404
+ HBMSYNVOICE* voice[HBMSYN_NUM_MIDI_CHANNELS]
+ [HBMSYN_NUM_MIDI_NOTES]; // size 0x2000, offset 0x0408
+}; // size 0x2408
+
+void HBMSYNInit(void);
+void HBMSYNQuit(void);
+
+void HBMSYNRunAudioFrame(void);
+
+void HBMSYNInitSynth(HBMSYNSYNTH* synth, u8* wavetable, u8* samples, u8* zerobuffer);
+void HBMSYNQuitSynth(HBMSYNSYNTH* synth);
+
+void HBMSYNMidiInput(HBMSYNSYNTH* synth, u8* input);
+
+void HBMSYNSetMasterVolume(HBMSYNSYNTH* synth, s32 dB);
+s32 HBMSYNGetMasterVolume(HBMSYNSYNTH* synth);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RVL_SDK_HBMSYN_H
diff --git a/src/revolution/homebuttonLib/sound/synctrl.cpp b/src/revolution/homebuttonLib/sound/synctrl.cpp
new file mode 100644
index 0000000000..a683e5528b
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synctrl.cpp
@@ -0,0 +1,309 @@
+#include "synprivate.h"
+
+
+#include <revolution/types.h>
+
+#include "mix.h"
+
+// #include <revolution/ax.h>
+// #include <revolution/os.h>
+// #include <revolution/os/OSInterrupt.h>
+
+
+
+// MidiChannelVoiceMessage
+#define MIDI_MESSAGE_CHANNEL_VOICE_NOTE_OFF 8
+#define MIDI_MESSAGE_CHANNEL_VOICE_NOTE_ON 9
+#define MIDI_MESSAGE_CHANNEL_VOICE_CONTROL_CHANGE 11
+#define MIDI_MESSAGE_CHANNEL_VOICE_PROGRAM_CHANGE 12
+
+// MidiChannelControl
+#define MIDI_CC_CHANNEL_VOLUME 7
+#define MIDI_CC_PAN 10
+#define MIDI_CC_EXPRESSION 11
+#define MIDI_CC_REVERB_SEND_LEVEL 91
+#define MIDI_CC_TREMOLO_DEPTH 92
+
+
+static void __HBMSYNProgramChange(HBMSYNSYNTH *synth, u8 midiChannel,
+ u8 program);
+
+static void __HBMSYNResetController(HBMSYNSYNTH *synth, u8 midiChannel);
+
+static void __HBMSYNNoteOff(HBMSYNSYNTH *synth, u8 midiChannel, u8 keyNum);
+static void __HBMSYNNoteOn(HBMSYNSYNTH *synth, u8 midiChannel, u8 keyNum,
+ u8 keyVel);
+static void __HBMSYNMidiIn(HBMSYNSYNTH *synth, u8 *input);
+
+
+// .data
+
+// Why is this here anyways if its only use is in another file
+f32 __HBMSYNn128[128] = // x/128, rounded to 5 decimal places
+{
+ 0.0f, 0.007813f, 0.015625f, 0.023438f,
+ 0.03125f, 0.039063f, 0.046875f, 0.054688f,
+ 0.0625f, 0.070313f, 0.078125f, 0.085938f,
+ 0.09375f, 0.101563f, 0.109375f, 0.117188f,
+ 0.125f, 0.132813f, 0.140625f, 0.148438f,
+ 0.15625f, 0.164063f, 0.171875f, 0.179688f,
+ 0.1875f, 0.195313f, 0.203125f, 0.210938f,
+ 0.21875f, 0.226563f, 0.234375f, 0.242188f,
+ 0.25f, 0.257813f, 0.265625f, 0.273438f,
+ 0.28125f, 0.289063f, 0.296875f, 0.304688f,
+ 0.3125f, 0.320313f, 0.328125f, 0.335938f,
+ 0.34375f, 0.351563f, 0.359375f, 0.367188f,
+ 0.375f, 0.382813f, 0.390625f, 0.398438f,
+ 0.40625f, 0.414063f, 0.421875f, 0.429688f,
+ 0.4375f, 0.445313f, 0.453125f, 0.460938f,
+ 0.46875f, 0.476563f, 0.484375f, 0.492188f,
+ 0.5f, 0.507813f, 0.515625f, 0.523438f,
+ 0.53125f, 0.539063f, 0.546875f, 0.554688f,
+ 0.5625f, 0.570313f, 0.578125f, 0.585938f,
+ 0.59375f, 0.601563f, 0.609375f, 0.617188f,
+ 0.625f, 0.632813f, 0.640625f, 0.648438f,
+ 0.65625f, 0.664063f, 0.671875f, 0.679688f,
+ 0.6875f, 0.695313f, 0.703125f, 0.710938f,
+ 0.71875f, 0.726563f, 0.734375f, 0.742188f,
+ 0.75f, 0.757813f, 0.765625f, 0.773438f,
+ 0.78125f, 0.789063f, 0.796875f, 0.804688f,
+ 0.8125f, 0.820313f, 0.828125f, 0.835938f,
+ 0.84375f, 0.851563f, 0.859375f, 0.867188f,
+ 0.875f, 0.882813f, 0.890625f, 0.898438f,
+ 0.90625f, 0.914063f, 0.921875f, 0.929688f,
+ 0.9375f, 0.945313f, 0.953125f, 0.960938f,
+ 0.96875f, 0.976563f, 0.984375f, 0.992188f
+};
+
+
+static void __HBMSYNProgramChange(HBMSYNSYNTH *synth, u8 midiChannel,
+ u8 program)
+{
+ ASSERTLINE(54, synth);
+ ASSERTLINE(55, midiChannel < 16);
+ ASSERTLINE(56, program < 128);
+
+ synth->inst[midiChannel] = &synth->melodicInst[program];
+}
+
+void __HBMSYNSetController(HBMSYNSYNTH *synth, u8 midiChannel, u8 function,
+ u8 value)
+{
+ ASSERTLINE(67, synth);
+ ASSERTLINE(68, midiChannel < 16);
+ ASSERTLINE(69, function < 128);
+ ASSERTLINE(70, value < 128);
+
+ switch (function)
+ {
+ case MIDI_CC_CHANNEL_VOLUME:
+ synth->volAttn[midiChannel] = __HBMSYNVolumeAttenuation[value];
+ break;
+
+ case MIDI_CC_PAN:
+ synth->pan[midiChannel] = value;
+ break;
+
+ case MIDI_CC_REVERB_SEND_LEVEL:
+ synth->auxAAttn[midiChannel] = __HBMSYNVolumeAttenuation[value];
+ break;
+
+ default: // the rest aren't supported
+ break;
+ }
+}
+
+static void __HBMSYNResetController(HBMSYNSYNTH *synth, u8 midiChannel)
+{
+ ASSERTLINE(102, synth);
+ ASSERTLINE(103, midiChannel < 16);
+
+ __HBMSYNSetController(synth, midiChannel, MIDI_CC_CHANNEL_VOLUME, 100);
+ __HBMSYNSetController(synth, midiChannel, MIDI_CC_PAN, 64);
+ __HBMSYNSetController(synth, midiChannel, MIDI_CC_EXPRESSION, 127);
+ __HBMSYNSetController(synth, midiChannel, MIDI_CC_REVERB_SEND_LEVEL, 0);
+ __HBMSYNSetController(synth, midiChannel, MIDI_CC_TREMOLO_DEPTH, 0);
+}
+
+void __HBMSYNResetAllControllers(HBMSYNSYNTH *synth)
+{
+ u8 midiChannel;
+
+ ASSERTLINE(121, synth);
+
+ for (midiChannel = 0; midiChannel < HBMSYN_NUM_MIDI_CHANNELS; ++midiChannel)
+ {
+ __HBMSYNProgramChange(synth, midiChannel, 0);
+ __HBMSYNResetController(synth, midiChannel);
+ }
+}
+
+static void __HBMSYNNoteOff(HBMSYNSYNTH *synth, u8 midiChannel, u8 keyNum)
+{
+ HBMSYNVOICE *voice;
+
+ ASSERTLINE(138, synth);
+ ASSERTLINE(139, midiChannel < 16);
+ ASSERTLINE(140, keyNum < 128);
+
+ voice = synth->voice[midiChannel][keyNum];
+
+ if (voice)
+ {
+ __HBMSYNSetVoiceToRelease(voice);
+ synth->voice[midiChannel][keyNum] = NULL;
+ }
+}
+
+static void __HBMSYNNoteOn(HBMSYNSYNTH *synth, u8 midiChannel, u8 keyNum,
+ u8 keyVel)
+{
+ ASSERTLINE(162, synth);
+ ASSERTLINE(163, midiChannel < 16);
+ ASSERTLINE(164, keyNum < 128);
+ ASSERTLINE(165, keyVel < 128);
+
+ if (keyVel)
+ {
+ AXVPB *axvpb;
+
+ if (synth->voice[midiChannel][keyNum])
+ {
+ __HBMSYNSetVoiceToRelease(synth->voice[midiChannel][keyNum]);
+ synth->voice[midiChannel][keyNum] = NULL;
+ }
+
+ axvpb = AXAcquireVoice(AX_PRIORITY_MAX, &__HBMSYNClearVoiceReferences,
+ (u32)synth);
+
+ if (axvpb)
+ {
+ int index = HBMAllocIndex(axvpb->index);
+
+ if (index >= 0)
+ {
+ HBMSYNVOICE *voice = __HBMSYNVoice + index;
+
+ voice->axvpb = axvpb;
+ voice->synth = synth;
+ voice->midiChannel = midiChannel;
+ voice->keyNum = keyNum;
+ voice->keyVel = keyVel;
+
+ if (__HBMSYNGetWavetableData(voice))
+ {
+ synth->voice[midiChannel][keyNum] = voice;
+ ++synth->notes;
+
+ __HBMSYNSetupPitch(voice);
+ __HBMSYNSetupVolume(voice);
+ __HBMSYNSetupPan(voice);
+ __HBMSYNSetupVolumeEnvelope(voice);
+
+ HBMMIXInitChannel(axvpb, __HBMSYNGetVoiceInput(voice),
+ synth->auxAAttn[midiChannel] >> 16,
+ synth->pan[midiChannel],
+ __HBMSYNGetVoiceFader(voice));
+
+ __HBMSYNSetupSample(voice);
+ __HBMSYNSetupSrc(voice);
+
+ axvpb->pb.state = 1;
+ axvpb->sync |= AX_VPB_SYNC_FLAG_STATE;
+ }
+ else
+ {
+ voice->synth = NULL;
+
+ HBMMIXReleaseChannel(axvpb);
+ HBMFreeIndex(index);
+ AXFreeVoice(axvpb);
+ }
+ }
+ else
+ {
+ AXFreeVoice(axvpb);
+ }
+ }
+ }
+ else
+ {
+ __HBMSYNNoteOff(synth, midiChannel, keyNum);
+ }
+}
+
+static void __HBMSYNMidiIn(HBMSYNSYNTH *synth, u8 *input)
+{
+ u8 *ch;
+ u8 midiFunction;
+ u8 midiChannel;
+ u8 _2ndByte;
+ u8 _3rdByte;
+
+ ASSERTLINE(275, synth);
+ ASSERTLINE(276, input);
+
+ ch = input;
+
+ midiFunction = *ch >> 4;
+ midiChannel = *ch & 0x0f;
+ ++ch;
+
+ _2ndByte = *ch;
+ ++ch;
+
+ switch (midiFunction)
+ {
+ case MIDI_MESSAGE_CHANNEL_VOICE_NOTE_OFF:
+ __HBMSYNNoteOff(synth, midiChannel, _2ndByte);
+ break;
+
+ case MIDI_MESSAGE_CHANNEL_VOICE_NOTE_ON:
+ _3rdByte = *ch;
+ __HBMSYNNoteOn(synth, midiChannel, _2ndByte, _3rdByte);
+ break;
+
+ case MIDI_MESSAGE_CHANNEL_VOICE_CONTROL_CHANGE:
+ _3rdByte = *ch;
+ __HBMSYNSetController(synth, midiChannel, _2ndByte, _3rdByte);
+ break;
+
+ case MIDI_MESSAGE_CHANNEL_VOICE_PROGRAM_CHANGE:
+ __HBMSYNProgramChange(synth, midiChannel, _2ndByte);
+ break;
+
+ default: // the rest aren't supported
+ break;
+ }
+}
+
+void __HBMSYNRunInputBufferEvents(HBMSYNSYNTH *synth)
+{
+ u8 (*input)[3];
+
+ for (input = synth->input; synth->inputCounter; --synth->inputCounter)
+ {
+ __HBMSYNMidiIn(synth, *input);
+
+ ++input;
+ }
+
+ synth->inputPosition = *synth->input;
+}
+
+BOOL __HBMSYNGetWavetableData(HBMSYNVOICE *voice)
+{
+ HBMSYNSYNTH *synth = voice->synth;
+ u32 regionIndex = synth->inst[voice->midiChannel]->keyRegion[voice->keyNum];
+
+ // TODO: a define, maybe?
+ if (regionIndex == 0xffff)
+ return FALSE;
+
+ voice->region = synth->region + regionIndex;
+ voice->art = synth->art + voice->region->articulationIndex;
+ voice->sample = synth->sample + voice->region->sampleIndex;
+ voice->adpcm = synth->adpcm + voice->sample->adpcmIndex;
+
+ return TRUE;
+}
diff --git a/src/revolution/homebuttonLib/sound/synenv.cpp b/src/revolution/homebuttonLib/sound/synenv.cpp
new file mode 100644
index 0000000000..2e7c27d8f9
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synenv.cpp
@@ -0,0 +1,138 @@
+#include "synprivate.h"
+
+
+#include <limits.h>
+#include <math.h>
+
+#include <revolution/types.h>
+
+// #include <revolution/os.h>
+
+
+
+// multiplication for negative numbers
+#define UpperHalf_(x) ((x) * (1 << 16))
+
+
+s32 __HBMSYNGetEnvelopeTime(s32 scale, s32 mod, u8 key)
+{
+ // I do not know what these big numbers are for
+
+ if (scale == LONG_MIN)
+ return 0;
+
+ if (mod == LONG_MIN)
+ return powf(2.0f, scale / (1.2e3f * 65536.0f)) * 1e3f;
+
+ return powf(2.0f, (mod * __HBMSYNn128[key] + scale) / (1.2e3f * 65535.0f))
+ * 1e3f;
+}
+
+void __HBMSYNSetupVolumeEnvelope(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(38, voice);
+
+ if (voice->art->eg1Attack == LONG_MIN)
+ {
+ voice->veState = EnvStateDecay;
+ voice->veAttn = UpperHalf_(0);
+
+ if (voice->art->eg1Decay == LONG_MIN)
+ {
+ voice->veState = EnvStateSustain;
+ voice->veAttn = voice->art->eg1Sustain;
+ }
+ }
+ else
+ {
+ s32 frames = __HBMSYNGetEnvelopeTime(voice->art->eg1Attack,
+ voice->art->eg1Vel2Attack,
+ voice->keyVel) / 3;
+
+ if (frames != 0)
+ {
+ // I do not know why these numbers are in the upper half
+ voice->veAttack = UpperHalf_(0);
+
+ // I think this line could set some of the lower half, though?
+ voice->veAttackDelta = UpperHalf_(100) / frames;
+ voice->veAttn = UpperHalf_(-960);
+ voice->veState = EnvStateAttack;
+ }
+ else
+ {
+ voice->veAttack = UpperHalf_(0);
+ voice->veAttackDelta = UpperHalf_(100);
+ voice->veAttn = UpperHalf_(-960);
+ voice->veState = EnvStateAttack;
+ }
+ }
+
+ // attack or decay
+ if (voice->veState < EnvStateSustain)
+ {
+ s32 frames = __HBMSYNGetEnvelopeTime(voice->art->eg1Decay,
+ voice->art->eg1Key2Decay,
+ voice->keyNum) / 3;
+
+ if (frames != 0)
+ voice->veDecay = UpperHalf_(-960) / frames;
+ else
+ voice->veDecay = UpperHalf_(-960);
+ }
+
+ voice->veSustain = voice->art->eg1Sustain;
+ voice->veRelease = voice->art->eg1Release;
+}
+
+void __HBMSYNRunVolumeEnvelope(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(101, voice);
+
+ switch (voice->veState)
+ {
+ case EnvStateAttack:
+ voice->veAttack += voice->veAttackDelta;
+
+ if (voice->veAttack >= UpperHalf_(99))
+ voice->veAttn = UpperHalf_(0);
+ else
+ voice->veAttn = __HBMSYNAttackAttnTable[voice->veAttack >> 16];
+
+ if (voice->veAttn == UpperHalf_(0))
+ voice->veState = EnvStateDecay;
+
+ break;
+
+ case EnvStateDecay:
+ voice->veAttn += voice->veDecay;
+
+ if (voice->veAttn <= voice->veSustain)
+ {
+ voice->veAttn = voice->veSustain;
+ voice->veState = EnvStateSustain;
+ }
+
+ if (voice->veAttn <= UpperHalf_(-720))
+ {
+ voice->veState = EnvStateEnd;
+ voice->synth->voice[voice->midiChannel][voice->keyNum] = NULL;
+ }
+
+ break;
+
+ case EnvStateSustain:
+ /* TODO: is this handled elsewhere? Or is it just nothing because it's
+ * a sustain state
+ */
+ break;
+
+ case EnvStateRelease:
+ if (voice->veAttn <= UpperHalf_(-720))
+ voice->veState = EnvStateEnd;
+ else
+ voice->veAttn += voice->veRelease;
+
+ break;
+ }
+}
diff --git a/src/revolution/homebuttonLib/sound/synmix.cpp b/src/revolution/homebuttonLib/sound/synmix.cpp
new file mode 100644
index 0000000000..a1857ca432
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synmix.cpp
@@ -0,0 +1,113 @@
+#include "synprivate.h"
+
+
+#include <revolution/types.h>
+
+#include "mix.h"
+
+// #include <revolution/os.h>
+
+
+
+s32 __HBMSYNVolumeAttenuation[128] =
+{
+ -62914560, -55149952, -47258631, -42642504,
+ -39367310, -36826872, -34751184, -32996214,
+ -31475990, -30135057, -28935552, -27850467,
+ -26859863, -25948595, -25104893, -24319425,
+ -23584669, -22894472, -22243736, -21628193,
+ -21044231, -20488766, -19959147, -19453074,
+ -18968542, -18503793, -18057274, -17627610,
+ -17213572, -16814066, -16428104, -16054800,
+ -15693348, -15343020, -15003151, -14673134,
+ -14352415, -14040484, -13736873, -13441148,
+ -13152910, -12871791, -12597446, -12329556,
+ -12067826, -11811978, -11561753, -11316910,
+ -11077221, -10842476, -10612472, -10387024,
+ -10165954, -9949094, -9736289, -9527388,
+ -9322252, -9120746, -8922745, -8728129,
+ -8536784, -8348601, -8163479, -7981319,
+ -7802027, -7625516, -7451699, -7280496,
+ -7111830, -6945626, -6781814, -6620325,
+ -6461095, -6304061, -6149164, -5996346,
+ -5845552, -5696729, -5549827, -5404796,
+ -5261590, -5120162, -4980470, -4842471,
+ -4706125, -4571392, -4438236, -4306618,
+ -4176505, -4047862, -3920657, -3794857,
+ -3670432, -3547352, -3425589, -3305114,
+ -3185901, -3067923, -2951155, -2835573,
+ -2721152, -2607870, -2495703, -2384632,
+ -2274633, -2165687, -2057774, -1950874,
+ -1844968, -1740039, -1636067, -1533037,
+ -1430931, -1329732, -1229425, -1129994,
+ -1031424, -933700, -836808, -740733,
+ -645463, -550983, -457281, -364343,
+ -272158, -180714, -89998, 0
+};
+
+// only used in synenv.cpp; it should really be there instead
+s32 __HBMSYNAttackAttnTable[100] =
+{
+ -62914560, -26157189, -22211529, -19903465,
+ -18265868, -16995649, -15957805, -15080320,
+ -14320208, -13649742, -13049989, -12507447,
+ -12012145, -11556511, -11134660, -10741926,
+ -10374548, -10029449, -9704081, -9396310,
+ -9104329, -8826596, -8561787, -8308750,
+ -8066484, -7834110, -7610850, -7396018,
+ -7188999, -6989246, -6796265, -6609613,
+ -6428887, -6253723, -6083789, -5918780,
+ -5758421, -5602455, -5450650, -5302787,
+ -5158668, -5018109, -4880936, -4746991,
+ -4616126, -4488202, -4363090, -4240668,
+ -4120824, -4003451, -3888449, -3775725,
+ -3665190, -3556760, -3450358, -3345907,
+ -3243339, -3142586, -3043586, -2946278,
+ -2850605, -2756514, -2663953, -2572873,
+ -2483227, -2394971, -2308063, -2222461,
+ -2138128, -2055026, -1973120, -1892376,
+ -1812761, -1734244, -1656795, -1580386,
+ -1504989, -1430578, -1357127, -1284611,
+ -1213008, -1142294, -1072448, -1003449,
+ -935276, -867909, -801331, -735522,
+ -670466, -606144, -542542, -479642,
+ -417429, -355889, -295008, -234770,
+ -175164, -116175, -57791, 0
+};
+
+
+void __HBMSYNSetupVolume(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(80, voice);
+
+ voice->attn =
+ voice->region->attn + __HBMSYNVolumeAttenuation[voice->keyVel];
+}
+
+void __HBMSYNSetupPan(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(93, voice);
+
+ voice->pan = voice->synth->pan[voice->midiChannel];
+}
+
+s32 __HBMSYNGetVoiceInput(HBMSYNVOICE *voice)
+{
+ return (voice->attn + voice->veAttn) >> 16;
+}
+
+s32 __HBMSYNGetVoiceFader(HBMSYNVOICE *voice)
+{
+ return (voice->synth->masterVolume
+ + voice->synth->volAttn[voice->midiChannel])
+ >> 16;
+}
+
+void __HBMSYNUpdateMix(HBMSYNVOICE *voice)
+{
+ HBMMIXSetInput(voice->axvpb, __HBMSYNGetVoiceInput(voice));
+ HBMMIXSetAuxA(voice->axvpb,
+ voice->synth->auxAAttn[voice->midiChannel] >> 16);
+ HBMMIXSetFader(voice->axvpb, __HBMSYNGetVoiceFader(voice));
+ HBMMIXSetPan(voice->axvpb, voice->synth->pan[voice->midiChannel]);
+}
diff --git a/src/revolution/homebuttonLib/sound/synpitch.cpp b/src/revolution/homebuttonLib/sound/synpitch.cpp
new file mode 100644
index 0000000000..921fe250f1
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synpitch.cpp
@@ -0,0 +1,193 @@
+#include "synprivate.h"
+
+
+#include <revolution/types.h>
+
+// #include <revolution/ax.h>
+
+
+
+static f32 __HBMSYNCentsTable[100] =
+{
+ 1.0f, 1.000578f, 1.001156f, 1.001734f,
+ 1.002313f, 1.002892f, 1.003472f, 1.004052f,
+ 1.004632f, 1.005212f, 1.005793f, 1.006374f,
+ 1.006956f, 1.007537f, 1.00812f, 1.008702f,
+ 1.009285f, 1.009868f, 1.010451f, 1.011035f,
+ 1.011619f, 1.012204f, 1.012789f, 1.013374f,
+ 1.013959f, 1.014545f, 1.015132f, 1.015718f,
+ 1.016305f, 1.016892f, 1.01748f, 1.018068f,
+ 1.018656f, 1.019244f, 1.019833f, 1.020423f,
+ 1.021012f, 1.021602f, 1.022192f, 1.022783f,
+ 1.023374f, 1.023965f, 1.024557f, 1.025149f,
+ 1.025741f, 1.026334f, 1.026927f, 1.02752f,
+ 1.028114f, 1.028708f, 1.029302f, 1.029897f,
+ 1.030492f, 1.031087f, 1.031683f, 1.032279f,
+ 1.032876f, 1.033472f, 1.03407f, 1.034667f,
+ 1.035265f, 1.035863f, 1.036462f, 1.03706f,
+ 1.03766f, 1.038259f, 1.038859f, 1.039459f,
+ 1.04006f, 1.040661f, 1.041262f, 1.041864f,
+ 1.042466f, 1.043068f, 1.043671f, 1.044274f,
+ 1.044877f, 1.045481f, 1.046085f, 1.046689f,
+ 1.047294f, 1.047899f, 1.048505f, 1.049111f,
+ 1.049717f, 1.050323f, 1.05093f, 1.051537f,
+ 1.052145f, 1.052753f, 1.053361f, 1.05397f,
+ 1.054579f, 1.055188f, 1.055798f, 1.056408f,
+ 1.057018f, 1.057629f, 1.05824f, 1.058851f
+};
+
+static f32 __HBMSYNOctavesTableUp[12] =
+{
+ 1.0f,
+ 2.0f,
+ 4.0f,
+ 8.0f,
+ 16.0f,
+ 32.0f,
+ 64.0f,
+ 128.0f,
+ 256.0f,
+ 512.0f,
+ 1024.0f,
+ 2048.0f
+};
+
+static f32 __HBMSYNSemitonesTableUp[12] =
+{
+ 1.0f,
+ 1.059463f,
+ 1.122462f,
+ 1.189207f,
+ 1.259921f,
+ 1.33484f,
+ 1.414214f,
+ 1.498307f,
+ 1.587401f,
+ 1.681793f,
+ 1.781797f,
+ 1.887749f
+};
+
+static f32 __HBMSYNSemitonesTableDown[128] =
+{
+ 1.0f, 0.943874f, 0.890899f, 0.840896f,
+ 0.793701f, 0.749154f, 0.707107f, 0.66742f,
+ 0.629961f, 0.594604f, 0.561231f, 0.529732f,
+
+ 0.5f, 0.471937f, 0.445449f, 0.420448f,
+ 0.39685f, 0.374577f, 0.353553f, 0.33371f,
+ 0.31498f, 0.297302f, 0.280616f, 0.264866f,
+
+ 0.25f, 0.235969f, 0.222725f, 0.210224f,
+ 0.198425f, 0.187288f, 0.176777f, 0.166855f,
+ 0.15749f, 0.148651f, 0.140308f, 0.132433f,
+
+ 0.125f, 0.117984f, 0.111362f, 0.105112f,
+ 0.099213f, 0.093644f, 0.088388f, 0.083427f,
+ 0.078745f, 0.074325f, 0.070154f, 0.066216f,
+
+ 0.0625f, 0.058992f, 0.055681f, 0.052556f,
+ 0.049606f, 0.046822f, 0.044194f, 0.041714f,
+ 0.039373f, 0.037163f, 0.035077f, 0.033108f,
+
+ 0.03125f, 0.029496f, 0.027841f, 0.026278f,
+ 0.024803f, 0.023411f, 0.022097f, 0.020857f,
+ 0.019686f, 0.018581f, 0.017538f, 0.016554f,
+
+ 0.015625f, 0.014748f, 0.01392f, 0.013139f,
+ 0.012402f, 0.011706f, 0.011049f, 0.010428f,
+ 0.009843f, 0.009291f, 0.008769f, 0.008277f,
+
+ 0.007813f, 0.007374f, 0.00696f, 0.00657f,
+ 0.006201f, 0.005853f, 0.005524f, 0.005214f,
+ 0.004922f, 0.004645f, 0.004385f, 0.004139f,
+
+ 0.003906f, 0.003687f, 0.00348f, 0.003285f,
+ 0.0031f, 0.002926f, 0.002762f, 0.002607f,
+ 0.002461f, 0.002323f, 0.002192f, 0.002069f,
+
+ 0.001953f, 0.001844f, 0.00174f, 0.001642f,
+ 0.00155f, 0.001463f, 0.001381f, 0.001304f,
+ 0.00123f, 0.001161f, 0.001096f, 0.001035f,
+
+ 9.77e-4f, 9.22e-4f, 8.7e-4f, 8.21e-4f,
+ 7.75e-4f, 7.32e-4f, 6.91e-4f, 6.52e-4f
+};
+
+
+f32 __HBMSYNGetRelativePitch(HBMSYNVOICE *voice)
+{
+ s32 cents = voice->cents;
+ cents /= 1 << 16;
+
+ if (cents > 0)
+ {
+ s32 octaves = cents / 1200;
+ s32 semitones = cents % 1200 / 100;
+
+ cents %= 100;
+
+ return __HBMSYNOctavesTableUp[octaves]
+ * __HBMSYNSemitonesTableUp[semitones] * __HBMSYNCentsTable[cents];
+ }
+ else if (cents < 0)
+ {
+ s32 semitones = cents / 100;
+
+ cents %= 100;
+ if (cents != 0)
+ {
+ cents += 100;
+ semitones -= 1;
+ }
+
+ semitones *= -1; // since semitones is negative
+
+ return __HBMSYNSemitonesTableDown[semitones]
+ * __HBMSYNCentsTable[cents];
+ }
+ else
+ {
+ return 1.0f;
+ }
+}
+
+void __HBMSYNSetupPitch(HBMSYNVOICE *voice)
+{
+ voice->srcRatio = voice->sample->sampleRate / 3.2e4f;
+
+ voice->cents = (voice->keyNum - voice->region->unityNote) * 100;
+ voice->cents += voice->region->fineTune;
+ voice->cents <<= 16;
+}
+
+void __HBMSYNSetupSrc(HBMSYNVOICE *voice)
+{
+ f32 srcRatio = voice->srcRatio * __HBMSYNGetRelativePitch(voice);
+ u32 value = srcRatio * 65536.0f;
+ u16 *p;
+
+ voice->axvpb->pb.srcSelect = 1;
+
+ // cool
+ p = (u16 *)&voice->axvpb->pb.src;
+
+ /* src.ratioHi */ *p++ = value >> 16;
+ /* src.ratioLo */ *p++ = value;
+ /* src.currentAdddressFrac */ *p++ = 0;
+ /* src.last_samples[0] */ *p++ = 0;
+ /* src.last_samples[1] */ *p++ = 0;
+ /* src.last_samples[2] */ *p++ = 0;
+ /* src.last_samples[3] */ *p = 0;
+
+ voice->axvpb->sync &= ~AX_VPB_SYNC_FLAG_SRC_RATIO;
+ voice->axvpb->sync |= AX_VPB_SYNC_FLAG_SRC_TYPE | AX_VPB_SYNC_FLAG_SRC;
+}
+
+void __HBMSYNUpdateSrc(HBMSYNVOICE *voice)
+{
+ u32 ratio = voice->srcRatio * __HBMSYNGetRelativePitch(voice) * 65536.0f;
+
+ *(u32 *)&voice->axvpb->pb.src.ratioHi = ratio;
+ voice->axvpb->sync |= AX_VPB_SYNC_FLAG_SRC_RATIO;
+}
diff --git a/src/revolution/homebuttonLib/sound/synprivate.h b/src/revolution/homebuttonLib/sound/synprivate.h
new file mode 100644
index 0000000000..80fc3e28f8
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synprivate.h
@@ -0,0 +1,106 @@
+#ifndef RVL_SDK_HBMSYN_PRIVATE_H
+#define RVL_SDK_HBMSYN_PRIVATE_H
+
+#include "syn.h" // IWYU pragma: export
+
+#include <revolution/ax.h>
+#include <revolution/types.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef u32 EnvelopeState;
+enum EnvelopeState_et {
+ EnvStateAttack,
+ EnvStateDecay,
+ EnvStateSustain,
+ EnvStateRelease,
+
+ EnvStateEnd
+};
+
+typedef u32 VoiceType;
+enum VoiceType_et {
+ VoiceTypeNormal,
+ VoiceTypeLooping,
+};
+
+// the typedef was declared in syn.h
+struct HBMSYNVOICE {
+ void* next; // size 0x04, offset 0x00
+ AXVPB* axvpb; // size 0x04, offset 0x04
+ HBMSYNSYNTH* synth; // size 0x04, offset 0x08
+ u8 midiChannel; // size 0x01, offset 0x0c
+ u8 keyNum; // size 0x01, offset 0x0d
+ u8 keyVel; // size 0x01, offset 0x0e
+ u8 pan; // size 0x01, offset 0x0f
+ WTREGION* region; // size 0x04, offset 0x10
+ WTART* art; // size 0x04, offset 0x14
+ WTSAMPLE* sample; // size 0x04, offset 0x18
+ WTADPCM* adpcm; // size 0x04, offset 0x1c
+ VoiceType type; // size 0x04, offset 0x20
+ f32 srcRatio; // size 0x04, offset 0x24
+ s32 cents; // size 0x04, offset 0x28
+ s32 attn; // size 0x04, offset 0x2c
+ EnvelopeState veState; // size 0x04, offset 0x30
+ s32 veAttn; // size 0x04, offset 0x34
+ s32 veAttack; // size 0x04, offset 0x38
+ s32 veAttackDelta; // size 0x04, offset 0x3c
+ s32 veDecay; // size 0x04, offset 0x40
+ s32 veSustain; // size 0x04, offset 0x44
+ s32 veRelease; // size 0x04, offset 0x48
+}; // size 0x4c
+
+// TODO: is the definition of HBMSYNVOICE visible at this point?
+
+// syn.c
+extern HBMSYNVOICE* __HBMSYNVoice;
+
+// TODO: are these declared with their complete type or no?
+
+// synctrl.c
+extern f32 __HBMSYNn128[];
+
+// synmix.c
+extern s32 __HBMSYNVolumeAttenuation[];
+extern s32 __HBMSYNAttackAttnTable[];
+
+// synctrl.c
+void __HBMSYNSetController(HBMSYNSYNTH* synth, u8 midiChannel, u8 function, u8 value);
+void __HBMSYNResetAllControllers(HBMSYNSYNTH* synth);
+void __HBMSYNRunInputBufferEvents(HBMSYNSYNTH* synth);
+BOOL __HBMSYNGetWavetableData(HBMSYNVOICE* voice);
+
+// synenv.c
+s32 __HBMSYNGetEnvelopeTime(s32 scale, s32 mod, u8 key);
+void __HBMSYNSetupVolumeEnvelope(HBMSYNVOICE* voice);
+void __HBMSYNRunVolumeEnvelope(HBMSYNVOICE* voice);
+
+// synmix.c
+void __HBMSYNSetupVolume(HBMSYNVOICE* voice);
+void __HBMSYNSetupPan(HBMSYNVOICE* voice);
+s32 __HBMSYNGetVoiceInput(HBMSYNVOICE* voice);
+s32 __HBMSYNGetVoiceFader(HBMSYNVOICE* voice);
+void __HBMSYNUpdateMix(HBMSYNVOICE* voice);
+
+// synpitch.c
+f32 __HBMSYNGetRelativePitch(HBMSYNVOICE* voice);
+void __HBMSYNSetupPitch(HBMSYNVOICE* voice);
+void __HBMSYNSetupSrc(HBMSYNVOICE* voice);
+void __HBMSYNUpdateSrc(HBMSYNVOICE* voice);
+
+// synsample.c
+void __HBMSYNSetupSample(HBMSYNVOICE* voice);
+
+// synvoice.c
+void __HBMSYNClearVoiceReferences(void* p);
+void __HBMSYNSetVoiceToRelease(HBMSYNVOICE* voice);
+void __HBMSYNServiceVoice(int i);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RVL_SDK_HBMSYN_PRIVATE_H
diff --git a/src/revolution/homebuttonLib/sound/synsample.cpp b/src/revolution/homebuttonLib/sound/synsample.cpp
new file mode 100644
index 0000000000..a6009ff96f
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synsample.cpp
@@ -0,0 +1,278 @@
+#include "synprivate.h"
+
+
+#include <revolution/types.h>
+
+// #include <revolution/ax.h>
+// #include <revolution/os.h>
+
+
+
+// clang-format off
+
+/* Compromise for enclosing these macros in a clang-format off block: I changed
+ * all of the indentation to spaces, so that everybody can see it the same way
+ * instead of having to switch to tabs:4 to enjoy it.
+ */
+
+#define SET_AXPBADDR(p_, flag_, fmt_, loop_, end_, cur_, gain_, pred_) \
+ do \
+ { \
+ /* contains doubled and tripled expressions */ \
+ \
+ /* addr.{loopFlag,format} */ *(p_) = (flag_) << 16 | (fmt_); ++(p_); \
+ /* addr.loopAddress{Hi,Lo} */ *(p_) = (loop_); ++(p_); \
+ /* addr.endAddress{Hi,Lo} */ *(p_) = (end_); ++(p_); \
+ /* addr.currentAddress{Hi,Lo} */ *(p_) = (cur_); ++(p_); \
+ \
+ /* adpcm.a[0][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[1][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[2][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[3][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[4][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[5][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[6][{0,1}] */ *(p_) = 0; ++(p_); \
+ /* adpcm.a[7][{0,1}] */ *(p_) = 0; ++(p_); \
+ \
+ /* adpcm.{gain,pred_scale} */ *(p_) = (gain_) << 16 | (pred_); ++(p_); \
+ /* adpcm.yn{1,2} */ *(p_) = 0; \
+ } while (0)
+
+#define SET_AXPBADDR_ADPCM(p_, flag_, fmt_, loop_, end_, cur_, adpcm_) \
+ do \
+ { \
+ /* contains doubled and tripled expressions */ \
+ \
+ /* addr.{loopFlag,format} */ *(p_) = (flag_) << 16 | (fmt_); ++(p_); \
+ /* addr.loopAddress{Hi,Lo} */ *(p_) = (loop_); ++(p_); \
+ /* addr.endAddress{Hi,Lo} */ *(p_) = (end_); ++(p_); \
+ /* addr.currentAddress{Hi,Lo} */ *(p_) = (cur_); ++(p_); \
+ \
+ /* adpcm.a[0][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[1][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[2][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[3][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[4][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[5][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[6][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.a[7][{0,1}] */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ \
+ /* adpcm.{gain,pred_scale} */ *(p_) = *(adpcm_); ++(p_); ++(adpcm_); \
+ /* adpcm.yn{1,2} */ *(p_) = *(adpcm_); \
+ } while (0)
+
+// clang-format on
+
+
+static u32 __HBMSYNGetNibbleAddress(u32 count);
+static void __HBMSYNSetupAdpcm(HBMSYNVOICE *voice);
+static void __HBMSYNSetupPcm16(HBMSYNVOICE *voice);
+static void __HBMSYNSetupPcm8(HBMSYNVOICE *voice);
+
+
+static u32 __HBMSYNGetNibbleAddress(u32 count)
+{
+ u32 const samples = count; // same thing as __HBMMIXGetVolume?
+ u32 frames = samples / 14;
+ u32 samplesLeft = samples % 14;
+
+ return 2 + frames * 16 + samplesLeft;
+}
+
+static void __HBMSYNSetupAdpcm(HBMSYNVOICE *voice)
+{
+ AXVPB *axvpb = voice->axvpb;
+
+ if (voice->region->loopStart + voice->region->loopLength != 0)
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+ u32 *adpcm;
+ u16 *adpcmloop;
+
+ adpcm = (u32 *)voice->adpcm;
+ voice->type = VoiceTypeLooping;
+
+ sampleStart = voice->synth->samplesBaseNibble + voice->sample->offset;
+ sampleLoop =
+ sampleStart + __HBMSYNGetNibbleAddress(voice->region->loopStart);
+ sampleEnd = sampleStart
+ + __HBMSYNGetNibbleAddress(voice->region->loopStart
+ + voice->region->loopLength - 1);
+
+ ASSERTLINE(64, (sampleStart & 0x000f) == 0);
+ ASSERTLINE(65, (sampleLoop & 0x000f) > 1);
+ ASSERTLINE(66, (sampleEnd & 0x000f) > 1);
+
+ sampleStart += sizeof(u16);
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR_ADPCM(p, TRUE, 0, sampleLoop, sampleEnd, sampleStart,
+ adpcm);
+
+ adpcmloop = (u16 *)++adpcm;
+
+ // doubled expressions
+ axvpb->pb.adpcmLoop.loop_pred_scale = *adpcmloop; ++adpcmloop;
+ axvpb->pb.adpcmLoop.loop_yn1 = *adpcmloop; ++adpcmloop;
+ axvpb->pb.adpcmLoop.loop_yn2 = *adpcmloop;
+
+ axvpb->sync &=
+ ~(AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG | AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_END_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_CURRENT_ADDR);
+
+ axvpb->sync |= AX_VPB_SYNC_FLAG_ADDR | AX_VPB_SYNC_FLAG_ADPCM
+ | AX_VPB_SYNC_FLAG_ADPCM_LOOP;
+ }
+ else
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+ u32 *adpcm;
+
+ adpcm = (u32 *)voice->adpcm;
+ voice->type = VoiceTypeNormal;
+
+ sampleStart = voice->synth->samplesBaseNibble + voice->sample->offset;
+ sampleLoop = voice->synth->samplesBaseNibble + voice->sample->offset;
+ sampleEnd =
+ sampleStart + __HBMSYNGetNibbleAddress(voice->sample->length - 1);
+
+ ASSERTLINE(120, (sampleStart & 0x000f) == 0);
+ ASSERTLINE(121, (sampleEnd & 0x000f) > 1);
+
+ sampleStart += sizeof(u16);
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR_ADPCM(p, FALSE, 0, sampleLoop, sampleEnd, sampleStart,
+ adpcm);
+
+ axvpb->sync &=
+ ~(AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG | AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_END_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_CURRENT_ADDR);
+
+ axvpb->sync |= AX_VPB_SYNC_FLAG_ADDR | AX_VPB_SYNC_FLAG_ADPCM;
+ }
+}
+
+static void __HBMSYNSetupPcm16(HBMSYNVOICE *voice)
+{
+ AXVPB *axvpb = voice->axvpb;
+
+ if (voice->region->loopStart + voice->region->loopLength != 0)
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+
+ voice->type = VoiceTypeLooping;
+
+ sampleStart = voice->synth->samplesBaseWord + voice->sample->offset;
+ sampleLoop = sampleStart + voice->region->loopStart;
+ sampleEnd = sampleLoop + voice->region->loopLength - 1;
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR(p, TRUE, 10, sampleLoop, sampleEnd, sampleStart, 0x0800,
+ 0);
+ }
+ else
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+
+ voice->type = VoiceTypeNormal;
+
+ sampleStart = voice->synth->samplesBaseWord + voice->sample->offset;
+ sampleLoop = voice->synth->samplesBaseWord + voice->sample->offset;
+ sampleEnd = sampleStart + voice->sample->length - 1;
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR(p, FALSE, 10, sampleLoop, sampleEnd, sampleStart, 0x0800,
+ 0);
+ }
+
+ axvpb->sync &= ~(
+ AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG | AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_END_ADDR | AX_VPB_SYNC_FLAG_ADDR_CURRENT_ADDR);
+
+ axvpb->sync |= AX_VPB_SYNC_FLAG_ADDR | AX_VPB_SYNC_FLAG_ADPCM;
+}
+
+static void __HBMSYNSetupPcm8(HBMSYNVOICE *voice)
+{
+ AXVPB *axvpb = voice->axvpb;
+
+ if (voice->region->loopStart + voice->region->loopLength != 0)
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+
+ voice->type = VoiceTypeLooping;
+
+ sampleStart = voice->synth->samplesBaseByte + voice->sample->offset;
+ sampleLoop = sampleStart + voice->region->loopStart;
+ sampleEnd = sampleLoop + voice->region->loopLength - 1;
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR(p, TRUE, 25, sampleLoop, sampleEnd, sampleStart, 0x0100,
+ 0);
+ }
+ else
+ {
+ u32 sampleStart;
+ u32 sampleLoop;
+ u32 sampleEnd;
+ u32 *p;
+
+ voice->type = VoiceTypeNormal;
+
+ sampleStart = voice->synth->samplesBaseByte + voice->sample->offset;
+ sampleLoop = voice->synth->samplesBaseByte + voice->sample->offset;
+ sampleEnd = sampleStart + voice->sample->length - 1;
+ p = (u32 *)&axvpb->pb.addr;
+
+ SET_AXPBADDR(p, FALSE, 25, sampleLoop, sampleEnd, sampleStart, 0x0100,
+ 0);
+ }
+
+ axvpb->sync &= ~(
+ AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG | AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR
+ | AX_VPB_SYNC_FLAG_ADDR_END_ADDR | AX_VPB_SYNC_FLAG_ADDR_CURRENT_ADDR);
+
+ axvpb->sync |= AX_VPB_SYNC_FLAG_ADDR | AX_VPB_SYNC_FLAG_ADPCM;
+}
+
+void __HBMSYNSetupSample(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(346, voice);
+
+ switch (voice->sample->format)
+ {
+ case 0:
+ __HBMSYNSetupAdpcm(voice);
+ break;
+
+ case 1:
+ __HBMSYNSetupPcm16(voice);
+ break;
+
+ case 2:
+ __HBMSYNSetupPcm8(voice);
+ break;
+
+ default:
+ OSAssertMessage_Line(370, FALSE, "unknown sample format\n");
+ break;
+ }
+}
diff --git a/src/revolution/homebuttonLib/sound/synvoice.cpp b/src/revolution/homebuttonLib/sound/synvoice.cpp
new file mode 100644
index 0000000000..0f26528142
--- /dev/null
+++ b/src/revolution/homebuttonLib/sound/synvoice.cpp
@@ -0,0 +1,78 @@
+#include "synprivate.h"
+
+
+#include <revolution/types.h>
+
+#include "mix.h"
+
+// #include <revolution/ax.h>
+// #include <revolution/os.h>
+
+
+
+void __HBMSYNClearVoiceReferences(void *p)
+{
+ AXVPB *axvpb;
+ HBMSYNSYNTH *synth;
+ HBMSYNVOICE *voice;
+ int index;
+
+ ASSERTLINE(27, p);
+
+ axvpb = (AXVPB *)p;
+ synth = (HBMSYNSYNTH *)axvpb->userContext;
+
+ index = HBMGetIndex(axvpb->index);
+ HBMFreeIndex(index);
+
+ voice = __HBMSYNVoice + index;
+ HBMMIXReleaseChannel(axvpb);
+
+ if (synth->voice[voice->midiChannel][voice->keyNum] == voice)
+ synth->voice[voice->midiChannel][voice->keyNum] = NULL;
+
+ voice->synth = NULL;
+ --synth->notes;
+}
+
+void __HBMSYNSetVoiceToRelease(HBMSYNVOICE *voice)
+{
+ ASSERTLINE(53, voice);
+
+ voice->veState = EnvStateRelease;
+}
+
+void __HBMSYNServiceVoice(int i)
+{
+ HBMSYNVOICE *voice = __HBMSYNVoice + i;
+ HBMSYNSYNTH *synth = voice->synth;
+
+ if (!synth)
+ return;
+
+ if (voice->type == VoiceTypeNormal && voice->axvpb->pb.state == 0)
+ {
+ if (synth->voice[voice->midiChannel][voice->keyNum] == voice)
+ synth->voice[voice->midiChannel][voice->keyNum] = NULL;
+
+ voice->veState = EnvStateEnd;
+ }
+
+ __HBMSYNRunVolumeEnvelope(voice);
+
+ if (voice->veState == EnvStateEnd)
+ {
+ voice->synth = NULL;
+
+ HBMMIXReleaseChannel(voice->axvpb);
+ HBMFreeIndexByKey(voice->axvpb->index);
+ AXFreeVoice(voice->axvpb);
+
+ --synth->notes;
+ }
+ else
+ {
+ __HBMSYNUpdateMix(voice);
+ __HBMSYNUpdateSrc(voice);
+ }
+}
diff --git a/src/revolution/wenc/wenc.c b/src/revolution/wenc/wenc.c
index a738e08f1a..570106c1e8 100644
--- a/src/revolution/wenc/wenc.c
+++ b/src/revolution/wenc/wenc.c
@@ -1,117 +1,131 @@
#include <revolution/wenc.h>
#include <string.h>
-s32 WENCGetEncodeData(WENCInfo* info, u32 flag, const s16* pcmData, s32 samples, u8* adpcmData) {
- const f64 table[] = {0.89843750, 0.89843750, 0.89843750, 0.89843750,
- 1.19921875, 1.59765625, 2.00000000, 2.39843750};
-
- u8* dst;
- const s16* src;
- s32 i;
- s32 da;
- s32 l3, l2, l1, l0;
- s32 dlx;
- s32 xnc;
- s32 offset;
- s32 index;
- s32 encodeSize;
- s32 xn;
- s32 dl;
- s32 qn;
- s32 dn;
- s32 dlh;
- s32 dlq;
- u8 by;
-
- encodeSize = (samples + 1) / 2;
- memset(adpcmData, 0, encodeSize);
+typedef struct {
+ s32 nXN; // size 0x04, offset 0x00
+ s32 nDL; // size 0x04, offset 0x04
+ s32 nQN; // size 0x04, offset 0x08
+ s32 nDN; // size 0x04, offset 0x0c
+ s32 nDLH; // size 0x04, offset 0x10
+ s32 nDLQ; // size 0x04, offset 0x14
+ u8 padding[8];
+} WENCBlock;
+
+s32 WENCGetEncodeData(WENCInfo* info, u32 flag, s16 const* pbyPcmData, s32 nSampleNum, u8* pbyAdpcmData) {
+ f64 const dTable[2 * 4] = {230.0 / 256.0, 230.0 / 256.0, 230.0 / 256.0, 230.0 / 256.0,
+ 307.0 / 256.0, 409.0 / 256.0, 512.0 / 256.0, 614.0 / 256.0};
- src = pcmData;
- dst = adpcmData;
-
- if ((flag & WENC_FLAG_USER_INFO) == 0) {
- xn = 0;
- dl = 127;
- qn = 0;
- dn = 0;
- dlh = 0;
- dlq = 0;
+ u8 by;
+ u8* pDst;
+ s16 const* pSrc;
+ int ii;
+ s32 nDA;
+ s32 nL3;
+ s32 nL2;
+ s32 nL1;
+ s32 nL0;
+ s32 nDLX;
+ s32 nXNC;
+ s32 nOffset;
+ s32 nIndex;
+ s32 nEncodeSize;
+ s32 nXN;
+ s32 nDL;
+ s32 nQN;
+ s32 nDN;
+ s32 nDLH;
+ s32 nDLQ;
+ WENCBlock* block;
+
+ block = (WENCBlock*)info;
+ nEncodeSize = (nSampleNum + 1) / 2;
+
+ memset(pbyAdpcmData, 0, nEncodeSize);
+
+ pSrc = pbyPcmData;
+ pDst = pbyAdpcmData;
+
+ if (!(flag & 1)) {
+ nXN = 0;
+ nDL = 127;
+ nQN = 0;
+ nDN = 0;
+ nDLH = 0;
+ nDLQ = 0;
} else {
- xn = info->xn;
- dl = info->dl;
- qn = info->qn;
- dn = info->dn;
- dlh = info->dlh;
- dlq = info->dlq;
+ nXN = block->nXN;
+ nDL = block->nDL;
+ nQN = block->nQN;
+ nDN = block->nDN;
+ nDLH = block->nDLH;
+ nDLQ = block->nDLQ;
}
- for (i = 0; i < samples; i++) {
- l3 = l2 = l1 = l0 = 0;
+ for (ii = 0; ii < nSampleNum; ii++) {
+ nL3 = nL2 = nL1 = nL0 = 0;
- da = *src++;
- if (da < xn) {
- l3 = 1;
- }
+ nDA = *pSrc++;
+ if (nDA < nXN)
+ nL3 = 1;
- dn = __abs(da - xn);
- if (dn >= dl) {
- l2 = 1;
- dn -= dl;
+ nDN = __abs(nDA - nXN);
+ if (nDN >= nDL) {
+ nL2 = 1;
+ nDN -= nDL;
}
- dlh = dl / 2;
- if (dn >= dlh) {
- l1 = 1;
- dn -= dlh;
+ nDLH = nDL / 2;
+ if (nDN >= nDLH) {
+ nL1 = 1;
+ nDN -= nDLH;
}
- dlq = dlh / 2;
- if (dn >= dlq) {
- l0 = 1;
- dn -= dlq;
+ nDLQ = nDLH / 2;
+ if (nDN >= nDLQ) {
+ nL0 = 1;
+ nDN -= nDLQ;
}
- dlx = dlq / 2;
- qn = (1 - l3 * 2) * (dl * l2 + dlh * l1 + dlq * l0 + dlx);
+ nDLX = nDLQ / 2;
- if (qn > 0xFFFF) {
- qn = 0xFFFF;
- }
- if (qn < -0x10000) {
- qn = -0x10000;
- }
+ nQN = (1 - nL3 * 2) * (nDL * nL2 + nDLH * nL1 + nDLQ * nL0 + nDLX);
- xnc = xn + qn;
- if (xnc > 0x7FFF) {
- xnc = 0x7FFF;
- }
- if (xnc < -0x8000) {
- xnc = -0x8000;
- }
+ if (nQN > 0xffff)
+ nQN = 0xffff;
- xn = xnc;
- offset = (i & 1) == 0 ? 4 : 0;
+ if (nQN < -0x10000)
+ nQN = -0x10000;
- by = l3 * 8 + l2 * 4 + l1 * 2 + l0;
- dst[i / 2] |= by << offset;
+ nXNC = nXN + nQN;
- index = l2 * 4 + l1 * 2 + l0;
- dl *= table[index];
+ if (nXNC > 0x7fff)
+ nXNC = 0x7fff;
- if (dl <= 127) {
- dl = 127;
- }
- if (dl >= 0x6000) {
- dl = 0x6000;
- }
+ if (nXNC < -0x8000)
+ nXNC = -0x8000;
+
+ nXN = nXNC;
+ nOffset = (ii & 1) == 0 ? 4 : 0;
+
+ by = nL3 * 8 + nL2 * 4 + nL1 * 2 + nL0;
+ pDst[ii / 2] |= by << nOffset;
+
+ nIndex = nL2 * 4 + nL1 * 2 + nL0;
+ nDL = nDL * dTable[nIndex];
+
+ if (nDL <= 0x007f)
+ nDL = 0x007f;
+
+ if (nDL >= 0x6000)
+ nDL = 0x6000;
}
- info->xn = xn;
- info->dl = dl;
- info->qn = qn;
- info->dn = dn;
- info->dlh = dlh;
- info->dlq = dlq;
+ block->nXN = nXN;
+ block->nDL = nDL;
+ block->nQN = nQN;
+ block->nDN = nDN;
+ block->nDLH = nDLH;
+ block->nDLQ = nDLQ;
- return samples;
+ return nSampleNum;
}
diff --git a/src/revolution/wpad/WPAD.c b/src/revolution/wpad/WPAD.c
index ab34be44bb..27ca0b8559 100644
--- a/src/revolution/wpad/WPAD.c
+++ b/src/revolution/wpad/WPAD.c
@@ -110,7 +110,7 @@ BOOL WPADIsSpeakerEnabled(s32 chan) {
return enabled;
}
-s32 WPADControlSpeaker(s32 chan, u32 command, WPADCallback* cb) {
+s32 WPADControlSpeaker(s32 chan, u32 command, WPADCallback cb) {
u8 data[7] = {0x00, 0x00, 0xd0, 0x07, 0x40, 0x0c, 0x0e};
wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
BOOL intrStatus = OSDisableInterrupts();
@@ -287,7 +287,7 @@ s32 WPADSendStreamData(s32 chan, void* p_buf, u16 len) {
return WPAD_ESUCCESS;
}
-BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback* cb) {
+BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback cb) {
BOOL success;
struct WPADCmd cmdBlk;
cmdBlk.reportID = RPTID_ENABLE_SPEAKER;
@@ -299,7 +299,7 @@ BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCal
return success;
}
-BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback* cb) {
+BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback cb) {
BOOL success;
struct WPADCmd cmdBlk;
cmdBlk.reportID = RPTID_REQUEST_STATUS;
@@ -312,12 +312,12 @@ BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPAD
return success;
}
-BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback* cb) {
+BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback cb) {
return WPADiSendWriteData(cmdQueue, &cmd, sizeof(cmd), address, cb);
}
BOOL WPADiSendWriteData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len, u32 address,
- WPADCallback* cb) {
+ WPADCallback cb) {
BOOL success;
u8 packedLen = len & 0x1f;
struct WPADCmd cmdBlk;
@@ -353,7 +353,7 @@ BOOL WPADiSendStreamData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 l
return success;
}
-BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback* cb) {
+BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback cb) {
BOOL success;
struct WPADCmd cmdBlk;
diff --git a/src/revolution/wpad/__wpad.h b/src/revolution/wpad/__wpad.h
index 4a14700fab..1bfb13099c 100644
--- a/src/revolution/wpad/__wpad.h
+++ b/src/revolution/wpad/__wpad.h
@@ -269,41 +269,6 @@ extern "C" {
#define WPAD_RADIO_QUALITY_GOOD 0 // 80+
#define WPAD_RADIO_QUALITY_BAD 1 // 80-
-
-
-#define WPAD_DEV_CORE 0
-#define WPAD_DEV_FS 1
-#define WPAD_DEV_CLASSIC 2
-#define WPAD_DEV_BALANCE_CHECKER 3
-#define WPAD_DEV_VSM 4
-#define WPAD_DEV_MOTION_PLUS 5
-#define WPAD_DEV_MPLS_PT_FS 6
-#define WPAD_DEV_MPLS_PT_CLASSIC 7
-
-#define WPAD_DEV_TRAIN 16
-#define WPAD_DEV_GUITAR 17
-#define WPAD_DEV_DRUM 18
-#define WPAD_DEV_TAIKO 19
-#define WPAD_DEV_TURNTABLE 20
-
-// seems to be like maybe general purpose non-specific device types
-// maybe this was for testing or something? idk
-#define WPAD_DEV_BULK_1 21
-#define WPAD_DEV_BULK_2 22
-#define WPAD_DEV_BULK_3 23
-#define WPAD_DEV_BULK_4 24
-#define WPAD_DEV_BULK_5 25
-#define WPAD_DEV_BULK_6 26
-#define WPAD_DEV_BULK_7 27
-#define WPAD_DEV_BULK_8 28
-
-#define WPAD_DEV_MPLS_PT_UNKNOWN 250
-#define WPAD_DEV_251 251
-#define WPAD_DEV_252 252 // invalid device mode?
-#define WPAD_DEV_NONE 253 // sort of like WPAD_ENODEV (see __wpadAbortInitExtension in WPADHIDParser.c)
-#define WPAD_DEV_INITIALIZING 255 // see __a1_20_status_report
-
-
struct WPADCmd {
/* 0x00 */ u32 reportID;
/* 0x04 */ u8 dataBuf[RPT_MAX_SIZE];
@@ -312,7 +277,7 @@ struct WPADCmd {
/* 0x20 */ u16 readLength;
/* 0x24 */ u32 readAddress;
/* 0x28 */ WPADInfo* statusReportOut;
- /* 0x2c */ WPADCallback* cmdCB;
+ /* 0x2c */ WPADCallback cmdCB;
}; // size 0x30
struct WPADCmdQueue {
@@ -420,10 +385,10 @@ typedef struct /* possibly untagged, like kpad */ {
/* 0x850 */ WPADInfo* infoOut;
/* 0x854 */ struct WPADDevConfig devConfig;
/* 0x884 */ struct WPADExtConfig extConfig;
- /* 0x8e0 */ WPADCallback* cmdBlkCB;
- /* 0x8e4 */ WPADExtensionCallback* extensionCB;
- /* 0x8e8 */ WPADConnectCallback* connectCB;
- /* 0x8ec */ WPADSamplingCallback* samplingCB;
+ /* 0x8e0 */ WPADCallback cmdBlkCB;
+ /* 0x8e4 */ WPADExtensionCallback extensionCB;
+ /* 0x8e8 */ WPADConnectCallback connectCB;
+ /* 0x8ec */ WPADSamplingCallback samplingCB;
/* 0x8f0 */ void* samplingBuf;
/* 0x8f4 */ u32 samplingBufIndex;
/* 0x8f8 */ u32 samplingBufSize;
@@ -448,7 +413,7 @@ typedef struct /* possibly untagged, like kpad */ {
/* 0x920 */ BOOL handshakeFinished;
/* 0x924 */ int configIndex;
/* 0x928 */ OSThreadQueue threadQueue; /* purpose unknown */
- /* 0x930 */ WPADCallback* vsmCallback;
+ /* 0x930 */ WPADCallback vsmCallback;
/* 0x934 */ u8 controlMplsBusy;
/* 0x935 */ u8 mplsCBReadBuf[2];
/* 0x937 */ u8 mplsCBCounter; // idk???
@@ -491,7 +456,7 @@ typedef struct /* possibly untagged, like kpad */ {
/* 0xb7c */ u16 copyOutCount;
/* 0xb7e */ u8 sleeping;
/* 0xb7f */ u8 lastReportID;
- /* 0xb80 */ WPADCallback* getInfoCB;
+ /* 0xb80 */ WPADCallback getInfoCB;
/* 0xb84 */ u8 getInfoBusy;
/* 0xb85 */ u8 extState;
/* 0xb86 */ u8 savePower;
@@ -500,7 +465,7 @@ typedef struct /* possibly untagged, like kpad */ {
/* 0xb89 */ u8 extWasDisconnected;
/* 0xb8a */ s16 reconnectExtMs;
/* 0xb8c */ struct WPADMemBlock memBlock;
- /* 0xba0 */ WPADCallback* controlMplsCB;
+ /* 0xba0 */ WPADCallback controlMplsCB;
/* 0xba4 */ u8 parseMPBuf;
/* 0xba5 */ u8 certProbeByte;
/* 0xba6 */ u8 dpdBusy;
@@ -524,12 +489,12 @@ typedef struct /* possibly untagged, like kpad */ {
} ATTRIBUTE_ALIGN(32) wpad_cb_st; // size 0xbe0
BOOL WPADiIsAvailableCmdQueue(struct WPADCmdQueue* cmdQueue, s8 num);
-BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback* cb);
-BOOL WPADiSendWriteData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len, u32 address, WPADCallback* cb);
+BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback cb);
+BOOL WPADiSendWriteData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len, u32 address, WPADCallback cb);
BOOL WPADiSendStreamData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len);
-BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback* cb);
-BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback* cb);
-BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback* cb);
+BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback cb);
+BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback cb);
+BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback cb);
#ifdef __cplusplus
}