diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index ca3226a606..b1612eb4a1 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -103,6 +103,7 @@ namespace BizHawk.Client.Common public bool ShowContextMenu = true; public bool EnableBackupMovies = true; public bool MoviesOnDisk = false; + public bool MoviesInAWE = false; public bool HotkeyConfigAutoTab = true; public bool InputConfigAutoTab = true; public bool ShowLogWindow = false; diff --git a/BizHawk.Client.Common/movie/bk2/StringLogs.cs b/BizHawk.Client.Common/movie/bk2/StringLogs.cs index e284fadf8c..3125df524e 100644 --- a/BizHawk.Client.Common/movie/bk2/StringLogs.cs +++ b/BizHawk.Client.Common/movie/bk2/StringLogs.cs @@ -11,11 +11,14 @@ namespace BizHawk.Client.Common { public static class StringLogUtil { - public static bool DefaultToDisk; + public static bool DefaultToDisk; + public static bool DefaultToAWE; public static IStringLog MakeStringLog() { if (DefaultToDisk) - return new DiskStringLog(); + return new StreamStringLog(true); + else if(DefaultToAWE) + return new StreamStringLog(false); else return new ListStringLog(); } } @@ -54,25 +57,34 @@ namespace BizHawk.Client.Common /// It should be faster than those alternatives, but wasteful of disk space. /// It should also be easier to add new IList-like methods than dealing with a database /// - class DiskStringLog : IStringLog + class StreamStringLog : IStringLog { List Offsets = new List(); long cursor = 0; BinaryWriter bw; - BinaryReader br; + BinaryReader br; + bool mDisk; - FileStream stream; - public DiskStringLog() - { - var path = TempFileCleaner.GetTempFilename("movieOnDisk"); - stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose); + Stream stream; + public StreamStringLog(bool disk) + { + mDisk = disk; + if (disk) + { + var path = TempFileCleaner.GetTempFilename("movieOnDisk"); + stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose); + } + else + { + stream = new AWEMemoryStream(); + } bw = new BinaryWriter(stream); br = new BinaryReader(stream); } public IStringLog Clone() - { - DiskStringLog ret = new DiskStringLog(); + { + StreamStringLog ret = new StreamStringLog(mDisk); //doesnt necessarily make sense to copy the mDisk value, they could be designated for different targets... for (int i = 0; i < Count; i++) ret.Add(this[i]); return ret; @@ -142,7 +154,7 @@ namespace BizHawk.Client.Common class Enumerator : IEnumerator { - public DiskStringLog log; + public StreamStringLog log; int index = -1; public string Current { get { return log[index]; } } object System.Collections.IEnumerator.Current { get { return log[index]; } } diff --git a/BizHawk.Client.EmuHawk/Program.cs b/BizHawk.Client.EmuHawk/Program.cs index 3160d4e98b..25760e4c0c 100644 --- a/BizHawk.Client.EmuHawk/Program.cs +++ b/BizHawk.Client.EmuHawk/Program.cs @@ -111,6 +111,7 @@ namespace BizHawk.Client.EmuHawk Global.Config = ConfigService.Load(iniPath); Global.Config.ResolveDefaults(); BizHawk.Client.Common.StringLogUtil.DefaultToDisk = Global.Config.MoviesOnDisk; + BizHawk.Client.Common.StringLogUtil.DefaultToAWE = Global.Config.MoviesInAWE; //super hacky! this needs to be done first. still not worth the trouble to make this system fully proper for (int i = 0; i < args.Length; i++) diff --git a/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs b/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs index 2306c006eb..bfe750f909 100644 --- a/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs @@ -48,6 +48,10 @@ this.EnableContextMenuCheckbox = new System.Windows.Forms.CheckBox(); this.PauseWhenMenuActivatedCheckbox = new System.Windows.Forms.CheckBox(); this.tabPage3 = new System.Windows.Forms.TabPage(); + this.label6 = new System.Windows.Forms.Label(); + this.cbMoviesInAWE = new System.Windows.Forms.CheckBox(); + this.label5 = new System.Windows.Forms.Label(); + this.cbMoviesOnDisk = new System.Windows.Forms.CheckBox(); this.LuaDuringTurboCheckbox = new System.Windows.Forms.CheckBox(); this.label12 = new System.Windows.Forms.Label(); this.label13 = new System.Windows.Forms.Label(); @@ -58,8 +62,6 @@ this.label4 = new System.Windows.Forms.Label(); this.LogWindowAsConsoleCheckbox = new System.Windows.Forms.CheckBox(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.cbMoviesOnDisk = new System.Windows.Forms.CheckBox(); - this.label5 = new System.Windows.Forms.Label(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); this.groupBox1.SuspendLayout(); @@ -69,11 +71,11 @@ // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(280, 367); + this.OkBtn.Location = new System.Drawing.Point(280, 372); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 0; - this.OkBtn.Text = "&Ok"; + this.OkBtn.Text = "&OK"; this.OkBtn.UseVisualStyleBackColor = true; this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); // @@ -81,7 +83,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(346, 367); + this.CancelBtn.Location = new System.Drawing.Point(346, 372); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 1; @@ -263,6 +265,8 @@ // // tabPage3 // + this.tabPage3.Controls.Add(this.label6); + this.tabPage3.Controls.Add(this.cbMoviesInAWE); this.tabPage3.Controls.Add(this.label5); this.tabPage3.Controls.Add(this.cbMoviesOnDisk); this.tabPage3.Controls.Add(this.LuaDuringTurboCheckbox); @@ -281,6 +285,47 @@ this.tabPage3.Text = "Advanced"; this.tabPage3.UseVisualStyleBackColor = true; // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(27, 270); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(296, 39); + this.label6.TabIndex = 19; + this.label6.Text = "Will reduce many Out Of Memory crashes during long movies.\r\nThis is experimental;" + + " it may require admin permissions.\r\nYou must restart the program after changing " + + "this."; + // + // cbMoviesInAWE + // + this.cbMoviesInAWE.AutoSize = true; + this.cbMoviesInAWE.Location = new System.Drawing.Point(6, 250); + this.cbMoviesInAWE.Name = "cbMoviesInAWE"; + this.cbMoviesInAWE.Size = new System.Drawing.Size(262, 17); + this.cbMoviesInAWE.TabIndex = 18; + this.cbMoviesInAWE.Text = "Store movie working data in extended > 1GB Ram"; + this.cbMoviesInAWE.UseVisualStyleBackColor = true; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(27, 221); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(299, 26); + this.label5.TabIndex = 17; + this.label5.Text = "Will prevent many Out Of Memory crashes during long movies.\r\nYou must restart the" + + " program after changing this."; + // + // cbMoviesOnDisk + // + this.cbMoviesOnDisk.AutoSize = true; + this.cbMoviesOnDisk.Location = new System.Drawing.Point(6, 201); + this.cbMoviesOnDisk.Name = "cbMoviesOnDisk"; + this.cbMoviesOnDisk.Size = new System.Drawing.Size(259, 17); + this.cbMoviesOnDisk.TabIndex = 16; + this.cbMoviesOnDisk.Text = "Store movie working data on disk instead of RAM"; + this.cbMoviesOnDisk.UseVisualStyleBackColor = true; + // // LuaDuringTurboCheckbox // this.LuaDuringTurboCheckbox.AutoSize = true; @@ -365,26 +410,6 @@ this.LogWindowAsConsoleCheckbox.TabIndex = 1; this.LogWindowAsConsoleCheckbox.Text = "Create the log window as a console window"; this.LogWindowAsConsoleCheckbox.UseVisualStyleBackColor = true; - // - // cbMoviesOnDisk - // - this.cbMoviesOnDisk.AutoSize = true; - this.cbMoviesOnDisk.Location = new System.Drawing.Point(6, 206); - this.cbMoviesOnDisk.Name = "cbMoviesOnDisk"; - this.cbMoviesOnDisk.Size = new System.Drawing.Size(259, 17); - this.cbMoviesOnDisk.TabIndex = 16; - this.cbMoviesOnDisk.Text = "Store movie working data on disk instead of RAM"; - this.cbMoviesOnDisk.UseVisualStyleBackColor = true; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(27, 226); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(299, 26); - this.label5.TabIndex = 17; - this.label5.Text = "Will prevent many Out Of Memory crashes during long movies.\r\nYou should probably " + - "restart the program after changing this."; // // EmuHawkOptions // @@ -399,7 +424,7 @@ this.Name = "EmuHawkOptions"; this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Gui Options"; + this.Text = "Customization Options"; this.Load += new System.EventHandler(this.GuiOptions_Load); this.tabControl1.ResumeLayout(false); this.tabPage1.ResumeLayout(false); @@ -445,5 +470,7 @@ private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.Label label5; private System.Windows.Forms.CheckBox cbMoviesOnDisk; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.CheckBox cbMoviesInAWE; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/GuiOptions.cs b/BizHawk.Client.EmuHawk/config/GuiOptions.cs index 4efe8cd154..e53d5deccf 100644 --- a/BizHawk.Client.EmuHawk/config/GuiOptions.cs +++ b/BizHawk.Client.EmuHawk/config/GuiOptions.cs @@ -35,6 +35,7 @@ namespace BizHawk.Client.EmuHawk LogWindowAsConsoleCheckbox.Checked = Global.Config.WIN32_CONSOLE; LuaDuringTurboCheckbox.Checked = Global.Config.RunLuaDuringTurbo; cbMoviesOnDisk.Checked = Global.Config.MoviesOnDisk; + cbMoviesInAWE.Checked = Global.Config.MoviesInAWE; if (LogConsole.ConsoleVisible) { @@ -62,6 +63,7 @@ namespace BizHawk.Client.EmuHawk Global.Config.WIN32_CONSOLE = LogWindowAsConsoleCheckbox.Checked; Global.Config.RunLuaDuringTurbo = LuaDuringTurboCheckbox.Checked; Global.Config.MoviesOnDisk = cbMoviesOnDisk.Checked; + Global.Config.MoviesInAWE = cbMoviesInAWE.Checked; Close(); DialogResult = DialogResult.OK; diff --git a/BizHawk.Common/AWEMemoryStream.cs b/BizHawk.Common/AWEMemoryStream.cs new file mode 100644 index 0000000000..1edad4d51f --- /dev/null +++ b/BizHawk.Common/AWEMemoryStream.cs @@ -0,0 +1,464 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace BizHawk.Common +{ + /// + /// A MemoryStream that uses AWE to achieve a large addressable area, without being subject to 32 bit address space limits. + /// + public class AWEMemoryStream : Stream + { + const int kBlockSizeBits = 20; + const int kBlockSize = 1 << 20; + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); + + [Flags] + enum MemoryProtection : uint + { + EXECUTE = 0x10, + EXECUTE_READ = 0x20, + EXECUTE_READWRITE = 0x40, + EXECUTE_WRITECOPY = 0x80, + NOACCESS = 0x01, + READONLY = 0x02, + READWRITE = 0x04, + WRITECOPY = 0x08, + GUARD_Modifierflag = 0x100, + NOCACHE_Modifierflag = 0x200, + WRITECOMBINE_Modifierflag = 0x400 + } + + [Flags] + enum AllocationType : uint + { + COMMIT = 0x1000, + RESERVE = 0x2000, + RESET = 0x80000, + LARGE_PAGES = 0x20000000, + PHYSICAL = 0x400000, + TOP_DOWN = 0x100000, + WRITE_WATCH = 0x200000 + } + + public AWEMemoryStream() + { + //bootstrap the datastructures + Position = 0; + + //allocate the window (address space that we'll allocate physical pages into) + mWindow = VirtualAlloc(IntPtr.Zero, new IntPtr(kBlockSize), AllocationType.RESERVE | AllocationType.PHYSICAL, MemoryProtection.READWRITE); + } + + protected override void Dispose(bool disposing) + { + if (mWindow != IntPtr.Zero) + { + VirtualFree(mWindow, IntPtr.Zero, 0x8000U); //MEM_RELEASE + mWindow = IntPtr.Zero; + } + + if (disposing) + { + foreach (var block in mBlocks) + block.Dispose(); + } + } + + ~AWEMemoryStream() + { + Dispose(false); + } + + long mLength = 0, mPosition = -1; + long mCurrBlock = -1; + List mBlocks = new List(); + IntPtr mWindow; + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return true; } } + public override bool CanWrite { get { return true; } } + public override void Flush() { } + public override long Length { get { return mLength; } } + public override long Position + { + get + { + return mPosition; + } + set + { + if (!Ensure(value + 1)) + throw new OutOfMemoryException("Couldn't set AWEMemoryStream to specified Position"); + mPosition = value; + } + } + + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: Position = offset; return Position; + case SeekOrigin.Current: Position += offset; return Position; + case SeekOrigin.End: Position = Length + offset; return Position; + default: throw new InvalidOperationException(); + } + } + + bool Ensure(long len) + { + long blocksNeeded = (len + kBlockSize - 1) >> kBlockSizeBits; + while (mBlocks.Count < blocksNeeded) + { + var block = new AWEMemoryBlock(); + if (!block.Allocate(kBlockSize)) + return false; + mBlocks.Add(block); + } + return true; + } + + public override void SetLength(long value) + { + if (!Ensure(value)) + throw new OutOfMemoryException("Couldn't set AWEMemoryStream to specified Length"); + mLength = value; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count + mPosition > mLength) + count = (int)(mLength - mPosition); + int ocount = count; + while (count > 0) + { + int todo = count; + long lblock = mPosition >> kBlockSizeBits; + if (lblock > int.MaxValue) throw new ArgumentOutOfRangeException(); + int block = (int)lblock; + int blockOfs = (int)(mPosition - (block << kBlockSizeBits)); + int remainsInBlock = kBlockSize - blockOfs; + if (remainsInBlock < todo) + todo = remainsInBlock; + if (mCurrBlock != block) + { + mCurrBlock = block; + if (!mBlocks[block].Map(mWindow)) + throw new Exception("Couldn't map required memory for AWEMemoryStream.Write"); + } + Marshal.Copy(IntPtr.Add(mWindow, blockOfs), buffer, offset, todo); + count -= todo; + mPosition += todo; + offset += todo; + } + return ocount - count; + } + + public override void Write(byte[] buffer, int offset, int count) + { + long end = mPosition + count; + if (!Ensure(end)) + throw new OutOfMemoryException("Couldn't reserve required resources for AWEMemoryStream.Write"); + SetLength(end); + while (count > 0) + { + int todo = count; + long lblock = mPosition >> kBlockSizeBits; + if (lblock > int.MaxValue) throw new ArgumentOutOfRangeException(); + int block = (int)lblock; + int blockOfs = (int)(mPosition - (block << kBlockSizeBits)); + int remainsInBlock = kBlockSize - blockOfs; + if (remainsInBlock < todo) + todo = remainsInBlock; + if (mCurrBlock != block) + { + mCurrBlock = block; + if (!mBlocks[block].Map(mWindow)) + throw new Exception("Couldn't map required memory for AWEMemoryStream.Write"); + } + Marshal.Copy(buffer, offset, IntPtr.Add(mWindow, blockOfs), todo); + count -= todo; + mPosition += todo; + offset += todo; + } + } + + unsafe class AWEMemoryBlock : IDisposable + { + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool AllocateUserPhysicalPages(IntPtr hProcess, ref uint NumberOfPages, IntPtr PageArray); + + [DllImport("kernel32.dll")] + static extern bool MapUserPhysicalPages(IntPtr lpAddress, uint NumberOfPages, IntPtr UserPfnArray); + + [DllImport("kernel32.dll")] + static extern bool FreeUserPhysicalPages(IntPtr hProcess, ref uint NumberOfPages, IntPtr UserPfnArray); + + public enum ProcessorArchitecture + { + X86 = 0, + X64 = 9, + Arm = -1, + Itanium = 6, + Unknown = 0xFFFF, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemInfo + { + public ProcessorArchitecture ProcessorArchitecture; // WORD + public uint PageSize; // DWORD + public IntPtr MinimumApplicationAddress; // (long)void* + public IntPtr MaximumApplicationAddress; // (long)void* + public IntPtr ActiveProcessorMask; // DWORD* + public uint NumberOfProcessors; // DWORD (WTF) + public uint ProcessorType; // DWORD + public uint AllocationGranularity; // DWORD + public ushort ProcessorLevel; // WORD + public ushort ProcessorRevision; // WORD + } + + [DllImport("kernel32", SetLastError = true)] + public static extern void GetSystemInfo(out SystemInfo lpSystemInfo); + + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + private uint lp; + private int hp; + + public uint LowPart + { + get { return lp; } + set { lp = value; } + } + + public int HighPart + { + get { return hp; } + set { hp = value; } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct LUID_AND_ATTRIBUTES + { + private LUID luid; + private uint attributes; + + public LUID LUID + { + get { return luid; } + set { luid = value; } + } + + public uint Attributes + { + get { return attributes; } + set { attributes = value; } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_PRIVILEGES + { + private uint prvct; + [MarshalAs(UnmanagedType.ByValArray)] //edited from stackoverflow article + private LUID_AND_ATTRIBUTES[] privileges; + + public uint PrivilegeCount + { + get { return prvct; } + set { prvct = value; } + } + + public LUID_AND_ATTRIBUTES[] Privileges + { + get { return privileges; } + set { privileges = value; } + } + } + + [DllImport("advapi32", SetLastError = true)] + public static extern bool OpenProcessToken(IntPtr ProcessHandle, TokenAccessLevels DesiredAccess, out IntPtr TokenHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, uint BufferLength, out TOKEN_PRIVILEGES PreviousState, out uint ReturnLength); + + [Flags] + internal enum TokenAccessLevels + { + AssignPrimary = 0x00000001, + Duplicate = 0x00000002, + Impersonate = 0x00000004, + Query = 0x00000008, + QuerySource = 0x00000010, + AdjustPrivileges = 0x00000020, + AdjustGroups = 0x00000040, + AdjustDefault = 0x00000080, + AdjustSessionId = 0x00000100, + + Read = 0x00020000 | Query, + + Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault, + + AllAccess = 0x000F0000 | + AssignPrimary | + Duplicate | + Impersonate | + Query | + QuerySource | + AdjustPrivileges | + AdjustGroups | + AdjustDefault | + AdjustSessionId, + + MaximumAllowed = 0x02000000 + } + + //http://stackoverflow.com/questions/13616330/c-sharp-adjusttokenprivileges-not-working-on-32bit + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); + public static bool EnableDisablePrivilege(string PrivilegeName, bool EnableDisable) + { + var htok = IntPtr.Zero; + if (!OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query, out htok)) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + return false; + } + var tkp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new LUID_AND_ATTRIBUTES[1] }; + LUID luid; + if (!LookupPrivilegeValue(null, PrivilegeName, out luid)) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + return false; + } + tkp.Privileges[0].LUID = luid; + tkp.Privileges[0].Attributes = (uint)(EnableDisable ? 2 : 0); + TOKEN_PRIVILEGES prv; + uint rb; + if (!AdjustTokenPrivileges(htok, false, ref tkp, 256, out prv, out rb)) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + return false; + } + + return true; + } + + static AWEMemoryBlock() + { + var si = new SystemInfo(); + GetSystemInfo(out si); + PageSize = si.PageSize; + } + + static uint PageSize; + static bool PrivilegeAcquired; + static object StaticLock = new object(); + + byte[] pageList; + + static bool TryAcquirePrivilege() + { + lock (StaticLock) + { + if (PrivilegeAcquired) + return true; + if (EnableDisablePrivilege("SeLockMemoryPrivilege", true)) + { + PrivilegeAcquired = true; + return true; + } + else return false; + } + } + + public bool Allocate(int byteSize) + { + if (!TryAcquirePrivilege()) + return false; + + long lnPagesRequested = byteSize / PageSize; + if (lnPagesRequested > uint.MaxValue) + throw new InvalidOperationException(); + uint nPagesRequested = (uint)lnPagesRequested; + + long sizePageList = IntPtr.Size * nPagesRequested; + pageList = new byte[sizePageList]; + + fixed (byte* pPageList = &pageList[0]) + { + uint nPagesAllocated = nPagesRequested; + bool bResult = AllocateUserPhysicalPages(System.Diagnostics.Process.GetCurrentProcess().Handle, ref nPagesAllocated, new IntPtr(pPageList)); + if (nPagesRequested != nPagesAllocated) + { + //abort! we're probably about to bomb the process, but just in case, we'll clean up + FreeUserPhysicalPages(System.Diagnostics.Process.GetCurrentProcess().Handle, ref nPagesAllocated, new IntPtr(pPageList)); + pageList = null; + return false; + } + } + + return true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public bool Map(IntPtr targetWindow) + { + //note: unmapping previous mapping seems unnecessary + + if (pageList == null) + return false; + + //map the desired physical pages + fixed (byte* pPageList = &pageList[0]) + { + bool bResult = MapUserPhysicalPages(targetWindow, NumPages, new IntPtr(pPageList)); + return bResult; + } + } + + uint NumPages + { + get + { + return (uint)(pageList.Length / (uint)IntPtr.Size); + } + } + + protected virtual void Dispose(bool disposing) + { + if (pageList == null) + return; + + fixed (byte* pPageList = &pageList[0]) + { + uint nPagesRequested = NumPages; + FreeUserPhysicalPages(System.Diagnostics.Process.GetCurrentProcess().Handle, ref nPagesRequested, new IntPtr(pPageList)); + pageList = null; + } + } + + ~AWEMemoryBlock() + { + Dispose(false); + } + + } + } +} \ No newline at end of file diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj index 2fe7d6586c..cd2662bed5 100644 --- a/BizHawk.Common/BizHawk.Common.csproj +++ b/BizHawk.Common/BizHawk.Common.csproj @@ -50,6 +50,7 @@ VersionInfo.cs +