2014-12-10 19:41:13 +00:00
//TODO hook up newer file ID stuff, think about how to combine it with the disc ID
//TODO change display manager to not require 0xFF alpha channel set on videoproviders. check gdi+ and opengl! this will get us a speedup in some places
//TODO Disc.Structure.Sessions[0].length_aba was 0
2014-12-16 04:02:55 +00:00
//TODO mednafen 0.9.37 changed some disc region detection heuristics. analyze and apply in c# side. also the SCEX id handling changed, maybe simplified
2014-12-10 19:41:13 +00:00
2014-12-17 10:50:20 +00:00
//TODO - ok, think about this. we MUST load a state with the CDC completely intact. no quickly changing discs. thats madness.
//well, I could savestate the disc index and validate the disc collection when loading a state.
//the big problem is, it's completely at odds with the slider-based disc changing model.
//but, maybe it can be reconciled with that model by using the disc ejection to our advantage.
//perhaps moving the slider is meaningless if the disc is ejected--it only affects what disc is inserted when the disc gets inserted!! yeah! this might could save us!
//not exactly user friendly but maybe we can build it from there with a custom UI.. a disk-changer? dunno if that would help
2014-12-10 19:41:13 +00:00
using System ;
2014-12-14 08:48:23 +00:00
using System.ComponentModel ;
2012-11-04 23:29:06 +00:00
using System.Runtime.InteropServices ;
using System.IO ;
2015-02-01 14:44:08 +00:00
using System.Linq ;
2012-11-04 23:29:06 +00:00
using System.Collections.Generic ;
2014-12-12 00:25:46 +00:00
using Newtonsoft.Json ;
2012-11-04 23:29:06 +00:00
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2014-12-14 08:48:23 +00:00
using BizHawk.Common ;
2013-11-04 01:39:19 +00:00
2013-10-27 17:07:37 +00:00
#pragma warning disable 649 //adelikat: Disable dumb warnings until this file is complete
2013-11-13 23:36:21 +00:00
namespace BizHawk.Emulation.Cores.Sony.PSX
2012-11-04 23:29:06 +00:00
{
2017-07-12 19:10:55 +00:00
[ Core (
2014-12-10 19:41:13 +00:00
"Octoshock" ,
2017-07-09 16:18:33 +00:00
"Mednafen Team" ,
2014-04-25 01:19:57 +00:00
isPorted : true ,
2017-05-06 00:05:36 +00:00
isReleased : true ) ]
2016-12-11 17:14:42 +00:00
public unsafe partial class Octoshock : IEmulator , IVideoProvider , ISoundProvider , ISaveRam , IStatable , IDriveLight , ISettable < Octoshock . Settings , Octoshock . SyncSettings > , IRegionable , IInputPollable
2012-11-04 23:29:06 +00:00
{
2017-05-06 00:05:36 +00:00
public Octoshock ( CoreComm comm , PSF psf , object settings , object syncSettings )
{
Load ( comm , null , null , null , settings , syncSettings , psf ) ;
OctoshockDll . shock_PowerOn ( psx ) ;
}
//note: its annoying that we have to have a disc before constructing this.
//might want to change that later. HOWEVER - we need to definitely have a region, at least
public Octoshock ( CoreComm comm , List < DiscSystem . Disc > discs , List < string > discNames , byte [ ] exe , object settings , object syncSettings )
{
Load ( comm , discs , discNames , exe , settings , syncSettings , null ) ;
OctoshockDll . shock_PowerOn ( psx ) ;
}
void Load ( CoreComm comm , List < DiscSystem . Disc > discs , List < string > discNames , byte [ ] exe , object settings , object syncSettings , PSF psf )
{
ConnectTracer ( ) ;
CoreComm = comm ;
DriveLightEnabled = true ;
_Settings = ( Settings ) settings ? ? new Settings ( ) ;
_SyncSettings = ( SyncSettings ) syncSettings ? ? new SyncSettings ( ) ;
Discs = discs ;
Attach ( ) ;
//assume this region for EXE and PSF, maybe not correct though
string firmwareRegion = "U" ;
SystemRegion = OctoshockDll . eRegion . NA ;
if ( discs ! = null )
{
HackyDiscButtons . AddRange ( discNames ) ;
foreach ( var disc in discs )
{
var discInterface = new DiscInterface ( disc ,
( di ) = >
{
//if current disc this delegate disc, activity is happening
if ( di = = currentDiscInterface )
DriveLightOn = true ;
} ) ;
discInterfaces . Add ( discInterface ) ;
}
}
else
{
//assume its NA region for test programs, for now. could it be read out of the ps-exe header?
}
if ( discInterfaces . Count ! = 0 )
{
//determine region of one of the discs
OctoshockDll . ShockDiscInfo discInfo ;
OctoshockDll . shock_AnalyzeDisc ( discInterfaces [ 0 ] . OctoshockHandle , out discInfo ) ;
//try to acquire the appropriate firmware
if ( discInfo . region = = OctoshockDll . eRegion . EU ) firmwareRegion = "E" ;
if ( discInfo . region = = OctoshockDll . eRegion . JP ) firmwareRegion = "J" ;
SystemRegion = discInfo . region ;
}
//see http://problemkaputt.de/psx-spx.htm
int CpuClock_n = 44100 * 768 ;
int CpuClock_d = 1 ;
int VidClock_n = CpuClock_n * 11 ;
int VidClock_d = CpuClock_d * 7 ;
if ( SystemRegion = = OctoshockDll . eRegion . EU )
{
VsyncNumerator = VidClock_n ;
VsyncDenominator = VidClock_d * 314 * 3406 ;
SystemVidStandard = OctoshockDll . eVidStandard . PAL ;
}
else
{
VsyncNumerator = VidClock_n ;
VsyncDenominator = VidClock_d * 263 * 3413 ;
SystemVidStandard = OctoshockDll . eVidStandard . NTSC ;
}
//TODO - known bad firmwares are a no-go. we should refuse to boot them. (thats the mednafen policy)
byte [ ] firmware = comm . CoreFileProvider . GetFirmware ( "PSX" , firmwareRegion , true , "A PSX `" + firmwareRegion + "` region bios file is required" ) ;
//create the instance
fixed ( byte * pFirmware = firmware )
OctoshockDll . shock_Create ( out psx , SystemRegion , pFirmware ) ;
SetMemoryDomains ( ) ;
InitMemCallbacks ( ) ;
//set a default framebuffer based on the first frame of emulation, to cut down on flickering or whatever
//this is probably quixotic, but we have to pick something
{
BufferWidth = 280 ;
BufferHeight = 240 ;
if ( SystemVidStandard = = OctoshockDll . eVidStandard . PAL )
{
BufferWidth = 280 ;
BufferHeight = 288 ;
}
CurrentVideoSize = new System . Drawing . Size ( BufferWidth , BufferHeight ) ;
var ri = Octoshock . CalculateResolution ( SystemVidStandard , _Settings , BufferWidth , BufferHeight ) ;
BufferWidth = VirtualWidth = ri . Resolution . Width ;
BufferHeight = VirtualHeight = ri . Resolution . Height ;
//VideoProvider_Padding = new System.Drawing.Size(50,50);
frameBuffer = new int [ BufferWidth * BufferHeight ] ;
}
if ( discInterfaces . Count ! = 0 )
{
//start with first disc inserted and tray closed. it's a sensible default.
//it will be possible for the user to specify a different initial configuration, but this will inform the UI
CurrentTrayOpen = false ;
CurrentDiscIndexMounted = 1 ;
}
else if ( psf = = null )
{
//must be an exe
fixed ( byte * pExeBuffer = exe )
OctoshockDll . shock_MountEXE ( psx , pExeBuffer , exe . Length , false ) ;
//start with no disc inserted and tray closed
CurrentTrayOpen = false ;
CurrentDiscIndexMounted = 0 ;
OctoshockDll . shock_CloseTray ( psx ) ;
}
else
{
//must be a psf
if ( psf . LibData ! = null )
fixed ( byte * pBuf = psf . LibData )
OctoshockDll . shock_MountEXE ( psx , pBuf , psf . LibData . Length , true ) ;
fixed ( byte * pBuf = psf . Data )
OctoshockDll . shock_MountEXE ( psx , pBuf , psf . Data . Length , false ) ;
//start with no disc inserted and tray closed
CurrentTrayOpen = false ;
CurrentDiscIndexMounted = 0 ;
OctoshockDll . shock_CloseTray ( psx ) ;
}
//setup the controller based on sync settings
SetControllerButtons ( ) ;
var fioCfg = _SyncSettings . FIOConfig ;
if ( fioCfg . Multitaps [ 0 ] )
{
OctoshockDll . shock_Peripheral_Connect ( psx , 0x01 , OctoshockDll . ePeripheralType . Multitap ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x11 , fioCfg . Devices8 [ 0 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x21 , fioCfg . Devices8 [ 1 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x31 , fioCfg . Devices8 [ 2 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x41 , fioCfg . Devices8 [ 3 ] ) ;
}
else
OctoshockDll . shock_Peripheral_Connect ( psx , 0x01 , fioCfg . Devices8 [ 0 ] ) ;
if ( fioCfg . Multitaps [ 1 ] )
{
OctoshockDll . shock_Peripheral_Connect ( psx , 0x02 , OctoshockDll . ePeripheralType . Multitap ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x12 , fioCfg . Devices8 [ 4 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x22 , fioCfg . Devices8 [ 5 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x32 , fioCfg . Devices8 [ 6 ] ) ;
OctoshockDll . shock_Peripheral_Connect ( psx , 0x42 , fioCfg . Devices8 [ 7 ] ) ;
}
else
OctoshockDll . shock_Peripheral_Connect ( psx , 0x02 , fioCfg . Devices8 [ 4 ] ) ;
var memcardTransaction = new OctoshockDll . ShockMemcardTransaction ( )
{
transaction = OctoshockDll . eShockMemcardTransaction . Connect
} ;
if ( fioCfg . Memcards [ 0 ] ) OctoshockDll . shock_Peripheral_MemcardTransact ( psx , 0x01 , ref memcardTransaction ) ;
if ( fioCfg . Memcards [ 1 ] ) OctoshockDll . shock_Peripheral_MemcardTransact ( psx , 0x02 , ref memcardTransaction ) ;
//do this after framebuffers and peripherals and whatever crap are setup. kind of lame, but thats how it is for now
StudySaveBufferSize ( ) ;
}
2014-12-11 20:31:55 +00:00
public string SystemId { get { return "PSX" ; } }
2014-12-10 23:43:11 +00:00
2015-09-22 00:02:49 +00:00
public static ControllerDefinition CreateControllerDefinition ( SyncSettings syncSettings )
2015-01-31 19:37:26 +00:00
{
2015-09-22 00:02:49 +00:00
ControllerDefinition definition = new ControllerDefinition ( ) ;
2015-10-03 22:38:42 +00:00
definition . Name = "PSX DualShock Controller" ; // <-- for compatibility
2016-08-21 18:19:48 +00:00
//ControllerDefinition.Name = "PSX FrontIO"; // TODO - later rename to this, I guess, so it's less misleading. don't want to wreck keybindings yet.
2015-01-31 21:00:30 +00:00
2015-10-03 22:38:42 +00:00
var cfg = syncSettings . FIOConfig . ToLogical ( ) ;
2015-10-03 22:27:52 +00:00
for ( int i = 0 ; i < cfg . NumPlayers ; i + + )
2015-01-31 19:37:26 +00:00
{
2015-10-03 22:27:52 +00:00
int pnum = i + 1 ;
2015-01-31 19:37:26 +00:00
2016-08-21 18:19:48 +00:00
var type = cfg . DevicesPlayer [ i ] ;
2017-04-26 03:59:37 +00:00
if ( type = = OctoshockDll . ePeripheralType . NegCon )
2016-08-21 18:19:48 +00:00
{
2017-04-26 03:59:37 +00:00
definition . BoolButtons . AddRange ( new [ ]
{
"P" + pnum + " Up" ,
"P" + pnum + " Down" ,
"P" + pnum + " Left" ,
"P" + pnum + " Right" ,
"P" + pnum + " Start" ,
"P" + pnum + " R" ,
"P" + pnum + " B" ,
"P" + pnum + " A" ,
} ) ;
2015-02-01 21:58:25 +00:00
2016-08-21 18:19:48 +00:00
definition . FloatControls . AddRange ( new [ ]
2017-04-26 03:59:37 +00:00
{
"P" + pnum + " Twist" ,
"P" + pnum + " 1" ,
"P" + pnum + " 2" ,
"P" + pnum + " L"
} ) ;
2017-05-06 00:05:36 +00:00
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
2017-04-26 03:59:37 +00:00
}
else
{
definition . BoolButtons . AddRange ( new [ ]
2016-08-21 18:19:48 +00:00
{
2017-04-26 03:59:37 +00:00
"P" + pnum + " Up" ,
"P" + pnum + " Down" ,
"P" + pnum + " Left" ,
"P" + pnum + " Right" ,
"P" + pnum + " Select" ,
"P" + pnum + " Start" ,
"P" + pnum + " Square" ,
"P" + pnum + " Triangle" ,
"P" + pnum + " Circle" ,
"P" + pnum + " Cross" ,
"P" + pnum + " L1" ,
"P" + pnum + " R1" ,
"P" + pnum + " L2" ,
"P" + pnum + " R2" ,
2015-02-01 21:58:25 +00:00
} ) ;
2015-01-31 19:37:26 +00:00
2017-04-26 03:59:37 +00:00
if ( type = = OctoshockDll . ePeripheralType . DualShock | | type = = OctoshockDll . ePeripheralType . DualAnalog )
{
definition . BoolButtons . Add ( "P" + pnum + " L3" ) ;
definition . BoolButtons . Add ( "P" + pnum + " R3" ) ;
definition . BoolButtons . Add ( "P" + pnum + " MODE" ) ;
definition . FloatControls . AddRange ( new [ ]
{
"P" + pnum + " LStick X" ,
"P" + pnum + " LStick Y" ,
"P" + pnum + " RStick X" ,
"P" + pnum + " RStick Y"
} ) ;
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 255.0f , 128.0f , 0.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 0.0f , 128.0f , 255.0f } ) ;
definition . FloatRanges . Add ( new [ ] { 255.0f , 128.0f , 0.0f } ) ;
}
2015-01-31 19:37:26 +00:00
}
2016-08-21 18:19:48 +00:00
}
2015-01-31 19:37:26 +00:00
2015-09-22 00:02:49 +00:00
definition . BoolButtons . AddRange ( new [ ]
2015-01-31 19:37:26 +00:00
{
"Open" ,
"Close" ,
"Reset"
} ) ;
2015-09-22 00:02:49 +00:00
definition . FloatControls . Add ( "Disc Select" ) ;
2015-01-31 19:37:26 +00:00
2015-09-22 00:02:49 +00:00
definition . FloatRanges . Add (
2015-07-17 21:04:56 +00:00
//new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set)
//hmm.. I don't see why this wouldn't work
2015-08-09 16:35:22 +00:00
new [ ] { 0f , 1f , 1f }
2015-01-31 19:37:26 +00:00
) ;
2015-09-22 00:02:49 +00:00
return definition ;
}
2015-09-21 21:33:29 +00:00
2015-01-31 19:37:26 +00:00
private void SetControllerButtons ( )
{
2015-09-22 00:02:49 +00:00
ControllerDefinition = CreateControllerDefinition ( _SyncSettings ) ;
2015-01-31 19:37:26 +00:00
}
2012-11-04 23:29:06 +00:00
private int [ ] frameBuffer = new int [ 0 ] ;
private Random rand = new Random ( ) ;
2012-12-10 00:43:43 +00:00
public CoreComm CoreComm { get ; private set ; }
2012-11-04 23:29:06 +00:00
//we can only have one active core at a time, due to the lib being so static.
//so we'll track the current one here and detach the previous one whenever a new one is booted up.
static Octoshock CurrOctoshockCore ;
2014-12-14 08:48:23 +00:00
2014-12-10 19:41:13 +00:00
IntPtr psx ;
2012-11-04 23:29:06 +00:00
bool disposed = false ;
public void Dispose ( )
{
if ( disposed ) return ;
2015-07-13 21:07:34 +00:00
disposed = true ;
//discs arent bound to shock core instances, but they may be mounted. kill the core instance first to effectively dereference the disc
2014-12-10 19:41:13 +00:00
OctoshockDll . shock_Destroy ( psx ) ;
psx = IntPtr . Zero ;
2012-11-04 23:29:06 +00:00
2015-07-13 21:07:34 +00:00
//destroy all discs we're managing (and the unmanaged octoshock resources)
foreach ( var di in discInterfaces )
{
di . Disc . Dispose ( ) ;
di . Dispose ( ) ;
}
discInterfaces . Clear ( ) ;
2012-11-04 23:29:06 +00:00
}
2014-12-11 08:30:37 +00:00
/// <summary>
/// Wraps the ShockDiscRef returned from the DLL and acts as a bridge between it and a DiscSystem disc
/// </summary>
2014-12-10 19:41:13 +00:00
class DiscInterface : IDisposable
2012-11-04 23:29:06 +00:00
{
2014-12-16 10:47:50 +00:00
public DiscInterface ( DiscSystem . Disc disc , Action < DiscInterface > cbActivity )
2014-12-10 19:41:13 +00:00
{
this . Disc = disc ;
cbReadTOC = ShockDisc_ReadTOC ;
cbReadLBA = ShockDisc_ReadLBA2448 ;
2014-12-10 23:48:47 +00:00
this . cbActivity = cbActivity ;
2015-07-08 03:29:11 +00:00
OctoshockDll . shock_CreateDisc ( out OctoshockHandle , IntPtr . Zero , disc . Session1 . LeadoutLBA , cbReadTOC , cbReadLBA , true ) ;
2014-12-10 19:41:13 +00:00
}
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
OctoshockDll . ShockDisc_ReadTOC cbReadTOC ;
OctoshockDll . ShockDisc_ReadLBA cbReadLBA ;
2014-12-16 10:47:50 +00:00
Action < DiscInterface > cbActivity ;
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
public DiscSystem . Disc Disc ;
public IntPtr OctoshockHandle ;
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
public void Dispose ( )
{
OctoshockDll . shock_DestroyDisc ( OctoshockHandle ) ;
OctoshockHandle = IntPtr . Zero ;
}
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
int ShockDisc_ReadTOC ( IntPtr opaque , OctoshockDll . ShockTOC * read_target , OctoshockDll . ShockTOCTrack * tracks101 )
{
2015-07-08 04:12:06 +00:00
read_target - > disc_type = ( byte ) Disc . TOC . Session1Format ;
read_target - > first_track = ( byte ) Disc . TOC . FirstRecordedTrackNumber ; //i _think_ thats what is meant here
read_target - > last_track = ( byte ) Disc . TOC . LastRecordedTrackNumber ; //i _think_ thats what is meant here
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
tracks101 [ 0 ] . lba = tracks101 [ 0 ] . adr = tracks101 [ 0 ] . control = 0 ;
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
for ( int i = 1 ; i < 100 ; i + + )
{
2015-07-08 04:12:06 +00:00
var item = Disc . TOC . TOCItems [ i ] ;
2015-06-23 18:57:11 +00:00
tracks101 [ i ] . adr = ( byte ) ( item . Exists ? 1 : 0 ) ;
2015-07-19 04:23:15 +00:00
tracks101 [ i ] . lba = ( uint ) item . LBA ;
2014-12-10 19:41:13 +00:00
tracks101 [ i ] . control = ( byte ) item . Control ;
}
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
////the lead-out track is to be synthesized
tracks101 [ read_target - > last_track + 1 ] . adr = 1 ;
tracks101 [ read_target - > last_track + 1 ] . control = 0 ;
2015-07-19 04:23:15 +00:00
tracks101 [ read_target - > last_track + 1 ] . lba = ( uint ) Disc . TOC . LeadoutLBA ;
2014-12-10 19:41:13 +00:00
//element 100 is to be copied as the lead-out track
tracks101 [ 100 ] = tracks101 [ read_target - > last_track + 1 ] ;
return OctoshockDll . SHOCK_OK ;
2012-11-04 23:29:06 +00:00
}
2015-07-05 18:24:51 +00:00
byte [ ] SectorBuffer = new byte [ 2448 ] ;
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
int ShockDisc_ReadLBA2448 ( IntPtr opaque , int lba , void * dst )
{
2014-12-16 10:47:50 +00:00
cbActivity ( this ) ;
2014-12-10 23:48:47 +00:00
2015-07-03 09:11:07 +00:00
//todo - cache reader
DiscSystem . DiscSectorReader dsr = new DiscSystem . DiscSectorReader ( Disc ) ;
2015-09-17 23:18:06 +00:00
int readed = dsr . ReadLBA_2448 ( lba , SectorBuffer , 0 ) ;
if ( readed = = 2448 )
{
Marshal . Copy ( SectorBuffer , 0 , new IntPtr ( dst ) , 2448 ) ;
return OctoshockDll . SHOCK_OK ;
}
else
return OctoshockDll . SHOCK_ERROR ;
2012-11-04 23:29:06 +00:00
}
2014-12-10 19:41:13 +00:00
}
2012-11-04 23:29:06 +00:00
2015-07-13 01:08:30 +00:00
public List < DiscSystem . Disc > Discs ;
2014-12-16 10:47:50 +00:00
List < DiscInterface > discInterfaces = new List < DiscInterface > ( ) ;
DiscInterface currentDiscInterface ;
2014-12-10 23:48:47 +00:00
2015-08-06 00:29:35 +00:00
public DisplayType Region { get { return SystemVidStandard = = OctoshockDll . eVidStandard . PAL ? DisplayType . PAL : DisplayType . NTSC ; } }
2014-12-17 02:54:43 +00:00
public OctoshockDll . eRegion SystemRegion { get ; private set ; }
public OctoshockDll . eVidStandard SystemVidStandard { get ; private set ; }
public System . Drawing . Size CurrentVideoSize { get ; private set ; }
2015-08-09 16:35:22 +00:00
2015-01-12 07:32:52 +00:00
public bool CurrentTrayOpen { get ; private set ; }
2014-12-19 03:24:48 +00:00
public int CurrentDiscIndexMounted { get ; private set ; }
public List < string > HackyDiscButtons = new List < string > ( ) ;
2014-12-17 01:23:24 +00:00
2014-12-10 19:41:13 +00:00
public IEmulatorServiceProvider ServiceProvider { get ; private set ; }
2014-12-12 01:49:54 +00:00
public bool DriveLightEnabled { get ; private set ; }
public bool DriveLightOn { get ; private set ; }
2014-12-10 19:41:13 +00:00
void Attach ( )
2012-11-04 23:29:06 +00:00
{
2014-12-10 19:41:13 +00:00
//attach this core as the current
if ( CurrOctoshockCore ! = null )
CurrOctoshockCore . Dispose ( ) ;
CurrOctoshockCore = this ;
//the psx instance cant be created until the desired region is known, which needs a disc, so we need the dll static attached first
2012-11-04 23:29:06 +00:00
}
2014-12-10 19:41:13 +00:00
static Octoshock ( )
2012-11-04 23:29:06 +00:00
{
}
2014-12-10 19:41:13 +00:00
2013-11-03 16:29:51 +00:00
public void ResetCounters ( )
2012-11-04 23:29:06 +00:00
{
Frame = 0 ;
2015-01-06 03:40:01 +00:00
LagCount = 0 ;
IsLagFrame = false ;
2012-11-04 23:29:06 +00:00
}
2014-12-10 23:43:11 +00:00
void SetInput ( )
{
2015-10-03 22:27:52 +00:00
var fioCfg = _SyncSettings . FIOConfig . ToLogical ( ) ;
2014-12-14 08:48:23 +00:00
2017-04-30 18:36:18 +00:00
for ( int port = 0 ; port < 2 ; port + + )
2015-01-31 21:00:30 +00:00
{
2017-04-30 18:36:18 +00:00
for ( int multiport = 0 ; multiport < 4 ; multiport + + )
{
//note: I would not say this port addressing scheme has been completely successful
//however, it may be because i was constantly constrained by having to adapt it to mednafen.. i dont know.
2015-10-03 22:27:52 +00:00
2017-04-30 18:36:18 +00:00
int portNum = ( port + 1 ) + ( ( multiport + 1 ) < < 4 ) ;
int slot = port * 4 + multiport ;
//no input to set
if ( fioCfg . Devices8 [ slot ] = = OctoshockDll . ePeripheralType . None )
continue ;
2015-10-03 22:27:52 +00:00
2017-04-30 18:36:18 +00:00
//address differently if it isn't multitap
if ( ! fioCfg . Multitaps [ port ] )
portNum = port + 1 ;
uint buttons = 0 ;
string pstring = "P" + fioCfg . PlayerAssignments [ slot ] + " " ;
if ( fioCfg . Devices8 [ slot ] = = OctoshockDll . ePeripheralType . NegCon )
2017-04-26 03:59:37 +00:00
{
2017-04-30 18:36:18 +00:00
//1,2,4 skipped (would be Select, L3, R3 on other pads)
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( pstring + "Start" ) ) buttons | = 8 ;
if ( _controller . IsPressed ( pstring + "Up" ) ) buttons | = 16 ;
if ( _controller . IsPressed ( pstring + "Right" ) ) buttons | = 32 ;
if ( _controller . IsPressed ( pstring + "Down" ) ) buttons | = 64 ;
if ( _controller . IsPressed ( pstring + "Left" ) ) buttons | = 128 ;
2017-04-30 18:36:18 +00:00
//256,512,1024 skipped (would be L2, R2, L1 on other pads)
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( pstring + "R" ) ) buttons | = 2048 ;
if ( _controller . IsPressed ( pstring + "B" ) ) buttons | = 4096 ;
if ( _controller . IsPressed ( pstring + "A" ) ) buttons | = 8192 ;
2017-04-30 18:36:18 +00:00
2017-05-02 01:09:11 +00:00
byte twist = ( byte ) _controller . GetFloat ( pstring + "Twist" ) ;
byte analog1 = ( byte ) _controller . GetFloat ( pstring + "1" ) ;
byte analog2 = ( byte ) _controller . GetFloat ( pstring + "2" ) ;
byte analogL = ( byte ) _controller . GetFloat ( pstring + "L" ) ;
2017-04-30 18:36:18 +00:00
OctoshockDll . shock_Peripheral_SetPadInput ( psx , portNum , buttons , twist , analog1 , analog2 , analogL ) ;
2017-04-26 03:59:37 +00:00
}
2017-04-30 18:36:18 +00:00
else
{
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( pstring + "Select" ) ) buttons | = 1 ;
if ( _controller . IsPressed ( pstring + "Start" ) ) buttons | = 8 ;
if ( _controller . IsPressed ( pstring + "Up" ) ) buttons | = 16 ;
if ( _controller . IsPressed ( pstring + "Right" ) ) buttons | = 32 ;
if ( _controller . IsPressed ( pstring + "Down" ) ) buttons | = 64 ;
if ( _controller . IsPressed ( pstring + "Left" ) ) buttons | = 128 ;
if ( _controller . IsPressed ( pstring + "L2" ) ) buttons | = 256 ;
if ( _controller . IsPressed ( pstring + "R2" ) ) buttons | = 512 ;
if ( _controller . IsPressed ( pstring + "L1" ) ) buttons | = 1024 ;
if ( _controller . IsPressed ( pstring + "R1" ) ) buttons | = 2048 ;
if ( _controller . IsPressed ( pstring + "Triangle" ) ) buttons | = 4096 ;
if ( _controller . IsPressed ( pstring + "Circle" ) ) buttons | = 8192 ;
if ( _controller . IsPressed ( pstring + "Cross" ) ) buttons | = 16384 ;
if ( _controller . IsPressed ( pstring + "Square" ) ) buttons | = 32768 ;
2017-04-30 18:36:18 +00:00
byte left_x = 0 , left_y = 0 , right_x = 0 , right_y = 0 ;
if ( fioCfg . Devices8 [ slot ] = = OctoshockDll . ePeripheralType . DualShock | | fioCfg . Devices8 [ slot ] = = OctoshockDll . ePeripheralType . DualAnalog )
{
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( pstring + "L3" ) ) buttons | = 2 ;
if ( _controller . IsPressed ( pstring + "R3" ) ) buttons | = 4 ;
if ( _controller . IsPressed ( pstring + "MODE" ) ) buttons | = 65536 ;
left_x = ( byte ) _controller . GetFloat ( pstring + "LStick X" ) ;
left_y = ( byte ) _controller . GetFloat ( pstring + "LStick Y" ) ;
right_x = ( byte ) _controller . GetFloat ( pstring + "RStick X" ) ;
right_y = ( byte ) _controller . GetFloat ( pstring + "RStick Y" ) ;
2017-04-30 18:36:18 +00:00
}
OctoshockDll . shock_Peripheral_SetPadInput ( psx , portNum , buttons , left_x , left_y , right_x , right_y ) ;
}
2015-10-03 22:27:52 +00:00
}
2015-01-31 21:00:30 +00:00
}
2014-12-10 23:43:11 +00:00
}
2015-08-09 16:35:22 +00:00
public class ResolutionInfo
{
public System . Drawing . Size Resolution , Padding ;
public System . Drawing . Size Total { get { return System . Drawing . Size . Add ( Resolution , Padding ) ; } }
}
2014-12-17 02:54:43 +00:00
/// <summary>
/// Calculates what the output resolution would be for the given input resolution and settings
/// </summary>
2015-08-09 16:35:22 +00:00
public static ResolutionInfo CalculateResolution ( OctoshockDll . eVidStandard standard , Settings settings , int w , int h )
2014-12-17 02:54:43 +00:00
{
2015-08-09 16:35:22 +00:00
ResolutionInfo ret = new ResolutionInfo ( ) ;
2015-08-02 16:35:19 +00:00
//some of this logic is duplicated in the c++ side, be sure to check there
2015-08-09 16:35:22 +00:00
//TODO - scanline control + framebuffer mode is majorly broken
2015-08-02 16:35:19 +00:00
int virtual_width = 800 ;
if ( settings . HorizontalClipping = = eHorizontalClipping . Basic ) virtual_width = 768 ;
if ( settings . HorizontalClipping = = eHorizontalClipping . Framebuffer ) virtual_width = 736 ;
2014-12-17 02:54:43 +00:00
int scanline_start = standard = = OctoshockDll . eVidStandard . NTSC ? settings . ScanlineStart_NTSC : settings . ScanlineStart_PAL ;
int scanline_end = standard = = OctoshockDll . eVidStandard . NTSC ? settings . ScanlineEnd_NTSC : settings . ScanlineEnd_PAL ;
int scanline_num = scanline_end - scanline_start + 1 ;
2015-08-09 16:35:22 +00:00
//int scanline_num = h; // I wanted to do this, but our logic for mednafen modes here is based on un-doubled resolution. i could do a hack to divide it by 2 though
2014-12-17 02:54:43 +00:00
int real_scanline_num = standard = = OctoshockDll . eVidStandard . NTSC ? 240 : 288 ;
int VirtualWidth = - 1 , VirtualHeight = - 1 ;
switch ( settings . ResolutionMode )
{
case eResolutionMode . Mednafen :
2015-08-09 16:35:22 +00:00
//mednafen uses 320xScanlines as the 1x size
//it does change the 1x width when doing basic clipping.
//and it does easily change the height when doing scanline removal.
//now, our framebuffer cropping mode is more complex...
VirtualWidth = ( standard = = OctoshockDll . eVidStandard . NTSC ) ? 320 : 363 ;
VirtualHeight = scanline_num ;
2015-08-02 16:35:19 +00:00
if ( settings . HorizontalClipping = = eHorizontalClipping . Basic )
2015-08-09 16:35:22 +00:00
VirtualWidth = ( standard = = OctoshockDll . eVidStandard . NTSC ) ? 302 : 384 ;
2015-08-02 16:35:19 +00:00
if ( settings . HorizontalClipping = = eHorizontalClipping . Framebuffer )
2015-08-09 16:35:22 +00:00
{
//mednafen typically sends us a framebuffer with overscan. 350x240 is a nominal example here. it's squished inward to 320x240 for correct PAR.
//ok: here we have a framebuffer without overscan. 320x240 nominal. So the VirtualWidth of what we got is off by a factor of 109.375%
//so a beginning approach would be this:
//VirtualWidth = (int)(VirtualWidth * 320.0f / 350);
//but that will shrink things which are already annoyingly shrunken.
//therefore, lets do that, but then scale the whole window by the same factor so the width becomes unscaled and now the height is scaled up!
//weird, huh?
VirtualHeight = ( int ) ( VirtualHeight * 350.0f / 320 ) ;
//now unfortunately we may have lost vertical pixels. common in the case of PAL (rendering 256 on a field of 288)
//therefore we'll be stretching way too much vertically here.
//lets add those pixels back with a new hack
if ( standard = = OctoshockDll . eVidStandard . PAL )
{
if ( h > 288 ) ret . Padding = new System . Drawing . Size ( 0 , 576 - h ) ;
else ret . Padding = new System . Drawing . Size ( 0 , 288 - h ) ;
}
else
{
if ( h > 288 ) ret . Padding = new System . Drawing . Size ( 0 , 480 - h ) ;
else ret . Padding = new System . Drawing . Size ( 0 , 240 - h ) ;
}
}
break ;
//384 / 288 = 1.3333333333333333333333333333333
case eResolutionMode . TweakedMednafen :
if ( standard = = OctoshockDll . eVidStandard . NTSC )
{
//dont make this 430, it's already been turned into 400 from 368+30 and then some fudge factor
VirtualWidth = 400 ;
VirtualHeight = ( int ) ( scanline_num * 300.0f / 240 ) ;
if ( settings . HorizontalClipping = = eHorizontalClipping . Basic )
VirtualWidth = 378 ;
}
else
{
//this is a bit tricky. we know we want 400 for the virtualwidth.
VirtualWidth = 400 ;
if ( settings . HorizontalClipping = = eHorizontalClipping . Basic )
VirtualWidth = 378 ;
//I'll be honest, I was just guessing here mostly
//I need the AR to basically work out to be 363/288 (thats what it was in mednafen mode) so...
VirtualHeight = ( int ) ( scanline_num * ( 400.0f / 363 * 288 ) / 288 ) ;
}
if ( settings . HorizontalClipping = = eHorizontalClipping . Framebuffer )
{
//see discussion above
VirtualHeight = ( int ) ( VirtualHeight * 350.0f / 320 ) ;
if ( standard = = OctoshockDll . eVidStandard . PAL )
{
if ( h > 288 ) ret . Padding = new System . Drawing . Size ( 0 , 576 - h ) ;
else ret . Padding = new System . Drawing . Size ( 0 , 288 - h ) ;
}
else
{
if ( h > 288 ) ret . Padding = new System . Drawing . Size ( 0 , 480 - h ) ;
else ret . Padding = new System . Drawing . Size ( 0 , 240 - h ) ;
}
}
2014-12-17 02:54:43 +00:00
break ;
2015-08-09 16:35:22 +00:00
2014-12-17 02:54:43 +00:00
case eResolutionMode . PixelPro :
VirtualWidth = virtual_width ;
VirtualHeight = scanline_num * 2 ;
break ;
2015-08-09 16:35:22 +00:00
case eResolutionMode . Debug :
VirtualWidth = w ;
VirtualHeight = h ;
2014-12-17 02:54:43 +00:00
break ;
}
2015-08-09 16:35:22 +00:00
ret . Resolution = new System . Drawing . Size ( VirtualWidth , VirtualHeight ) ;
return ret ;
2014-12-17 02:54:43 +00:00
}
2015-02-01 04:55:09 +00:00
void PokeDisc ( )
{
if ( CurrentDiscIndexMounted = = 0 )
{
currentDiscInterface = null ;
OctoshockDll . shock_PokeDisc ( psx , IntPtr . Zero ) ;
}
else
{
currentDiscInterface = discInterfaces [ CurrentDiscIndexMounted - 1 ] ;
OctoshockDll . shock_PokeDisc ( psx , currentDiscInterface . OctoshockHandle ) ;
}
}
2014-12-19 03:24:48 +00:00
void FrameAdvance_PrepDiscState ( )
2012-11-04 23:29:06 +00:00
{
2015-01-12 07:32:52 +00:00
//reminder: if this is the beginning of time, we can begin with the disc ejected or inserted.
//if tray open is requested, and valid, apply it
//in the first frame, go ahead and open it up so we have a chance to put a disc in it
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( "Open" ) & & ! CurrentTrayOpen | | Frame = = 0 )
2014-12-19 03:24:48 +00:00
{
OctoshockDll . shock_OpenTray ( psx ) ;
2015-01-12 07:32:52 +00:00
CurrentTrayOpen = true ;
2014-12-19 03:24:48 +00:00
}
2014-12-10 23:43:11 +00:00
2014-12-19 03:24:48 +00:00
//change the disc if needed, and valid
2015-01-12 07:32:52 +00:00
//also if frame is 0, we need to set a disc no matter what
2017-05-02 01:09:11 +00:00
int requestedDisc = ( int ) _controller . GetFloat ( "Disc Select" ) ;
2015-01-12 07:32:52 +00:00
if ( requestedDisc ! = CurrentDiscIndexMounted & & CurrentTrayOpen
| | Frame = = 0
)
2014-12-19 03:24:48 +00:00
{
2015-01-12 07:32:52 +00:00
//dont replace default disc with the leave-default placeholder!
2015-01-31 21:00:30 +00:00
if ( requestedDisc = = - 1 )
{
}
else
{
CurrentDiscIndexMounted = requestedDisc ;
}
2015-07-21 04:10:49 +00:00
if ( CurrentDiscIndexMounted = = 0 | | discInterfaces . Count = = 0 )
2014-12-19 03:24:48 +00:00
{
currentDiscInterface = null ;
OctoshockDll . shock_SetDisc ( psx , IntPtr . Zero ) ;
}
else
{
currentDiscInterface = discInterfaces [ CurrentDiscIndexMounted - 1 ] ;
OctoshockDll . shock_SetDisc ( psx , currentDiscInterface . OctoshockHandle ) ;
}
}
2014-12-16 10:47:50 +00:00
2015-01-12 07:32:52 +00:00
//if tray close is requested, and valid, apply it.
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( "Close" ) & & CurrentTrayOpen )
2015-01-12 07:32:52 +00:00
{
OctoshockDll . shock_CloseTray ( psx ) ;
CurrentTrayOpen = false ;
}
//if frame is 0 and user has made no preference, close the tray
2017-05-02 01:09:11 +00:00
if ( ! _controller . IsPressed ( "Close" ) & & ! _controller . IsPressed ( "Open" ) & & Frame = = 0 & & CurrentTrayOpen )
2014-12-16 10:47:50 +00:00
{
2014-12-19 03:24:48 +00:00
OctoshockDll . shock_CloseTray ( psx ) ;
2015-01-12 07:32:52 +00:00
CurrentTrayOpen = false ;
2014-12-16 10:47:50 +00:00
}
2014-12-19 03:24:48 +00:00
}
2014-12-16 10:47:50 +00:00
2017-05-02 01:09:11 +00:00
private IController _controller ;
public void FrameAdvance ( IController controller , bool render , bool rendersound )
2014-12-19 03:24:48 +00:00
{
2017-05-02 01:09:11 +00:00
_controller = controller ;
2015-01-12 07:32:52 +00:00
FrameAdvance_PrepDiscState ( ) ;
2014-12-19 03:24:48 +00:00
//clear drive light. itll get set to light up by sector-reading callbacks
//TODO - debounce this by a frame or so perhaps?
2015-08-02 16:35:19 +00:00
//TODO - actually, make this feedback from the core. there should be a register or status which effectively corresponds to whether it's reading.
2014-12-19 03:24:48 +00:00
DriveLightOn = false ;
2015-01-12 07:32:52 +00:00
Frame + + ;
2014-12-19 03:24:48 +00:00
SetInput ( ) ;
2014-12-16 10:47:50 +00:00
2015-09-14 00:17:07 +00:00
OctoshockDll . shock_SetLEC ( psx , _SyncSettings . EnableLEC ) ;
2014-12-17 01:23:24 +00:00
var ropts = new OctoshockDll . ShockRenderOptions ( )
{
scanline_start = SystemVidStandard = = OctoshockDll . eVidStandard . NTSC ? _Settings . ScanlineStart_NTSC : _Settings . ScanlineStart_PAL ,
scanline_end = SystemVidStandard = = OctoshockDll . eVidStandard . NTSC ? _Settings . ScanlineEnd_NTSC : _Settings . ScanlineEnd_PAL ,
} ;
2015-08-02 16:35:19 +00:00
if ( _Settings . HorizontalClipping = = eHorizontalClipping . Basic )
ropts . renderType = OctoshockDll . eShockRenderType . ClipOverscan ;
if ( _Settings . HorizontalClipping = = eHorizontalClipping . Framebuffer )
ropts . renderType = OctoshockDll . eShockRenderType . Framebuffer ;
2015-08-09 16:35:22 +00:00
if ( _Settings . DeinterlaceMode = = eDeinterlaceMode . Weave ) ropts . deinterlaceMode = OctoshockDll . eShockDeinterlaceMode . Weave ;
if ( _Settings . DeinterlaceMode = = eDeinterlaceMode . Bob ) ropts . deinterlaceMode = OctoshockDll . eShockDeinterlaceMode . Bob ;
if ( _Settings . DeinterlaceMode = = eDeinterlaceMode . BobOffset ) ropts . deinterlaceMode = OctoshockDll . eShockDeinterlaceMode . BobOffset ;
2014-12-17 01:23:24 +00:00
OctoshockDll . shock_SetRenderOptions ( psx , ref ropts ) ;
2014-12-16 10:47:50 +00:00
2014-12-18 08:35:56 +00:00
//prep tracer
2016-08-21 19:37:35 +00:00
if ( Tracer . Enabled )
2016-08-21 20:36:56 +00:00
OctoshockDll . shock_SetTraceCallback ( psx , IntPtr . Zero , trace_cb ) ;
2014-12-18 08:35:56 +00:00
else
OctoshockDll . shock_SetTraceCallback ( psx , IntPtr . Zero , null ) ;
2015-07-17 21:37:20 +00:00
//apply soft reset if needed
2017-05-02 01:09:11 +00:00
if ( _controller . IsPressed ( "Reset" ) )
2015-07-17 21:37:20 +00:00
OctoshockDll . shock_SoftReset ( psx ) ;
2014-12-18 08:35:56 +00:00
//------------------------
2014-12-10 19:41:13 +00:00
OctoshockDll . shock_Step ( psx , OctoshockDll . eShockStep . Frame ) ;
2014-12-18 08:35:56 +00:00
//------------------------
2014-12-10 19:41:13 +00:00
2015-01-06 03:40:01 +00:00
//lag maintenance:
int pad1 = OctoshockDll . shock_Peripheral_PollActive ( psx , 0x01 , true ) ;
int pad2 = OctoshockDll . shock_Peripheral_PollActive ( psx , 0x02 , true ) ;
IsLagFrame = true ;
if ( pad1 = = OctoshockDll . SHOCK_TRUE ) IsLagFrame = false ;
if ( pad2 = = OctoshockDll . SHOCK_TRUE ) IsLagFrame = false ;
2016-01-28 19:53:07 +00:00
if ( _Settings . GPULag )
IsLagFrame = OctoshockDll . shock_GetGPUUnlagged ( psx ) ! = OctoshockDll . SHOCK_TRUE ;
2015-01-06 03:40:01 +00:00
if ( IsLagFrame )
LagCount + + ;
2014-12-14 08:48:23 +00:00
//what happens to sound in this case?
if ( render = = false ) return ;
2012-11-04 23:29:06 +00:00
2014-12-14 08:48:23 +00:00
OctoshockDll . ShockFramebufferInfo fb = new OctoshockDll . ShockFramebufferInfo ( ) ;
2014-12-17 02:54:43 +00:00
//run this once to get current logical size
OctoshockDll . shock_GetFramebuffer ( psx , ref fb ) ;
CurrentVideoSize = new System . Drawing . Size ( fb . width , fb . height ) ;
2014-12-14 08:48:23 +00:00
if ( _Settings . ResolutionMode = = eResolutionMode . PixelPro )
fb . flags = OctoshockDll . eShockFramebufferFlags . Normalize ;
2012-11-04 23:29:06 +00:00
2014-12-14 08:48:23 +00:00
OctoshockDll . shock_GetFramebuffer ( psx , ref fb ) ;
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
int w = fb . width ;
int h = fb . height ;
2014-12-16 10:59:39 +00:00
BufferWidth = w ;
2012-11-04 23:29:06 +00:00
BufferHeight = h ;
2015-08-09 16:35:22 +00:00
var ri = CalculateResolution ( this . SystemVidStandard , _Settings , w , h ) ;
VirtualWidth = ri . Resolution . Width ;
VirtualHeight = ri . Resolution . Height ;
VideoProvider_Padding = ri . Padding ;
2014-12-14 08:48:23 +00:00
int len = w * h ;
2012-11-04 23:29:06 +00:00
if ( frameBuffer . Length ! = len )
2014-12-10 19:41:13 +00:00
{
Console . WriteLine ( "PSX FB size: {0},{1}" , fb . width , fb . height ) ;
2012-11-04 23:29:06 +00:00
frameBuffer = new int [ len ] ;
2014-12-10 19:41:13 +00:00
}
2012-11-04 23:29:06 +00:00
2014-12-10 19:41:13 +00:00
fixed ( int * ptr = frameBuffer )
{
fb . ptr = ptr ;
OctoshockDll . shock_GetFramebuffer ( psx , ref fb ) ;
}
2014-12-11 00:57:47 +00:00
fixed ( short * samples = sbuff )
{
2014-12-11 01:56:21 +00:00
sbuffcontains = OctoshockDll . shock_GetSamples ( psx , null ) ;
2014-12-11 00:57:47 +00:00
if ( sbuffcontains * 2 > sbuff . Length ) throw new InvalidOperationException ( "shock_GetSamples returned too many samples: " + sbuffcontains ) ;
2014-12-11 01:56:21 +00:00
OctoshockDll . shock_GetSamples ( psx , samples ) ;
2014-12-11 00:57:47 +00:00
}
2012-11-04 23:29:06 +00:00
}
2014-11-24 01:17:05 +00:00
2015-02-01 07:45:41 +00:00
public ControllerDefinition ControllerDefinition { get ; private set ; }
2012-11-04 23:29:06 +00:00
2014-12-12 00:25:46 +00:00
public int Frame { get ; private set ; }
2015-07-09 17:05:30 +00:00
public int LagCount { get ; set ; }
2016-01-26 10:34:42 +00:00
public bool IsLagFrame { get ; set ; }
2014-11-24 01:17:05 +00:00
2015-10-12 22:53:57 +00:00
public IInputCallbackSystem InputCallbacks
{
[FeatureNotImplemented]
get
{ throw new NotImplementedException ( ) ; }
}
2014-11-24 01:17:05 +00:00
[FeatureNotImplemented]
2012-11-04 23:29:06 +00:00
public bool DeterministicEmulation { get { return true ; } }
2014-11-24 01:17:05 +00:00
2012-11-04 23:29:06 +00:00
public int [ ] GetVideoBuffer ( ) { return frameBuffer ; }
public int VirtualWidth { get ; private set ; }
2014-12-10 21:56:12 +00:00
public int VirtualHeight { get ; private set ; }
2012-11-04 23:29:06 +00:00
public int BufferWidth { get ; private set ; }
public int BufferHeight { get ; private set ; }
public int BackgroundColor { get { return 0 ; } }
2017-05-05 16:25:38 +00:00
public int VsyncNumerator { get ; private set ; }
public int VsyncDenominator { get ; private set ; }
2017-05-05 16:21:37 +00:00
2015-08-09 16:35:22 +00:00
public System . Drawing . Size VideoProvider_Padding { get ; private set ; }
2014-12-11 00:57:47 +00:00
2014-12-11 01:56:21 +00:00
#region Debugging
2016-01-22 03:47:16 +00:00
OctoshockDll . ShockCallback_Mem mem_cb ;
void ShockMemCallback ( uint address , OctoshockDll . eShockMemCb type , uint size , uint value )
{
switch ( type )
{
case OctoshockDll . eShockMemCb . Read :
MemoryCallbacks . CallReads ( address ) ;
break ;
case OctoshockDll . eShockMemCb . Write :
MemoryCallbacks . CallWrites ( address ) ;
break ;
case OctoshockDll . eShockMemCb . Execute :
MemoryCallbacks . CallExecutes ( address ) ;
break ;
}
}
void InitMemCallbacks ( )
{
mem_cb = new OctoshockDll . ShockCallback_Mem ( ShockMemCallback ) ;
_memoryCallbacks . ActiveChanged + = RefreshMemCallbacks ;
}
void RefreshMemCallbacks ( )
{
OctoshockDll . eShockMemCb mask = OctoshockDll . eShockMemCb . None ;
if ( MemoryCallbacks . HasReads ) mask | = OctoshockDll . eShockMemCb . Read ;
if ( MemoryCallbacks . HasWrites ) mask | = OctoshockDll . eShockMemCb . Write ;
if ( MemoryCallbacks . HasExecutes ) mask | = OctoshockDll . eShockMemCb . Execute ;
OctoshockDll . shock_SetMemCb ( psx , mem_cb , mask ) ;
}
2014-12-11 01:56:21 +00:00
unsafe void SetMemoryDomains ( )
{
var mmd = new List < MemoryDomain > ( ) ;
IntPtr ptr ;
int size ;
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . MainRAM ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "MainRAM" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-11 01:56:21 +00:00
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . GPURAM ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "GPURAM" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-11 01:56:21 +00:00
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . SPURAM ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "SPURAM" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-11 01:56:21 +00:00
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . BiosROM ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "BiosROM" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-11 01:56:21 +00:00
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . PIOMem ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "PIOMem" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-11 01:56:21 +00:00
2014-12-15 05:28:06 +00:00
OctoshockDll . shock_GetMemData ( psx , out ptr , out size , OctoshockDll . eMemType . DCache ) ;
2017-04-24 18:47:40 +00:00
mmd . Add ( new MemoryDomainIntPtr ( "DCache" , MemoryDomain . Endian . Little , ptr , size , true , 4 ) ) ;
2014-12-15 05:28:06 +00:00
2015-01-24 16:02:28 +00:00
MemoryDomains = new MemoryDomainList ( mmd ) ;
2015-01-14 21:55:48 +00:00
( ServiceProvider as BasicServiceProvider ) . Register < IMemoryDomains > ( MemoryDomains ) ;
2014-12-11 01:56:21 +00:00
}
2015-01-14 21:55:48 +00:00
private IMemoryDomains MemoryDomains ;
2014-12-11 01:56:21 +00:00
#endregion
2014-12-11 00:57:47 +00:00
#region ISoundProvider
2014-12-17 01:23:24 +00:00
//private short[] sbuff = new short[1454 * 2]; //this is the most ive ever seen.. dont know why. two frames worth i guess
private short [ ] sbuff = new short [ 1611 * 2 ] ; //need this for pal
2014-12-11 00:57:47 +00:00
private int sbuffcontains = 0 ;
2016-12-11 17:14:42 +00:00
public void GetSamplesSync ( out short [ ] samples , out int nsamp )
2014-12-11 00:57:47 +00:00
{
samples = sbuff ;
nsamp = sbuffcontains ;
}
public void DiscardSamples ( )
{
sbuffcontains = 0 ;
}
2016-12-11 17:14:42 +00:00
public bool CanProvideAsync
{
get { return false ; }
}
public void SetSyncMode ( SyncSoundMode mode )
{
if ( mode = = SyncSoundMode . Async )
{
throw new NotSupportedException ( "Async mode is not supported." ) ;
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode . Sync ; }
}
public void GetSamplesAsync ( short [ ] samples )
{
throw new InvalidOperationException ( "Async mode is not supported." ) ;
}
2014-12-11 00:57:47 +00:00
#endregion
2014-12-11 07:27:21 +00:00
#region ISaveRam
public byte [ ] CloneSaveRam ( )
{
2015-10-03 22:27:52 +00:00
var cfg = _SyncSettings . FIOConfig . ToLogical ( ) ;
int nMemcards = cfg . NumMemcards ;
var buf = new byte [ 128 * 1024 * nMemcards ] ;
for ( int i = 0 , idx = 0 , addr = 0x01 ; i < 2 ; i + + , addr < < = 1 )
2014-12-11 07:27:21 +00:00
{
2015-10-03 22:27:52 +00:00
if ( cfg . Memcards [ i ] )
{
fixed ( byte * pbuf = buf )
{
var transaction = new OctoshockDll . ShockMemcardTransaction ( ) ;
transaction . buffer128k = pbuf + idx * 128 * 1024 ;
transaction . transaction = OctoshockDll . eShockMemcardTransaction . Read ;
OctoshockDll . shock_Peripheral_MemcardTransact ( psx , addr , ref transaction ) ;
idx + + ;
}
}
2014-12-11 07:27:21 +00:00
}
return buf ;
}
public void StoreSaveRam ( byte [ ] data )
{
2015-10-03 22:27:52 +00:00
var cfg = _SyncSettings . FIOConfig . ToLogical ( ) ;
for ( int i = 0 , idx = 0 , addr = 0x01 ; i < 2 ; i + + , addr < < = 1 )
2014-12-11 07:27:21 +00:00
{
2015-10-03 22:27:52 +00:00
if ( cfg . Memcards [ i ] )
{
fixed ( byte * pbuf = data )
{
var transaction = new OctoshockDll . ShockMemcardTransaction ( ) ;
transaction . buffer128k = pbuf + idx * 128 * 1024 ;
transaction . transaction = OctoshockDll . eShockMemcardTransaction . Write ;
OctoshockDll . shock_Peripheral_MemcardTransact ( psx , addr , ref transaction ) ;
idx + + ;
}
}
2014-12-11 07:27:21 +00:00
}
}
public bool SaveRamModified
{
get
{
2015-10-03 22:27:52 +00:00
var cfg = _SyncSettings . FIOConfig . ToLogical ( ) ;
for ( int i = 0 , addr = 0x01 ; i < 2 ; i + + , addr < < = 1 )
{
if ( cfg . Memcards [ i ] )
{
var transaction = new OctoshockDll . ShockMemcardTransaction ( ) ;
transaction . transaction = OctoshockDll . eShockMemcardTransaction . CheckDirty ;
OctoshockDll . shock_Peripheral_MemcardTransact ( psx , addr , ref transaction ) ;
if ( OctoshockDll . shock_Peripheral_MemcardTransact ( psx , addr , ref transaction ) = = OctoshockDll . SHOCK_TRUE )
return true ;
}
}
return false ;
2014-12-11 07:27:21 +00:00
}
}
#endregion //ISaveRam
2014-12-12 00:25:46 +00:00
#region Savestates
//THIS IS STILL AWFUL
JsonSerializer ser = new JsonSerializer ( ) { Formatting = Formatting . Indented } ;
class TextStateData
{
public int Frame ;
public int LagCount ;
public bool IsLagFrame ;
2014-12-19 03:24:48 +00:00
public bool CurrentDiscEjected ;
public int CurrentDiscIndexMounted ;
2014-12-12 00:25:46 +00:00
}
public void SaveStateText ( TextWriter writer )
{
var s = new TextState < TextStateData > ( ) ;
s . Prepare ( ) ;
var transaction = new OctoshockDll . ShockStateTransaction ( )
{
transaction = OctoshockDll . eShockStateTransaction . TextSave ,
ff = s . GetFunctionPointersSave ( )
} ;
int result = OctoshockDll . shock_StateTransaction ( psx , ref transaction ) ;
if ( result ! = OctoshockDll . SHOCK_OK )
throw new InvalidOperationException ( "eShockStateTransaction.TextSave returned error!" ) ;
s . ExtraData . IsLagFrame = IsLagFrame ;
s . ExtraData . LagCount = LagCount ;
s . ExtraData . Frame = Frame ;
2015-01-12 07:32:52 +00:00
s . ExtraData . CurrentDiscEjected = CurrentTrayOpen ;
2014-12-19 03:24:48 +00:00
s . ExtraData . CurrentDiscIndexMounted = CurrentDiscIndexMounted ;
2014-12-12 00:25:46 +00:00
ser . Serialize ( writer , s ) ;
// TODO write extra copy of stuff we don't use (WHY?)
}
public void LoadStateText ( TextReader reader )
{
var s = ( TextState < TextStateData > ) ser . Deserialize ( reader , typeof ( TextState < TextStateData > ) ) ;
s . Prepare ( ) ;
var transaction = new OctoshockDll . ShockStateTransaction ( )
{
transaction = OctoshockDll . eShockStateTransaction . TextLoad ,
2014-12-12 11:07:47 +00:00
ff = s . GetFunctionPointersLoad ( )
2014-12-12 00:25:46 +00:00
} ;
2014-12-14 08:48:23 +00:00
2014-12-12 00:25:46 +00:00
int result = OctoshockDll . shock_StateTransaction ( psx , ref transaction ) ;
if ( result ! = OctoshockDll . SHOCK_OK )
throw new InvalidOperationException ( "eShockStateTransaction.TextLoad returned error!" ) ;
IsLagFrame = s . ExtraData . IsLagFrame ;
LagCount = s . ExtraData . LagCount ;
Frame = s . ExtraData . Frame ;
2015-01-12 07:32:52 +00:00
CurrentTrayOpen = s . ExtraData . CurrentDiscEjected ;
2014-12-19 03:24:48 +00:00
CurrentDiscIndexMounted = s . ExtraData . CurrentDiscIndexMounted ;
2015-02-01 04:55:09 +00:00
PokeDisc ( ) ;
2014-12-12 00:25:46 +00:00
}
byte [ ] savebuff ;
2014-12-13 08:44:39 +00:00
byte [ ] savebuff2 ;
2014-12-12 00:25:46 +00:00
void StudySaveBufferSize ( )
{
var transaction = new OctoshockDll . ShockStateTransaction ( ) ;
transaction . transaction = OctoshockDll . eShockStateTransaction . BinarySize ;
int size = OctoshockDll . shock_StateTransaction ( psx , ref transaction ) ;
savebuff = new byte [ size ] ;
2015-08-09 16:35:22 +00:00
savebuff2 = new byte [ savebuff . Length + 4 + 4 + 4 + 1 + 1 + 4 ] ;
2014-12-12 00:25:46 +00:00
}
public void SaveStateBinary ( BinaryWriter writer )
{
fixed ( byte * psavebuff = savebuff )
{
var transaction = new OctoshockDll . ShockStateTransaction ( )
{
transaction = OctoshockDll . eShockStateTransaction . BinarySave ,
buffer = psavebuff ,
bufferLength = savebuff . Length
} ;
2014-12-14 08:48:23 +00:00
2014-12-12 00:25:46 +00:00
int result = OctoshockDll . shock_StateTransaction ( psx , ref transaction ) ;
if ( result ! = OctoshockDll . SHOCK_OK )
throw new InvalidOperationException ( "eShockStateTransaction.BinarySave returned error!" ) ;
writer . Write ( savebuff . Length ) ;
writer . Write ( savebuff ) ;
2014-12-13 08:44:39 +00:00
// other variables
writer . Write ( IsLagFrame ) ;
writer . Write ( LagCount ) ;
writer . Write ( Frame ) ;
2015-01-12 07:32:52 +00:00
writer . Write ( CurrentTrayOpen ) ;
2014-12-19 03:24:48 +00:00
writer . Write ( CurrentDiscIndexMounted ) ;
2014-12-12 00:25:46 +00:00
}
}
public void LoadStateBinary ( BinaryReader reader )
{
fixed ( byte * psavebuff = savebuff )
{
var transaction = new OctoshockDll . ShockStateTransaction ( )
{
transaction = OctoshockDll . eShockStateTransaction . BinaryLoad ,
buffer = psavebuff ,
bufferLength = savebuff . Length
} ;
int length = reader . ReadInt32 ( ) ;
if ( length ! = savebuff . Length )
throw new InvalidOperationException ( "Save buffer size mismatch!" ) ;
reader . Read ( savebuff , 0 , length ) ;
int ret = OctoshockDll . shock_StateTransaction ( psx , ref transaction ) ;
if ( ret ! = OctoshockDll . SHOCK_OK )
throw new InvalidOperationException ( "eShockStateTransaction.BinaryLoad returned error!" ) ;
2014-12-13 08:44:39 +00:00
// other variables
IsLagFrame = reader . ReadBoolean ( ) ;
LagCount = reader . ReadInt32 ( ) ;
Frame = reader . ReadInt32 ( ) ;
2015-01-12 07:32:52 +00:00
CurrentTrayOpen = reader . ReadBoolean ( ) ;
2014-12-19 03:24:48 +00:00
CurrentDiscIndexMounted = reader . ReadInt32 ( ) ;
2015-02-01 04:55:09 +00:00
PokeDisc ( ) ;
2014-12-12 00:25:46 +00:00
}
}
public byte [ ] SaveStateBinary ( )
{
2014-12-13 08:44:39 +00:00
//this are objectionable shenanigans, but theyre required to get the extra info in the stream. we need a better approach.
var ms = new MemoryStream ( savebuff2 , true ) ;
var bw = new BinaryWriter ( ms ) ;
SaveStateBinary ( bw ) ;
bw . Flush ( ) ;
if ( ms . Position ! = savebuff2 . Length )
throw new InvalidOperationException ( ) ;
ms . Close ( ) ;
return savebuff2 ;
2014-12-12 00:25:46 +00:00
}
public bool BinarySaveStatesPreferred
{
get { return true ; }
}
#endregion
2014-12-14 08:48:23 +00:00
#region Settings
Settings _Settings = new Settings ( ) ;
SyncSettings _SyncSettings ;
public enum eResolutionMode
{
PixelPro , Debug ,
Mednafen , TweakedMednafen
}
public class SyncSettings
{
public SyncSettings Clone ( )
{
2015-10-03 22:27:52 +00:00
return JsonConvert . DeserializeObject < SyncSettings > ( JsonConvert . SerializeObject ( this ) ) ;
2014-12-14 08:48:23 +00:00
}
2015-01-31 19:37:26 +00:00
2015-09-14 00:17:07 +00:00
public bool EnableLEC ;
2015-10-03 22:27:52 +00:00
public SyncSettings ( )
2015-02-01 14:44:08 +00:00
{
2015-10-03 22:27:52 +00:00
//initialize with historical default settings
var user = new OctoshockFIOConfigUser ( ) ;
user . Memcards [ 0 ] = user . Memcards [ 1 ] = true ;
user . Multitaps [ 0 ] = user . Multitaps [ 0 ] = false ;
user . Devices8 [ 0 ] = OctoshockDll . ePeripheralType . DualShock ;
user . Devices8 [ 4 ] = OctoshockDll . ePeripheralType . DualShock ;
FIOConfig = user ;
2015-02-01 14:44:08 +00:00
}
2015-10-03 22:27:52 +00:00
public OctoshockFIOConfigUser FIOConfig ;
2014-12-14 08:48:23 +00:00
}
2015-08-02 16:35:19 +00:00
public enum eHorizontalClipping
{
None ,
Basic ,
Framebuffer
}
2015-08-09 16:35:22 +00:00
public enum eDeinterlaceMode
{
Weave ,
Bob ,
BobOffset
}
2014-12-14 08:48:23 +00:00
public class Settings
{
2016-01-28 19:53:07 +00:00
[DisplayName("Determine Lag from GPU Frames")]
[DefaultValue(false)]
public bool GPULag { get ; set ; }
2014-12-14 08:48:23 +00:00
[DisplayName("Resolution Mode")]
[DefaultValue(eResolutionMode.PixelPro)]
public eResolutionMode ResolutionMode { get ; set ; }
2015-08-02 16:35:19 +00:00
[DisplayName("Horizontal Clipping")]
[DefaultValue(eHorizontalClipping.None)]
public eHorizontalClipping HorizontalClipping { get ; set ; }
2014-12-17 01:23:24 +00:00
[DisplayName("ScanlineStart_NTSC")]
[DefaultValue(0)]
public int ScanlineStart_NTSC { get ; set ; }
[DisplayName("ScanlineEnd_NTSC")]
[DefaultValue(239)]
public int ScanlineEnd_NTSC { get ; set ; }
[DisplayName("ScanlineStart_PAL")]
[DefaultValue(0)]
public int ScanlineStart_PAL { get ; set ; }
[DisplayName("ScanlineEnd_PAL")]
[DefaultValue(287)]
public int ScanlineEnd_PAL { get ; set ; }
2015-08-09 16:35:22 +00:00
[DisplayName("DeinterlaceMode")]
[DefaultValue(eDeinterlaceMode.Weave)]
public eDeinterlaceMode DeinterlaceMode { get ; set ; }
2014-12-17 01:23:24 +00:00
public void Validate ( )
{
if ( ScanlineStart_NTSC < 0 ) ScanlineStart_NTSC = 0 ;
if ( ScanlineStart_PAL < 0 ) ScanlineStart_PAL = 0 ;
if ( ScanlineEnd_NTSC > 239 ) ScanlineEnd_NTSC = 239 ;
if ( ScanlineEnd_PAL > 287 ) ScanlineEnd_PAL = 287 ;
2015-08-09 16:35:22 +00:00
2014-12-17 01:23:24 +00:00
//make sure theyre not in the wrong order
if ( ScanlineEnd_NTSC < ScanlineStart_NTSC )
{
int temp = ScanlineEnd_NTSC ;
ScanlineEnd_NTSC = ScanlineStart_NTSC ;
ScanlineStart_NTSC = temp ;
}
if ( ScanlineEnd_PAL < ScanlineStart_PAL )
{
int temp = ScanlineEnd_PAL ;
ScanlineEnd_PAL = ScanlineStart_PAL ;
ScanlineStart_PAL = temp ;
}
}
2014-12-14 08:48:23 +00:00
public Settings ( )
{
SettingsUtil . SetDefaultValues ( this ) ;
}
public Settings Clone ( )
{
return ( Settings ) MemberwiseClone ( ) ;
}
}
public Settings GetSettings ( )
{
return _Settings . Clone ( ) ;
}
public SyncSettings GetSyncSettings ( )
{
return _SyncSettings . Clone ( ) ;
}
public bool PutSettings ( Settings o )
{
2014-12-17 01:23:24 +00:00
_Settings . Validate ( ) ;
2014-12-14 08:48:23 +00:00
_Settings = o ;
2015-09-14 00:17:07 +00:00
//TODO - store settings into core? or we can just keep doing it before frameadvance
2014-12-14 08:48:23 +00:00
return false ;
}
public bool PutSyncSettings ( SyncSettings o )
{
2015-10-03 22:27:52 +00:00
//currently LEC and pad settings changes both require reboot
bool reboot = true ;
//we could do it this way roughly if we need to
//if(JsonConvert.SerializeObject(o.FIOConfig) != JsonConvert.SerializeObject(_SyncSettings.FIOConfig)
2015-09-14 00:17:07 +00:00
2014-12-14 08:48:23 +00:00
_SyncSettings = o ;
2015-10-03 22:27:52 +00:00
2015-09-14 00:17:07 +00:00
return reboot ;
2014-12-14 08:48:23 +00:00
}
#endregion
2012-11-04 23:29:06 +00:00
}
}