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, @@ -1614,6 +1622,25 @@ config.libs = [ Object(NonMatching, "revolution/dsp/dsp_task.c"), ], ), + 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", [ @@ -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 +#else #include #include #include @@ -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 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 + +#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 +#include #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 +#include +#include + +#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 +#include #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 +#include +#include + +#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 +#include + +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 + +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 +#include + +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 + +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) - } - - // clang-format on - return out; +#if defined(__MWERKS__) + asm { psq_st in, 0(ptr), 1, 2 }; +#else +# pragma unused(in) #endif + + r = *(u8 *)ptr; + + return r; } -inline void OSf32tos16(f32* f, s16* out) { - *out = __OSf32tos16(*f); -} +static inline u16 __OSf32tou16(register f32 in) { + f32 a; + register f32* ptr = &a; + u16 r; -inline u8 __OSf32tou8(__REGISTER f32 inF) { -#ifdef __MWERKS__ - register u8 out; - u32 tmp; - register u32* tmpPtr = &tmp; - // clang-format off - - asm { - psq_st inF, 0(tmpPtr), 0x1, 2 - lbz out, 0(tmpPtr) - } - - // clang-format on - return out; +#if defined(__MWERKS__) + asm { psq_st in, 0(ptr), 1, 3 }; +#else +# pragma unused(in) #endif + + r = *(u16 *)ptr; + + return r; } -inline void OSf32tou8(f32* f, u8* out) { - *out = __OSf32tou8(*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; +} + +static inline f32 __OSu16tof32(register u16 const* arg) { + register f32 ret; + +#if defined(__MWERKS__) + asm { psq_l ret, 0(arg), 1, 3 }; +#else +# pragma unused(arg) + ret = 0; +#endif + + 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); +} + +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 + +#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 +#include #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 + +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 + +#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 + +#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 + +#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, ¤tDir); + errors += VerifyFAT(card, ¤tFat); + 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 + +#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 + +#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 + +#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 + +#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 + +#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 +#include + +#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 + +#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 + +#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 + +#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 + +#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 + +#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 + +#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 + +#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 + +#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 +#include + +#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 +#include +#include + +#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 + +#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 +#include + +#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 +#include +#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(mpHBInfo->configBuf); + char* pEnd = static_cast(mpHBInfo->configBuf) + mpHBInfo->memSize; + u32 len = get_comma_length(pConfig); + + mpLayoutName = static_cast(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(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(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(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(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(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(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(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(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 +#include +#include +#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 + +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 +#include +#include +#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 + +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(&rColor)); + GXPosition3f32(x1, y1, z); + GXColor1u32(*reinterpret_cast(&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(p); + } + } + } + + void Manager::init() { + for (u32 i = 0; i < nw4hbm::ut::List_GetSize(&mIDToComponent); i++) { + const IDToComponent* p = + static_cast(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(nw4hbm::ut::List_GetNext(&mIDToComponent, NULL)); + + while (p) { + if (p->mpComponent == pComponent) { + break; + } + + p = static_cast(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(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(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(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(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(nw4hbm::ut::List_GetNth(&mIDToComponent, i)); + + p->mpComponent->setTriggerTarget(b); + } + } + + PaneManager::~PaneManager() { + PaneToComponent* pPaneToComponent = + static_cast(nw4hbm::ut::List_GetFirst(&mPaneToComponent)); + + for (; pPaneToComponent; pPaneToComponent = static_cast( + 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(&(*it))) { + pPaneComponent->setTriggerTarget(true); + } + + if (nw4hbm::ut::DynamicCast(&(*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( + nw4hbm::ut::List_GetNext(&mPaneToComponent, NULL)); + + while (pPaneToComponent) { + if (pPaneToComponent->mpPane == &(*it)) { + break; + } + + pPaneToComponent = static_cast( + 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(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(nw4hbm::ut::List_GetNth(&mPaneToComponent, i)); + + if (nw4hbm::ut::DynamicCast(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(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(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 +#include +#include +#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(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(*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(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(&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 +#include +#include +#include + +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 + +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 + +#include + +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 + +#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* 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& 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 +#include +#include +#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 +#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(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(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* 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* 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(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(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 +#include +#include "assert.h" +#include "directPrint.h" + +#include + +#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(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(framebuf); + sFrameBufferInfo.frameWidth = width; + sFrameBufferInfo.frameHeight = height; + + sFrameBufferInfo.frameRow = ROUND_UP(static_cast(width), 16); + sFrameBufferInfo.frameSize = + sFrameBufferInfo.frameRow * sFrameBufferInfo.frameHeight * 2; + } + + void DirectPrint_ChangeXfb(void* framebuf) { + sFrameBufferInfo.frameMemory = static_cast(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(y << 8); + + sFrameBufferColor.colorU = static_cast(u); + sFrameBufferColor.colorU2 = static_cast(u / 2); + sFrameBufferColor.colorU4 = static_cast(u / 4); + + sFrameBufferColor.colorV = static_cast(v); + sFrameBufferColor.colorV2 = static_cast(v / 2); + sFrameBufferColor.colorV4 = static_cast(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(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(frameBuf)); + + return reinterpret_cast(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 +#include +#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(c - '0'); + } else if ('a' <= c && c <= 'z') { + num = static_cast(c - ('a' - 10)); // ? + } else if ('A' <= c && c <= 'Z') { + num = static_cast(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(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 + +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 + +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 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 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 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 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(&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(sig); + } + + inline const char* GetStrTableStr(const void* pStrTable, int index) { + const u32* offsets = static_cast(pStrTable); + const char* stringPool = static_cast(pStrTable); + + return &stringPool[offsets[index]]; + } + + inline u8 GetVtxColorElement(const ut::Color* cols, u32 idx) { + NW4HBM_ASSERT(199, idx < ANIMTARGET_VERTEXCOLOR_MAX); + return reinterpret_cast(cols + idx / 4)[idx % 4]; + } + + inline void SetVtxColorElement(ut::Color* cols, u32 idx, u8 value) { + NW4HBM_ASSERT(212, idx < ANIMTARGET_VERTEXCOLOR_MAX); + reinterpret_cast(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 + +#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 + +#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 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 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 +#include + +#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 + +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(pAnimInfo, animTargetOffsets[i]); + + NW4HBM_ASSERT(197, pAnimTarget->target < ANIMTARGET_PANE_MAX); + NW4HBM_ASSERT(198, pAnimTarget->curveType == ANIMCURVE_HERMITE); + + const res::HermiteKey* keys = + detail::ConvertOffsToPtr(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(pAnimInfo, animTargetOffsets[i]); + + NW4HBM_ASSERT(217, pAnimTarget->target < ANIMTARGET_PANE_MAX); + NW4HBM_ASSERT(218, pAnimTarget->curveType == ANIMCURVE_STEP); + + const res::StepKey* keys = + detail::ConvertOffsToPtr(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(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(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(&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(pAnimInfo, animTargetOffsets[i]); + + NW4HBM_ASSERT(262, pAnimTarget->target < ANIMTARGET_MATCOLOR_MAX); + NW4HBM_ASSERT(263, pAnimTarget->curveType == ANIMCURVE_HERMITE); + + const res::HermiteKey* keys = + detail::ConvertOffsToPtr(pAnimTarget, pAnimTarget->keysOffset); + + f32 value = GetHermiteCurveValue(frame, keys, pAnimTarget->keyNum); + value += 0.5f; + + s16 s16Val; + OSf32tos16(&value, &s16Val); + s16Val = ut::Min(ut::Max(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(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(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(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( + pAnimTarget, pAnimTarget->keysOffset); + u16 fileIdx = GetStepCurveValue(frame, keys, pAnimTarget->keyNum); + pMaterial->SetTextureNoWrap(pAnimTarget->id, + static_cast(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(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(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(Layout::AllocMemory(sizeof(*mpFileResAry) * pRes->fileNum)); + + if (mpFileResAry) { + const u32* fileNameOffsets = + detail::ConvertOffsToPtr(mpRes, sizeof(*mpRes)); + + for (int i = 0; i < mpRes->fileNum; i++) { + mpFileResAry[i] = pResAccessor->GetResource( + 'timg', detail::GetStrTableStr(fileNameOffsets, i), 0); + } + } + } + + mAnimLinkAry = static_cast( + 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(mpRes, mpRes->animContOffsetsOffset); + + for (u16 i = 0; i < mpRes->animContNum; i++) { + const res::AnimationContent* pAnimCont = + detail::ConvertOffsToPtr(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(mpRes, mpRes->animContOffsetsOffset); + + for (u16 i = 0; i < mpRes->animContNum; i++) { + const res::AnimationContent* pAnimCont = + detail::ConvertOffsToPtr(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(mpRes, mpRes->animContOffsetsOffset)[idx]; + + const res::AnimationContent* pAnimCont = + detail::ConvertOffsToPtr(mpRes, animContOffsets); + + const u32* animInfoOffsets = + detail::ConvertOffsToPtr(pAnimCont, sizeof(*pAnimCont)); + + for (int i = 0; i < pAnimCont->num; i++) { + const res::AnimationInfo* pAnimInfo = + detail::ConvertOffsToPtr(pAnimCont, animInfoOffsets[i]); + + const u32* animTargetOffsets = + detail::ConvertOffsToPtr(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(mpRes, mpRes->animContOffsetsOffset)[idx]; + + const res::AnimationContent* pAnimCont = + detail::ConvertOffsToPtr(mpRes, animContOffsets); + + const u32* animInfoOffsets = + detail::ConvertOffsToPtr(pAnimCont, sizeof(*pAnimCont)); + + for (int i = 0; i < pAnimCont->num; i++) { + const res::AnimationInfo* pAnimInfo = + detail::ConvertOffsToPtr(pAnimCont, animInfoOffsets[i]); + + const u32* animTargetOffsets = + detail::ConvertOffsToPtr(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 + +#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 + +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(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(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(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(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(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(tdp->textureHeader->format), tdp->textureHeader->wrapS, + tdp->textureHeader->wrapT, static_cast(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(tdp->textureHeader->format), tdp->textureHeader->wrapS, + tdp->textureHeader->wrapT, static_cast(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 + +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 +#include + + +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(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 + +#define CONVERT_OFFSET_TO_PTR(type_, ptr_, offset_) \ + reinterpret_cast(reinterpret_cast(ptr_) + offset_) + +namespace { + // pretend this is nw4hbm::lyt + using namespace nw4hbm; + using namespace nw4hbm::lyt; + + void SetTagProcessorImpl(Pane* pPane, ut::TagProcessorBase* pTagProcesssor); + + // wait until these guys hear about c++11 + template + 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 + T* CreateObject(Param1 p1) { + void* pMem = Layout::AllocMemory(sizeof(T)); + + if (pMem) { + return new (pMem) T(p1); + } else { + return NULL; + } + } + + template + 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* pTagProcessor) { + if (TextBox* pTextBox = ut::DynamicCast(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(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(dataPtr); + + switch (detail::GetSignatureInt(pDataBlockHead->kind)) { + case 'lyt1': { + res::Layout* pResLyt = static_cast(dataPtr); + + mOriginType = pResLyt->originType != 0; // ? + mLayoutSize = pResLyt->layoutSize; + } + + break; + + case 'txl1': + resBlockSet.pTextureList = static_cast(dataPtr); + break; + + case 'fnl1': + resBlockSet.pFontList = static_cast(dataPtr); + break; + + case 'mat1': + resBlockSet.pMaterialList = static_cast(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(); + } else if (mpGroupContainer && groupNestLevel == 1) { + if (Group* pGroup = CreateObject( + reinterpret_cast(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(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(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()) { + pInfoBlock = + reinterpret_cast(pDataBlockHead); + + pAnimTrans->SetResource(pInfoBlock, pResAcsr); + ret = pAnimTrans; + } + } + + if (ret) { + mAnimTransList.PushBack(ret); + } + } + + pDataBlockHead = detail::ConvertOffsToPtr( + 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* 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(dataPtr); + + return CreateObject(pResPane); + } + + case res::OBJECT_SIGNATURE_PICTURE: { + const res::Picture* pResPic = static_cast(dataPtr); + + return CreateObject(pResPic, resBlockSet); + } + + case res::OBJECT_SIGNATURE_TEXT_BOX: { + // block? + const res::TextBox* pBlock = static_cast(dataPtr); + + return CreateObject(pBlock, resBlockSet); + } + + case res::OBJECT_SIGNATURE_WINDOW: { + // block? + const res::Window* pBlock = static_cast(dataPtr); + + return CreateObject(pBlock, resBlockSet); + } + + case res::OBJECT_SIGNATURE_BOUNDING: { + const res::Bounding* pResBounding = static_cast(dataPtr); + + return CreateObject(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 + +#include + +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(ut::Max(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(pRes, resOffs); + resOffs += pRes->resNum.GetTexMapNum() * sizeof(res::TexMap); + + const TexSRT* const resTexSRTs = detail::ConvertOffsToPtr(pRes, resOffs); + resOffs += pRes->resNum.GetTexSRTNum() * sizeof(TexSRT); + + const TexCoordGen* const resTexCoordGens = + detail::ConvertOffsToPtr(pRes, resOffs); + resOffs += pRes->resNum.GetTexCoordGenNum() * sizeof(TexCoordGen); + + u8 texMapNum = ut::Min(pRes->resNum.GetTexMapNum(), GX_MAX_TEXMAP); + u8 texSRTNum = ut::Min(pRes->resNum.GetTexSRTNum(), MAX_TEX_SRT); + u8 texCoordGenNum = ut::Min(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(pRes->resNum.GetIndTexSRTNum(), MAX_IND_SRT); + u8 indStageNum = ut::Min(pRes->resNum.GetIndTexStageNum(), GX_MAX_INDTEXSTAGE); + u8 tevStageNum = ut::Min(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( + 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( + textures, textures[pResTexMap[i].texIdx].nameStrOffset); + + void* pTplRes = resBlockSet.pResAccessor->GetResource(RESOURCE_TYPE_TEXTURE, + fileName, NULL); + + SetTexture(i, static_cast(pTplRes)); + GXInitTexObjWrapMode(&texMaps[i], + static_cast(pResTexMap[i].wrapS), + static_cast(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(pRes, resOffs); + *GetChanCtrlAry() = *pResChanCtrl; + resOffs += sizeof(ChanCtrl); + } + + if (allocMatCol) { + NW4HBM_ASSERT(512, IsMatColorCap()); + const ut::Color* pResMatCol = + detail::ConvertOffsToPtr(pRes, resOffs); + *GetMatColAry() = *pResMatCol; + resOffs += sizeof(ut::Color); + } + + if (allocTevSwap) { + NW4HBM_ASSERT(520, IsTevSwapCap()); + const TevSwapMode* pResTevSwap = + detail::ConvertOffsToPtr(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(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(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(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(pRes, resOffs); + *GetAlphaComparePtr() = *pResAlphaCompare; + resOffs += sizeof(AlphaCompare); + } + + if (allocBlendMode) { + NW4HBM_ASSERT(576, IsBlendModeCap()); + const BlendMode* pResBlendMode = + detail::ConvertOffsToPtr(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(mpGXMem, 0); + } + + GXTexObj* Material::GetTexMapAry() { + return detail::ConvertOffsToPtr(mpGXMem, 0); + } + + const TexSRT* Material::GetTexSRTAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetTexSRTAry(mGXMemCap)); + } + + TexSRT* Material::GetTexSRTAry() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetTexSRTAry(mGXMemCap)); + } + + const TexCoordGen* Material::GetTexCoordGenAry() const { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetTexCoordGenAry(mGXMemCap)); + } + + TexCoordGen* Material::GetTexCoordGenAry() { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetTexCoordGenAry(mGXMemCap)); + } + + const ChanCtrl* Material::GetChanCtrlAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetChanCtrlAry(mGXMemCap)); + } + + ChanCtrl* Material::GetChanCtrlAry() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetChanCtrlAry(mGXMemCap)); + } + + const ut::Color* Material::GetMatColAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetMatColAry(mGXMemCap)); + } + + ut::Color* Material::GetMatColAry() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetMatColAry(mGXMemCap)); + } + + const TevSwapMode* Material::GetTevSwapAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetTevSwapAry(mGXMemCap)); + } + + TevSwapMode* Material::GetTevSwapAry() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetTevSwapAry(mGXMemCap)); + } + + const AlphaCompare* Material::GetAlphaComparePtr() const { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetGetAlphaCompare(mGXMemCap)); + } + + AlphaCompare* Material::GetAlphaComparePtr() { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetGetAlphaCompare(mGXMemCap)); + } + + const BlendMode* Material::GetBlendModePtr() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetBlendMode(mGXMemCap)); + } + + BlendMode* Material::GetBlendModePtr() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetBlendMode(mGXMemCap)); + } + + const IndirectStage* Material::GetIndirectStageAry() const { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetIndirectStageAry(mGXMemCap)); + } + + IndirectStage* Material::GetIndirectStageAry() { + return detail::ConvertOffsToPtr(mpGXMem, + CalcOffsetIndirectStageAry(mGXMemCap)); + } + + const TexSRT* Material::GetIndTexSRTAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetIndTexSRTAry(mGXMemCap)); + } + + TexSRT* Material::GetIndTexSRTAry() { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetIndTexSRTAry(mGXMemCap)); + } + + const TevStage* Material::GetTevStageAry() const { + return detail::ConvertOffsToPtr(mpGXMem, CalcOffsetTevStageAry(mGXMemCap)); + } + + TevStage* Material::GetTevStageAry() { + return detail::ConvertOffsToPtr(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(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(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(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(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(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(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(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(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(tevStageID); + + GXSetTevOrder(tevStage, static_cast(i), + static_cast(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(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(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(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(i + 1), mtx); + } + } + } + + GXSetNumIndStages(mGXMemNum.indStage); + + if (mGXMemNum.indStage) { + IndirectStage* indirectStages = GetIndirectStageAry(); + + for (int i = 0; i < mGXMemNum.indStage; i++) { + GXIndTexStageID indStage = static_cast(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 + +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 DummyLinkList; + DECOMP_FORCE_CLASS_METHOD(DummyLinkList, GetNodeFromPointer(NULL)); + typedef nw4hbm::ut::LinkList 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 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); + + mtDrawInfo.SetGlobalAlpha(crGlobalAlpha * mAlpha * invAlpha); + mtDrawInfo.SetInfluencedAlpha(true); + } + + CalculateMtxChild(drawInfo); + + // restore changed values if applicable + if (bModDrawInfo) { + DrawInfo& mtDrawInfo = const_cast(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(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(Layout::AllocMemory(sizeof(*pMemMaterial)))) + { + NW4HBM_ASSERT_CHECK_NULL(149, resBlockSet.pMaterialList); + const u32* matOffsTbl = + detail::ConvertOffsToPtr(resBlockSet.pMaterialList, 0xc); + const res::Material* pResMaterial = detail::ConvertOffsToPtr( + 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 + int CalcLineRectImpl(ut::Rect* pRect, ut::TextWriterBase* 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 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(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(reader.GetCurrentPos()) - str <= length; + prStrPos = static_cast(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(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(reader.GetCurrentPos()) - str; + } + } + + template + void CalcStringRectImpl(ut::Rect* pRect, ut::TextWriterBase* 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 + int CalcLineStrNum(f32* pWidth, ut::TextWriterBase* 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 myCopy = *pTextWriter; + myCopy.SetCursor(0.0f, 0.0f); + + int ret = CalcLineRectImpl(&rect, &myCopy, str, length, maxWidth, pbOver); + + *pWidth = rect.GetWidth(); + return ret; + } + + template + void CalcStringRect(ut::Rect* pRect, ut::TextWriterBase* 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 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(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( + resBlockSet.pFontList, sizeof(*resBlockSet.pFontList)); + + const char* fontName = + detail::ConvertOffsToPtr(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( + resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList)); + + const res::Material* pResMaterial = detail::ConvertOffsToPtr( + 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(mTextColors + idx / 8)[idx % 4]; + } + + void TextBox::SetVtxColorElement(u32 idx, u8 value) { + NW4HBM_ASSERT(494, idx < ANIMTARGET_VERTEXCOLOR_MAX); + reinterpret_cast(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(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(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(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 + +#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 + const T* ConvertOffsToPtr(const void* baseAddress, unsigned offset) { + return reinterpret_cast(reinterpret_cast(baseAddress) + offset); + } + + template + T* ConvertOffsToPtr(void* baseAddress, unsigned offset) { + return reinterpret_cast(reinterpret_cast(baseAddress) + offset); + } + + template + inline void SetBit(T* pBits, int pos, bool val) { + const T mask = (T)(~(1 << pos)); + *pBits &= mask; + *pBits |= (val ? 1 : 0) << pos; + } + + template + inline bool TestBit(T bits, int pos) { + const T mask = (T)(1 << pos); + return 0 != (bits & mask); + } + + template + T GetBits(T bits, int pos, int len) { + T mask = static_cast(~(-1 << len)); + return static_cast((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(texGenType); } + GXTexGenSrc GetTexGenSrc() const { return static_cast(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(texCoordGen); } + + GXTexMapID GetTexMap() const { return static_cast(texMap); } + + GXIndTexScale GetScaleS() const { return static_cast(scaleS); } + GXIndTexScale GetScaleT() const { return static_cast(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((swap) & 0x03); } + GXTevColorChan GetG() const { return static_cast((swap >> 2) & 0x03); } + GXTevColorChan GetB() const { return static_cast((swap >> 4) & 0x03); } + GXTevColorChan GetA() const { return static_cast((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(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(texCoordGen); } + GXChannelID GetColorChan() const { return static_cast(colChan); } + + GXTexMapID GetTexMap() const { + return static_cast((swapSel & 1) << 8 | texMap); + } + + GXTevSwapSel GetTexSwapSel() const { + return static_cast((swapSel >> 3) & 0x03); + } + GXTevSwapSel GetRasSwapSel() const { + return static_cast((swapSel >> 1) & 0x03); + } + + GXTevColorArg GetColorInA() const { return static_cast(colIn.GetA()); } + GXTevColorArg GetColorInB() const { return static_cast(colIn.GetB()); } + GXTevColorArg GetColorInC() const { return static_cast(colIn.GetC()); } + GXTevColorArg GetColorInD() const { return static_cast(colIn.GetD()); } + + GXTevOp GetColorOp() const { return static_cast(colIn.GetOp()); } + GXTevBias GetColorBias() const { return static_cast(colIn.GetBias()); } + GXTevScale GetColorScale() const { return static_cast(colIn.GetScale()); } + + bool IsColorClamp() const { return colIn.IsClamp(); } + + GXTevRegID GetColorOutReg() const { return static_cast(colIn.GetOutReg()); } + + GXTevKColorSel GetKColorSel() const { + return static_cast(colIn.GetKSel()); + } + + GXTevAlphaArg GetAlphaInA() const { return static_cast(alpIn.GetA()); } + GXTevAlphaArg GetAlphaInB() const { return static_cast(alpIn.GetB()); } + GXTevAlphaArg GetAlphaInC() const { return static_cast(alpIn.GetC()); } + GXTevAlphaArg GetAlphaInD() const { return static_cast(alpIn.GetD()); } + + GXTevOp GetAlphaOp() const { return static_cast(alpIn.GetOp()); } + GXTevBias GetAlphaBias() const { return static_cast(alpIn.GetBias()); } + GXTevScale GetAlphaScale() const { return static_cast(alpIn.GetScale()); } + + bool IsAlphaClamp() const { return alpIn.IsClamp(); } + + GXTevRegID GetAlphaOutReg() const { return static_cast(alpIn.GetOutReg()); } + + GXTevKAlphaSel GetKAlphaSel() const { + return static_cast(alpIn.GetKSel()); + } + + GXIndTexStageID GetIndStage() const { return static_cast(indStage); } + GXIndTexMtxID GetIndMtxSel() const { + return static_cast((indBiMt >> 3) & 0x0F); + } + GXIndTexBiasSel GetIndBiasSel() const { + return static_cast(indBiMt & 0x07); + } + + GXIndTexWrap GetIndWrapS() const { return static_cast(indWrap & 0x07); } + GXIndTexWrap GetIndWrapT() const { + return static_cast((indWrap >> 3) & 0x07); + } + + GXIndTexAlphaSel GetIndAlphaSel() const { + return static_cast((indFoAdUtAl >> 4) & 0x03); + } + + bool IsIndUtcLod() const { return static_cast((indFoAdUtAl >> 3) & 0x01); } + bool IsIndAddPrev() const { return static_cast((indFoAdUtAl >> 2) & 0x01); } + + GXIndTexFormat GetIndFormat() const { + return static_cast(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(matSrcCol); } + GXColorSrc GetAlphaSrc() const { return static_cast(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(comp & 0x0F); } + GXCompare GetComp1() const { return static_cast((comp >> 4) & 0x0F); } + + GXAlphaOp GetOp() const { return static_cast(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(type); } + GXBlendFactor GetSrcFactor() const { return static_cast(srcFactor); } + GXBlendFactor GetDstFactor() const { return static_cast(dstFactor); } + GXLogicOp GetOp() const { return static_cast(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( + resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList)); + + const res::WindowContent* pResContent = + detail::ConvertOffsToPtr(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(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( + resBlockSet.pMaterialList, matOffsTbl[pResContent->materialIdx]); + + mpMaterial = new (pMemMaterial) Material(pResMaterial, resBlockSet); + } + + mFrameNum = 0; + mFrames = NULL; + + if (pBlock->frameNum) { + if ((mFrames = static_cast( + Layout::AllocMemory(sizeof(*mFrames) * pBlock->frameNum)))) + { + mFrameNum = pBlock->frameNum; + const u32* frameOffsetTable = + detail::ConvertOffsToPtr(pBlock, pBlock->frameOffsetTableOffset); + + for (int i = 0; i < mFrameNum; i++) { + const res::WindowFrame* pResWindowFrame = + detail::ConvertOffsToPtr(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( + 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 +#include +#include + +#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(mGXMemCap.tevSwap); } + bool IsBlendModeCap() const { return static_cast(mGXMemCap.blendMode); } + bool IsAlphaCompareCap() const { return static_cast(mGXMemCap.alpComp); } + + bool IsMatColorCap() const { return static_cast(mGXMemCap.matCol); } + bool IsChanCtrlCap() const { return static_cast(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(&GetTexSRTAry()[texSRTIdx]); + srtAry[eleIdx] = value; + } + + void SetIndTexSRTElement(u32 texSRTIdx, u32 eleIdx, f32 value) { + NW4HBM_ASSERT(309, texSRTIdx < mGXMemNum.indSRT); + f32* srtAry = reinterpret_cast(&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 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(&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 +#include + +#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(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 +#include + +#include +#include + + +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(&x); + } + + inline f32 U32AsF32(u32 x) { + return *reinterpret_cast(&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 + +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 + +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 + +#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 + +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 +#include + +#include + +#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 +#include + +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(this); } + operator f32 const*() const { return reinterpret_cast(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(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 +#include + +#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 + 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 mHomeButtonMenuVolume; + /* 0xA244 */ MoveValue mMasterVolume; + /* 0xA254 */ MoveValue mVolumeForReset; + /* 0xA264 */ AIDCallback mOldAidCallback; + /* 0xA268 */ vs32 mResetReadyCounter; + /* 0xA26C */ MoveValue mAuxFadeVolume[AUX_BUS_NUM]; + /* 0xA29C */ MoveValue 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 +#include +#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(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(&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 + 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 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 + +#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 + +#include "Util.h" +#include "WaveFile.h" + +#include "../ut/binaryFileFormat.h" + +#include + +namespace nw4hbm { + namespace snd { + namespace detail { + + inline u8 ReadByte(const void* address) { + return *static_cast(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 lfoTableRef; + /* 0x1C */ Util::DataRef graphEnvTablevRef; + /* 0x24 */ Util::DataRef randomizerTableRef; + /* 0x2C */ u32 reserved; + } InstParam; + + typedef struct RangeTable { + /* 0x00 */ u8 tableSize; + /* 0x01 */ u8 key[]; + } RangeTable; + + typedef struct IndexTable IndexTable; + + typedef Util::DataRef DataRegion; + typedef Util::DataRef 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 instTable; + } DataBlock; + + typedef struct WaveBlock { + /* 0x00 */ ut::BinaryBlockHeader blockHeader; + /* 0x08 */ Util::Table 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 +#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 +#include + +#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 mFadeVolume; + /* 0x54 */ MoveValue 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 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 + BasicSoundPrioList; + typedef ut::LinkList + BasicSoundPlayerPlayList; + typedef ut::LinkList + BasicSoundPlayerPrioList; + typedef ut::LinkList + 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 // USHRT_MAX + +#include + +#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 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 { + 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 + 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 + +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 + +#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 + 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 + +#include "../ut/LinkList.h" + +#include + +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 BlockList; + + typedef struct Section { + /* 0x00 */ ut::LinkListNode mLink; + /* 0x08 */ BlockList mBlockList; + + ~Section(); + void AppendBlock(Block* block) { mBlockList.PushBack(block); } + } Section; + typedef ut::LinkList 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 + +#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 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 + class InstanceManager { + public: + typedef typename ut::LinkList::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 mFreeList; + /* 0x0C */ ut::LinkList 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 +#include + +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 + 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 + 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(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 + +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 +#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 +#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 + +#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 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 + +namespace nw4hbm { + namespace snd { + namespace detail { + + template + 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 +#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 + +#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 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 +#include +#include + +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 +#include +#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(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 + +#include "Util.h" + +#include "../ut/binaryFileFormat.h" + +#include + +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 + +#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 + 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 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 + +#include "debug.h" + +#include "../ut/LinkList.h" + +namespace nw4hbm { + namespace snd { + + class SeqSoundHandle; + + namespace detail { + class NoteOnCallback; + class SeqTrackAllocator; + class SeqPlayer; + class BasicSound; + template + 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* 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* 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 + +#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 +#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 + +#include "SoundArchive.h" +#include "Util.h" + +#include "../ut/binaryFileFormat.h" +#include "../ut/inlines.h" + +#include + +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 nodeTable; + } StringTree; + + typedef struct StringTable { + /* 0x00 */ Util::Table 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 + 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 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 SoundCommonInfoRef; + typedef Util::Table SoundCommonTable; + + typedef struct BankInfo { + /* 0x00 */ u32 stringId; + /* 0x04 */ u32 fileId; + } BankInfo; + typedef Util::DataRef BankInfoRef; + typedef Util::Table BankTable; + + typedef struct PlayerInfo { + /* 0x00 */ u32 stringId; + /* 0x04 */ u8 playableSoundCount; + /* 0x08 */ u32 heapSize; + } PlayerInfo; + typedef Util::DataRef PlayerInfoRef; + typedef Util::Table PlayerTable; + + typedef Util::DataRef SoundArchiveRef; + typedef Util::Table FilePosTable; + + typedef struct FileInfo { + /* 0x00 */ u32 fileSize; + /* 0x04 */ u32 waveDataSize; + /* 0x08 */ s32 entryNum; + /* 0x0C */ Util::DataRef extFilePathRef; + /* 0x14 */ Util::DataRef filePosTableRef; + } FileInfo; + typedef Util::DataRef FileInfoRef; + typedef Util::Table FileTable; + + typedef struct GroupItemInfo { + /* 0x0 */ u32 fileId; + /* 0x4 */ u32 offset; + /* 0x8 */ u32 size; + /* 0xC */ u32 waveDataOffset; + /* 0x10 */ u32 waveDataSize; + } GroupItemInfo; + typedef Util::DataRef GroupItemInfoRef; + typedef Util::Table GroupItemTable; + + typedef struct GroupInfo { + /* 0x00 */ u32 stringId; + /* 0x04 */ s32 entryNum; + /* 0x08 */ Util::DataRef extFilePathRef; + /* 0x10 */ u32 offset; + /* 0x14 */ u32 size; + /* 0x18 */ u32 waveDataOffset; + /* 0x1C */ u32 waveDataSize; + /* 0x20 */ Util::DataRef itemTableRef; + } GroupInfo; + typedef Util::DataRef GroupInfoRef; + typedef Util::Table 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 soundTableRef; + /* 0x08 */ Util::DataRef bankTableRef; + /* 0x10 */ Util::DataRef playerTableRef; + /* 0x18 */ Util::DataRef fileTableRef; + /* 0x20 */ Util::DataRef groupTableRef; + /* 0x28 */ Util::DataRef 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 +#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(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(id), allocatable, loadBlockSize); + } + bool LoadGroup(u32 id, SoundMemoryAllocatable* allocatable, + unsigned int loadBlockSize) { + return LoadGroup(static_cast(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 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 mSeqSoundInstanceManager; + /* 0x88 */ detail::SoundInstanceManager mStrmSoundInstanceManager; + /* 0x98 */ detail::SoundInstanceManager 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 + +#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 + +#include "FrameHeap.h" +#include "SoundMemoryAllocatable.h" + +#include "../ut/Lock.h" + +#include + +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 lock(mMutex); + return mFrameHeap.GetCurrentLevel(); + } + + u32 GetFreeSize() const { + ut::detail::AutoLock 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 + +#include + +#include "InstancePool.h" + +#include "../ut/Lock.h" +#include "../ut/inlines.h" + +#include + +#include "../db/assert.h" + +namespace nw4hbm { + namespace snd { + namespace detail { + + template + 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(&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 TPrioList; + + private: + /* 0x00 */ MemoryPool 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 + +#include "BasicSound.h" +#include "PlayerHeap.h" +#include "snd_global.h" + +namespace nw4hbm { + namespace snd { + namespace detail { + class ExternalSoundPlayer; + class SeqSound; + template + 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* manager); + + detail::StrmSound* + detail_AllocStrmSound(int priority, int startPriority, + detail::BasicSound::AmbientArgInfo* ambientArgInfo, + detail::ExternalSoundPlayer* extPlayer, u32 id, + detail::SoundInstanceManager* manager); + + detail::WaveSound* + detail_AllocWaveSound(int priority, int startPriority, + detail::BasicSound::AmbientArgInfo* ambientArgInfo, + detail::ExternalSoundPlayer* extPlayer, u32 id, + detail::SoundInstanceManager* 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 + +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 + +#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 + +#include "AxManager.h" + +#include "../ut/LinkList.h" +#include "../ut/inlines.h" + +#include +#include +#include + +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 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 +#include + +#include + +#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 refTrackHeader[]; + } TrackTable; + + typedef struct ChannelInfo { + /* 0x00 */ Util::DataRef refAdpcmInfo; + } ChannelInfo; + + typedef struct ChannelTable { + /* 0x00 */ u8 channelCount; + /* 0x04 */ Util::DataRef 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 refDataHeader; + /* 0x10 */ Util::DataRef refTrackTable; + /* 0x18 */ Util::DataRef 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 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 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 + +#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 + class SoundInstanceManager; + } + + namespace detail { + + class StrmSound : public BasicSound { + friend class StrmSoundHandle; + + public: + explicit StrmSound(SoundInstanceManager* 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* 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 +#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 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 + +#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 +#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 + +#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(10 * VOLUME_MIN_DB); // -90.4db + static const int VOLUME_MAX = static_cast(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 + struct DataRef { + /* 0x00 */ u8 refType; + /* 0x01 */ u8 dataType; + /* 0x02 */ u16 reserved; + /* 0x04 */ u32 value; + }; + + template + static inline const T0* GetDataRefAddress0(const DataRef& ref, + const void* base) { + NW4HBM_ASSERT(111, ref.dataType == 0); + return static_cast( + GetDataRefAddressImpl(static_cast(ref.refType), ref.value, base)); + } + + template + static inline const T1* GetDataRefAddress1(const DataRef& ref, + const void* base) { + NW4HBM_ASSERT(112, ref.dataType == 1); + return static_cast( + GetDataRefAddressImpl(static_cast(ref.refType), ref.value, base)); + } + + template + static inline const T2* GetDataRefAddress2(const DataRef& ref, + const void* base) { + NW4HBM_ASSERT(113, ref.dataType == 2); + return static_cast( + GetDataRefAddressImpl(static_cast(ref.refType), ref.value, base)); + } + + template + static inline const T3* GetDataRefAddress3(const DataRef& ref, + const void* base) { + NW4HBM_ASSERT(114, ref.dataType == 3); + return static_cast( + GetDataRefAddressImpl(static_cast(ref.refType), ref.value, base)); + } + + template + 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 + +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 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 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 + +#include "BasicSound.h" +#include "WsdPlayer.h" +#include "debug.h" + +#include "../ut/LinkList.h" + +namespace nw4hbm { + namespace snd { + class WaveSoundHandle; + namespace detail { + template + class SoundInstanceManager; + + class WaveSound : public BasicSound { + friend class WaveSoundHandle; + + public: + explicit WaveSound(SoundInstanceManager* 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* 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 + +#include "Util.h" +#include "WaveFile.h" +#include "snd_types.h" + +#include "../ut/binaryFileFormat.h" + +#include + +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 graphEnvTablevRef; + /* 0x14 */ Util::DataRef 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 lfoTableRef; + /* 0x1C */ Util::DataRef graphEnvTablevRef; + /* 0x24 */ Util::DataRef randomizerTableRef; + /* 0x2C */ u32 reserved; + } NoteInfo; + + typedef Util::DataRef TrackInfoRef; + typedef Util::Table TrackTable; + + typedef Util::DataRef NoteInfoRef; + typedef Util::Table NoteTable; + + typedef struct Wsd { + /* 0x00 */ Util::DataRef refWsdInfo; + /* 0x08 */ Util::DataRef refTrackTable; + /* 0x10 */ Util::DataRef 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 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 + +#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 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 + +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 + +#include "../ut/LinkList.h" + +namespace nw4hbm { + namespace snd { + class LinkedInstance { + public: + /* 0x00 */ ut::LinkListNode mInstanceLink; + }; + template + class InstanceManager { + public: + typedef typename ut::LinkList::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 mFreeList; + /* 0x0C */ ut::LinkList 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(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 + T GetChar() const { + const T* const charStrm = static_cast(mCharStrm); + return charStrm[0]; + } + + template + T GetChar(int offset) const { + const T* const charStrm = static_cast(mCharStrm); + return charStrm[offset]; + } + + template + void StepStrm(int step) { + const T*& charStrm = *reinterpret_cast(&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 + +#include "Color.h" +#include "fontResources.h" + +#include "../math/types.h" + +#include + +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 +#include + + +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(&color); + } + + Color operator|(u32 color) const { return Color(ToU32() | color); } + Color operator&(u32 color) const { return Color(ToU32() & color); } + + u32& ToU32ref() { return *reinterpret_cast(this); } + const u32& ToU32ref() const { return *reinterpret_cast(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 + +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 + +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 +#include + +#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 + +#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 + +#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 + 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 + 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 RevIterator; + typedef detail::ReverseIterator ConstRevIterator; + + public: + LinkList() {} + + Iterator GetBeginIter() { return Iterator(LinkListImpl::GetBeginIter()); } + ConstIterator GetBeginIter() const { + return ConstIterator(const_cast(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(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(reinterpret_cast(p) + Ofs); + } + + static T* GetPointerFromNode(LinkListNode* p) { + NW4HBM_ASSERT_CHECK_NULL(573, p); + return reinterpret_cast(reinterpret_cast(p) - Ofs); + } + + static const T* GetPointerFromNode(const LinkListNode* p) { + NW4HBM_ASSERT_CHECK_NULL(578, p); + return reinterpret_cast(reinterpret_cast(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 + +namespace nw4hbm { + namespace ut { + namespace detail { + + inline void Lock(OSMutex& rMutex) { + OSLockMutex(&rMutex); + } + inline void Unlock(OSMutex& rMutex) { + OSUnlockMutex(&rMutex); + } + + template + 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 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 +#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 + +#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 + +#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 + +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 + inline const RuntimeTypeInfo* GetTypeInfoFromPtr_(T* pPtr) { + return &pPtr->typeInfo; + } + } // namespace detail + + template + inline TDerived DynamicCast(TBase* pPtr) { + const detail::RuntimeTypeInfo* pDerivedTypeInfo = + detail::GetTypeInfoFromPtr_(static_cast(NULL)); + if (pPtr->GetRuntimeTypeInfo()->IsDerivedFrom(pDerivedTypeInfo)) { + return static_cast(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 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 + +#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 + class TextWriterBase; + + template + struct PrintContext { + /* 0x00 */ TextWriterBase* writer; + /* 0x04 */ const T* str; + /* 0x08 */ f32 xOrigin; + /* 0x0C */ f32 yOrigin; + /* 0x10 */ u32 flags; + }; + + template + class TagProcessorBase { + public: + TagProcessorBase(); + + /* 0x08 */ virtual ~TagProcessorBase(); + /* 0x0C */ virtual Operation Process(u16 code, PrintContext* context); + /* 0x10 */ virtual Operation CalcRect(Rect* pRect, u16 code, PrintContext* context); + + void ProcessLinefeed(PrintContext* context); + void ProcessTab(PrintContext* 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 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 + +#include "CharWriter.h" +#include "Rect.h" + +#include +#include + +#include "TagProcessorBase.h" + +namespace nw4hbm { + namespace ut { + + struct Rect; + template + class TagProcessorBase; + + template + 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* tagProcessor); + void ResetTagProcessor(); + TagProcessorBase& 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* mTagProcessor; + + private: + static T* mFormatBuffer; + static u32 mFormatBufferSize; + static TagProcessorBase 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 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 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 + +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 + +#include + +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 + +namespace nw4hbm { + namespace ut { + + class NonCopyable { + protected: + NonCopyable() {} + ~NonCopyable() {} + + private: + NonCopyable(const NonCopyable&); + const NonCopyable& operator=(const NonCopyable&); + }; + + template + inline T Min(T a, T b) { + return (a > b) ? b : a; + } + template + inline T Max(T a, T b) { + return (a < b) ? b : a; + } + template + inline T Clamp(T x, T low, T high) { + return (x > high) ? high : ((x < low) ? low : x); + } + + template + inline T Abs(T x) { + // Static cast needed to break abs optimization + return x < 0 ? static_cast(-x) : static_cast(x); + } + + template + 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(pPtr); + } + + template + inline const void* AddOffsetToPtr(const void* base, T offset) { + return reinterpret_cast(GetIntPtr(base) + offset); + } + template + inline void* AddOffsetToPtr(void* base, T offset) { + return reinterpret_cast(GetIntPtr(base) + offset); + } + + inline s32 GetOffsetFromPtr(const void* start, const void* end) { + return static_cast(GetIntPtr(end) - GetIntPtr(start)); + } + + inline int ComparePtr(const void* pPtr1, const void* pPtr2) { + return static_cast(GetIntPtr(pPtr1) - GetIntPtr(pPtr2)); + } + + template + inline T RoundUp(T t, u32 alignment) { + return (alignment + t - 1) & ~(alignment - 1); + } + + template + inline void* RoundUp(T* pPtr, u32 alignment) { + u32 value = reinterpret_cast(pPtr); + u32 rounded = (alignment + value - 1) & ~(alignment - 1); + return reinterpret_cast(rounded); + } + + template + inline T RoundDown(T t, u32 alignment) { + return t & ~(alignment - 1); + } + + template + inline void* RoundDown(T* pPtr, u32 alignment) { + u32 value = reinterpret_cast(pPtr); + u32 rounded = value & ~(alignment - 1); + return reinterpret_cast(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 +#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() & 0xC0) != 0x80); + u16 code; + + if ((GetChar(0) & 0x80) == 0x00) { + // 1-byte UTF-8 sequence + code = GetChar(0); + StepStrm(1); + } else if ((GetChar(0) & 0xE0) == 0xC0) { + // 2-byte UTF-8 sequence + code = (GetChar(0) & 0x1f) << 6 | (GetChar(1) & 0x3f); + StepStrm(2); + } else { + // 3-byte UTF-8 sequence + + NW4HBM_ASSERT(100, (GetChar() & 0xF0) == 0xE0); + /* technical ERRATUM: the mask of GetChar(0) should be 0x0f */ + code = (GetChar(0) & 0x1f) << 12 | (GetChar(1) & 0x3f) << 6 | + (GetChar(2) & 0x3f); + StepStrm(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(0); + StepStrm(1); + + return code; + } + + u16 CharStrmReader::ReadNextCharCP1252() { + NW4HBM_ASSERT_VALID_PTR(155, this); + NW4HBM_ASSERT_VALID_PTR(156, mCharStrm); + u16 code = GetChar(0); + StepStrm(1); + + return code; + } + + u16 CharStrmReader::ReadNextCharSJIS() { + NW4HBM_ASSERT_VALID_PTR(180, this); + NW4HBM_ASSERT_VALID_PTR(181, mCharStrm); + u16 code; + + if (IsSJISLeadByte(GetChar(0))) { + code = GetChar(0) << 8 | GetChar(1); + StepStrm(2); + } else { + code = GetChar(0); + StepStrm(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 + +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(glyph.cellX << 15) / glyph.texWidth; + u16 texTop = static_cast(glyph.cellY << 15) / glyph.texHeight; + u16 texRight = + static_cast((glyph.cellX + glyph.widths.glyphWidth) << 15) / glyph.texWidth; + u16 texBottom = static_cast((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(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(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 +#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(reinterpret_cast(ptr_) + offset_) + +namespace nw4hbm { + namespace ut { + + namespace { + template + void ResolveOffset(T*& ptr, void* base) { + *reinterpret_cast(&ptr) = + reinterpret_cast(base) + reinterpret_cast(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(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(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(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(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(tg.cellHeight); + glyph->texFormat = static_cast(tg.sheetFormat); + glyph->texWidth = static_cast(tg.sheetWidth); + glyph->texHeight = static_cast(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 + TagProcessorBase::TagProcessorBase() {} + + template + TagProcessorBase::~TagProcessorBase() {} + + template + Operation TagProcessorBase::Process(u16 code, PrintContext* 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 + Operation TagProcessorBase::CalcRect(Rect* pRect, u16 code, PrintContext* context) { + NW4HBM_ASSERT_VALID_PTR(132, pRect); + NW4HBM_ASSERT(133, code < ' '); + NW4HBM_ASSERT_VALID_PTR(134, context); + + switch (code) { + case '\n': { + TextWriterBase& 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& 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 + void TagProcessorBase::ProcessLinefeed(PrintContext* context) { + NW4HBM_ASSERT_VALID_PTR(195, context); + TextWriterBase& writer = *context->writer; + + f32 x = context->xOrigin; + f32 y = writer.GetCursorY() + writer.GetLineHeight(); + + writer.SetCursor(x, y); + } + + template + void TagProcessorBase::ProcessTab(PrintContext* context) { + NW4HBM_ASSERT_VALID_PTR(211, context); + TextWriterBase& 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(tabWidth) * aCharWidth; + int numTab = static_cast(dx / tabPixel) + 1; + f32 x = tabPixel * static_cast(numTab) + context->xOrigin; + + writer.SetCursorX(x); + } + } + + template class TagProcessorBase; + template class TagProcessorBase; + + } // 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 + u32 TextWriterBase::mFormatBufferSize = 0x100; + template + charT* TextWriterBase::mFormatBuffer; + template + TagProcessorBase TextWriterBase::mDefaultTagProcessor; + + template + TextWriterBase::TextWriterBase() + : mCharSpace(0.0f), mLineSpace(0.0f), mTabWidth(4), mDrawFlag(0), + mTagProcessor(&mDefaultTagProcessor) {} + + template + TextWriterBase::~TextWriterBase() {} + + template + void TextWriterBase::SetLineHeight(f32 lineHeight) { + const Font* font = GetFont(); + int linefeed = font ? font->GetLineFeed() : 0; + + mLineSpace = lineHeight - linefeed * GetScaleV(); + } + + template + f32 TextWriterBase::GetLineHeight() const { + NW4HBM_ASSERT_VALID_PTR(241, this); + const Font* font = GetFont(); + int linefeed = font ? font->GetLineFeed() : 0; + + return mLineSpace + linefeed * GetScaleV(); + } + + template + void TextWriterBase::SetLineSpace(f32 lineSpace) { + NW4HBM_ASSERT_VALID_PTR(261, this); + mLineSpace = lineSpace; + } + + template + void TextWriterBase::SetCharSpace(f32 charSpace) { + NW4HBM_ASSERT_VALID_PTR(278, this); + mCharSpace = charSpace; + } + + template + f32 TextWriterBase::GetLineSpace() const { + return mLineSpace; + } + + template + f32 TextWriterBase::GetCharSpace() const { + NW4HBM_ASSERT_VALID_PTR(312, this); + return mCharSpace; + } + + template + void TextWriterBase::SetTabWidth(int tabWidth) { + mTabWidth = tabWidth; + } + + template + int TextWriterBase::GetTabWidth() const { + NW4HBM_ASSERT_VALID_PTR(346, this); + return mTabWidth; + } + + template + void TextWriterBase::SetDrawFlag(u32 flags) { + mDrawFlag = flags; + } + + template + u32 TextWriterBase::GetDrawFlag() const { + return mDrawFlag; + } + + template + void TextWriterBase::SetTagProcessor(TagProcessorBase* tagProcessor) { + NW4HBM_ASSERT_VALID_PTR(114, this); + NW4HBM_ASSERT_VALID_PTR(115, tagProcessor); + mTagProcessor = tagProcessor; + } + + template + void TextWriterBase::ResetTagProcessor() { + mTagProcessor = &mDefaultTagProcessor; + } + + template + TagProcessorBase& TextWriterBase::GetTagProcessor() const { + NW4HBM_ASSERT_VALID_PTR(151, this); + return *mTagProcessor; + } + + template + f32 TextWriterBase::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 + f32 TextWriterBase::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 + void TextWriterBase::CalcFormatStringRect(Rect* pRect, const charT* format, + ...) const { + std::va_list vargs; + + va_start(vargs, format); + CalcVStringRect(pRect, format, vargs); + va_end(vargs); + } + + template + f32 TextWriterBase::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 + f32 TextWriterBase::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 context = {this, str, xOrigin, yOrigin, 0}; + CharStrmReader reader = GetFont()->GetCharStrmReader(); + reader.Set(str); + + u16 code = reader.Next(); + + while (static_cast(reader.GetCurrentPos()) - str <= length) { + if (code < ' ') { // C0 control characters under space + context.str = static_cast(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 + void TextWriterBase::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 + void TextWriterBase::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 + int TextWriterBase::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 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(reader.GetCurrentPos()) - str <= length; + code = reader.Next()) + { + if (code < ' ') { + Operation operation; + Rect rect(x, 0.0f, 0.0f, 0.0f); + + context.str = static_cast(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(reader.GetCurrentPos()) - str; + } + + template + f32 TextWriterBase::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 myCopy(*this); + + myCopy.SetCursor(0.0f, 0.0f); + myCopy.CalcLineRectImpl(&rect, str, length); + return rect.GetWidth(); + } + + template + charT* TextWriterBase::GetBuffer() { + return mFormatBuffer; + } + + template + u32 TextWriterBase::GetBufferSize() { + return mFormatBufferSize; + } + + template + charT* TextWriterBase::SetBuffer(u32 size) { + charT* oldBuffer = mFormatBuffer; + + mFormatBuffer = NULL; + mFormatBufferSize = size; + + return oldBuffer; + } + + template + charT* TextWriterBase::SetBuffer(charT* buffer, u32 size) { + charT* oldBuffer = mFormatBuffer; + + mFormatBuffer = buffer; + mFormatBufferSize = size; + + return oldBuffer; + } + + template + f32 TextWriterBase::Print(const charT* str) { + NW4HBM_ASSERT_VALID_PTR(733, this); + NW4HBM_ASSERT_VALID_PTR(734, str); + return Print(str, StrLen(str)); + } + + template + f32 TextWriterBase::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 myCopy(*this); + + f32 width = myCopy.PrintImpl(str, length); + SetCursor(myCopy.GetCursorX(), myCopy.GetCursorY()); + + return width; + } + + template + f32 TextWriterBase::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(__alloca(mFormatBufferSize)); + + int length = VSNPrintf(buffer, mFormatBufferSize, format, args); + f32 width = Print(buffer, length); + return width; + } + + template + f32 TextWriterBase::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 + void TextWriterBase::CalcStringRect(Rect* pRect, const charT* str) const { + CalcStringRect(pRect, str, StrLen(str)); + } + + template + void TextWriterBase::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 myCopy(*this); + + myCopy.CalcStringRectImpl(pRect, str, length); + } + + template + f32 TextWriterBase::CalcStringHeight(const charT* str) const { + return CalcStringHeight(str, StrLen(str)); + } + + template + f32 TextWriterBase::CalcStringHeight(const charT* str, int length) const { + Rect rect; + + CalcStringRect(&rect, str, length); + return rect.GetHeight(); + } + + template + f32 TextWriterBase::CalcStringWidth(const charT* str) const { + return CalcStringWidth(str, StrLen(str)); + } + + template + f32 TextWriterBase::CalcStringWidth(const charT* str, int length) const { + Rect rect; + + CalcStringRect(&rect, str, length); + return rect.GetWidth(); + } + + template + void TextWriterBase::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(__alloca(mFormatBufferSize)); + + int length = VSNPrintf(buffer, mFormatBufferSize, format, args); + CalcStringRect(pRect, buffer, length); + } + + template class TextWriterBase; + template class TextWriterBase; + + } // 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(reinterpret_cast(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(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 + +#include + +// #include +// #include +// #include + + + +#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 +#include + + +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 + +#include "syn.h" + +// #include +// #include + + + +// 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 + +#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 + +#include + +#include "synprivate.h" + +#include "mix.h" + +// #include +// #include +// #include +// #include + + + +#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 + +#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 + +#include "mix.h" + +// #include +// #include +// #include + + + +// 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 +#include + +#include + +// #include + + + +// 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 + +#include "mix.h" + +// #include + + + +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 + +// #include + + + +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 +#include + + +#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 + +// #include +// #include + + + +// 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 + +#include "mix.h" + +// #include +// #include + + + +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 #include -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}; +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}; - 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; + 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; - encodeSize = (samples + 1) / 2; - memset(adpcmData, 0, encodeSize); + block = (WENCBlock*)info; + nEncodeSize = (nSampleNum + 1) / 2; - src = pcmData; - dst = adpcmData; + memset(pbyAdpcmData, 0, nEncodeSize); - if ((flag & WENC_FLAG_USER_INFO) == 0) { - xn = 0; - dl = 127; - qn = 0; - dn = 0; - dlh = 0; - dlq = 0; + 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; + + nDN = __abs(nDA - nXN); + if (nDN >= nDL) { + nL2 = 1; + nDN -= nDL; } - dn = __abs(da - xn); - if (dn >= dl) { - l2 = 1; - dn -= dl; + nDLH = nDL / 2; + if (nDN >= nDLH) { + nL1 = 1; + nDN -= nDLH; } - dlh = dl / 2; - if (dn >= dlh) { - l1 = 1; - dn -= dlh; + nDLQ = nDLH / 2; + if (nDN >= nDLQ) { + nL0 = 1; + nDN -= nDLQ; } - dlq = dlh / 2; - if (dn >= dlq) { - l0 = 1; - dn -= dlq; - } + nDLX = nDLQ / 2; - dlx = dlq / 2; - qn = (1 - l3 * 2) * (dl * l2 + dlh * l1 + dlq * l0 + dlx); + nQN = (1 - nL3 * 2) * (nDL * nL2 + nDLH * nL1 + nDLQ * nL0 + nDLX); - if (qn > 0xFFFF) { - qn = 0xFFFF; - } - if (qn < -0x10000) { - qn = -0x10000; - } + if (nQN > 0xffff) + nQN = 0xffff; - xnc = xn + qn; - if (xnc > 0x7FFF) { - xnc = 0x7FFF; - } - if (xnc < -0x8000) { - xnc = -0x8000; - } + if (nQN < -0x10000) + nQN = -0x10000; - xn = xnc; - offset = (i & 1) == 0 ? 4 : 0; + nXNC = nXN + nQN; - by = l3 * 8 + l2 * 4 + l1 * 2 + l0; - dst[i / 2] |= by << offset; + if (nXNC > 0x7fff) + nXNC = 0x7fff; - index = l2 * 4 + l1 * 2 + l0; - dl *= table[index]; + if (nXNC < -0x8000) + nXNC = -0x8000; - if (dl <= 127) { - dl = 127; - } - if (dl >= 0x6000) { - dl = 0x6000; - } + 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 }