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 BizHawk.Common.CollectionExtensions;
|
||||||
|
|
||||||
using static BizHawk.Common.RawInputImports;
|
using static BizHawk.Common.RawInputImports;
|
||||||
using static BizHawk.Common.Win32Imports;
|
using static BizHawk.Common.WmImports;
|
||||||
|
|
||||||
using RawKey = Vortice.DirectInput.Key;
|
using RawKey = Vortice.DirectInput.Key;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ namespace BizHawk.Bizware.Input
|
||||||
{
|
{
|
||||||
var wc = default(WNDCLASS);
|
var wc = default(WNDCLASS);
|
||||||
wc.lpfnWndProc = _wndProc;
|
wc.lpfnWndProc = _wndProc;
|
||||||
wc.hInstance = GetModuleHandle(null);
|
wc.hInstance = Win32Imports.GetModuleHandle(null);
|
||||||
wc.lpszClassName = "RawKeyInputClass";
|
wc.lpszClassName = "RawKeyInputClass";
|
||||||
|
|
||||||
var atom = RegisterClass(ref wc);
|
var atom = RegisterClass(ref wc);
|
||||||
|
@ -125,7 +125,7 @@ namespace BizHawk.Bizware.Input
|
||||||
nHeight: 1,
|
nHeight: 1,
|
||||||
hWndParent: HWND_MESSAGE,
|
hWndParent: HWND_MESSAGE,
|
||||||
hMenu: IntPtr.Zero,
|
hMenu: IntPtr.Zero,
|
||||||
hInstance: GetModuleHandle(null),
|
hInstance: Win32Imports.GetModuleHandle(null),
|
||||||
lpParam: IntPtr.Zero);
|
lpParam: IntPtr.Zero);
|
||||||
|
|
||||||
if (window == 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)
|
// similar code shouldn't be needed on other platforms (which have global message queues and not thread local message queues)
|
||||||
if (!OSTailoredCode.IsUnixHost)
|
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);
|
WmImports.TranslateMessage(ref msg);
|
||||||
Win32Imports.DispatchMessage(ref msg);
|
WmImports.DispatchMessage(ref msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ using BizHawk.Common;
|
||||||
using BizHawk.Common.PathExtensions;
|
using BizHawk.Common.PathExtensions;
|
||||||
using BizHawk.Emulation.Common;
|
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
|
// some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967
|
||||||
namespace BizHawk.Client.EmuHawk
|
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.")]
|
"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
|
internal class AviWriter : IVideoWriter
|
||||||
{
|
{
|
||||||
private CodecToken _currVideoCodecToken = null;
|
private CodecToken _currVideoCodecToken;
|
||||||
private AviWriterSegment _currSegment;
|
private AviWriterSegment _currSegment;
|
||||||
|
|
||||||
private readonly IDialogParent _dialogParent;
|
private readonly IDialogParent _dialogParent;
|
||||||
|
@ -35,9 +37,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private bool IsOpen => _nameProvider != null;
|
private bool IsOpen => _nameProvider != null;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
=> _currSegment?.Dispose();
|
||||||
_currSegment?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>sets the codec token to be used for video compression</summary>
|
/// <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>
|
/// <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();
|
var (dir, baseName, ext) = template.SplitPathToDirFileAndExt();
|
||||||
yield return template;
|
yield return template;
|
||||||
int counter = 1;
|
var counter = 1;
|
||||||
for (;;)
|
while (counter < int.MaxValue)
|
||||||
{
|
{
|
||||||
yield return Path.Combine(dir, $"{baseName}_{counter}{ext}");
|
yield return Path.Combine(dir, $"{baseName}_{counter}{ext}");
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield return Path.Combine(dir, $"{baseName}_{counter}{ext}");
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Reached maximum names");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -70,9 +74,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// set a video codec token first.
|
/// set a video codec token first.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void OpenFile(string baseName)
|
public void OpenFile(string baseName)
|
||||||
{
|
=> OpenFile(CreateBasicNameProvider(baseName));
|
||||||
OpenFile(CreateBasicNameProvider(baseName));
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread communication
|
// thread communication
|
||||||
// synchronized queue with custom messages
|
// synchronized queue with custom messages
|
||||||
|
@ -86,19 +88,18 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
object o = _threadQ.Take();
|
var o = _threadQ.Take();
|
||||||
if (o is IVideoProvider provider)
|
switch (o)
|
||||||
{
|
{
|
||||||
AddFrameEx(provider);
|
case IVideoProvider provider:
|
||||||
}
|
AddFrameEx(provider);
|
||||||
else if (o is short[] arr)
|
break;
|
||||||
{
|
case short[] arr:
|
||||||
AddSamplesEx(arr);
|
AddSamplesEx(arr);
|
||||||
}
|
break;
|
||||||
else
|
default:
|
||||||
{
|
// anything else is assumed to be quit time
|
||||||
// anything else is assumed to be quit time
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +122,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public int BackgroundColor { get; }
|
public int BackgroundColor { get; }
|
||||||
public int VsyncNumerator { get; }
|
public int VsyncNumerator { get; }
|
||||||
public int VsyncDenominator { get; }
|
public int VsyncDenominator { get; }
|
||||||
|
|
||||||
public VideoCopy(IVideoProvider c)
|
public VideoCopy(IVideoProvider c)
|
||||||
{
|
{
|
||||||
_vb = (int[])c.GetVideoBuffer().Clone();
|
_vb = (int[])c.GetVideoBuffer().Clone();
|
||||||
|
@ -134,9 +136,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] GetVideoBuffer()
|
public int[] GetVideoBuffer()
|
||||||
{
|
=> _vb;
|
||||||
return _vb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>opens an avi file for recording, with <paramref name="nameProvider"/> being used to name files</summary>
|
/// <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");
|
throw new InvalidOperationException("Tried to start recording an AVI with no video codec token set");
|
||||||
}
|
}
|
||||||
|
|
||||||
_threadQ = new System.Collections.Concurrent.BlockingCollection<object>(30);
|
_threadQ = new(30);
|
||||||
_workerT = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));
|
_workerT = new(ThreadProc) { IsBackground = true };
|
||||||
_workerT.Start();
|
_workerT.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseFile()
|
public void CloseFile()
|
||||||
{
|
{
|
||||||
_threadQ.Add(new object()); // acts as stop message
|
_threadQ.Add(new()); // acts as stop message
|
||||||
_workerT.Join();
|
_workerT.Join();
|
||||||
_currSegment?.Dispose();
|
_currSegment?.Dispose();
|
||||||
_currSegment = null;
|
_currSegment = null;
|
||||||
|
@ -169,7 +169,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
if (!_workerT.IsAlive)
|
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();
|
Segment();
|
||||||
}
|
}
|
||||||
|
|
||||||
_currSegment.AddFrame(source);
|
_currSegment!.AddFrame(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Exception">worker thrread died</exception>
|
/// <exception cref="Exception">worker thrread died</exception>
|
||||||
|
@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
if (!_workerT.IsAlive)
|
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();
|
Segment();
|
||||||
}
|
}
|
||||||
|
|
||||||
_currSegment.AddSamples(samples);
|
_currSegment!.AddSamples(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConsiderLengthSegment()
|
private void ConsiderLengthSegment()
|
||||||
|
@ -218,7 +218,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long len = _currSegment.GetLengthApproximation();
|
var len = _currSegment.GetLengthApproximation();
|
||||||
const long segment_length_limit = 2 * 1000 * 1000 * 1000; // 2GB
|
const long segment_length_limit = 2 * 1000 * 1000 * 1000; // 2GB
|
||||||
|
|
||||||
// const long segment_length_limit = 10 * 1000 * 1000; //for testing
|
// 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()
|
private void Segment()
|
||||||
{
|
{
|
||||||
if (!IsOpen)
|
if (!IsOpen)
|
||||||
|
@ -240,18 +235,12 @@ namespace BizHawk.Client.EmuHawk
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currSegment == null)
|
_currSegment?.Dispose();
|
||||||
{
|
_currSegment = new();
|
||||||
StartRecording();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_currSegment.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_currSegment = new AviWriterSegment();
|
|
||||||
_nameProvider.MoveNext();
|
_nameProvider.MoveNext();
|
||||||
_currSegment.OpenFile(_nameProvider.Current, _parameters, _currVideoCodecToken);
|
_currSegment.OpenFile(_nameProvider.Current, _parameters, _currVideoCodecToken);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_currSegment.OpenStreams();
|
_currSegment.OpenStreams();
|
||||||
|
@ -278,17 +267,25 @@ namespace BizHawk.Client.EmuHawk
|
||||||
a_samplerate = 44100,
|
a_samplerate = 44100,
|
||||||
a_channels = 2
|
a_channels = 2
|
||||||
};
|
};
|
||||||
var temp = new AviWriterSegment();
|
|
||||||
string tempfile = Path.GetTempFileName();
|
var tempSegment = new AviWriterSegment();
|
||||||
File.Delete(tempfile);
|
var tempFile = Path.GetTempFileName();
|
||||||
tempfile = Path.ChangeExtension(tempfile, "avi");
|
File.Delete(tempFile);
|
||||||
temp.OpenFile(tempfile, tempParams, null);
|
tempFile = Path.ChangeExtension(tempFile, "avi");
|
||||||
var ret = temp.AcquireVideoCodecToken(_dialogParent.AsWinFormsHandle().Handle, _currVideoCodecToken);
|
tempSegment.OpenFile(tempFile, tempParams, null);
|
||||||
CodecToken token = (CodecToken)ret;
|
|
||||||
config.AviCodecToken = token?.Serialize();
|
try
|
||||||
temp.CloseFile();
|
{
|
||||||
File.Delete(tempfile);
|
var ret = tempSegment.AcquireVideoCodecToken(_dialogParent.AsWinFormsHandle().Handle, _currVideoCodecToken);
|
||||||
return token;
|
var token = (CodecToken)ret;
|
||||||
|
config.AviCodecToken = token?.Serialize();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempSegment.CloseFile();
|
||||||
|
File.Delete(tempFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Parameters
|
private class Parameters
|
||||||
|
@ -329,11 +326,18 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public void PopulateWAVEFORMATEX(ref AVIWriterImports.WAVEFORMATEX wfex)
|
public void PopulateWAVEFORMATEX(ref AVIWriterImports.WAVEFORMATEX wfex)
|
||||||
{
|
{
|
||||||
const int WAVE_FORMAT_PCM = 1;
|
const int WAVE_FORMAT_PCM = 1;
|
||||||
int bytes = 0;
|
var bytes = a_bits switch
|
||||||
if (a_bits == 16) bytes = 2;
|
{
|
||||||
else if (a_bits == 8) bytes = 1;
|
16 => 2,
|
||||||
else throw new InvalidOperationException($"only 8/16 bits audio are supported by {nameof(AviWriter)} and you chose: {a_bits}");
|
8 => 1,
|
||||||
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}");
|
_ => 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.Init();
|
||||||
wfex.nBlockAlign = (ushort)(bytes * a_channels);
|
wfex.nBlockAlign = (ushort)(bytes * a_channels);
|
||||||
wfex.nChannels = (ushort)a_channels;
|
wfex.nChannels = (ushort)a_channels;
|
||||||
|
@ -373,12 +377,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetVideoParameters(int width, int height)
|
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;
|
_parameters.width = width;
|
||||||
|
|
||||||
change |= _parameters.height != height;
|
|
||||||
_parameters.height = height;
|
_parameters.height = height;
|
||||||
|
|
||||||
if (change)
|
if (change)
|
||||||
|
@ -392,18 +394,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetAudioParameters(int sampleRate, int channels, int bits)
|
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;
|
_parameters.a_samplerate = sampleRate;
|
||||||
|
|
||||||
change |= _parameters.a_channels != channels;
|
|
||||||
_parameters.a_channels = channels;
|
_parameters.a_channels = channels;
|
||||||
|
|
||||||
change |= _parameters.a_bits != bits;
|
|
||||||
_parameters.a_bits = bits;
|
_parameters.a_bits = bits;
|
||||||
|
|
||||||
change |= _parameters.has_audio != true;
|
|
||||||
_parameters.has_audio = true;
|
_parameters.has_audio = true;
|
||||||
|
|
||||||
if (change)
|
if (change)
|
||||||
|
@ -414,24 +412,31 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
public class CodecToken : IDisposable
|
public class CodecToken : IDisposable
|
||||||
{
|
{
|
||||||
public void Dispose() { }
|
public void Dispose()
|
||||||
private CodecToken() { }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodecToken()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private AVIWriterImports.AVICOMPRESSOPTIONS _comprOptions;
|
private AVIWriterImports.AVICOMPRESSOPTIONS _comprOptions;
|
||||||
public string codec;
|
public string codec;
|
||||||
public byte[] Format = Array.Empty<byte>();
|
public byte[] Format = Array.Empty<byte>();
|
||||||
public byte[] Parms = 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);
|
chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
|
||||||
if (!char.IsLetterOrDigit(chs[i]))
|
if (!char.IsLetterOrDigit(chs[i]))
|
||||||
chs[i] = ' ';
|
chs[i] = ' ';
|
||||||
}
|
}
|
||||||
return new string(chs);
|
|
||||||
|
return new(chs, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts)
|
public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts)
|
||||||
|
@ -461,27 +466,36 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
#if false // test: increase stability by never freeing anything, ever
|
#if false // test: increase stability by never freeing anything, ever
|
||||||
if (opts.lpParms != IntPtr.Zero) Win32Imports.HeapFree(Win32Imports.GetProcessHeap(), 0, opts.lpParms);
|
if (opts.lpParms != IntPtr.Zero)
|
||||||
if (opts.lpFormat != IntPtr.Zero) Win32Imports.HeapFree(Win32Imports.GetProcessHeap(), 0, opts.lpFormat);
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, opts.lpParms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.lpFormat != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, opts.lpFormat);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if AVI_SUPPORT
|
#if AVI_SUPPORT
|
||||||
opts.lpParms = IntPtr.Zero;
|
opts.lpParms = IntPtr.Zero;
|
||||||
opts.lpFormat = 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;
|
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()
|
private byte[] SerializeToByteArray()
|
||||||
|
@ -511,7 +525,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
var m = new MemoryStream(data, false);
|
var m = new MemoryStream(data, false);
|
||||||
var b = new BinaryReader(m);
|
var b = new BinaryReader(m);
|
||||||
|
|
||||||
AVIWriterImports.AVICOMPRESSOPTIONS comprOptions = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS);
|
||||||
|
|
||||||
byte[] format;
|
byte[] format;
|
||||||
byte[] parms;
|
byte[] parms;
|
||||||
|
@ -544,25 +558,20 @@ namespace BizHawk.Client.EmuHawk
|
||||||
b.Close();
|
b.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = new CodecToken
|
return new()
|
||||||
{
|
{
|
||||||
_comprOptions = comprOptions,
|
_comprOptions = comprOptions,
|
||||||
Format = format,
|
Format = format,
|
||||||
Parms = parms,
|
Parms = parms,
|
||||||
codec = Decode_mmioFOURCC(comprOptions.fccHandler)
|
codec = Decode_mmioFOURCC(comprOptions.fccHandler)
|
||||||
};
|
};
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Serialize()
|
public string Serialize()
|
||||||
{
|
=> Convert.ToBase64String(SerializeToByteArray());
|
||||||
return Convert.ToBase64String(SerializeToByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CodecToken DeSerialize(string s)
|
public static CodecToken DeSerialize(string s)
|
||||||
{
|
=> DeSerializeFromByteArray(Convert.FromBase64String(s));
|
||||||
return DeSerializeFromByteArray(Convert.FromBase64String(s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -573,21 +582,15 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe class AviWriterSegment : IDisposable
|
private class AviWriterSegment : IDisposable
|
||||||
{
|
{
|
||||||
static AviWriterSegment()
|
static AviWriterSegment()
|
||||||
{
|
{
|
||||||
AVIWriterImports.AVIFileInit();
|
AVIWriterImports.AVIFileInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AviWriterSegment()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
=> CloseFile();
|
||||||
CloseFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CodecToken _currVideoCodecToken;
|
private CodecToken _currVideoCodecToken;
|
||||||
private bool _isOpen;
|
private bool _isOpen;
|
||||||
|
@ -631,19 +634,15 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private OutputStatus _outStatus;
|
private OutputStatus _outStatus;
|
||||||
|
|
||||||
public long GetLengthApproximation()
|
public long GetLengthApproximation()
|
||||||
{
|
=> _outStatus.video_bytes + _outStatus.audio_bytes;
|
||||||
return _outStatus.video_bytes + _outStatus.audio_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool FAILED(int hr) => hr < 0;
|
private static unsafe int AVISaveOptions(IntPtr stream, ref AVIWriterImports.AVICOMPRESSOPTIONS opts, IntPtr owner)
|
||||||
|
|
||||||
private static int AVISaveOptions(IntPtr stream, ref AVIWriterImports.AVICOMPRESSOPTIONS opts, IntPtr owner)
|
|
||||||
{
|
{
|
||||||
fixed (AVIWriterImports.AVICOMPRESSOPTIONS* _popts = &opts)
|
fixed (AVIWriterImports.AVICOMPRESSOPTIONS* _popts = &opts)
|
||||||
{
|
{
|
||||||
IntPtr* pStream = &stream;
|
var pStream = &stream;
|
||||||
AVIWriterImports.AVICOMPRESSOPTIONS* popts = _popts;
|
var popts = _popts;
|
||||||
AVIWriterImports.AVICOMPRESSOPTIONS** ppopts = &popts;
|
var ppopts = &popts;
|
||||||
return AVIWriterImports.AVISaveOptions(owner, 0, 1, pStream, ppopts);
|
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)
|
public void OpenFile(string destPath, Parameters parameters, CodecToken videoCodecToken)
|
||||||
{
|
{
|
||||||
static int mmioFOURCC(string str) => (
|
static int mmioFOURCC(string str) => (
|
||||||
(byte)(str[0])
|
(byte)str[0] |
|
||||||
| ((byte)(str[1]) << 8)
|
((byte)str[1] << 8) |
|
||||||
| ((byte)(str[2]) << 16)
|
((byte)str[2] << 16) |
|
||||||
| ((byte)(str[3]) << 24)
|
((byte)str[3] << 24)
|
||||||
);
|
);
|
||||||
|
|
||||||
this._parameters = parameters;
|
this._parameters = parameters;
|
||||||
|
@ -671,28 +670,34 @@ namespace BizHawk.Client.EmuHawk
|
||||||
File.Delete(destPath);
|
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
|
// initialize the video stream
|
||||||
AVIWriterImports.AVISTREAMINFOW vidstream_header = new AVIWriterImports.AVISTREAMINFOW();
|
var vidstream_header = default(AVIWriterImports.AVISTREAMINFOW);
|
||||||
AVIWriterImports.BITMAPINFOHEADER bmih = new AVIWriterImports.BITMAPINFOHEADER();
|
var bmih = default(AVIWriterImports.BITMAPINFOHEADER);
|
||||||
parameters.PopulateBITMAPINFOHEADER24(ref bmih);
|
parameters.PopulateBITMAPINFOHEADER24(ref bmih);
|
||||||
vidstream_header.fccType = mmioFOURCC("vids");
|
vidstream_header.fccType = mmioFOURCC("vids");
|
||||||
vidstream_header.dwRate = parameters.fps;
|
vidstream_header.dwRate = parameters.fps;
|
||||||
vidstream_header.dwScale = parameters.fps_scale;
|
vidstream_header.dwScale = parameters.fps_scale;
|
||||||
vidstream_header.dwSuggestedBufferSize = (int)bmih.biSizeImage;
|
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();
|
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
|
// initialize audio stream
|
||||||
AVIWriterImports.AVISTREAMINFOW audstream_header = new AVIWriterImports.AVISTREAMINFOW();
|
var audstream_header = default(AVIWriterImports.AVISTREAMINFOW);
|
||||||
AVIWriterImports.WAVEFORMATEX wfex = new AVIWriterImports.WAVEFORMATEX();
|
var wfex = default(AVIWriterImports.WAVEFORMATEX);
|
||||||
parameters.PopulateWAVEFORMATEX(ref wfex);
|
parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||||
audstream_header.fccType = mmioFOURCC("auds");
|
audstream_header.fccType = mmioFOURCC("auds");
|
||||||
audstream_header.dwQuality = -1;
|
audstream_header.dwQuality = -1;
|
||||||
|
@ -700,13 +705,16 @@ namespace BizHawk.Client.EmuHawk
|
||||||
audstream_header.dwRate = (int)wfex.nAvgBytesPerSec;
|
audstream_header.dwRate = (int)wfex.nAvgBytesPerSec;
|
||||||
audstream_header.dwSampleSize = wfex.nBlockAlign;
|
audstream_header.dwSampleSize = wfex.nBlockAlign;
|
||||||
audstream_header.dwInitialFrames = 1; // ??? optimal value?
|
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();
|
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;
|
_isOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,11 +734,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
|
|
||||||
// encoder params
|
// encoder params
|
||||||
AVIWriterImports.AVICOMPRESSOPTIONS comprOptions = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS);
|
||||||
_currVideoCodecToken?.AllocateToAVICOMPRESSOPTIONS(ref comprOptions);
|
_currVideoCodecToken?.AllocateToAVICOMPRESSOPTIONS(out comprOptions);
|
||||||
|
|
||||||
bool result = AVISaveOptions(_pAviRawVideoStream, ref comprOptions, hwnd) != 0;
|
var result = AVISaveOptions(_pAviRawVideoStream, ref comprOptions, hwnd) != 0;
|
||||||
CodecToken ret = CodecToken.CreateFromAVICOMPRESSOPTIONS(ref comprOptions);
|
var ret = CodecToken.CreateFromAVICOMPRESSOPTIONS(ref comprOptions);
|
||||||
|
|
||||||
// so, AVISaveOptions may have changed some of the pointers
|
// 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
|
// 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.
|
// guess what? doesn't matter. We'll free them all ourselves.
|
||||||
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref comprOptions);
|
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref comprOptions);
|
||||||
|
|
||||||
if (result)
|
return result ? ret : null;
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>begin recording</summary>
|
/// <summary>begin recording</summary>
|
||||||
|
@ -759,19 +762,19 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
|
|
||||||
// open compressed video stream
|
// open compressed video stream
|
||||||
AVIWriterImports.AVICOMPRESSOPTIONS opts = new AVIWriterImports.AVICOMPRESSOPTIONS();
|
_currVideoCodecToken.AllocateToAVICOMPRESSOPTIONS(out var opts);
|
||||||
_currVideoCodecToken.AllocateToAVICOMPRESSOPTIONS(ref opts);
|
|
||||||
bool failed = FAILED(AVIWriterImports.AVIMakeCompressedStream(out _pAviCompressedVideoStream, _pAviRawVideoStream, ref opts, IntPtr.Zero));
|
var hr = AVIWriterImports.AVIMakeCompressedStream(out _pAviCompressedVideoStream, _pAviRawVideoStream, ref opts, IntPtr.Zero);
|
||||||
|
var hrEx = Marshal.GetExceptionForHR(hr);
|
||||||
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref opts);
|
CodecToken.DeallocateAVICOMPRESSOPTIONS(ref opts);
|
||||||
|
if (hrEx != null)
|
||||||
if (failed)
|
|
||||||
{
|
{
|
||||||
CloseStreams();
|
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
|
// set the compressed video stream input format
|
||||||
AVIWriterImports.BITMAPINFOHEADER bmih = new AVIWriterImports.BITMAPINFOHEADER();
|
var bmih = default(AVIWriterImports.BITMAPINFOHEADER);
|
||||||
if (_bit32)
|
if (_bit32)
|
||||||
{
|
{
|
||||||
_parameters.PopulateBITMAPINFOHEADER32(ref bmih);
|
_parameters.PopulateBITMAPINFOHEADER32(ref bmih);
|
||||||
|
@ -781,17 +784,22 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_parameters.PopulateBITMAPINFOHEADER24(ref bmih);
|
_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
|
_bit32 = true; // we'll try again
|
||||||
CloseStreams();
|
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
|
// set audio stream input format
|
||||||
AVIWriterImports.WAVEFORMATEX wfex = new AVIWriterImports.WAVEFORMATEX();
|
var wfex = default(AVIWriterImports.WAVEFORMATEX);
|
||||||
_parameters.PopulateWAVEFORMATEX(ref wfex);
|
_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();
|
CloseStreams();
|
||||||
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
||||||
|
@ -833,7 +841,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// end recording
|
/// end recording
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CloseStreams()
|
private void CloseStreams()
|
||||||
{
|
{
|
||||||
if (_pAviRawAudioStream != IntPtr.Zero)
|
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
|
// 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;
|
var todo = samples.Count;
|
||||||
int idx = 0;
|
var idx = 0;
|
||||||
while (todo > 0)
|
while (todo > 0)
|
||||||
{
|
{
|
||||||
int remain = OutputStatus.AUDIO_SEGMENT_SIZE - _outStatus.audio_buffered_shorts;
|
var remain = OutputStatus.AUDIO_SEGMENT_SIZE - _outStatus.audio_buffered_shorts;
|
||||||
int chunk = Math.Min(remain, todo);
|
var chunk = Math.Min(remain, todo);
|
||||||
for (int i = 0; i < chunk; i++)
|
|
||||||
|
for (var i = 0; i < chunk; i++)
|
||||||
{
|
{
|
||||||
_outStatus.BufferedShorts[_outStatus.audio_buffered_shorts++] = samples[idx++];
|
_outStatus.BufferedShorts[_outStatus.audio_buffered_shorts++] = samples[idx++];
|
||||||
}
|
}
|
||||||
|
|
||||||
todo -= chunk;
|
todo -= chunk;
|
||||||
|
|
||||||
if (_outStatus.audio_buffered_shorts == OutputStatus.AUDIO_SEGMENT_SIZE)
|
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;
|
var todo = _outStatus.audio_buffered_shorts;
|
||||||
int todo_realsamples = todo / 2;
|
var todo_realsamples = todo / 2;
|
||||||
IntPtr buf = GetStaticGlobalBuf(todo * 2);
|
var buf = GetStaticGlobalBuf(todo * 2);
|
||||||
|
|
||||||
short* sptr = (short*)buf.ToPointer();
|
var sptr = (short*)buf.ToPointer();
|
||||||
for (int i = 0; i < todo; i++)
|
for (var i = 0; i < todo; i++)
|
||||||
{
|
{
|
||||||
sptr[i] = _outStatus.BufferedShorts[i];
|
sptr[i] = _outStatus.BufferedShorts[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// (TODO - inefficient- build directly in a buffer)
|
// (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_samples += todo_realsamples;
|
||||||
_outStatus.audio_bytes += bytes_written;
|
_outStatus.audio_bytes += bytes_written;
|
||||||
_outStatus.audio_buffered_shorts = 0;
|
_outStatus.audio_buffered_shorts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException">attempted frame resize during encoding</exception>
|
/// <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;
|
const int AVIIF_KEYFRAME = 0x00000010;
|
||||||
|
|
||||||
|
@ -897,40 +908,42 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|| _parameters.height != source.BufferHeight)
|
|| _parameters.height != source.BufferHeight)
|
||||||
throw new InvalidOperationException("video buffer changed between start and now");
|
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;
|
var todo = _parameters.pitch * _parameters.height;
|
||||||
int w = source.BufferWidth;
|
var w = source.BufferWidth;
|
||||||
int h = source.BufferHeight;
|
var h = source.BufferHeight;
|
||||||
|
|
||||||
if (!_bit32)
|
if (!_bit32)
|
||||||
{
|
{
|
||||||
IntPtr buf = GetStaticGlobalBuf(todo);
|
var buf = GetStaticGlobalBuf(todo);
|
||||||
|
|
||||||
// TODO - would using a byte* be faster?
|
// TODO - would using a byte* be faster?
|
||||||
int[] buffer = source.GetVideoBuffer();
|
var buffer = source.GetVideoBuffer();
|
||||||
fixed (int* buffer_ptr = &buffer[0])
|
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 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;
|
var r = (buffer_ptr[idx] >> 0) & 0xFF;
|
||||||
int g = (buffer[idx] >> 8) & 0xFF;
|
var g = (buffer_ptr[idx] >> 8) & 0xFF;
|
||||||
int b = (buffer[idx] >> 16) & 0xFF;
|
var b = (buffer_ptr[idx] >> 16) & 0xFF;
|
||||||
*bp++ = (byte)r;
|
*bp++ = (byte)r;
|
||||||
*bp++ = (byte)g;
|
*bp++ = (byte)g;
|
||||||
*bp++ = (byte)b;
|
*bp++ = (byte)b;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx -= w * 2;
|
idx -= w * 2;
|
||||||
bp += pitch_add;
|
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_bytes += bytes_written;
|
||||||
_outStatus.video_frames++;
|
_outStatus.video_frames++;
|
||||||
}
|
}
|
||||||
|
@ -938,30 +951,32 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
else // 32 bit
|
else // 32 bit
|
||||||
{
|
{
|
||||||
IntPtr buf = GetStaticGlobalBuf(todo * 4);
|
var buf = GetStaticGlobalBuf(todo * 4);
|
||||||
int[] buffer = source.GetVideoBuffer();
|
var buffer = source.GetVideoBuffer();
|
||||||
fixed (int* buffer_ptr = &buffer[0])
|
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 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;
|
var r = (buffer_ptr[idx] >> 0) & 0xFF;
|
||||||
int g = (buffer[idx] >> 8) & 0xFF;
|
var g = (buffer_ptr[idx] >> 8) & 0xFF;
|
||||||
int b = (buffer[idx] >> 16) & 0xFF;
|
var b = (buffer_ptr[idx] >> 16) & 0xFF;
|
||||||
*bp++ = (byte)r;
|
*bp++ = (byte)r;
|
||||||
*bp++ = (byte)g;
|
*bp++ = (byte)g;
|
||||||
*bp++ = (byte)b;
|
*bp++ = (byte)b;
|
||||||
*bp++ = 0;
|
*bp++ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx -= w * 2;
|
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_bytes += bytes_written;
|
||||||
_outStatus.video_frames++;
|
_outStatus.video_frames++;
|
||||||
}
|
}
|
||||||
|
@ -974,13 +989,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public void SetDefaultVideoCodecToken(Config config)
|
public void SetDefaultVideoCodecToken(Config config)
|
||||||
{
|
{
|
||||||
var ct = CodecToken.DeSerialize(config.AviCodecToken);
|
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()
|
public string DesiredExtension()
|
||||||
{
|
=> "avi";
|
||||||
return "avi";
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UsesAudio => _parameters.has_audio;
|
public bool UsesAudio => _parameters.has_audio;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ using System.Windows.Forms;
|
||||||
|
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
|
|
||||||
|
using static BizHawk.Common.ShlobjImports;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -21,8 +23,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public sealed class FolderBrowserEx : Component
|
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 const BROWSEINFO.FLAGS BrowseOptions = BROWSEINFO.FLAGS.RestrictToFilesystem | BROWSEINFO.FLAGS.RestrictToDomain |
|
||||||
private readonly Win32Imports.BROWSEINFO.FLAGS publicOptions = Win32Imports.BROWSEINFO.FLAGS.RestrictToFilesystem | Win32Imports.BROWSEINFO.FLAGS.RestrictToDomain;
|
BROWSEINFO.FLAGS.NewDialogStyle | BROWSEINFO.FLAGS.ShowTextBox;
|
||||||
|
|
||||||
public string Description = "Please select a folder below:";
|
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>
|
/// <summary>Shows the folder browser dialog box with the specified owner window.</summary>
|
||||||
public DialogResult ShowDialog(IWin32Window owner = null)
|
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
|
const int startLocation = 0; // = Desktop CSIDL
|
||||||
int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData)
|
int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData)
|
||||||
{
|
{
|
||||||
if (uMsg == 1)
|
if (uMsg == BFFM_INITIALIZED)
|
||||||
{
|
{
|
||||||
var str = Marshal.StringToHGlobalUni(SelectedPath);
|
var str = Marshal.StringToHGlobalUni(SelectedPath);
|
||||||
Win32Imports.SendMessage(hwnd, 0x400 + 103, (IntPtr) 1, str);
|
try
|
||||||
Marshal.FreeHGlobal(str);
|
{
|
||||||
|
WmImports.SendMessage(hwnd, BFFM_SETSELECTIONW, new(1), str);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hWndOwner = owner?.Handle ?? Win32Imports.GetActiveWindow();
|
var hWndOwner = owner?.Handle ?? WmImports.GetActiveWindow();
|
||||||
_ = Win32Imports.SHGetSpecialFolderLocation(hWndOwner, startLocation, out var pidlRoot);
|
_ = SHGetSpecialFolderLocation(hWndOwner, startLocation, out var pidlRoot);
|
||||||
if (pidlRoot == IntPtr.Zero) return DialogResult.Cancel;
|
if (pidlRoot == IntPtr.Zero)
|
||||||
var mergedOptions = publicOptions | privateOptions;
|
|
||||||
if ((mergedOptions & Win32Imports.BROWSEINFO.FLAGS.NewDialogStyle) != 0 && ApartmentState.MTA == Application.OleRequired())
|
|
||||||
{
|
{
|
||||||
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
|
try
|
||||||
{
|
{
|
||||||
var buffer = Marshal.AllocHGlobal(Win32Imports.MAX_PATH);
|
var buffer = Marshal.AllocHGlobal(Win32Imports.MAX_PATH);
|
||||||
var bi = new Win32Imports.BROWSEINFO
|
var bi = new BROWSEINFO
|
||||||
{
|
{
|
||||||
hwndOwner = hWndOwner,
|
hwndOwner = hWndOwner,
|
||||||
pidlRoot = pidlRoot,
|
pidlRoot = pidlRoot,
|
||||||
pszDisplayName = buffer,
|
pszDisplayName = buffer,
|
||||||
lpszTitle = Description,
|
lpszTitle = Description,
|
||||||
ulFlags = mergedOptions,
|
ulFlags = browseOptions,
|
||||||
lpfn = Callback
|
lpfn = Callback
|
||||||
};
|
};
|
||||||
pidlRet = Win32Imports.SHBrowseForFolder(ref bi);
|
|
||||||
|
pidlRet = SHBrowseForFolder(ref bi);
|
||||||
Marshal.FreeHGlobal(buffer);
|
Marshal.FreeHGlobal(buffer);
|
||||||
if (pidlRet == IntPtr.Zero) return DialogResult.Cancel; // user clicked Cancel
|
if (pidlRet == IntPtr.Zero)
|
||||||
var sb = new StringBuilder(Win32Imports.MAX_PATH);
|
{
|
||||||
if (Win32Imports.SHGetPathFromIDList(pidlRet, sb) == 0) return DialogResult.Cancel;
|
return DialogResult.Cancel; // user clicked Cancel
|
||||||
SelectedPath = sb.ToString();
|
}
|
||||||
|
|
||||||
|
var path = new StringBuilder(Win32Imports.MAX_PATH);
|
||||||
|
if (SHGetPathFromIDList(pidlRet, path) == 0)
|
||||||
|
{
|
||||||
|
return DialogResult.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedPath = path.ToString();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_ = Win32Imports.SHGetMalloc(out var malloc);
|
_ = SHGetMalloc(out var malloc);
|
||||||
malloc.Free(pidlRoot);
|
malloc.Free(pidlRoot);
|
||||||
if (pidlRet != IntPtr.Zero) malloc.Free(pidlRet);
|
|
||||||
|
if (pidlRet != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
malloc.Free(pidlRet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DialogResult.OK;
|
return DialogResult.OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
protected override void OnMouseClick(MouseEventArgs e)
|
protected override void OnMouseClick(MouseEventArgs e)
|
||||||
{
|
{
|
||||||
if (!OSTailoredCode.IsUnixHost) Win32Imports.HideCaret(Handle);
|
if (!OSTailoredCode.IsUnixHost)
|
||||||
|
{
|
||||||
|
WmImports.HideCaret(Handle);
|
||||||
|
}
|
||||||
|
|
||||||
base.OnMouseClick(e);
|
base.OnMouseClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +257,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
protected override void OnGotFocus(EventArgs e)
|
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)
|
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||||
|
|
|
@ -10,11 +10,14 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
using BizHawk.Client.Common;
|
using BizHawk.Client.Common;
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Common.ReflectionExtensions;
|
using BizHawk.Common.ReflectionExtensions;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
using static BizHawk.Common.CommctrlImports;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
public static class ControlExtensions
|
public static class ControlExtensions
|
||||||
|
@ -183,32 +186,42 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// <exception cref="Win32Exception">unmanaged call failed</exception>
|
/// <exception cref="Win32Exception">unmanaged call failed</exception>
|
||||||
public static void SetSortIcon(this ListView listViewControl, int columnIndex, SortOrder order)
|
public static void SetSortIcon(this ListView listViewControl, int columnIndex, SortOrder order)
|
||||||
{
|
{
|
||||||
if (OSTailoredCode.IsUnixHost) return;
|
if (OSTailoredCode.IsUnixHost)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const int LVM_GETHEADER = 4127;
|
var columnHeader = WmImports.SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
|
||||||
const int HDM_GETITEM = 4619;
|
|
||||||
const int HDM_SETITEM = 4620;
|
|
||||||
var columnHeader = Win32Imports.SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
|
|
||||||
for (int columnNumber = 0, l = listViewControl.Columns.Count; columnNumber < l; columnNumber++)
|
for (int columnNumber = 0, l = listViewControl.Columns.Count; columnNumber < l; columnNumber++)
|
||||||
{
|
{
|
||||||
var columnPtr = new IntPtr(columnNumber);
|
var columnPtr = new IntPtr(columnNumber);
|
||||||
var item = new Win32Imports.HDITEM { mask = Win32Imports.HDITEM.Mask.Format };
|
var item = new HDITEM { mask = HDITEM.Mask.Format };
|
||||||
if (Win32Imports.SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero) throw new Win32Exception();
|
if (SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new Win32Exception();
|
||||||
|
}
|
||||||
|
|
||||||
if (columnNumber != columnIndex || order == SortOrder.None)
|
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;
|
case SortOrder.Ascending:
|
||||||
item.fmt |= Win32Imports.HDITEM.Format.SortUp;
|
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;
|
throw new Win32Exception();
|
||||||
item.fmt |= Win32Imports.HDITEM.Format.SortDown;
|
|
||||||
}
|
}
|
||||||
if (Win32Imports.SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero) throw new Win32Exception();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -868,7 +868,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
groupFreeze.SuspendLayout();
|
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;
|
var tp = tabctrlDetails.SelectedTab;
|
||||||
|
|
||||||
|
@ -886,7 +886,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
groupFreeze.ResumeLayout();
|
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();
|
groupFreeze.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace BizHawk.Common.PathExtensions
|
||||||
if (File.Exists(path1.SubstringBefore('|'))) return FileAttributes.Normal;
|
if (File.Exists(path1.SubstringBefore('|'))) return FileAttributes.Normal;
|
||||||
throw new FileNotFoundException();
|
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))
|
return Win32Imports.PathRelativePathTo(path, fromPath, GetPathAttribute(fromPath), toPath, GetPathAttribute(toPath))
|
||||||
? path.ToString()
|
? path.ToString()
|
||||||
: throw new ArgumentException(message: "Paths must have a common prefix", paramName: nameof(toPath));
|
: throw new ArgumentException(message: "Paths must have a common prefix", paramName: nameof(toPath));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using static BizHawk.Common.Kernel32Imports;
|
using static BizHawk.Common.MemoryApiImports;
|
||||||
using static BizHawk.Common.MemoryBlock;
|
using static BizHawk.Common.MemoryBlock;
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
public static class AVIWriterImports
|
public static class AVIWriterImports
|
||||||
|
@ -35,7 +37,8 @@ namespace BizHawk.Common
|
||||||
public RECT rcFrame;
|
public RECT rcFrame;
|
||||||
public int dwEditCount;
|
public int dwEditCount;
|
||||||
public int dwFormatChangeCount;
|
public int dwFormatChangeCount;
|
||||||
[MarshalAs(UnmanagedType.LPWStr, SizeConst=64)] public string szName;
|
[MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
|
||||||
|
public string szName;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct RECT
|
public struct RECT
|
||||||
|
@ -63,9 +66,7 @@ namespace BizHawk.Common
|
||||||
public uint biClrImportant;
|
public uint biClrImportant;
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
=> biSize = (uint)Marshal.SizeOf(this);
|
||||||
biSize = (uint)Marshal.SizeOf(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
@ -96,9 +97,7 @@ namespace BizHawk.Common
|
||||||
public ushort cbSize;
|
public ushort cbSize;
|
||||||
|
|
||||||
public void Init()
|
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>
|
/// <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;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BizHawk.Common
|
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>
|
/// <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", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||||
|
private static extern bool SetCurrentDirectoryW(string lpPathName);
|
||||||
[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);
|
|
||||||
|
|
||||||
public static bool Set(string newCWD)
|
public static bool Set(string newCWD)
|
||||||
{
|
=> SetCurrentDirectoryW(newCWD);
|
||||||
fixed (byte* pstr = &Encoding.Unicode.GetBytes($"{newCWD}\0")[0])
|
|
||||||
return SetCurrentDirectoryW(pstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Get()
|
public static unsafe string Get()
|
||||||
{
|
{
|
||||||
uint result;
|
static Exception GetExceptionForFailure()
|
||||||
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 ex = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||||
var buf = new byte[result];
|
?? new("Marshal.GetExceptionForHR returned null?");
|
||||||
uint result1;
|
return new InvalidOperationException("GetCurrentDirectoryW returned 0!", ex);
|
||||||
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();
|
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;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
public static class Kernel32Imports
|
public static class MemoryApiImports
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum AllocationType : uint
|
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>
|
/// <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 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.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
public static class ShellLinkImports
|
public static class ShellLinkImports
|
||||||
|
@ -19,15 +21,21 @@ namespace BizHawk.Common
|
||||||
public uint nFileSizeLow;
|
public uint nFileSizeLow;
|
||||||
public uint dwReserved0;
|
public uint dwReserved0;
|
||||||
public uint dwReserved1;
|
public uint dwReserved1;
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName;
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName;
|
public string cFileName;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
|
||||||
|
public string cAlternateFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum SLGP_FLAGS {}
|
public enum SLGP_FLAGS
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum SLR_FLAGS {}
|
public enum SLR_FLAGS
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
||||||
[ComImport]
|
[ComImport]
|
||||||
|
@ -108,6 +116,8 @@ namespace BizHawk.Common
|
||||||
/// <remarks>CLSID_ShellLink from ShlGuid.h</remarks>
|
/// <remarks>CLSID_ShellLink from ShlGuid.h</remarks>
|
||||||
[ComImport]
|
[ComImport]
|
||||||
[Guid("00021401-0000-0000-C000-000000000046")]
|
[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 QS_ALLINPUT = 0x4FFU;
|
||||||
public const uint MWMO_INPUTAVAILABLE = 0x0004U;
|
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)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
public static extern uint MsgWaitForMultipleObjectsEx(uint nCount, IntPtr[] pHandles, uint dwMilliseconds, uint dwWakeMask, uint dwFlags);
|
public static extern uint MsgWaitForMultipleObjectsEx(uint nCount, IntPtr[] pHandles, uint dwMilliseconds, uint dwWakeMask, uint dwFlags);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||||
[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)]
|
|
||||||
public static extern int WaitForSingleObject(SafeWaitHandle handle, uint milliseconds);
|
public static extern int WaitForSingleObject(SafeWaitHandle handle, uint milliseconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,227 +7,29 @@ using System.Text;
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is more just an assorted bunch of Win32 functions
|
||||||
|
/// </summary>
|
||||||
public static class Win32Imports
|
public static class Win32Imports
|
||||||
{
|
{
|
||||||
public const int MAX_PATH = 260;
|
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);
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
|
|
||||||
[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)]
|
|
||||||
public static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
|
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)]
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
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)]
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
|
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)]
|
[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);
|
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)]
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
public static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
|
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);
|
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.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
public class Win32ShellContextMenu
|
public class Win32ShellContextMenu
|
||||||
|
@ -117,6 +120,7 @@ namespace BizHawk.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CA1069
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum TPM
|
public enum TPM
|
||||||
{
|
{
|
||||||
|
@ -140,6 +144,7 @@ namespace BizHawk.Common
|
||||||
TPM_NOANIMATION = 0x4000,
|
TPM_NOANIMATION = 0x4000,
|
||||||
TPM_LAYOUTRTL = 0x8000,
|
TPM_LAYOUTRTL = 0x8000,
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1069
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum CMF : uint
|
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() };
|
IntPtr[] waitHandles = { handle.SafeWaitHandle.DangerousGetHandle() };
|
||||||
const uint count = 1;
|
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???
|
QS_MASK = 0; //bizhawk edit?? did we need any messages here?? apparently not???
|
||||||
uint nativeResult;
|
uint nativeResult;
|
||||||
ThreadHacks.MSG msg;
|
WmImports.MSG msg;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// MsgWaitForMultipleObjectsEx with MWMO_INPUTAVAILABLE returns,
|
// 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);
|
nativeResult = ThreadHacks.MsgWaitForMultipleObjectsEx(count, waitHandles, 0xFFFFFFFF, QS_MASK, ThreadHacks.MWMO_INPUTAVAILABLE);
|
||||||
if (IsNativeWaitSuccessful(count, nativeResult, out int managedResult) || WaitHandle.WaitTimeout == managedResult) break;
|
if (IsNativeWaitSuccessful(count, nativeResult, out int managedResult) || WaitHandle.WaitTimeout == managedResult) break;
|
||||||
// there is a message, pump and dispatch it
|
// 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);
|
WmImports.TranslateMessage(ref msg);
|
||||||
ThreadHacks.DispatchMessage(ref msg);
|
WmImports.DispatchMessage(ref msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle.WaitOne();
|
// handle.WaitOne();
|
||||||
|
|
Loading…
Reference in New Issue