2018-10-17 16:00:01 +02:00
2018-10-16 01:43:49 +02:00
# include "fanart.hpp"
# include "memory/mem2.hpp"
2019-02-11 21:55:44 +01:00
# include "types.h"
2018-10-26 22:30:29 +02:00
2018-10-16 01:43:49 +02:00
using namespace std ;
static guVector _GRRaxisx = ( guVector ) { 1 , 0 , 0 } ; // DO NOT MODIFY!!!
static guVector _GRRaxisy = ( guVector ) { 0 , 1 , 0 } ; // Even at runtime
static guVector _GRRaxisz = ( guVector ) { 0 , 0 , 1 } ; // NOT ever!
CFanart : : CFanart ( void )
2018-10-26 22:30:29 +02:00
: m_animationComplete ( false ) , m_loaded ( false ) , m_faConfig ( ) , m_bg ( ) , m_bglq ( )
2018-10-16 01:43:49 +02:00
{
}
CFanart : : ~ CFanart ( void )
{
}
void CFanart : : unload ( )
{
2018-10-26 22:30:29 +02:00
m_faConfig . unload ( ) ;
2018-10-16 01:43:49 +02:00
m_loaded = false ;
for ( vector < CFanartElement > : : iterator Elm = m_elms . begin ( ) ; Elm ! = m_elms . end ( ) ; Elm + + )
Elm - > Cleanup ( ) ;
m_elms . clear ( ) ;
TexHandle . Cleanup ( m_bg ) ;
TexHandle . Cleanup ( m_bglq ) ;
}
2018-10-25 00:05:02 +02:00
char fanartDir [ 164 ] ;
2019-02-11 21:55:44 +01:00
bool CFanart : : load ( Config & m_wiiflowConfig , const char * path , const dir_discHdr * hdr )
2018-10-16 01:43:49 +02:00
{
2018-10-26 22:30:29 +02:00
if ( ! m_wiiflowConfig . getBool ( " FANART " , " enable_fanart " , true ) )
2019-02-11 21:55:44 +01:00
return false ;
2018-10-16 01:43:49 +02:00
unload ( ) ;
2019-02-11 21:55:44 +01:00
char id [ 64 ] ;
memset ( id , 0 , sizeof ( id ) ) ;
if ( NoGameID ( hdr - > type ) )
{
if ( strrchr ( hdr - > path , ' / ' ) ! = NULL )
wcstombs ( id , hdr - > title , sizeof ( id ) - 1 ) ;
else
strncpy ( id , hdr - > path , sizeof ( id ) - 1 ) ; // scummvm
}
else
strcpy ( id , hdr - > id ) ;
2018-10-25 00:05:02 +02:00
fanartDir [ 163 ] = ' \0 ' ;
strncpy ( fanartDir , fmt ( " %s/%s " , path , id ) , 163 ) ;
2018-10-16 01:43:49 +02:00
2018-10-25 00:05:02 +02:00
TexErr texErr = TexHandle . fromImageFile ( m_bg , fmt ( " %s/background.png " , fanartDir ) ) ;
2019-02-11 21:55:44 +01:00
if ( texErr = = TE_ERROR & & ! NoGameID ( hdr - > type ) )
2018-10-16 01:43:49 +02:00
{
2018-10-25 00:05:02 +02:00
strncpy ( fanartDir , fmt ( " %s/%.3s " , path , id ) , 163 ) ;
texErr = TexHandle . fromImageFile ( m_bg , fmt ( " %s/background.png " , fanartDir ) ) ;
2018-10-16 01:43:49 +02:00
}
2019-02-17 22:35:36 +01:00
if ( texErr = = TE_ERROR )
2018-10-16 01:43:49 +02:00
{
2019-02-17 22:35:36 +01:00
TexHandle . Cleanup ( m_bg ) ;
return false ;
}
char faConfig_Path [ 164 ] ;
faConfig_Path [ 163 ] = ' \0 ' ;
strncpy ( faConfig_Path , fmt ( " %s/%s.ini " , fanartDir , id ) , 163 ) ;
m_faConfig . load ( faConfig_Path ) ;
if ( ! m_faConfig . loaded ( ) & & ! NoGameID ( hdr - > type ) )
{
strncpy ( faConfig_Path , fmt ( " %s/%.3s.ini " , fanartDir , id ) , 163 ) ;
2018-10-26 22:30:29 +02:00
m_faConfig . load ( faConfig_Path ) ;
2018-10-16 01:43:49 +02:00
}
2019-02-17 22:35:36 +01:00
if ( ! m_faConfig . loaded ( ) )
{
TexHandle . Cleanup ( m_bg ) ;
return false ;
}
TexHandle . fromImageFile ( m_bglq , fmt ( " %s/background_lq.png " , fanartDir ) ) ;
for ( int i = 1 ; i < = 6 ; i + + )
{
CFanartElement elm ( m_faConfig , fanartDir , i ) ;
if ( elm . IsValid ( ) ) m_elms . push_back ( elm ) ;
}
m_loaded = true ;
m_defaultDelay = m_wiiflowConfig . getInt ( " FANART " , " delay_after_animation " , 200 ) ;
m_delayAfterAnimation = m_faConfig . getInt ( " GENERAL " , " delay_after_animation " , m_defaultDelay ) ;
m_globalShowCoverAfterAnimation = m_wiiflowConfig . getOptBool ( " FANART " , " show_cover_after_animation " , 2 ) ;
2019-02-11 21:55:44 +01:00
return true ;
2018-10-16 01:43:49 +02:00
}
void CFanart : : getBackground ( const TexData * & hq , const TexData * & lq )
{
if ( m_loaded )
{
hq = & m_bg ;
lq = & m_bglq ;
}
if ( lq = = NULL | | lq - > data = = NULL )
lq = hq ;
}
2018-10-25 00:05:02 +02:00
void CFanart : : reset ( )
2018-10-16 01:43:49 +02:00
{
2018-10-25 00:05:02 +02:00
for ( vector < CFanartElement > : : iterator Elm = m_elms . begin ( ) ; Elm ! = m_elms . end ( ) ; Elm + + )
Elm - > Cleanup ( ) ;
m_elms . clear ( ) ;
for ( int i = 1 ; i < = 6 ; i + + )
{
2018-10-26 22:30:29 +02:00
CFanartElement elm ( m_faConfig , fanartDir , i ) ;
2018-10-25 00:05:02 +02:00
if ( elm . IsValid ( ) ) m_elms . push_back ( elm ) ;
}
2018-10-16 01:43:49 +02:00
}
2018-10-25 00:05:02 +02:00
bool CFanart : : noLoop ( )
2018-10-16 01:43:49 +02:00
{
if ( m_globalShowCoverAfterAnimation ! = 2 )
2018-10-25 00:05:02 +02:00
return m_globalShowCoverAfterAnimation = = 1 ;
2018-10-26 22:30:29 +02:00
return m_faConfig . getBool ( " GENERAL " , " show_cover_after_animation " , false ) ;
2018-10-16 01:43:49 +02:00
}
bool CFanart : : isLoaded ( )
{
return m_loaded ;
}
bool CFanart : : isAnimationComplete ( )
{
return m_animationComplete & & m_delayAfterAnimation < = 0 ;
}
void CFanart : : tick ( )
{
2018-10-25 00:05:02 +02:00
if ( ! m_loaded ) return ;
2018-10-16 01:43:49 +02:00
m_animationComplete = true ;
for ( u32 i = 0 ; i < m_elms . size ( ) ; + + i )
{
m_elms [ i ] . tick ( ) ;
if ( ! m_elms [ i ] . IsAnimationComplete ( ) )
m_animationComplete = false ;
}
if ( m_animationComplete & & m_delayAfterAnimation > 0 )
m_delayAfterAnimation - - ;
}
2018-10-26 22:30:29 +02:00
void CFanart : : draw ( )
2018-10-16 01:43:49 +02:00
{
2018-10-26 22:30:29 +02:00
if ( ! m_loaded ) return ;
2018-10-16 01:43:49 +02:00
GX_SetNumChans ( 1 ) ;
GX_ClearVtxDesc ( ) ;
GX_SetVtxDesc ( GX_VA_POS , GX_DIRECT ) ;
GX_SetVtxAttrFmt ( GX_VTXFMT0 , GX_VA_POS , GX_POS_XYZ , GX_F32 , 0 ) ;
GX_SetVtxDesc ( GX_VA_CLR0 , GX_DIRECT ) ;
GX_SetVtxAttrFmt ( GX_VTXFMT0 , GX_VA_CLR0 , GX_CLR_RGBA , GX_RGBA8 , 0 ) ;
GX_SetVtxDesc ( GX_VA_TEX0 , GX_DIRECT ) ;
GX_SetVtxAttrFmt ( GX_VTXFMT0 , GX_VA_TEX0 , GX_TEX_ST , GX_F32 , 0 ) ;
GX_SetNumTexGens ( 1 ) ;
GX_SetTevOp ( GX_TEVSTAGE0 , GX_MODULATE ) ;
GX_SetTevOrder ( GX_TEVSTAGE0 , GX_TEXCOORD0 , GX_TEXMAP0 , GX_COLOR0A0 ) ;
GX_SetTexCoordGen ( GX_TEXCOORD0 , GX_TG_MTX2x4 , GX_TG_TEX0 , GX_IDENTITY ) ;
GX_SetBlendMode ( GX_BM_BLEND , GX_BL_SRCALPHA , GX_BL_INVSRCALPHA , GX_LO_CLEAR ) ;
GX_SetAlphaUpdate ( GX_TRUE ) ;
GX_SetCullMode ( GX_CULL_NONE ) ;
GX_SetZMode ( GX_DISABLE , GX_LEQUAL , GX_TRUE ) ;
for ( u32 i = 0 ; i < m_elms . size ( ) ; + + i )
2018-10-26 22:30:29 +02:00
m_elms [ i ] . draw ( ) ;
2018-10-16 01:43:49 +02:00
}
CFanartElement : : CFanartElement ( Config & cfg , const char * dir , int artwork )
: m_artwork ( artwork ) , m_isValid ( false )
{
m_isValid = ( TexHandle . fromImageFile ( m_art , fmt ( " %s/artwork%d.png " , dir , artwork ) ) = = TE_OK ) ;
if ( ! m_isValid )
return ;
2018-10-26 22:30:29 +02:00
char * domain = fmt_malloc ( " artwork%d " , artwork ) ;
if ( domain = = NULL )
2018-10-16 01:43:49 +02:00
return ;
2018-10-26 22:30:29 +02:00
m_x = cfg . getInt ( domain , " x " , 0 ) ;
m_y = cfg . getInt ( domain , " y " , 0 ) ;
m_scaleX = cfg . getFloat ( domain , " scale_x " , 1.f ) ;
m_scaleY = cfg . getFloat ( domain , " scale_y " , 1.f ) ;
m_alpha = min ( cfg . getInt ( domain , " alpha " , 255 ) , 255 ) ;
m_delay = ( int ) ( cfg . getFloat ( domain , " delay " , 0.f ) * 50 ) ;
m_angle = cfg . getFloat ( domain , " angle " , 0.f ) ;
m_event_duration = ( int ) ( cfg . getFloat ( domain , " duration " , 0.f ) * 50 ) ;
m_event_x = m_event_duration = = 0 ? m_x : cfg . getInt ( domain , " event_x " , m_x ) ;
m_event_y = m_event_duration = = 0 ? m_y : cfg . getInt ( domain , " event_y " , m_y ) ;
m_event_scaleX = m_event_duration = = 0 ? m_scaleX : cfg . getInt ( domain , " event_scale_x " , m_scaleX ) ;
m_event_scaleY = m_event_duration = = 0 ? m_scaleY : cfg . getInt ( domain , " event_scale_y " , m_scaleY ) ;
m_event_alpha = m_event_duration = = 0 ? m_alpha : min ( cfg . getInt ( domain , " event_alpha " , m_alpha ) , 255 ) ; // Not from m_alpha, because the animation can start less translucent than m_alpha
m_event_angle = m_event_duration = = 0 ? m_angle : cfg . getFloat ( domain , " event_angle " , m_angle ) ;
2018-10-16 01:43:49 +02:00
m_step_x = m_event_duration = = 0 ? 0 : ( m_x - m_event_x ) / m_event_duration ;
m_step_y = m_event_duration = = 0 ? 0 : ( m_y - m_event_y ) / m_event_duration ;
m_step_scaleX = m_event_duration = = 0 ? 0 : ( m_scaleX - m_event_scaleX ) / m_event_duration ;
m_step_scaleY = m_event_duration = = 0 ? 0 : ( m_scaleY - m_event_scaleY ) / m_event_duration ;
m_step_alpha = m_event_duration = = 0 ? 0 : ( m_alpha - m_event_alpha ) / m_event_duration ;
m_step_angle = m_event_duration = = 0 ? 0 : ( m_angle - m_event_angle ) / m_event_duration ;
2018-10-26 22:30:29 +02:00
MEM2_free ( domain ) ;
2018-10-16 01:43:49 +02:00
}
void CFanartElement : : Cleanup ( void )
{
TexHandle . Cleanup ( m_art ) ;
}
bool CFanartElement : : IsValid ( )
{
return m_isValid ;
}
bool CFanartElement : : IsAnimationComplete ( )
{
return m_event_duration = = 0 ;
}
void CFanartElement : : tick ( )
{
if ( m_delay > 0 )
{
m_delay - - ;
return ;
}
if ( ( m_step_x < 0 & & m_event_x > m_x ) | | ( m_step_x > 0 & & m_event_x < m_x ) )
m_event_x = ( int ) ( m_event_x + m_step_x ) ;
if ( ( m_step_y < 0 & & m_event_y > m_y ) | | ( m_step_y > 0 & & m_event_y < m_y ) )
m_event_y = ( int ) ( m_event_y + m_step_y ) ;
if ( ( m_step_alpha < 0 & & m_event_alpha > m_alpha ) | | ( m_step_alpha > 0 & & m_event_alpha < m_alpha ) )
m_event_alpha = ( int ) ( m_event_alpha + m_step_alpha ) ;
if ( ( m_step_scaleX < 0 & & m_event_scaleX > m_scaleX ) | | ( m_step_scaleX > 0 & & m_event_scaleX < m_scaleX ) )
m_event_scaleX + = m_step_scaleX ;
if ( ( m_step_scaleY < 0 & & m_event_scaleY > m_scaleY ) | | ( m_step_scaleY > 0 & & m_event_scaleY < m_scaleY ) )
m_event_scaleY + = m_step_scaleY ;
if ( ( m_step_angle < 0 & & m_event_angle > m_angle ) | | ( m_step_angle > 0 & & m_event_angle < m_angle ) )
m_event_angle = ( int ) ( m_event_angle + m_step_angle ) ;
if ( m_event_duration > 0 )
m_event_duration - - ;
}
void CFanartElement : : draw ( )
{
if ( m_event_alpha = = 0 | | m_event_scaleX = = 0.f | | m_event_scaleY = = 0.f | | m_delay > 0 )
return ;
GXTexObj artwork ;
Mtx modelViewMtx , idViewMtx , rotViewMtxZ ;
guMtxIdentity ( idViewMtx ) ;
guMtxScaleApply ( idViewMtx , idViewMtx , m_event_scaleX , m_event_scaleY , 1.f ) ;
guMtxRotAxisDeg ( rotViewMtxZ , & _GRRaxisz , m_event_angle ) ;
guMtxConcat ( rotViewMtxZ , idViewMtx , modelViewMtx ) ;
guMtxTransApply ( modelViewMtx , modelViewMtx , m_event_x , m_event_y , 0.f ) ;
GX_LoadPosMtxImm ( modelViewMtx , GX_PNMTX0 ) ;
GX_InitTexObj ( & artwork , m_art . data , m_art . width , m_art . height , m_art . format , GX_CLAMP , GX_CLAMP , GX_FALSE ) ;
GX_LoadTexObj ( & artwork , GX_TEXMAP0 ) ;
float w = ( float ) ( m_art . width / 2 ) ; // * m_event_scaleX;
float h = ( float ) ( m_art . height / 2 ) ; // * m_event_scaleY;
GX_Begin ( GX_QUADS , GX_VTXFMT0 , 4 ) ;
// Draw top left
GX_Position3f32 ( - w , - h , 0.f ) ;
GX_Color4u8 ( 0xFF , 0xFF , 0xFF , m_event_alpha ) ;
GX_TexCoord2f32 ( 0.f , 0.f ) ;
// Draw top right
GX_Position3f32 ( w , - h , 0.f ) ;
GX_Color4u8 ( 0xFF , 0xFF , 0xFF , m_event_alpha ) ;
GX_TexCoord2f32 ( 1.f , 0.f ) ;
// Draw bottom right
GX_Position3f32 ( w , h , 0.f ) ;
GX_Color4u8 ( 0xFF , 0xFF , 0xFF , m_event_alpha ) ;
GX_TexCoord2f32 ( 1.f , 1.f ) ;
// Draw bottom left
GX_Position3f32 ( - w , h , 0.f ) ;
GX_Color4u8 ( 0xFF , 0xFF , 0xFF , m_event_alpha ) ;
GX_TexCoord2f32 ( 0.f , 1.f ) ;
GX_End ( ) ;
}