organize and cleanup a ton of win32 imports, do cleanup elsewhere with that
This commit is contained in:
parent
b4a6b06fba
commit
686119c7dd
|
@ -9,7 +9,7 @@ using BizHawk.Common;
|
|||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
using static BizHawk.Common.RawInputImports;
|
||||
using static BizHawk.Common.Win32Imports;
|
||||
using static BizHawk.Common.WmImports;
|
||||
|
||||
using RawKey = Vortice.DirectInput.Key;
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace BizHawk.Bizware.Input
|
|||
{
|
||||
var wc = default(WNDCLASS);
|
||||
wc.lpfnWndProc = _wndProc;
|
||||
wc.hInstance = GetModuleHandle(null);
|
||||
wc.hInstance = Win32Imports.GetModuleHandle(null);
|
||||
wc.lpszClassName = "RawKeyInputClass";
|
||||
|
||||
var atom = RegisterClass(ref wc);
|
||||
|
@ -125,7 +125,7 @@ namespace BizHawk.Bizware.Input
|
|||
nHeight: 1,
|
||||
hWndParent: HWND_MESSAGE,
|
||||
hMenu: IntPtr.Zero,
|
||||
hInstance: GetModuleHandle(null),
|
||||
hInstance: Win32Imports.GetModuleHandle(null),
|
||||
lpParam: IntPtr.Zero);
|
||||
|
||||
if (window == IntPtr.Zero)
|
||||
|
|
|
@ -46,10 +46,10 @@ namespace BizHawk.Bizware.Input
|
|||
// similar code shouldn't be needed on other platforms (which have global message queues and not thread local message queues)
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
while (Win32Imports.PeekMessage(out var msg, IntPtr.Zero, 0, 0, Win32Imports.PM_REMOVE))
|
||||
while (WmImports.PeekMessage(out var msg, IntPtr.Zero, 0, 0, WmImports.PM_REMOVE))
|
||||
{
|
||||
Win32Imports.TranslateMessage(ref msg);
|
||||
Win32Imports.DispatchMessage(ref msg);
|
||||
WmImports.TranslateMessage(ref msg);
|
||||
WmImports.DispatchMessage(ref msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ using BizHawk.Common;
|
|||
using BizHawk.Common.PathExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
using static BizHawk.Common.HeapApiImports;
|
||||
|
||||
// some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
@ -19,7 +21,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
"Uses the Microsoft AVIFIL32 system to write .avi files. Audio is uncompressed; Video can be compressed with any installed VCM codec. Splits on 2G and resolution change.")]
|
||||
internal class AviWriter : IVideoWriter
|
||||
{
|
||||
private CodecToken _currVideoCodecToken = null;
|
||||
private CodecToken _currVideoCodecToken;
|
||||
private AviWriterSegment _currSegment;
|
||||
|
||||
private readonly IDialogParent _dialogParent;
|
||||
|
@ -35,9 +37,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private bool IsOpen => _nameProvider != null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_currSegment?.Dispose();
|
||||
}
|
||||
=> _currSegment?.Dispose();
|
||||
|
||||
/// <summary>sets the codec token to be used for video compression</summary>
|
||||
/// <exception cref="ArgumentException"><paramref name="token"/> does not inherit <see cref="CodecToken"/></exception>
|
||||
|
@ -57,12 +57,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
var (dir, baseName, ext) = template.SplitPathToDirFileAndExt();
|
||||
yield return template;
|
||||
int counter = 1;
|
||||
for (;;)
|
||||
var counter = 1;
|
||||
while (counter < int.MaxValue)
|
||||
{
|
||||
yield return Path.Combine(dir, $"{baseName}_{counter}{ext}");
|
||||
counter++;
|
||||
}
|
||||
|
||||
yield return Path.Combine(dir, $"{baseName}_{counter}{ext}");
|
||||
|
||||
throw new InvalidOperationException("Reached maximum names");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -70,9 +74,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// set a video codec token first.
|
||||
/// </summary>
|
||||
public void OpenFile(string baseName)
|
||||
{
|
||||
OpenFile(CreateBasicNameProvider(baseName));
|
||||
}
|
||||
=> OpenFile(CreateBasicNameProvider(baseName));
|
||||
|
||||
// thread communication
|
||||
// synchronized queue with custom messages
|
||||
|
@ -86,19 +88,18 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
object o = _threadQ.Take();
|
||||
if (o is IVideoProvider provider)
|
||||
var o = _threadQ.Take();
|
||||
switch (o)
|
||||
{
|
||||
AddFrameEx(provider);
|
||||
}
|
||||
else if (o is short[] arr)
|
||||
{
|
||||
AddSamplesEx(arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// anything else is assumed to be quit time
|
||||
return;
|
||||
case IVideoProvider provider:
|
||||
AddFrameEx(provider);
|
||||
break;
|
||||
case short[] arr:
|
||||
AddSamplesEx(arr);
|
||||
break;
|
||||
default:
|
||||
// anything else is assumed to be quit time
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +122,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
public int BackgroundColor { get; }
|
||||
public int VsyncNumerator { get; }
|
||||
public int VsyncDenominator { get; }
|
||||
|
||||
public VideoCopy(IVideoProvider c)
|
||||
{
|
||||
_vb = (int[])c.GetVideoBuffer().Clone();
|
||||
|
@ -134,9 +136,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return _vb;
|
||||
}
|
||||
=> _vb;
|
||||
}
|
||||
|
||||
/// <summary>opens an avi file for recording, with <paramref name="nameProvider"/> being used to name files</summary>
|
||||
|
@ -149,14 +149,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
throw new InvalidOperationException("Tried to start recording an AVI with no video codec token set");
|
||||
}
|
||||
|
||||
_threadQ = new System.Collections.Concurrent.BlockingCollection<object>(30);
|
||||
_workerT = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));
|
||||
_threadQ = new(30);
|
||||
_workerT = new(ThreadProc) { IsBackground = true };
|
||||
_workerT.Start();
|
||||
}
|
||||
|
||||
public void CloseFile()
|
||||
{
|
||||
_threadQ.Add(new object()); // acts as stop message
|
||||
_threadQ.Add(new()); // acts as stop message
|
||||
_workerT.Join();
|
||||
_currSegment?.Dispose();
|
||||
_currSegment = null;
|
||||
|
@ -169,7 +169,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if (!_workerT.IsAlive)
|
||||
{
|
||||
throw new Exception("AVI Worker thread died!");
|
||||
throw new("AVI Worker thread died!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
Segment();
|
||||
}
|
||||
|
||||
_currSegment.AddFrame(source);
|
||||
_currSegment!.AddFrame(source);
|
||||
}
|
||||
|
||||
/// <exception cref="Exception">worker thrread died</exception>
|
||||
|
@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if (!_workerT.IsAlive)
|
||||
{
|
||||
throw new Exception("AVI Worker thread died!");
|
||||
throw new("AVI Worker thread died!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
Segment();
|
||||
}
|
||||
|
||||
_currSegment.AddSamples(samples);
|
||||
_currSegment!.AddSamples(samples);
|
||||
}
|
||||
|
||||
private void ConsiderLengthSegment()
|
||||
|
@ -218,7 +218,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
return;
|
||||
}
|
||||
|
||||
long len = _currSegment.GetLengthApproximation();
|
||||
var len = _currSegment.GetLengthApproximation();
|
||||
const long segment_length_limit = 2 * 1000 * 1000 * 1000; // 2GB
|
||||
|
||||
// const long segment_length_limit = 10 * 1000 * 1000; //for testing
|
||||
|
@ -228,11 +228,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
private void StartRecording()
|
||||
{
|
||||
// i guess theres nothing to do here
|
||||
}
|
||||
|
||||
private void Segment()
|
||||
{
|
||||
if (!IsOpen)
|
||||
|
@ -240,18 +235,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
return;
|
||||
}
|
||||
|
||||
if (_currSegment == null)
|
||||
{
|
||||
StartRecording();
|
||||
}
|
||||
else
|
||||
{
|
||||
_currSegment.Dispose();
|
||||
}
|
||||
_currSegment?.Dispose();
|
||||
_currSegment = new();
|
||||
|
||||
_currSegment = new AviWriterSegment();
|
||||
_nameProvider.MoveNext();
|
||||
_currSegment.OpenFile(_nameProvider.Current, _parameters, _currVideoCodecToken);
|
||||
|
||||
try
|
||||
{
|
||||
_currSegment.OpenStreams();
|
||||
|
@ -278,17 +267,25 @@ namespace BizHawk.Client.EmuHawk
|
|||
a_samplerate = 44100,
|
||||
a_channels = 2
|
||||
};
|
||||
var temp = new AviWriterSegment();
|
||||
string tempfile = Path.GetTempFileName();
|
||||
File.Delete(tempfile);
|
||||
tempfile = Path.ChangeExtension(tempfile, "avi");
|
||||
temp.OpenFile(tempfile, tempParams, null);
|
||||
var ret = temp.AcquireVideoCodecToken(_dialogParent.AsWinFormsHandle().Handle, _currVideoCodecToken);
|
||||
CodecToken token = (CodecToken)ret;
|
||||
config.AviCodecToken = token?.Serialize();
|
||||
temp.CloseFile();
|
||||
File.Delete(tempfile);
|
||||
return token;
|
||||
|
||||
var tempSegment = new AviWriterSegment();
|
||||
var tempFile = Path.GetTempFileName();
|
||||
File.Delete(tempFile);
|
||||
tempFile = Path.ChangeExtension(tempFile, "avi");
|
||||
tempSegment.OpenFile(tempFile, tempParams, null);
|
||||
|
||||
try
|
||||
{
|
||||
var ret = tempSegment.AcquireVideoCodecToken(_dialogParent.AsWinFormsHandle().Handle, _currVideoCodecToken);
|
||||
var token = (CodecToken)ret;
|
||||
config.AviCodecToken = token?.Serialize();
|
||||
return token;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempSegment.CloseFile();
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
private class Parameters
|
||||
|
@ -329,11 +326,18 @@ namespace BizHawk.Client.EmuHawk
|
|||
public void PopulateWAVEFORMATEX(ref AVIWriterImports.WAVEFORMATEX wfex)
|
||||
{
|
||||
const int WAVE_FORMAT_PCM = 1;
|
||||
int bytes = 0;
|
||||
if (a_bits == 16) bytes = 2;
|
||||
else if (a_bits == 8) bytes = 1;
|
||||
else throw new InvalidOperationException($"only 8/16 bits audio are supported by {nameof(AviWriter)} and you chose: {a_bits}");
|
||||
if (a_channels is not (1 or 2)) throw new InvalidOperationException($"only 1/2 channels audio are supported by {nameof(AviWriter)} and you chose: {a_channels}");
|
||||
var bytes = a_bits switch
|
||||
{
|
||||
16 => 2,
|
||||
8 => 1,
|
||||
_ => throw new InvalidOperationException($"only 8/16 bits audio are supported by {nameof(AviWriter)} and you chose: {a_bits}")
|
||||
};
|
||||
|
||||
if (a_channels is not (1 or 2))
|
||||
{
|
||||
throw new InvalidOperationException($"only 1/2 channels audio are supported by {nameof(AviWriter)} and you chose: {a_channels}");
|
||||
}
|
||||
|
||||
wfex.Init();
|
||||
wfex.nBlockAlign = (ushort)(bytes * a_channels);
|
||||
wfex.nChannels = (ushort)a_channels;
|
||||
|
@ -373,12 +377,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// </summary>
|
||||
public void SetVideoParameters(int width, int height)
|
||||
{
|
||||
bool change = false;
|
||||
var change = _parameters.width != width ||
|
||||
_parameters.height != height;
|
||||
|
||||
change |= _parameters.width != width;
|
||||
_parameters.width = width;
|
||||
|
||||
change |= _parameters.height != height;
|
||||
_parameters.height = height;
|
||||
|
||||
if (change)
|
||||
|
@ -392,18 +394,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// </summary>
|
||||
public void SetAudioParameters(int sampleRate, int channels, int bits)
|
||||
{
|
||||
bool change = false;
|
||||
var change = _parameters.a_samplerate != sampleRate ||
|
||||
_parameters.a_channels != channels ||
|
||||
_parameters.a_bits != bits ||
|
||||
_parameters.has_audio != true;
|
||||
|
||||
change |= _parameters.a_samplerate != sampleRate;
|
||||
_parameters.a_samplerate = sampleRate;
|
||||
|
||||
change |= _parameters.a_channels != channels;
|
||||
_parameters.a_channels = channels;
|
||||
|
||||
change |= _parameters.a_bits != bits;
|
||||
_parameters.a_bits = bits;
|
||||
|
||||
change |= _parameters.has_audio != true;
|
||||
_parameters.has_audio = true;
|
||||
|
||||
if (change)
|
||||
|
@ -414,24 +412,31 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public class CodecToken : IDisposable
|
||||
{
|
||||
public void Dispose() { }
|
||||
private CodecToken() { }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private CodecToken()
|
||||
{
|
||||
}
|
||||
|
||||
private AVIWriterImports.AVICOMPRESSOPTIONS _comprOptions;
|
||||
public string codec;
|
||||
public byte[] Format = Array.Empty<byte>();
|
||||
public byte[] Parms = Array.Empty<byte>();
|
||||
|
||||
private static string Decode_mmioFOURCC(int code)
|
||||
private static unsafe string Decode_mmioFOURCC(int code)
|
||||
{
|
||||
char[] chs = new char[4];
|
||||
var chs = stackalloc char[4];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
|
||||
if (!char.IsLetterOrDigit(chs[i]))
|
||||
chs[i] = ' ';
|
||||
}
|
||||
return new string(chs);
|
||||
|
||||
return new(chs, 0, 4);
|
||||
}
|
||||
|
||||
public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts)
|
||||
|
@ -461,27 +466,36 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
#endif
|
||||
#if false // test: increase stability by never freeing anything, ever
|
||||
if (opts.lpParms != IntPtr.Zero) Win32Imports.HeapFree(Win32Imports.GetProcessHeap(), 0, opts.lpParms);
|
||||
if (opts.lpFormat != IntPtr.Zero) Win32Imports.HeapFree(Win32Imports.GetProcessHeap(), 0, opts.lpFormat);
|
||||
if (opts.lpParms != IntPtr.Zero)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, opts.lpParms);
|
||||
}
|
||||
|
||||
if (opts.lpFormat != IntPtr.Zero)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, opts.lpFormat);
|
||||
}
|
||||
#endif
|
||||
#if AVI_SUPPORT
|
||||
opts.lpParms = IntPtr.Zero;
|
||||
opts.lpFormat = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void AllocateToAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts)
|
||||
public void AllocateToAVICOMPRESSOPTIONS(out AVIWriterImports.AVICOMPRESSOPTIONS opts)
|
||||
{
|
||||
if (_comprOptions.cbParms != 0)
|
||||
{
|
||||
_comprOptions.lpParms = HeapAlloc(GetProcessHeap(), 0, _comprOptions.cbParms);
|
||||
Marshal.Copy(Parms, 0, _comprOptions.lpParms, _comprOptions.cbParms);
|
||||
}
|
||||
|
||||
if (_comprOptions.cbFormat != 0)
|
||||
{
|
||||
_comprOptions.lpFormat = HeapAlloc(GetProcessHeap(), 0, _comprOptions.cbFormat);
|
||||
Marshal.Copy(Format, 0, _comprOptions.lpFormat, _comprOptions.cbFormat);
|
||||
}
|
||||
|
||||
opts = _comprOptions;
|
||||
if (opts.cbParms != 0)
|
||||
{
|
||||
opts.lpParms = Win32Imports.HeapAlloc(Win32Imports.GetProcessHeap(), 0, opts.cbParms);
|
||||
Marshal.Copy(Parms, 0, opts.lpParms, opts.cbParms);
|
||||
}
|
||||
if (opts.cbFormat != 0)
|
||||
{
|
||||
opts.lpFormat = Win32Imports.HeapAlloc(Win32Imports.GetProcessHeap(), 0, opts.cbFormat);
|
||||
Marshal.Copy(Format, 0, opts.lpFormat, opts.cbFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] SerializeToByteArray()
|
||||
|
@ -511,7 +525,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
var m = new MemoryStream(data, false);
|
||||
var b = new BinaryReader(m);
|
||||
|
||||
AVIWriterImports.AVICOMPRESSOPTIONS comprOptions = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
||||
var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS);
|
||||
|
||||
byte[] format;
|
||||
byte[] parms;
|
||||
|
@ -544,25 +558,20 @@ namespace BizHawk.Client.EmuHawk
|
|||
b.Close();
|
||||
}
|
||||
|
||||
var ret = new CodecToken
|
||||
return new()
|
||||
{
|
||||
_comprOptions = comprOptions,
|
||||
Format = format,
|
||||
Parms = parms,
|
||||
codec = Decode_mmioFOURCC(comprOptions.fccHandler)
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
return Convert.ToBase64String(SerializeToByteArray());
|
||||
}
|
||||
=> Convert.ToBase64String(SerializeToByteArray());
|
||||
|
||||
public static CodecToken DeSerialize(string s)
|
||||
{
|
||||
return DeSerializeFromByteArray(Convert.FromBase64String(s));
|
||||
}
|
||||
=> DeSerializeFromByteArray(Convert.FromBase64String(s));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -573,21 +582,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
}
|
||||
|
||||
private unsafe class AviWriterSegment : IDisposable
|
||||
private class AviWriterSegment : IDisposable
|
||||
{
|
||||
static AviWriterSegment()
|
||||
{
|
||||
AVIWriterImports.AVIFileInit();
|
||||
}
|
||||
|
||||
public AviWriterSegment()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CloseFile();
|
||||
}
|
||||
=> CloseFile();
|
||||
|
||||
private CodecToken _currVideoCodecToken;
|
||||
private bool _isOpen;
|
||||
|
@ -631,19 +634,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
private OutputStatus _outStatus;
|
||||
|
||||
public long GetLengthApproximation()
|
||||
{
|
||||
return _outStatus.video_bytes + _outStatus.audio_bytes;
|
||||
}
|
||||
=> _outStatus.video_bytes + _outStatus.audio_bytes;
|
||||
|
||||
private static bool FAILED(int hr) => hr < 0;
|
||||
|
||||
private static int AVISaveOptions(IntPtr stream, ref AVIWriterImports.AVICOMPRESSOPTIONS opts, IntPtr owner)
|
||||
private static unsafe int AVISaveOptions(IntPtr stream, ref AVIWriterImports.AVICOMPRESSOPTIONS opts, IntPtr owner)
|
||||
{
|
||||
fixed (AVIWriterImports.AVICOMPRESSOPTIONS* _popts = &opts)
|
||||
{
|
||||
IntPtr* pStream = &stream;
|
||||
AVIWriterImports.AVICOMPRESSOPTIONS* popts = _popts;
|
||||
AVIWriterImports.AVICOMPRESSOPTIONS** ppopts = &popts;
|
||||
var pStream = &stream;
|
||||
var popts = _popts;
|
||||
var ppopts = &popts;
|
||||
return AVIWriterImports.AVISaveOptions(owner, 0, 1, pStream, ppopts);
|
||||
}
|
||||
}
|
||||
|
@ -654,10 +653,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
public void OpenFile(string destPath, Parameters parameters, CodecToken videoCodecToken)
|
||||
{
|
||||
static int mmioFOURCC(string str) => (
|
||||
(byte)(str[0])
|
||||
| ((byte)(str[1]) << 8)
|
||||
| ((byte)(str[2]) << 16)
|
||||
| ((byte)(str[3]) << 24)
|
||||
(byte)str[0] |
|
||||
((byte)str[1] << 8) |
|
||||
((byte)str[2] << 16) |
|
||||
((byte)str[3] << 24)
|
||||
);
|
||||
|
||||
this._parameters = parameters;
|
||||
|
@ -671,28 +670,34 @@ namespace BizHawk.Client.EmuHawk
|
|||
File.Delete(destPath);
|
||||
}
|
||||
|
||||
if (FAILED(AVIWriterImports.AVIFileOpenW(ref _pAviFile, destPath, AVIWriterImports.OpenFileStyle.OF_CREATE | AVIWriterImports.OpenFileStyle.OF_WRITE, 0)))
|
||||
var hr = AVIWriterImports.AVIFileOpenW(ref _pAviFile, destPath,
|
||||
AVIWriterImports.OpenFileStyle.OF_CREATE | AVIWriterImports.OpenFileStyle.OF_WRITE, 0);
|
||||
var hrEx = Marshal.GetExceptionForHR(hr);
|
||||
if (hrEx != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Couldnt open dest path for avi file: {destPath}");
|
||||
throw new InvalidOperationException($"Couldnt open dest path for avi file: {destPath}", hrEx);
|
||||
}
|
||||
|
||||
// initialize the video stream
|
||||
AVIWriterImports.AVISTREAMINFOW vidstream_header = new AVIWriterImports.AVISTREAMINFOW();
|
||||
AVIWriterImports.BITMAPINFOHEADER bmih = new AVIWriterImports.BITMAPINFOHEADER();
|
||||
var vidstream_header = default(AVIWriterImports.AVISTREAMINFOW);
|
||||
var bmih = default(AVIWriterImports.BITMAPINFOHEADER);
|
||||
parameters.PopulateBITMAPINFOHEADER24(ref bmih);
|
||||
vidstream_header.fccType = mmioFOURCC("vids");
|
||||
vidstream_header.dwRate = parameters.fps;
|
||||
vidstream_header.dwScale = parameters.fps_scale;
|
||||
vidstream_header.dwSuggestedBufferSize = (int)bmih.biSizeImage;
|
||||
if (FAILED(AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawVideoStream, ref vidstream_header)))
|
||||
|
||||
hr = AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawVideoStream, ref vidstream_header);
|
||||
hrEx = Marshal.GetExceptionForHR(hr);
|
||||
if (hrEx != null)
|
||||
{
|
||||
CloseFile();
|
||||
throw new InvalidOperationException("Failed opening raw video stream. Not sure how this could happen");
|
||||
throw new InvalidOperationException("Failed opening raw video stream. Not sure how this could happen", hrEx);
|
||||
}
|
||||
|
||||
// initialize audio stream
|
||||
AVIWriterImports.AVISTREAMINFOW audstream_header = new AVIWriterImports.AVISTREAMINFOW();
|
||||
AVIWriterImports.WAVEFORMATEX wfex = new AVIWriterImports.WAVEFORMATEX();
|
||||
var audstream_header = default(AVIWriterImports.AVISTREAMINFOW);
|
||||
var wfex = default(AVIWriterImports.WAVEFORMATEX);
|
||||
parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||
audstream_header.fccType = mmioFOURCC("auds");
|
||||
audstream_header.dwQuality = -1;
|
||||
|
@ -700,13 +705,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
audstream_header.dwRate = (int)wfex.nAvgBytesPerSec;
|
||||
audstream_header.dwSampleSize = wfex.nBlockAlign;
|
||||
audstream_header.dwInitialFrames = 1; // ??? optimal value?
|
||||
if (FAILED(AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawAudioStream, ref audstream_header)))
|
||||
|
||||
hr = AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawAudioStream, ref audstream_header);
|
||||
hrEx = Marshal.GetExceptionForHR(hr);
|
||||
if (hrEx != null)
|
||||
{
|
||||
CloseFile();
|
||||
throw new InvalidOperationException("Failed opening raw audio stream. Not sure how this could happen");
|
||||
throw new InvalidOperationException("Failed opening raw audio stream. Not sure how this could happen", hrEx);
|
||||
}
|
||||
|
||||
_outStatus = new OutputStatus();
|
||||
_outStatus = new();
|
||||
_isOpen = true;
|
||||
}
|
||||
|
||||
|
@ -726,11 +734,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
// encoder params
|
||||
AVIWriterImports.AVICOMPRESSOPTIONS comprOptions = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
||||
_currVideoCodecToken?.AllocateToAVICOMPRESSOPTIONS(ref comprOptions);
|
||||
var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS);
|
||||
_currVideoCodecToken?.AllocateToAVICOMPRESSOPTIONS(out comprOptions);
|
||||
|
||||
bool result = AVISaveOptions(_pAviRawVideoStream, ref comprOptions, hwnd) != 0;
|
||||
CodecToken ret = CodecToken.CreateFromAVICOMPRESSOPTIONS(ref comprOptions);
|
||||
var result = AVISaveOptions(_pAviRawVideoStream, ref comprOptions, hwnd) != 0;
|
||||
var ret = CodecToken.CreateFromAVICOMPRESSOPTIONS(ref comprOptions);
|
||||
|
||||
// so, AVISaveOptions may have changed some of the pointers
|
||||
// if it changed the pointers, did it it free the old ones? we don't know
|
||||
|
@ -741,12 +749,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
// guess what? doesn't matter. We'll free them all ourselves.
|
||||
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref comprOptions);
|
||||
|
||||
if (result)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
return result ? ret : null;
|
||||
}
|
||||
|
||||
/// <summary>begin recording</summary>
|
||||
|
@ -759,19 +762,19 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
// open compressed video stream
|
||||
AVIWriterImports.AVICOMPRESSOPTIONS opts = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
||||
_currVideoCodecToken.AllocateToAVICOMPRESSOPTIONS(ref opts);
|
||||
bool failed = FAILED(AVIWriterImports.AVIMakeCompressedStream(out _pAviCompressedVideoStream, _pAviRawVideoStream, ref opts, IntPtr.Zero));
|
||||
_currVideoCodecToken.AllocateToAVICOMPRESSOPTIONS(out var opts);
|
||||
|
||||
var hr = AVIWriterImports.AVIMakeCompressedStream(out _pAviCompressedVideoStream, _pAviRawVideoStream, ref opts, IntPtr.Zero);
|
||||
var hrEx = Marshal.GetExceptionForHR(hr);
|
||||
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref opts);
|
||||
|
||||
if (failed)
|
||||
if (hrEx != null)
|
||||
{
|
||||
CloseStreams();
|
||||
throw new InvalidOperationException("Failed making compressed video stream");
|
||||
throw new InvalidOperationException("Failed making compressed video stream", hrEx);
|
||||
}
|
||||
|
||||
// set the compressed video stream input format
|
||||
AVIWriterImports.BITMAPINFOHEADER bmih = new AVIWriterImports.BITMAPINFOHEADER();
|
||||
var bmih = default(AVIWriterImports.BITMAPINFOHEADER);
|
||||
if (_bit32)
|
||||
{
|
||||
_parameters.PopulateBITMAPINFOHEADER32(ref bmih);
|
||||
|
@ -781,17 +784,22 @@ namespace BizHawk.Client.EmuHawk
|
|||
_parameters.PopulateBITMAPINFOHEADER24(ref bmih);
|
||||
}
|
||||
|
||||
if (FAILED(AVIWriterImports.AVIStreamSetFormat(_pAviCompressedVideoStream, 0, ref bmih, Marshal.SizeOf(bmih))))
|
||||
hr = AVIWriterImports.AVIStreamSetFormat(_pAviCompressedVideoStream, 0, ref bmih, Marshal.SizeOf(bmih));
|
||||
hrEx = Marshal.GetExceptionForHR(hr);
|
||||
if (hrEx != null)
|
||||
{
|
||||
_bit32 = true; // we'll try again
|
||||
CloseStreams();
|
||||
throw new InvalidOperationException("Failed setting compressed video stream input format");
|
||||
throw new InvalidOperationException("Failed setting compressed video stream input format", hrEx);
|
||||
}
|
||||
|
||||
// set audio stream input format
|
||||
AVIWriterImports.WAVEFORMATEX wfex = new AVIWriterImports.WAVEFORMATEX();
|
||||
var wfex = default(AVIWriterImports.WAVEFORMATEX);
|
||||
_parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||
if (FAILED(AVIWriterImports.AVIStreamSetFormat(_pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex))))
|
||||
|
||||
hr = AVIWriterImports.AVIStreamSetFormat(_pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex));
|
||||
hrEx = Marshal.GetExceptionForHR(hr);
|
||||
if (hrEx != null)
|
||||
{
|
||||
CloseStreams();
|
||||
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
||||
|
@ -833,7 +841,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// <summary>
|
||||
/// end recording
|
||||
/// </summary>
|
||||
public void CloseStreams()
|
||||
private void CloseStreams()
|
||||
{
|
||||
if (_pAviRawAudioStream != IntPtr.Zero)
|
||||
{
|
||||
|
@ -848,18 +856,20 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
// todo - why couldnt this take an ISoundProvider? it could do the timekeeping as well.. hmm
|
||||
public void AddSamples(short[] samples)
|
||||
public void AddSamples(IReadOnlyList<short> samples)
|
||||
{
|
||||
int todo = samples.Length;
|
||||
int idx = 0;
|
||||
var todo = samples.Count;
|
||||
var idx = 0;
|
||||
while (todo > 0)
|
||||
{
|
||||
int remain = OutputStatus.AUDIO_SEGMENT_SIZE - _outStatus.audio_buffered_shorts;
|
||||
int chunk = Math.Min(remain, todo);
|
||||
for (int i = 0; i < chunk; i++)
|
||||
var remain = OutputStatus.AUDIO_SEGMENT_SIZE - _outStatus.audio_buffered_shorts;
|
||||
var chunk = Math.Min(remain, todo);
|
||||
|
||||
for (var i = 0; i < chunk; i++)
|
||||
{
|
||||
_outStatus.BufferedShorts[_outStatus.audio_buffered_shorts++] = samples[idx++];
|
||||
}
|
||||
|
||||
todo -= chunk;
|
||||
|
||||
if (_outStatus.audio_buffered_shorts == OutputStatus.AUDIO_SEGMENT_SIZE)
|
||||
|
@ -869,27 +879,28 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
private void FlushBufferedAudio()
|
||||
private unsafe void FlushBufferedAudio()
|
||||
{
|
||||
int todo = _outStatus.audio_buffered_shorts;
|
||||
int todo_realsamples = todo / 2;
|
||||
IntPtr buf = GetStaticGlobalBuf(todo * 2);
|
||||
var todo = _outStatus.audio_buffered_shorts;
|
||||
var todo_realsamples = todo / 2;
|
||||
var buf = GetStaticGlobalBuf(todo * 2);
|
||||
|
||||
short* sptr = (short*)buf.ToPointer();
|
||||
for (int i = 0; i < todo; i++)
|
||||
var sptr = (short*)buf.ToPointer();
|
||||
for (var i = 0; i < todo; i++)
|
||||
{
|
||||
sptr[i] = _outStatus.BufferedShorts[i];
|
||||
}
|
||||
|
||||
// (TODO - inefficient- build directly in a buffer)
|
||||
_ = AVIWriterImports.AVIStreamWrite(_pAviRawAudioStream, _outStatus.audio_samples, todo_realsamples, buf, todo_realsamples * 4, 0, IntPtr.Zero, out var bytes_written);
|
||||
_ = AVIWriterImports.AVIStreamWrite(_pAviRawAudioStream, _outStatus.audio_samples,
|
||||
todo_realsamples, buf, todo_realsamples * 4, 0, IntPtr.Zero, out var bytes_written);
|
||||
_outStatus.audio_samples += todo_realsamples;
|
||||
_outStatus.audio_bytes += bytes_written;
|
||||
_outStatus.audio_buffered_shorts = 0;
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">attempted frame resize during encoding</exception>
|
||||
public void AddFrame(IVideoProvider source)
|
||||
public unsafe void AddFrame(IVideoProvider source)
|
||||
{
|
||||
const int AVIIF_KEYFRAME = 0x00000010;
|
||||
|
||||
|
@ -897,40 +908,42 @@ namespace BizHawk.Client.EmuHawk
|
|||
|| _parameters.height != source.BufferHeight)
|
||||
throw new InvalidOperationException("video buffer changed between start and now");
|
||||
|
||||
int pitch_add = _parameters.pitch_add;
|
||||
var pitch_add = _parameters.pitch_add;
|
||||
|
||||
int todo = _parameters.pitch * _parameters.height;
|
||||
int w = source.BufferWidth;
|
||||
int h = source.BufferHeight;
|
||||
var todo = _parameters.pitch * _parameters.height;
|
||||
var w = source.BufferWidth;
|
||||
var h = source.BufferHeight;
|
||||
|
||||
if (!_bit32)
|
||||
{
|
||||
IntPtr buf = GetStaticGlobalBuf(todo);
|
||||
var buf = GetStaticGlobalBuf(todo);
|
||||
|
||||
// TODO - would using a byte* be faster?
|
||||
int[] buffer = source.GetVideoBuffer();
|
||||
fixed (int* buffer_ptr = &buffer[0])
|
||||
var buffer = source.GetVideoBuffer();
|
||||
fixed (int* buffer_ptr = buffer)
|
||||
{
|
||||
byte* bytes_ptr = (byte*)buf.ToPointer();
|
||||
var bytes_ptr = (byte*)buf.ToPointer();
|
||||
{
|
||||
byte* bp = bytes_ptr;
|
||||
var bp = bytes_ptr;
|
||||
|
||||
for (int idx = w * h - w, y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++, idx++)
|
||||
for (var x = 0; x < w; x++, idx++)
|
||||
{
|
||||
int r = (buffer[idx] >> 0) & 0xFF;
|
||||
int g = (buffer[idx] >> 8) & 0xFF;
|
||||
int b = (buffer[idx] >> 16) & 0xFF;
|
||||
var r = (buffer_ptr[idx] >> 0) & 0xFF;
|
||||
var g = (buffer_ptr[idx] >> 8) & 0xFF;
|
||||
var b = (buffer_ptr[idx] >> 16) & 0xFF;
|
||||
*bp++ = (byte)r;
|
||||
*bp++ = (byte)g;
|
||||
*bp++ = (byte)b;
|
||||
}
|
||||
|
||||
idx -= w * 2;
|
||||
bp += pitch_add;
|
||||
}
|
||||
|
||||
int ret = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames, 1, new IntPtr(bytes_ptr), todo, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written);
|
||||
_ = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames,
|
||||
1, new(bytes_ptr), todo, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written);
|
||||
_outStatus.video_bytes += bytes_written;
|
||||
_outStatus.video_frames++;
|
||||
}
|
||||
|
@ -938,30 +951,32 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
else // 32 bit
|
||||
{
|
||||
IntPtr buf = GetStaticGlobalBuf(todo * 4);
|
||||
int[] buffer = source.GetVideoBuffer();
|
||||
fixed (int* buffer_ptr = &buffer[0])
|
||||
var buf = GetStaticGlobalBuf(todo * 4);
|
||||
var buffer = source.GetVideoBuffer();
|
||||
fixed (int* buffer_ptr = buffer)
|
||||
{
|
||||
byte* bytes_ptr = (byte*)buf.ToPointer();
|
||||
var bytes_ptr = (byte*)buf.ToPointer();
|
||||
{
|
||||
byte* bp = bytes_ptr;
|
||||
var bp = bytes_ptr;
|
||||
|
||||
for (int idx = w * h - w, y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++, idx++)
|
||||
for (var x = 0; x < w; x++, idx++)
|
||||
{
|
||||
int r = (buffer[idx] >> 0) & 0xFF;
|
||||
int g = (buffer[idx] >> 8) & 0xFF;
|
||||
int b = (buffer[idx] >> 16) & 0xFF;
|
||||
var r = (buffer_ptr[idx] >> 0) & 0xFF;
|
||||
var g = (buffer_ptr[idx] >> 8) & 0xFF;
|
||||
var b = (buffer_ptr[idx] >> 16) & 0xFF;
|
||||
*bp++ = (byte)r;
|
||||
*bp++ = (byte)g;
|
||||
*bp++ = (byte)b;
|
||||
*bp++ = 0;
|
||||
}
|
||||
|
||||
idx -= w * 2;
|
||||
}
|
||||
|
||||
int ret = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames, 1, new IntPtr(bytes_ptr), todo * 3, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written);
|
||||
_ = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames,
|
||||
1, new(bytes_ptr), todo * 3, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written);
|
||||
_outStatus.video_bytes += bytes_written;
|
||||
_outStatus.video_frames++;
|
||||
}
|
||||
|
@ -974,13 +989,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
public void SetDefaultVideoCodecToken(Config config)
|
||||
{
|
||||
var ct = CodecToken.DeSerialize(config.AviCodecToken);
|
||||
_currVideoCodecToken = ct ?? throw new Exception($"No default {nameof(config.AviCodecToken)} in config!");
|
||||
_currVideoCodecToken = ct ?? throw new($"No default {nameof(config.AviCodecToken)} in config!");
|
||||
}
|
||||
|
||||
public string DesiredExtension()
|
||||
{
|
||||
return "avi";
|
||||
}
|
||||
=> "avi";
|
||||
|
||||
public bool UsesAudio => _parameters.has_audio;
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ using System.Windows.Forms;
|
|||
|
||||
using BizHawk.Common;
|
||||
|
||||
using static BizHawk.Common.ShlobjImports;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -21,8 +23,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// </remarks>
|
||||
public sealed class FolderBrowserEx : Component
|
||||
{
|
||||
/// <remarks>is this supposed to be public? we're obviously not using it at callsites at the moment --yoshi</remarks>
|
||||
private readonly Win32Imports.BROWSEINFO.FLAGS publicOptions = Win32Imports.BROWSEINFO.FLAGS.RestrictToFilesystem | Win32Imports.BROWSEINFO.FLAGS.RestrictToDomain;
|
||||
private const BROWSEINFO.FLAGS BrowseOptions = BROWSEINFO.FLAGS.RestrictToFilesystem | BROWSEINFO.FLAGS.RestrictToDomain |
|
||||
BROWSEINFO.FLAGS.NewDialogStyle | BROWSEINFO.FLAGS.ShowTextBox;
|
||||
|
||||
public string Description = "Please select a folder below:";
|
||||
|
||||
|
@ -31,54 +33,78 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// <summary>Shows the folder browser dialog box with the specified owner window.</summary>
|
||||
public DialogResult ShowDialog(IWin32Window owner = null)
|
||||
{
|
||||
const Win32Imports.BROWSEINFO.FLAGS privateOptions = Win32Imports.BROWSEINFO.FLAGS.NewDialogStyle | Win32Imports.BROWSEINFO.FLAGS.ShowTextBox;
|
||||
const int startLocation = 0; // = Desktop CSIDL
|
||||
int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData)
|
||||
{
|
||||
if (uMsg == 1)
|
||||
if (uMsg == BFFM_INITIALIZED)
|
||||
{
|
||||
var str = Marshal.StringToHGlobalUni(SelectedPath);
|
||||
Win32Imports.SendMessage(hwnd, 0x400 + 103, (IntPtr) 1, str);
|
||||
Marshal.FreeHGlobal(str);
|
||||
try
|
||||
{
|
||||
WmImports.SendMessage(hwnd, BFFM_SETSELECTIONW, new(1), str);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(str);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
var hWndOwner = owner?.Handle ?? Win32Imports.GetActiveWindow();
|
||||
_ = Win32Imports.SHGetSpecialFolderLocation(hWndOwner, startLocation, out var pidlRoot);
|
||||
if (pidlRoot == IntPtr.Zero) return DialogResult.Cancel;
|
||||
var mergedOptions = publicOptions | privateOptions;
|
||||
if ((mergedOptions & Win32Imports.BROWSEINFO.FLAGS.NewDialogStyle) != 0 && ApartmentState.MTA == Application.OleRequired())
|
||||
var hWndOwner = owner?.Handle ?? WmImports.GetActiveWindow();
|
||||
_ = SHGetSpecialFolderLocation(hWndOwner, startLocation, out var pidlRoot);
|
||||
if (pidlRoot == IntPtr.Zero)
|
||||
{
|
||||
mergedOptions &= ~Win32Imports.BROWSEINFO.FLAGS.NewDialogStyle;
|
||||
return DialogResult.Cancel;
|
||||
}
|
||||
|
||||
IntPtr pidlRet = default;
|
||||
var browseOptions = BrowseOptions;
|
||||
if (ApartmentState.MTA == Application.OleRequired())
|
||||
{
|
||||
browseOptions &= ~BROWSEINFO.FLAGS.NewDialogStyle;
|
||||
}
|
||||
|
||||
var pidlRet = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
var buffer = Marshal.AllocHGlobal(Win32Imports.MAX_PATH);
|
||||
var bi = new Win32Imports.BROWSEINFO
|
||||
var bi = new BROWSEINFO
|
||||
{
|
||||
hwndOwner = hWndOwner,
|
||||
pidlRoot = pidlRoot,
|
||||
pszDisplayName = buffer,
|
||||
lpszTitle = Description,
|
||||
ulFlags = mergedOptions,
|
||||
ulFlags = browseOptions,
|
||||
lpfn = Callback
|
||||
};
|
||||
pidlRet = Win32Imports.SHBrowseForFolder(ref bi);
|
||||
|
||||
pidlRet = SHBrowseForFolder(ref bi);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
if (pidlRet == IntPtr.Zero) return DialogResult.Cancel; // user clicked Cancel
|
||||
var sb = new StringBuilder(Win32Imports.MAX_PATH);
|
||||
if (Win32Imports.SHGetPathFromIDList(pidlRet, sb) == 0) return DialogResult.Cancel;
|
||||
SelectedPath = sb.ToString();
|
||||
if (pidlRet == IntPtr.Zero)
|
||||
{
|
||||
return DialogResult.Cancel; // user clicked Cancel
|
||||
}
|
||||
|
||||
var path = new StringBuilder(Win32Imports.MAX_PATH);
|
||||
if (SHGetPathFromIDList(pidlRet, path) == 0)
|
||||
{
|
||||
return DialogResult.Cancel;
|
||||
}
|
||||
|
||||
SelectedPath = path.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = Win32Imports.SHGetMalloc(out var malloc);
|
||||
_ = SHGetMalloc(out var malloc);
|
||||
malloc.Free(pidlRoot);
|
||||
if (pidlRet != IntPtr.Zero) malloc.Free(pidlRet);
|
||||
|
||||
if (pidlRet != IntPtr.Zero)
|
||||
{
|
||||
malloc.Free(pidlRet);
|
||||
}
|
||||
}
|
||||
|
||||
return DialogResult.OK;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
protected override void OnMouseClick(MouseEventArgs e)
|
||||
{
|
||||
if (!OSTailoredCode.IsUnixHost) Win32Imports.HideCaret(Handle);
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
WmImports.HideCaret(Handle);
|
||||
}
|
||||
|
||||
base.OnMouseClick(e);
|
||||
}
|
||||
|
||||
|
@ -253,7 +257,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
protected override void OnGotFocus(EventArgs e)
|
||||
{
|
||||
if (!OSTailoredCode.IsUnixHost) Win32Imports.HideCaret(Handle);
|
||||
if (!OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
WmImports.HideCaret(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
|
|
|
@ -10,11 +10,14 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.ReflectionExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
using static BizHawk.Common.CommctrlImports;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public static class ControlExtensions
|
||||
|
@ -183,32 +186,42 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// <exception cref="Win32Exception">unmanaged call failed</exception>
|
||||
public static void SetSortIcon(this ListView listViewControl, int columnIndex, SortOrder order)
|
||||
{
|
||||
if (OSTailoredCode.IsUnixHost) return;
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int LVM_GETHEADER = 4127;
|
||||
const int HDM_GETITEM = 4619;
|
||||
const int HDM_SETITEM = 4620;
|
||||
var columnHeader = Win32Imports.SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
|
||||
var columnHeader = WmImports.SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
|
||||
for (int columnNumber = 0, l = listViewControl.Columns.Count; columnNumber < l; columnNumber++)
|
||||
{
|
||||
var columnPtr = new IntPtr(columnNumber);
|
||||
var item = new Win32Imports.HDITEM { mask = Win32Imports.HDITEM.Mask.Format };
|
||||
if (Win32Imports.SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero) throw new Win32Exception();
|
||||
var item = new HDITEM { mask = HDITEM.Mask.Format };
|
||||
if (SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero)
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
if (columnNumber != columnIndex || order == SortOrder.None)
|
||||
{
|
||||
item.fmt &= ~Win32Imports.HDITEM.Format.SortDown & ~Win32Imports.HDITEM.Format.SortUp;
|
||||
item.fmt &= ~HDITEM.Format.SortDown & ~HDITEM.Format.SortUp;
|
||||
}
|
||||
else if (order == SortOrder.Ascending)
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
else switch (order)
|
||||
{
|
||||
item.fmt &= ~Win32Imports.HDITEM.Format.SortDown;
|
||||
item.fmt |= Win32Imports.HDITEM.Format.SortUp;
|
||||
case SortOrder.Ascending:
|
||||
item.fmt &= ~HDITEM.Format.SortDown;
|
||||
item.fmt |= HDITEM.Format.SortUp;
|
||||
break;
|
||||
case SortOrder.Descending:
|
||||
item.fmt &= ~HDITEM.Format.SortUp;
|
||||
item.fmt |= HDITEM.Format.SortDown;
|
||||
break;
|
||||
}
|
||||
else if (order == SortOrder.Descending)
|
||||
|
||||
if (SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero)
|
||||
{
|
||||
item.fmt &= ~Win32Imports.HDITEM.Format.SortUp;
|
||||
item.fmt |= Win32Imports.HDITEM.Format.SortDown;
|
||||
throw new Win32Exception();
|
||||
}
|
||||
if (Win32Imports.SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero) throw new Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -868,7 +868,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
groupFreeze.SuspendLayout();
|
||||
|
||||
Win32Imports.SendMessage(groupFreeze.Handle, 11, (IntPtr)0, IntPtr.Zero); //WM_SETREDRAW false
|
||||
WmImports.SendMessage(groupFreeze.Handle, 11, (IntPtr)0, IntPtr.Zero); //WM_SETREDRAW false
|
||||
|
||||
var tp = tabctrlDetails.SelectedTab;
|
||||
|
||||
|
@ -886,7 +886,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
groupFreeze.ResumeLayout();
|
||||
|
||||
Win32Imports.SendMessage(groupFreeze.Handle, 11, (IntPtr)1, IntPtr.Zero); //WM_SETREDRAW true
|
||||
WmImports.SendMessage(groupFreeze.Handle, 11, (IntPtr)1, IntPtr.Zero); //WM_SETREDRAW true
|
||||
groupFreeze.Refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace BizHawk.Common.PathExtensions
|
|||
if (File.Exists(path1.SubstringBefore('|'))) return FileAttributes.Normal;
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
var path = new StringBuilder(260 /* = MAX_PATH */);
|
||||
var path = new StringBuilder(Win32Imports.MAX_PATH);
|
||||
return Win32Imports.PathRelativePathTo(path, fromPath, GetPathAttribute(fromPath), toPath, GetPathAttribute(toPath))
|
||||
? path.ToString()
|
||||
: throw new ArgumentException(message: "Paths must have a common prefix", paramName: nameof(toPath));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
using static BizHawk.Common.Kernel32Imports;
|
||||
using static BizHawk.Common.MemoryApiImports;
|
||||
using static BizHawk.Common.MemoryBlock;
|
||||
|
||||
namespace BizHawk.Common
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class AVIWriterImports
|
||||
|
@ -35,7 +37,8 @@ namespace BizHawk.Common
|
|||
public RECT rcFrame;
|
||||
public int dwEditCount;
|
||||
public int dwFormatChangeCount;
|
||||
[MarshalAs(UnmanagedType.LPWStr, SizeConst=64)] public string szName;
|
||||
[MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
|
||||
public string szName;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct RECT
|
||||
|
@ -63,9 +66,7 @@ namespace BizHawk.Common
|
|||
public uint biClrImportant;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
biSize = (uint)Marshal.SizeOf(this);
|
||||
}
|
||||
=> biSize = (uint)Marshal.SizeOf(this);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
@ -96,9 +97,7 @@ namespace BizHawk.Common
|
|||
public ushort cbSize;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
cbSize = (ushort)Marshal.SizeOf(this);
|
||||
}
|
||||
=> cbSize = (ushort)Marshal.SizeOf(this);
|
||||
}
|
||||
|
||||
/// <summary>Create a new stream in an existing file and creates an interface to the new stream</summary>
|
||||
|
|
|
@ -1,38 +1,60 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>Gets/Sets the current working directory while bypassing the security checks triggered by the public API (<see cref="Environment.CurrentDirectory"/>).</summary>
|
||||
public static unsafe class CWDHacks
|
||||
public static class CWDHacks
|
||||
{
|
||||
private const uint BUFFER_LEN = 0x200U;
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern unsafe int GetCurrentDirectoryW(int nBufferLength, char* lpBuffer);
|
||||
|
||||
private static readonly byte[] BUFFER = new byte[BUFFER_LEN];
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern uint GetCurrentDirectoryW(uint nBufferLength, byte* pBuffer);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetCurrentDirectoryW(byte* lpPathName);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern bool SetCurrentDirectoryW(string lpPathName);
|
||||
|
||||
public static bool Set(string newCWD)
|
||||
{
|
||||
fixed (byte* pstr = &Encoding.Unicode.GetBytes($"{newCWD}\0")[0])
|
||||
return SetCurrentDirectoryW(pstr);
|
||||
}
|
||||
=> SetCurrentDirectoryW(newCWD);
|
||||
|
||||
public static string Get()
|
||||
public static unsafe string Get()
|
||||
{
|
||||
uint result;
|
||||
fixed (byte* pBuf = &BUFFER[0]) result = GetCurrentDirectoryW(BUFFER_LEN, pBuf);
|
||||
if (result <= BUFFER_LEN && result is not 0U) return Encoding.Unicode.GetString(BUFFER, 0, (int) (2U * result));
|
||||
var buf = new byte[result];
|
||||
uint result1;
|
||||
fixed (byte* pBuf = &buf[0]) result1 = GetCurrentDirectoryW(BUFFER_LEN, pBuf);
|
||||
if (result1 == result) return Encoding.Unicode.GetString(buf, 0, (int) (2U * result));
|
||||
throw new Exception();
|
||||
static Exception GetExceptionForFailure()
|
||||
{
|
||||
var ex = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||
?? new("Marshal.GetExceptionForHR returned null?");
|
||||
return new InvalidOperationException("GetCurrentDirectoryW returned 0!", ex);
|
||||
}
|
||||
|
||||
const int STARTING_BUF_SIZE = Win32Imports.MAX_PATH + 1;
|
||||
var startingBuffer = stackalloc char[STARTING_BUF_SIZE];
|
||||
var ret = GetCurrentDirectoryW(STARTING_BUF_SIZE, startingBuffer);
|
||||
switch (ret)
|
||||
{
|
||||
case 0:
|
||||
throw GetExceptionForFailure();
|
||||
case < STARTING_BUF_SIZE: // ret should be smaller than the buffer, as ret doesn't include null terminator
|
||||
return new(startingBuffer, 0, ret);
|
||||
}
|
||||
|
||||
// since current directory could suddenly grow (due to it being global / modifiable by other threads), a while true loop is used here
|
||||
// although it's fairly unlikely we'll even reach this point, MAX_PATH can only be bypassed under certain circumstances
|
||||
while (true)
|
||||
{
|
||||
var bufSize = ret + 1;
|
||||
var buffer = new char[bufSize];
|
||||
fixed (char* p = buffer)
|
||||
{
|
||||
ret = GetCurrentDirectoryW(bufSize, p);
|
||||
if (ret == 0)
|
||||
{
|
||||
throw GetExceptionForFailure();
|
||||
}
|
||||
|
||||
if (ret < bufSize)
|
||||
{
|
||||
return new(p, 0, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class CommctrlImports
|
||||
{
|
||||
public const int LVM_GETHEADER = 4127;
|
||||
public const int HDM_GETITEM = 4619;
|
||||
public const int HDM_SETITEM = 4620;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HDITEM
|
||||
{
|
||||
public Mask mask;
|
||||
public int cxy;
|
||||
[MarshalAs(UnmanagedType.LPTStr)]
|
||||
public string pszText;
|
||||
public IntPtr hbm;
|
||||
public int cchTextMax;
|
||||
public Format fmt;
|
||||
public IntPtr lParam;
|
||||
|
||||
// _WIN32_IE >= 0x0300
|
||||
public int iImage;
|
||||
public int iOrder;
|
||||
|
||||
// _WIN32_IE >= 0x0500
|
||||
public uint type;
|
||||
public IntPtr pvFilter;
|
||||
|
||||
// _WIN32_WINNT >= 0x0600
|
||||
public uint state;
|
||||
|
||||
[Flags]
|
||||
public enum Mask
|
||||
{
|
||||
Format = 0x4
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Format
|
||||
{
|
||||
SortDown = 0x200,
|
||||
SortUp = 0x400
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, ref HDITEM lParam);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class HeapApiImports
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetProcessHeap();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = false)]
|
||||
public static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, int dwBytes);
|
||||
|
||||
/// <remarks>used in <c>#if false</c> code in <c>AviWriter.CodecToken.DeallocateAVICOMPRESSOPTIONS</c>, don't delete it</remarks>
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,11 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class Kernel32Imports
|
||||
public static class MemoryApiImports
|
||||
{
|
||||
[Flags]
|
||||
public enum AllocationType : uint
|
|
@ -3,6 +3,7 @@ namespace BizHawk.Common
|
|||
/// <remarks>This code (and an import for <see cref="Win32Imports.DeleteFileW"/>) is duplicated in each executable project because it needs to be used before loading assemblies.</remarks>
|
||||
public static class MotWHack
|
||||
{
|
||||
public static void RemoveMOTW(string path) => Win32Imports.DeleteFileW($"{path}:Zone.Identifier");
|
||||
public static void RemoveMOTW(string path)
|
||||
=> Win32Imports.DeleteFileW($"{path}:Zone.Identifier");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ using System;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class ShellLinkImports
|
||||
|
@ -19,15 +21,21 @@ namespace BizHawk.Common
|
|||
public uint nFileSizeLow;
|
||||
public uint dwReserved0;
|
||||
public uint dwReserved1;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||
public string cFileName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
|
||||
public string cAlternateFileName;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SLGP_FLAGS {}
|
||||
public enum SLGP_FLAGS
|
||||
{
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SLR_FLAGS {}
|
||||
public enum SLR_FLAGS
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
||||
[ComImport]
|
||||
|
@ -108,6 +116,8 @@ namespace BizHawk.Common
|
|||
/// <remarks>CLSID_ShellLink from ShlGuid.h</remarks>
|
||||
[ComImport]
|
||||
[Guid("00021401-0000-0000-C000-000000000046")]
|
||||
public class ShellLink /* : IPersistFile, IShellLinkW */ {}
|
||||
public class ShellLink /* : IPersistFile, IShellLinkW */
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class ShlobjImports
|
||||
{
|
||||
public const int BFFM_INITIALIZED = 1;
|
||||
public const int BFFM_SETSELECTIONW = 0x400 + 103;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct BROWSEINFO
|
||||
{
|
||||
public IntPtr hwndOwner;
|
||||
public IntPtr pidlRoot;
|
||||
public IntPtr pszDisplayName;
|
||||
[MarshalAs(UnmanagedType.LPTStr)]
|
||||
public string lpszTitle;
|
||||
public FLAGS ulFlags;
|
||||
[MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public BFFCALLBACK lpfn;
|
||||
public IntPtr lParam;
|
||||
public int iImage;
|
||||
|
||||
[Flags]
|
||||
public enum FLAGS
|
||||
{
|
||||
/// <remarks>BIF_RETURNONLYFSDIRS</remarks>
|
||||
RestrictToFilesystem = 0x0001,
|
||||
/// <remarks>BIF_DONTGOBELOWDOMAIN</remarks>
|
||||
RestrictToDomain = 0x0002,
|
||||
/// <remarks>BIF_RETURNFSANCESTORS</remarks>
|
||||
RestrictToSubfolders = 0x0008,
|
||||
/// <remarks>BIF_EDITBOX</remarks>
|
||||
ShowTextBox = 0x0010,
|
||||
/// <remarks>BIF_VALIDATE</remarks>
|
||||
ValidateSelection = 0x0020,
|
||||
/// <remarks>BIF_NEWDIALOGSTYLE</remarks>
|
||||
NewDialogStyle = 0x0040,
|
||||
/// <remarks>BIF_BROWSEFORCOMPUTER</remarks>
|
||||
BrowseForComputer = 0x1000,
|
||||
/// <remarks>BIF_BROWSEFORPRINTER</remarks>
|
||||
BrowseForPrinter = 0x2000,
|
||||
/// <remarks>BIF_BROWSEINCLUDEFILES</remarks>
|
||||
BrowseForEverything = 0x4000
|
||||
}
|
||||
}
|
||||
|
||||
[Guid("00000002-0000-0000-C000-000000000046")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IMalloc
|
||||
{
|
||||
[PreserveSig]
|
||||
IntPtr Alloc([In] int cb);
|
||||
|
||||
[PreserveSig]
|
||||
IntPtr Realloc([In] IntPtr pv, [In] int cb);
|
||||
|
||||
[PreserveSig]
|
||||
void Free([In] IntPtr pv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetSize([In] IntPtr pv);
|
||||
|
||||
[PreserveSig]
|
||||
int DidAlloc(IntPtr pv);
|
||||
|
||||
[PreserveSig]
|
||||
void HeapMinimize();
|
||||
}
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHGetMalloc(out IMalloc ppMalloc);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, int nFolder, out IntPtr ppidl);
|
||||
}
|
||||
}
|
|
@ -15,35 +15,11 @@ namespace BizHawk.Common
|
|||
{
|
||||
public const uint QS_ALLINPUT = 0x4FFU;
|
||||
public const uint MWMO_INPUTAVAILABLE = 0x0004U;
|
||||
public const uint PM_REMOVE = 0x0001U;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint message;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr DispatchMessage([In] ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern uint MsgWaitForMultipleObjectsEx(uint nCount, IntPtr[] pHandles, uint dwMilliseconds, uint dwWakeMask, uint dwFlags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool TranslateMessage([In] ref MSG lpMsg);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
public static extern int WaitForSingleObject(SafeWaitHandle handle, uint milliseconds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,227 +7,29 @@ using System.Text;
|
|||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// This is more just an assorted bunch of Win32 functions
|
||||
/// </summary>
|
||||
public static class Win32Imports
|
||||
{
|
||||
public const int MAX_PATH = 260;
|
||||
public const uint PM_REMOVE = 0x0001U;
|
||||
public static readonly IntPtr HWND_MESSAGE = new(-3);
|
||||
public const int GWLP_USERDATA = -21;
|
||||
|
||||
public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct BROWSEINFO
|
||||
{
|
||||
public IntPtr hwndOwner;
|
||||
public IntPtr pidlRoot;
|
||||
public IntPtr pszDisplayName;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;
|
||||
public FLAGS ulFlags;
|
||||
[MarshalAs(UnmanagedType.FunctionPtr)] public BFFCALLBACK lpfn;
|
||||
public IntPtr lParam;
|
||||
public int iImage;
|
||||
|
||||
[Flags]
|
||||
public enum FLAGS
|
||||
{
|
||||
/// <remarks>BIF_RETURNONLYFSDIRS</remarks>
|
||||
RestrictToFilesystem = 0x0001,
|
||||
/// <remarks>BIF_DONTGOBELOWDOMAIN</remarks>
|
||||
RestrictToDomain = 0x0002,
|
||||
/// <remarks>BIF_RETURNFSANCESTORS</remarks>
|
||||
RestrictToSubfolders = 0x0008,
|
||||
/// <remarks>BIF_EDITBOX</remarks>
|
||||
ShowTextBox = 0x0010,
|
||||
/// <remarks>BIF_VALIDATE</remarks>
|
||||
ValidateSelection = 0x0020,
|
||||
/// <remarks>BIF_NEWDIALOGSTYLE</remarks>
|
||||
NewDialogStyle = 0x0040,
|
||||
/// <remarks>BIF_BROWSEFORCOMPUTER</remarks>
|
||||
BrowseForComputer = 0x1000,
|
||||
/// <remarks>BIF_BROWSEFORPRINTER</remarks>
|
||||
BrowseForPrinter = 0x2000,
|
||||
/// <remarks>BIF_BROWSEINCLUDEFILES</remarks>
|
||||
BrowseForEverything = 0x4000
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HDITEM
|
||||
{
|
||||
public Mask mask;
|
||||
public int cxy;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string pszText;
|
||||
public IntPtr hbm;
|
||||
public int cchTextMax;
|
||||
public Format fmt;
|
||||
public IntPtr lParam;
|
||||
|
||||
// _WIN32_IE >= 0x0300
|
||||
public int iImage;
|
||||
public int iOrder;
|
||||
|
||||
// _WIN32_IE >= 0x0500
|
||||
public uint type;
|
||||
public IntPtr pvFilter;
|
||||
|
||||
// _WIN32_WINNT >= 0x0600
|
||||
public uint state;
|
||||
|
||||
[Flags]
|
||||
public enum Mask
|
||||
{
|
||||
Format = 0x4
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Format
|
||||
{
|
||||
SortDown = 0x200,
|
||||
SortUp = 0x400
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint message;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
|
||||
public delegate IntPtr WNDPROC(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct WNDCLASS
|
||||
{
|
||||
public uint style;
|
||||
public WNDPROC lpfnWndProc;
|
||||
public int cbClsExtra;
|
||||
public int cbWndExtra;
|
||||
public IntPtr hInstance;
|
||||
public IntPtr hIcon;
|
||||
public IntPtr hCursor;
|
||||
public IntPtr hbrBackground;
|
||||
public string lpszMenuName;
|
||||
public string lpszClassName;
|
||||
}
|
||||
|
||||
[Guid("00000002-0000-0000-C000-000000000046")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IMalloc
|
||||
{
|
||||
[PreserveSig] IntPtr Alloc([In] int cb);
|
||||
[PreserveSig] IntPtr Realloc([In] IntPtr pv, [In] int cb);
|
||||
[PreserveSig] void Free([In] IntPtr pv);
|
||||
[PreserveSig] int GetSize([In] IntPtr pv);
|
||||
[PreserveSig] int DidAlloc(IntPtr pv);
|
||||
[PreserveSig] void HeapMinimize();
|
||||
}
|
||||
|
||||
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern uint _control87(uint @new, uint mask);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr CreateWindowEx(int dwExStyle, IntPtr lpClassName, string lpWindowName,
|
||||
int dwStyle, int X, int Y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
public static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr DispatchMessage([In] ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string className, string windowTitle);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetActiveWindow();
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, EntryPoint = "GetProcAddress")]
|
||||
public static extern IntPtr GetProcAddressOrdinal(IntPtr hModule, IntPtr procName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetProcessHeap();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = false)]
|
||||
public static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, int dwBytes);
|
||||
|
||||
/// <remarks>used in <c>#if false</c> code in <c>AviWriter.CodecToken.DeallocateAVICOMPRESSOPTIONS</c>, don't delete it</remarks>
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern bool HideCaret(IntPtr hWnd);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool IsDebuggerPresent();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
|
||||
|
||||
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
||||
public static extern IntPtr MemSet(IntPtr dest, int c, uint count);
|
||||
|
||||
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool PathRelativePathTo([Out] StringBuilder pszPath, [In] string pszFrom, [In] FileAttributes dwAttrFrom, [In] string pszTo, [In] FileAttributes dwAttrTo);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr RegisterClass([In] ref WNDCLASS lpWndClass);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, ref HDITEM lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHGetMalloc(out IMalloc ppMalloc);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder Path);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, int nFolder, out IntPtr ppidl);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||
[DllImport("winmm.dll")]
|
||||
public static extern uint timeBeginPeriod(uint uMilliseconds);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool TranslateMessage([In] ref MSG lpMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public class Win32ShellContextMenu
|
||||
|
@ -117,6 +120,7 @@ namespace BizHawk.Common
|
|||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1069
|
||||
[Flags]
|
||||
public enum TPM
|
||||
{
|
||||
|
@ -140,6 +144,7 @@ namespace BizHawk.Common
|
|||
TPM_NOANIMATION = 0x4000,
|
||||
TPM_LAYOUTRTL = 0x8000,
|
||||
}
|
||||
#pragma warning restore CA1069
|
||||
|
||||
[Flags]
|
||||
public enum CMF : uint
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class WmImports
|
||||
{
|
||||
public const uint PM_REMOVE = 0x0001U;
|
||||
public static readonly IntPtr HWND_MESSAGE = new(-3);
|
||||
public const int GWLP_USERDATA = -21;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint message;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
public delegate IntPtr WNDPROC(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct WNDCLASS
|
||||
{
|
||||
public uint style;
|
||||
public WNDPROC lpfnWndProc;
|
||||
public int cbClsExtra;
|
||||
public int cbWndExtra;
|
||||
public IntPtr hInstance;
|
||||
public IntPtr hIcon;
|
||||
public IntPtr hCursor;
|
||||
public IntPtr hbrBackground;
|
||||
public string lpszMenuName;
|
||||
public string lpszClassName;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr CreateWindowEx(int dwExStyle, IntPtr lpClassName, string lpWindowName,
|
||||
int dwStyle, int X, int Y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr DispatchMessage([In] ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetActiveWindow();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool HideCaret(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr RegisterClass([In] ref WNDCLASS lpWndClass);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool TranslateMessage([In] ref MSG lpMsg);
|
||||
}
|
||||
}
|
|
@ -785,10 +785,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64.NativeApi
|
|||
{
|
||||
IntPtr[] waitHandles = { handle.SafeWaitHandle.DangerousGetHandle() };
|
||||
const uint count = 1;
|
||||
var QS_MASK = ThreadHacks.QS_ALLINPUT; // message queue status
|
||||
var QS_MASK = WmImports.QS_ALLINPUT; // message queue status
|
||||
QS_MASK = 0; //bizhawk edit?? did we need any messages here?? apparently not???
|
||||
uint nativeResult;
|
||||
ThreadHacks.MSG msg;
|
||||
WmImports.MSG msg;
|
||||
while (true)
|
||||
{
|
||||
// MsgWaitForMultipleObjectsEx with MWMO_INPUTAVAILABLE returns,
|
||||
|
@ -796,10 +796,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64.NativeApi
|
|||
nativeResult = ThreadHacks.MsgWaitForMultipleObjectsEx(count, waitHandles, 0xFFFFFFFF, QS_MASK, ThreadHacks.MWMO_INPUTAVAILABLE);
|
||||
if (IsNativeWaitSuccessful(count, nativeResult, out int managedResult) || WaitHandle.WaitTimeout == managedResult) break;
|
||||
// there is a message, pump and dispatch it
|
||||
if (ThreadHacks.PeekMessage(out msg, IntPtr.Zero, 0, 0, ThreadHacks.PM_REMOVE))
|
||||
if (WmImports.PeekMessage(out msg, IntPtr.Zero, 0, 0, WmImports.PM_REMOVE))
|
||||
{
|
||||
ThreadHacks.TranslateMessage(ref msg);
|
||||
ThreadHacks.DispatchMessage(ref msg);
|
||||
WmImports.TranslateMessage(ref msg);
|
||||
WmImports.DispatchMessage(ref msg);
|
||||
}
|
||||
}
|
||||
// handle.WaitOne();
|
||||
|
|
Loading…
Reference in New Issue