organize and cleanup a ton of win32 imports, do cleanup elsewhere with that

This commit is contained in:
CasualPokePlayer 2023-10-28 05:04:41 -07:00
parent b4a6b06fba
commit 686119c7dd
22 changed files with 654 additions and 517 deletions

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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));

View File

@ -1,6 +1,6 @@
using System;
using static BizHawk.Common.Kernel32Imports;
using static BizHawk.Common.MemoryApiImports;
using static BizHawk.Common.MemoryBlock;
namespace BizHawk.Common

View File

@ -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>

View File

@ -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);
}
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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");
}
}

View File

@ -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 */
{
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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();