2014-01-27 00:02:21 +00:00
using System ;
using System.Linq ;
using System.Text ;
using System.Drawing ;
using System.Collections.Generic ;
2014-11-30 20:29:30 +00:00
using BizHawk.Emulation.Common ;
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2014-01-27 00:02:21 +00:00
using BizHawk.Client.Common ;
2014-06-26 19:07:17 +00:00
using BizHawk.Client.Common.InputAdapterExtensions ;
2014-01-27 00:02:21 +00:00
using BizHawk.Bizware.BizwareGL ;
namespace BizHawk.Client.EmuHawk
{
2014-01-28 04:39:27 +00:00
/// <summary>
/// This is an old abstracted rendering class that the OSD system is using to get its work done.
/// We should probably just use a GuiRenderer (it was designed to do that) although wrapping it with
/// more information for OSDRendering could be helpful I suppose
/// </summary>
public interface IBlitter
{
IBlitterFont GetFontType ( string fontType ) ;
void DrawString ( string s , IBlitterFont font , Color color , float x , float y ) ;
SizeF MeasureString ( string s , IBlitterFont font ) ;
Rectangle ClipBounds { get ; set ; }
}
2014-01-27 06:03:18 +00:00
class UIMessage
{
public string Message ;
public DateTime ExpireAt ;
}
class UIDisplay
{
public string Message ;
public int X ;
public int Y ;
public int Anchor ;
public Color ForeColor ;
public Color BackGround ;
}
2014-01-27 00:02:21 +00:00
public class OSDManager
{
public string FPS { get ; set ; }
public IBlitterFont MessageFont ;
2014-04-27 13:01:10 +00:00
2014-01-27 00:02:21 +00:00
public void Dispose ( )
{
}
public void Begin ( IBlitter blitter )
{
2019-03-28 03:17:14 +00:00
MessageFont = blitter . GetFontType ( nameof ( MessageFont ) ) ;
2014-01-27 00:02:21 +00:00
}
2014-06-29 02:28:48 +00:00
public Color FixedMessagesColor { get { return Color . FromArgb ( Global . Config . MessagesColor ) ; } }
public Color FixedAlertMessageColor { get { return Color . FromArgb ( Global . Config . AlertMessageColor ) ; } }
2014-01-27 00:02:21 +00:00
public OSDManager ( )
{
}
2014-04-27 13:53:20 +00:00
private float GetX ( IBlitter g , int x , int anchor , string message )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:53:20 +00:00
var size = g . MeasureString ( message , MessageFont ) ;
2014-01-27 00:02:21 +00:00
switch ( anchor )
{
default :
case 0 : //Top Left
case 2 : //Bottom Left
return x ;
case 1 : //Top Right
case 3 : //Bottom Right
return g . ClipBounds . Width - x - size . Width ;
}
}
2014-04-27 13:53:20 +00:00
private float GetY ( IBlitter g , int y , int anchor , string message )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:53:20 +00:00
var size = g . MeasureString ( message , MessageFont ) ;
2014-01-27 00:02:21 +00:00
switch ( anchor )
{
default :
case 0 : //Top Left
case 1 : //Top Right
return y ;
case 2 : //Bottom Left
case 3 : //Bottom Right
return g . ClipBounds . Height - y - size . Height ;
}
}
private string MakeFrameCounter ( )
{
if ( Global . MovieSession . Movie . IsFinished )
{
var sb = new StringBuilder ( ) ;
sb
. Append ( Global . Emulator . Frame )
. Append ( '/' )
. Append ( Global . MovieSession . Movie . FrameCount )
. Append ( " (Finished)" ) ;
return sb . ToString ( ) ;
}
2014-04-27 13:53:20 +00:00
if ( Global . MovieSession . Movie . IsPlaying )
2014-01-27 00:02:21 +00:00
{
var sb = new StringBuilder ( ) ;
sb
. Append ( Global . Emulator . Frame )
. Append ( '/' )
. Append ( Global . MovieSession . Movie . FrameCount ) ;
return sb . ToString ( ) ;
}
2014-04-27 13:53:20 +00:00
if ( Global . MovieSession . Movie . IsRecording )
2014-01-27 00:02:21 +00:00
{
return Global . Emulator . Frame . ToString ( ) ;
}
2014-04-27 13:53:20 +00:00
return Global . Emulator . Frame . ToString ( ) ;
2014-01-27 00:02:21 +00:00
}
private List < UIMessage > messages = new List < UIMessage > ( 5 ) ;
private List < UIDisplay > GUITextList = new List < UIDisplay > ( ) ;
public void AddMessage ( string message )
{
messages . Add ( new UIMessage { Message = message , ExpireAt = DateTime . Now + TimeSpan . FromSeconds ( 2 ) } ) ;
}
2014-04-27 13:01:10 +00:00
public void AddGUIText ( string message , int x , int y , Color backGround , Color foreColor , int anchor )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:01:10 +00:00
GUITextList . Add ( new UIDisplay
{
Message = message ,
X = x ,
Y = y ,
BackGround = backGround ,
ForeColor = foreColor ,
Anchor = anchor
} ) ;
2014-01-27 00:02:21 +00:00
}
public void ClearGUIText ( )
{
GUITextList . Clear ( ) ;
}
public void DrawMessages ( IBlitter g )
{
2015-09-05 20:48:37 +00:00
if ( ! Global . Config . DisplayMessages )
{
return ;
}
2015-01-13 04:21:32 +00:00
messages . RemoveAll ( m = > DateTime . Now > m . ExpireAt ) ;
int line = 1 ;
if ( Global . Config . StackOSDMessages )
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
for ( int i = messages . Count - 1 ; i > = 0 ; i - - , line + + )
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
float x = GetX ( g , Global . Config . DispMessagex , Global . Config . DispMessageanchor , messages [ i ] . Message ) ;
float y = GetY ( g , Global . Config . DispMessagey , Global . Config . DispMessageanchor , messages [ i ] . Message ) ;
if ( Global . Config . DispMessageanchor < 2 )
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
y + = ( ( line - 1 ) * 18 ) ;
2014-01-27 00:02:21 +00:00
}
2015-01-13 04:21:32 +00:00
else
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
y - = ( ( line - 1 ) * 18 ) ;
2014-01-27 00:02:21 +00:00
}
2015-11-26 19:51:48 +00:00
//g.DrawString(messages[i].Message, MessageFont, Color.Black, x + 2, y + 2);
2015-01-13 04:21:32 +00:00
g . DrawString ( messages [ i ] . Message , MessageFont , FixedMessagesColor , x , y ) ;
}
}
else
{
if ( messages . Any ( ) )
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
int i = messages . Count - 1 ;
2014-01-27 00:02:21 +00:00
2015-01-13 04:21:32 +00:00
float x = GetX ( g , Global . Config . DispMessagex , Global . Config . DispMessageanchor , messages [ i ] . Message ) ;
float y = GetY ( g , Global . Config . DispMessagey , Global . Config . DispMessageanchor , messages [ i ] . Message ) ;
if ( Global . Config . DispMessageanchor < 2 )
{
y + = ( ( line - 1 ) * 18 ) ;
2014-01-27 00:02:21 +00:00
}
2015-01-13 04:21:32 +00:00
else
2014-01-27 00:02:21 +00:00
{
2015-01-13 04:21:32 +00:00
y - = ( ( line - 1 ) * 18 ) ;
2014-01-27 00:02:21 +00:00
}
2015-01-13 04:21:32 +00:00
2015-11-26 19:51:48 +00:00
//g.DrawString(messages[i].Message, MessageFont, Color.Black, x + 2, y + 2);
2015-01-13 04:21:32 +00:00
g . DrawString ( messages [ i ] . Message , MessageFont , FixedMessagesColor , x , y ) ;
}
}
foreach ( var text in GUITextList )
{
try
{
float posx = GetX ( g , text . X , text . Anchor , text . Message ) ;
float posy = GetY ( g , text . Y , text . Anchor , text . Message ) ;
2015-11-26 19:51:48 +00:00
//g.DrawString(text.Message, MessageFont, text.BackGround, posx + 2, posy + 2);
2015-01-13 04:21:32 +00:00
g . DrawString ( text . Message , MessageFont , text . ForeColor , posx , posy ) ;
}
catch ( Exception )
{
return ;
2014-01-27 00:02:21 +00:00
}
}
}
2014-04-28 00:39:40 +00:00
public string InputStrMovie ( )
{
2014-06-18 19:34:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
2014-06-18 02:39:29 +00:00
lg . SetSource ( Global . MovieSession . MovieControllerAdapter ) ;
return lg . GenerateInputDisplay ( ) ;
2014-04-28 00:39:40 +00:00
}
public string InputStrImmediate ( )
{
2014-06-18 19:34:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
2014-06-14 22:36:32 +00:00
lg . SetSource ( Global . AutofireStickyXORAdapter ) ;
2014-01-27 00:02:21 +00:00
2014-06-14 22:36:32 +00:00
return lg . GenerateInputDisplay ( ) ;
2014-04-28 00:39:40 +00:00
}
public string InputPrevious ( )
2014-01-27 00:02:21 +00:00
{
2014-06-18 19:18:35 +00:00
if ( Global . MovieSession . Movie . IsActive & & ! Global . MovieSession . Movie . IsFinished )
2014-05-02 20:07:50 +00:00
{
2014-06-18 19:34:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
2014-06-18 19:18:35 +00:00
var state = Global . MovieSession . Movie . GetInputState ( Global . Emulator . Frame - 1 ) ;
if ( state ! = null )
{
lg . SetSource ( state ) ;
return lg . GenerateInputDisplay ( ) ;
}
2014-05-02 20:07:50 +00:00
}
2017-05-10 11:45:23 +00:00
return "" ;
2014-04-28 00:39:40 +00:00
}
public string InputStrOrAll ( )
{
2014-06-19 21:55:15 +00:00
var m = ( Global . MovieSession . Movie . IsActive & &
! Global . MovieSession . Movie . IsFinished & &
Global . Emulator . Frame > 0 ) ?
2014-06-19 00:37:42 +00:00
Global . MovieSession . Movie . GetInputState ( Global . Emulator . Frame - 1 ) :
Global . MovieSession . MovieControllerInstance ( ) ;
2014-04-28 00:39:40 +00:00
2014-06-18 19:34:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
2014-06-19 21:55:15 +00:00
2014-06-26 19:07:17 +00:00
lg . SetSource ( Global . AutofireStickyXORAdapter . Or ( m ) ) ;
2014-06-17 00:33:33 +00:00
return lg . GenerateInputDisplay ( ) ;
2014-04-28 00:39:40 +00:00
}
public string InputStrSticky ( )
{
var stickyOr = new StickyOrAdapter
2014-01-27 00:02:21 +00:00
{
2014-04-28 00:39:40 +00:00
Source = Global . StickyXORAdapter ,
SourceStickyOr = Global . AutofireStickyXORAdapter
} ;
2014-01-27 00:02:21 +00:00
2018-03-14 00:19:03 +00:00
return MakeStringFor ( stickyOr ) ;
}
2014-06-19 21:29:57 +00:00
2018-03-14 00:19:03 +00:00
private string MakeStringFor ( IController controller )
{
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
lg . SetSource ( controller ) ;
2014-06-14 22:36:32 +00:00
return lg . GenerateInputDisplay ( ) ;
2014-04-28 00:39:40 +00:00
}
public string MakeIntersectImmediatePrevious ( )
{
2014-05-03 12:58:34 +00:00
if ( Global . MovieSession . Movie . IsActive )
2014-04-28 00:39:40 +00:00
{
2014-06-19 00:37:42 +00:00
var m = Global . MovieSession . Movie . IsActive & & ! Global . MovieSession . Movie . IsFinished ?
2014-06-26 19:07:17 +00:00
Global . MovieSession . Movie . GetInputState ( Global . Emulator . Frame - 1 ) :
Global . MovieSession . MovieControllerInstance ( ) ;
2014-04-28 00:39:40 +00:00
2014-06-18 19:34:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
2014-06-26 19:07:17 +00:00
lg . SetSource ( Global . AutofireStickyXORAdapter . And ( m ) ) ;
2014-06-14 22:36:32 +00:00
return lg . GenerateInputDisplay ( ) ;
2014-05-03 12:58:34 +00:00
}
2017-05-10 11:45:23 +00:00
return "" ;
2014-01-27 00:02:21 +00:00
}
public string MakeRerecordCount ( )
{
if ( Global . MovieSession . Movie . IsActive )
{
2015-07-09 18:14:06 +00:00
return Global . MovieSession . Movie . Rerecords . ToString ( ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-28 00:39:40 +00:00
2017-05-10 11:45:23 +00:00
return "" ;
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:17:35 +00:00
private void DrawOsdMessage ( IBlitter g , string message , Color color , float x , float y )
{
2015-11-26 19:51:48 +00:00
//g.DrawString(message, MessageFont, Color.Black, x + 1, y + 1);
2014-04-27 13:17:35 +00:00
g . DrawString ( message , MessageFont , color , x , y ) ;
}
2014-01-27 00:02:21 +00:00
/// <summary>
/// Display all screen info objects like fps, frame counter, lag counter, and input display
/// </summary>
public void DrawScreenInfo ( IBlitter g )
{
2014-12-16 04:31:07 +00:00
if ( Global . Config . DisplayFrameCounter & & ! Global . Game . IsNullInstance )
2014-01-27 00:02:21 +00:00
{
string message = MakeFrameCounter ( ) ;
2014-04-27 13:53:20 +00:00
float x = GetX ( g , Global . Config . DispFrameCx , Global . Config . DispFrameanchor , message ) ;
float y = GetY ( g , Global . Config . DispFrameCy , Global . Config . DispFrameanchor , message ) ;
2014-04-27 13:17:35 +00:00
2014-05-18 16:58:17 +00:00
DrawOsdMessage ( g , message , Color . FromArgb ( Global . Config . MessagesColor ) , x , y ) ;
2014-11-30 20:29:30 +00:00
if ( GlobalWin . MainForm . IsLagFrame )
2014-05-18 16:58:17 +00:00
{
DrawOsdMessage ( g , Global . Emulator . Frame . ToString ( ) , FixedAlertMessageColor , x , y ) ;
}
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:17:35 +00:00
2014-12-16 03:36:38 +00:00
if ( Global . Config . DisplayInput & & ! Global . Game . IsNullInstance )
2014-01-27 00:02:21 +00:00
{
2018-11-18 17:26:15 +00:00
if ( ( Global . MovieSession . Movie . IsPlaying & & ! Global . MovieSession . Movie . IsFinished )
| | ( Global . MovieSession . Movie . IsFinished & & Global . Emulator . Frame = = Global . MovieSession . Movie . InputLogLength ) ) // Account for the last frame of the movie, the movie state is immediately "Finished" here but we still want to show the input
2014-01-27 00:02:21 +00:00
{
2014-04-28 00:39:40 +00:00
var input = InputStrMovie ( ) ;
var x = GetX ( g , Global . Config . DispInpx , Global . Config . DispInpanchor , input ) ;
var y = GetY ( g , Global . Config . DispInpy , Global . Config . DispInpanchor , input ) ;
Color c = Color . FromArgb ( Global . Config . MovieInput ) ;
2015-11-26 19:51:48 +00:00
//g.DrawString(input, MessageFont, Color.Black, x + 1, y + 1);
2014-04-28 00:39:40 +00:00
g . DrawString ( input , MessageFont , c , x , y ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-28 00:39:40 +00:00
else // TODO: message config -- allow setting of "previous", "mixed", and "auto"
2014-01-27 00:02:21 +00:00
{
2018-03-14 00:19:03 +00:00
var previousColor = Color . Orange ;
Color immediateColor = Color . FromArgb ( Global . Config . MessagesColor ) ;
var autoColor = Color . Pink ;
var changedColor = Color . PeachPuff ;
//we need some kind of string for calculating position when right-anchoring, of something like that
2014-04-28 00:39:40 +00:00
var bgStr = InputStrOrAll ( ) ;
var x = GetX ( g , Global . Config . DispInpx , Global . Config . DispInpanchor , bgStr ) ;
var y = GetY ( g , Global . Config . DispInpy , Global . Config . DispInpanchor , bgStr ) ;
2018-03-14 00:19:03 +00:00
//now, we're going to render these repeatedly, with higher-priority things overriding
2014-01-27 00:02:21 +00:00
2018-03-14 00:19:03 +00:00
//first display previous frame's input.
//note: that's only available in case we're working on a movie
2014-04-28 00:39:40 +00:00
var previousStr = InputPrevious ( ) ;
2018-03-14 00:19:03 +00:00
g . DrawString ( previousStr , MessageFont , previousColor , x , y ) ;
2014-04-28 00:39:40 +00:00
2018-03-14 00:19:03 +00:00
//next, draw the immediate input.
//that is, whatever's being held down interactively right this moment even if the game is paused
//this includes things held down due to autohold or autofire
2018-03-14 20:49:19 +00:00
//I know, this is all really confusing
2014-04-28 00:39:40 +00:00
var immediate = InputStrImmediate ( ) ;
g . DrawString ( immediate , MessageFont , immediateColor , x , y ) ;
2018-03-14 00:19:03 +00:00
//next draw anything that's pressed because it's sticky.
//this applies to autofire and autohold both. somehow. I dont understand it.
//basically we're tinting whatever's pressed because it's sticky specially
//in order to achieve this we want to avoid drawing anything pink that isnt actually held down right now
//so we make an AND adapter and combine it using immediate & sticky
2018-03-14 20:49:19 +00:00
var autoString = MakeStringFor ( Global . StickyXORAdapter . Source . Xor ( Global . AutofireStickyXORAdapter ) . And ( Global . AutofireStickyXORAdapter ) ) ;
2018-03-14 00:19:03 +00:00
g . DrawString ( autoString , MessageFont , autoColor , x , y ) ;
2014-04-28 00:39:40 +00:00
2018-03-14 00:19:03 +00:00
//recolor everything that's changed from the previous input
var immediateOverlay = MakeIntersectImmediatePrevious ( ) ;
g . DrawString ( immediateOverlay , MessageFont , changedColor , x , y ) ;
2014-04-28 00:39:40 +00:00
}
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:17:35 +00:00
2014-01-27 00:02:21 +00:00
if ( Global . MovieSession . MultiTrack . IsActive )
{
2014-08-17 15:04:23 +00:00
float x = GetX ( g , Global . Config . DispMultix , Global . Config . DispMultianchor , Global . MovieSession . MultiTrack . Status ) ;
float y = GetY ( g , Global . Config . DispMultiy , Global . Config . DispMultianchor , Global . MovieSession . MultiTrack . Status ) ;
2014-04-27 13:17:35 +00:00
2014-08-17 15:04:23 +00:00
DrawOsdMessage ( g , Global . MovieSession . MultiTrack . Status , FixedMessagesColor , x , y ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:17:35 +00:00
2014-01-27 00:02:21 +00:00
if ( Global . Config . DisplayFPS & & FPS ! = null )
{
2014-04-27 13:53:20 +00:00
float x = GetX ( g , Global . Config . DispFPSx , Global . Config . DispFPSanchor , FPS ) ;
float y = GetY ( g , Global . Config . DispFPSy , Global . Config . DispFPSanchor , FPS ) ;
2014-04-27 13:17:35 +00:00
DrawOsdMessage ( g , FPS , FixedMessagesColor , x , y ) ;
2014-01-27 00:02:21 +00:00
}
2014-11-30 20:29:30 +00:00
if ( Global . Config . DisplayLagCounter & & Global . Emulator . CanPollInput ( ) )
2014-01-27 00:02:21 +00:00
{
2014-12-05 00:59:00 +00:00
var counter = Global . Emulator . AsInputPollable ( ) . LagCount . ToString ( ) ;
2014-04-27 18:45:21 +00:00
var x = GetX ( g , Global . Config . DispLagx , Global . Config . DispLaganchor , counter ) ;
var y = GetY ( g , Global . Config . DispLagy , Global . Config . DispLaganchor , counter ) ;
2014-01-27 00:02:21 +00:00
2014-05-17 20:55:10 +00:00
DrawOsdMessage ( g , counter , FixedAlertMessageColor , x , y ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:17:35 +00:00
2014-01-27 00:02:21 +00:00
if ( Global . Config . DisplayRerecordCount )
{
string rerec = MakeRerecordCount ( ) ;
2014-04-27 13:53:20 +00:00
float x = GetX ( g , Global . Config . DispRecx , Global . Config . DispRecanchor , rerec ) ;
float y = GetY ( g , Global . Config . DispRecy , Global . Config . DispRecanchor , rerec ) ;
2014-04-27 13:17:35 +00:00
DrawOsdMessage ( g , rerec , FixedMessagesColor , x , y ) ;
2014-01-27 00:02:21 +00:00
}
if ( Global . ClientControls [ "Autohold" ] | | Global . ClientControls [ "Autofire" ] )
{
2014-04-27 13:53:20 +00:00
var disp = new StringBuilder ( "Held: " ) ;
2014-01-27 00:02:21 +00:00
2014-04-27 13:53:20 +00:00
foreach ( string sticky in Global . StickyXORAdapter . CurrentStickies )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:53:20 +00:00
disp . Append ( sticky ) . Append ( ' ' ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:53:20 +00:00
foreach ( string autoSticky in Global . AutofireStickyXORAdapter . CurrentStickies )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:53:20 +00:00
disp
. Append ( "Auto-" )
. Append ( autoSticky )
. Append ( ' ' ) ;
2014-01-27 00:02:21 +00:00
}
2014-04-27 13:53:20 +00:00
var message = disp . ToString ( ) ;
g . DrawString (
message ,
MessageFont ,
Color . White ,
GetX ( g , Global . Config . DispAutoholdx , Global . Config . DispAutoholdanchor , message ) ,
GetY ( g , Global . Config . DispAutoholdy , Global . Config . DispAutoholdanchor , message ) ) ;
2014-01-27 00:02:21 +00:00
}
if ( Global . MovieSession . Movie . IsActive & & Global . Config . DisplaySubtitles )
{
2014-06-08 22:12:15 +00:00
var subList = Global . MovieSession . Movie . Subtitles . GetSubtitles ( Global . Emulator . Frame ) ;
2014-01-27 00:02:21 +00:00
2014-04-27 13:17:35 +00:00
foreach ( var sub in subList )
2014-01-27 00:02:21 +00:00
{
2014-04-27 13:17:35 +00:00
DrawOsdMessage ( g , sub . Message , Color . FromArgb ( ( int ) sub . Color ) , sub . X , sub . Y ) ;
2014-01-27 00:02:21 +00:00
}
}
}
}
}