diff --git a/bin/segment2.c b/bin/segment2.c index 203109c7..20740545 100644 --- a/bin/segment2.c +++ b/bin/segment2.c @@ -5,40 +5,152 @@ #include "macros.h" #include "types.h" #include "game/ingame_menu.h" +#include "game/puppyprint.h" #include "make_const_nonconst.h" // SM64 (US/JP/EU/SH) Segment 02 #ifdef PUPPYPRINT -ALIGNED8 static const Texture small_font_1[] = { +ALIGNED8 static const Texture small_font_default[] = { #include "textures/segment2/custom_text.i4.inc.c" }; -ALIGNED8 static const Texture small_font_2[] = { -#include "textures/segment2/custom_text2.i4.inc.c" +ALIGNED8 static const Texture small_font_outline[] = { +#include "textures/segment2/custom_text2.ia4.inc.c" +}; +ALIGNED8 static const Texture small_font_plain[] = { +#include "textures/segment2/custom_text3.i4.inc.c" +}; +ALIGNED8 static const Texture small_font_vanilla[] = { +#include "textures/segment2/custom_text4.i4.inc.c" }; -const Texture *const puppyprint_font_lut[2] = { - small_font_1, small_font_2 +const u8 small_font_kerning_default[] = { + /*!*/ 4, /*"*/ 5, /*#*/ 0, /*$*/ 0, /*%*/ 8, /*&*/ 8, /*'*/ 2, /*(*/ 5, /*)*/ 5, /***/ 0, /*+*/ 8, /*,*/ 3, /*-*/ 8, /*.*/ 3, /*/*/ 8, /*0*/ 6, + /*1*/ 5, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 8, /*7*/ 7, /*8*/ 7, /*9*/ 6, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 6, /*F*/ 5, /*G*/ 8, /*H*/ 6, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 6, + /*Q*/ 8, /*R*/ 6, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 8, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 8, /*_*/ 0, /*`*/ 0, + /*a*/ 7, /*b*/ 7, /*c*/ 6, /*d*/ 7, /*e*/ 7, /*f*/ 7, /*g*/ 7, /*h*/ 7, /*i*/ 3, /*j*/ 5, /*k*/ 8, /*l*/ 4, /*m*/ 7, /*n*/ 7, /*o*/ 7, /*p*/ 7, + /*q*/ 7, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 7, /*w*/ 8, /*x*/ 6, /*y*/ 8, /*z*/ 7, /*~*/ 8, }; -static const u8 small_font_kerning_1[80] = { - /*0*/ 6, /*1*/ 5, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 8, /*7*/ 7, /*8*/ 7, /*9*/ 6, /*-*/ 8, /*+*/ 8, /*(*/ 5, /*)*/ 5, /*!*/ 4, /*?*/ 6, - /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 6, /*F*/ 5, /*G*/ 8, /*H*/ 6, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 6, - /*Q*/ 8, /*R*/ 6, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 8, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*"*/ 5, /*'*/ 2, /*:*/ 3, /*;*/ 3, /*.*/ 3, /*,*/ 3, - /*a*/ 7, /*b*/ 7, /*c*/ 6, /*d*/ 7, /*e*/ 7, /*f*/ 7, /*g*/ 7, /*h*/ 7, /*i*/ 3, /*j*/ 5, /*k*/ 8, /*l*/ 4, /*m*/ 7, /*n*/ 7, /*o*/ 7, /*p*/ 7, - /*q*/ 7, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 7, /*w*/ 8, /*x*/ 6, /*y*/ 8, /*z*/ 7, /*~*/ 8, /*¨*/ 7, /*^*/ 8, /*/*/ 8, /*%*/ 8, /*&*/ 8, +const u16 small_font_offsets_default[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 }; -static const u8 small_font_kerning_2[80] = { - /*0*/ 6, /*1*/ 6, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*-*/ 6, /*+*/ 6, /*(*/ 3, /*)*/ 3, /*!*/ 4, /*?*/ 5, - /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 6, /*J*/ 6, /*K*/ 6, /*L*/ 6, /*M*/ 6, /*N*/ 6, /*O*/ 6, /*P*/ 6, - /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 6, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*"*/ 4, /*'*/ 1, /*:*/ 2, /*;*/ 2, /*.*/ 2, /*,*/ 2, - /*a*/ 5, /*b*/ 5, /*c*/ 5, /*d*/ 5, /*e*/ 5, /*f*/ 5, /*g*/ 5, /*h*/ 5, /*i*/ 1, /*j*/ 5, /*k*/ 5, /*l*/ 3, /*m*/ 5, /*n*/ 5, /*o*/ 5, /*p*/ 5, - /*q*/ 5, /*r*/ 5, /*s*/ 5, /*t*/ 5, /*u*/ 5, /*v*/ 5, /*w*/ 5, /*x*/ 5, /*y*/ 5, /*z*/ 5, /*~*/ 6, /*¨*/ 5, /*^*/ 6, /*/*/ 5, /*%*/ 5, /*&*/ 6, +static const u8 small_font_kerning_outline[] = { + /*!*/ 3, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 6, /*'*/ 2, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 6, /*.*/ 2, /*/*/ 6, /*0*/ 6, + /*1*/ 6, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*:*/ 2, /*;*/ 2, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 5, /*J*/ 7, /*K*/ 6, /*L*/ 6, /*M*/ 6, /*N*/ 6, /*O*/ 6, /*P*/ 6, + /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 6, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 5, /*b*/ 5, /*c*/ 5, /*d*/ 5, /*e*/ 5, /*f*/ 5, /*g*/ 5, /*h*/ 5, /*i*/ 2, /*j*/ 6, /*k*/ 4, /*l*/ 2, /*m*/ 5, /*n*/ 5, /*o*/ 5, /*p*/ 5, + /*q*/ 5, /*r*/ 5, /*s*/ 5, /*t*/ 5, /*u*/ 5, /*v*/ 5, /*w*/ 5, /*x*/ 5, /*y*/ 5, /*z*/ 5, /*~*/ 6, }; -const u8 *const puppyprint_kerning_lut[2][80] = { - small_font_kerning_1, small_font_kerning_2 +const u16 small_font_offsets_outline[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +static const u8 small_font_kerning_plain[] = { + /*!*/ 5, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 7, /*'*/ 2, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 6, /*.*/ 2, /*/*/ 6, /*0*/ 6, + /*1*/ 5, /*2*/ 5, /*3*/ 5, /*4*/ 5, /*5*/ 5, /*6*/ 5, /*7*/ 5, /*8*/ 5, /*9*/ 5, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 4, /*J*/ 6, /*K*/ 6, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 6, /*P*/ 6, + /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 7, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 6, /*b*/ 6, /*c*/ 6, /*d*/ 6, /*e*/ 6, /*f*/ 6, /*g*/ 6, /*h*/ 6, /*i*/ 3, /*j*/ 4, /*k*/ 6, /*l*/ 5, /*m*/ 7, /*n*/ 6, /*o*/ 6, /*p*/ 6, + /*q*/ 6, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 6, /*w*/ 7, /*x*/ 6, /*y*/ 6, /*z*/ 6, /*~*/ 7, +}; + +const u16 small_font_offsets_plain[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +static const u8 small_font_kerning_vanilla[] = { + /*!*/ 4, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 7, /*'*/ 3, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 4, /*.*/ 3, /*/*/ 4, /*0*/ 5, + /*1*/ 5, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*:*/ 4, /*;*/ 4, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 5, /*@*/ 0, + /*A*/ 5, /*B*/ 5, /*C*/ 5, /*D*/ 5, /*E*/ 5, /*F*/ 5, /*G*/ 5, /*H*/ 5, /*I*/ 3, /*J*/ 5, /*K*/ 5, /*L*/ 5, /*M*/ 7, /*N*/ 7, /*O*/ 6, /*P*/ 5, + /*Q*/ 6, /*R*/ 5, /*S*/ 5, /*T*/ 5, /*U*/ 5, /*V*/ 5, /*W*/ 7, /*X*/ 6, /*Y*/ 5, /*Z*/ 5, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 5, /*b*/ 4, /*c*/ 4, /*d*/ 4, /*e*/ 4, /*f*/ 5, /*g*/ 5, /*h*/ 4, /*i*/ 3, /*j*/ 4, /*k*/ 3, /*l*/ 2, /*m*/ 6, /*n*/ 4, /*o*/ 4, /*p*/ 4, + /*q*/ 5, /*r*/ 4, /*s*/ 4, /*t*/ 4, /*u*/ 4, /*v*/ 4, /*w*/ 7, /*x*/ 5, /*y*/ 4, /*z*/ 5, /*~*/ 6, +}; + +const u16 small_font_offsets_vanilla[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +const struct PPTextFont sPPFont_default = { + small_font_default, + small_font_kerning_default, + small_font_offsets_default, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_outline = { + small_font_outline, + small_font_kerning_outline, + small_font_offsets_outline, + NULL, + G_IM_FMT_IA, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_plain = { + small_font_plain, + small_font_kerning_plain, + small_font_offsets_plain, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_vanilla = { + small_font_vanilla, + small_font_kerning_vanilla, + small_font_offsets_vanilla, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont *const gPuppyPrintFontTable[] = { + &sPPFont_default, &sPPFont_outline, &sPPFont_plain, &sPPFont_vanilla +}; + + +const Texture *const puppyprint_font_lut[] = { + small_font_default, small_font_outline, small_font_plain, small_font_vanilla +}; + +const u8 *const puppyprint_kerning_lut[][95] = { + small_font_kerning_default, small_font_kerning_outline, small_font_kerning_plain, small_font_kerning_vanilla }; #endif @@ -2753,6 +2865,7 @@ const Gfx dl_waterbox_rgba16_begin[] = { gsDPSetCombineMode(G_CC_MODULATERGBA, G_CC_MODULATERGBA), gsSPClearGeometryMode(G_LIGHTING | G_CULL_BACK), gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsSPClipRatio(FRUSTRATIO_1), gsDPTileSync(), gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, (G_TX_WRAP | G_TX_NOMIRROR), 5, G_TX_NOLOD, (G_TX_WRAP | G_TX_NOMIRROR), 5, G_TX_NOLOD), gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((32 - 1) << G_TEXTURE_IMAGE_FRAC)), @@ -2765,6 +2878,7 @@ const Gfx dl_waterbox_ia16_begin[] = { gsDPSetCombineMode(G_CC_MODULATEIA, G_CC_MODULATEIA), gsSPClearGeometryMode(G_LIGHTING | G_CULL_BACK), gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsSPClipRatio(FRUSTRATIO_1), gsDPTileSync(), gsDPSetTile(G_IM_FMT_IA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, (G_TX_WRAP | G_TX_NOMIRROR), 5, G_TX_NOLOD, (G_TX_WRAP | G_TX_NOMIRROR), 5, G_TX_NOLOD), gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((32 - 1) << G_TEXTURE_IMAGE_FRAC)), @@ -2774,6 +2888,7 @@ const Gfx dl_waterbox_ia16_begin[] = { // 0x02014810 - 0x02014838 const Gfx dl_waterbox_end[] = { gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsSPClipRatio(FRUSTRATIO_2), gsDPPipeSync(), gsSPSetGeometryMode(G_LIGHTING | G_CULL_BACK), gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), diff --git a/data/behavior_data.c b/data/behavior_data.c index 6a02545d..50d6791b 100644 --- a/data/behavior_data.c +++ b/data/behavior_data.c @@ -403,8 +403,8 @@ const BehaviorScript bhvStarDoor[] = { SET_INT(oIntangibleTimer, 0), BEGIN_LOOP(), CALL_NATIVE(bhv_star_door_loop), - CALL_NATIVE(bhv_door_rendering_loop), CALL_NATIVE(load_object_collision_model), + CALL_NATIVE(bhv_door_rendering_loop), END_LOOP(), }; diff --git a/include/config/config_debug.h b/include/config/config_debug.h index eee44caf..86e27755 100644 --- a/include/config/config_debug.h +++ b/include/config/config_debug.h @@ -16,7 +16,7 @@ /** * Enables a comprehensive standalone profiler. Automatically enabled by PUPPYPRINT_DEBUG. - * If not using PUPPYPRINT_DEBUG, press L to toggle the profiler. + * If not using PUPPYPRINT_DEBUG, press L + D-Pad Up to toggle the profiler. */ #define USE_PROFILER @@ -41,7 +41,7 @@ /** * Enables a custom, enhanced performance profiler. (Enables PUPPYPRINT by default in config_safeguards). */ -// #define PUPPYPRINT_DEBUG 1 +// #define PUPPYPRINT_DEBUG /** * Uses cycles instead of microseconds in Puppyprint debug output. diff --git a/include/config/config_safeguards.h b/include/config/config_safeguards.h index 6cf2e986..3a7b9659 100644 --- a/include/config/config_safeguards.h +++ b/include/config/config_safeguards.h @@ -100,7 +100,7 @@ #define PUPPYPRINT #undef PUPPYPRINT_DEBUG - #define PUPPYPRINT_DEBUG 1 + #define PUPPYPRINT_DEBUG #undef VISUAL_DEBUG #define VISUAL_DEBUG diff --git a/include/object_constants.h b/include/object_constants.h index 28cdc596..7ee6ad17 100644 --- a/include/object_constants.h +++ b/include/object_constants.h @@ -55,7 +55,7 @@ enum ObjFlags { OBJ_FLAG_OCCLUDE_SILHOUETTE = (1 << 20), // 0x00100000 OBJ_FLAG_OPACITY_FROM_CAMERA_DIST = (1 << 21), // 0x00200000 OBJ_FLAG_EMIT_LIGHT = (1 << 22), // 0x00400000 - OBJ_FLAG_PROCESS_OUTSIDE_ROOM = (1 << 23), // 0x00800000 + OBJ_FLAG_ONLY_PROCESS_INSIDE_ROOM = (1 << 23), // 0x00800000 OBJ_FLAG_HITBOX_WAS_SET = (1 << 30), // 0x40000000 }; @@ -779,7 +779,6 @@ enum ExclamationBoxContentsList { // oBehParams2ndByte, ExclamationBoxContents-> EXCLAMATION_BOX_BP_STAR_4, EXCLAMATION_BOX_BP_STAR_5, EXCLAMATION_BOX_BP_STAR_6, - EXCLAMATION_BOX_BP_NULL = 99 }; enum oBehParam1stByteExclamationBox { EXCLAMATION_BOX_BP1_NEEDS_SWITCH, diff --git a/include/segment_symbols.h b/include/segment_symbols.h index eba62dc8..e664c475 100644 --- a/include/segment_symbols.h +++ b/include/segment_symbols.h @@ -51,11 +51,18 @@ DECLARE_SEGMENT(goddard) DECLARE_SEGMENT(framebuffers) DECLARE_SEGMENT(assets) extern u8 _goddardSegmentStart[]; +extern u8 _goddardSegmentEnd[]; extern u8 _engineSegmentStart[]; extern u8 _engineSegmentBssEnd[]; +extern u8 _mainSegmentStart[]; extern u8 _mainSegmentEnd[]; extern u8 _engineSegmentEnd[]; +extern u8 _framebuffersSegmentBssStart[]; extern u8 _framebuffersSegmentBssEnd[]; +extern u8 _zbufferSegmentBssStart[]; +extern u8 _zbufferSegmentBssEnd[]; +extern u8 _buffersSegmentBssStart[]; +extern u8 _buffersSegmentBssEnd[]; DECLARE_LEVEL_SEGMENT(menu) DECLARE_LEVEL_SEGMENT(intro) diff --git a/levels/castle_inside/areas/1/room.inc.c b/levels/castle_inside/areas/1/room.inc.c index fb101699..12312186 100644 --- a/levels/castle_inside/areas/1/room.inc.c +++ b/levels/castle_inside/areas/1/room.inc.c @@ -2,8 +2,8 @@ const RoomData inside_castle_seg7_area_1_rooms[] = { 1, 9, 9, 1, 9, 9, 9, 9, // 0-7 9, 9, 1, 1, 1, 9, 9, 17, // 8-15 - 1, 9, 9, 9, 9, 9, 17, 9, // 16-23 - 9, 9, 9, 9, 9, 9, 9, 9, // 24-31 + 1, 17, 17, 9, 17, 9, 17, 9, // 16-23 + 9, 9, 17, 9, 9, 9, 9, 9, // 24-31 8, 8, 8, 8, 8, 8, 8, 8, // 32-39 8, 8, 8, 8, 8, 8, 8, 8, // 40-47 8, 8, 8, 8, 8, 8, 8, 8, // 48-55 diff --git a/levels/ccm/areas/2/1/model.inc.c b/levels/ccm/areas/2/1/model.inc.c index 2ddfd1da..b1c73cf4 100644 --- a/levels/ccm/areas/2/1/model.inc.c +++ b/levels/ccm/areas/2/1/model.inc.c @@ -1999,6 +1999,7 @@ static const Gfx ccm_seg7_dl_0701C5C8[] = { // 0x0701CC40 - 0x0701CE30 static const Gfx ccm_seg7_dl_0701CC40[] = { gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, snow_09001000), + gsSPClipRatio(FRUSTRATIO_1), gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 32 * 64 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)), gsSPVertex(ccm_seg7_vertex_0701AB50, 16, 0), @@ -2035,6 +2036,7 @@ static const Gfx ccm_seg7_dl_0701CC40[] = { gsSPVertex(ccm_seg7_vertex_0701B040, 10, 0), gsSP2Triangles( 0, 1, 2, 0x0, 1, 3, 2, 0x0), gsSP2Triangles( 4, 5, 6, 0x0, 7, 8, 9, 0x0), + gsSPClipRatio(FRUSTRATIO_2), gsSPEndDisplayList(), }; diff --git a/levels/ending/geo.c b/levels/ending/geo.c index f6946e24..176752e8 100644 --- a/levels/ending/geo.c +++ b/levels/ending/geo.c @@ -21,9 +21,7 @@ const GeoLayout ending_geo_area_1[] = { GEO_OPEN_NODE(), GEO_NODE_ORTHO(100), GEO_OPEN_NODE(), -#ifdef VERSION_EU - GEO_BACKGROUND_COLOR(0x0001), -#endif + GEO_BACKGROUND_COLOR(GPACK_RGBA5551(0, 0, 0, 1)), GEO_ASM(0, geo_exec_cake_end_screen), GEO_CLOSE_NODE(), GEO_CLOSE_NODE(), @@ -37,4 +35,3 @@ const GeoLayout ending_geo_area_1[] = { GEO_CLOSE_NODE(), GEO_END(), }; - diff --git a/src/audio/data.c b/src/audio/data.c index b235130d..97c843cf 100644 --- a/src/audio/data.c +++ b/src/audio/data.c @@ -48,19 +48,19 @@ u32 delaysArr[][NUM_ALLPASS] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4 + 4, 4, 4, }, { /* 1 */ 1080, 1352, 1200, 1200, 1232, 1432, 1384, 1048, 1352, - 928, 1504, 1512 + 928, 1504, 1512, }, { /* 2 */ 1384, 1352, 1048, - 928, 1512, 1504, + 928, 1512, 1504, 1080, 1200, 1352, - 1200, 1432, 1232 + 1200, 1432, 1232, }, }; @@ -74,34 +74,72 @@ s32 reverbMultsArr[][NUM_ALLPASS / 3] = { /** * Format: - * - downsampleRate (Higher values exponentially reduce the number of input samples to process, improving perfomance at cost of quality) - * - isMono (Only process reverb on the left channel and share it with the right channel, improving performance at cost of quality) - * - filterCount (Number of filters to process data with; in general, more filters means higher quality at the cost of performance demand) - * - windowSize (Size of circular reverb buffer; higher values work better for a more open soundscape, lower is better for a more compact sound) - * - gain (Amount of audio retransmitted into the circular reverb buffer, emulating decay; higher values represent a lengthier decay period) - * - gainIndex (Advanced parameter used to tune the outputs of every first two of three filters) - * - reverbIndex (Advanced parameter used to tune the incoming output of every third filter) + * - useLightweightSettings (Reduce some runtime configurability options in favor of a slight speed boost during processing; Light configurability settings are found in synthesis.h) + * - downsampleRate (Higher values exponentially reduce the number of input samples to process, improving perfomance at cost of quality) + * - isMono (Only process reverb on the left channel and share it with the right channel, improving performance at cost of quality) + * - filterCount (Number of filters to process data with; in general, more filters means higher quality at the cost of performance demand; always 3 with light settings) * - * - *delaysL (Array of variable audio buffer sizes / delays for each respective filter [left channel]) - * - *delaysR (Array of variable audio buffer sizes / delays for each respective filter [right channel]) - * - *reverbMultsL (Array of multipliers applied to the final output of each group of 3 filters [left channel]) - * - *reverbMultsR (Array of multipliers applied to the final output of each group of 3 filters [right channel]) + * - windowSize (Size of circular reverb buffer; higher values work better for a more open soundscape, lower is better for a more compact sound) + * - gain (Amount of audio retransmitted into the circular reverb buffer, emulating decay; higher values represent a lengthier decay period) + * - gainIndex (Advanced parameter; used to tune the outputs of every first two of three filters; overridden when using light settings) + * - reverbIndex (Advanced parameter; used to tune the incoming output of every third filter; overridden when using light settings) * - * NOTE: First entry will always be used by default when not using the level commands to specify a preset. + * - *delaysL (Advanced parameter; array of variable audio buffer sizes / delays for each respective filter [left channel]; overridden when using light settings) + * - *delaysR (Advanced parameter; array of variable audio buffer sizes / delays for each respective filter [right channel]; overridden when using light settings) + * - *reverbMultsL (Advanced parameter; array of multipliers applied to the final output of each group of 3 filters [left channel]) + * - *reverbMultsR (Advanced parameter; array of multipliers applied to the final output of each group of 3 filters [right channel]) + * + * NOTE: The first entry will always be used by default when not using the level commands to specify a preset. * Please reference the HackerSM64 Wiki for more descriptive documentation of these parameters and usage of BETTER_REVERB in general. */ struct BetterReverbSettings gBetterReverbSettings[] = { - { /* 0 */ - -1, FALSE, NUM_ALLPASS, -1, -1, 0x00, 0x00, // Vanilla Reverb - delaysArr[0], delaysArr[0], reverbMultsArr[0], reverbMultsArr[0] + { /* Preset 0 - Vanilla Reverb [Default Preset] */ + .useLightweightSettings = FALSE, // Ignored with vanilla reverb + .downsampleRate = -1, // Signifies use of vanilla reverb + .isMono = FALSE, // Ignored with vanilla reverb + .filterCount = NUM_ALLPASS, // Ignored with vanilla reverb + + .windowSize = -1, // Use vanilla preset window size + .gain = -1, // Use vanilla preset gain value + .gainIndex = 0x00, // Ignored with vanilla reverb + .reverbIndex = 0x00, // Ignored with vanilla reverb + + .delaysL = delaysArr[0], // Ignored with vanilla reverb + .delaysR = delaysArr[0], // Ignored with vanilla reverb + .reverbMultsL = reverbMultsArr[0], // Ignored with vanilla reverb + .reverbMultsR = reverbMultsArr[0], // Ignored with vanilla reverb }, - { /* 1 */ - 2, FALSE, (NUM_ALLPASS - 9), 0xE00, 0x43FF, 0xA0, 0x30, // Default Console - delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2] + { /* Preset 1 - Sample Console Configuration */ + .useLightweightSettings = TRUE, + .downsampleRate = 2, + .isMono = FALSE, + .filterCount = (NUM_ALLPASS - 9), // Ignored with lightweight settings + + .windowSize = 0x0E00, + .gain = 0x43FF, + .gainIndex = 0xA0, // Ignored with lightweight settings + .reverbIndex = 0x30, // Ignored with lightweight settings + + .delaysL = delaysArr[1], + .delaysR = delaysArr[2], + .reverbMultsL = reverbMultsArr[1], // Ignored with lightweight settings + .reverbMultsR = reverbMultsArr[2], // Ignored with lightweight settings }, - { /* 2 */ - 1, FALSE, NUM_ALLPASS, 0xE00, 0x28FF, 0xA0, 0x60, // Default Emulator (RCVI Hack only) - delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2] + { /* Preset 2 - Sample Emulator Configuration (RCVI Hack Only) */ + .useLightweightSettings = FALSE, + .downsampleRate = 1, + .isMono = FALSE, + .filterCount = NUM_ALLPASS, + + .windowSize = 0x0E00, + .gain = 0x28FF, + .gainIndex = 0xA0, + .reverbIndex = 0x60, + + .delaysL = delaysArr[1], + .delaysR = delaysArr[2], + .reverbMultsL = reverbMultsArr[1], + .reverbMultsR = reverbMultsArr[2], }, }; #endif diff --git a/src/audio/external.c b/src/audio/external.c index 4b8d9760..3502da1e 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -688,6 +688,7 @@ struct SPTask *create_next_audio_frame_task(void) { task->yield_data_size = 0; decrease_sample_dma_ttls(); + return gAudioTask; } #endif @@ -2186,7 +2187,7 @@ void play_music(u8 player, u16 seqArgs, u16 fadeTimer) { // Abort if the queue is already full. if (sBackgroundMusicQueueSize >= MAX_BACKGROUND_MUSIC_QUEUE_SIZE) { -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Sequence queue full, aborting."); #endif return; diff --git a/src/audio/heap.c b/src/audio/heap.c index 0270021d..3a0703fc 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -301,7 +301,7 @@ void sound_init_main_pools(s32 sizeForAudioInitPool) { sound_alloc_pool_init(&gAudioSessionPool, (gAudioHeap + sizeForAudioInitPool), (gAudioHeapSize - sizeForAudioInitPool)); } -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void puppyprint_get_allocated_pools(s32 *audioPoolList) { u32 i, j; const struct SoundAllocPool *pools[NUM_AUDIO_POOLS] = { @@ -1051,6 +1051,7 @@ void init_reverb_us(s32 presetId) { // This will likely crash if given an invalid preset value. Adding a safety check here isn't worth the usability interference. struct BetterReverbSettings *betterReverbPreset = &gBetterReverbSettings[gBetterReverbPreset]; + betterReverbLightweight = betterReverbPreset->useLightweightSettings; betterReverbDownsampleRate = betterReverbPreset->downsampleRate; monoReverb = betterReverbPreset->isMono; reverbFilterCount = betterReverbPreset->filterCount; @@ -1204,7 +1205,7 @@ void audio_reset_session(void) { #if defined(VERSION_JP) || defined(VERSION_US) s8 updatesPerFrame; #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG OSTime first = osGetTime(); #endif s32 j; @@ -1436,7 +1437,7 @@ void audio_reset_session(void) { gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; } #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG #ifdef PUPPYPRINT_DEBUG_CYCLES append_puppyprint_log("Audio Initialised in %dc.", (s32)(osGetTime() - first)); #else diff --git a/src/audio/heap.h b/src/audio/heap.h index c7b1494e..26a3e2dd 100644 --- a/src/audio/heap.h +++ b/src/audio/heap.h @@ -129,7 +129,7 @@ void *soundAlloc(struct SoundAllocPool *pool, u32 size); void *sound_alloc_uninitialized(struct SoundAllocPool *pool, u32 size); void sound_init_main_pools(s32 sizeForAudioInitPool); void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size); -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void puppyprint_get_allocated_pools(s32 *audioPoolList); #endif #ifdef VERSION_SH diff --git a/src/audio/internal.h b/src/audio/internal.h index bae4ff6d..f60ca5ea 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -87,7 +87,7 @@ enum Codecs { #include "game/puppyprint.h" #ifdef VERSION_EU -/*#if PUPPYPRINT_DEBUG +/*#ifdef PUPPYPRINT_DEBUG #define eu_stubbed_printf_0(msg) append_puppyprint_log(msg) #define eu_stubbed_printf_1(msg, a) append_puppyprint_log(msg, a) #define eu_stubbed_printf_2(msg, a, b) append_puppyprint_log(msg, a, b) @@ -732,6 +732,7 @@ struct NoteSynthesisBuffers { #ifdef BETTER_REVERB struct BetterReverbSettings { + u8 useLightweightSettings; s8 downsampleRate; u8 isMono; u8 filterCount; diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index 00144342..423062dd 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -2682,8 +2682,10 @@ void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { // This runs 240 times per second. void process_sequences(UNUSED s32 iterationsRemaining) { s32 i; + for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled == TRUE) { + #if defined(VERSION_EU) || defined(VERSION_SH) sequence_player_process_sequence(&gSequencePlayers[i]); sequence_player_process_sound(&gSequencePlayers[i]); @@ -2693,8 +2695,13 @@ void process_sequences(UNUSED s32 iterationsRemaining) { #endif } } + #if defined(VERSION_JP) || defined(VERSION_US) + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM); reclaim_notes(); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM, PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING); +#else + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING); #endif process_notes(); } diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 48a23d5d..1884fdde 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -42,6 +42,7 @@ // Do not touch these values manually, unless you want potential for problems. u8 gBetterReverbPreset = 0; u8 toggleBetterReverb = FALSE; +u8 betterReverbLightweight = FALSE; u8 monoReverb; s8 betterReverbDownsampleRate; static u8 reverbMultsL[NUM_ALLPASS / 3] = {0}; @@ -52,6 +53,8 @@ static s32 delaysL[NUM_ALLPASS] = {0}; static s32 delaysR[NUM_ALLPASS] = {0}; static s32 **delayBufsL; static s32 **delayBufsR; +static s32 lastDelayLightL; +static s32 lastDelayLightR; s32 reverbLastFilterIndex; s32 reverbFilterCount; s32 betterReverbWindowsSize; @@ -73,13 +76,11 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI #ifdef VERSION_EU u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd); u64 *load_wave_samples(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamplesToLoad); -u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags); u64 *process_envelope(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight); #else u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd); u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad); -u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags); u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); u64 *process_envelope_inner(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, @@ -101,13 +102,13 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 s32 *curDelaySampleR; s32 historySampleL; s32 historySampleR; - s32 i = 0; - s32 j = 0; - s32 k = 0; s32 outTmpL = 0; s32 outTmpR = 0; s32 tmpCarryoverL = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleL; s32 tmpCarryoverR = ((delayBufsR[reverbLastFilterIndex][allpassIdxR[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleR; + s32 i = 0; + s32 j = 0; + s32 k = 0; for (; i <= reverbLastFilterIndex; ++i, ++j) { curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; @@ -143,11 +144,11 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 static void reverb_mono_sample(s16 *outSample, s32 inSample) { s32 *curDelaySample; s32 historySample; + s32 outTmp = 0; + s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample; s32 i = 0; s32 j = 0; s32 k = 0; - s32 outTmp = 0; - s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample; for (; i <= reverbLastFilterIndex; ++i, ++j) { curDelaySample = &delayBufsL[i][allpassIdxL[i]]; @@ -170,6 +171,72 @@ static void reverb_mono_sample(s16 *outSample, s32 inSample) { *outSample = CLAMP_S16(outTmp); } +// Light reverb processing functions! +#define FILTERS_MINUS_1 (BETTER_REVERB_FILTER_COUNT_LIGHT - 1) +static void reverb_samples_light(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { + s32 *curDelaySampleL; + s32 *curDelaySampleR; + s32 historySampleL; + s32 historySampleR; + s32 tmpCarryoverL = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleL); + s32 tmpCarryoverR = (((delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleR); + s32 i = 0; + + for (; i < FILTERS_MINUS_1; ++i) { + curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; + curDelaySampleR = &delayBufsR[i][allpassIdxR[i]]; + historySampleL = *curDelaySampleL; + historySampleR = *curDelaySampleR; + + *curDelaySampleL = (((historySampleL * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverL); + *curDelaySampleR = (((historySampleR * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverR); + tmpCarryoverL = (((*curDelaySampleL * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleL); + tmpCarryoverR = (((*curDelaySampleR * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleR); + + if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; + if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0; + } + + curDelaySampleL = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]]; + curDelaySampleR = &delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]]; + historySampleL = ((*curDelaySampleL * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpL variable not needed, as there is no sample addition happening here. Not really a history sample though. + historySampleR = ((*curDelaySampleR * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpR variable not needed, as there is no sample addition happening here. Not really a history sample though. + *curDelaySampleL = tmpCarryoverL; + *curDelaySampleR = tmpCarryoverR; + + if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0; + if (++allpassIdxR[FILTERS_MINUS_1] == lastDelayLightR) allpassIdxR[FILTERS_MINUS_1] = 0; + + *outSampleL = CLAMP_S16(historySampleL); + *outSampleR = CLAMP_S16(historySampleR); +} + +static void reverb_mono_sample_light(s16 *outSample, s32 inSample) { + s32 *curDelaySample; + s32 historySample; + s32 tmpCarryover = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSample); + s32 i = 0; + + for (; i < FILTERS_MINUS_1; ++i) { + curDelaySample = &delayBufsL[i][allpassIdxL[i]]; + historySample = *curDelaySample; + + *curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover); + tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample); + + if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; + } + + curDelaySample = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]]; + historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmp variable not needed, as there is no sample addition happening here. Not really a history sample though. + *curDelaySample = tmpCarryover; + + if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0; + + *outSample = CLAMP_S16(historySample); +} +#undef FILTERS_MINUS_1 + void initialize_better_reverb_buffers(void) { delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); delayBufsR = &delayBufsL[NUM_ALLPASS]; @@ -178,6 +245,10 @@ void initialize_better_reverb_buffers(void) { void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { s32 bufOffset = 0; s32 i; + s32 filterCount = reverbFilterCount; + + if (betterReverbLightweight) + filterCount = BETTER_REVERB_FILTER_COUNT_LIGHT; gBetterReverbPool.cur = gBetterReverbPool.start + ALIGN16(BETTER_REVERB_PTR_SIZE); // Reset reverb data pool @@ -185,9 +256,9 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { if (!toggleBetterReverb) return; - // NOTE: Using reverbFilterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional + // NOTE: Using filterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional // risk to anybody testing on console with performance compromises, as emulator can be easily overlooked. - for (i = 0; i < reverbFilterCount; ++i) { + for (i = 0; i < filterCount; ++i) { delaysL[i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate); delaysR[i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate); delayBufsL[i] = soundAlloc(&gBetterReverbPool, delaysL[i] * sizeof(s32)); @@ -198,6 +269,9 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { aggress(bufOffset * sizeof(s32) <= BETTER_REVERB_SIZE - ALIGN16(BETTER_REVERB_PTR_SIZE), "BETTER_REVERB_SIZE is too small for this preset!"); + lastDelayLightL = delaysL[filterCount-1]; + lastDelayLightR = delaysR[filterCount-1]; + bzero(allpassIdxL, sizeof(allpassIdxL)); bzero(allpassIdxR, sizeof(allpassIdxR)); } @@ -302,40 +376,82 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } #ifdef BETTER_REVERB else if (toggleBetterReverb) { + s32 firstLoopMax; + s32 secondLoopMax; item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gSoundMode == SOUND_MODE_MONO || monoReverb) { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; - } - for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + dstPos = item->startPos; + firstLoopMax = ((item->lengthA / 2) + item->startPos); + secondLoopMax = (item->lengthB / 2); + + // This block could be simplified probably with function pointers or rewriting reverb_mono_sample to support L and R individually. Another idea would be to process more than one sample at a time + // for each function call. In practice with HackerSM64, these ideas either aren't super trivial to implement efficiently or don't benefit performance at all, so I'm leaving this as it is for now. + if (betterReverbLightweight) { + if (gSoundMode == SOUND_MODE_MONO || monoReverb) { + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); + + for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); + } else { + for (; dstPos < firstLoopMax; dstPos++) + reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); + + for (dstPos = 0; dstPos < secondLoopMax; dstPos++) + reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); } } else { - for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; - } - for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + } else { + for (; dstPos < firstLoopMax; dstPos++) + reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + for (dstPos = 0; dstPos < secondLoopMax; dstPos++) + reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); } } } else { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + if (gSoundMode == SOUND_MODE_MONO || monoReverb) { + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); + + for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); + } else { + for (; dstPos < firstLoopMax; dstPos++) + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); + + for (dstPos = 0; dstPos < secondLoopMax; dstPos++) + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); + } } else { - for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); - for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + } else { + for (; dstPos < firstLoopMax; dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + for (dstPos = 0; dstPos < secondLoopMax; dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + } } } } @@ -409,29 +525,6 @@ void synthesis_load_note_subs_eu(s32 updateIndex) { } #endif -#ifndef VERSION_EU -s32 get_volume_ramping(u16 sourceVol, u16 targetVol, s32 arg2) { - // This roughly computes 2^16 * (targetVol / sourceVol) ^ (8 / arg2), - // but with discretizations of targetVol, sourceVol and arg2. - f32 ret; - switch (arg2) { - default: - ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; - break; - case 128: - ret = gVolRampingLhs128[targetVol >> 8] * gVolRampingRhs128[sourceVol >> 8]; - break; - case 136: - ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; - break; - case 144: - ret = gVolRampingLhs144[targetVol >> 8] * gVolRampingRhs144[sourceVol >> 8]; - break; - } - return ret; -} -#endif - #ifdef VERSION_EU // TODO: (Scrub C) pointless mask and whitespace u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { @@ -536,11 +629,22 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { chunkLen += 8; } } + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_UPDATE, PROFILER_TIME_SUB_AUDIO_SEQUENCES); + AUDIO_PROFILER_START_SHARED(PROFILER_TIME_SUB_AUDIO_SEQUENCES, PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT); + process_sequences(i - 1); + + AUDIO_PROFILER_COMPLETE_AND_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING, PROFILER_TIME_SUB_AUDIO_SEQUENCES, PROFILER_TIME_SUB_AUDIO_SYNTHESIS); + AUDIO_PROFILER_START_SHARED(PROFILER_TIME_SUB_AUDIO_SYNTHESIS, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); + if (gSynthesisReverb.useReverb) { prepare_reverb_ring_buffer(chunkLen, gAudioUpdatesPerFrame - i); } cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioUpdatesPerFrame - i); + + AUDIO_PROFILER_COMPLETE_AND_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS, PROFILER_TIME_SUB_AUDIO_UPDATE); + bufLen -= chunkLen; aiBufPtr += chunkLen; } @@ -705,7 +809,10 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI if (!gSynthesisReverb.useReverb) { aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); } else { if (gReverbDownsampleRate == 1) { // Put the oldest samples in the ring buffer into the wet channels @@ -741,7 +848,11 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI aMix(cmd++, 0, /*gain*/ 0x8000 + gSynthesisReverb.reverbGain, /*in*/ DMEM_ADDR_LEFT_CH, /*out*/ DMEM_ADDR_LEFT_CH); aDMEMMove(cmd++, DMEM_ADDR_LEFT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); } + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); + if (gReverbDownsampleRate == 1) { aSetSaveBufferPair(cmd++, 0, v1->lengthA, v1->startPos); if (v1->lengthB != 0) { @@ -1020,15 +1131,25 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { if (audioBookSample->loaded == 0x81) { v0_2 = sampleAddr + temp * 9; } else { + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA); + v0_2 = dma_sample_data( (uintptr_t) (sampleAddr + temp * 9), t0 * 9, flags, &synthesisState->sampleDmaIndex); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); } #else + // HACKERSM64_TODO: Is the EU thing above applicable to US? Could potentially save some resources. temp = (note->samplePosInt - s2 + 0x10) / 16; + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA); + v0_2 = dma_sample_data( (uintptr_t) (sampleAddr + temp * 9), t0 * 9, flags, ¬e->sampleDmaIndex); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); #endif a3 = (u32)((uintptr_t) v0_2 & 0xf); aSetBuffer(cmd++, 0, DMEM_ADDR_COMPRESSED_ADPCM_DATA, 0, t0 * 9 + a3); @@ -1197,16 +1318,18 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { noteSubEu->needsInit = FALSE; } - cmd = final_resample(cmd, synthesisState, bufLen * 2, resamplingRateFixedPoint, - noteSamplesDmemAddrBeforeResampling, flags); + // final resample + aSetBuffer(cmd++, /*flags*/ 0, noteSamplesDmemAddrBeforeResampling, /*dmemout*/ DMEM_ADDR_TEMP, bufLen * 2); + aResample(cmd++, flags, resamplingRateFixedPoint, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->finalResampleState)); #else if (note->needsInit == TRUE) { flags = A_INIT; note->needsInit = FALSE; } - cmd = final_resample(cmd, note, bufLen * 2, resamplingRateFixedPoint, - noteSamplesDmemAddrBeforeResampling, flags); + // final resample + aSetBuffer(cmd++, /*flags*/ 0, noteSamplesDmemAddrBeforeResampling, /*dmemout*/ DMEM_ADDR_TEMP, bufLen * 2); + aResample(cmd++, flags, resamplingRateFixedPoint, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->finalResampleState)); #endif #ifdef VERSION_EU @@ -1224,11 +1347,13 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { leftRight = 0; } + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); #ifdef VERSION_EU cmd = process_envelope(cmd, noteSubEu, synthesisState, bufLen, 0, leftRight, flags); #else cmd = process_envelope(cmd, note, bufLen, 0, leftRight, flags); #endif + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); #ifdef VERSION_EU if (noteSubEu->usesHeadsetPanEffects) { @@ -1293,20 +1418,6 @@ u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad) { } #endif -#ifdef VERSION_EU -u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags) { - aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); - aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->finalResampleState)); - return cmd; -} -#else -u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags) { - aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); - aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->finalResampleState)); - return cmd; -} -#endif - #ifndef VERSION_EU u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, UNUSED u32 flags) { @@ -1406,8 +1517,24 @@ u64 *process_envelope(u64 *cmd, struct NoteSubEu *note, struct NoteSynthesisStat rampLeft = gCurrentLeftVolRamping[targetLeft >> 5] * gCurrentRightVolRamping[sourceLeft >> 5]; rampRight = gCurrentLeftVolRamping[targetRight >> 5] * gCurrentRightVolRamping[sourceRight >> 5]; #else - rampLeft = get_volume_ramping(vol->sourceLeft, vol->targetLeft, nSamples); - rampRight = get_volume_ramping(vol->sourceRight, vol->targetRight, nSamples); + // volume ramping + // This roughly computes 2^16 * (targetVol / sourceVol) ^ (8 / arg2), + // but with discretizations of targetVol, sourceVol and arg2. + switch (nSamples) { + case 128: + rampLeft = gVolRampingLhs128[vol->targetLeft >> 8] * gVolRampingRhs128[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs128[vol->targetRight >> 8] * gVolRampingRhs128[vol->sourceRight >> 8]; + break; + case 144: + rampLeft = gVolRampingLhs144[vol->targetLeft >> 8] * gVolRampingRhs144[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs144[vol->targetRight >> 8] * gVolRampingRhs144[vol->sourceRight >> 8]; + break; + case 136: + default: + rampLeft = gVolRampingLhs136[vol->targetLeft >> 8] * gVolRampingRhs136[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs136[vol->targetRight >> 8] * gVolRampingRhs136[vol->sourceRight >> 8]; + break; + } #endif // The operation's parameters change meanings depending on flags diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 79592b26..c6564e3c 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -19,14 +19,37 @@ #ifdef BETTER_REVERB +#define REVERB_WINDOW_SIZE_MAX 0x2000 + + +/* ------------ BETTER REVERB GENERAL PARAMETERS ------------ */ + #define NUM_ALLPASS 12 // Maximum number of delay filters to use with better reverb; do not change this value if you don't know what you're doing. #define BETTER_REVERB_PTR_SIZE ALIGN16(NUM_ALLPASS * sizeof(s32*) * 2) // Allocation space consumed by dynamically allocated pointers // Size determined by (all delaysL/R values * 8) / (2 ^ Minimum Downsample Factor). -// The default value can be increased or decreased in conjunction with the values in delaysL/R -#define BETTER_REVERB_SIZE ALIGN16(0x1E000 + BETTER_REVERB_PTR_SIZE) // This can be significantly decreased if a downsample rate of 1 is not being used. +// The default value can be increased or decreased in conjunction with the values in delaysL/R. +// This can be significantly decreased if a downsample rate of 1 is not being used or if filter count is less than NUM_ALLPASS, +// as this default is configured to handle the emulator RCVI settings. +#define BETTER_REVERB_SIZE ALIGN16(0x1E000 + BETTER_REVERB_PTR_SIZE) + +/* ------ BETTER REVERB LIGHTWEIGHT PARAMETER OVERRIDES ------ */ + +// Filter count works differently than normal when used with light settings and can support numbers that are not multiples of 3, though 3 is generally recommended. +// This can be reduced to 2 to save a third of runtime overhead, but substantially reduces reverb saturation. +// Similarly this can be increased from 3, but likely won't have beneficial outcomes worth the runtime expense compared to the modification of other parameters without using light settings. +#define BETTER_REVERB_FILTER_COUNT_LIGHT 3 +#define BETTER_REVERB_GAIN_INDEX_LIGHT 0xA0 // Advanced parameter; used to tune the outputs of every filter except for the final one +#define BETTER_REVERB_REVERB_INDEX_LIGHT 0x30 // Advanced parameter; used to tune the incoming output of the final filter +#define BETTER_REVERB_MULTIPLE_LIGHT 0xD0 // Advanced parameter; multiplier applied to the final output signal for both the left and right channels (divided by 256) + + +/* ------------ BETTER REVERB EXTERNED VARIABLES ------------ */ + +extern u8 toggleBetterReverb; extern u8 gBetterReverbPreset; +extern u8 betterReverbLightweight; extern s8 betterReverbDownsampleRate; extern u8 monoReverb; extern s32 reverbFilterCount; @@ -36,10 +59,18 @@ extern s32 betterReverbGainIndex; extern s32 *gReverbMultsL; extern s32 *gReverbMultsR; -extern u8 toggleBetterReverb; -#define REVERB_WINDOW_SIZE_MAX 0x2000 + +/* ------------ BETTER REVERB EXTERNED FUNCTIONS ------------ */ + +void initialize_better_reverb_buffers(void); +void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR); + + +/* -------------- BETTER REVERB STATIC ASSERTS -------------- */ STATIC_ASSERT(NUM_ALLPASS % 3 == 0, "NUM_ALLPASS must be a multiple of 3!"); +STATIC_ASSERT(BETTER_REVERB_FILTER_COUNT_LIGHT >= 2, "BETTER_REVERB_FILTER_COUNT_LIGHT should be no less than 2!"); +STATIC_ASSERT(BETTER_REVERB_FILTER_COUNT_LIGHT <= NUM_ALLPASS, "BETTER_REVERB_FILTER_COUNT_LIGHT cannot be larger than NUM_ALLPASS!"); #else @@ -137,11 +168,6 @@ extern struct SynthesisReverb gSynthesisReverb; extern s16 D_SH_803479B4; #endif -#ifdef BETTER_REVERB -void initialize_better_reverb_buffers(void); -void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR); -#endif - u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen); #if defined(VERSION_JP) || defined(VERSION_US) void note_init_volume(struct Note *note); diff --git a/src/boot/memory.c b/src/boot/memory.c index 3703589b..5d06b47c 100644 --- a/src/boot/memory.c +++ b/src/boot/memory.c @@ -129,7 +129,7 @@ void main_pool_init(void *start, void *end) { sPoolListHeadL->next = NULL; sPoolListHeadR->prev = NULL; sPoolListHeadR->next = NULL; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG mempool = sPoolFreeSpace; #endif } @@ -334,8 +334,9 @@ void *load_segment(s32 segment, u8 *srcStart, u8 *srcEnd, u32 side, u8 *bssStart set_segment_base_addr(segment, addr); } } -#if PUPPYPRINT_DEBUG - ramsizeSegment[(segment + nameTable) - 2] = ((s32)srcEnd - (s32)srcStart); +#ifdef PUPPYPRINT_DEBUG + u32 ppSize = ALIGN16(srcEnd - srcStart) + 16; + set_segment_memory_printout(segment, ppSize); #endif return addr; } @@ -411,8 +412,9 @@ void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd) { main_pool_free(compressed); } } -#if PUPPYPRINT_DEBUG - ramsizeSegment[(segment + nameTable) - 2] = (s32)srcEnd - (s32)srcStart; +#ifdef PUPPYPRINT_DEBUG + u32 ppSize = ALIGN16((u32)*size) + 16; + set_segment_memory_printout(segment, ppSize); #endif return dest; } @@ -543,6 +545,9 @@ struct MemoryPool *mem_pool_init(u32 size, u32 side) { block->next = NULL; block->size = pool->totalSpace; } +#ifdef PUPPYPRINT_DEBUG + gPoolMem += ALIGN16(size) + 16; +#endif return pool; } diff --git a/src/buffers/buffers.h b/src/buffers/buffers.h index 064dac6b..941df2e0 100644 --- a/src/buffers/buffers.h +++ b/src/buffers/buffers.h @@ -8,10 +8,11 @@ #include "game/game_init.h" #include "game/main.h" #include "config.h" +#include "audio/data.h" extern u8 gDecompressionHeap[]; -extern u8 gAudioHeap[]; +extern u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE)]; extern u8 gIdleThreadStack[THREAD1_STACK]; extern u8 gThread3Stack[THREAD3_STACK]; diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index 7b63aa86..5bc76f69 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -820,13 +820,11 @@ void cur_obj_update(void) { BhvCommandProc bhvCmdProc; s32 bhvProcResult; - s32 inRoom = is_mario_in_room(); + s32 inRoom = cur_obj_is_mario_in_room(); - if (!(objFlags & OBJ_FLAG_PROCESS_OUTSIDE_ROOM)) { - if (inRoom == MARIO_OUTSIDE_ROOM) { - cur_obj_enable_disable_room_rendering(MARIO_OUTSIDE_ROOM); - return; - } + if (inRoom == MARIO_OUTSIDE_ROOM && (objFlags & OBJ_FLAG_ONLY_PROCESS_INSIDE_ROOM)) { + cur_obj_disable_rendering_in_room(); + return; } // Calculate the distance from the object to Mario. @@ -932,7 +930,11 @@ void cur_obj_update(void) { (objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR) || distanceFromMario < o->oDrawingDistance ) { - cur_obj_enable_disable_room_rendering(inRoom); + if (inRoom == MARIO_OUTSIDE_ROOM) { + cur_obj_disable_rendering_in_room(); + } else if (inRoom == MARIO_INSIDE_ROOM) { + cur_obj_enable_rendering_in_room(); + } o->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; } else { o->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; diff --git a/src/engine/level_script.c b/src/engine/level_script.c index 1de2a462..8b41a2d9 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -347,6 +347,9 @@ void unmap_tlbs(void) { osUnmapTLB(gTlbEntries); gTlbSegments[i]--; gTlbEntries--; +#ifdef PUPPYPRINT_DEBUG + set_segment_memory_printout(i, 0); +#endif } } else { gTlbEntries -= gTlbSegments[i]; @@ -823,7 +826,7 @@ static void level_cmd_puppyvolume(void) { if ((sPuppyVolumeStack[gPuppyVolumeCount] = mem_pool_alloc(gPuppyMemoryPool, sizeof(struct sPuppyVolume))) == NULL) { sCurrentCmd = CMD_NEXT; gPuppyError |= PUPPY_ERROR_POOL_FULL; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Puppycamera volume allocation failed."); #endif return; @@ -873,7 +876,7 @@ static void level_cmd_puppylight_node(void) { #ifdef PUPPYLIGHTS gPuppyLights[gNumLights] = mem_pool_alloc(gLightsPool, sizeof(struct PuppyLight)); if (gPuppyLights[gNumLights] == NULL) { -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Puppylight allocation failed."); #endif sCurrentCmd = CMD_NEXT; diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 9597f004..d82fd110 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -322,6 +322,7 @@ void mtxf_copy(register Mat4 dest, register Mat4 src) { /// Set mtx to the identity matrix. void mtxf_identity(register Mat4 mtx) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; f32 *dest; for (dest = ((f32 *) mtx + 1), i = 0; i < 14; dest++, i++) { @@ -334,6 +335,7 @@ void mtxf_identity(register Mat4 mtx) { /// Set dest to a translation matrix of vector b. void mtxf_translate(Mat4 dest, Vec3f b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i; register f32 *pen; for (pen = ((f32 *) dest + 1), i = 0; i < 12; pen++, i++) { @@ -354,6 +356,7 @@ void mtxf_translate(Mat4 dest, Vec3f b) { * i.e. a matrix representing a linear transformation over 3 space. */ void linear_mtxf_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = ((m[0][i] * v[0]) @@ -363,6 +366,7 @@ void linear_mtxf_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { } void linear_mtxf_mul_vec3f_and_translate(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = ((m[0][i] * v[0]) @@ -381,6 +385,7 @@ void linear_mtxf_mul_vec3f_and_translate(Mat4 m, Vec3f dst, Vec3f v) { * i.e. a matrix representing a linear transformation over 3 space. */ void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = vec3_dot(m[i], v); @@ -389,6 +394,7 @@ void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { /// Build a matrix that rotates around the z axis, then the x axis, then the y axis, and then translates. void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -414,6 +420,7 @@ void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { /// Build a matrix that rotates around the x axis, then the y axis, then the z axis, and then translates. UNUSED void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -439,6 +446,7 @@ UNUSED void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { /// Build a matrix that rotates around the z axis, then the x axis, then the y axis, and then translates and multiplies. void mtxf_rotate_zxy_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -469,6 +477,7 @@ void mtxf_rotate_zxy_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Ma /// Build a matrix that rotates around the x axis, then the y axis, then the z axis, and then translates and multiplies. void mtxf_rotate_xyz_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -504,6 +513,7 @@ void mtxf_rotate_xyz_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Ma * angle allows a bank rotation of the camera. */ void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f colX, colY, colZ; register f32 dx = (to[0] - from[0]); register f32 dz = (to[2] - from[2]); @@ -544,6 +554,7 @@ void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) { * 'angle' rotates the object while still facing the camera. */ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i; register f32 sx = scale[0]; register f32 sy = scale[1]; @@ -591,6 +602,7 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle) * 'yaw' is the angle which it should face */ void mtxf_shadow(Mat4 dest, Vec3f upDir, Vec3f pos, Vec3f scale, s16 yaw) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f lateralDir; Vec3f leftDir; Vec3f forwardDir; @@ -616,6 +628,7 @@ void mtxf_shadow(Mat4 dest, Vec3f upDir, Vec3f pos, Vec3f scale, s16 yaw) { * 'pos' is the object's position in the world */ void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f lateralDir; Vec3f leftDir; Vec3f forwardDir; @@ -641,6 +654,7 @@ void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) { * 'radius' is the distance from each triangle vertex to the center */ void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); struct Surface *floor; Vec3f point0, point1, point2; Vec3f forward; @@ -691,6 +705,7 @@ void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) { * then a. */ void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f entry; register f32 *temp = (f32 *)a; register f32 *temp2 = (f32 *)dest; @@ -717,6 +732,7 @@ void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) { * Set matrix 'dest' to 'mtx' scaled by vector s */ void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, register Vec3f s) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 *temp = (f32 *)dest; register f32 *temp2 = (f32 *)mtx; register s32 i; @@ -737,6 +753,7 @@ void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, register Vec3f s) { * true for transformation matrices if the translation has a w component of 1. */ UNUSED void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 x = b[0]; register f32 y = b[1]; register f32 z = b[2]; @@ -760,6 +777,7 @@ UNUSED void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { ((s16 *) mtx)[a ] = (((s32) b) >> 16); \ ((s16 *) mtx)[a + 16] = (((s32) b) & 0xFFFF); void mtxf_rotate_xy(Mtx *mtx, s16 angle) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i = (coss(angle) * 0x10000); register s32 j = (sins(angle) * 0x10000); register f32 *temp = (f32 *)mtx; @@ -1264,7 +1282,7 @@ s32 anim_spline_poll(Vec3f result) { /** * @brief Checks if a ray intersects a surface using Möller–Trumbore intersection algorithm. - * + * * @param orig is the starting point of the ray. * @param dir is the normalized ray direction. * @param dir_length is the length of the ray. @@ -1312,7 +1330,7 @@ s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface f32 u = f * vec3f_dot(s, h); // Check if 'u' is within bounds. if ((u < 0.0f) || (u > 1.0f)) return FALSE; - // Make 'q' the cross product of 's' and edge 1. + // Make 'q' the cross product of 's' and edge 1. Vec3f q; vec3f_cross(q, s, e1); // Make 'v' the cos(angle) between the ray and 'q', divided by 'det'. @@ -1338,6 +1356,7 @@ void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f f32 length; Vec3f chk_hit_pos; f32 top, bottom; + PUPPYPRINT_GET_SNAPSHOT(); // Get upper and lower bounds of ray if (dir[1] >= 0.0f) { // Ray is upwards. @@ -1361,6 +1380,7 @@ void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f *max_length = length; } } + profiler_collision_update(first); } void find_surface_on_ray_cell(s32 cellX, s32 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length, s32 flags) { @@ -1391,6 +1411,7 @@ void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Ve f32 step; s32 i; const f32 invcell = 1.0f / CELL_SIZE; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_raycast); // Set that no surface has been hit *hit_surface = NULL; @@ -1488,6 +1509,7 @@ static ALWAYS_INLINE float construct_float(const float f) // Converts a floating point matrix to a fixed point matrix // Makes some assumptions about certain fields in the matrix, which will always be true for valid matrices. OPTIMIZE_OS void mtxf_to_mtx_fast(s16* dst, float* src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); float scale = construct_float(65536.0f / WORLD_SCALE); // Iterate over pairs of values in the input matrix for (int i = 0; i < 8; i++) diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index a597b8e7..007c5748 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -205,10 +205,13 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { s32 numCollisions = 0; s32 x = colData->x; s32 z = colData->z; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_wall); + PUPPYPRINT_GET_SNAPSHOT(); colData->numWalls = 0; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return numCollisions; } @@ -232,6 +235,7 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { gNumCalls.wall++; #endif + profiler_collision_update(first); return numCollisions; } @@ -347,12 +351,15 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) { f32 height = CELL_HEIGHT_LIMIT; f32 dynamicHeight = CELL_HEIGHT_LIMIT; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_ceil); + PUPPYPRINT_GET_SNAPSHOT(); s32 x = posX; s32 y = posY; s32 z = posZ; *pceil = NULL; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return height; } @@ -395,6 +402,7 @@ f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) { gNumCalls.ceil++; #endif + profiler_collision_update(first); return height; } @@ -575,6 +583,9 @@ f32 unused_find_dynamic_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfl * Find the highest floor under a given position and return the height. */ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_floor); + PUPPYPRINT_GET_SNAPSHOT(); + f32 height = FLOOR_LOWER_LIMIT; f32 dynamicHeight = FLOOR_LOWER_LIMIT; @@ -588,6 +599,7 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { *pfloor = NULL; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return height; } // Each level is split into cells to limit load, find the appropriate cell. @@ -632,11 +644,14 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { // Increment the debug tracker. gNumCalls.floor++; #endif + + profiler_collision_update(first); return height; } f32 find_room_floor(f32 x, f32 y, f32 z, struct Surface **pfloor) { - gCollisionFlags |= (COLLISION_FLAG_RETURN_FIRST | COLLISION_FLAG_EXCLUDE_DYNAMIC | COLLISION_FLAG_INCLUDE_INTANGIBLE); + gCollisionFlags |= (COLLISION_FLAG_EXCLUDE_DYNAMIC | COLLISION_FLAG_INCLUDE_INTANGIBLE); + return find_floor(x, y, z, pfloor); } @@ -646,11 +661,14 @@ f32 find_room_floor(f32 x, f32 y, f32 z, struct Surface **pfloor) { s32 get_room_at_pos(f32 x, f32 y, f32 z) { if (gCurrentArea->surfaceRooms != NULL) { struct Surface *floor; + find_room_floor(x, y, z, &floor); - if (floor) { + + if (floor != NULL) { return floor->room; } } + return -1; } @@ -698,6 +716,8 @@ s32 find_water_level_and_floor(s32 x, s32 y, s32 z, struct Surface **pfloor) { s32 loX, hiX, loZ, hiZ; TerrainData *p = gEnvironmentRegions; struct Surface *floor = NULL; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); s32 waterLevel = find_water_floor(x, y, z, &floor); if (p != NULL && waterLevel == FLOOR_LOWER_LIMIT) { @@ -723,6 +743,7 @@ s32 find_water_level_and_floor(s32 x, s32 y, s32 z, struct Surface **pfloor) { *pfloor = floor; } + profiler_collision_update(first); return waterLevel; } @@ -734,6 +755,8 @@ s32 find_water_level(s32 x, s32 z) { // TODO: Allow y pos s32 loX, hiX, loZ, hiZ; TerrainData *p = gEnvironmentRegions; struct Surface *floor = NULL; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); s32 waterLevel = find_water_floor(x, ((gCollisionFlags & COLLISION_FLAG_CAMERA) ? gLakituState.pos[1] : gMarioState->pos[1]), z, &floor); if ((p != NULL) && (waterLevel == FLOOR_LOWER_LIMIT)) { @@ -757,6 +780,8 @@ s32 find_water_level(s32 x, s32 z) { // TODO: Allow y pos } } + profiler_collision_update(first); + return waterLevel; } @@ -768,6 +793,8 @@ s32 find_poison_gas_level(s32 x, s32 z) { s32 loX, hiX, loZ, hiZ; s32 gasLevel = FLOOR_LOWER_LIMIT; TerrainData *p = gEnvironmentRegions; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); if (p != NULL) { s32 numRegions = *p++; @@ -793,7 +820,8 @@ s32 find_poison_gas_level(s32 x, s32 z) { p += 6; } } - + + profiler_collision_update(first); return gasLevel; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index e4a505b9..1200d20f 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -498,6 +498,7 @@ u32 get_area_terrain_size(TerrainData *data) { * boxes (water, gas, JRB fog). */ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 *macroObjects) { + PUPPYPRINT_GET_SNAPSHOT(); s32 terrainLoadType; TerrainData *vertexData = NULL; u32 surfacePoolData; @@ -558,12 +559,14 @@ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 gNumStaticSurfaceNodes = gSurfaceNodesAllocated; gNumStaticSurfaces = gSurfacesAllocated; + profiler_collision_update(first); } /** * If not in time stop, clear the surface partitions. */ void clear_dynamic_surfaces(void) { + PUPPYPRINT_GET_SNAPSHOT(); if (!(gTimeStopState & TIME_STOP_ACTIVE)) { gSurfacesAllocated = gNumStaticSurfaces; gSurfaceNodesAllocated = gNumStaticSurfaceNodes; @@ -578,6 +581,7 @@ void clear_dynamic_surfaces(void) { sNumCellsUsed = 0; sClearAllCells = FALSE; } + profiler_collision_update(first); } /** @@ -691,6 +695,7 @@ static TerrainData sVertexData[600]; * Transform an object's vertices, reload them, and render the object. */ void load_object_collision_model(void) { + PUPPYPRINT_GET_SNAPSHOT(); TerrainData *collisionData = o->collisionData; f32 marioDist = o->oDistanceToMario; @@ -727,12 +732,14 @@ void load_object_collision_model(void) { } } COND_BIT((marioDist < o->oDrawingDistance), o->header.gfx.node.flags, GRAPH_RENDER_ACTIVE); + profiler_collision_update(first); } /** * Transform an object's vertices and add them to the static surface pool. */ void load_object_static_model(void) { + PUPPYPRINT_GET_SNAPSHOT(); TerrainData *collisionData = o->collisionData; u32 surfacePoolData; @@ -756,4 +763,5 @@ void load_object_static_model(void) { gNumStaticSurfaceNodes = gSurfaceNodesAllocated; gNumStaticSurfaces = gSurfacesAllocated; + profiler_collision_update(first); } diff --git a/src/game/area.c b/src/game/area.c index 3b5b851f..0072563a 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -376,10 +376,14 @@ void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 b } void render_game(void) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) { if (gCurrentArea->graphNode) { geo_process_root(gCurrentArea->graphNode, gViewportOverride, gViewportClip, gFBSetColor); } +#ifdef PUPPYPRINT + bzero(gCurrEnvCol, sizeof(ColorRGBA)); +#endif gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gViewport)); @@ -389,6 +393,9 @@ void render_game(void) { gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); render_text_labels(); +#ifdef PUPPYPRINT + puppyprint_print_deferred(); +#endif do_cutscene_handler(); print_displaying_credits_entry(); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, gBorderHeight, SCREEN_WIDTH, @@ -422,6 +429,9 @@ void render_game(void) { } } else { render_text_labels(); +#ifdef PUPPYPRINT + puppyprint_print_deferred(); +#endif if (gViewportClip != NULL) { clear_viewport(gViewportClip, gWarpTransFBSetColor); } else { @@ -431,11 +441,10 @@ void render_game(void) { gViewportOverride = NULL; gViewportClip = NULL; - - profiler_update(PROFILER_TIME_GFX); - profiler_print_times(); -#if PUPPYPRINT_DEBUG + profiler_update(PROFILER_TIME_GFX, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); + profiler_print_times(); +#ifdef PUPPYPRINT_DEBUG puppyprint_render_profiler(); #endif } diff --git a/src/game/behaviors/door.inc.c b/src/game/behaviors/door.inc.c index c75bf238..17c84832 100644 --- a/src/game/behaviors/door.inc.c +++ b/src/game/behaviors/door.inc.c @@ -65,6 +65,7 @@ void bhv_door_loop(void) { switch (o->oAction) { case DOOR_ACT_CLOSED: cur_obj_init_animation_with_sound(DOOR_ANIM_CLOSED); + load_object_collision_model(); break; case DOOR_ACT_PULLED: door_animation_and_reset(DOOR_ANIM_PULLED); @@ -83,49 +84,55 @@ void bhv_door_loop(void) { play_warp_door_open_noise(); break; } - if (o->oAction == DOOR_ACT_CLOSED) { - load_object_collision_model(); - } + bhv_door_rendering_loop(); } void bhv_door_init(void) { + const f32 checkDist = 200.0f; + f32 x = o->oPosX; f32 y = o->oPosY; f32 z = o->oPosZ; - struct Surface *floor; - find_room_floor(x, y, z, &floor); - if (floor != NULL) o->oDoorSelfRoom = floor->room; + o->oDoorSelfRoom = get_room_at_pos(x, y, z); - x = o->oPosX + sins(o->oMoveAngleYaw) * 200.0f; - z = o->oPosZ + coss(o->oMoveAngleYaw) * 200.0f; + x = o->oPosX + (sins(o->oMoveAngleYaw) * checkDist); + z = o->oPosZ + (coss(o->oMoveAngleYaw) * checkDist); - find_room_floor(x, y, z, &floor); - if (floor != NULL) o->oDoorForwardRoom = floor->room; + o->oDoorForwardRoom = get_room_at_pos(x, y, z); - x = o->oPosX + sins(o->oMoveAngleYaw) * -200.0f; - z = o->oPosZ + coss(o->oMoveAngleYaw) * -200.0f; + x = o->oPosX + (sins(o->oMoveAngleYaw) * -checkDist); + z = o->oPosZ + (coss(o->oMoveAngleYaw) * -checkDist); - find_room_floor(x, y, z, &floor); - if (floor != NULL) o->oDoorBackwardRoom = floor->room; + o->oDoorBackwardRoom = get_room_at_pos(x, y, z); - if (o->oDoorSelfRoom > 0 && o->oDoorSelfRoom < 60) { - gDoorAdjacentRooms[o->oDoorSelfRoom][0] = o->oDoorForwardRoom; - gDoorAdjacentRooms[o->oDoorSelfRoom][1] = o->oDoorBackwardRoom; + if ( + // Ensure the room number is in bounds. + o->oDoorSelfRoom > 0 && o->oDoorSelfRoom < ARRAY_COUNT(gDoorAdjacentRooms) + // Only set gDoorAdjacentRooms for transition rooms. + && o->oDoorSelfRoom != o->oDoorForwardRoom + && o->oDoorSelfRoom != o->oDoorBackwardRoom + && o->oDoorForwardRoom != o->oDoorBackwardRoom + ) { + gDoorAdjacentRooms[o->oDoorSelfRoom].forwardRoom = o->oDoorForwardRoom; + gDoorAdjacentRooms[o->oDoorSelfRoom].backwardRoom = o->oDoorBackwardRoom; } } void bhv_door_rendering_loop(void) { + struct TransitionRoomData* transitionRoom = &gDoorAdjacentRooms[gMarioCurrentRoom]; + o->oDoorIsRendering = ( - gMarioCurrentRoom == 0 - || gMarioCurrentRoom == o->oDoorSelfRoom - || gMarioCurrentRoom == o->oDoorForwardRoom - || gMarioCurrentRoom == o->oDoorBackwardRoom - || gDoorAdjacentRooms[gMarioCurrentRoom][0] == o->oDoorForwardRoom - || gDoorAdjacentRooms[gMarioCurrentRoom][0] == o->oDoorBackwardRoom - || gDoorAdjacentRooms[gMarioCurrentRoom][1] == o->oDoorForwardRoom - || gDoorAdjacentRooms[gMarioCurrentRoom][1] == o->oDoorBackwardRoom + gMarioCurrentRoom == 0 || // Mario is in the "global" room. + gMarioCurrentRoom == o->oDoorSelfRoom || // Mario is in the same room as the door. + gMarioCurrentRoom == o->oDoorForwardRoom || // Mario is in the door's forward room. + gMarioCurrentRoom == o->oDoorBackwardRoom || // Mario is in the door's backward room. + transitionRoom->forwardRoom == o->oDoorForwardRoom || // The transition room's forward room is in the same room as this door's forward room. + transitionRoom->forwardRoom == o->oDoorBackwardRoom || // The transition room's forward room is in the same room as this door's backward room. + transitionRoom->backwardRoom == o->oDoorForwardRoom || // The transition room's backward room is in the same room as this door's forward room. + transitionRoom->backwardRoom == o->oDoorBackwardRoom // The transition room's backward room is in the same room as this door's backward room. ); + COND_BIT(o->oDoorIsRendering, o->header.gfx.node.flags, GRAPH_RENDER_ACTIVE); } diff --git a/src/game/behaviors/exclamation_box.inc.c b/src/game/behaviors/exclamation_box.inc.c index 34738d57..f14e9722 100644 --- a/src/game/behaviors/exclamation_box.inc.c +++ b/src/game/behaviors/exclamation_box.inc.c @@ -1,42 +1,39 @@ // exclamation_box.inc.c -struct ExclamationBoxContents { - u8 id; - u8 unk1; - u8 behParams; - ModelID16 model; - const BehaviorScript *behavior; +struct ObjectHitbox sExclamationBoxHitbox = { + .interactType = INTERACT_BREAKABLE, + .downOffset = 5, + .damageOrCoinValue = 0, + .health = 1, + .numLootCoins = 0, + .radius = 40, + .height = 30, + .hurtboxRadius = 40, + .hurtboxHeight = 30, }; -struct ObjectHitbox sExclamationBoxHitbox = { - /* interactType: */ INTERACT_BREAKABLE, - /* downOffset: */ 5, - /* damageOrCoinValue: */ 0, - /* health: */ 1, - /* numLootCoins: */ 0, - /* radius: */ 40, - /* height: */ 30, - /* hurtboxRadius: */ 40, - /* hurtboxHeight: */ 30, +struct ExclamationBoxContents { + ModelID32 model; + const BehaviorScript *behavior; + u8 behParam; }; struct ExclamationBoxContents sExclamationBoxContents[] = { - { EXCLAMATION_BOX_BP_WING_CAP, 0, 0, MODEL_MARIOS_WING_CAP, bhvWingCap }, - { EXCLAMATION_BOX_BP_METAL_CAP, 0, 0, MODEL_MARIOS_METAL_CAP, bhvMetalCap }, - { EXCLAMATION_BOX_BP_VANISH_CAP, 0, 0, MODEL_MARIOS_CAP, bhvVanishCap }, - { EXCLAMATION_BOX_BP_KOOPA_SHELL, 0, 0, MODEL_KOOPA_SHELL, bhvKoopaShell }, - { EXCLAMATION_BOX_BP_COINS_1, 0, 0, MODEL_YELLOW_COIN, bhvSingleCoinGetsSpawned }, - { EXCLAMATION_BOX_BP_COINS_3, 0, 0, MODEL_NONE, bhvThreeCoinsSpawn }, - { EXCLAMATION_BOX_BP_COINS_10, 0, 0, MODEL_NONE, bhvTenCoinsSpawn }, - { EXCLAMATION_BOX_BP_1UP_WALKING, 0, 0, MODEL_1UP, bhv1upWalking }, - { EXCLAMATION_BOX_BP_STAR_1, 0, 0, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_1UP_RUNNING_AWAY, 0, 0, MODEL_1UP, bhv1upRunningAway }, - { EXCLAMATION_BOX_BP_STAR_2, 0, 1, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_STAR_3, 0, 2, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_STAR_4, 0, 3, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_STAR_5, 0, 4, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_STAR_6, 0, 5, MODEL_STAR, bhvSpawnedStar }, - { EXCLAMATION_BOX_BP_NULL, 0, 0, MODEL_NONE, NULL } + [EXCLAMATION_BOX_BP_WING_CAP ] = { MODEL_MARIOS_WING_CAP, bhvWingCap, 0 }, + [EXCLAMATION_BOX_BP_METAL_CAP ] = { MODEL_MARIOS_METAL_CAP, bhvMetalCap, 0 }, + [EXCLAMATION_BOX_BP_VANISH_CAP ] = { MODEL_MARIOS_CAP, bhvVanishCap, 0 }, + [EXCLAMATION_BOX_BP_KOOPA_SHELL ] = { MODEL_KOOPA_SHELL, bhvKoopaShell, 0 }, + [EXCLAMATION_BOX_BP_COINS_1 ] = { MODEL_YELLOW_COIN, bhvSingleCoinGetsSpawned, 0 }, + [EXCLAMATION_BOX_BP_COINS_3 ] = { MODEL_NONE, bhvThreeCoinsSpawn, 0 }, + [EXCLAMATION_BOX_BP_COINS_10 ] = { MODEL_NONE, bhvTenCoinsSpawn, 0 }, + [EXCLAMATION_BOX_BP_1UP_WALKING ] = { MODEL_1UP, bhv1upWalking, 0 }, + [EXCLAMATION_BOX_BP_STAR_1 ] = { MODEL_STAR, bhvSpawnedStar, 0 }, + [EXCLAMATION_BOX_BP_1UP_RUNNING_AWAY] = { MODEL_1UP, bhv1upRunningAway, 0 }, + [EXCLAMATION_BOX_BP_STAR_2 ] = { MODEL_STAR, bhvSpawnedStar, 1 }, + [EXCLAMATION_BOX_BP_STAR_3 ] = { MODEL_STAR, bhvSpawnedStar, 2 }, + [EXCLAMATION_BOX_BP_STAR_4 ] = { MODEL_STAR, bhvSpawnedStar, 3 }, + [EXCLAMATION_BOX_BP_STAR_5 ] = { MODEL_STAR, bhvSpawnedStar, 4 }, + [EXCLAMATION_BOX_BP_STAR_6 ] = { MODEL_STAR, bhvSpawnedStar, 5 }, }; void bhv_rotating_exclamation_mark_loop(void) { @@ -49,16 +46,12 @@ void exclamation_box_act_init(void) { if (o->oBehParams2ndByte < EXCLAMATION_BOX_BP_KOOPA_SHELL) { o->oAnimState = o->oBehParams2ndByte; #ifdef UNLOCK_ALL - u8 tangible = TRUE; + const u8 tangible = TRUE; #else - u8 tangible = ((save_file_get_flags() & sCapSaveFlags[o->oBehParams2ndByte]) - || (GET_BPARAM1(o->oBehParams) != EXCLAMATION_BOX_BP1_NEEDS_SWITCH)); + const u8 tangible = ((save_file_get_flags() & sCapSaveFlags[o->oBehParams2ndByte]) + || (GET_BPARAM1(o->oBehParams) != EXCLAMATION_BOX_BP1_NEEDS_SWITCH)); #endif - if (tangible) { - o->oAction = EXCLAMATION_BOX_ACT_ACTIVE; - } else { - o->oAction = EXCLAMATION_BOX_ACT_OUTLINE; - } + o->oAction = tangible ? EXCLAMATION_BOX_ACT_ACTIVE : EXCLAMATION_BOX_ACT_OUTLINE; } else { o->oAnimState = EXCLAMATION_BOX_ANIM_STATE_YELLOW; o->oAction = EXCLAMATION_BOX_ACT_ACTIVE; @@ -107,13 +100,15 @@ void exclamation_box_act_scaling(void) { o->oVelY = 0.0f; o->oGravity = 0.0f; } - o->oExclamationBoxVerticalScale = (sins(o->oExclamationBoxScaleAngle) + 1.0f) * 0.3f + 0.0f; - o->oExclamationBoxHorizontalScale = (-sins(o->oExclamationBoxScaleAngle) + 1.0f) * 0.5f + 1.0f; - o->oGraphYOffset = (-sins(o->oExclamationBoxScaleAngle) + 1.0f) * 26.0f; + o->oGraphYOffset = ((-sins(o->oExclamationBoxScaleAngle) + 1.0f) * 26.0f); + o->oExclamationBoxHorizontalScale = ((-sins(o->oExclamationBoxScaleAngle) + 1.0f) * 0.5f) + 1.0f; + o->oExclamationBoxVerticalScale = (( sins(o->oExclamationBoxScaleAngle) + 1.0f) * 0.3f) + 0.0f; o->oExclamationBoxScaleAngle += 0x1000; - o->header.gfx.scale[0] = o->oExclamationBoxHorizontalScale * 2.0f; - o->header.gfx.scale[1] = o->oExclamationBoxVerticalScale * 2.0f; - o->header.gfx.scale[2] = o->oExclamationBoxHorizontalScale * 2.0f; + obj_scale_xyz(o, + (o->oExclamationBoxHorizontalScale * 2.0f), + (o->oExclamationBoxVerticalScale * 2.0f), + (o->oExclamationBoxHorizontalScale * 2.0f) + ); if (o->oTimer == 7) { o->oAction = EXCLAMATION_BOX_ACT_EXPLODE; } @@ -122,19 +117,17 @@ void exclamation_box_act_scaling(void) { void exclamation_box_spawn_contents(struct ExclamationBoxContents *contentsList, u8 boxType) { struct Object *contentsObj = NULL; - while (contentsList->id != EXCLAMATION_BOX_BP_NULL) { - if (boxType == contentsList->id) { - contentsObj = spawn_object(o, contentsList->model, contentsList->behavior); - contentsObj->oVelY = 20.0f; - contentsObj->oForwardVel = 3.0f; - contentsObj->oMoveAngleYaw = gMarioObject->oMoveAngleYaw; - OR_BPARAM1(o->oBehParams, contentsList->behParams); - if (contentsList->model == MODEL_STAR) { - o->oFlags |= OBJ_FLAG_PERSISTENT_RESPAWN; - } - break; + if (boxType < ARRAY_COUNT(sExclamationBoxContents)) { + struct ExclamationBoxContents *contents = &contentsList[boxType]; + + contentsObj = spawn_object(o, contents->model, contents->behavior); + contentsObj->oVelY = 20.0f; + contentsObj->oForwardVel = 3.0f; + contentsObj->oMoveAngleYaw = gMarioObject->oMoveAngleYaw; + OR_BPARAM1(o->oBehParams, contents->behParam); + if (contents->model == MODEL_STAR) { + o->oFlags |= OBJ_FLAG_PERSISTENT_RESPAWN; } - contentsList++; } } @@ -143,7 +136,8 @@ void exclamation_box_act_explode(void) { spawn_mist_particles_variable(0, 0, 46.0f); spawn_triangle_break_particles(20, MODEL_CARTOON_STAR, 0.3f, o->oAnimState); create_sound_spawner(SOUND_GENERAL_BREAK_BOX); - if (o->oBehParams2ndByte < EXCLAMATION_BOX_BP_COINS_1) { + if (o->oBehParams2ndByte <= EXCLAMATION_BOX_BP_KOOPA_SHELL) { + // Cap boxes + Koopa shell boxes. o->oAction = EXCLAMATION_BOX_ACT_WAIT_FOR_RESPAWN; cur_obj_hide(); } else { diff --git a/src/game/behaviors/koopa_shell.inc.c b/src/game/behaviors/koopa_shell.inc.c index becfbe34..a9c7364a 100644 --- a/src/game/behaviors/koopa_shell.inc.c +++ b/src/game/behaviors/koopa_shell.inc.c @@ -1,19 +1,22 @@ // koopa_shell.inc.c struct ObjectHitbox sKoopaShellHitbox = { - /* interactType: */ INTERACT_KOOPA_SHELL, - /* downOffset: */ 0, - /* damageOrCoinValue: */ 4, - /* health: */ 1, - /* numLootCoins: */ 1, - /* radius: */ 50, - /* height: */ 50, - /* hurtboxRadius: */ 50, - /* hurtboxHeight: */ 50, + .interactType = INTERACT_KOOPA_SHELL, + .downOffset = 0, + .damageOrCoinValue = 4, + .health = 1, + .numLootCoins = 1, + .radius = 50, + .height = 50, + .hurtboxRadius = 50, + .hurtboxHeight = 50, }; -void shell_despawn(void) { - if (o->oTimer > 300) obj_flicker_and_disappear(o, 300); +void check_shell_despawn(void) { + if (o->parentObj->behavior == segmented_to_virtual(bhvExclamationBox) + && o->oTimer > 300) { + obj_flicker_and_disappear(o, 300); + } } void koopa_shell_spawn_water_drop(void) { @@ -77,7 +80,7 @@ void bhv_koopa_shell_loop(void) { o->oFaceAngleYaw += 0x1000; cur_obj_move_standard(-20); koopa_shell_spawn_sparkles(10.0f); - shell_despawn(); + check_shell_despawn(); break; case KOOPA_SHELL_ACT_MARIO_RIDING: diff --git a/src/game/camera.c b/src/game/camera.c index f9bf782c..cd4f7ee1 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -27,6 +27,7 @@ #include "level_table.h" #include "config.h" #include "puppyprint.h" +#include "profiling.h" #define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS) @@ -2865,12 +2866,12 @@ void update_lakitu(struct Camera *c) { gLakituState.defMode = c->defMode; } - /** * The main camera update function. * Gets controller input, checks for cutscenes, handles mode changes, and moves the camera */ void update_camera(struct Camera *c) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); gCamera = c; update_camera_hud_status(c); if (c->cutscene == CUTSCENE_NONE @@ -3113,6 +3114,7 @@ void update_camera(struct Camera *c) { } #endif gLakituState.lastFrameAction = sMarioCamState->action; + profiler_update(PROFILER_TIME_CAMERA, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } /** @@ -11023,7 +11025,9 @@ Gfx *geo_camera_fov(s32 callContext, struct GraphNode *g, UNUSED void *context) case CAM_FOV_APP_60: approach_fov_60(marioState); break; - //! No default case + default: + set_fov_45(marioState); + break; } } diff --git a/src/game/crash_screen.c b/src/game/crash_screen.c index b7fccab4..c716856d 100644 --- a/src/game/crash_screen.c +++ b/src/game/crash_screen.c @@ -18,7 +18,7 @@ enum crashPages { PAGE_CONTEXT, -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG PAGE_LOG, #endif PAGE_STACKTRACE, @@ -233,7 +233,7 @@ void draw_crash_context(OSThread *thread, s32 cause) { } -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void draw_crash_log(void) { s32 i; crash_screen_draw_rect(25, 20, 270, 210); @@ -361,7 +361,7 @@ void draw_crash_screen(OSThread *thread) { crash_screen_print(30, 10, "Page:%02d L/Z: Left R: Right", crashPage); switch (crashPage) { case PAGE_CONTEXT: draw_crash_context(thread, cause); break; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG case PAGE_LOG: draw_crash_log(); break; #endif case PAGE_STACKTRACE: draw_stacktrace(thread, cause); break; diff --git a/src/game/fasttext.c b/src/game/fasttext.c index 3cb5bf9c..1332c2ac 100644 --- a/src/game/fasttext.c +++ b/src/game/fasttext.c @@ -38,7 +38,7 @@ int computeS(unsigned char letter) { static const u8 fast_text_font_kerning[] = { /* */ 2, /*!*/ 4, /*"*/ 5, /*#*/ 0, /*$*/ 0, /*%*/ 7, /*&*/ 7, /*'*/ 2, /*(*/ 5, /*)*/ 5, /***/ 0, /*+*/ 7, /*,*/ 3, /*-*/ 7, /*.*/ 3, /*/*/ 7, /*0*/ 7, /*1*/ 7, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 7, /*7*/ 7, /*8*/ 7, /*9*/ 7, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 7, - /*@*/ 0, /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 7, /*F*/ 7, /*G*/ 7, /*H*/ 7, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 7, /*M*/ 7, /*N*/ 7, /*O*/ 7, + /*@*/ 0, /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 7, /*F*/ 7, /*G*/ 7, /*H*/ 7, /*I*/ 6, /*J*/ 7, /*K*/ 7, /*L*/ 7, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 7, /*Q*/ 7, /*R*/ 7, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 7, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*[*/ 0, /*\*/ 0, /*]*/ 0, /*^*/ 6, /*_*/ 0, /*`*/ 0, /*a*/ 6, /*b*/ 6, /*c*/ 6, /*d*/ 6, /*e*/ 6, /*f*/ 6, /*g*/ 6, /*h*/ 6, /*i*/ 2, /*j*/ 6, /*k*/ 5, /*l*/ 3, /*m*/ 6, /*n*/ 6, /*o*/ 6, /*p*/ 6, /*q*/ 6, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 6, /*w*/ 6, /*x*/ 6, /*y*/ 6, /*z*/ 6, /*{*/ 0, /*|*/ 0, /*}*/ 0, /*~*/ 7, @@ -58,6 +58,7 @@ void drawSmallString_impl(Gfx **dl, int x, int y, const char* string, int r, int while (string[i] != '\0') { unsigned int cur_char = string[i]; + s32 goddamnJMeasure = string[i] == 'j' ? -1 : 0; if (cur_char == '\n') { xPos = x; @@ -73,7 +74,7 @@ void drawSmallString_impl(Gfx **dl, int x, int y, const char* string, int r, int } else { if (cur_char != ' ') { s = computeS(cur_char); - gSPTextureRectangle(dlHead++, (xPos + 0) << 2, (yPos + 0) << 2, (xPos + 8) << 2, (yPos + 12) << 2, 0, s << 5, 0, 1 << 10, 1 << 10); + gSPTextureRectangle(dlHead++, (xPos + 0) << 2, (yPos + 0) << 2, (xPos + 8) << 2, (yPos + 12) << 2, 0, (s << 5) - goddamnJMeasure, 0, 1 << 10, 1 << 10); } xPos += fast_text_font_kerning[cur_char - ' ']; } diff --git a/src/game/game_init.c b/src/game/game_init.c index 2c6b2bb2..35919db7 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -715,6 +715,10 @@ void setup_game_memory(void) { gMarioAnimsMemAlloc = main_pool_alloc(MARIO_ANIMS_POOL_SIZE, MEMORY_POOL_LEFT); set_segment_base_addr(SEGMENT_MARIO_ANIMS, (void *) gMarioAnimsMemAlloc); setup_dma_table_list(&gMarioAnimsBuf, gMarioAnims, gMarioAnimsMemAlloc); +#ifdef PUPPYPRINT_DEBUG + set_segment_memory_printout(SEGMENT_MARIO_ANIMS, MARIO_ANIMS_POOL_SIZE); + set_segment_memory_printout(SEGMENT_DEMO_INPUTS, DEMO_INPUTS_POOL_SIZE); +#endif // Setup Demo Inputs List gDemoInputsMemAlloc = main_pool_alloc(DEMO_INPUTS_POOL_SIZE, MEMORY_POOL_LEFT); set_segment_base_addr(SEGMENT_DEMO_INPUTS, (void *) gDemoInputsMemAlloc); @@ -764,7 +768,9 @@ void thread5_game_loop(UNUSED void *arg) { draw_reset_bars(); continue; } - +#ifdef PUPPYPRINT_DEBUG + bzero(&gPuppyCallCounter, sizeof(gPuppyCallCounter)); +#endif // If any controllers are plugged in, start read the data for when // read_controller_inputs is called later. if (gControllerBits) { @@ -777,12 +783,14 @@ void thread5_game_loop(UNUSED void *arg) { audio_game_loop_tick(); select_gfx_pool(); read_controller_inputs(THREAD_5_GAME_LOOP); - profiler_update(PROFILER_TIME_CONTROLLERS); + profiler_update(PROFILER_TIME_CONTROLLERS, 0); + profiler_collision_reset(); addr = level_script_execute(addr); -#if !PUPPYPRINT_DEBUG && defined(VISUAL_DEBUG) + profiler_collision_completed(); +#if !defined(PUPPYPRINT_DEBUG) && defined(VISUAL_DEBUG) debug_box_input(); #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG puppyprint_profiler_process(); #endif diff --git a/src/game/hud.c b/src/game/hud.c index 66c9ccfc..75118b56 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -597,9 +597,6 @@ void render_hud(void) { if (gCustomDebugMode) { render_debug_mode(); } -#endif -#ifdef PUPPYPRINT - print_set_envcolour(255, 255, 255, 255); #endif } } diff --git a/src/game/level_update.c b/src/game/level_update.c index b22407d6..c3556d68 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -435,6 +435,11 @@ void init_mario_after_warp(void) { } #endif } +#ifdef PUPPYPRINT_DEBUG + gPuppyWarp = 0; + gLastWarpID = sWarpDest.nodeId; + gPuppyWarpArea = 0; +#endif } // used for warps inside one level @@ -845,6 +850,12 @@ void initiate_delayed_warp(void) { struct ObjectWarpNode *warpNode; s32 destWarpNode; +#ifdef PUPPYPRINT_DEBUG + if (gPuppyWarp) { + initiate_warp(gPuppyWarp, gPuppyWarpArea, 0x0A, 0); + } +#endif + if (sDelayedWarpOp != WARP_OP_NONE && --sDelayedWarpTimer == 0) { reset_dialog_render_state(); @@ -996,18 +1007,31 @@ s32 play_mode_normal(void) { warp_area(); check_instant_warp(); +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage != PUPPYPRINT_PAGE_RAM && sPPDebugPage != PUPPYPRINT_PAGE_LEVEL_SELECT) { + if (sTimerRunning && gHudDisplay.timer < 17999) { + gHudDisplay.timer++; + } + area_update_objects(); + } +#else if (sTimerRunning && gHudDisplay.timer < 17999) { gHudDisplay.timer++; } - area_update_objects(); +#endif update_hud_values(); #ifdef PUPPYLIGHTS delete_lights(); #endif - if (gCurrentArea != NULL) { +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage != PUPPYPRINT_PAGE_RAM && sPPDebugPage != PUPPYPRINT_PAGE_LEVEL_SELECT) { + update_camera(gCurrentArea->camera); + } +#else update_camera(gCurrentArea->camera); +#endif } initiate_painting_warp(); @@ -1029,7 +1053,7 @@ s32 play_mode_normal(void) { set_play_mode(PLAY_MODE_PAUSED); } } - + return FALSE; } @@ -1177,7 +1201,7 @@ s32 update_level(void) { s32 init_level(void) { s32 fadeFromColor = FALSE; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG OSTime first = osGetTime(); #endif @@ -1263,7 +1287,7 @@ s32 init_level(void) { puppylights_allocate(); #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG #ifdef PUPPYPRINT_DEBUG_CYCLES append_puppyprint_log("Level loaded in %dc", (s32)(osGetTime() - first)); #else diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 64cdb0b2..6af30231 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -119,33 +119,21 @@ Gfx *geo_switch_anim_state(s32 callContext, struct GraphNode *node, UNUSED void } Gfx *geo_switch_area(s32 callContext, struct GraphNode *node, UNUSED void *context) { - struct Surface *floor; struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + RoomData room; - if (callContext == GEO_CONTEXT_RENDER) { - if (gMarioObject == NULL) { - switchCase->selectedCase = 0; - } else { -#ifdef ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS - if (gCurrLevelNum == LEVEL_BBH) { - // In BBH, check for a floor manually, since there is an intangible floor. In custom hacks this can be removed. - find_room_floor(gMarioObject->oPosX, gMarioObject->oPosY, gMarioObject->oPosZ, &floor); - } else { - // Since no intangible floors are nearby, use Mario's floor instead. - floor = gMarioState->floor; - } -#else - floor = gMarioState->floor; -#endif - if (floor) { - gMarioCurrentRoom = floor->room; - s16 roomCase = floor->room - 1; - print_debug_top_down_objectinfo("areainfo %d", floor->room); + if (callContext == GEO_CONTEXT_RENDER && gMarioObject != NULL) { + room = get_room_at_pos( + gMarioObject->oPosX, + gMarioObject->oPosY, + gMarioObject->oPosZ + ); - if (roomCase >= 0) { - switchCase->selectedCase = roomCase; - } - } + gMarioCurrentRoom = room; + print_debug_top_down_objectinfo("areainfo %d", room); + + if (room > 0) { + switchCase->selectedCase = (room - 1); } } else { switchCase->selectedCase = 0; @@ -1880,30 +1868,31 @@ void bhv_init_room(void) { o->oRoom = get_room_at_pos(o->oPosX, o->oPosY, o->oPosZ); } -s32 is_mario_in_room(void) { +s32 cur_obj_is_mario_in_room(void) { if (o->oRoom != -1 && gMarioCurrentRoom != 0) { - if ( - gMarioCurrentRoom == o->oRoom || - gDoorAdjacentRooms[gMarioCurrentRoom][0] == o->oRoom || - gDoorAdjacentRooms[gMarioCurrentRoom][1] == o->oRoom + if (gMarioCurrentRoom == o->oRoom // Object is in Mario's room. + || gDoorAdjacentRooms[gMarioCurrentRoom].forwardRoom == o->oRoom // Object is in the transition room's forward room. + || gDoorAdjacentRooms[gMarioCurrentRoom].backwardRoom == o->oRoom // Object is in the transition room's backward room. ) { return MARIO_INSIDE_ROOM; } + return MARIO_OUTSIDE_ROOM; } + return MARIO_ROOM_UNDEFINED; } -void cur_obj_enable_disable_room_rendering(s32 inRoom) { - if (inRoom == MARIO_INSIDE_ROOM) { - cur_obj_enable_rendering(); - o->activeFlags &= ~ACTIVE_FLAG_IN_DIFFERENT_ROOM; - gNumRoomedObjectsInMarioRoom++; - } else if (inRoom == MARIO_OUTSIDE_ROOM) { - cur_obj_disable_rendering(); - o->activeFlags |= ACTIVE_FLAG_IN_DIFFERENT_ROOM; - gNumRoomedObjectsNotInMarioRoom++; - } +void cur_obj_enable_rendering_in_room(void) { + cur_obj_enable_rendering(); + o->activeFlags &= ~ACTIVE_FLAG_IN_DIFFERENT_ROOM; + gNumRoomedObjectsInMarioRoom++; +} + +void cur_obj_disable_rendering_in_room(void) { + cur_obj_disable_rendering(); + o->activeFlags |= ACTIVE_FLAG_IN_DIFFERENT_ROOM; + gNumRoomedObjectsNotInMarioRoom++; } s32 cur_obj_set_hitbox_and_die_if_attacked(struct ObjectHitbox *hitbox, s32 deathSound, s32 noLootCoins) { diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 3c257b34..f6858b8a 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -241,8 +241,9 @@ void cur_obj_call_action_function(ObjActionFunc actionFunctions[]); s32 cur_obj_mario_far_away(void); s32 is_mario_moving_fast_or_in_air(s32 speedThreshold); s32 is_item_in_array(s8 item, s8 *array); -s32 is_mario_in_room(void); -void cur_obj_enable_disable_room_rendering(s32 inRoom); +s32 cur_obj_is_mario_in_room(void); +void cur_obj_enable_rendering_in_room(void); +void cur_obj_disable_rendering_in_room(void); s32 cur_obj_set_hitbox_and_die_if_attacked(struct ObjectHitbox *hitbox, s32 deathSound, s32 noLootCoins); void obj_explode_and_spawn_coins(f32 mistSize, s32 coinType); void obj_set_collision_data(struct Object *obj, const void *segAddr); diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 4ee6d7d4..2b8bfe81 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -147,7 +147,7 @@ struct MemoryPool *gObjectMemoryPool; s16 gCollisionFlags = COLLISION_FLAGS_NONE; TerrainData *gEnvironmentRegions; s32 gEnvironmentLevels[20]; -RoomData gDoorAdjacentRooms[60][2]; +struct TransitionRoomData gDoorAdjacentRooms[MAX_NUM_TRANSITION_ROOMS]; s16 gMarioCurrentRoom; s16 gTHIWaterDrained; s16 gTTCSpeedSetting; @@ -522,10 +522,7 @@ void clear_objects(void) { gMarioObject = NULL; gMarioCurrentRoom = 0; - for (i = 0; i < 60; i++) { - gDoorAdjacentRooms[i][0] = 0; - gDoorAdjacentRooms[i][1] = 0; - } + bzero(gDoorAdjacentRooms, sizeof(gDoorAdjacentRooms)); debug_unknown_level_select_check(); @@ -547,11 +544,15 @@ void clear_objects(void) { * Update spawner and surface objects. */ void update_terrain_objects(void) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); gObjectCounter = update_objects_in_list(&gObjectLists[OBJ_LIST_SPAWNER]); - profiler_update(PROFILER_TIME_SPAWNER); + profiler_update(PROFILER_TIME_SPAWNER, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); +#ifdef PUPPYPRINT_DEBUG + first = profiler_get_delta(PROFILER_DELTA_COLLISION); +#endif gObjectCounter += update_objects_in_list(&gObjectLists[OBJ_LIST_SURFACE]); - profiler_update(PROFILER_TIME_DYNAMIC); + profiler_update(PROFILER_TIME_DYNAMIC, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); // If the dynamic surface pool has overflowed, throw an error. assert((uintptr_t)gDynamicSurfacePoolEnd <= (uintptr_t)gDynamicSurfacePool + DYNAMIC_SURFACE_POOL_SIZE, "Dynamic surface pool size exceeded"); @@ -566,12 +567,13 @@ void update_non_terrain_objects(void) { s32 i = 2; while ((listIndex = sObjectListUpdateOrder[i]) != -1) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); if (listIndex == OBJ_LIST_PLAYER) { - profiler_update(PROFILER_TIME_BEHAVIOR_BEFORE_MARIO); + profiler_update(PROFILER_TIME_BEHAVIOR_BEFORE_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } gObjectCounter += update_objects_in_list(&gObjectLists[listIndex]); if (listIndex == OBJ_LIST_PLAYER) { - profiler_update(PROFILER_TIME_MARIO); + profiler_update(PROFILER_TIME_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } i++; } @@ -648,6 +650,9 @@ void update_objects(UNUSED s32 unused) { // Update all other objects that haven't been updated yet update_non_terrain_objects(); + + // Take a snapshot of the current collision processing time. + UNUSED u32 firstPoint = profiler_get_delta(PROFILER_DELTA_COLLISION); // Unload any objects that have been deactivated unload_deactivated_objects(); @@ -666,6 +671,6 @@ void update_objects(UNUSED s32 unused) { } gPrevFrameObjectCount = gObjectCounter; - - profiler_update(PROFILER_TIME_BEHAVIOR_AFTER_MARIO); + // Set the recorded behaviour time, minus the difference between the snapshotted collision time and the actual collision time. + profiler_update(PROFILER_TIME_BEHAVIOR_AFTER_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - firstPoint); } diff --git a/src/game/object_list_processor.h b/src/game/object_list_processor.h index d8fe0251..a5cb4b85 100644 --- a/src/game/object_list_processor.h +++ b/src/game/object_list_processor.h @@ -109,9 +109,22 @@ enum CollisionFlags { }; extern s16 gCollisionFlags; + extern TerrainData *gEnvironmentRegions; extern s32 gEnvironmentLevels[20]; -extern RoomData gDoorAdjacentRooms[60][2]; + +/** + * The maximum number of door/transition rooms that load two rooms of objects at once. + */ +#define MAX_NUM_TRANSITION_ROOMS 60 + +struct TransitionRoomData { + /*0x00*/ RoomData forwardRoom; + /*0x01*/ RoomData backwardRoom; +}; /*0x02*/ + +extern struct TransitionRoomData gDoorAdjacentRooms[MAX_NUM_TRANSITION_ROOMS]; + extern s16 gMarioCurrentRoom; extern s16 gTHIWaterDrained; extern s16 gTTCSpeedSetting; diff --git a/src/game/profiling.c b/src/game/profiling.c index 31dfcdef..644dff47 100644 --- a/src/game/profiling.c +++ b/src/game/profiling.c @@ -4,16 +4,12 @@ #include "profiling.h" #include "fasttext.h" +#include "puppyprint.h" #ifdef USE_PROFILER #define RDP_CYCLE_CONV(x) ((10 * (x)) / 625) // 62.5 million cycles per frame -typedef struct { - u32 counts[PROFILING_BUFFER_SIZE]; - u32 total; -} ProfileTimeData; - ProfileTimeData all_profiling_data[PROFILER_TIME_COUNT]; int profile_buffer_index = -1; @@ -21,11 +17,17 @@ int rsp_buffer_indices[PROFILER_RSP_COUNT]; // Holds either the start time if the task is running, or the amount of time the task has run for so far if yielded u32 rsp_pending_times[PROFILER_RSP_COUNT]; u32 prev_start; -u32 start; +u32 cur_start; u32 prev_time; u32 audio_start; u32 audio_buffer_index; u32 preempted_time; +u32 collision_time = 0; + +#ifdef AUDIO_PROFILING +u32 audio_subset_starts[AUDIO_SUBSET_SIZE]; +u32 audio_subset_tallies[AUDIO_SUBSET_SIZE]; +#endif static void buffer_update(ProfileTimeData* data, u32 new, int buffer_index) { u32 old = data->counts[buffer_index]; @@ -34,12 +36,12 @@ static void buffer_update(ProfileTimeData* data, u32 new, int buffer_index) { data->counts[buffer_index] = new; } -void profiler_update(enum ProfilerTime which) { +void profiler_update(enum ProfilerTime which, u32 delta) { u32 cur_time = osGetCount(); u32 diff; ProfileTimeData* cur_data = &all_profiling_data[which]; - diff = cur_time - prev_time; + diff = cur_time - prev_time - delta; u32 saved = __osDisableInt(); u32 cur_preempted_time = preempted_time; @@ -47,7 +49,7 @@ void profiler_update(enum ProfilerTime which) { __osRestoreInt(saved); if (cur_preempted_time > 0) { diff -= cur_preempted_time; - start += cur_preempted_time; + cur_start += cur_preempted_time; } buffer_update(cur_data, diff, profile_buffer_index); @@ -83,15 +85,58 @@ void profiler_rsp_resumed() { void profiler_audio_started() { audio_start = osGetCount(); + +#ifdef AUDIO_PROFILING + for (s32 i = 0; i < AUDIO_SUBSET_SIZE; i++) { + audio_subset_tallies[i] = 0; + } + + audio_subset_starts[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START] = audio_start; +#endif +} + +#ifdef PUPPYPRINT_DEBUG + +void profiler_collision_reset() { + collision_time = 0; +} + +void profiler_collision_update(u32 time) { + collision_time += osGetCount() - time; +} + +void profiler_collision_completed() { + ProfileTimeData* cur_data = &all_profiling_data[PROFILER_TIME_COLLISION]; + buffer_update(cur_data, collision_time, profile_buffer_index); +} + +#endif + +u32 profiler_get_delta(enum ProfilerDeltaTime which) { + if (which == PROFILER_DELTA_COLLISION) { + return collision_time; + } else { + return 0; + } } void profiler_audio_completed() { ProfileTimeData* cur_data = &all_profiling_data[PROFILER_TIME_AUDIO]; - u32 time = osGetCount() - audio_start; + u32 time = osGetCount(); u32 cur_index = audio_buffer_index; - preempted_time = time; - buffer_update(cur_data, time, cur_index); + preempted_time = time - audio_start; + buffer_update(cur_data, time - audio_start, cur_index); + +#ifdef AUDIO_PROFILING + audio_subset_tallies[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START] += time - audio_subset_starts[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START]; + + for (s32 i = 0; i < AUDIO_SUBSET_SIZE; i++) { + cur_data = &all_profiling_data[i + PROFILER_TIME_SUB_AUDIO_START]; + buffer_update(cur_data, audio_subset_tallies[i], cur_index); + } +#endif + cur_index++; if (cur_index >= PROFILING_BUFFER_SIZE) { cur_index = 0; @@ -101,10 +146,10 @@ void profiler_audio_completed() { } static void update_fps_timer() { - u32 diff = start - prev_start; + u32 diff = cur_start - prev_start; buffer_update(&all_profiling_data[PROFILER_TIME_FPS], diff, profile_buffer_index); - prev_start = start; + prev_start = cur_start; } static void update_total_timer() { @@ -113,10 +158,15 @@ static void update_total_timer() { preempted_time = 0; __osRestoreInt(saved); - prev_time = start + cur_preempted_time; - profiler_update(PROFILER_TIME_TOTAL); + prev_time = cur_start + cur_preempted_time; + profiler_update(PROFILER_TIME_TOTAL, PROFILER_TIME_PUPPYPRINT1 + PROFILER_DELTA_PUPPYPRINT2); } +#ifdef PUPPYPRINT_DEBUG +extern u8 sPPDebugPage; +extern u8 fDebug; +#endif + static void update_rdp_timers() { u32 tmem = IO_READ(DPC_TMEM_REG); u32 cmd = IO_READ(DPC_BUFBUSY_REG); @@ -131,11 +181,6 @@ static void update_rdp_timers() { buffer_update(&all_profiling_data[PROFILER_TIME_PIPE], pipe, profile_buffer_index); } -#ifdef PUPPYPRINT_DEBUG -extern u8 sPPDebugPage; -extern u8 fDebug; -#endif - float profiler_get_fps() { return (1000000.0f * PROFILING_BUFFER_SIZE) / (OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_FPS].total)); } @@ -194,13 +239,13 @@ void profiler_print_times() { #ifndef PUPPYPRINT_DEBUG static u8 show_profiler = 0; - if (gPlayer1Controller->buttonPressed & L_TRIG) { + if ((gPlayer1Controller->buttonPressed & (L_TRIG | U_JPAD)) && (gPlayer1Controller->buttonDown & L_TRIG) && (gPlayer1Controller->buttonDown & U_JPAD)) { show_profiler ^= 1; } #endif #ifdef PUPPYPRINT_DEBUG - if (fDebug && sPPDebugPage == 0) { + if (fDebug && sPPDebugPage == PUPPYPRINT_PAGE_PROFILER) { #else if (show_profiler) { #endif @@ -222,11 +267,18 @@ void profiler_print_times() { "\n" "CPU\t\t%d (%d%%)\n" " Input\t\t%d\n" +#ifdef PUPPYPRINT_DEBUG + " Collision\t\t%d\n" +#else " Dynamic\t\t%d\n" +#endif " Mario\t\t\t%d\n" " Behavior\t\t%d\n" " Graph\t\t%d\n" " Audio\t\t\t%d\n" +#ifdef PUPPYPRINT_DEBUG + " Camera\t\t%d\n" +#endif "\n" "RDP\t\t%d (%d%%)\n" " Tmem\t\t\t%d\n" @@ -239,11 +291,18 @@ void profiler_print_times() { 1000000.0f / microseconds[PROFILER_TIME_FPS], total_cpu, total_cpu / 333, microseconds[PROFILER_TIME_CONTROLLERS], +#ifdef PUPPYPRINT_DEBUG + microseconds[PROFILER_TIME_COLLISION], +#else microseconds[PROFILER_TIME_DYNAMIC], +#endif microseconds[PROFILER_TIME_MARIO], microseconds[PROFILER_TIME_BEHAVIOR_BEFORE_MARIO] + microseconds[PROFILER_TIME_BEHAVIOR_AFTER_MARIO], microseconds[PROFILER_TIME_GFX], microseconds[PROFILER_TIME_AUDIO] * 2, // audio is 60Hz, so double the average +#ifdef PUPPYPRINT_DEBUG + microseconds[PROFILER_TIME_CAMERA], +#endif max_rdp, max_rdp / 333, microseconds[PROFILER_TIME_TMEM], microseconds[PROFILER_TIME_CMD], @@ -260,7 +319,7 @@ void profiler_print_times() { gDPSetTexturePersp(dlHead++, G_TP_NONE); gDPSetTextureFilter(dlHead++, G_TF_POINT); gDPSetTextureLUT(dlHead++, G_TT_NONE); - drawSmallStringCol(&dlHead, 10, 10, text_buffer, 255, 255, 255); + drawSmallStringCol(&dlHead, 10, 8, text_buffer, 255, 255, 255); gDisplayListHead = dlHead; } } @@ -273,7 +332,7 @@ void profiler_frame_setup() { profile_buffer_index = 0; } - prev_time = start = osGetCount(); + prev_time = cur_start = osGetCount(); } #endif diff --git a/src/game/profiling.h b/src/game/profiling.h index 48bbe34c..a52b801d 100644 --- a/src/game/profiling.h +++ b/src/game/profiling.h @@ -6,8 +6,30 @@ #include "config/config_debug.h" #include "config/config_safeguards.h" +#if defined(USE_PROFILER) && defined(PUPPYPRINT_DEBUG) +/** + * Toggle this define to enable verbose audio profiling with Pupprprint Debug. +*/ +#define AUDIO_PROFILING +#endif + +#define OS_GET_COUNT_INLINE(x) asm volatile("mfc0 %0, $9" : "=r"(x): ) + #define PROFILING_BUFFER_SIZE 64 +#define AUDIO_SUBSET_ENTRIES \ + PROFILER_TIME_SUB_AUDIO_START, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES = PROFILER_TIME_SUB_AUDIO_START, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, \ + PROFILER_TIME_SUB_AUDIO_UPDATE, \ + PROFILER_TIME_SUB_AUDIO_END + enum ProfilerTime { PROFILER_TIME_FPS, PROFILER_TIME_CONTROLLERS, @@ -17,7 +39,19 @@ enum ProfilerTime { PROFILER_TIME_MARIO, PROFILER_TIME_BEHAVIOR_AFTER_MARIO, PROFILER_TIME_GFX, + PROFILER_TIME_COLLISION, + PROFILER_TIME_CAMERA, +#ifdef PUPPYPRINT_DEBUG + PROFILER_TIME_PUPPYPRINT1, + PROFILER_TIME_PUPPYPRINT2, +#endif +#ifdef AUDIO_PROFILING + AUDIO_SUBSET_ENTRIES, + + PROFILER_TIME_AUDIO = PROFILER_TIME_SUB_AUDIO_END, +#else PROFILER_TIME_AUDIO, +#endif PROFILER_TIME_TOTAL, PROFILER_TIME_RSP_GFX, PROFILER_TIME_RSP_AUDIO, @@ -33,8 +67,29 @@ enum ProfilerRSPTime { PROFILER_RSP_COUNT }; +enum ProfilerDeltaTime { + PROFILER_DELTA_COLLISION, +#ifdef PUPPYPRINT_DEBUG + PROFILER_DELTA_PUPPYPRINT1, + PROFILER_DELTA_PUPPYPRINT2 +#endif +}; + +#ifndef PUPPYPRINT_DEBUG +#define PROFILER_TIME_PUPPYPRINT1 0 +#define PROFILER_TIME_PUPPYPRINT2 0 +#define PROFILER_DELTA_PUPPYPRINT1 0 +#define PROFILER_DELTA_PUPPYPRINT2 0 +#endif + #ifdef USE_PROFILER -void profiler_update(enum ProfilerTime which); +typedef struct { + u32 counts[PROFILING_BUFFER_SIZE]; + u32 total; +} ProfileTimeData; +extern ProfileTimeData all_profiling_data[PROFILER_TIME_COUNT]; + +void profiler_update(enum ProfilerTime which, u32 delta); void profiler_print_times(); void profiler_frame_setup(); void profiler_rsp_started(enum ProfilerRSPTime which); @@ -42,12 +97,29 @@ void profiler_rsp_completed(enum ProfilerRSPTime which); void profiler_rsp_resumed(); void profiler_audio_started(); void profiler_audio_completed(); +#ifdef PUPPYPRINT_DEBUG +void profiler_collision_reset(); +void profiler_collision_completed(); +void profiler_collision_update(u32 time); +#else +#define profiler_collision_reset() +#define profiler_collision_completed() +#define profiler_collision_update(time) +#endif +u32 profiler_get_delta(enum ProfilerDeltaTime which); +u32 profiler_get_cpu_microseconds(); +u32 profiler_get_rsp_microseconds(); +u32 profiler_get_rdp_microseconds(); // See profiling.c to see why profiler_rsp_yielded isn't its own function static ALWAYS_INLINE void profiler_rsp_yielded() { profiler_rsp_resumed(); +#define PROFILER_GET_SNAPSHOT() u32 first = osGetCount() +#define PROFILER_GET_SNAPSHOT_TYPE(type) u32 first = profiler_get_delta(type) } #else -#define profiler_update(which) +#define PROFILER_GET_SNAPSHOT() +#define PROFILER_GET_SNAPSHOT_TYPE(type) +#define profiler_update(which, delta) #define profiler_print_times() #define profiler_frame_setup() #define profiler_rsp_started(which) @@ -56,6 +128,71 @@ static ALWAYS_INLINE void profiler_rsp_yielded() { #define profiler_audio_started() #define profiler_audio_completed() #define profiler_rsp_yielded() +#define profiler_collision_reset() +#define profiler_collision_completed() +#define profiler_collision_update(time) +#define profiler_get_delta(which) 0 +#define profiler_get_cpu_microseconds() 0 +#define profiler_get_rsp_microseconds() 0 +#define profiler_get_rdp_microseconds() 0 #endif +#ifdef AUDIO_PROFILING +#define AUDIO_SUBSET_SIZE PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START +extern u32 audio_subset_starts[AUDIO_SUBSET_SIZE]; +extern u32 audio_subset_tallies[AUDIO_SUBSET_SIZE]; + +static ALWAYS_INLINE void profiler_audio_subset_switch_func(enum ProfilerTime complete, enum ProfilerTime start) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[complete] += time - audio_subset_starts[complete]; + audio_subset_starts[start] = time; +} + +static ALWAYS_INLINE void profiler_audio_subset_complete_and_switch_func(enum ProfilerTime complete1, enum ProfilerTime complete2, enum ProfilerTime start) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[complete1] += time - audio_subset_starts[complete1]; + audio_subset_tallies[complete2] += time - audio_subset_starts[complete2]; + audio_subset_starts[start] = time; +} + +static ALWAYS_INLINE void profiler_audio_subset_start_shared_func(enum ProfilerTime first, enum ProfilerTime new) { + audio_subset_starts[new] = audio_subset_starts[first]; +} + +static ALWAYS_INLINE void profiler_audio_subset_start_func(enum ProfilerTime index) { + OS_GET_COUNT_INLINE(audio_subset_starts[index]); +} + +static ALWAYS_INLINE void profiler_audio_subset_complete_func(enum ProfilerTime index) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[index] += time - audio_subset_starts[index]; +} + +#define AUDIO_PROFILER_SWITCH(complete, begin) profiler_audio_subset_switch_func(complete - PROFILER_TIME_SUB_AUDIO_START, begin - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_COMPLETE_AND_SWITCH(complete1, complete2, begin) profiler_audio_subset_complete_and_switch_func(complete1 - PROFILER_TIME_SUB_AUDIO_START, \ + complete2 - PROFILER_TIME_SUB_AUDIO_START, begin - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_START_SHARED(first, new) profiler_audio_subset_start_shared_func(first - PROFILER_TIME_SUB_AUDIO_START, new - PROFILER_TIME_SUB_AUDIO_START) + +// These two are unused by the default audio profiler; left in for cases of manual profiling of smaller functions as needed +#define AUDIO_PROFILER_START(which) profiler_audio_subset_start_func(which - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_COMPLETE(which) profiler_audio_subset_complete_func(which - PROFILER_TIME_SUB_AUDIO_START) +#else // AUDIO_PROFILING +enum ProfilerTimeAudioUnused { + AUDIO_SUBSET_ENTRIES +}; +#define AUDIO_PROFILER_SWITCH(complete, begin) +#define AUDIO_PROFILER_COMPLETE_AND_SWITCH(complete1, complete2, begin) +#define AUDIO_PROFILER_START_SHARED(first, new) + +// These two are unused by the default audio profiler; left in for cases of manual profiling of smaller functions as needed +#define AUDIO_PROFILER_START(which) +#define AUDIO_PROFILER_COMPLETE(which) +#endif // AUDIO_PROFILING + #endif diff --git a/src/game/puppycam2.c b/src/game/puppycam2.c index 089155ad..20c6e205 100644 --- a/src/game/puppycam2.c +++ b/src/game/puppycam2.c @@ -1346,6 +1346,10 @@ void puppycam_loop(void) { if (!gPuppyCam.cutscene && sDelayedWarpOp == 0) { // Sets this before going through any possible modifications. gPuppyCam.flags = gPuppyCam.intendedFlags; +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage == PUPPYPRINT_PAGE_CAMERA) + gPuppyCam.flags |= PUPPYCAM_BEHAVIOUR_FREE | PUPPYCAM_BEHAVIOUR_PITCH_ROTATION | PUPPYCAM_BEHAVIOUR_YAW_ROTATION; +#endif puppycam_input_core(); puppycam_projection(); puppycam_script(); diff --git a/src/game/puppyprint.c b/src/game/puppyprint.c index 141d2ed8..30651dcb 100644 --- a/src/game/puppyprint.c +++ b/src/game/puppyprint.c @@ -44,90 +44,105 @@ a modern game engine's developer's console. #include "hud.h" #include "debug_box.h" #include "color_presets.h" +#include "buffers/buffers.h" +#include "profiling.h" +#include "segment_symbols.h" #ifdef PUPPYPRINT -ColorRGBA currEnv; +#define TAB_WIDTH 16 + #ifdef ENABLE_CREDITS_BENCHMARK u8 fDebug = TRUE; #else u8 fDebug = FALSE; #endif +u8 sPuppyprintTextBuffer[PUPPYPRINT_DEFERRED_BUFFER_SIZE]; +u32 sPuppyprintTextBufferPos; // Location in the buffer of puppyprint deferred text. +ColorRGBA gCurrEnvCol; + +#ifdef PUPPYPRINT_DEBUG -#if PUPPYPRINT_DEBUG -s8 benchViewer = FALSE; -u8 benchOption = 0; s8 logViewer = FALSE; u8 sPPDebugPage = 0; u8 sDebugMenu = FALSE; u8 sDebugOption = 0; -// Profiler values -s8 perfIteration = 0; -s16 benchmarkLoop = 0; -s32 benchmarkTimer = 0; -s32 benchmarkProgramTimer = 0; -s8 benchmarkType = 0; -// General -u32 rspTime = 0; -u32 rdpTime = 0; -s32 benchMark[NUM_BENCH_ITERATIONS + 2]; -// RAM -s8 ramViewer = FALSE; -s32 ramsizeSegment[NUM_TLB_SEGMENTS + 1] = { - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0 -}; -s8 audioRamViewer = FALSE; +s32 ramsizeSegment[NUM_TLB_SEGMENTS + 1] = { 0 }; s32 mempool; +u32 gPoolMem; +u32 gPPSegScroll = 0; +u32 gMiscMem = 0; +struct CallCounter gPuppyCallCounter; -extern u8 _mainSegmentStart[]; -extern u8 _mainSegmentEnd[]; -extern u8 _engineSegmentStart[]; -extern u8 _engineSegmentEnd[]; -extern u8 _framebuffersSegmentBssStart[]; -extern u8 _framebuffersSegmentBssEnd[]; -extern u8 _buffersSegmentBssStart[]; -extern u8 _buffersSegmentBssEnd[]; -extern u8 _goddardSegmentStart[]; -extern u8 _goddardSegmentEnd[]; +#define NUM_RAM_CHARS 32 -// Here is stored the rom addresses of the global code segments. If you get rid of any, it's best to just write them as NULL. -u32 ramP[5][2] = { - {(u32)_buffersSegmentBssStart, (u32)_buffersSegmentBssEnd}, - {(u32)_mainSegmentStart, (u32)_mainSegmentEnd}, - {(u32)_engineSegmentStart, (u32)_engineSegmentEnd}, - {(u32)_framebuffersSegmentBssStart, (u32)_framebuffersSegmentBssEnd}, - {(u32)_goddardSegmentStart, (u32)_goddardSegmentEnd}, +// Another epic lookup table, for text this time. +const char ramNames[][NUM_RAM_CHARS] = { + "Buffers", + "Main", + "Engine", + "Framebuffers", + "ZBuffer", + "Goddard", + "Pools", + "Collision", + "Misc", + "Audio Heap" }; +enum RamNames { + RAM_BUFFERS, + RAM_MAIN, + RAM_ENGINE, + RAM_FRAMEBUFFERS, + RAM_ZBUFFER, + RAM_GODDARD, + RAM_POOLS, + RAM_COLLISION, + RAM_MISC, + RAM_AUDIO +}; + +const char segNames[][NUM_RAM_CHARS] = { + "HUD", + "Common1 GFX", + "Group0 GFX", + "GroupA GFX", + "GroupB GFX", + "Level GFX", + "Common0 GFX", + "Textures", + "Skybox", + "Effects", + "GroupA Geo", + "GroupB Geo", + "Level Geo", + "Common0 Geo", + "Entry", + "Mario Anims", + "Demos", + "Bhv Scripts", + "Menu", + "Level Scripts", + "Common1 Geo", + "Group0 Geo", + "", + "Languages" +}; + +const s8 nameTable = sizeof(ramNames) / NUM_RAM_CHARS; + void puppyprint_calculate_ram_usage(void) { - u32 temp[2]; - s32 i = 0; - - for (i = 0; i < 5; i++) { - if (!ramP[i][0] || !ramP[i][1]) { - continue; - } - temp[0] = ramP[i][0]; - temp[1] = ramP[i][1]; - ramsizeSegment[i] = temp[1] - temp[0]; - } - - // These are a bit hacky, but what can ye do eh? - // gEffectsMemoryPool is 0x4000, gObjectMemoryPool is 0x800. Epic C limitations mean I can't just sizeof their values :) - ramsizeSegment[5] = (EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL - + EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL); - ramsizeSegment[6] = gTotalStaticSurfaceData + DYNAMIC_SURFACE_POOL_SIZE; - ramsizeSegment[7] = gAudioHeapSize; + ramsizeSegment[RAM_BUFFERS] = (u32)&_buffersSegmentBssEnd - (u32)&_buffersSegmentBssStart - gAudioHeapSize; + ramsizeSegment[RAM_MAIN] = (u32)&_mainSegmentEnd - (u32)&_mainSegmentStart; + ramsizeSegment[RAM_ENGINE] = (u32)&_engineSegmentEnd - (u32)&_engineSegmentStart; + ramsizeSegment[RAM_FRAMEBUFFERS] = (u32)&_framebuffersSegmentBssEnd - (u32)&_framebuffersSegmentBssStart; + ramsizeSegment[RAM_ZBUFFER] = (u32)&_zbufferSegmentBssEnd - (u32)&_zbufferSegmentBssStart; + ramsizeSegment[RAM_GODDARD] = (u32)&_goddardSegmentEnd - (u32)&_goddardSegmentStart; + ramsizeSegment[RAM_POOLS] = gPoolMem; + ramsizeSegment[RAM_COLLISION] = ((u32) gCurrStaticSurfacePoolEnd - (u32) gCurrStaticSurfacePool) + ((u32) gDynamicSurfacePoolEnd - (u32) gDynamicSurfacePool); + ramsizeSegment[RAM_MISC] = gMiscMem; + ramsizeSegment[RAM_AUDIO] = gAudioHeapSize; } #ifdef PUPPYPRINT_DEBUG_CYCLES @@ -138,24 +153,6 @@ void puppyprint_calculate_ram_usage(void) { #define RDP_CYCLE_CONV(x) ((10 * (x)) / 625) // 62.5 million cycles per frame #endif -void puppyprint_profiler_finished(void) { - s32 i = 0; - benchMark[NUM_BENCH_ITERATIONS ] = 0; - benchMark[NUM_BENCH_ITERATIONS + 1] = 0; - benchmarkTimer = 300; - benchViewer = FALSE; - - for (i = 0; i < (NUM_BENCH_ITERATIONS - 2); i++) { - benchMark[NUM_BENCH_ITERATIONS] += benchMark[i]; - if (benchMark[i] > benchMark[NUM_BENCH_ITERATIONS + 1]) { - benchMark[NUM_BENCH_ITERATIONS + 1] = benchMark[i]; - } - } - - benchMark[NUM_BENCH_ITERATIONS] /= NUM_BENCH_ITERATIONS; - benchmarkProgramTimer = CYCLE_CONV(osGetTime() - benchmarkProgramTimer); -} - // RGB colour lookup table for colouring all the funny ram prints. ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 255, 0, 0 }, @@ -170,8 +167,8 @@ ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 204, 0, 102 }, { 0, 153, 153 }, { 153, 255, 153 }, - { 0, 0, 128 }, - { 128, 0, 128 }, + { 127, 127, 255 }, + { 255, 127, 255 }, { 218, 165, 32 }, { 107, 142, 35 }, { 188, 143, 143 }, @@ -191,135 +188,145 @@ ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 255, 255, 255 } }; -// Change this to alter the width of the bar at the bottom. -#define RAM_BAR_LENGTH 200 -#define RAM_BAR_MIN (SCREEN_CENTER_X - (RAM_BAR_LENGTH / 2)) -#define RAM_BAR_MAX (SCREEN_CENTER_X + (RAM_BAR_LENGTH / 2)) -#define RAM_BAR_TOP (SCREEN_HEIGHT - 30) -#define RAM_BAR_BOTTOM (SCREEN_HEIGHT - 22) - -void print_ram_bar(void) { - s32 i = 0; - f32 perfPercentage; - s32 graphPos = 0; - s32 prevGraph = RAM_BAR_MIN; - s32 ramsize = osGetMemSize(); - - prepare_blank_box(); - - for (i = 0; i < NUM_TLB_SEGMENTS; i++) { - if (ramsizeSegment[i] == 0) { - continue; - } - - perfPercentage = (RAM_BAR_LENGTH * ((f32)ramsizeSegment[i] / ramsize)); - graphPos = (prevGraph + CLAMP(perfPercentage, 1, RAM_BAR_MAX)); - render_blank_box(prevGraph, RAM_BAR_TOP, graphPos, RAM_BAR_BOTTOM, - colourChart[i][0], - colourChart[i][1], - colourChart[i][2], 255); - prevGraph = graphPos; - } - - perfPercentage = (RAM_BAR_LENGTH * ((f32)ramsizeSegment[NUM_TLB_SEGMENTS] / ramsize)); - graphPos = (prevGraph + CLAMP(perfPercentage, 1, RAM_BAR_MAX)); - render_blank_box(prevGraph, RAM_BAR_TOP, graphPos, RAM_BAR_BOTTOM, 255, 255, 255, 255); - prevGraph = graphPos; - - render_blank_box(prevGraph, RAM_BAR_TOP, RAM_BAR_MAX, RAM_BAR_BOTTOM, 0, 0, 0, 255); - - finish_blank_box(); +void swap(int* xp, int* yp) +{ + int temp = *xp; + *xp = *yp; + *yp = temp; } -// Another epic lookup table, for text this time. -const char ramNames[8][32] = { - "Buffers", - "Main", - "Engine", - "Framebuffers", - "Goddard", - "Pools", - "Collision", - "Audio Heap", -}; +void swapu(u8* xp, u8* yp) +{ + u8 temp = *xp; + *xp = *yp; + *yp = temp; +} -s8 nameTable = sizeof(ramNames) / NUM_TLB_SEGMENTS; +void sort_numbers(s32 *values, u8 *values2) +{ + int i, j, min_idx; + + // One by one move boundary of unsorted subarray + for (i = 0; i < NUM_TLB_SEGMENTS; i++) { + + if (values[i] == 0) + continue; + // Find the minimum element in unsorted array + min_idx = i; + for (j = i + 1; j < NUM_TLB_SEGMENTS; j++) + if (values[j] > values[min_idx]) + min_idx = j; + + // Swap the found minimum element + // with the first element + swap(&values[min_idx], &values[i]); + swapu(&values2[min_idx], &values2[i]); + } +} + +void set_segment_memory_printout(u32 segment, u32 amount) { + ramsizeSegment[segment + nameTable - 2] = amount; +} void print_ram_overview(void) { - s32 i = 0; - char textBytes[32]; - s32 x = 80; - s32 y = 16; - s32 drawn = 0; + char textBytes[64]; + s32 y = 56; + f32 ramSize = RAM_END - 0x80000000; + s32 tempNums[32]; + u8 tempPos[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}; prepare_blank_box(); - render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 192); + render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 168); finish_blank_box(); - for (i = 0; i <= NUM_TLB_SEGMENTS; i++) { - if (drawn == 16) { - x = 240; - y = 16; - } + memcpy(tempNums, &ramsizeSegment, 32 * 4); - if (ramsizeSegment[i] == 0) { + sort_numbers(tempNums, tempPos); + + print_set_envcolour(255, 255, 255, 255); + sprintf(textBytes, "Total:"); + print_small_text_light(24, 16- gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%06X",RAM_END - 0x80000000); + print_small_text_light(SCREEN_WIDTH/2, 16 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", mempool); + print_small_text_light(SCREEN_WIDTH - 24, 16 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "Used:"); + print_small_text_light(24, 28- gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%06X", (RAM_END - 0x80000000) - (main_pool_available() - 0x400)); + print_small_text_light(SCREEN_WIDTH/2, 28 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", 100.0f - (((f32)(main_pool_available() - 0x400) / (f32)(RAM_END - 0x80000000)) * 100)); + print_small_text_light(SCREEN_WIDTH - 24, 28 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "Free:"); + print_small_text_light(24, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", (main_pool_available() - 0x400)); + print_small_text_light(SCREEN_WIDTH/2, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", (((f32)(main_pool_available() - 0x400) / (f32)(RAM_END - 0x80000000)) * 100)); + print_small_text_light(SCREEN_WIDTH - 24, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + for (u8 i = 0; i < NUM_TLB_SEGMENTS; i++) { + if (tempNums[i] == 0) { continue; } - - if (i < 8) { - sprintf(textBytes, "%s: %X", ramNames[i], ramsizeSegment[i]); - } else { - sprintf(textBytes, "Segment %02X: %X", ((i - nameTable) + 2), ramsizeSegment[i]); + if (y - gPPSegScroll > 0 && y - gPPSegScroll < SCREEN_HEIGHT) { + if (tempPos[i] < nameTable) { + sprintf(textBytes, "%s:", ramNames[tempPos[i]]); + } else { + sprintf(textBytes, "%s:", segNames[tempPos[i] - nameTable]); + } + print_small_text_light(24, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", tempNums[i]); + print_small_text_light(SCREEN_WIDTH/2, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", ((f32)tempNums[i] / ramSize) * 100.0f); + print_small_text_light(SCREEN_WIDTH - 24, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); } - - print_set_envcolour(colourChart[i][0], colourChart[i][1], colourChart[i][2], 255); - print_small_text(x, y, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); y += 12; - drawn++; } - - sprintf(textBytes, "RAM: %06X/%06X (%d_)", main_pool_available(), mempool, (s32)(((f32)main_pool_available() / (f32)mempool) * 100)); - print_small_text(SCREEN_CENTER_X, (SCREEN_HEIGHT - 16), textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); - - puppyprint_calculate_ram_usage(); - print_ram_bar(); } -const char *audioPoolNames[NUM_AUDIO_POOLS] = { - "Audio Init Pool", - "Notes And Buffers Pool", - "Persistent Sequence Pool", - "Persistent Bank Pool", - "Temporary Sequence Pool", - "Temporary Bank Pool", +static const char *audioPoolNames[NUM_AUDIO_POOLS] = { + "Audio Init Pool:\t\t\t\t ", + "Notes And Buffers Pool:\t ", + "Persistent Sequence Pool: ", + "Persistent Bank Pool:\t\t ", + "Temporary Sequence Pool:\t ", + "Temporary Bank Pool:\t\t ", #ifdef BETTER_REVERB - "Better Reverb Pool", + "Better Reverb Pool:\t\t ", #endif }; -void print_audio_ram_overview(void) { - char textBytes[128]; - const s32 x = 16; - s32 y = 16; - s32 i = 0; +#ifdef AUDIO_PROFILING +static const char *audioBenchmarkNames[PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START] = { + "Sequence Processing:\t ", + " Script Parsing:\t\t\t ", + " Reclaim Notes:\t\t\t ", + " Note Processing:\t\t ", + "Synthesis:\t\t\t\t\t ", + " Note Processing:\t\t ", + " Envelopes / Reverb:\t ", + " Audio DMAs:\t\t\t\t ", + "Audio Update:\t\t\t\t " +}; + +STATIC_ASSERT(ARRAY_COUNT(audioBenchmarkNames) == PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START, "audioBenchmarkNames has incorrect number of entries!"); +#endif + +static void print_audio_ram_overview(s32 x, char *textBytes) { s32 percentage = 0; - s32 tmpY = y; + s32 y = SCREEN_HEIGHT - 6; s32 totalMemory[2] = { 0, 0 }; s32 audioPoolSizes[NUM_AUDIO_POOLS][2]; - prepare_blank_box(); - render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 192); - finish_blank_box(); puppyprint_get_allocated_pools(audioPoolSizes[0]); - y += 24; - for (i = 0; i < NUM_AUDIO_POOLS; i++) { + for (s8 i = NUM_AUDIO_POOLS - 1; i >= 0; i--) { + y -= 12; + if (audioPoolSizes[i][0] == 0) { percentage = 1000; } else { percentage = (((s64) audioPoolSizes[i][1] * 1000) / audioPoolSizes[i][0]); } - sprintf(textBytes, "%s: %X / %X (%d.%d_)", audioPoolNames[i], + sprintf(textBytes, " %s%X / %X (%d.%d%%)", audioPoolNames[i], audioPoolSizes[i][1], audioPoolSizes[i][0], percentage / 10, @@ -328,13 +335,12 @@ void print_audio_ram_overview(void) { print_set_envcolour(colourChart[i][0], colourChart[i][1], colourChart[i][2], 255); - print_small_text(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); - - y += 12; + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); totalMemory[0] += audioPoolSizes[i][0]; totalMemory[1] += audioPoolSizes[i][1]; } + y -= 12; if (totalMemory[0] == 0) { percentage = 0; @@ -342,63 +348,70 @@ void print_audio_ram_overview(void) { percentage = (((s64) totalMemory[1] * 1000) / totalMemory[0]); } if (totalMemory[0] == gAudioHeapSize) { - sprintf(textBytes, "TOTAL AUDIO MEMORY: %X / %X (%d.%d_)", + sprintf(textBytes, "TOTAL AUDIO MEMORY:\t\t%X / %X (%d.%d%%)", totalMemory[1], totalMemory[0], percentage / 10, percentage % 10); } else { - sprintf(textBytes, "TOTAL AUDIO MEMORY: %X / %X (Incorrect!)", + sprintf(textBytes, "TOTAL AUDIO MEMORY:\t\t%X / %X (Incorrect!)", totalMemory[1], totalMemory[0]); } - print_set_envcolour(colourChart[30][0], - colourChart[30][1], - colourChart[30][2], 255); - print_small_text(x, tmpY, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + print_set_envcolour(255, 255, 255, 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); } -void benchmark_custom(void) { - if ((benchmarkLoop == 0) - || (benchOption != 2)) { - return; - } - - OSTime lastTime; - while (TRUE) { - lastTime = osGetTime(); - // Insert your function here! - - if ((benchmarkLoop > 0) - && (benchOption == 2)) { - benchmarkLoop--; - - benchMark[benchmarkLoop] = (osGetTime() - lastTime); - if (benchmarkLoop == 0) { - puppyprint_profiler_finished(); - break; - } - } else { - break; - } - } -} - -const char benchNames[][32] = { - "Game Thread", - "Audio Thread", - "Custom", -}; - -void print_which_benchmark(void) { - char textBytes[40]; +static void print_audio_overview(void) { + char textBytes[128]; + const s32 x = 12; + s32 y = 6; + u32 us; + u32 percentInt; + u32 percentDec; prepare_blank_box(); - render_blank_box((SCREEN_CENTER_X - 50), 115, (SCREEN_CENTER_X + 50), 160, 0, 0, 0, 255); + render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 64, 64, 64, 95); finish_blank_box(); - sprintf(textBytes, "Select Option#%s#L: Confirm", benchNames[benchOption]); - print_small_text(SCREEN_CENTER_X, 120, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + + us = OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_AUDIO].total / PROFILING_BUFFER_SIZE) * 2; + percentInt = us / 33; + percentDec = percentInt % 10; + percentInt /= 10; + + sprintf(textBytes, "TOTAL AUDIO CPU:\t\t%d (%d.%d%%)", us, percentInt, percentDec); + + print_set_envcolour(255, 255, 255, 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + +#ifdef AUDIO_PROFILING + for (s32 i = 0; i < ARRAY_COUNT(audioBenchmarkNames); i++) { + y += 12; + + us = OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_SUB_AUDIO_START + i].total / PROFILING_BUFFER_SIZE) * 2; + + if (audioBenchmarkNames[i][0] != ' ') { + percentInt = us / 33; + percentDec = percentInt % 10; + percentInt /= 10; + sprintf(textBytes, " %s%d (%d.%d%%)", audioBenchmarkNames[i], us, percentInt, percentDec); + } else { + sprintf(textBytes, " %s%d", audioBenchmarkNames[i], us); + } + + print_set_envcolour(colourChart[NUM_AUDIO_POOLS + i][0], + colourChart[NUM_AUDIO_POOLS + i][1], + colourChart[NUM_AUDIO_POOLS + i][2], 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + } +#else + print_set_envcolour(255, 95, 95, 255); + print_small_text(x + 8, y + 12, "Verbose audio profiling is disabled!\nPlease toggle the AUDIO PROFILING define\n" + "In profiling.h.", PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); +#endif + + print_audio_ram_overview(x, textBytes); } char consoleLogTable[LOG_BUFFER_SIZE][255]; @@ -408,7 +421,6 @@ static char *write_to_buf(char *buffer, const char *data, size_t size) { } void append_puppyprint_log(const char *str, ...) { - s32 i; char textBytes[255]; memset(textBytes, 0, sizeof(textBytes)); @@ -421,7 +433,7 @@ void append_puppyprint_log(const char *str, ...) { #ifdef UNF osSyncPrintf(textBytes); #endif - for (i = 0; i < (LOG_BUFFER_SIZE - 1); i++) { + for (u8 i = 0; i < (LOG_BUFFER_SIZE - 1); i++) { memcpy(consoleLogTable[i], consoleLogTable[i + 1], 255); } memcpy(consoleLogTable[LOG_BUFFER_SIZE - 1], textBytes, 255); @@ -430,16 +442,15 @@ void append_puppyprint_log(const char *str, ...) { #define LINE_HEIGHT (8 + ((LOG_BUFFER_SIZE - 1) * 12)) void print_console_log(void) { - s32 i; prepare_blank_box(); render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 96); finish_blank_box(); - for (i = 0; i < LOG_BUFFER_SIZE; i++) { + for (u8 i = 0; i < LOG_BUFFER_SIZE; i++) { if (consoleLogTable[i] == NULL) { continue; } - print_small_text(16, (LINE_HEIGHT - (i * 12)), consoleLogTable[i], PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + print_small_text_light(16, (LINE_HEIGHT - (i * 12)), consoleLogTable[i], PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); } } #undef LINE_HEIGHT @@ -451,29 +462,21 @@ extern s16 gVisualSurfaceCount; #endif void puppyprint_render_collision(void) { - char textBytes[200]; - - sprintf(textBytes, "Static Pool Size: 0x%X#Dynamic Pool Size: 0x%X#Dynamic Pool Used: 0x%X#Surfaces Allocated: %d#Nodes Allocated: %d", gTotalStaticSurfaceData, DYNAMIC_SURFACE_POOL_SIZE,(uintptr_t)gDynamicSurfacePoolEnd - (uintptr_t)gDynamicSurfacePool, - gSurfacesAllocated, gSurfaceNodesAllocated); - print_small_text(304, 60, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, 1); - + char textBytes[128]; + sprintf(textBytes, "Static Pool Size: 0x%X\nDynamic Pool Size: 0x%X\nDynamic Pool Used: 0x%X\nSurfaces Allocated: %d\nNodes Allocated: %d", + gTotalStaticSurfaceData, + DYNAMIC_SURFACE_POOL_SIZE, + (uintptr_t)gDynamicSurfacePoolEnd - (uintptr_t)gDynamicSurfacePool, + gSurfacesAllocated, gSurfaceNodesAllocated); + print_small_text_light(SCREEN_WIDTH-16, 60, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, 1); #ifdef VISUAL_DEBUG - print_small_text(160, (SCREEN_HEIGHT - 42), "Use the dpad to toggle visual collision modes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); + print_small_text_light(160, (SCREEN_HEIGHT - 42), "Use the dpad to toggle visual collision modes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); switch (viewCycle) { - case 0: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: None", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 1: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 2: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 3: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes and Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - } - if (gPlayer1Controller->buttonPressed & R_JPAD) viewCycle++; - if (gPlayer1Controller->buttonPressed & L_JPAD) viewCycle--; - - if (viewCycle == 4) { - viewCycle = 0; - } - if (viewCycle == 255) { - viewCycle = 3; + case 0: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: None", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 1: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 2: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 3: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes and Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; } hitboxView = ((viewCycle == 1) || (viewCycle == 3)); @@ -483,122 +486,183 @@ void puppyprint_render_collision(void) { extern void print_fps(s32 x, s32 y); -u32 profiler_get_cpu_cycles(); -u32 profiler_get_rsp_cycles(); -u32 profiler_get_rdp_cycles(); - -u32 profiler_get_cpu_microseconds(); -u32 profiler_get_rsp_microseconds(); -u32 profiler_get_rdp_microseconds(); - void print_basic_profiling(void) { - u32 cpuTime; - u32 rspTime; - u32 rdpTime; char textBytes[90]; - print_fps(16, 16); -#ifdef PUPPYPRINT_DEBUG_CYCLES - cpuTime = profiler_get_cpu_cycles(); - rspTime = profiler_get_rsp_cycles(); - rdpTime = profiler_get_rdp_cycles(); - sprintf(textBytes, "CPU: %dc (%d_)#RSP: %dc (%d_)#RDP: %dc (%d_)", - cpuTime, (cpuTime / 15625), - rspTime, (rspTime / 15625), - rdpTime, (rdpTime / 15625)); -#else - cpuTime = profiler_get_cpu_microseconds(); - rspTime = profiler_get_rsp_microseconds(); - rdpTime = profiler_get_rdp_microseconds(); - sprintf(textBytes, "CPU: %dus (%d_)#RSP: %dus (%d_)#RDP: %dus (%d_)", + u32 cpuTime = profiler_get_cpu_microseconds(); + u32 rspTime = profiler_get_rsp_microseconds(); + u32 rdpTime = profiler_get_rdp_microseconds(); + print_fps(16, 40); + sprintf(textBytes, "CPU: %dus (%d%%)\nRSP: %dus (%d%%)\nRDP: %dus (%d%%)", cpuTime, (cpuTime / 333), rspTime, (rspTime / 333), rdpTime, (rdpTime / 333)); -#endif - print_small_text(16, 28, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + print_small_text_light(16, 52, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); } void puppyprint_render_standard(void) { - char textBytes[80]; + char textBytes[128]; - sprintf(textBytes, "OBJ: %d/%d", gObjectCounter, OBJECT_POOL_CAPACITY); - print_small_text((SCREEN_WIDTH - 16), 16, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - -#ifndef ENABLE_CREDITS_BENCHMARK - // Very little point printing useless info if Mario doesn't even exist. - if (gMarioState->marioObj) { - sprintf(textBytes, "Mario Pos#X: %d#Y: %d#Z: %d#D: %X#A: %x", - (s32)(gMarioState->pos[0]), - (s32)(gMarioState->pos[1]), - (s32)(gMarioState->pos[2]), - (u16)(gMarioState->faceAngle[1]), - (u32)(gMarioState->action & ACT_ID_MASK)); - print_small_text((SCREEN_WIDTH - 16), 32, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - } - // Same for the camera, especially so because this will crash otherwise. - if (gCamera) { - sprintf(textBytes, "Camera Pos#X: %d#Y: %d#Z: %d#D: %X", - (s32)(gCamera->pos[0]), - (s32)(gCamera->pos[1]), - (s32)(gCamera->pos[2]), - (u16)(gCamera->yaw)); - print_small_text((SCREEN_WIDTH - 16), 140, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - } -#endif - - // Old profiler bar code, can be restored one day if desired to work with new profiler - // s32 graphPos; - // s32 prevGraph; - // u32 i; - // s32 viewedNums; - - // prepare_blank_box(); - // viewedNums = 0; - - // // Render CPU breakdown bar. - // for (i = 0; i < CPU_TABLE_MAX; i++) { - // if (perfPercentage[i] == 0 - // && (i != CPU_TABLE_MAX - 1)) { - // continue; - // } - - // if (viewedNums == 0) { - // graphPos = ((SCREEN_WIDTH - 96) + perfPercentage[i]); - // render_blank_box((SCREEN_WIDTH - 96), barY, graphPos, (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } else if (i == (CPU_TABLE_MAX - 1)) { - // graphPos = ((SCREEN_WIDTH - 96) + perfPercentage[i]); - // render_blank_box(prevGraph, barY, (SCREEN_WIDTH - 16), (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } else { - // graphPos += perfPercentage[i]; - // render_blank_box(prevGraph, barY, graphPos, (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } - - // viewedNums++; - // prevGraph = graphPos; - // } - - // finish_blank_box(); + sprintf(textBytes, "Matrix Muls: %d\n\nCollision Checks\nFloors: %d\nWalls: %d\nCeilings: %d\n Water: %d\nRaycasts: %d", + gPuppyCallCounter.matrix, + gPuppyCallCounter.collision_floor, + gPuppyCallCounter.collision_wall, + gPuppyCallCounter.collision_ceil, + gPuppyCallCounter.collision_water, + gPuppyCallCounter.collision_raycast + ); + print_small_text_light(SCREEN_WIDTH-16, 32, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); } void puppyprint_render_minimal(void) { print_basic_profiling(); } +void render_coverage_map(void) { + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); + gDPSetBlendColor(gDisplayListHead++, 0xFF, 0xFF, 0xFF, 0xFF); + gDPSetPrimDepth(gDisplayListHead++, 0xFFFF, 0xFFFF); + gDPSetDepthSource(gDisplayListHead++, G_ZS_PRIM); + gDPSetRenderMode(gDisplayListHead++, G_RM_VISCVG, G_RM_VISCVG2); + gDPFillRectangle(gDisplayListHead++, 0,0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1); +} + +void puppycamera_debug_view(void) { + char textBytes[80]; + // Very little point printing useless info if Mayro doesn't even exist. + if (gMarioState->marioObj) { + sprintf(textBytes, "Mario Pos\nX: %d\nY: %d\nZ: %d\nD: %X\nA: %x", + (s32)(gMarioState->pos[0]), + (s32)(gMarioState->pos[1]), + (s32)(gMarioState->pos[2]), + (u16)(gMarioState->faceAngle[1]), + (u32)(gMarioState->action & ACT_ID_MASK)); + print_small_text_light(16, 140, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + } + // Same for the camera, especially so because this will crash otherwise. + if (gCamera) { + sprintf(textBytes, "Camera Pos\nX: %d\nY: %d\nZ: %d\nD: %X", + (s32)(gCamera->pos[0]), + (s32)(gCamera->pos[1]), + (s32)(gCamera->pos[2]), + (u16)(gCamera->yaw)); + print_small_text_light((SCREEN_WIDTH - 16), 140, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); + } +} + +#define STUB_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8) textname, +#define DEFINE_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) textname, + +static char sLevelNames[][32] = { + #include "levels/level_defines.h" +}; +#undef STUB_LEVEL +#undef DEFINE_LEVEL + +static s16 sLevelSelectOption = 0; +static s8 sLevelSelectOptionArea = 0; +u8 gPuppyWarp = 0; +u8 gPuppyWarpArea = 0; + +void puppyprint_level_select_menu(void) { + s32 posY; + s32 renderedText = 0; + char textBytes[32]; + prepare_blank_box(); + render_blank_box_rounded((SCREEN_WIDTH/2) - 80, (SCREEN_HEIGHT/2) - 60, (SCREEN_WIDTH/2) + 80, (SCREEN_HEIGHT/2) + 60, 0, 0, 0, 160); + finish_blank_box(); + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) - 58, "Pick a level", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) + 64, "(Area must have warp node of 0x0A)\nDpad Left/Right: Area / A: Warp\nYellow is current level.", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); + for (u32 i = 0; i < sizeof(sLevelNames) / 32; i++) { + s32 yOffset = sLevelSelectOption > 8 ? sLevelSelectOption-8 : 0; + posY = ((renderedText-yOffset) * 10); + if (sLevelNames[i][0] == 0) { + continue; + } + renderedText++; + if (posY < 0 || posY > 84) { + continue; + } + if ((u32) sLevelSelectOption == i) { + sprintf(textBytes, "%s - %d", sLevelNames[i], sLevelSelectOptionArea + 1); + print_set_envcolour(0xFF, 0x40, 0x40, 0xFF); + } + else + if ((u32) gCurrLevelNum-1 == i) { + sprintf(textBytes, "%s", sLevelNames[i]); + print_set_envcolour(0xFF, 0xFF, 0x40, 0xFF); + } + else{ + sprintf(textBytes, "%s", sLevelNames[i]); + print_set_envcolour(0xFF, 0xFF, 0xFF, 0xFF); + } + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) - 40 + posY, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + } +} + +u8 gLastWarpID = 0; + +void puppyprint_render_general_vars(void) { + + char textBytes[200]; + u32 floorType = 0; + u32 objParams = 0; + if (gMarioState->floor) { + floorType = gMarioState->floor->type; + } + if (gMarioState->interactObj) { + objParams = gMarioState->interactObj->oBehParams; + } + + + sprintf(textBytes, "World\n\nObjects: %d/%d\n\nLevel ID: %d\nCourse ID: %d\nArea ID: %d\nRoom ID: %d\n\nInteract: \n0x%08X\nWarp: 0x%02X", + gObjectCounter, + OBJECT_POOL_CAPACITY, + gCurrLevelNum, + gCurrCourseNum, + gCurrAreaIndex, + gMarioCurrentRoom, + objParams, + gLastWarpID + ); + print_small_text_light(SCREEN_WIDTH - 16, 36, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); + +#ifndef ENABLE_CREDITS_BENCHMARK + // Very little point printing useless info if Mario doesn't even exist. + if (gMarioState->marioObj) { + sprintf(textBytes, "Mario\n\nX: %d\nY: %d\nZ: %d\nYaw: 0x%04X\n\nfVel: %1.1f\nyVel: %1.1f\n\nHealth: %03X\nAction: 0x%02X\nFloor Type: 0x%02X\nWater Height: %d", + (s32)(gMarioState->pos[0]), + (s32)(gMarioState->pos[1]), + (s32)(gMarioState->pos[2]), + (u16)(gMarioState->faceAngle[1]), + (f32)(gMarioState->forwardVel), + (f32)(gMarioState->vel[1]), + (s32)(gMarioState->health), + (u32)(gMarioState->action & ACT_ID_MASK), + (u32)(floorType), + (s32)(gMarioState->waterLevel) + ); + print_small_text_light(16, 36, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + sprintf(textBytes, "Gfx Pool: %d / %d", ((u32)gDisplayListHead - ((u32)gGfxPool->buffer)) / 4, GFX_POOL_SIZE); + print_small_text_light(SCREEN_WIDTH/2, SCREEN_HEIGHT-16, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); + } +#endif +} + struct PuppyPrintPage ppPages[] = { - {&puppyprint_render_standard, "Standard" }, - {&puppyprint_render_minimal, "Minimal" }, - {&print_audio_ram_overview, "Audio" }, - {&print_ram_overview, "Segments" }, - {&puppyprint_render_collision, "Collision"}, - {&print_console_log, "Log" }, +#ifdef USE_PROFILER + {&puppyprint_render_standard, "Profiler"}, + {&puppyprint_render_minimal, "Minimal"}, +#endif + {&puppyprint_render_general_vars, "General"}, + {&print_audio_overview, "Audio"}, + {&print_ram_overview, "Segments"}, + {&puppyprint_render_collision, "Collision"}, + {&print_console_log, "Log"}, + {&puppyprint_level_select_menu, "Level Select"}, + {&render_coverage_map, "Coverage"}, +#ifdef PUPPYCAM + {&puppycamera_debug_view, "Unlock Camera"}, +#endif }; #define MENU_BOX_WIDTH 128 @@ -610,8 +674,8 @@ void render_page_menu(void) { s32 scrollY = (36 / (MAX_DEBUG_OPTIONS - 1)); prepare_blank_box(); - render_blank_box(32, 32, (32 + MENU_BOX_WIDTH), (32 + 72), 0x00, 0x00, 0x00, 0xC0); - render_blank_box(((32 + MENU_BOX_WIDTH) - 8), (32 + (scrollY * sDebugOption)), (32 + MENU_BOX_WIDTH), (32 + (scrollY * sDebugOption) + 36), 0xFF, 0xFF, 0xFF, 0xFF); + render_blank_box_rounded(32, 32, (32 + MENU_BOX_WIDTH), (32 + 72), 0x00, 0x00, 0x00, 0xC0); + render_blank_box_rounded(((32 + MENU_BOX_WIDTH) - 8), (32 + (scrollY * sDebugOption)), (32 + MENU_BOX_WIDTH), (32 + (scrollY * sDebugOption) + 36), 0xFF, 0xFF, 0xFF, 0xFF); finish_blank_box(); for (i = 0; i < (s32)MAX_DEBUG_OPTIONS; i++) { @@ -624,27 +688,34 @@ void render_page_menu(void) { print_set_envcolour(0xFF, 0xFF, 0xFF, 0xFF); } - print_small_text((28 + (MENU_BOX_WIDTH / 2)), posY, ppPages[i].name, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 0); + print_small_text_light((28 + (MENU_BOX_WIDTH / 2)), posY, ppPages[i].name, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); } } } void puppyprint_render_profiler(void) { + PUPPYPRINT_GET_SNAPSHOT(); + bzero(&gCurrEnvCol, sizeof(ColorRGBA)); print_set_envcolour(255, 255, 255, 255); if (!fDebug) { + profiler_update(PROFILER_TIME_PUPPYPRINT1, osGetCount() - first); return; } - - (ppPages[sPPDebugPage].func)(); + if (ppPages[sPPDebugPage].func != NULL) { + (ppPages[sPPDebugPage].func)(); + } if (sDebugMenu) { render_page_menu(); } + profiler_update(PROFILER_TIME_PUPPYPRINT1, osGetCount() - first); } void puppyprint_profiler_process(void) { + PUPPYPRINT_GET_SNAPSHOT(); + if (fDebug && (gPlayer1Controller->buttonPressed & L_TRIG)) { sDebugMenu ^= TRUE; if (sDebugMenu == FALSE) { @@ -671,18 +742,91 @@ void puppyprint_profiler_process(void) { if (sDebugOption >= (sizeof(ppPages) / sizeof(struct PuppyPrintPage))) { sDebugOption = 0; } + } else { + if (sPPDebugPage == PUPPYPRINT_PAGE_LEVEL_SELECT) + { + if (gPlayer1Controller->buttonPressed & U_JPAD) { + sLevelSelectOption--; + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption--; + } + if (sLevelSelectOption <= 0) { + sLevelSelectOption = LEVEL_COUNT - 2; + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption--; + } + } + } + if (gPlayer1Controller->buttonPressed & D_JPAD) { + sLevelSelectOption = (sLevelSelectOption + 1) % (LEVEL_COUNT - 2); + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption = (sLevelSelectOption + 1) % (LEVEL_COUNT - 2); + } + } + if (gPlayer1Controller->buttonPressed & R_JPAD) { + sLevelSelectOptionArea++; + if (sLevelSelectOptionArea > AREA_COUNT - 1) { + sLevelSelectOptionArea = 0; + } + } else if (gPlayer1Controller->buttonPressed & L_JPAD) { + sLevelSelectOptionArea--; + if (sLevelSelectOptionArea < 0) { + sLevelSelectOptionArea = AREA_COUNT - 1; + } + } + if (gPlayer1Controller->buttonPressed & A_BUTTON) { + sPPDebugPage = 0; + gPuppyWarp = sLevelSelectOption + 1; + gPuppyWarpArea = sLevelSelectOptionArea + 1; + } + } else { + if (gCurrLevelNum > 3) { + sLevelSelectOption = gCurrLevelNum; + } else { + sLevelSelectOption = LEVEL_CASTLE_GROUNDS; + } + } + // Collision toggles. +#ifdef VISUAL_DEBUG + if (sPPDebugPage == PUPPYPRINT_PAGE_COLLISION) + { + if (gPlayer1Controller->buttonPressed & R_JPAD) + viewCycle++; + if (gPlayer1Controller->buttonPressed & L_JPAD) + viewCycle--; + if (viewCycle == 4) + viewCycle = 0; + if (viewCycle == 255) + viewCycle = 3; + } +#endif + if (sPPDebugPage == PUPPYPRINT_PAGE_RAM) { + if (gPlayer1Controller->buttonDown & U_JPAD && gPPSegScroll > 0) { + gPPSegScroll -= 4; + } else if (gPlayer1Controller->buttonDown & D_JPAD && gPPSegScroll < (12 * 32)){ + gPPSegScroll += 4; + } + } } + profiler_update(PROFILER_TIME_PUPPYPRINT2, osGetCount() - first); } #endif -void print_set_envcolour(s32 r, s32 g, s32 b, s32 a) { - if ((r != currEnv[0]) - || (g != currEnv[1]) - || (b != currEnv[2]) - || (a != currEnv[3])) { +s32 print_set_envcolour(u8 r, u8 g, u8 b, u8 a) { + if ((r != gCurrEnvCol[0]) + || (g != gCurrEnvCol[1]) + || (b != gCurrEnvCol[2]) + || (a != gCurrEnvCol[3])) { gDPSetEnvColor(gDisplayListHead++, (Color)r, (Color)g, (Color)b, (Color)a); - vec4_set(currEnv, r, g, b, a); + vec4_set(gCurrEnvCol, r, g, b, a); + + return TRUE; } + + return FALSE; } #define BLANK 0, 0, 0, ENVIRONMENT, 0, 0, 0, ENVIRONMENT @@ -700,11 +844,27 @@ void finish_blank_box(void) { // This does some epic shenanigans to figure out the optimal way to draw this. // If the width is a multiple of 4, then use fillmode (fastest) // Otherwise, if there's transparency, it uses that rendermode, which is slower than using opaque rendermodes. -void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a) { +void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a) { + if (x2 < x1) + { + u32 temp = x2; + x2 = x1; + x1 = temp; + } + if (y2 < y1) + { + u32 temp = y2; + y2 = y1; + y1 = temp; + } + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + if (x2 > SCREEN_WIDTH) x2 = SCREEN_WIDTH; + if (y2 > SCREEN_HEIGHT) y2 = SCREEN_HEIGHT; s32 cycleadd = 0; gDPPipeSync(gDisplayListHead++); if (((absi(x1 - x2) % 4) == 0) && (a == 255)) { - gDPSetCycleType( gDisplayListHead++, G_CYC_FILL); + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetRenderMode(gDisplayListHead++, G_RM_NOOP, G_RM_NOOP); cycleadd = 1; } else { @@ -720,28 +880,95 @@ void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a gDPSetFillColor(gDisplayListHead++, (GPACK_RGBA5551(r, g, b, 1) << 16) | GPACK_RGBA5551(r, g, b, 1)); print_set_envcolour(r, g, b, a); gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - cycleadd, y2 - cycleadd); + gDPPipeSync(gDisplayListHead++); } -extern s32 text_iterate_command(const char *str, s32 i, s32 runCMD); -extern void get_char_from_byte(u8 letter, s32 *textX, s32 *textY, s32 *spaceX, s32 *offsetY, s32 font); +// Same as above, but with rounded edges. +// Follows all the same rules of usage. +void render_blank_box_rounded(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a) { + if (x2 < x1) + { + u32 temp = x2; + x2 = x1; + x1 = temp; + } + if (y2 < y1) + { + u32 temp = y2; + y2 = y1; + y1 = temp; + } + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + if (x2 > SCREEN_WIDTH) x2 = SCREEN_WIDTH; + if (y2 > SCREEN_HEIGHT) y2 = SCREEN_HEIGHT; + s32 cycleadd = 0; + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); + if (a == 255) { + gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + } + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 1) << 16 | GPACK_RGBA5551(r, g, b, 1)); + print_set_envcolour(r, g, b, a); + gDPFillRectangle(gDisplayListHead++, x1+4, y1, x2-4, y1+1); + gDPFillRectangle(gDisplayListHead++, x1+2, y1+1, x2-2, y1+2); + gDPFillRectangle(gDisplayListHead++, x1+1, y1+2, x2-1, y1+4); + gDPFillRectangle(gDisplayListHead++, x1+1, y2-4, x2-1, y2-2); + gDPFillRectangle(gDisplayListHead++, x1+2, y2-2, x2-2, y2-1); + gDPFillRectangle(gDisplayListHead++, x1+4, y2-1, x2-4, y2); + if (ABS(x1 - x2) % 4 == 0 && a == 255) { + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); + gDPSetRenderMode(gDisplayListHead++, G_RM_NOOP, G_RM_NOOP); + cycleadd = 1; + } + gDPFillRectangle(gDisplayListHead++, x1, y1+4, x2 - cycleadd, y2-4 - cycleadd); + gDPPipeSync(gDisplayListHead++); +} + +s8 shakeToggle = 0; +s8 waveToggle = 0; +s8 rainbowToggle = 0; +f32 textSize = 1.0f; // The value that's used as a baseline multiplier before applying text size modifiers. Make sure to set it back when you're done. +f32 textSizeTotal = 1.0f; // The value that's read to set the text size. Do not mess with this. +f32 textSizeTemp = 1.0f; // The value that's set when modifying text size mid draw. Also do not mess with this. +u16 textTempScale = 1024; // A fixed point means of referring to scale. +u8 textOffsets[2]; // Represents the dimensions of the text (12 x 8), and written to when size is modified. +u8 topLineHeight; // Represents the peak line height of the current line. Prevents vertical overlapping. +u8 gMonoSpace = FALSE; // Ignore kerning. s32 get_text_width(const char *str, s32 font) { s32 i = 0; s32 textPos = 0; s32 wideX = 0; - s32 textX, textY, offsetY, spaceX; + s32 textX; + s8 offsetY; + u8 spaceX; + s32 strLen = (signed)strlen(str); + s32 commandOffset; + u8 pad; - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos = 0; - } - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); + textSizeTemp = 1.0f; + textSizeTotal = textSizeTemp * textSize; + + for (i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + if (str[i] == '\n') { + textPos = 0; + break; + } } - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - textPos += spaceX + 1; + if (i >= strLen) + break; + + get_char_from_byte(&textX, &textPos, str[i], &pad, &spaceX, &offsetY, font); + textPos += (spaceX + 1) * textSizeTotal; wideX = MAX(textPos, wideX); } return wideX; @@ -749,18 +976,39 @@ s32 get_text_width(const char *str, s32 font) { s32 get_text_height(const char *str) { s32 i= 0; - s32 textPos = 0; + s32 textPos; + s32 strLen = (signed)strlen(str); + s32 commandOffset; - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos += 12; + textSizeTemp = 1.0f; + textSizeTotal = textSizeTemp * textSize; + topLineHeight = 12 * textSizeTotal; + textPos = topLineHeight; + + for (i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + } + + if (i >= strLen) + break; + + if (str[i] == '\n') { + textPos += topLineHeight; + topLineHeight = 12 * textSizeTotal; + continue; } } return textPos; } + + const Gfx dl_small_text_begin[] = { gsDPPipeSync(), gsDPSetCycleType( G_CYC_1CYCLE), @@ -770,256 +1018,510 @@ const Gfx dl_small_text_begin[] = { gsSPEndDisplayList(), }; -s8 shakeToggle = 0; -s8 waveToggle = 0; +void set_text_size_params(void) { + textSizeTotal = textSizeTemp * textSize; + textTempScale = 1024/textSizeTotal; + textOffsets[0] = 8 * textSizeTotal; + textOffsets[1] = 12 * textSizeTotal; + topLineHeight = MAX(12.0f * textSizeTotal, topLineHeight); +} -void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, s32 font) { +static s8 sTextShakeTable[] = { + 1, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 1, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 1, 0, 1, 0, 0 +}; + +void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font) { s32 textX = 0; - s32 textY = 0; - s32 offsetY = 0; - s32 i = 0; s32 textPos[2] = { 0, 0 }; - s32 spaceX = 0; - s32 wideX[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - s32 tx = amount; - s32 shakePos[2]; - s32 wavePos; - s32 lines = 0; - s32 xlu = currEnv[3]; + u16 wideX[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + s32 textLength = amount; s32 prevxlu = 256; // Set out of bounds, so it will *always* be different at first. - Texture *(*fontTex)[] = segmented_to_virtual(&puppyprint_font_lut); + s32 strLen = strlen(str); + s32 commandOffset; + f32 wavePos; + f32 shakePos[2]; + s8 offsetY = 0; + u8 spaceX = 0; + u8 widthX = 0; + u8 lines = 0; + u8 xlu = gCurrEnvCol[3]; + u8 shakeTablePos = 0; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); shakeToggle = 0; - waveToggle = 0; + waveToggle = 0; + rainbowToggle = 0; + textSizeTemp = 1.0f; + set_text_size_params(); - if (amount == PRINT_ALL) { - tx = (signed)strlen(str); + if (amount <= PRINT_ALL || amount > strLen) { + textLength = strLen; } + // Calculate the text width for centre and right aligned text. gSPDisplayList(gDisplayListHead++, dl_small_text_begin); - if (align == PRINT_TEXT_ALIGN_CENTRE) { - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; + if (align == PRINT_TEXT_ALIGN_CENTRE || align == PRINT_TEXT_ALIGN_RIGHT) { + for (s32 i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + } + + if (i >= strLen || str[i] == '\0') + break; + + if (str[i] == '\n') { textPos[0] = 0; lines++; + wideX[lines] = 0; + continue; } - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); - } - - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - textPos[0] += (spaceX + 1); + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + textPos[0] += (spaceX + 1) * textSizeTotal; wideX[lines] = MAX(textPos[0], wideX[lines]); } - textPos[0] = -(wideX[0] / 2); - } else if (align == PRINT_TEXT_ALIGN_RIGHT) { - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos[0] = 0; - lines++; - } else { - textPos[0] += (spaceX + 1); - } - - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); - } - - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - - wideX[lines] = MAX(textPos[0], wideX[lines]); + if (align == PRINT_TEXT_ALIGN_CENTRE) { + textPos[0] = -(wideX[0] / 2); + } else { + textPos[0] = -(wideX[0]); } - textPos[0] = -wideX[0]; } + //Reset text size properties. + textSizeTemp = 1.0f; + set_text_size_params(); + topLineHeight = 12.0f * textSizeTotal; lines = 0; - gDPLoadTextureBlock_4b(gDisplayListHead++, (*fontTex)[font], G_IM_FMT_I, 128, 60, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); - for (i = 0; i < tx; i++) { - if (str[i] == '#') { - i++; + + shakeTablePos = gGlobalTimer % sizeof(sTextShakeTable); + gDPLoadTextureBlock_4b(gDisplayListHead++, fnt->tex, fnt->fmt, fnt->imW, fnt->imH, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); + + for (s32 i = 0, j = 0; i < textLength; i++, j++) { + if (str[i] == '\n') { lines++; if (align == PRINT_TEXT_ALIGN_RIGHT) { - textPos[0] = -(wideX[lines] ); + textPos[0] = -(wideX[lines]); } else { textPos[0] = -(wideX[lines] / 2); } - textPos[1] += 12; + textPos[1] += topLineHeight; + topLineHeight = (f32) fnt->txH * textSizeTotal; + continue; } - if (str[i] == '<') { - i += text_iterate_command(str, i, TRUE); + while (i < textLength && str[i] == '<') { + commandOffset = text_iterate_command(str, i, TRUE); + if (commandOffset == 0) + break; + + i += commandOffset; + textLength += commandOffset; + } + + if (i >= textLength || str[i] == '\0') { + break; } if (shakeToggle) { - shakePos[0] = (-1 + (random_u16() & 0x1)); - shakePos[1] = (-1 + (random_u16() & 0x1)); + shakePos[0] = (sTextShakeTable[shakeTablePos++] * textSizeTotal); + if (shakeTablePos == sizeof(sTextShakeTable)) + shakeTablePos = 0; + shakePos[1] = sTextShakeTable[shakeTablePos++] * textSizeTotal; + if (shakeTablePos == sizeof(sTextShakeTable)) + shakeTablePos = 0; } else { shakePos[0] = 0; shakePos[1] = 0; } + if (waveToggle) { - wavePos = ((sins((gGlobalTimer * 3000) + (i * 10000))) * 2); + wavePos = ((sins((gGlobalTimer * 3000) + (j * 10000))) * 2) * textSizeTotal; } else { wavePos = 0; } - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - if (xlu != prevxlu) { - prevxlu = xlu; - if (xlu > 250) { - gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); - } else { - gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + s32 goddamnJMeasure = textX == 256 ? -1 : 0; // Hack to fix a rendering bug. + if (str[i] != ' ' && str[i] != '\t') { + if (xlu != prevxlu) { + prevxlu = xlu; + if (xlu > 250) { + gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + } } + + gSPScisTextureRectangle(gDisplayListHead++, (x + textPos[0] + (s16)(shakePos[0])) << 2, + (y + textPos[1] + (s16)((shakePos[1] + offsetY + wavePos))) << 2, + (x + textPos[0] + (s16)((shakePos[0] + (widthX * textSizeTotal)))) << 2, + (y + textPos[1] + (s16)((wavePos + offsetY + shakePos[1] + textOffsets[1]))) << 2, + G_TX_RENDERTILE, (textX << 6) + goddamnJMeasure, 0, textTempScale, textTempScale); + } + textPos[0] += (spaceX + 1) * textSizeTotal; + } + + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + // Color reverted to pure white in dl_rgba16_text_end, so carry it over to gCurrEnvCol! + // NOTE: if this behavior is ever removed, make sure gCurrEnvCol gets enforced here if the text color is ever altered in the text_iterate_command function. + gCurrEnvCol[0] = 255; gCurrEnvCol[1] = 255; gCurrEnvCol[2] = 255; gCurrEnvCol[3] = 255; +} + +// A more lightweight version of print_small_text. +// Strips all text modifiers so that only standard text remains. +// Can still support external colouring. +// Around 30% faster than regular printing. +void print_small_text_light(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font) { + s32 textX = 0; + s32 textPos[2] = { 0, 0 }; + u16 wideX[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + s32 textLength = amount; + s32 prevxlu = 256; // Set out of bounds, so it will *always* be different at first. + s32 strLen = strlen(str); + s8 offsetY = 0; + u8 spaceX = 0; + u8 lines = 0; + u8 widthX = 0; + u8 xlu = gCurrEnvCol[3]; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); + + if (amount <= PRINT_ALL || amount > strLen) { + textLength = strLen; + } + + // Calculate the text width for centre and right aligned text. + gSPDisplayList(gDisplayListHead++, dl_small_text_begin); + if (align == PRINT_TEXT_ALIGN_CENTRE || align == PRINT_TEXT_ALIGN_RIGHT) { + for (s32 i = 0; i < strLen; i++) { + if (str[i] == '\n') { + textPos[0] = 0; + lines++; + wideX[lines] = 0; + continue; + } + + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + textPos[0] += (spaceX + 1) * textSizeTotal; + wideX[lines] = MAX(textPos[0], wideX[lines]); } - gSPScisTextureRectangle(gDisplayListHead++, ((x + shakePos[0] + textPos[0] ) << 2), - ((y + shakePos[1] + offsetY + textPos[1] + wavePos) << 2), - ((x + textPos[0] + shakePos[0] + 8) << 2), - ((y + wavePos + offsetY + shakePos[1] + 12 + textPos[1]) << 2), - G_TX_RENDERTILE, (textX << 6), (textY << 6), (1 << 10), (1 << 10)); + if (align == PRINT_TEXT_ALIGN_CENTRE) { + textPos[0] = -(wideX[0] / 2); + } else { + textPos[0] = -(wideX[0]); + } + } + + lines = 0; + gDPLoadTextureBlock_4b(gDisplayListHead++, fnt->tex, fnt->fmt, fnt->imW, fnt->imH, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); + + for (s32 i = 0, j = 0; i < textLength; i++, j++) { + if (str[i] == '\n') { + lines++; + if (align == PRINT_TEXT_ALIGN_RIGHT) { + textPos[0] = -(wideX[lines]); + } else { + textPos[0] = -(wideX[lines] / 2); + } + textPos[1] += 12; + continue; + } + + if (i >= textLength || str[i] == '\0') { + break; + } + + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + s32 goddamnJMeasure = textX == 256 ? -1 : 0; // Hack to fix a rendering bug. + if (str[i] != ' ' && str[i] != '\t') { + if (xlu != prevxlu) { + prevxlu = xlu; + if (xlu > 250) { + gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + } + } + + gSPScisTextureRectangle(gDisplayListHead++, (x + textPos[0]) << 2, + (y + textPos[1] + offsetY) << 2, + (x + textPos[0] + widthX) << 2, + (y + textPos[1] + offsetY + fnt->txH) << 2, + G_TX_RENDERTILE, (textX << 6) + goddamnJMeasure, 0, 1024, 1024); + } textPos[0] += (spaceX + 1); } gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + // Color reverted to pure white in dl_rgba16_text_end, so carry it over to gCurrEnvCol! + // NOTE: if this behavior is ever removed, make sure gCurrEnvCol gets enforced here if the text color is ever altered in the text_iterate_command function. + gCurrEnvCol[0] = 255; gCurrEnvCol[1] = 255; gCurrEnvCol[2] = 255; gCurrEnvCol[3] = 255; +} + +// Return color hex nibble +s32 get_hex_value_at_offset(const char *str, s32 primaryOffset, u32 nibbleOffset, u32 garbageReturnsEnv) { + s32 val = str[primaryOffset + nibbleOffset]; + s32 shiftVal = 4 * ((nibbleOffset + 1) % 2); + + if (nibbleOffset > 7) + garbageReturnsEnv = FALSE; + + if (val >= 'A' && val <= 'F') + return (val - 'A' + 0xA) << shiftVal; + if (val >= 'a' && val <= 'f') + return (val - 'a' + 0xA) << shiftVal; + if (val >= '0' && val <= '9') + return (val - '0') << shiftVal; + + if (garbageReturnsEnv) // Return gCurrEnvCol color value + return gCurrEnvCol[nibbleOffset / 2] & (0x0F << shiftVal); + + // Just return 0 otherwise + return 0; } s32 text_iterate_command(const char *str, s32 i, s32 runCMD) { s32 len = 0; - while ((str[i + len] != '>') && ((i + len) < (signed)strlen(str))) len++; + const char *newStr = &str[i]; + s32 lastCharIndex = (signed)strlen(newStr) - 1; + + while ((newStr[len] != '>') && (len < lastCharIndex)) len++; + if (newStr[len] != '>') + return 0; + len++; - if (runCMD) { - if (strncmp((str + i), "", 5) == 0) { // Simple text colour effect. goes up to 99 for each, so 99000000 is red. - // Each value is taken from the strong. The first is multiplied by 10, because it's a larger significant value, then it adds the next digit onto it. - s32 r = (((str[i + 5] - '0') * 10) - + (str[i + 6] - '0')); - s32 g = (((str[i + 7] - '0') * 10) - + (str[i + 8] - '0')); - s32 b = (((str[i + 9] - '0') * 10) - + (str[i + 10] - '0')); - s32 a = (((str[i + 11] - '0') * 10) - + (str[i + 12] - '0')); - // Multiply each value afterwards by 2.575f to make 255. - print_set_envcolour((r * 2.575f), - (g * 2.575f), - (b * 2.575f), - (a * 2.575f)); - } else if (strncmp((str + i), "", 6) == 0) { // Same as above, except it fades between two colours. The third set of numbers is the speed it fades. - s32 r = (((str[i + 6] - '0') * 10) - + (str[i + 7] - '0')); - s32 g = (((str[i + 8] - '0') * 10) - + (str[i + 9] - '0')); - s32 b = (((str[i + 10] - '0') * 10) - + (str[i + 11] - '0')); - s32 a = (((str[i + 12] - '0') * 10) - + (str[i + 13] - '0')); - s32 r2 = (((str[i + 15] - '0') * 10) - + (str[i + 16] - '0')); - s32 g2 = (((str[i + 17] - '0') * 10) - + (str[i + 18] - '0')); - s32 b2 = (((str[i + 19] - '0') * 10) - + (str[i + 20] - '0')); - s32 a2 = (((str[i + 21] - '0') * 10) - + (str[i + 22] - '0')); - s32 spd = (((str[i + 24] - '0') * 10) - + (str[i + 25] - '0')); - // Find the median. - s32 r3 = (r + r2) * 1.2875f; - s32 g3 = (g + g2) * 1.2875f; - s32 b3 = (b + b2) * 1.2875f; - s32 a3 = (a + a2) * 1.2875f; - // Find the difference. - s32 r4 = (r - r2) * 1.2875f; - s32 g4 = (g - g2) * 1.2875f; - s32 b4 = (b - b2) * 1.2875f; - s32 a4 = (a - a2) * 1.2875f; - // Now start from the median, and wave from end to end with the difference, to create the fading effect. - f32 sTimer = sins(gGlobalTimer * spd * 50); - print_set_envcolour((r3 + (sTimer * r4)), - (g3 + (sTimer * g4)), - (b3 + (sTimer * b4)), - (a3 + (sTimer * a4))); - } else if (strncmp((str + i), "", 8) == 0) { // Toggles the happy colours :o) Do it again to disable it. - s32 r = (coss( gGlobalTimer * 600 ) + 1) * 127; - s32 g = (coss((gGlobalTimer * 600) + 21845) + 1) * 127; - s32 b = (coss((gGlobalTimer * 600) - 21845) + 1) * 127; - print_set_envcolour(r, g, b, 255); - } else if (strncmp((str + i), "", 7) == 0) { // Toggles text that shakes on the spot. Do it again to disable it. - shakeToggle ^= 1; - } else if (strncmp((str + i), "", 6) == 0) { // Toggles text that waves around. Do it again to disable it. - waveToggle ^= 1; + // Ignores runCMD, because it's important this is ALWAYS ran. + if (len == 10 && strncmp((newStr), "", 6) == 0) { // Set the text size here. 100 is scale 1.0, with 001 being scale 0.01. this caps at 999. Going lower than 001 + // Will make the text unreadable on console, so only do it, + textSizeTemp = (newStr[6] - '0'); + textSizeTemp += (newStr[7] - '0')/10.0f; + textSizeTemp += (newStr[8] - '0')/100.0f; + textSizeTemp = CLAMP(textSizeTemp, 0.01f, 10.0f); + set_text_size_params(); + } else if (len == 14 && strncmp((newStr), "", 5) == 0) { // Simple text colour effect. goes up to FF for each, so FF0000FF is red. + // Each value is taken from the string. The first is shifted left 4 bits, because it's a larger significant value, then it adds the next digit onto it. + // Reverting to envcoluor can be achieved by passing something like , or it could be combined with real colors for just partial reversion like for instance. + if (!runCMD) + return len; + + s32 rgba[4]; + + for (s32 j = 0; j < 4; j++) { + rgba[j] = get_hex_value_at_offset(newStr, 5, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 5, (2 * j) + 1, TRUE); } + + rainbowToggle = 0; + gDPSetEnvColor(gDisplayListHead++, (Color) rgba[0], (Color) rgba[1], (Color) rgba[2], (Color) rgba[3]); // Don't use print_set_envcolour here + } else if (len == 27 && strncmp((newStr), "", 6) == 0) { // Same as above, except it fades between two colours. The third set of numbers is the speed it fades. + if (!runCMD) + return len; + + s32 rgba[4]; + + // Find transition speed and set timer value + s32 spd = get_hex_value_at_offset(newStr, 24, 0, FALSE) | get_hex_value_at_offset(newStr, 24, 1, FALSE); + f32 sTimer = sins(gGlobalTimer * spd * 50); + + for (s32 j = 0; j < 4; j++) { + s32 col1 = get_hex_value_at_offset(newStr, 6, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 6, (2 * j) + 1, TRUE); + s32 col2 = get_hex_value_at_offset(newStr, 15, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 15, (2 * j) + 1, TRUE); + + // Final color value determined by median of two colors + a point in the end-to-end width of the difference between the two colors. + // Said point changes based on the sTimer value in the form of a sine wave, which helps to create the fading effect. + rgba[j] = ((col1 + col2) / 2) + (s32) (sTimer * ((col1 - col2) / 2)); + } + + rainbowToggle = 0; + gDPSetEnvColor(gDisplayListHead++, (Color) rgba[0], (Color) rgba[1], (Color) rgba[2], (Color) rgba[3]); // Don't use print_set_envcolour here + } else if (len == 9 && strncmp((newStr), "", 9) == 0) { // Toggles the happy colours :o) Do it again to disable it. + if (!runCMD) + return len; + + rainbowToggle ^= 1; + if (rainbowToggle) { + s32 r = (coss(gGlobalTimer * 600) + 1) * 127; + s32 g = (coss((gGlobalTimer * 600) + (0x10000 / 3)) + 1) * 127; + s32 b = (coss((gGlobalTimer * 600) - (0x10000 / 3)) + 1) * 127; + gDPSetEnvColor(gDisplayListHead++, (Color) r, (Color) g, (Color) b, (Color) gCurrEnvCol[3]); // Don't use print_set_envcolour here, also opt to use alpha value from gCurrEnvCol + } else { + gDPSetEnvColor(gDisplayListHead++, (Color) gCurrEnvCol[0], (Color) gCurrEnvCol[1], (Color) gCurrEnvCol[2], (Color) gCurrEnvCol[3]); // Reset text to envcolor + } + } else if (len == 7 && strncmp((newStr), "", 7) == 0) { // Toggles text that shakes on the spot. Do it again to disable it. + if (!runCMD) + return len; + + shakeToggle ^= 1; + } else if (len == 6 && strncmp((newStr), "", 6) == 0) { // Toggles text that waves around. Do it again to disable it. + if (!runCMD) + return len; + + waveToggle ^= 1; + } else { + return 0; // Invalid command string; display everything inside to make this clear to the user. } + return len; } -void get_char_from_byte(u8 letter, s32 *textX, s32 *textY, s32 *spaceX, s32 *offsetY, s32 font) { +void get_char_from_byte(s32 *textX, s32 *textPos, u8 letter, u8 *wideX, u8 *spaceX, s8 *offsetY, u8 font) { *offsetY = 0; - u8 **textKern = segmented_to_virtual(puppyprint_kerning_lut); - u8 *textLen = segmented_to_virtual(textKern[font]); + u32 let = letter - '!'; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); - if (letter >= '0' && letter <= '9') { // Line 1 - *textX = ((letter - '0') * 4); - *textY = 0; - *spaceX = textLen[(letter - '0') + 0]; - } else if (letter >= 'A' && letter <= 'P') { // Line 2 - *textX = ((letter - 'A') * 4); - *textY = 6; - *spaceX = textLen[(letter - 'A') + 16]; - } else if (letter >= 'Q' && letter <= 'Z') { // Line 3 - *textX = ((letter - 'Q') * 4); - *textY = 12; - *spaceX = textLen[(letter - 'Q') + 32]; - } else if (letter >= 'a' && letter <= 'p') { // Line 4 - *textX = ((letter - 'a') * 4); - *textY = 18; - *spaceX = textLen[(letter - 'a') + 48]; - } else if (letter >= 'q' && letter <= 'z') { // Line 5 - *textX = ((letter - 'q') * 4); - *textY = 24; - *spaceX = textLen[(letter - 'q') + 64]; - } else { // Space, the final frontier. - *textX = 128; - *textY = 12; - *spaceX = 2; + if (letter == ' ') { + *spaceX = 2; + return; + } + + if (letter == '\t') { + s32 offset = (*textPos % TAB_WIDTH); + if (offset == TAB_WIDTH - 1) { + offset -= TAB_WIDTH; + } + *textPos += TAB_WIDTH - offset - 1; + *spaceX = 0; + return; + } + + if (fnt->kern == NULL || gMonoSpace) { + *spaceX = fnt->txW; + } else { + u8 *kern = segmented_to_virtual(fnt->kern); + *spaceX = kern[let]; + + } + if (fnt->offset == NULL) { + *textX = let >> 2; + *wideX = fnt->txW; + } else { + u16 *off = segmented_to_virtual(fnt->offset); + *textX = off[let] >> 1; + *wideX = (off[let + 1] - off[let]); } switch (letter) { - case '-': *textX = 40; *textY = 0; *spaceX = textLen[10]; break; // Hyphen - case '+': *textX = 44; *textY = 0; *spaceX = textLen[11]; break; // Plus - case '(': *textX = 48; *textY = 0; *spaceX = textLen[12]; break; // Open Bracket - case ')': *textX = 52; *textY = 0; *spaceX = textLen[13]; break; // Close Bracket - case '!': *textX = 56; *textY = 0; *spaceX = textLen[14]; break; // Exclamation mark - case '?': *textX = 60; *textY = 0; *spaceX = textLen[15]; break; // Question mark - - case '"': *textX = 40; *textY = 12; *spaceX = textLen[42]; break; // Speech mark - case'\'': *textX = 44; *textY = 12; *spaceX = textLen[43]; break; // Apostrophe - case ':': *textX = 48; *textY = 12; *spaceX = textLen[44]; break; // Colon - case ';': *textX = 52; *textY = 12; *spaceX = textLen[45]; break; // Semicolon - case '.': *textX = 56; *textY = 12; *spaceX = textLen[46]; break; // Full stop - case ',': *textX = 60; *textY = 12; *spaceX = textLen[47]; break; // Comma - - case '~': *textX = 40; *textY = 24; *spaceX = textLen[74]; break; // Tilde - case '@': *textX = 44; *textY = 24; *spaceX = textLen[75]; break; // Umlaut - case '^': *textX = 48; *textY = 24; *spaceX = textLen[76]; break; // Caret - case '/': *textX = 52; *textY = 24; *spaceX = textLen[77]; break; // Slash - case '_': *textX = 56; *textY = 24; *spaceX = textLen[78]; break; // Percent - case '&': *textX = 60; *textY = 24; *spaceX = textLen[79]; break; // Ampersand - // This is for the letters that sit differently on the line. It just moves them down a bit. - case 'g': *offsetY = 1; break; - case 'q': *offsetY = 1; break; - case 'p': if (font == FONT_DEFAULT) *offsetY = 3; break; - case 'y': if (font == FONT_DEFAULT) *offsetY = 1; break; + case 'g': *offsetY = 2 * textSizeTotal; break; + case 'q': *offsetY = 2 * textSizeTotal; break; + case 'j': *offsetY = 2 * textSizeTotal; break; + case 'p': *offsetY = 2 * textSizeTotal; break; + case 'y': *offsetY = 2 * textSizeTotal; break; } } +// Because we're using bcopy when both reading and writing to the text buffer, this doesn't care about alignment with the multi-byte types. +struct PuppyprintDeferredBufferHeader { + u16 x; + u16 y; + u8 red; + u8 green; + u8 blue; + u8 alpha; + u8 alignment; + u8 textBufferLength; + u8 font; + u8 isLightText; +}; + +static u8 gIsLightText = FALSE; + +// This is where the deferred printing will be stored. When text is made, it will store text with a 12 byte header, then the rest will be the text data itself. +// The first 4 bytes of the header will be the X and Y pos +// The next 4 bytes will be the current envcolour set by print_set_envcolour +// Then the string length, text alignment, amount and font each get a byte. +// The data afterwards is the text data itself, using the string length byte to know when to stop. +#define HEADERSIZE sizeof(struct PuppyprintDeferredBufferHeader) +#define MAX_U8_STRING_SIZE (U8_MAX - 1) // Needs 255th character reserved for null terminator +void print_small_text_buffered(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font) { + s32 strLen = strlen(str); + + if (amount <= PRINT_ALL || amount > MAX_U8_STRING_SIZE) + amount = MAX_U8_STRING_SIZE; + + amount = MIN(strLen, amount); + if (amount <= 0) + return; // No point in printing an empty string + + // Compare the cursor position and the string length with null terminator, plus 12 (header size) and return if it overflows. + if (sPuppyprintTextBufferPos + HEADERSIZE + (u8) amount + 1 > sizeof(sPuppyprintTextBuffer)) + return; + + x += 0x8000; + y += 0x8000; + + struct PuppyprintDeferredBufferHeader header; + header.x = (x & 0xFFFF); + header.y = (y & 0xFFFF); + header.red = gCurrEnvCol[0]; + header.green = gCurrEnvCol[1]; + header.blue = gCurrEnvCol[2]; + header.alpha = gCurrEnvCol[3]; + header.alignment = align; + header.textBufferLength = (u8) amount; + header.font = font; + header.isLightText = gIsLightText; + + bcopy(&header, &sPuppyprintTextBuffer[sPuppyprintTextBufferPos], HEADERSIZE); + sPuppyprintTextBufferPos += HEADERSIZE; + bcopy(str, &sPuppyprintTextBuffer[sPuppyprintTextBufferPos], header.textBufferLength); + sPuppyprintTextBufferPos += header.textBufferLength; + sPuppyprintTextBuffer[sPuppyprintTextBufferPos++] = '\0'; // Apply null terminator onto end of string +} + +void print_small_text_buffered_light(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font) { + gIsLightText = TRUE; + print_small_text_buffered(x, y, str, align, amount, font); + gIsLightText = FALSE; +} + +void puppyprint_print_deferred(void) { + if (sPuppyprintTextBufferPos == 0) + return; + bzero(&gCurrEnvCol, sizeof(ColorRGBA)); + print_set_envcolour(255, 255, 255, 255); + + for (u32 i = 0; i < sPuppyprintTextBufferPos;) { + struct PuppyprintDeferredBufferHeader header; + bcopy(&sPuppyprintTextBuffer[i], &header, HEADERSIZE); + i += HEADERSIZE; + + s32 x = header.x; + s32 y = header.y; + x -= 0x8000; + y -= 0x8000; + ColorRGBA originalEnvCol = {gCurrEnvCol[0], gCurrEnvCol[1], gCurrEnvCol[2], gCurrEnvCol[3]}; + print_set_envcolour(header.red, header.green, header.blue, header.alpha); + + char *text = (char *) &sPuppyprintTextBuffer[i]; + if (header.isLightText) { + print_small_text_light(x, y, text, header.alignment, header.textBufferLength, header.font); + } else { + print_small_text(x, y, text, header.alignment, header.textBufferLength, header.font); + } + + print_set_envcolour(originalEnvCol[0], originalEnvCol[1], originalEnvCol[2], originalEnvCol[3]); + i += header.textBufferLength + 1; // Null terminator not accounted for by text buffer length + } + + //Reset the position back to zero, effectively clearing the buffer. + sPuppyprintTextBufferPos = 0; +} + void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNUSED s32 scaleX, UNUSED s32 scaleY, s32 mode) { s32 posW, posH, imW, imH, modeSC, mOne; s32 i = 0; @@ -1098,7 +1600,7 @@ void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNU gDPLoadSync(gDisplayListHead++); gDPLoadTextureTile(gDisplayListHead++, - image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), ((posH + imH) - 1), 0, (G_TX_NOMIRROR | G_TX_WRAP), (G_TX_NOMIRROR | G_TX_WRAP), maskW, maskH, 0, 0); + image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), ((posH + imH) - 1), 0, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), maskW, maskH, 0, 0); gSPScisTextureRectangle(gDisplayListHead++, ((x + posW) << 2), ((y + posH) << 2), @@ -1114,7 +1616,7 @@ void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNU posW = i * imW; gDPLoadSync(gDisplayListHead++); gDPLoadTextureTile(gDisplayListHead++, - image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), (height - 1), 0, (G_TX_NOMIRROR | G_TX_WRAP), (G_TX_NOMIRROR | G_TX_WRAP), maskW, maskH, 0, 0); + image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), (height - 1), 0, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), maskW, maskH, 0, 0); gSPScisTextureRectangle(gDisplayListHead++, (x + posW) << 2, (y + posH) << 2, diff --git a/src/game/puppyprint.h b/src/game/puppyprint.h index c5d90f88..39708df6 100644 --- a/src/game/puppyprint.h +++ b/src/game/puppyprint.h @@ -1,9 +1,45 @@ #pragma once -#include "segment2.h" +#include "profiling.h" -#define NUM_BENCH_ITERATIONS 150 +// This is how many indexes of timers are saved at once. higher creates a smoother average, but naturally uses more RAM. 32's fine. +#define NUM_PERF_ITERATIONS 32 +#define PERF_AGGREGATE NUM_PERF_ITERATIONS +#define PERF_TOTAL NUM_PERF_ITERATIONS + 1 #define LOG_BUFFER_SIZE 16 +#define PUPPYPRINT_DEFERRED_BUFFER_SIZE 0x1000 + +#ifdef PUPPYPRINT_DEBUG +#define PUPPYPRINT_ADD_COUNTER(x) x++ +#define PUPPYPRINT_GET_SNAPSHOT() u32 first = osGetCount() +#define PUPPYPRINT_GET_SNAPSHOT_TYPE(type) u32 first = profiler_get_delta(type) +#else +#define PUPPYPRINT_ADD_COUNTER(x) +#define PUPPYPRINT_GET_SNAPSHOT() +#define PUPPYPRINT_GET_SNAPSHOT_TYPE(type) +#endif + +struct PPTextFont { + Texture *tex; // Pointer to the texture for the font + u8 *kern; // Pointer to the kerning table + u16 *offset; // Pointer to the character offset table + u8 *pal; // Pointer to the texture pallete. Coming + u8 fmt; // Texture format, same as F3D macros + u8 siz; // Texture size, same as F3D macros + u16 imW; // Texture width + u8 imH; // Texture height + u8 txW; // Individual character width + u8 txH; // Individual character height +}; + +struct CallCounter { + u16 collision_floor; + u16 collision_wall; + u16 collision_ceil; + u16 collision_water; + u16 collision_raycast; + u16 matrix; +}; struct PuppyPrintPage{ void (*func)(); @@ -20,11 +56,39 @@ enum Benchmark { enum PuppyprintTextAlign { PRINT_TEXT_ALIGN_LEFT = 0, PRINT_TEXT_ALIGN_CENTRE = 1, + PRINT_TEXT_ALIGN_CENTER = 1, PRINT_TEXT_ALIGN_RIGHT = 2, PRINT_ALL = -1, }; -#if PUPPYPRINT_DEBUG +enum rspFlags +{ + RSP_NONE, + RSP_AUDIO_START, + RSP_GFX_START, + RSP_AUDIO_FINISHED, + RSP_GFX_FINISHED, + RSP_GFX_PAUSED, + RSP_GFX_RESUME, +}; + +enum PPPages { +#ifdef USE_PROFILER + PUPPYPRINT_PAGE_PROFILER, + PUPPYPRINT_PAGE_MINIMAL, +#endif + PUPPYPRINT_PAGE_GENERAL, + PUPPYPRINT_PAGE_AUDIO, + PUPPYPRINT_PAGE_RAM, + PUPPYPRINT_PAGE_COLLISION, + PUPPYPRINT_PAGE_LOG, + PUPPYPRINT_PAGE_LEVEL_SELECT, +#ifdef PUPPYCAM + PUPPYPRINT_PAGE_CAMERA +#endif +}; + +#ifdef PUPPYPRINT_DEBUG #ifdef BETTER_REVERB #define NUM_AUDIO_POOLS 7 #else @@ -35,35 +99,45 @@ enum PuppyprintTextAlign { enum PuppyFont { FONT_DEFAULT, FONT_OUTLINE, - FONT_NUM, + FONT_PLAIN, + FONT_VANILLA, + FONT_NUM }; extern u8 sPPDebugPage; extern u8 gPuppyFont; -extern s8 perfIteration; -extern s16 benchmarkLoop; -extern s32 benchmarkTimer; -extern ColorRGBA currEnv; +extern ColorRGBA gCurrEnvCol; extern s32 ramsizeSegment[33]; -extern s8 nameTable; +extern const s8 nameTable; extern s32 mempool; -extern u8 benchOption; +extern f32 textSize; +extern u32 gPoolMem; +extern u32 gMiscMem; +extern u8 gPuppyWarp; +extern u8 gPuppyWarpArea; +extern u8 gLastWarpID; +extern struct CallCounter gPuppyCallCounter; -// General -extern s32 benchMark[NUM_BENCH_ITERATIONS + 2]; - -extern void puppyprint_profiler_process(void); extern void puppyprint_render_profiler(void); -extern void puppyprint_profiler_finished(void); -extern void print_set_envcolour(s32 r, s32 g, s32 b, s32 a); +extern s32 print_set_envcolour(u8 r, u8 g, u8 b, u8 a); extern void prepare_blank_box(void); extern void finish_blank_box(void); -extern void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, s32 font); +extern void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font); extern void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, s32 scaleX, s32 scaleY, s32 mode); extern s32 get_text_height(const char *str); extern s32 get_text_width(const char *str, s32 font); extern void prepare_blank_box(void); extern void finish_blank_box(void); -extern void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a); +extern void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a); +extern void render_blank_box_rounded(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a); extern void append_puppyprint_log(const char *str, ...); extern char consoleLogTable[LOG_BUFFER_SIZE][255]; +extern void print_small_text_buffered(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font); +extern void puppyprint_print_deferred(void); +extern s32 puppyprint_strlen(const char *str); +extern void set_segment_memory_printout(u32 segment, u32 amount); +extern void print_small_text_light(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font); +extern void print_small_text_buffered_light(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font); +void puppyprint_profiler_process(void); +s32 text_iterate_command(const char *str, s32 i, s32 runCMD); +void get_char_from_byte(s32 *textX, s32 *textPos, u8 letter, u8 *wideX, u8 *spaceX, s8 *offsetY, u8 font); diff --git a/src/game/segment2.h b/src/game/segment2.h index c83f8ed7..20fd6d23 100644 --- a/src/game/segment2.h +++ b/src/game/segment2.h @@ -4,8 +4,9 @@ #include #include -extern void *puppyprint_font_lut[2]; -extern void *puppyprint_kerning_lut[2][80]; +extern void *puppyprint_font_lut[]; +extern void *puppyprint_kerning_lut[][82]; +extern const struct PPTextFont *const gPuppyPrintFontTable[]; extern u8 seg2_course_name_table[]; extern u8 seg2_act_name_table[]; diff --git a/src/game/sound_init.c b/src/game/sound_init.c index 3e5c41eb..d0988f59 100644 --- a/src/game/sound_init.c +++ b/src/game/sound_init.c @@ -368,14 +368,13 @@ void thread4_sound(UNUSED void *arg) { OSMesg msg; osRecvMesg(&sSoundMesgQueue, &msg, OS_MESG_BLOCK); - profiler_audio_started(); + profiler_audio_started(); // also starts PROFILER_TIME_SUB_AUDIO_UPDATE inside if (gResetTimer < 25) { - struct SPTask *spTask; - spTask = create_next_audio_frame_task(); + struct SPTask *spTask = create_next_audio_frame_task(); if (spTask != NULL) { dispatch_audio_sptask(spTask); } } - profiler_audio_completed(); + profiler_audio_completed(); // also completes PROFILER_TIME_SUB_AUDIO_UPDATE inside } } diff --git a/textures/segment2/custom_text.i4.png b/textures/segment2/custom_text.i4.png index c6ed0af8..4bf8a055 100644 Binary files a/textures/segment2/custom_text.i4.png and b/textures/segment2/custom_text.i4.png differ diff --git a/textures/segment2/custom_text2.i4.png b/textures/segment2/custom_text2.i4.png deleted file mode 100644 index 00c4f282..00000000 Binary files a/textures/segment2/custom_text2.i4.png and /dev/null differ diff --git a/textures/segment2/custom_text2.ia4.png b/textures/segment2/custom_text2.ia4.png new file mode 100644 index 00000000..389a6cf0 Binary files /dev/null and b/textures/segment2/custom_text2.ia4.png differ diff --git a/textures/segment2/custom_text3.i4.png b/textures/segment2/custom_text3.i4.png new file mode 100644 index 00000000..0e106e66 Binary files /dev/null and b/textures/segment2/custom_text3.i4.png differ diff --git a/textures/segment2/custom_text4.i4.png b/textures/segment2/custom_text4.i4.png new file mode 100644 index 00000000..7316c4bd Binary files /dev/null and b/textures/segment2/custom_text4.i4.png differ diff --git a/tools/n64graphics.c b/tools/n64graphics.c index f1783137..0e4513e1 100644 --- a/tools/n64graphics.c +++ b/tools/n64graphics.c @@ -466,7 +466,7 @@ ia *png2ia(const char *png_filename, int *width, int *height) switch (channels) { case 3: // red, green, blue case 4: // red, green, blue, alpha - ERROR("Warning: averaging RGB PNG to create IA\n"); + // ERROR("Warning: averaging RGB PNG to create IA\n"); // Comment out to silence Puppyprint texture warnings for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { int idx = j*w + i;