From ff2efca658d3b582b984fedfcc04194a975442bc Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Thu, 23 Jan 2020 10:22:54 +1000 Subject: [PATCH] Cleanup Util --- .../movie/import/Fm2Import.cs | 2 +- BizHawk.Client.Common/rewind/Rewinder.cs | 33 +- .../Api/Libraries/ToolApi.cs | 4 +- BizHawk.Common/BizHawk.Common.csproj | 1 + BizHawk.Common/Serializer.cs | 76 +- BizHawk.Common/SuperGloballyUniqueID.cs | 19 + BizHawk.Common/Util.cs | 762 +++++++----------- .../Consoles/Atari/2600/Mappers/mCM.cs | 4 +- 8 files changed, 378 insertions(+), 523 deletions(-) create mode 100644 BizHawk.Common/SuperGloballyUniqueID.cs diff --git a/BizHawk.Client.Common/movie/import/Fm2Import.cs b/BizHawk.Client.Common/movie/import/Fm2Import.cs index f73a301222..8151af5f0e 100644 --- a/BizHawk.Client.Common/movie/import/Fm2Import.cs +++ b/BizHawk.Client.Common/movie/import/Fm2Import.cs @@ -261,7 +261,7 @@ namespace BizHawk.Client.Common if (blob[0] == '0' && (blob[1] == 'x' || blob[1] == 'X')) { // hex - return Util.HexStringToBytes(blob.Substring(2)); + return blob.Substring(2).HexStringToBytes(); } // base64 diff --git a/BizHawk.Client.Common/rewind/Rewinder.cs b/BizHawk.Client.Common/rewind/Rewinder.cs index 0122e875d1..22fd07ca7b 100644 --- a/BizHawk.Client.Common/rewind/Rewinder.cs +++ b/BizHawk.Client.Common/rewind/Rewinder.cs @@ -1,7 +1,6 @@ using System; using System.IO; -using BizHawk.Common; using BizHawk.Emulation.Common.IEmulatorExtensions; namespace BizHawk.Client.Common @@ -388,4 +387,36 @@ namespace BizHawk.Client.Common } } } + + public static class VLInteger + { + public static void WriteUnsigned(uint value, byte[] data, ref int index) + { + // This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking. + do + { + var x = value & 0x7FU; + value >>= 7; + data[index++] = (byte)((value != 0U ? 0x80U : 0U) | x); + } + while (value != 0U); + } + + public static uint ReadUnsigned(byte[] data, ref int index) + { + // This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking. + var value = 0U; + var shiftCount = 0; + bool isLastByte; // Negating the comparison and moving it earlier in the loop helps a lot on x86 for some reason + do + { + var x = (uint)data[index++]; + isLastByte = (x & 0x80U) == 0U; + value |= (x & 0x7FU) << shiftCount; + shiftCount += 7; + } + while (!isLastByte); + return value; + } + } } diff --git a/BizHawk.Client.EmuHawk/Api/Libraries/ToolApi.cs b/BizHawk.Client.EmuHawk/Api/Libraries/ToolApi.cs index da4447c6a7..6ef258818e 100644 --- a/BizHawk.Client.EmuHawk/Api/Libraries/ToolApi.cs +++ b/BizHawk.Client.EmuHawk/Api/Libraries/ToolApi.cs @@ -10,14 +10,14 @@ namespace BizHawk.Client.EmuHawk { public Type GetTool(string name) { - var toolType = ReflectionUtil.GetTypeByName(name).FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface); + var toolType = Util.GetTypeByName(name).FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface); if (toolType != null) GlobalWin.Tools.Load(toolType); return GlobalWin.Tools.AvailableTools.FirstOrDefault(tool => tool.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); } public object CreateInstance(string name) { - var found = ReflectionUtil.GetTypeByName(name).FirstOrDefault(); + var found = Util.GetTypeByName(name).FirstOrDefault(); return found != null ? Activator.CreateInstance(found) : null; } diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj index 2b70b478f7..eb5504e9b8 100644 --- a/BizHawk.Common/BizHawk.Common.csproj +++ b/BizHawk.Common/BizHawk.Common.csproj @@ -14,5 +14,6 @@ + diff --git a/BizHawk.Common/Serializer.cs b/BizHawk.Common/Serializer.cs index c25694f1ce..0c40d102fe 100644 --- a/BizHawk.Common/Serializer.cs +++ b/BizHawk.Common/Serializer.cs @@ -184,11 +184,11 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ReadByteBuffer(_br, useNull); + val = _br.ReadByteBuffer(useNull); } else { - Util.WriteByteBuffer(_bw, val); + _bw.WriteByteBuffer(val); } } @@ -198,7 +198,7 @@ namespace BizHawk.Common { if (Present(name)) { - val = Util.HexStringToBytes(Item(name)); + val = Item(name).HexStringToBytes(); } if (val != null && val.Length == 0 && useNull) @@ -221,7 +221,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToBoolBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToBoolBuffer(); if (val == null && !useNull) { val = new bool[0]; @@ -229,7 +229,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.BoolBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -239,8 +239,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToBoolBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToBoolBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -251,7 +251,7 @@ namespace BizHawk.Common else { var temp = val ?? new bool[0]; - _tw.WriteLine("{0} {1}", name, Util.BoolBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } public void Sync(string name, ref short[] val, bool useNull) @@ -262,7 +262,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToShortBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToShortBuffer(); if (val == null && !useNull) { val = new short[0]; @@ -270,7 +270,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.ShortBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -282,7 +282,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToUshortBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToUShortBuffer(); if (val == null && !useNull) { val = new ushort[0]; @@ -290,7 +290,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.UshortBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -300,8 +300,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToShortBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToShortBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -312,7 +312,7 @@ namespace BizHawk.Common else { var temp = val ?? new short[0]; - _tw.WriteLine("{0} {1}", name, Util.ShortBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } @@ -322,8 +322,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToUshortBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToUShortBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -334,7 +334,7 @@ namespace BizHawk.Common else { var temp = val ?? new ushort[0]; - _tw.WriteLine("{0} {1}", name, Util.UshortBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } @@ -346,7 +346,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToIntBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToIntBuffer(); if (val == null && !useNull) { val = new int[0]; @@ -354,7 +354,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.IntBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -364,8 +364,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToIntBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToIntBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -376,7 +376,7 @@ namespace BizHawk.Common else { var temp = val ?? new int[0]; - _tw.WriteLine("{0} {1}", name, Util.IntBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } @@ -388,7 +388,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToUintBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToUIntBuffer(); if (val == null && !useNull) { val = new uint[0]; @@ -396,7 +396,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.UintBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -406,8 +406,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToUintBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToUIntBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -418,7 +418,7 @@ namespace BizHawk.Common else { var temp = val ?? new uint[0]; - _tw.WriteLine("{0} {1}", name, Util.UintBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } @@ -430,7 +430,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToFloatBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToFloatBuffer(); if (val == null && !useNull) { val = new float[0]; @@ -438,7 +438,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.FloatBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -448,8 +448,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToFloatBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToFloatBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -460,7 +460,7 @@ namespace BizHawk.Common else { var temp = val ?? new float[0]; - _tw.WriteLine("{0} {1}", name, Util.FloatBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } @@ -472,7 +472,7 @@ namespace BizHawk.Common } else if (IsReader) { - val = Util.ByteBufferToDoubleBuffer(Util.ReadByteBuffer(_br, false)); + val = _br.ReadByteBuffer(false).ToDoubleBuffer(); if (val == null && !useNull) { val = new double[0]; @@ -480,7 +480,7 @@ namespace BizHawk.Common } else { - Util.WriteByteBuffer(_bw, Util.DoubleBufferToByteBuffer(val)); + _bw.WriteByteBuffer(val.ToUByteBuffer()); } } @@ -490,8 +490,8 @@ namespace BizHawk.Common { if (Present(name)) { - var bytes = Util.HexStringToBytes(Item(name)); - val = Util.ByteBufferToDoubleBuffer(bytes); + var bytes = Item(name).HexStringToBytes(); + val = bytes.ToDoubleBuffer(); } if (val != null && val.Length == 0 && useNull) @@ -502,7 +502,7 @@ namespace BizHawk.Common else { var temp = val ?? new double[0]; - _tw.WriteLine("{0} {1}", name, Util.DoubleBufferToByteBuffer(temp).BytesToHexString()); + _tw.WriteLine("{0} {1}", name, temp.ToUByteBuffer().BytesToHexString()); } } diff --git a/BizHawk.Common/SuperGloballyUniqueID.cs b/BizHawk.Common/SuperGloballyUniqueID.cs new file mode 100644 index 0000000000..97c720bc80 --- /dev/null +++ b/BizHawk.Common/SuperGloballyUniqueID.cs @@ -0,0 +1,19 @@ +namespace BizHawk.Common +{ + using System; + using System.Diagnostics; + + internal static class SuperGloballyUniqueID + { + private static readonly string StaticPart = $"bizhawk-{Process.GetCurrentProcess().Id}-{Guid.NewGuid()}"; + + private static int ctr; + + public static string Next() + { + int myctr; + lock (typeof(SuperGloballyUniqueID)) myctr = ctr++; + return $"{StaticPart}-{myctr}"; + } + } +} diff --git a/BizHawk.Common/Util.cs b/BizHawk.Common/Util.cs index 6ed9e29e3e..a0b779b3a5 100644 --- a/BizHawk.Common/Util.cs +++ b/BizHawk.Common/Util.cs @@ -1,140 +1,325 @@ -#nullable disable - -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Reflection; -using System.Text; - -namespace BizHawk.Common +namespace BizHawk.Common { + using System; + using System.Collections.Generic; + using System.IO; + using System.IO.Compression; + using System.Linq; + using System.Threading; + public static unsafe class Util { public static void CopyStream(Stream src, Stream dest, long len) { const int size = 0x2000; - byte[] buffer = new byte[size]; + var buffer = new byte[size]; while (len > 0) { - long todo = len; - if (len > size) todo = size; - int n = src.Read(buffer, 0, (int)todo); + var todo = Math.Min(len, size); + var n = src.Read(buffer, 0, (int) todo); dest.Write(buffer, 0, n); len -= n; } } - /// - /// Waits 250ms for a file to vanish. Returns whether it succeeded - /// - public static bool TryWaitForFileToVanish(string path) + /// issues with parsing + /// TODO use and instead of using and keeping a reference to the array? --yoshi + public static byte[] DecompressGzipFile(Stream src) { - for (int i = 0; i < 25; i++) // 250ms - { - if (!File.Exists(path)) - { - return true; - } - - System.Threading.Thread.Sleep(10); - } - return false; + var tmp = new byte[4]; + if (src.Read(tmp, 0, 2) != 2) throw new InvalidOperationException("Unexpected end of stream"); + if (tmp[0] != 0x1F || tmp[1] != 0x8B) throw new InvalidOperationException("GZIP header not present"); + src.Seek(-4, SeekOrigin.End); + src.Read(tmp, 0, 4); + src.Seek(0, SeekOrigin.Begin); + using var gs = new GZipStream(src, CompressionMode.Decompress, true); + var data = new byte[BitConverter.ToInt32(tmp, 0)]; + using var ms = new MemoryStream(data); + gs.CopyTo(ms); + return data; } - /// - /// Tries to moves `pathWant` out of the way to `pathBackup`, delaying as needed to accomodate filesystem being sucky. - /// `pathWant` might not be removed after all, in case it's snagged by something. - /// - public static bool TryMoveBackupFile(string pathWant, string pathBackup) + /// adapted from https://stackoverflow.com/a/3928856/7467292, values are compared using EqualityComparer.Default + public static bool DictionaryEqual(IDictionary a, IDictionary b) + where TKey : notnull { - // If the path we want is available we dont actually have to make a backup - if (!File.Exists(pathWant)) + if (a == b) return true; + if (a.Count != b.Count) return false; + var comparer = EqualityComparer.Default; + return a.All(kvp => b.TryGetValue(kvp.Key, out var bVal) && comparer.Equals(kvp.Value, bVal)); + } + + /// in bytes + /// human-readable filesize (converts units up to tebibytes) + public static string FormatFileSize(long filesize) + { + if (filesize < 1024) return $"{filesize} B"; + if (filesize < 1048576) return $"{filesize / 1024.0:.##} KiB"; + if (filesize < 1073741824) return $"{filesize / 1048576.0:.##} MiB"; + if (filesize < 1099511627776) return $"{filesize / 1073741824.0:.##} GiB"; + return $"{filesize / 1099511627776.0:.##} TiB"; + } + + /// all Types with the name + /// adapted from https://stackoverflow.com/a/13727044/7467292 + public static IList GetTypeByName(string className) => AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(asm => asm.GetTypes().Where(type => className.Equals(type.Name, StringComparison.InvariantCultureIgnoreCase))).ToList(); + + /// has an odd number of chars or contains a char not in [0-9A-Fa-f] + public static byte[] HexStringToBytes(this string str) + { + if (str.Length % 2 != 0) throw new ArgumentException(); + static int CharToNybble(char c) { - return true; + if ('0' <= c && c <= '9') return c - 0x30; + if ('A' <= c && c <= 'F') return c - 0x37; + if ('a' <= c && c <= 'f') return c - 0x57; + throw new ArgumentException(); } + using var ms = new MemoryStream(); + for (int i = 0, l = str.Length / 2; i != l; i++) ms.WriteByte((byte) ((CharToNybble(str[2 * i]) << 4) + CharToNybble(str[2 * i + 1]))); + return ms.ToArray(); + } + + public static int Memcmp(void* a, void* b, int len) + { + var ba = (byte*) a; + var bb = (byte*) b; + for (var i = 0; i != len; i++) + { + var _a = ba[i]; + var _b = bb[i]; + var c = _a - _b; + if (c != 0) return c; + } + return 0; + } + + public static void Memset(void* ptr, int val, int len) + { + var bptr = (byte*) ptr; + for (var i = 0; i != len; i++) bptr[i] = (byte) val; + } + + public static byte[]? ReadByteBuffer(this BinaryReader br, bool returnNull) + { + var len = br.ReadInt32(); + if (len == 0 && returnNull) return null; + var ret = new byte[len]; + var ofs = 0; + while (len > 0) + { + var done = br.Read(ret, ofs, len); + ofs += done; + len -= done; + } + return ret; + } + + /// Any non-zero element is interpreted as . + public static bool[] ToBoolBuffer(this byte[] buf) + { + var ret = new bool[buf.Length]; + for (int i = 0, len = buf.Length; i != len; i++) ret[i] = buf[i] != 0; + return ret; + } + + public static double[] ToDoubleBuffer(this byte[] buf) + { + var len = buf.Length; + var ret = new double[len / 8]; + Buffer.BlockCopy(buf, 0, ret, 0, len); + return ret; + } + + public static float[] ToFloatBuffer(this byte[] buf) + { + var len = buf.Length / 4; + var ret = new float[len]; + Buffer.BlockCopy(buf, 0, ret, 0, len); //TODO bug? the last arg should be in bytes + return ret; + } + + /// Each set of 4 elements in becomes 1 element in the returned buffer. The first of each set is interpreted as the LSB, with the 4th being the MSB. Elements are used as raw bits without regard for sign. + public static int[] ToIntBuffer(this byte[] buf) + { + var len = buf.Length / 4; + var ret = new int[len]; + unchecked + { + for (var i = 0; i != len; i++) ret[i] = (buf[4 * i + 3] << 24) | (buf[4 * i + 2] << 16) | (buf[4 * i + 1] << 8) | buf[4 * i]; + } + return ret; + } + + /// Each pair of elements in becomes 1 element in the returned buffer. The first of each pair is interpreted as the LSB. Elements are used as raw bits without regard for sign. + public static short[] ToShortBuffer(this byte[] buf) + { + var len = buf.Length / 2; + var ret = new short[len]; + unchecked + { + for (var i = 0; i != len; i++) ret[i] = (short) ((buf[2 * i + 1] << 8) | buf[2 * i]); + } + return ret; + } + + public static byte[] ToUByteBuffer(this bool[] buf) + { + var ret = new byte[buf.Length]; + for (int i = 0, len = buf.Length; i != len; i++) ret[i] = buf[i] ? (byte) 1 : (byte) 0; + return ret; + } + + public static byte[] ToUByteBuffer(this double[] buf) + { + var len = buf.Length * 8; + var ret = new byte[len]; + Buffer.BlockCopy(buf, 0, ret, 0, len); + return ret; + } + + public static byte[] ToUByteBuffer(this float[] buf) + { + var len = buf.Length * 4; + var ret = new byte[len]; + Buffer.BlockCopy(buf, 0, ret, 0, len); + return ret; + } + + /// Each element of becomes 4 elements in the returned buffer, with the LSB coming first. Elements are used as raw bits without regard for sign. + public static byte[] ToUByteBuffer(this int[] buf) + { + var len = buf.Length; + var ret = new byte[4 * len]; + unchecked + { + for (var i = 0; i != len; i++) + { + ret[4 * i] = (byte) buf[i]; + ret[4 * i + 1] = (byte) (buf[i] >> 8); + ret[4 * i + 2] = (byte) (buf[i] >> 16); + ret[4 * i + 3] = (byte) (buf[i] >> 24); + } + } + return ret; + } + + /// Each element of becomes 2 elements in the returned buffer, with the LSB coming first. Elements are used as raw bits without regard for sign. + public static byte[] ToUByteBuffer(this short[] buf) + { + var len = buf.Length; + var ret = new byte[2 * len]; + unchecked + { + for (var i = 0; i != len; i++) + { + ret[2 * i] = (byte) buf[i]; + ret[2 * i + 1] = (byte) (buf[i] >> 8); + } + } + return ret; + } + + /// + public static byte[] ToUByteBuffer(this uint[] buf) + { + var len = buf.Length; + var ret = new byte[4 * len]; + unchecked + { + for (var i = 0; i != len; i++) + { + ret[4 * i] = (byte) buf[i]; + ret[4 * i + 1] = (byte) (buf[i] >> 8); + ret[4 * i + 2] = (byte) (buf[i] >> 16); + ret[4 * i + 3] = (byte) (buf[i] >> 24); + } + } + return ret; + } + + /// + public static byte[] ToUByteBuffer(this ushort[] buf) + { + var len = buf.Length; + var ret = new byte[2 * len]; + unchecked + { + for (var i = 0; i != len; i++) + { + ret[2 * i] = (byte) buf[i]; + ret[2 * i + 1] = (byte) (buf[i] >> 8); + } + } + return ret; + } + + /// + public static uint[] ToUIntBuffer(this byte[] buf) + { + var len = buf.Length / 4; + var ret = new uint[len]; + unchecked + { + for (var i = 0; i != len; i++) ret[i] = (uint) ((buf[4 * i + 3] << 24) | (buf[4 * i + 2] << 16) | (buf[4 * i + 1] << 8) | buf[4 * i]); + } + return ret; + } + + /// + public static ushort[] ToUShortBuffer(this byte[] buf) + { + var len = buf.Length / 2; + var ret = new ushort[len]; + unchecked + { + for (var i = 0; i != len; i++) ret[i] = (ushort) ((buf[2 * i + 1] << 8) | buf[2 * i]); + } + return ret; + } + + /// Tries really hard to keep the contents of saved (as ) while freeing that path to be used for a new file. + /// If both and exist, is always deleted. + public static bool TryMoveBackupFile(string desiredPath, string backupPath) + { + if (!File.Exists(desiredPath)) return true; // desired path already free // delete any existing backup try { - if (File.Exists(pathBackup)) - { - File.Delete(pathBackup); - } + if (File.Exists(backupPath)) File.Delete(backupPath); } catch { - // just give up on the whole thing in case of exceptions. pathWant will get overwritten by the caller. - return false; + return false; // if Exists or Delete threw, there's not much we can do -- the caller will either overwrite the file or fail itself } - // deletes are asynchronous, need to wait for it to be gone - if(!TryWaitForFileToVanish(pathBackup)) + // deletions are asynchronous, so wait for a while and then give up + static bool TryWaitForFileToVanish(string path) { - // gave up waiting for existing backup to be gone. the whole thing's a total loss + for (var i = 25; i != 0; i--) + { + if (!File.Exists(path)) return true; + Thread.Sleep(10); + } return false; } + if (!TryWaitForFileToVanish(backupPath)) return false; + // the backup path is available now, so perform the backup and then wait for it to finish try { - // actually move pathWant out of the way to pathBackup now that pathBackup is free - File.Move(pathWant, pathBackup); + File.Move(desiredPath, backupPath); + return TryWaitForFileToVanish(desiredPath); } catch { - // Eat it, this will happen rarely and the user will rarely need the file, so the odds of simply not making the backup is very unlikely - return false; + return false; // this will be hit in the unlikely event that something else wrote to the backup path after we checked it was okay } - - // hmm these might be asynchronous too - // wait for the move to complete, at least enough for pathWant to be cleared up - return TryWaitForFileToVanish(pathWant); } - /// has an odd number of chars or contains a char not in [0-9A-Fa-f] - /// could be extension method - public static byte[] HexStringToBytes(string str) - { - var ms = new MemoryStream(); - if (str.Length % 2 != 0) - { - throw new ArgumentException(); - } - - int len = str.Length / 2; - for (int i = 0; i < len; i++) - { - int d = 0; - for (int j = 0; j < 2; j++) - { - var c = char.ToLower(str[(i * 2) + j]); - if (c >= '0' && c <= '9') - { - d += c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - d += (c - 'a') + 10; - } - else - { - throw new ArgumentException(); - } - - if (j == 0) - { - d <<= 4; - } - } - - ms.WriteByte((byte)d); - } - - return ms.ToArray(); - } - - // Could be extension method - public static void WriteByteBuffer(BinaryWriter bw, byte[] data) + public static void WriteByteBuffer(this BinaryWriter bw, byte[]? data) { if (data == null) { @@ -146,388 +331,5 @@ namespace BizHawk.Common bw.Write(data); } } - - public static bool[] ByteBufferToBoolBuffer(byte[] buf) - { - var ret = new bool[buf.Length]; - for (int i = 0; i < buf.Length; i++) - { - ret[i] = buf[i] != 0; - } - - return ret; - } - - public static byte[] BoolBufferToByteBuffer(bool[] buf) - { - var ret = new byte[buf.Length]; - for (int i = 0; i < buf.Length; i++) - { - ret[i] = (byte)(buf[i] ? 1 : 0); - } - - return ret; - } - - public static short[] ByteBufferToShortBuffer(byte[] buf) - { - int num = buf.Length / 2; - var ret = new short[num]; - for (int i = 0; i < num; i++) - { - ret[i] = (short)(buf[i * 2] | (buf[i * 2 + 1] << 8)); - } - - return ret; - } - - public static byte[] ShortBufferToByteBuffer(short[] buf) - { - int num = buf.Length; - var ret = new byte[num * 2]; - for (int i = 0; i < num; i++) - { - ret[i * 2 + 0] = (byte)(buf[i] & 0xFF); - ret[i * 2 + 1] = (byte)((buf[i] >> 8) & 0xFF); - } - - return ret; - } - - public static ushort[] ByteBufferToUshortBuffer(byte[] buf) - { - int num = buf.Length / 2; - var ret = new ushort[num]; - for (int i = 0; i < num; i++) - { - ret[i] = (ushort)(buf[i * 2] | (buf[i * 2 + 1] << 8)); - } - - return ret; - } - - public static byte[] UshortBufferToByteBuffer(ushort[] buf) - { - int num = buf.Length; - var ret = new byte[num * 2]; - for (int i = 0; i < num; i++) - { - ret[i * 2 + 0] = (byte)(buf[i] & 0xFF); - ret[i * 2 + 1] = (byte)((buf[i] >> 8) & 0xFF); - } - - return ret; - } - - public static uint[] ByteBufferToUintBuffer(byte[] buf) - { - int num = buf.Length / 4; - var ret = new uint[num]; - for (int i = 0; i < num; i++) - { - ret[i] = (uint)(buf[i * 4] | (buf[i * 4 + 1] << 8) | (buf[i * 4 + 2] << 16) | (buf[i * 4 + 3] << 24)); - } - - return ret; - } - - public static byte[] UintBufferToByteBuffer(uint[] buf) - { - int num = buf.Length; - var ret = new byte[num * 4]; - for (int i = 0; i < num; i++) - { - ret[i * 4 + 0] = (byte)(buf[i] & 0xFF); - ret[i * 4 + 1] = (byte)((buf[i] >> 8) & 0xFF); - ret[i * 4 + 2] = (byte)((buf[i] >> 16) & 0xFF); - ret[i * 4 + 3] = (byte)((buf[i] >> 24) & 0xFF); - } - - return ret; - } - - public static int[] ByteBufferToIntBuffer(byte[] buf) - { - int num = buf.Length / 4; - var ret = new int[num]; - for (int i = 0; i < num; i++) - { - ret[i] = buf[(i * 4) + 3]; - ret[i] <<= 8; - ret[i] |= buf[(i * 4) + 2]; - ret[i] <<= 8; - ret[i] |= buf[(i * 4) + 1]; - ret[i] <<= 8; - ret[i] |= buf[(i * 4)]; - } - - return ret; - } - - public static byte[] IntBufferToByteBuffer(int[] buf) - { - int num = buf.Length; - var ret = new byte[num * 4]; - for (int i = 0; i < num; i++) - { - ret[i * 4 + 0] = (byte)(buf[i] & 0xFF); - ret[i * 4 + 1] = (byte)((buf[i] >> 8) & 0xFF); - ret[i * 4 + 2] = (byte)((buf[i] >> 16) & 0xFF); - ret[i * 4 + 3] = (byte)((buf[i] >> 24) & 0xFF); - } - - return ret; - } - - public static float[] ByteBufferToFloatBuffer(byte[] buf) - { - int num = buf.Length / sizeof(float); - var ret = new float[num]; - Buffer.BlockCopy(buf, 0, ret, 0, num); - return ret; - } - - public static byte[] FloatBufferToByteBuffer(float[] buf) - { - int num = buf.Length; - var ret = new byte[num * sizeof(float)]; - Buffer.BlockCopy(buf, 0, ret, 0, ret.Length); - return ret; - } - - public static double[] ByteBufferToDoubleBuffer(byte[] buf) - { - int num = buf.Length; - var ret = new double[num / sizeof(double)]; - Buffer.BlockCopy(buf, 0, ret, 0, num); - return ret; - } - - public static byte[] DoubleBufferToByteBuffer(double[] buf) - { - int num = buf.Length; - var ret = new byte[num * sizeof(double)]; - Buffer.BlockCopy(buf, 0, ret, 0, ret.Length); - return ret; - } - - public static byte[] ReadByteBuffer(BinaryReader br, bool returnNull) - { - int len = br.ReadInt32(); - if (len == 0 && returnNull) - { - return null; - } - - var ret = new byte[len]; - int ofs = 0; - while (len > 0) - { - int done = br.Read(ret, ofs, len); - ofs += done; - len -= done; - } - - return ret; - } - - public static int Memcmp(void* a, void* b, int len) - { - var ba = (byte*)a; - var bb = (byte*)b; - for (int i = 0; i < len; i++) - { - byte _a = ba[i]; - byte _b = bb[i]; - int c = _a - _b; - if (c != 0) - { - return c; - } - } - - return 0; - } - - public static void Memset(void* ptr, int val, int len) - { - var bptr = (byte*)ptr; - for (int i = 0; i < len; i++) - { - bptr[i] = (byte)val; - } - } - - public static string FormatFileSize(long filesize) - { - decimal size = filesize; - - string suffix; - if (size > 1024 * 1024 * 1024) - { - size /= 1024 * 1024 * 1024; - suffix = "GB"; - } - else if (size > 1024 * 1024) - { - size /= 1024 * 1024; - suffix = "MB"; - } - else if (size > 1024) - { - size /= 1024; - suffix = "KB"; - } - else - { - suffix = "B"; - } - - const string precision = "2"; - return string.Format($"{{0:N{precision}}}{{1}}", size, suffix); - } - - // http://stackoverflow.com/questions/3928822/comparing-2-dictionarystring-string-instances - public static bool DictionaryEqual( - IDictionary first, IDictionary second) - { - if (first == second) - { - return true; - } - - if ((first == null) || (second == null)) - { - return false; - } - - if (first.Count != second.Count) - { - return false; - } - - var comparer = EqualityComparer.Default; - - foreach (var kvp in first) - { - if (!second.TryGetValue(kvp.Key, out var secondValue)) - { - return false; - } - - if (!comparer.Equals(kvp.Value, secondValue)) - { - return false; - } - } - - return true; - } - - /// issues with parsing - public static byte[] DecompressGzipFile(Stream src) - { - var tmp = new byte[4]; - if (src.Read(tmp, 0, 2) != 2) - throw new InvalidOperationException("Unexpected end of stream"); - if (tmp[0] != 0x1f || tmp[1] != 0x8b) - throw new InvalidOperationException("GZIP header not present"); - src.Seek(-4, SeekOrigin.End); - src.Read(tmp, 0, 4); - int size = BitConverter.ToInt32(tmp, 0); - var data = new byte[size]; - var ms = new MemoryStream(data); - src.Seek(0, SeekOrigin.Begin); - using (var gs = new GZipStream(src, CompressionMode.Decompress, true)) - gs.CopyTo(ms); - return data; - } - } - - public static class VLInteger - { - public static void WriteUnsigned(uint value, byte[] data, ref int index) - { - // This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking. - do - { - uint x = value & 0x7FU; - value >>= 7; - data[index++] = (byte)((value != 0U ? 0x80U : 0U) | x); - } - while (value != 0U); - } - - public static uint ReadUnsigned(byte[] data, ref int index) - { - // This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking. - uint value = 0U; - int shiftCount = 0; - bool isLastByte; // Negating the comparison and moving it earlier in the loop helps a lot on x86 for some reason - do - { - uint x = (uint)data[index++]; - isLastByte = (x & 0x80U) == 0U; - value |= (x & 0x7FU) << shiftCount; - shiftCount += 7; - } - while (!isLastByte); - return value; - } - } - - [Serializable] - public class NotTestedException : Exception - { - } - - internal class SuperGloballyUniqueID - { - private static readonly string StaticPart; - private static int ctr; - - static SuperGloballyUniqueID() - { - StaticPart = $"bizhawk-{System.Diagnostics.Process.GetCurrentProcess().Id}-{Guid.NewGuid()}"; - } - - public static string Next() - { - int myctr; - lock (typeof(SuperGloballyUniqueID)) - { - myctr = ctr++; - } - - return $"{StaticPart}-{myctr}"; - } - } - - public static class ReflectionUtil - { - // http://stackoverflow.com/questions/9273629/avoid-giving-namespace-name-in-type-gettype - /// - /// Gets a all Type instances matching the specified class name with just non-namespace qualified class name. - /// - /// Name of the class sought. - /// Types that have the class name specified. They may not be in the same namespace. - public static Type[] GetTypeByName(string className) - { - var returnVal = new List(); - - foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) - { - Type[] assemblyTypes = a.GetTypes(); - for (int j = 0; j < assemblyTypes.Length; j++) - { - if (assemblyTypes[j].Name.ToLower() == className.ToLower()) - { - returnVal.Add(assemblyTypes[j]); - } - } - } - - return returnVal.ToArray(); - } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mCM.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mCM.cs index bbf8d25288..a52f3f0269 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mCM.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mCM.cs @@ -3,6 +3,8 @@ using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.Atari.Atari2600 { + using System; + /* * Spectravideo Compumate Add-on Kevtris Documentation @@ -275,7 +277,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } // Attempting to read while in write mode - throw new NotTestedException(); + throw new Exception("this hasn't been tested"); } public override byte ReadMemory(ushort addr)