2013-04-29 01:57:41 +00:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Runtime.InteropServices ;
using System.Threading ;
namespace BizHawk.Emulation.Consoles.Nintendo.N64
{
2013-05-01 14:38:47 +00:00
public class N64 : IEmulator , IVideoProvider
2013-05-06 03:22:27 +00:00
{
2013-04-29 01:57:41 +00:00
public string SystemId { get { return "N64" ; } }
public CoreComm CoreComm { get ; private set ; }
public byte [ ] rom ;
public GameInfo game ;
public IVideoProvider VideoProvider { get { return this ; } }
2013-05-04 02:46:37 +00:00
public int [ ] frameBuffer ; // = new int[800 * 600];
2013-05-06 03:22:27 +00:00
public int [ ] GetVideoBuffer ( ) { return frameBuffer ; }
2013-05-04 02:46:37 +00:00
public int VirtualWidth { get ; set ; }
public int BufferWidth { get ; set ; }
public int BufferHeight { get ; set ; }
2013-04-29 01:57:41 +00:00
public int BackgroundColor { get { return 0 ; } }
2013-05-06 03:22:27 +00:00
2013-05-04 02:46:37 +00:00
2013-05-06 03:22:27 +00:00
public Sound . Utilities . SpeexResampler resampler ;
2013-05-04 02:46:37 +00:00
2013-05-01 14:38:47 +00:00
public ISoundProvider SoundProvider { get { return null ; } }
public ISyncSoundProvider SyncSoundProvider { get { return resampler ; } }
public bool StartAsyncSound ( ) { return false ; }
2013-04-29 01:57:41 +00:00
public void EndAsyncSound ( ) { }
public ControllerDefinition ControllerDefinition { get { return N64ControllerDefinition ; } }
public IController Controller { get ; set ; }
public static readonly ControllerDefinition N64ControllerDefinition = new ControllerDefinition
{
Name = "Nintento 64 Controller" ,
BoolButtons =
{
2013-07-29 02:11:00 +00:00
"P1 A Up" , "P1 A Down" , "P1 A Left" , "P1 A Right" , "P1 DPad U" , "P1 DPad D" , "P1 DPad L" , "P1 DPad R" , "P1 Start" , "P1 Z" , "P1 B" , "P1 A" , "P1 C Up" , "P1 C Down" , "P1 C Right" , "P1 C Left" , "P1 L" , "P1 R" ,
"P2 A Up" , "P2 A Down" , "P2 A Left" , "P2 A Right" , "P2 DPad U" , "P2 DPad D" , "P2 DPad L" , "P2 DPad R" , "P2 Start" , "P2 Z" , "P2 B" , "P2 A" , "P2 C Up" , "P2 C Down" , "P2 C Right" , "P2 C Left" , "P2 L" , "P2 R" ,
"P3 A Up" , "P3 A Down" , "P3 A Left" , "P3 A Right" , "P3 DPad U" , "P3 DPad D" , "P3 DPad L" , "P3 DPad R" , "P3 Start" , "P3 Z" , "P3 B" , "P3 A" , "P3 C Up" , "P3 C Down" , "P3 C Right" , "P3 C Left" , "P3 L" , "P3 R" ,
"P4 A Up" , "P4 A Down" , "P4 A Left" , "P4 A Right" , "P4 DPad U" , "P4 DPad D" , "P4 DPad L" , "P4 DPad R" , "P4 Start" , "P4 Z" , "P4 B" , "P4 A" , "P4 C Up" , "P4 C Down" , "P4 C Right" , "P4 C Left" , "P4 L" , "P4 R" ,
2013-05-02 01:54:44 +00:00
"Reset" , "Power"
2013-05-07 01:42:48 +00:00
} ,
FloatControls =
{
"P1 X Axis" , "P1 Y Axis" ,
"P2 X Axis" , "P2 Y Axis" ,
"P3 X Axis" , "P3 Y Axis" ,
"P4 X Axis" , "P4 Y Axis"
2013-07-18 16:18:17 +00:00
} ,
FloatRanges =
{
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f } ,
new [ ] { - 128.0f , 0.0f , 127.0f }
2013-04-29 01:57:41 +00:00
}
} ;
public int Frame { get ; set ; }
public int LagCount { get ; set ; }
2013-05-04 04:07:04 +00:00
public bool IsLagFrame { get ; set ; }
2013-05-05 22:53:22 +00:00
public void ResetFrameCounter ( )
{
Frame = 0 ;
LagCount = 0 ;
IsLagFrame = false ;
}
2013-04-30 00:36:54 +00:00
public void FrameAdvance ( bool render , bool rendersound )
{
2013-05-03 21:13:23 +00:00
if ( Controller [ "Reset" ] )
{
2013-05-06 03:22:27 +00:00
api . soft_reset ( ) ;
2013-05-03 21:13:23 +00:00
}
if ( Controller [ "Power" ] )
{
2013-05-06 03:22:27 +00:00
api . hard_reset ( ) ;
}
2013-05-11 23:44:20 +00:00
IsLagFrame = true ;
api . frame_advance ( ) ;
if ( IsLagFrame ) LagCount + + ;
Frame + + ;
}
public void setControllers ( )
{
if ( CoreComm . InputCallback ! = null ) CoreComm . InputCallback ( ) ;
IsLagFrame = false ;
2013-05-10 23:29:14 +00:00
// Analog stick right = +X
// Analog stick up = +Y
2013-05-20 00:03:08 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
2013-07-25 00:51:56 +00:00
string p = "P" + ( i + 1 ) ;
sbyte x ;
if ( Controller . IsPressed ( p + " A Left" ) ) { x = - 127 ; }
else if ( Controller . IsPressed ( p + " A Right" ) ) { x = 127 ; }
else { x = ( sbyte ) Controller . GetFloat ( p + " X Axis" ) ; }
sbyte y ;
if ( Controller . IsPressed ( p + " A Up" ) ) { y = 127 ; }
else if ( Controller . IsPressed ( p + " A Down" ) ) { y = - 127 ; }
else { y = ( sbyte ) Controller . GetFloat ( p + " Y Axis" ) ; }
api . set_buttons ( i , ReadController ( i + 1 ) , x , y ) ;
2013-05-20 00:03:08 +00:00
}
2013-04-30 00:36:54 +00:00
}
2013-04-29 01:57:41 +00:00
2013-05-02 03:38:57 +00:00
public int ReadController ( int num )
{
int buttons = 0 ;
2013-05-20 00:03:08 +00:00
if ( Controller [ "P" + num + " DPad R" ] ) buttons | = ( 1 < < 0 ) ;
if ( Controller [ "P" + num + " DPad L" ] ) buttons | = ( 1 < < 1 ) ;
if ( Controller [ "P" + num + " DPad D" ] ) buttons | = ( 1 < < 2 ) ;
if ( Controller [ "P" + num + " DPad U" ] ) buttons | = ( 1 < < 3 ) ;
if ( Controller [ "P" + num + " Start" ] ) buttons | = ( 1 < < 4 ) ;
if ( Controller [ "P" + num + " Z" ] ) buttons | = ( 1 < < 5 ) ;
if ( Controller [ "P" + num + " B" ] ) buttons | = ( 1 < < 6 ) ;
if ( Controller [ "P" + num + " A" ] ) buttons | = ( 1 < < 7 ) ;
if ( Controller [ "P" + num + " C Right" ] ) buttons | = ( 1 < < 8 ) ;
if ( Controller [ "P" + num + " C Left" ] ) buttons | = ( 1 < < 9 ) ;
if ( Controller [ "P" + num + " C Down" ] ) buttons | = ( 1 < < 10 ) ;
if ( Controller [ "P" + num + " C Up" ] ) buttons | = ( 1 < < 11 ) ;
if ( Controller [ "P" + num + " R" ] ) buttons | = ( 1 < < 12 ) ;
if ( Controller [ "P" + num + " L" ] ) buttons | = ( 1 < < 13 ) ;
2013-05-02 03:38:57 +00:00
return buttons ;
}
2013-04-29 01:57:41 +00:00
public bool DeterministicEmulation { get ; set ; }
2013-05-07 22:37:26 +00:00
public byte [ ] ReadSaveRam ( )
{
2013-05-09 00:36:01 +00:00
return api . SaveSaveram ( ) ;
2013-05-07 22:37:26 +00:00
}
public void StoreSaveRam ( byte [ ] data )
{
api . LoadSaveram ( data ) ;
}
public void ClearSaveRam ( )
{
api . InitSaveram ( ) ;
}
public bool SaveRamModified { get { return true ; } set { } }
2013-04-29 01:57:41 +00:00
void SyncState ( Serializer ser )
{
ser . BeginSection ( "N64" ) ;
ser . EndSection ( ) ;
}
2013-05-04 04:07:04 +00:00
// these next 5 functions are all exact copy paste from gambatte.
// if something's wrong here, it's probably wrong there too
public void SaveStateText ( TextWriter writer )
{
var temp = SaveStateBinary ( ) ;
2013-06-06 00:59:09 +00:00
temp . SaveAsHexFast ( writer ) ;
2013-05-04 04:07:04 +00:00
// write extra copy of stuff we don't use
writer . WriteLine ( "Frame {0}" , Frame ) ;
}
public void LoadStateText ( TextReader reader )
{
string hex = reader . ReadLine ( ) ;
if ( hex . StartsWith ( "emuVersion" ) ) // movie save
{
do // theoretically, our portion should start right after StartsFromSavestate, maybe...
{
hex = reader . ReadLine ( ) ;
} while ( ! hex . StartsWith ( "StartsFromSavestate" ) ) ;
hex = reader . ReadLine ( ) ;
}
byte [ ] state = new byte [ hex . Length / 2 ] ;
2013-06-06 00:59:09 +00:00
state . ReadFromHexFast ( hex ) ;
2013-05-04 04:07:04 +00:00
LoadStateBinary ( new BinaryReader ( new MemoryStream ( state ) ) ) ;
}
2013-07-30 00:01:32 +00:00
byte [ ] SaveStatePrivateBuff = new byte [ 16788288 + 1024 ] ;
2013-05-04 04:07:04 +00:00
public void SaveStateBinary ( BinaryWriter writer )
{
2013-07-30 00:01:32 +00:00
byte [ ] data = SaveStatePrivateBuff ;
2013-05-06 03:22:27 +00:00
int bytes_used = api . SaveState ( data ) ;
2013-05-04 04:07:04 +00:00
writer . Write ( data . Length ) ;
writer . Write ( data ) ;
2013-05-11 03:44:01 +00:00
byte [ ] saveram = api . SaveSaveram ( ) ;
writer . Write ( saveram ) ;
2013-05-04 04:07:04 +00:00
// other variables
writer . Write ( IsLagFrame ) ;
writer . Write ( LagCount ) ;
writer . Write ( Frame ) ;
}
public void LoadStateBinary ( BinaryReader reader )
{
int length = reader . ReadInt32 ( ) ;
byte [ ] data = reader . ReadBytes ( length ) ;
2013-05-06 03:22:27 +00:00
api . LoadState ( data ) ;
2013-05-04 04:07:04 +00:00
2013-05-11 03:44:01 +00:00
data = reader . ReadBytes ( 0x800 + 0x8000 * 4 ) ;
api . LoadSaveram ( data ) ;
2013-05-04 04:07:04 +00:00
// other variables
IsLagFrame = reader . ReadBoolean ( ) ;
LagCount = reader . ReadInt32 ( ) ;
Frame = reader . ReadInt32 ( ) ;
}
2013-04-29 01:57:41 +00:00
public byte [ ] SaveStateBinary ( )
{
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
SaveStateBinary ( bw ) ;
bw . Flush ( ) ;
return ms . ToArray ( ) ;
}
2013-05-06 20:51:28 +00:00
public bool BinarySaveStatesPreferred { get { return true ; } }
2013-05-19 20:14:34 +00:00
#region memorydomains
MemoryDomain MakeMemoryDomain ( string name , mupen64plusApi . N64_MEMORY id , Endian endian )
{
int size = api . get_memory_size ( id ) ;
//if this type of memory isnt available, dont make the memory domain
if ( size = = 0 )
return null ;
IntPtr memPtr = api . get_memory_ptr ( id ) ;
MemoryDomain md = new MemoryDomain (
name ,
size ,
endian ,
delegate ( int addr )
{
if ( addr < 0 | | addr > = size )
throw new ArgumentOutOfRangeException ( ) ;
return Marshal . ReadByte ( memPtr , addr ) ;
} ,
delegate ( int addr , byte val )
{
if ( addr < 0 | | addr > = size )
throw new ArgumentOutOfRangeException ( ) ;
Marshal . WriteByte ( memPtr + addr , val ) ;
} ) ;
MemoryDomains . Add ( md ) ;
return md ;
}
void InitMemoryDomains ( )
{
MemoryDomains = new List < MemoryDomain > ( ) ;
MakeMemoryDomain ( "RDRAM" , mupen64plusApi . N64_MEMORY . RDRAM , Endian . Little ) ;
MakeMemoryDomain ( "PI Register" , mupen64plusApi . N64_MEMORY . PI_REG , Endian . Little ) ;
MakeMemoryDomain ( "SI Register" , mupen64plusApi . N64_MEMORY . SI_REG , Endian . Little ) ;
MakeMemoryDomain ( "VI Register" , mupen64plusApi . N64_MEMORY . VI_REG , Endian . Little ) ;
MakeMemoryDomain ( "RI Register" , mupen64plusApi . N64_MEMORY . RI_REG , Endian . Little ) ;
MakeMemoryDomain ( "AI Register" , mupen64plusApi . N64_MEMORY . AI_REG , Endian . Little ) ;
MakeMemoryDomain ( "EEPROM" , mupen64plusApi . N64_MEMORY . EEPROM , Endian . Little ) ;
MakeMemoryDomain ( "Mempak 1" , mupen64plusApi . N64_MEMORY . MEMPAK1 , Endian . Little ) ;
MakeMemoryDomain ( "Mempak 2" , mupen64plusApi . N64_MEMORY . MEMPAK2 , Endian . Little ) ;
MakeMemoryDomain ( "Mempak 3" , mupen64plusApi . N64_MEMORY . MEMPAK3 , Endian . Little ) ;
MakeMemoryDomain ( "Mempak 4" , mupen64plusApi . N64_MEMORY . MEMPAK4 , Endian . Little ) ;
}
2013-05-05 03:39:01 +00:00
public IList < MemoryDomain > MemoryDomains { get ; private set ; }
2013-05-19 20:14:34 +00:00
public MemoryDomain MainMemory { get { return MemoryDomains [ 0 ] ; } }
#endregion
2013-04-29 01:57:41 +00:00
2013-05-01 14:38:47 +00:00
public void Dispose ( )
{
2013-05-06 03:22:27 +00:00
api . Dispose ( ) ;
2013-05-01 14:38:47 +00:00
}
2013-04-29 01:57:41 +00:00
2013-05-06 03:22:27 +00:00
mupen64plusApi api ;
2013-05-04 01:16:27 +00:00
2013-06-08 03:36:57 +00:00
public N64 ( CoreComm comm , GameInfo game , byte [ ] rom , VideoPluginSettings video_settings , int SaveType )
2013-04-29 01:57:41 +00:00
{
CoreComm = comm ;
this . rom = rom ;
this . game = game ;
2013-04-30 00:08:21 +00:00
2013-06-08 03:36:57 +00:00
api = new mupen64plusApi ( this , rom , video_settings , SaveType ) ;
2013-05-11 23:44:20 +00:00
api . SetM64PInputCallback ( new mupen64plusApi . InputCallback ( setControllers ) ) ;
2013-04-30 01:50:27 +00:00
2013-05-19 20:14:34 +00:00
InitMemoryDomains ( ) ;
2013-04-30 00:08:21 +00:00
}
2013-04-29 01:57:41 +00:00
}
}