From 8a1e0c2b09c4f725f04863ac9b26870e25193078 Mon Sep 17 00:00:00 2001 From: zeromus Date: Sun, 24 Jul 2011 06:45:05 +0000 Subject: [PATCH] unmanaged core experiment --- BizHawk.Emulation/BizHawk.Emulation.csproj | 4 + BizHawk.Emulation/ExternalCores/EmuFile.cs | 110 +++++++ .../ExternalCores/ExternalCore.cs | 237 ++++++++++++++ BizHawk.Emulation/ExternalCores/PsxCore.cs | 80 +++++ BizHawk.Emulation/ExternalCores/Snippets.txt | 103 ++++++ .../BizHawk.MultiClient.csproj | 5 + BizHawk.MultiClient/Global.cs | 3 + BizHawk.MultiClient/MainForm.cs | 156 +++++---- BizHawk.MultiClient/RomGame.cs | 2 + .../BizHawk.UnmanagedCore.vcproj | 177 +++++++++++ BizHawk.UnmanagedCore/core.cpp | 91 ++++++ BizHawk.UnmanagedCore/core.h | 72 +++++ BizHawk.UnmanagedCore/emufile.cpp | 28 ++ BizHawk.UnmanagedCore/emufile.h | 299 ++++++++++++++++++ BizHawk.UnmanagedCore/emufile_hawk.cpp | 32 ++ BizHawk.UnmanagedCore/emufile_hawk.h | 65 ++++ BizHawk.UnmanagedCore/types.h | 8 + BizHawk.sln | 58 ++++ PsxHawk.Core/PsxCore.cpp | 40 +++ PsxHawk.Core/PsxCore.h | 33 ++ PsxHawk.Core/PsxHawk.Core.vcproj | 184 +++++++++++ 21 files changed, 1730 insertions(+), 57 deletions(-) create mode 100644 BizHawk.Emulation/ExternalCores/EmuFile.cs create mode 100644 BizHawk.Emulation/ExternalCores/ExternalCore.cs create mode 100644 BizHawk.Emulation/ExternalCores/PsxCore.cs create mode 100644 BizHawk.Emulation/ExternalCores/Snippets.txt create mode 100644 BizHawk.UnmanagedCore/BizHawk.UnmanagedCore.vcproj create mode 100644 BizHawk.UnmanagedCore/core.cpp create mode 100644 BizHawk.UnmanagedCore/core.h create mode 100644 BizHawk.UnmanagedCore/emufile.cpp create mode 100644 BizHawk.UnmanagedCore/emufile.h create mode 100644 BizHawk.UnmanagedCore/emufile_hawk.cpp create mode 100644 BizHawk.UnmanagedCore/emufile_hawk.h create mode 100644 BizHawk.UnmanagedCore/types.h create mode 100644 PsxHawk.Core/PsxCore.cpp create mode 100644 PsxHawk.Core/PsxCore.h create mode 100644 PsxHawk.Core/PsxHawk.Core.vcproj diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 0703028e19..fed03a47bd 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -190,6 +190,9 @@ + + + @@ -241,6 +244,7 @@ + diff --git a/BizHawk.Emulation/ExternalCores/EmuFile.cs b/BizHawk.Emulation/ExternalCores/EmuFile.cs new file mode 100644 index 0000000000..ab60130237 --- /dev/null +++ b/BizHawk.Emulation/ExternalCores/EmuFile.cs @@ -0,0 +1,110 @@ +using System; +using System.Security; +using System.Runtime.InteropServices; +using System.Linq; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Reflection.Emit; +using System.Threading; + +namespace BizHawk +{ + + public class EmuFile : ExternalCore + { + + public EmuFile(IExternalCoreAccessor accessor) + : base(accessor) + { + UnmanagedOpaque = QueryCoreCall>("EmuFile.Construct")(ManagedOpaque); + + QueryCoreCall>("EmuFile.Set_fp")("fgetc", ExportDelegate(new fgetcDelegate(fgetc))); + QueryCoreCall>("EmuFile.Set_fp")("fread", ExportDelegate(new freadDelegate(fread))); + QueryCoreCall>("EmuFile.Set_fp")("fwrite", ExportDelegate(new fwriteDelegate(fwrite))); + QueryCoreCall>("EmuFile.Set_fp")("fseek", ExportDelegate(new fseekDelegate(fseek))); + QueryCoreCall>("EmuFile.Set_fp")("ftell", ExportDelegate(new ftellDelegate(ftell))); + QueryCoreCall>("EmuFile.Set_fp")("size", ExportDelegate(new sizeDelegate(size))); + QueryCoreCall>("EmuFile.Set_fp")("size", ExportDelegate(new sizeDelegate(size))); + } + + public Stream BaseStream { get; set; } + + //do we want to have a finalizer? not sure. + bool disposed = false; + public override void Dispose() + { + if (disposed) return; + disposed = true; + + //we will call Delete in the c++ side, which will delete the object, and cause Dispose() to get called. + //but, Dispose() can never be called again due to setting the flag above + QueryCoreCall("EmuFile.Delete")(); + + //do we always want to do this? not sure. but usually. + BaseStream.Dispose(); + + base.Dispose(); + } + + int fgetc() + { + return BaseStream.ReadByte(); + } + IntPtr fread( + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] ptr, + IntPtr bytes) + { + long len = bytes.ToInt64(); + if (len >= int.MaxValue || len < 0) throw new ArgumentException(); + + int ret = BaseStream.Read(ptr, 0, (int)len); + return new IntPtr(ret); + } + IntPtr fseek(IntPtr offset, IntPtr origin) + { + SeekOrigin so = (SeekOrigin)origin.ToInt32(); + long loffset = offset.ToInt64(); + return new IntPtr(BaseStream.Seek(loffset, so)); + } + IntPtr ftell() { return new IntPtr(BaseStream.Position); } + IntPtr size() { return new IntPtr(BaseStream.Length); } + + void fwrite( + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] ptr, + IntPtr bytes) + { + long len = bytes.ToInt64(); + if (len >= int.MaxValue || len < 0) throw new ArgumentException(); + + BaseStream.Write(ptr, 0, (int)len); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate int fgetcDelegate(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void disposeDelegate(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate IntPtr freadDelegate( + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] ptr, + IntPtr bytes); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void fwriteDelegate( + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] ptr, + IntPtr bytes); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate IntPtr fseekDelegate(IntPtr offset, IntPtr origin); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate IntPtr ftellDelegate(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate IntPtr sizeDelegate(); + + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/ExternalCores/ExternalCore.cs b/BizHawk.Emulation/ExternalCores/ExternalCore.cs new file mode 100644 index 0000000000..f55bb72fb0 --- /dev/null +++ b/BizHawk.Emulation/ExternalCores/ExternalCore.cs @@ -0,0 +1,237 @@ +using System; +using System.Security; +using System.Threading; +using System.Text; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Linq; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Collections.Generic; + +namespace BizHawk +{ + + /// + /// universal interface to a shared library + /// + public interface ILibAccessor : IDisposable + { + IntPtr GetProcAddress(string name); + bool IsOpen { get; } + } + + /// + /// universal access to an external emulator core + /// + public interface IExternalCoreAccessor : IDisposable + { + IntPtr Signal(string type, IntPtr obj, string param, IntPtr value); + bool IsOpen { get; } + void RegisterCore(ExternalCore core, bool register); + } + + + public class CoreAccessor : IExternalCoreAccessor + { + ILibAccessor mLibAccessor; + public CoreAccessor(ILibAccessor accessor) + { + mLibAccessor = accessor; + if (accessor.IsOpen) + { + mSignal = (SignalCallbackDelegate)Marshal.GetDelegateForFunctionPointer(accessor.GetProcAddress("Core_signal"), typeof(SignalCallbackDelegate)); + IsOpen = true; + } + } + + public void RegisterCore(ExternalCore core, bool register) + { + if (core is StaticCoreCommon) return; + + //defer initialization until the core is needed, to avoid pointless costs for cores the user isnt using + if (!IsInitialized) + { + IsInitialized = true; + scc = new StaticCoreCommon(this); + scc.RegisterClientSignal(new SignalCallbackDelegate(ClientSignal)); + scc.Initialize(); + } + + if (register) + { + } + else + { + //delete + } + } + + StaticCoreCommon scc; + + public void Dispose() + { + if (mLibAccessor == null) return; + scc.Dispose(); + mLibAccessor.Dispose(); + mLibAccessor = null; + IsOpen = false; + } + + public bool IsOpen { get; private set; } + bool IsInitialized; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr SignalCallbackDelegate(string type, IntPtr obj, string param, IntPtr value); + SignalCallbackDelegate mSignal; + + //external cores call into the client from here + public IntPtr ClientSignal(string type, IntPtr obj, string param, IntPtr value) + { + //static calls + if (obj == IntPtr.Zero) + { + return scc.ClientSignal(type, obj, param, value); + } + else return IntPtr.Zero; + } + + public IntPtr Signal(string type, IntPtr obj, string param, IntPtr value) + { + if (!IsOpen) throw new InvalidOperationException("core accessor is open"); + return mSignal(type, obj, param, value); + } + } + + //todo - make abstract + public class ExternalCore : IDisposable + { + //rename to managed and unmanaged + public IntPtr ManagedOpaque; + public IntPtr UnmanagedOpaque; + static int _ManagedOpaque_Counter = 1; + + protected IExternalCoreAccessor mAccessor; + public ExternalCore(IExternalCoreAccessor accessor) + { + mAccessor = accessor; + mAccessor.RegisterCore(this,true); + lock (this) + { + ManagedOpaque = new IntPtr(_ManagedOpaque_Counter); + _ManagedOpaque_Counter++; + } + } + + public virtual void Dispose() + { + mAccessor.RegisterCore(this, false); + + //universal delete mechanism? + //probably not. + } + + /// + /// cores call into the client from here. this system is not fully baked yet, though + /// + virtual public IntPtr ClientSignal(string type, IntPtr obj, string param, IntPtr value) + { + return IntPtr.Zero; + } + + /// + /// merely emits an integer of the current system int size to an ILGenerator + /// + static void EmitIntPtr(ILGenerator gen, IntPtr val) + { + if (IntPtr.Size == 4) gen.Emit(OpCodes.Ldc_I4, val.ToInt32()); + else gen.Emit(OpCodes.Ldc_I8, val.ToInt64()); + } + + /// + /// retrieves a function pointer from the core and returns it as the specified delegate type + /// + protected void QueryCoreCall(out T del, string name) + { + del = QueryCoreCall(name); + } + + /// + /// retrieves a function pointer from the core and returns it as the specified delegate type + /// + protected T QueryCoreCall(string name) + { + MethodInfo mi = typeof(T).GetMethod("Invoke"); + ParameterInfo[] pis = mi.GetParameters(); + Type[] unmanagedParamTypes = new Type[pis.Length + 1]; + Type[] managedParamTypes = new Type[pis.Length]; + unmanagedParamTypes[0] = typeof(int); + for (int i = 0; i < pis.Length; i++) + { + unmanagedParamTypes[i + 1] = pis[i].ParameterType; + managedParamTypes[i] = pis[i].ParameterType; + } + + IntPtr fptr = mAccessor.Signal("QUERY_FUNCTION", IntPtr.Zero, name, IntPtr.Zero); + if (fptr == IntPtr.Zero) + throw new InvalidOperationException("external core was missing requested function: " + name); + + DynamicMethod dm = new DynamicMethod("", mi.ReturnType, managedParamTypes, GetType().Module); + ILGenerator gen = dm.GetILGenerator(); + EmitIntPtr(gen, UnmanagedOpaque); + for (int i = 0; i < pis.Length; i++) gen.Emit(OpCodes.Ldarg, i); + EmitIntPtr(gen, fptr); + gen.EmitCalli(OpCodes.Calli, CallingConvention.ThisCall, mi.ReturnType, unmanagedParamTypes); + gen.Emit(OpCodes.Ret); + + Delegate d = dm.CreateDelegate(typeof(T)); + return (T)(object)d; + } + + /// + /// exports a delegate as an IntPtr for use in unmanaged code and manages its life cycle to keep it from getting freed + /// + protected IntPtr ExportDelegate(Delegate d) + { + IntPtr retPtr = Marshal.GetFunctionPointerForDelegate(d); + listLiveDelegates.Add(d); + return retPtr; + } + + //need to hold on to these callbacks to make sure they dont get GCed while unmanaged code has a pointer to them + List listLiveDelegates = new List(); + } + + + public class StaticCoreCommon : ExternalCore + { + //keep in mind that we may need to make the console thread safe if we ever do any zany multithreaded things + + public StaticCoreCommon(IExternalCoreAccessor accessor) + : base(accessor) + { + } + + EmuFile Console; + public void Initialize() + { + Console = new EmuFile(mAccessor); + if (Log.HACK_LOG_STREAM != null) + Console.BaseStream = Log.HACK_LOG_STREAM; + else + Console.BaseStream = System.Console.OpenStandardOutput(); + mAccessor.Signal("SET_CONSOLE", IntPtr.Zero, null, Console.UnmanagedOpaque); + } + + public void RegisterClientSignal(CoreAccessor.SignalCallbackDelegate ClientSignal) + { + mAccessor.Signal("SET_CLIENT_SIGNAL", IntPtr.Zero, null, ExportDelegate(ClientSignal)); + } + + } + + + +} \ No newline at end of file diff --git a/BizHawk.Emulation/ExternalCores/PsxCore.cs b/BizHawk.Emulation/ExternalCores/PsxCore.cs new file mode 100644 index 0000000000..61e74f75c1 --- /dev/null +++ b/BizHawk.Emulation/ExternalCores/PsxCore.cs @@ -0,0 +1,80 @@ +using System; +using System.Runtime.InteropServices; +using System.Linq; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Collections.Generic; + +namespace BizHawk +{ + + public class PsxCore : ExternalCore, IEmulator, IVideoProvider, ISoundProvider + { + public PsxCore(IExternalCoreAccessor accessor) + : base(accessor) + { + var domains = new List(1); + memoryDomains = domains.AsReadOnly(); + CoreOutputComm = new CoreOutputComm(); + CoreInputComm = new CoreInputComm(); + + UnmanagedOpaque = QueryCoreCall>("PsxCore.Construct")(ManagedOpaque); + + QueryCoreCall(out cGetResolution, "PsxCore.GetResolution"); + QueryCoreCall(out cUpdateVideoBuffer, "PsxCore.UpdateVideoBuffer"); + QueryCoreCall(out cFrameAdvance, "PsxCore.FrameAdvance"); + } + + Func cGetResolution; + Action cFrameAdvance; + Action cUpdateVideoBuffer; + + //video provider + int[] videoBuffer = new int[256 * 256]; + public int[] GetVideoBuffer() { return videoBuffer; } + public int BufferWidth { get { return 256; } } + public int BufferHeight { get { return 192; } } + public int BackgroundColor { get { return 0; } } + + + + public string SystemId { get { return "PSX"; } } + public static readonly ControllerDefinition NullController = new ControllerDefinition { Name = "Null Controller" }; + + private Random rand = new Random(); + public CoreInputComm CoreInputComm { get; set; } + public CoreOutputComm CoreOutputComm { get; private set; } + public IVideoProvider VideoProvider { get { return this; } } + public ISoundProvider SoundProvider { get { return this; } } + public void LoadGame(IGame game) { } + public unsafe void FrameAdvance(bool render) + { + //if (render == false) return; + cFrameAdvance(); + fixed (int* vidbuf = &videoBuffer[0]) + cUpdateVideoBuffer(new IntPtr(vidbuf)); + } + public ControllerDefinition ControllerDefinition { get { return NullController; } } + public IController Controller { get; set; } + + public int Frame { get; set; } + public int LagCount { get { return 0; } set { return; } } + public bool IsLagFrame { get { return false; } } + + public byte[] SaveRam { get { return new byte[0]; } } + public bool DeterministicEmulation { get; set; } + public bool SaveRamModified { get; set; } + public void SaveStateText(TextWriter writer) { } + public void LoadStateText(TextReader reader) { } + public void SaveStateBinary(BinaryWriter writer) { } + public void LoadStateBinary(BinaryReader reader) { } + public byte[] SaveStateBinary() { return new byte[1]; } + public void GetSamples(short[] samples) { } + public void DiscardSamples() { } + private IList memoryDomains; + public IList MemoryDomains { get { return memoryDomains; } } + public MemoryDomain MainMemory { get { return memoryDomains[0]; } } + public void Dispose() { } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/ExternalCores/Snippets.txt b/BizHawk.Emulation/ExternalCores/Snippets.txt new file mode 100644 index 0000000000..56ee3b912e --- /dev/null +++ b/BizHawk.Emulation/ExternalCores/Snippets.txt @@ -0,0 +1,103 @@ +//////////////////////////// +//we can't use these because we need more clever control over the delegate type (marshalling attributes, for one thing) + + Delegate MyMakeDelegate(string methodName) + { + MethodInfo mi = GetType().GetMethod(methodName,BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + var parameters = mi.GetParameters() + .Select(p => p.ParameterType) + .ToArray(); + + Type t = DelegateCreator.MakeNewCustomDelegate(mi.ReturnType, parameters); + return Delegate.CreateDelegate(t, this, mi); + } + + + static class DelegateCreator + { + public static Type MakeNewCustomDelegate(Type ret_type, Type[] argtypes) + { + var _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + Type returnType = ret_type; + Type[] parameterTypes = argtypes; + TypeBuilder builder = DefineDelegateType("Delegate" + argtypes.Length); + builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + builder.DefineMethod("Invoke", MethodAttributes.VtableLayoutMask | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public, returnType, parameterTypes).SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + + //[UnmanagedFunctionPointer(CallingConvention.ThisCall)] + //builder.SetCustomAttribute(new CustomAttributeBuilder(new ConstructorInfo( + ConstructorInfo ci = typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) }); + CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[] { CallingConvention.ThisCall }); + builder.SetCustomAttribute(cab); + + return builder.CreateType(); + } + + internal static TypeBuilder DefineDelegateType(string name) + { + return DefineType(name, typeof(MulticastDelegate), TypeAttributes.AutoClass | TypeAttributes.Sealed | TypeAttributes.Public); + } + + static int _index; + private static TypeBuilder DefineType(string name, Type parent, TypeAttributes attr) + { + StringBuilder builder = new StringBuilder(name); + int num = Interlocked.Increment(ref _index); + builder.Append("$"); + builder.Append(num); + builder.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_'); + name = builder.ToString(); + return _myModule.DefineType(name, attr, parent); + } + + static AssemblyBuilder _myAssembly; + static ModuleBuilder _myModule; + static void InitializeAssemblyGen() + { + AssemblyName name = new AssemblyName("Snippets"); + CustomAttributeBuilder[] assemblyAttributes = new CustomAttributeBuilder[] { new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]) }; + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, assemblyAttributes); + _myModule = _myAssembly.DefineDynamicModule(name.Name, false); + _myAssembly.DefineVersionInfoResource(); + } + static DelegateCreator() + { + InitializeAssemblyGen(); + } + } + + +//////////////// +these are members of external core. theyre deprecated. + + + public IntPtr Signal(string param, IntPtr value) + { + return mAccessor.Signal(null, IntPtr.Zero, param, value); + } + + public IntPtr Signal(string param) + { + return mAccessor.Signal(null, IntPtr.Zero, param, IntPtr.Zero); + } + + public IntPtr Signal(string type, IntPtr obj, string param, Delegate value) + { + liveDelegates[value.Target ?? ostatic][param] = value; + return mAccessor.Signal(type, obj, param, Marshal.GetFunctionPointerForDelegate(value)); + } + + public IntPtr Signal(string param, Delegate value) + { + return Signal(null, IntPtr.Zero, param, value); + } + + public IntPtr Signal(string param, int value) + { + return mAccessor.Signal(null, IntPtr.Zero, param, new IntPtr(value)); + } + + public IntPtr Signal(string type, IntPtr obj, string param, IntPtr value) + { + return mAccessor.Signal(type, obj, param, value); + } \ No newline at end of file diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj index 6327367f56..abcb71bf15 100644 --- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj +++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj @@ -128,6 +128,7 @@ SoundConfig.cs + Form @@ -667,4 +668,8 @@ --> + + + + \ No newline at end of file diff --git a/BizHawk.MultiClient/Global.cs b/BizHawk.MultiClient/Global.cs index 4b6da1a98d..15d8ca330f 100644 --- a/BizHawk.MultiClient/Global.cs +++ b/BizHawk.MultiClient/Global.cs @@ -56,5 +56,8 @@ namespace BizHawk.MultiClient //TODO - wtf is this being used for public static bool MovieMode; + + public static CoreAccessor PsxCoreLibrary = new CoreAccessor(new Win32LibAccessor("PsxHawk.Core.dll")); + } } \ No newline at end of file diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs index 8bd5e4c37f..cdc2dc336f 100644 --- a/BizHawk.MultiClient/MainForm.cs +++ b/BizHawk.MultiClient/MainForm.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using System.Threading; using System.Drawing; using System.Drawing.Imaging; @@ -74,6 +75,7 @@ namespace BizHawk.MultiClient } UpdateStatusSlots(); + //in order to allow late construction of this database, we hook up a delegate here to dearchive the data and provide it on demand //we could background thread this later instead if we wanted to be real clever NES.BootGodDB.GetDatabaseBytes = () => @@ -819,70 +821,83 @@ namespace BizHawk.MultiClient else return false; } - var game = new RomGame(file); IEmulator nextEmulator = null; + RomGame game = null; - switch (game.System) + if (Path.GetExtension(path).ToLower() == ".iso") { - case "SG": - case "SMS": - nextEmulator = new SMS(); - if (Global.Config.SmsEnableFM) game.AddOptions("UseFM"); - if (Global.Config.SmsAllowOverlock) game.AddOptions("AllowOverclock"); - if (Global.Config.SmsForceStereoSeparation) game.AddOptions("ForceStereo"); - break; - case "GG": - nextEmulator = new SMS { IsGameGear = true }; - if (Global.Config.SmsAllowOverlock) game.AddOptions("AllowOverclock"); - break; - case "PCE": - nextEmulator = new PCEngine(NecSystemType.TurboGrafx); - break; - case "SGX": - nextEmulator = new PCEngine(NecSystemType.SuperGrafx); - break; - case "GEN": - nextEmulator = new Genesis(true);//TODO - break; - case "TI83": - nextEmulator = new TI83(); - if (Global.Config.TI83autoloadKeyPad) - LoadTI83KeyPad(); - break; - case "NES": - { - NES nes = new NES(); - nextEmulator = nes; - if (Global.Config.NESAutoLoadPalette && Global.Config.NESPaletteFile.Length > 0 && HawkFile.ExistsAt(Global.Config.NESPaletteFile)) + if (Global.PsxCoreLibrary.IsOpen) + { + nextEmulator = new PsxCore(Global.PsxCoreLibrary); + game = new RomGame(); + } + } + else + { + game = new RomGame(file); + + switch (game.System) + { + case "SG": + case "SMS": + nextEmulator = new SMS(); + if (Global.Config.SmsEnableFM) game.AddOptions("UseFM"); + if (Global.Config.SmsAllowOverlock) game.AddOptions("AllowOverclock"); + if (Global.Config.SmsForceStereoSeparation) game.AddOptions("ForceStereo"); + break; + case "GG": + nextEmulator = new SMS { IsGameGear = true }; + if (Global.Config.SmsAllowOverlock) game.AddOptions("AllowOverclock"); + break; + case "PCE": + nextEmulator = new PCEngine(NecSystemType.TurboGrafx); + break; + case "SGX": + nextEmulator = new PCEngine(NecSystemType.SuperGrafx); + break; + case "GEN": + nextEmulator = new Genesis(true);//TODO + break; + case "TI83": + nextEmulator = new TI83(); + if (Global.Config.TI83autoloadKeyPad) + LoadTI83KeyPad(); + break; + case "NES": { - nes.SetPalette(NES.Palettes.Load_FCEUX_Palette(HawkFile.ReadAllBytes(Global.Config.NESPaletteFile))); + NES nes = new NES(); + nextEmulator = nes; + if (Global.Config.NESAutoLoadPalette && Global.Config.NESPaletteFile.Length > 0 && HawkFile.ExistsAt(Global.Config.NESPaletteFile)) + { + nes.SetPalette(NES.Palettes.Load_FCEUX_Palette(HawkFile.ReadAllBytes(Global.Config.NESPaletteFile))); + } } - } - break; - case "GB": - nextEmulator = new Gameboy(); - break; + break; + case "GB": + nextEmulator = new Gameboy(); + break; + } + + if (nextEmulator == null) throw new Exception(); + + try + { + nextEmulator.CoreInputComm = Global.CoreInputComm; + + //this is a bit hacky, but many cores do not take responsibility for setting this, so we need to set it for them. + nextEmulator.CoreOutputComm.RomStatus = game.Status; + + nextEmulator.LoadGame(game); + } + catch (Exception ex) + { + MessageBox.Show("Exception during loadgame:\n\n" + ex.ToString()); + return false; + } } - if (nextEmulator == null) - { - throw new Exception(); - } + if (nextEmulator == null) throw new Exception(); - try - { - nextEmulator.CoreInputComm = Global.CoreInputComm; - - //this is a bit hacky, but many cores do not take responsibility for setting this, so we need to set it for them. - nextEmulator.CoreOutputComm.RomStatus = game.Status; - - nextEmulator.LoadGame(game); - } - catch (Exception ex) - { - MessageBox.Show("Exception during loadgame:\n\n" + ex.ToString()); - return false; - } CloseGame(); Global.Emulator.Dispose(); @@ -1916,11 +1931,38 @@ namespace BizHawk.MultiClient typeof(ToolStrip).InvokeMember("ProcessMnemonicInternal", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, menuStrip1, new object[] { c }); } + string FormatFilter(params string[] args) + { + var sb = new StringBuilder(); + if (args.Length % 2 != 0) throw new ArgumentException(); + int num = args.Length / 2; + for (int i = 0; i < num; i++) + { + sb.AppendFormat("{0} ({1})|{1}", args[i * 2], args[i * 2 + 1]); + if (i != num - 1) sb.Append('|'); + } + string str = sb.ToString().Replace("%ARCH%", "*.zip;*.rar;*.7z"); + str = str.Replace(";", "; "); + return str; + } + private void OpenROM() { var ofd = new OpenFileDialog(); ofd.InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId); - ofd.Filter = "Rom Files|*.NES;*.SMS;*.GG;*.SG;*.PCE;*.SGX;*.GB;*.BIN;*.SMD;*.ROM;*.ZIP;*.7z|NES|*.NES|Master System|*.SMS;*.GG;*.SG;*.ZIP;*.7z|PC Engine|*.PCE;*.SGX;*.ZIP;*.7z|Gameboy|*.GB;*.ZIP;*.7z|TI-83|*.rom|Archive Files|*.zip;*.7z|Savestate|*.state|All Files|*.*"; + //"Rom Files|*.NES;*.SMS;*.GG;*.SG;*.PCE;*.SGX;*.GB;*.BIN;*.SMD;*.ROM;*.ZIP;*.7z|NES (*.NES)|*.NES|Master System|*.SMS;*.GG;*.SG;*.ZIP;*.7z|PC Engine|*.PCE;*.SGX;*.ZIP;*.7z|Gameboy|*.GB;*.ZIP;*.7z|TI-83|*.rom|Archive Files|*.zip;*.7z|Savestate|*.state|All Files|*.*"; + ofd.Filter = FormatFilter( + "Rom Files", "*.nes;*.sms;*.gg;*.sg;*.pce;*.sgx;*.gb;*.bin;*.smd;*.rom;*.iso;%ARCH%", + "Disc Images", "*.iso", + "NES", "*.nes;%ARCH%", + "Master System", "*.sms;*.gg;*.sg;%ARCH%", + "PC Engine", "*.pce;*.sgx;%ARCH%", + "Gameboy", "*.gb;%ARCH%", + "TI-83", "*.rom;%ARCH%", + "Archive Files", "%ARCH%", + "Savestate", "*.state", + "All Files", "*.*"); + ofd.RestoreDirectory = false; Global.Sound.StopSound(); diff --git a/BizHawk.MultiClient/RomGame.cs b/BizHawk.MultiClient/RomGame.cs index 321f6a0348..2c2f1de2e9 100644 --- a/BizHawk.MultiClient/RomGame.cs +++ b/BizHawk.MultiClient/RomGame.cs @@ -17,6 +17,8 @@ namespace BizHawk.MultiClient private List options; private const int BankSize = 4096; + public RomGame() { } + public RomGame(HawkFile file) : this(file, null) { } public RomGame(HawkFile file, string patch) diff --git a/BizHawk.UnmanagedCore/BizHawk.UnmanagedCore.vcproj b/BizHawk.UnmanagedCore/BizHawk.UnmanagedCore.vcproj new file mode 100644 index 0000000000..c74ddbd85d --- /dev/null +++ b/BizHawk.UnmanagedCore/BizHawk.UnmanagedCore.vcproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BizHawk.UnmanagedCore/core.cpp b/BizHawk.UnmanagedCore/core.cpp new file mode 100644 index 0000000000..9b530e2871 --- /dev/null +++ b/BizHawk.UnmanagedCore/core.cpp @@ -0,0 +1,91 @@ +#include +#include "core.h" +#include "emufile.h" +#include "emufile_hawk.h" + + +//TODO +class DISC_INTERFACE +{ +}; + + +//TODO - setup a null file to use as the default console, so we dont have to check whether its set to null everywhere +class EMUFILE_HAWK; +EMUFILE_HAWK* con = NULL; + +static void* (*ClientSignal_cb)(const char*,void*,const char*,void*); +void* ClientSignal(const char* type, void* obj, const char* _param, void* value) +{ + return ClientSignal_cb(type,obj,_param,value); +} + + +//core objects function pointers get registered here +class FunctionRegistry +{ +private: + + typedef std::map TFunctionMap; + TFunctionMap map; + +public: + static FunctionRegistry& Instance() { + static FunctionRegistry inst; + return inst; + } + + void Register(const char* _name, void* _funcptr) + { + map[_name] = _funcptr; + } + + void* Lookup(const char* name) + { + TFunctionMap::iterator it(map.find(name)); + if(it == map.end()) return NULL; + else return it->second; + } + +private: + FunctionRegistry() {} +}; + +void _registerFunction(const char* _name, void* _funcptr) +{ + FunctionRegistry::Instance().Register(_name,_funcptr); +} + +//maybe youll need this some day... but probably not. +//#pragma comment(linker, "/include:_Core_signal") +extern "C" __declspec(dllexport) void* Core_signal(const char* type, void* obj, const char* param, void* value) +{ + //use this to log signals + if(con) con->fprintf("core signal: %s : %s\n",type?type:"n/a",param?param:"n/a"); + + if(!strcmp(type,"SET_CLIENT_SIGNAL")) + { + ClientSignal_cb = (void *(*)(const char*,void*,const char*,void*))value; + return 0; + } + + if(!strcmp(type,"SET_CONSOLE")) + { + con = (EMUFILE_HAWK*)value; + return 0; + } + + //query a function pointer for later blazing fast reuse + if(!strcmp(type,"QUERY_FUNCTION")) + return FunctionRegistry::Instance().Lookup(param); + + //TODO - custom core static operations? + + //force a reference to our core types. a bit annoying but if its this easy i guess i dont mind + if(!strcmp(type,"IMPOSSIBLE")) + { + return new EMUFILE_HAWK(0); + } + + return 0; +} diff --git a/BizHawk.UnmanagedCore/core.h b/BizHawk.UnmanagedCore/core.h new file mode 100644 index 0000000000..9af8231feb --- /dev/null +++ b/BizHawk.UnmanagedCore/core.h @@ -0,0 +1,72 @@ +#ifndef _CORE_H_ +#define _CORE_H_ + +#include +#include + +#ifndef CTASSERT +#define CTASSERT(x) typedef char __assert ## y[(x) ? 1 : -1]; +#endif + +//use this to send a signal to the client. +//it may not be completely fully-baked yet though +void* ClientSignal(const char* type, void* obj, const char* _param, void* value); + +class EMUFILE_HAWK; + +//use this to print output to the client +extern EMUFILE_HAWK* con; + + +//this is supposedly illegal. but i say its perfectly legal, as long as im not using virtual functions. so stuff it. +//well, since we're doing it illegally, we need to resist the urge to generalize the function pointer system (to emufile and disc) +//since we may need to change all these later to work un-generalized +//but seriously. before doing that, i would rather return sizeof(functionpointer) bytes as a token to the managed code and pass that back in +//(MP stands for MEMBER POINTER) +template void* MP(const T& a) +{ + union U{ + void* vp; + T t; + } u; + u.t = a; + return u.vp; + CTASSERT(sizeof(U)==4||(sizeof(U)==8&&sizeof(void*)==8)); +} + +//this is a function pointer which can be assigned without having to type the function protoype again to cast it. +template class FP +{ +private: + template T MPX(void* a) + { + union U{ + void* vp; + T t; + } u; + u.vp = a; + return u.t; + CTASSERT(sizeof(U)==4||(sizeof(U)==8&&sizeof(void*)==8)); + } +public: + T func; + void set(void* val) { func = MPX(val); } +}; + +//nothing important +void _registerFunction(const char* _name, void* _funcptr); +struct FunctionRecord +{ + FunctionRecord(const char* _name, void* _funcptr) + { + _registerFunction(_name,_funcptr); + } +}; + +//register a core object member function. put it in a global static array +template FunctionRecord FUNC(const char* name, const T& a) +{ + return FunctionRecord(name,MP(a)); +} + +#endif //_CORE_H_ diff --git a/BizHawk.UnmanagedCore/emufile.cpp b/BizHawk.UnmanagedCore/emufile.cpp new file mode 100644 index 0000000000..d07ab65f50 --- /dev/null +++ b/BizHawk.UnmanagedCore/emufile.cpp @@ -0,0 +1,28 @@ +#include "types.h" +#include "emufile.h" + +#include + +bool EMUFILE::readAllBytes(std::vector* dstbuf, const std::string& fname) +{ + EMUFILE_FILE file(fname.c_str(),"rb"); + if(file.fail()) return false; + int size = file.size(); + dstbuf->resize(size); + file.fread(&dstbuf->at(0),size); + return true; +} + +EMUFILE* EMUFILE::memwrap(EMUFILE* fp) +{ + EMUFILE_FILE* file; + EMUFILE_MEMORY* mem; + file = dynamic_cast(fp); + mem = dynamic_cast(fp); + if(mem) return mem; + mem = new EMUFILE_MEMORY(file->size()); + if(file->size()==0) return mem; + file->fread(mem->buf(),file->size()); + delete file; + return mem; +} \ No newline at end of file diff --git a/BizHawk.UnmanagedCore/emufile.h b/BizHawk.UnmanagedCore/emufile.h new file mode 100644 index 0000000000..77ab466b2b --- /dev/null +++ b/BizHawk.UnmanagedCore/emufile.h @@ -0,0 +1,299 @@ + /* Copyright (C) 2009 DeSmuME team + * + * This file is part of DeSmuME + * + * DeSmuME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * DeSmuME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DeSmuME; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef EMUFILE_H +#define EMUFILE_H + +#include +#include +#include +#include +#include +#include +#include + +//should be changed to #ifdef FCEUX but too much work +#ifndef DESMUME +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef char s8; +typedef short s16; +typedef int s32; +#endif + +#ifdef _XBOX +#undef min; +#undef max; +#endif + +class EMUFILE { +protected: + bool failbit; + +public: + EMUFILE() + : failbit(false) + {} + + + //takes control of the provided EMUFILE and returns a new EMUFILE which is guranteed to be in memory + static EMUFILE* memwrap(EMUFILE* fp); + + virtual ~EMUFILE() {} + + static bool readAllBytes(std::vector* buf, const std::string& fname); + + bool fail() { return failbit; } + + bool eof() { return size()==ftell(); } + + size_t fread(const void *ptr, size_t bytes){ + return _fread(ptr,bytes); + } + + void unget() { fseek(-1,SEEK_CUR); } + + //virtuals +public: + + virtual FILE *get_fp() = 0; + + virtual int fprintf(const char *format, ...) = 0; + + virtual int fgetc() = 0; + virtual int fputc(int c) = 0; + + virtual size_t _fread(const void *ptr, size_t bytes) = 0; + + //removing these return values for now so we can find any code that might be using them and make sure + //they handle the return values correctly + + virtual void fwrite(const void *ptr, size_t bytes) = 0; + + virtual int fseek(int offset, int origin) = 0; + + virtual int ftell() = 0; + virtual int size() = 0; +}; + +//todo - handle read-only specially? +class EMUFILE_MEMORY : public EMUFILE { +protected: + std::vector *vec; + bool ownvec; + s32 pos, len; + + void reserve(u32 amt) { + if(vec->size() < amt) + vec->resize(amt); + } + +public: + + EMUFILE_MEMORY(std::vector *underlying) : vec(underlying), ownvec(false), pos(0), len(underlying->size()) { } + EMUFILE_MEMORY(u32 preallocate) : vec(new std::vector()), ownvec(true), pos(0), len(0) { + vec->resize(preallocate); + len = preallocate; + } + EMUFILE_MEMORY() : vec(new std::vector()), ownvec(true), pos(0), len(0) { vec->reserve(1024); } + EMUFILE_MEMORY(void* buf, s32 size) : vec(new std::vector()), ownvec(true), pos(0), len(size) { + vec->resize(size); + if(size != 0) + memcpy(&vec[0],buf,size); + } + + ~EMUFILE_MEMORY() { + if(ownvec) delete vec; + } + + u8* buf() { return &(*vec)[0]; } + + std::vector* get_vec() { return vec; }; + + virtual FILE *get_fp() { return NULL; } + + virtual int fprintf(const char *format, ...) { + va_list argptr; + va_start(argptr, format); + + //we dont generate straight into the buffer because it will null terminate (one more byte than we want) + int amt = vsnprintf(0,0,format,argptr); + char* tempbuf = new char[amt+1]; + vsprintf(tempbuf,format,argptr); + fwrite(tempbuf,amt); + delete[] tempbuf; + va_end(argptr); + return amt; + }; + + virtual int fgetc() { + u8 temp; + + //need an optimized codepath + //if(_fread(&temp,1) != 1) + // return EOF; + //else return temp; + u32 remain = len-pos; + if(remain<1) { + failbit = true; + return -1; + } + temp = buf()[pos]; + pos++; + return temp; + } + virtual int fputc(int c) { + u8 temp = (u8)c; + //TODO + //if(fwrite(&temp,1)!=1) return EOF; + fwrite(&temp,1); + + return 0; + } + + virtual size_t _fread(const void *ptr, size_t bytes){ + u32 remain = len-pos; + u32 todo = std::min(remain,(u32)bytes); + memcpy((void*)ptr,buf()+pos,todo); + pos += todo; + if(todoresize(len); + } + + virtual int size() { return (int)len; } +}; + +class EMUFILE_FILE : public EMUFILE { +protected: + FILE* fp; + +private: + void open(const char* fname, const char* mode) + { + fp = fopen(fname,mode); + if(!fp) + failbit = true; + } + +public: + + EMUFILE_FILE(const std::string& fname, const char* mode) { open(fname.c_str(),mode); } + EMUFILE_FILE(const char* fname, const char* mode) { open(fname,mode); } + + virtual ~EMUFILE_FILE() { + if(NULL != fp) + fclose(fp); + } + + virtual FILE *get_fp() { + return fp; + } + + bool is_open() { return fp != NULL; } + + virtual int fprintf(const char *format, ...) { + va_list argptr; + va_start(argptr, format); + int ret = ::vfprintf(fp, format, argptr); + va_end(argptr); + return ret; + }; + + virtual int fgetc() { + return ::fgetc(fp); + } + virtual int fputc(int c) { + return ::fputc(c, fp); + } + + virtual size_t _fread(const void *ptr, size_t bytes){ + size_t ret = ::fread((void*)ptr, 1, bytes, fp); + if(ret < bytes) + failbit = true; + return ret; + } + + //removing these return values for now so we can find any code that might be using them and make sure + //they handle the return values correctly + + virtual void fwrite(const void *ptr, size_t bytes){ + size_t ret = ::fwrite((void*)ptr, 1, bytes, fp); + if(ret < bytes) + failbit = true; + } + + virtual int fseek(int offset, int origin){ + return ::fseek(fp, offset, origin); + } + + virtual int ftell() { + return (u32)::ftell(fp); + } + + virtual int size() { + int oldpos = ftell(); + fseek(0,SEEK_END); + int len = ftell(); + fseek(oldpos,SEEK_SET); + return len; + } + +}; + +#endif diff --git a/BizHawk.UnmanagedCore/emufile_hawk.cpp b/BizHawk.UnmanagedCore/emufile_hawk.cpp new file mode 100644 index 0000000000..d6faa98cc2 --- /dev/null +++ b/BizHawk.UnmanagedCore/emufile_hawk.cpp @@ -0,0 +1,32 @@ +#include "emufile_hawk.h" +#include "core.h" + +static FunctionRecord records[] = { + FUNC("EmuFile.Construct", &EMUFILE_HAWK::Construct), + FUNC("EmuFile.Set_fp", &EMUFILE_HAWK::Set_fp), + FUNC("EmuFile.Delete", &EMUFILE_HAWK::Delete), +}; + + +int EMUFILE_HAWK::fprintf(const char *format, ...) +{ + va_list argptr; + va_start(argptr, format); + + //could use a small static buf here optionally for quickness's sake but we may regret it if we multithread later + + int amt = vsnprintf(0,0,format,argptr); + char* tempbuf = new char[amt+1]; + vsprintf(tempbuf,format,argptr); + fwrite(tempbuf,amt); + delete[] tempbuf; + va_end(argptr); + return amt; +} + + +void* EMUFILE_HAWK::Construct(void* ManagedOpaque) +{ + return new EMUFILE_HAWK(ManagedOpaque); +} + diff --git a/BizHawk.UnmanagedCore/emufile_hawk.h b/BizHawk.UnmanagedCore/emufile_hawk.h new file mode 100644 index 0000000000..3de897b92b --- /dev/null +++ b/BizHawk.UnmanagedCore/emufile_hawk.h @@ -0,0 +1,65 @@ +#ifndef _EMUFILE_HAWK_H_ +#define _EMUFILE_HAWK_H_ + +#include "emufile.h" +#include "core.h" + +class EMUFILE_HAWK : public EMUFILE +{ + void* ManagedOpaque; + + + struct { + FP fgetc; + FP fread; + FP fwrite; + FP fseek; + FP ftell; + FP size; + FP dispose; + } _; + +public: + ~EMUFILE_HAWK() + { + _.dispose.func(); + } + EMUFILE_HAWK(void* _ManagedOpaque) + : ManagedOpaque(_ManagedOpaque) + { + } + + void* Construct(void* ManagedOpaque); + + void Delete() + { + delete this; + } + + void Set_fp(const char* param, void* value) + { + if(!strcmp(param,"fgetc")) _.fgetc.set(value); + if(!strcmp(param,"fread")) _.fread.set(value); + if(!strcmp(param,"fwrite")) _.fwrite.set(value); + if(!strcmp(param,"fseek")) _.fseek.set(value); + if(!strcmp(param,"ftell")) _.ftell.set(value); + if(!strcmp(param,"size")) _.size.set(value); + if(!strcmp(param,"dispose")) _.dispose.set(value); + } + + virtual int fgetc() { return _.fgetc.func(); } + virtual FILE *get_fp() { return NULL; } + virtual int fputc(int c) { return -1; } + virtual int fprintf(const char *format, ...); + virtual size_t _fread(const void *ptr, size_t bytes) { return _.fread.func(ptr,bytes); } + virtual void fwrite(const void *ptr, size_t bytes) { return _.fwrite.func(ptr,bytes); } + virtual int fseek(int offset, int origin) { return _.fseek.func(offset,origin); } + virtual int ftell() { return _.ftell.func(); } + virtual int size() { return _.size.func(); } + + + void* signal(const char* _param, void* value); +}; + + +#endif //_EMUFILE_HAWK_H_ diff --git a/BizHawk.UnmanagedCore/types.h b/BizHawk.UnmanagedCore/types.h new file mode 100644 index 0000000000..7862994f1c --- /dev/null +++ b/BizHawk.UnmanagedCore/types.h @@ -0,0 +1,8 @@ +typedef unsigned char u8; +typedef char s8; +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; +typedef unsigned __int64 u64; +typedef __int64 s64; \ No newline at end of file diff --git a/BizHawk.sln b/BizHawk.sln index 71b4354095..5f332330e1 100644 --- a/BizHawk.sln +++ b/BizHawk.sln @@ -6,31 +6,89 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Util", "BizHawk.Util\BizHawk.Util.csproj", "{EE135301-08B3-4EFC-A61C-1C53E1C65CB9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.MultiClient", "BizHawk.MultiClient\BizHawk.MultiClient.csproj", "{DD448B37-BA3F-4544-9754-5406E8094723}" + ProjectSection(ProjectDependencies) = postProject + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25} = {7B67D95B-29E0-4F9D-9767-42C35CEC3F25} + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoHawk", "DiscoHawk\DiscoHawk.csproj", "{C4366030-6D03-424B-AE53-F4F43BB217C3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PsxHawk.Core", "PsxHawk.Core\PsxHawk.Core.vcproj", "{7B67D95B-29E0-4F9D-9767-42C35CEC3F25}" + ProjectSection(ProjectDependencies) = postProject + {1CE74E20-B345-4126-AACB-A21FA23149DE} = {1CE74E20-B345-4126-AACB-A21FA23149DE} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BizHawk.UnmanagedCore", "BizHawk.UnmanagedCore\BizHawk.UnmanagedCore.vcproj", "{1CE74E20-B345-4126-AACB-A21FA23149DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Debug|Win32.ActiveCfg = Debug|Any CPU {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Release|Any CPU.Build.0 = Release|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA}.Release|Win32.ActiveCfg = Release|Any CPU {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Debug|Win32.ActiveCfg = Debug|Any CPU {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Release|Any CPU.Build.0 = Release|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EE135301-08B3-4EFC-A61C-1C53E1C65CB9}.Release|Win32.ActiveCfg = Release|Any CPU {DD448B37-BA3F-4544-9754-5406E8094723}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD448B37-BA3F-4544-9754-5406E8094723}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Debug|Win32.ActiveCfg = Debug|Any CPU {DD448B37-BA3F-4544-9754-5406E8094723}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD448B37-BA3F-4544-9754-5406E8094723}.Release|Any CPU.Build.0 = Release|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {DD448B37-BA3F-4544-9754-5406E8094723}.Release|Win32.ActiveCfg = Release|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Debug|Win32.ActiveCfg = Debug|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Any CPU.Build.0 = Release|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Win32.ActiveCfg = Release|Any CPU + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Debug|Win32.Build.0 = Debug|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Release|Any CPU.ActiveCfg = Release|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Release|Mixed Platforms.Build.0 = Release|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Release|Win32.ActiveCfg = Release|Win32 + {7B67D95B-29E0-4F9D-9767-42C35CEC3F25}.Release|Win32.Build.0 = Release|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Debug|Win32.ActiveCfg = Debug|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Debug|Win32.Build.0 = Debug|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Release|Any CPU.ActiveCfg = Release|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Release|Mixed Platforms.Build.0 = Release|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Release|Win32.ActiveCfg = Release|Win32 + {1CE74E20-B345-4126-AACB-A21FA23149DE}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PsxHawk.Core/PsxCore.cpp b/PsxHawk.Core/PsxCore.cpp new file mode 100644 index 0000000000..f0db85e28b --- /dev/null +++ b/PsxHawk.Core/PsxCore.cpp @@ -0,0 +1,40 @@ +#include "PsxCore.h" +#include "core.h" +#include +#include +#include "emufile_hawk.h" + + +static FunctionRecord records[] = { + FUNC("PsxCore.Construct", &PsxCore::Construct), + FUNC("PsxCore.GetResolution", &PsxCore::GetResolution), + FUNC("PsxCore.FrameAdvance", &PsxCore::FrameAdvance), + FUNC("PsxCore.UpdateVideoBuffer", &PsxCore::UpdateVideoBuffer) +}; + + +PsxCore::Size PsxCore::GetResolution() +{ + con->fprintf("in PsxCore::GetResolution\n"); + Size size = {256,256}; + return size; +} + +int videoBuffer[256*256]; + +void PsxCore::FrameAdvance() +{ + for(int i=0;i<256*256;i++) + { + videoBuffer[i] = rand() | (rand()<<15) | 0xFF000000; + } +} + +void PsxCore::UpdateVideoBuffer(void* target) +{ + int* dest = (int*)target; + int* src = (int*)videoBuffer; + for(int i=0;i<256*256;i++) + *dest++ = *src++; +} + diff --git a/PsxHawk.Core/PsxCore.h b/PsxHawk.Core/PsxCore.h new file mode 100644 index 0000000000..f67f77cd3c --- /dev/null +++ b/PsxHawk.Core/PsxCore.h @@ -0,0 +1,33 @@ +#ifndef _PSXCORE_H_ +#define _PSXCORE_H_ + +#include + +class PsxCore +{ +public: + PsxCore(void* _opaque) + : opaque(_opaque) + { + } + +private: + void* opaque; + +public: + void* Construct(void* ManagedOpaque) + { + return new PsxCore(ManagedOpaque); + } + + struct Size + { + int width,height; + }; + + Size GetResolution(); + void FrameAdvance(); + void UpdateVideoBuffer(void* target); +}; + +#endif //_PSXCORE_H_ diff --git a/PsxHawk.Core/PsxHawk.Core.vcproj b/PsxHawk.Core/PsxHawk.Core.vcproj new file mode 100644 index 0000000000..768f4e671e --- /dev/null +++ b/PsxHawk.Core/PsxHawk.Core.vcproj @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +