├── images ├── debug.png ├── main.png ├── explorer.png └── options.png ├── src ├── BipEmulator.Proxy │ ├── libbip.h │ ├── ProxyLib.cpp │ ├── BipEmulator.Proxy.vcxproj.filters │ ├── calend.h │ ├── BipEmulator.Proxy.vcxproj │ └── calend.cpp ├── BipEmulator.Host │ ├── App.config │ ├── packages.config │ ├── ResParam.cs │ ├── GestureEnum.cs │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── ColorIndexEnum.cs │ ├── ColorArray.cs │ ├── HrmData.cs │ ├── LocaleEnum.cs │ ├── Program.cs │ ├── NaviData.cs │ ├── DisplayNameAttribute.cs │ ├── WatchScreen.Designer.cs │ ├── ResFile.cs │ ├── ColorSelectionForm.cs │ ├── FunctionNames.cs │ ├── LocalSettings.cs │ ├── BitReader.cs │ ├── FtFile.cs │ ├── FtMaker.cs │ ├── MainForm.resx │ ├── WatchScreen.resx │ ├── ColorSelectionForm.resx │ ├── UnsafeBitmapContext.cs │ ├── BipEmulator.Host.csproj │ ├── WatchScreen.cs │ ├── ResImage.cs │ ├── ColorSelectionForm.Designer.cs │ ├── SymbolSet.cs │ └── MainForm.cs └── BipEmulator.sln ├── .gitattributes ├── README.md └── .gitignore /images/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/images/debug.png -------------------------------------------------------------------------------- /images/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/images/main.png -------------------------------------------------------------------------------- /images/explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/images/explorer.png -------------------------------------------------------------------------------- /images/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/images/options.png -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/libbip.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/src/BipEmulator.Proxy/libbip.h -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/ProxyLib.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freebip/BipEmulator/HEAD/src/BipEmulator.Proxy/ProxyLib.cpp -------------------------------------------------------------------------------- /src/BipEmulator.Host/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ResParam.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace BipEmulator.Host 4 | { 5 | [StructLayout(LayoutKind.Sequential, Pack = 0)] 6 | public struct ResParam 7 | { 8 | public short Width; 9 | public short Height; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/GestureEnum.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | public enum GestureEnum 4 | { 5 | GESTURE_CLICK = 1, 6 | GESTURE_SWIPE_UP = 2, 7 | GESTURE_SWIPE_DOWN = 3, 8 | GESTURE_SWIPE_LEFT = 4, 9 | GESTURE_SWIPE_RIGHT = 5, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ColorIndexEnum.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | public enum ColorIndexEnum 4 | { 5 | Black = 0, 6 | Blue = 1, 7 | Green = 2, 8 | Aqua = 3, 9 | Red = 4, 10 | Purple = 5, 11 | Yellow = 6, 12 | White = 7, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ColorArray.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace BipEmulator.Host 4 | { 5 | public struct ColorArray 6 | { 7 | public Color Black; 8 | public Color Red; 9 | public Color Green; 10 | public Color Blue; 11 | public Color Yellow; 12 | public Color Aqua; 13 | public Color Purple; 14 | public Color White; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/HrmData.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace BipEmulator.Host 4 | { 5 | [StructLayout(LayoutKind.Sequential, Pack = 0)] 6 | public struct HrmData 7 | { 8 | int t_start; 9 | public short last_hr; 10 | byte field_2; 11 | byte field_3; 12 | byte field_4; 13 | public byte heart_rate; 14 | public byte ret_code; 15 | byte field_5; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/LocaleEnum.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | public enum LocaleEnum 4 | { 5 | [DisplayName("Русский (ru_RU)")] 6 | ru_RU = 1049, 7 | [DisplayName("U.S. English (en_US)")] 8 | en_US = 1033, 9 | [DisplayName("Italiano (it_IT)")] 10 | it_IT = 1040, 11 | [DisplayName("Español (es_ES)")] 12 | es_ES = 3082, 13 | [DisplayName("Français (fr_FR)")] 14 | fr_FR = 1036, 15 | [DisplayName("Deutsch (de_DE)")] 16 | de_DE = 1031, 17 | [DisplayName("Ελληνικά (el_GR)")] 18 | el_GR = 1032 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace BipEmulator.Host 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// Главная точка входа для приложения. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new MainForm()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/NaviData.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace BipEmulator.Host 4 | { 5 | [StructLayout(LayoutKind.Sequential, Pack = 0)] 6 | public struct NaviData 7 | { 8 | public int ready; // Готовность данных: bit 0: давление ; bit 1: высота ; bit 2: широта ; bit 3: долгота 9 | public uint pressure; // значение давления в Паскалях 10 | public float altitude; // значение высоты в метрах 11 | public int latitude; // модуль значения долготы в градусах, умноженное на 3000000 12 | public int ns; // ns: 0-северное полушарие; 1-южное 13 | public int longitude; // модуль знаения долготы в градусах, умноженное на 3000000 14 | public int ew; // ew: 2-западное полушарие; 3-восточное; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BipEmulator.Host.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/BipEmulator.Proxy.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Файлы заголовков 20 | 21 | 22 | Файлы заголовков 23 | 24 | 25 | 26 | 27 | Исходные файлы 28 | 29 | 30 | Исходные файлы 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Общие сведения об этой сборке предоставляются следующим набором 6 | // набора атрибутов. Измените значения этих атрибутов для изменения сведений, 7 | // связанных со сборкой. 8 | [assembly: AssemblyTitle("BipEmulator.Host")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BipEmulator.Host")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми 18 | // для компонентов COM. Если необходимо обратиться к типу в этой сборке через 19 | // COM, следует установить атрибут ComVisible в TRUE для этого типа. 20 | [assembly: ComVisible(false)] 21 | 22 | // Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM 23 | [assembly: Guid("71dd5d93-2f61-47e9-a0a3-8d0d3bc685fd")] 24 | 25 | // Сведения о версии сборки состоят из указанных ниже четырех значений: 26 | // 27 | // Основной номер версии 28 | // Дополнительный номер версии 29 | // Номер сборки 30 | // Редакция 31 | // 32 | // Можно задать все значения или принять номера сборки и редакции по умолчанию 33 | // используя "*", как показано ниже: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/DisplayNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BipEmulator.Host 4 | { 5 | [AttributeUsage(AttributeTargets.All)] 6 | public class DisplayNameAttribute : System.ComponentModel.DisplayNameAttribute 7 | { 8 | public DisplayNameAttribute(string name) 9 | { 10 | DisplayNameValue = name; 11 | } 12 | 13 | public string Name { get { return DisplayName; } } 14 | 15 | public static string GetName(Object o) 16 | { 17 | if (o == null) return String.Empty; 18 | 19 | var t = o.GetType(); 20 | if (t.IsEnum) 21 | { 22 | var fi = t.GetFields(); 23 | foreach (var fieldInfo in fi) 24 | { 25 | if (fieldInfo.Name == o.ToString()) 26 | { 27 | var a = (DisplayNameAttribute)GetCustomAttribute(fieldInfo, typeof(DisplayNameAttribute)); 28 | return a == null ? fieldInfo.Name : a.Name; 29 | } 30 | } 31 | } 32 | else 33 | { 34 | var a = (DisplayNameAttribute)GetCustomAttribute(t, typeof(DisplayNameAttribute)); 35 | if (a != null) 36 | return a.Name; 37 | 38 | } 39 | return String.Empty; 40 | } 41 | 42 | public static string GetName(Type t) 43 | { 44 | if (t == null) return String.Empty; 45 | 46 | var a = (DisplayNameAttribute)GetCustomAttribute(t, typeof(DisplayNameAttribute)); 47 | return a != null ? a.Name : String.Empty; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/BipEmulator.Host/WatchScreen.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | partial class WatchScreen 4 | { 5 | /// 6 | /// Обязательная переменная конструктора. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Освободить все используемые ресурсы. 12 | /// 13 | /// истинно, если управляемый ресурс должен быть удален; иначе ложно. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Код, автоматически созданный конструктором компонентов 24 | 25 | /// 26 | /// Требуемый метод для поддержки конструктора — не изменяйте 27 | /// содержимое этого метода с помощью редактора кода. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // WatchScreen 34 | // 35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 37 | this.MaximumSize = new System.Drawing.Size(176, 176); 38 | this.MinimumSize = new System.Drawing.Size(176, 176); 39 | this.Name = "WatchScreen"; 40 | this.Size = new System.Drawing.Size(176, 176); 41 | this.ResumeLayout(false); 42 | 43 | } 44 | 45 | #endregion 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ResFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace BipEmulator.Host 7 | { 8 | public class ResFile 9 | { 10 | const string SIGNATURE = "NERES"; 11 | 12 | public byte Version { get; private set; } 13 | public List Resources { get; private set; } 14 | 15 | public static ResFile Load(string fileName) 16 | { 17 | var resFile = new ResFile(); 18 | using (var br = new BinaryReader(File.OpenRead(fileName))) 19 | { 20 | var signature = Encoding.ASCII.GetString(br.ReadBytes(5)); 21 | if (signature != SIGNATURE) 22 | throw new ArgumentException($"Unknown signature: {signature}"); 23 | resFile.Version = br.ReadByte(); 24 | 25 | br.BaseStream.Position = 0x20; 26 | var resCount = br.ReadUInt32(); 27 | 28 | var offsetTable = new uint[resCount]; 29 | for (var i = 0; i < resCount; i++) 30 | offsetTable[i] = br.ReadUInt32(); 31 | 32 | var resourceFileOffset = br.BaseStream.Position; 33 | var fileLength = br.BaseStream.Length; 34 | 35 | resFile.Resources = new List(); 36 | for (var i = 0; i < resCount; i++) 37 | { 38 | var offset = resourceFileOffset + offsetTable[i]; 39 | var nextOffset = i + 1 < resCount ? resourceFileOffset + offsetTable[i+1] : fileLength; 40 | var resLength = nextOffset - offset; 41 | if (resLength < 0 || resLength > fileLength) 42 | throw new ArgumentException($"Format error. Offset: {offset}"); 43 | resFile.Resources.Add(br.ReadBytes((int)resLength)); 44 | } 45 | } 46 | 47 | return resFile; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ColorSelectionForm.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace BipEmulator.Host 5 | { 6 | public partial class ColorSelectionForm : Form 7 | { 8 | public ColorArray Colors { get; set; } 9 | 10 | public ColorSelectionForm(ColorArray colors) 11 | { 12 | InitializeComponent(); 13 | 14 | Colors = colors; 15 | 16 | SetColor(lblBlack, Colors.Black); 17 | SetColor(lblRed, Colors.Red); 18 | SetColor(lblGreen, Colors.Green); 19 | SetColor(lblYellow, Colors.Yellow); 20 | SetColor(lblAqua, Colors.Aqua); 21 | SetColor(lblPurple,Colors.Purple); 22 | SetColor(lblWhite,Colors.White); 23 | SetColor(lblBlue,Colors.Blue); 24 | } 25 | 26 | private void SetColor(Label label, Color color) 27 | { 28 | label.BackColor = color; 29 | label.ForeColor = Color.FromArgb(~color.ToArgb()); 30 | } 31 | 32 | private void ColorLabel_Click(object sender, System.EventArgs e) 33 | { 34 | var label = (Label)sender; 35 | 36 | var frm = new ColorDialog(); 37 | frm.FullOpen = true; 38 | frm.Color = label.BackColor; 39 | if (frm.ShowDialog() == DialogResult.OK) 40 | { 41 | SetColor(label, frm.Color); 42 | } 43 | } 44 | 45 | private void btnOK_Click(object sender, System.EventArgs e) 46 | { 47 | Colors = new ColorArray 48 | { 49 | Black = lblBlack.BackColor, 50 | Red = lblRed.BackColor, 51 | Green = lblGreen.BackColor, 52 | Yellow = lblYellow.BackColor, 53 | Aqua = lblAqua.BackColor, 54 | Purple = lblPurple.BackColor, 55 | White = lblWhite.BackColor, 56 | Blue = lblBlue.BackColor 57 | }; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/FunctionNames.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | public static class FunctionNames 4 | { 5 | public const string GET_HRM_STRUCT = "get_hrm_struct"; 6 | public const string GET_LAST_HEARTRATE = "get_last_heartrate"; 7 | public const string GET_NAVI_DATA = "get_navi_data"; 8 | public const string GET_RES_PARAMS = "get_res_params"; 9 | public const string GET_SELECTED_LOCALE = "get_selected_locale"; 10 | public const string GET_TEXT_HEIGHT = "get_text_height"; 11 | public const string DRAW_FILLED_RECT = "draw_filled_rect"; 12 | public const string DRAW_FILLED_RECT_BG = "draw_filled_rect_bg"; 13 | public const string DRAW_HORIZONTAL_LINE = "draw_horizontal_line"; 14 | public const string DRAW_RECT = "draw_rect"; 15 | public const string DRAW_VERTICAL_LINE = "draw_vertical_line"; 16 | public const string IS_GPS_FIXED = "is_gps_fixed"; 17 | public const string FILL_SCREEN_BG = "fill_screen_bg"; 18 | public const string LOAD_FONT = "load_font"; 19 | public const string LOG_PRINTF = "log_printf"; 20 | public const string REG_MENU = "reg_menu"; 21 | public const string REPAINT_SCREEN_LINES = "repaint_screen_lines"; 22 | public const string READ_ELF_RES_BY_ID = "read_elf_res_by_id"; 23 | public const string SET_BG_COLOR = "set_bg_color"; 24 | public const string SET_FG_COLOR = "set_fg_color"; 25 | public const string SET_UPDATE_PERIOD = "set_update_period"; 26 | public const string SHOW_BIG_DIGITS = "show_big_digit"; 27 | public const string SHOW_ELF_RES_BY_ID = "show_elf_res_by_id"; 28 | public const string SHOW_RES_BY_ID = "show_res_by_id"; 29 | public const string TEXT_OUT = "text_out"; 30 | public const string TEXT_OUT_CENTER = "text_out_center"; 31 | public const string TEXT_WIDTH = "text_width"; 32 | public const string VIBRATE = "vibrate"; 33 | 34 | // special emulator functions 35 | public const string SHARED_MEMORY_ENABLED = "__shared_memory_enabled__"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/LocalSettings.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Drawing; 3 | using System.IO; 4 | 5 | namespace BipEmulator.Host 6 | { 7 | [System.Serializable] 8 | public class LocalSettings 9 | { 10 | const string SettingsFilename = "settings.json"; 11 | 12 | public int HeartRate = 70; 13 | public bool HeartRateMeasurementCompleted = false; 14 | public double Latitude = 37.401583; 15 | public double Longitude = -116.867808; 16 | public double Altitude = 1592.12; 17 | public bool GeoLocationMeasurementCompleted = false; 18 | public int Pressure = 760; 19 | public bool PressureMeasurementCompleted = false; 20 | public LocaleEnum Locale = LocaleEnum.ru_RU; 21 | public int MaxDistanceClick = 5; 22 | 23 | public ColorArray Colors = new ColorArray 24 | { 25 | Black = Color.FromArgb(56, 56, 56), 26 | Red = Color.FromArgb(187, 113, 136), 27 | Green = Color.FromArgb(89, 181, 132), 28 | Blue = Color.FromArgb(28, 110, 194), 29 | Yellow = Color.FromArgb(221, 214, 133), 30 | Aqua = Color.FromArgb(70, 198, 207), 31 | Purple = Color.FromArgb(173, 147, 208), 32 | White = Color.FromArgb(220, 220, 220) 33 | }; 34 | 35 | public string FontFilename = "Presets//Mili_chaohu.ft.latin"; 36 | public string SystemResourceFilename = "Presets//Mili_chaohu.res.latin"; 37 | public string UserResourceFilename = "assert.res"; 38 | 39 | public static LocalSettings Load() 40 | { 41 | try 42 | { 43 | return JsonConvert.DeserializeObject(File.ReadAllText(SettingsFilename)); 44 | } 45 | catch 46 | { 47 | return new LocalSettings(); 48 | } 49 | } 50 | 51 | public void Save() 52 | { 53 | try 54 | { 55 | File.WriteAllText(SettingsFilename, JsonConvert.SerializeObject(this)); 56 | } 57 | catch 58 | { 59 | } 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/BitReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace BipEmulator.Host 5 | { 6 | public class BitReader 7 | { 8 | private readonly byte[] _masks = { 128, 192, 224, 240, 248, 252, 254, 255 }; 9 | private readonly Stream _stream; 10 | 11 | private int _bitsRemaining; 12 | private int _currentByte; 13 | private bool _isDataPresent = true; 14 | 15 | public BitReader(Stream stream) 16 | { 17 | _stream = stream; 18 | } 19 | 20 | public BitReader(byte[] arrayBytes) 21 | { 22 | _stream = new MemoryStream(arrayBytes); 23 | } 24 | 25 | public bool IsDataPresent 26 | { 27 | get 28 | { 29 | if (_bitsRemaining > 0) return true; 30 | 31 | TryReadNext(); 32 | return _isDataPresent; 33 | } 34 | } 35 | 36 | public bool ReadBit() 37 | { 38 | return ReadBits(1) != 0; 39 | } 40 | 41 | public byte ReadByte() 42 | { 43 | return (byte)ReadBits(8); 44 | } 45 | 46 | public ulong ReadBits(int length) 47 | { 48 | ulong data = 0; 49 | while (length > 0) 50 | { 51 | if (_bitsRemaining == 0 && _isDataPresent) 52 | TryReadNext(); 53 | 54 | var dataLength = Math.Min(length, _bitsRemaining); 55 | 56 | var currentData = (ulong)(_currentByte & _masks[dataLength - 1]); 57 | if (length > 8) 58 | currentData = currentData << (length - 8); 59 | else 60 | currentData = currentData >> (8 - length); 61 | data = data | currentData; 62 | 63 | _currentByte = _currentByte << dataLength; 64 | length -= dataLength; 65 | _bitsRemaining -= dataLength; 66 | } 67 | return data; 68 | } 69 | 70 | private void TryReadNext() 71 | { 72 | _currentByte = _stream.ReadByte(); 73 | _isDataPresent = _currentByte > -1; 74 | if (_isDataPresent) 75 | _bitsRemaining = 8; 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/FtFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace BipEmulator.Host 7 | { 8 | public class FtFile 9 | { 10 | const string SIGNATURE = "NEZK"; 11 | public byte Version { get; private set; } 12 | public Hashtable Letters { get; } = new Hashtable(); 13 | 14 | public static FtFile Load(string filename) 15 | { 16 | var ftFile = new FtFile(); 17 | 18 | using (var br = new BinaryReader(File.OpenRead(filename))) 19 | { 20 | var signature = Encoding.ASCII.GetString(br.ReadBytes(4)); 21 | if (signature != SIGNATURE) 22 | throw new ArgumentException($"Wrong signature: {signature}"); 23 | 24 | ftFile.Version = br.ReadByte(); 25 | br.BaseStream.Position = 0x20; 26 | int letterBlockCount = br.ReadUInt16(); 27 | var hashLen = ftFile.Version == 8 ? 1 : 2; 28 | 29 | for (var block = 0; block < 6 * letterBlockCount; block += 6) 30 | { 31 | int letterCodeStart = br.ReadUInt16(); 32 | int letterCodeStop = br.ReadUInt16(); 33 | int lettersOffset = br.ReadUInt16(); 34 | 35 | var saveOffset = br.BaseStream.Position; 36 | // устанавливаем смещение дял чтения блока данных набора символов 37 | // с кодами от letterCodeStart до letterCodeStop 38 | br.BaseStream.Position = 6 * letterBlockCount + 0x22 + (34 - hashLen) * lettersOffset; 39 | 40 | for (var l = letterCodeStart; l <= letterCodeStop; l++) 41 | { 42 | // 33 байт блок 43 | if (ftFile.Version == 8) 44 | { 45 | ftFile.Letters.Add(l, br.ReadBytes(32)); 46 | // hash??? 47 | br.ReadByte(); 48 | } 49 | // 32 байт блок 50 | else 51 | { 52 | ftFile.Letters.Add(l, br.ReadBytes(30)); 53 | // hash??? 54 | br.ReadUInt16(); 55 | } 56 | } 57 | br.BaseStream.Position = saveOffset; 58 | } 59 | } 60 | 61 | return ftFile; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/BipEmulator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BipEmulator.Host", "BipEmulator.Host\BipEmulator.Host.csproj", "{71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BipEmulator.Proxy", "BipEmulator.Proxy\BipEmulator.Proxy.vcxproj", "{D2090F65-51DB-4643-8E60-25AFF97321AF}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|x64.ActiveCfg = Debug|Any CPU 23 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|x64.Build.0 = Debug|Any CPU 24 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|x86.ActiveCfg = Debug|Any CPU 25 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Debug|x86.Build.0 = Debug|Any CPU 26 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|x64.ActiveCfg = Release|Any CPU 29 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|x64.Build.0 = Release|Any CPU 30 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|x86.ActiveCfg = Release|Any CPU 31 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD}.Release|x86.Build.0 = Release|Any CPU 32 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Debug|Any CPU.ActiveCfg = Debug|Win32 33 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Debug|x64.ActiveCfg = Debug|x64 34 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Debug|x64.Build.0 = Debug|x64 35 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Debug|x86.ActiveCfg = Debug|Win32 36 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Debug|x86.Build.0 = Debug|Win32 37 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Release|Any CPU.ActiveCfg = Release|Win32 38 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Release|x64.ActiveCfg = Release|x64 39 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Release|x64.Build.0 = Release|x64 40 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Release|x86.ActiveCfg = Release|Win32 41 | {D2090F65-51DB-4643-8E60-25AFF97321AF}.Release|x86.Build.0 = Release|Win32 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {FD4270FB-74B3-4190-BBB6-EB647469ADA1} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bip Emulator 2 | 3 | Эмулятор окружения BipOS для часов Amazfit Bip 4 | 5 | ## Назначение 6 | 7 | Написание и отладка приложений для часов Amazfit Bip под управлением мода BipOS (0.5.X) 8 | в среде Microsoft Visual Studio 2019 9 | 10 | ![alt-текст](https://github.com/freebip/BipEmulator/raw/master/images/main.png "Главное окно") 11 | 12 | ## Описание 13 | 14 | Эмулятор состоит из двух модулей: Host и Proxy. 15 | Proxy обрабатывает вызовы функций библиотеки Libbip поступающие от программы, работа которой 16 | происходит в эмуляторе. Часть этих вызовов модуль Proxy обрабатывает сам, часть перенаправляет 17 | модулю Host. 18 | На модуль Host возложены более высокоуровневые задачи: 19 | * Обработка вызовов графических функций и отображение результата их работы на пользовательском интерфейсе. 20 | * Обработка ввода команд пользователя, специфичных для эмулируемого устройства. 21 | * Эмуляция специфичных данных эмулируемого устройства (Давление, Геолокационные координаты и пр.) 22 | * Отображение отладочных данных программы 23 | 24 | ## Изображения 25 | 26 | 27 | ![alt-текст](https://github.com/freebip/BipEmulator/raw/master/images/options.png "Настройки") 28 | 29 | ![alt-текст](https://github.com/freebip/BipEmulator/raw/master/images/debug.png "Отладочный вывод") 30 | 31 | 32 | ## Использование 33 | 34 | * Добавьте исходный код в Proxy модуль. 35 | * Откомпилируйте Proxy модуль. 36 | * Запустите Host модуль. 37 | * Завершите исполнение эмулятора 38 | * Исправьте исходный код 39 | * Повторять со второго пункта бесконечное кол-во раз 40 | 41 | ## Особенности использования 42 | 43 | В данном проекте используется связка C# <-> CLI/C++. 44 | Поэтому добавляемые исходные файлы должны иметь расширение .cpp (не относится к заголовочным файлам) 45 | 46 | ***Но*** код должен быть написан в соответствии со стандартами чистого Си т.к. в последствии его предстоит 47 | собирать с помощью gnu toolchain под Си. 48 | 49 | Так как при использовании расширения .cpp мы переводим исходные файлы в разряд C++, то на код накладываются 50 | более суровые ограничения (например, в плане приведения типов по-умолчанию) 51 | 52 | Было так: 53 | ``` 54 | void* get_ptr_temp_buf_2(); 55 | struct calend_** calend_p = get_ptr_temp_buf_2(); 56 | ``` 57 | Надо так: 58 | ``` 59 | void* get_ptr_temp_buf_2(); 60 | struct calend_** calend_p = (struct calend_**) get_ptr_temp_buf_2(); 61 | ``` 62 | 63 | Proxy модуль является динамически линкуемой библиотекой (.dll), от которой зависит компиляция Host модуля. 64 | Поэтому после всех изменений принудительно откомпилируйте Proxy модуль, а после этого запускайте программу. 65 | 66 | После первого запуска необходимо на закладке Options указать файл стандартных ресурсов и 67 | файл шрифтов. Иначе Календарь не будет отображаться, т.к. шрифты ему взять не откуда. 68 | 69 | 70 | ## Пример 71 | 72 | Для демонстрации возможностей эмулятора в него добавлено приложение [Календарь](https://github.com/MNVolkov/Calend) 73 | 74 | ![alt-текст](https://github.com/freebip/BipEmulator/raw/master/images/explorer.png "Исходные фаайлы календаря") 75 | 76 | ## Примечание 77 | 78 | У проекта WIP статус. Обработка части функций библиотеки Libbip не реализована. 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Этот код создан программным средством. 4 | // Версия среды выполнения: 4.0.30319.42000 5 | // 6 | // Изменения в этом файле могут привести к неправильному поведению и будут утрачены, если 7 | // код создан повторно. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BipEmulator.Host.Properties 12 | { 13 | 14 | 15 | /// 16 | /// Класс ресурсов со строгим типом для поиска локализованных строк и пр. 17 | /// 18 | // Этот класс был автоматически создан при помощи StronglyTypedResourceBuilder 19 | // класс с помощью таких средств, как ResGen или Visual Studio. 20 | // Для добавления или удаления члена измените файл .ResX, а затем перезапустите ResGen 21 | // с параметром /str или заново постройте свой VS-проект. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Возврат кэшированного экземпляра ResourceManager, используемого этим классом. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BipEmulator.Host.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Переопределяет свойство CurrentUICulture текущего потока для всех 56 | /// подстановки ресурсов с помощью этого класса ресурсов со строгим типом. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/calend.h: -------------------------------------------------------------------------------- 1 | /* 2 | (С) Волков Максим 2019 ( mr-volkov+bip@yandex.ru ) 3 | Календарь для Amazfit Bip 4 | 5 | */ 6 | #ifndef __CALEND_H__ 7 | #define __CALEND_H__ 8 | 9 | #include "libbip.h" 10 | 11 | // Значения цветовой схемы 12 | #define CALEND_COLOR_BG 0 // фон календаря 13 | #define CALEND_COLOR_MONTH 1 // цвет названия текущего месяца 14 | #define CALEND_COLOR_YEAR 2 // цвет текущего года 15 | #define CALEND_COLOR_WORK_NAME 3 // цвет названий дней будни 16 | #define CALEND_COLOR_HOLY_NAME_BG 4 // фон названий дней выходные 17 | #define CALEND_COLOR_HOLY_NAME_FG 5 // цвет названий дней выходные 18 | #define CALEND_COLOR_SEPAR 6 // цвет разделителей календаря 19 | #define CALEND_COLOR_NOT_CUR_WORK 7 // цвет чисел НЕ текущего месяца будни 20 | #define CALEND_COLOR_NOT_CUR_HOLY_BG 8 // фон чисел НЕ текущего месяца выходные 21 | #define CALEND_COLOR_NOT_CUR_HOLY_FG 9 // цвет чисел НЕ текущего месяца выходные 22 | #define CALEND_COLOR_CUR_WORK 10 // цвет чисел текущего месяца будни 23 | #define CALEND_COLOR_CUR_HOLY_BG 11 // фон чисел текущего месяца выходные 24 | #define CALEND_COLOR_CUR_HOLY_FG 12 // цвет чисел текущего месяца выходные 25 | #define CALEND_COLOR_TODAY_BG 13 // цвет чисел текущего дня 26 | #define CALEND_COLOR_TODAY_FG 14 // фон чисел текущего дня 27 | 28 | // количество цыетовых схем 29 | #define COLOR_SCHEME_COUNT 5 30 | 31 | // смещение адреса для хранения настроек календаря 32 | #define OPT_OFFSET_CALEND_OPT 0 33 | 34 | #if FW_VERSION==latin_1_1_5_12 || FW_VERSION==latin_1_1_5_36 35 | // параметры рисования цифр календаря 36 | // строка: от 7 до 169 = 162рх в ширину 7 чисел по 24рх на число 7+(24)*6+22+3 37 | // строки: от 57 до 174 = 117рх в высоту 6 строк по 22рх на строку 1+(22)*5+22 38 | 39 | #define CALEND_Y_BASE 30 // базовая высота начала отрисовки календаря 40 | //#define CALEND_NAME_HEIGHT 19 // высота строки названий дней недели 41 | //#define CALEND_DAYS_Y_BASE CALEND_Y_BASE+1+V_MARGIN+CALEND_NAME_HEIGHT+V_MARGIN+1 // высота базы чисел месяца 42 | #define WIDTH 24 // ширина цифр числа 43 | #define HEIGHT 19 // высота цифр числа 44 | #define V_SPACE 0 // вертикальный отступ между строками чисел 45 | #define H_SPACE 0 // горизонтальный отступ между колонками чисел 46 | #define H_MARGIN 4 // горизонтальный отступ от края экрана 47 | #define V_MARGIN 1 // вертикальный отступ от заголовка (базы) 48 | 49 | #elif FW_VERSION==not_latin_1_1_2_05 50 | // параметры рисования цифр календаря 51 | // строка: от 7 до 169 = 162рх в ширину 7 чисел по 24рх на число 7+(24)*6+24+3 52 | // строки: от 57 до 174 = 117рх в высоту 6 строк по 22рх на строку 1+(22)*5+20 53 | 54 | #define CALEND_Y_BASE 25 // базовая высота начала отрисовки календаря 55 | //#define CALEND_NAME_HEIGHT FONT_HEIGHT+2 // высота строки названий дней недели 56 | //#define CALEND_DAYS_Y_BASE CALEND_Y_BASE+1+V_MARGIN+CALEND_NAME_HEIGHT+V_MARGIN+1 // высота базы чисел месяца 56 57 | #define WIDTH 24 // ширина цифр числа 58 | #define HEIGHT 20 // высота цифр числа 59 | #define V_SPACE 0 // вертикальный отступ между строками чисел 60 | #define H_SPACE 0 // горизонтальный отступ между колонками чисел 61 | #define H_MARGIN 4 // горизонтальный отступ от края экрана 62 | #define V_MARGIN 1 // вертикальный отступ от заголовка (базы) 63 | #endif 64 | 65 | 66 | #define INACTIVITY_PERIOD 30000 // время по прошествии которого выходим 67 | 68 | // сохраняемые опции календаря 69 | struct calend_opt_ { 70 | unsigned char color_scheme; // цветовая схема 71 | }; 72 | 73 | // текущие данные просматриваемого/редактируемого календаря 74 | struct calend_ { 75 | Elf_proc_* proc; // указатель на данные запущенного процесса 76 | void* ret_f; // адрес функции возврата 77 | unsigned char color_scheme; // цветовая схема 78 | // отображаемый месяц 79 | unsigned int day; // день 80 | unsigned int month; // месяц 81 | unsigned int year; // год 82 | }; 83 | 84 | 85 | void show_calend_screen (void *return_screen); 86 | void key_press_calend_screen(); 87 | int dispatch_calend_screen (void *param); 88 | void calend_screen_job(); 89 | 90 | void draw_month(unsigned int day, unsigned int month, unsigned int year); 91 | unsigned char wday(unsigned int day,unsigned int month,unsigned int year); 92 | unsigned char isLeapYear(unsigned int year); 93 | 94 | #endif -------------------------------------------------------------------------------- /src/BipEmulator.Host/FtMaker.cs: -------------------------------------------------------------------------------- 1 | using FastBitmapLib; 2 | using System; 3 | using System.Drawing; 4 | 5 | namespace BipEmulator.Host 6 | { 7 | public class FtMaker 8 | { 9 | const int EMPTY_LETTER_WIDTH = 9; 10 | const int LETTER_SPACING = 2; 11 | 12 | private FtFile _ftFile; 13 | public FtMaker(FtFile ftFile) 14 | { 15 | _ftFile = ftFile; 16 | } 17 | 18 | public Bitmap GetImage(string text) 19 | { 20 | var bmp = new Bitmap(GetTextWidth(text), GetTextHeight()); 21 | TextOutCenter(bmp, text, 0, 0, Color.White, Color.Black); 22 | return bmp; 23 | } 24 | 25 | private void SetPixel(FastBitmap fbmp, int x, int y, Color color) 26 | { 27 | if (x < 0 || y < 0 || x >= fbmp.Width || y >= fbmp.Height) 28 | return; 29 | fbmp.SetPixel(x, y, color); 30 | } 31 | 32 | public void TextOutCenter(Bitmap bmp, string text, int x, int y, Color fgColor, Color bgColor, bool bgTransparent = false) 33 | { 34 | var h = GetTextHeight(); 35 | var w = GetTextWidth(text); 36 | TextOut(bmp, text, x - w / 2, y, fgColor, bgColor, bgTransparent); 37 | } 38 | 39 | 40 | public void TextOut(Bitmap bmp, string text, int x, int y, Color fgColor, Color bgColor, bool bgTransparent = false) 41 | { 42 | var fbmp = new FastBitmap(bmp); 43 | fbmp.Lock(); 44 | var xCurrent = x; 45 | 46 | for (var i = 0; i < text.Length; i++) 47 | { 48 | var key = (int)text[i]; 49 | var letterWidth = GetLetterWidth(text[i]); 50 | 51 | if (_ftFile.Letters.ContainsKey(key)) 52 | { 53 | var data = (byte[])_ftFile.Letters[key]; 54 | for (var j = 0; j < data.Length / 2; j++) 55 | { 56 | var line = Swap(BitConverter.ToUInt16(data, 2 * j)); 57 | 58 | for (var k = 0; k < letterWidth; k++) 59 | { 60 | if ((line & 0x8000) != 0) 61 | SetPixel(fbmp, xCurrent + k, y + j, fgColor); 62 | else if (!bgTransparent) 63 | SetPixel(fbmp, xCurrent + k, y + j, bgColor); 64 | line = (ushort)(line << 1); 65 | } 66 | 67 | // для пропорционального шрифта добавляем межбуквенный интервал 68 | if (!bgTransparent && _ftFile.Version == 8) 69 | { 70 | for (var k = 0; k < LETTER_SPACING; k++) 71 | { 72 | SetPixel(fbmp, xCurrent + letterWidth + k, y + j, bgColor); 73 | } 74 | } 75 | } 76 | } 77 | else 78 | { 79 | if (!bgTransparent) 80 | { 81 | for (var j = 0; j < GetTextHeight(); j++) 82 | for (var k = 0; k < EMPTY_LETTER_WIDTH + LETTER_SPACING; k++) 83 | SetPixel(fbmp, xCurrent + k, y + j, bgColor); 84 | } 85 | } 86 | xCurrent += letterWidth + (_ftFile.Version == 8 ? LETTER_SPACING : 0); 87 | } 88 | fbmp.Unlock(); 89 | } 90 | 91 | private ushort Swap(ushort value) 92 | { 93 | return (ushort)((value >> 8) | (value << 8)); 94 | } 95 | 96 | private int GetLetterWidth(char letter) 97 | { 98 | if (_ftFile.Version == 9) 99 | return 9; 100 | 101 | int width = 0; 102 | if (_ftFile.Letters.ContainsKey((int)letter)) 103 | { 104 | var data = (byte[])_ftFile.Letters[(int)letter]; 105 | var maxLen = 0; 106 | for (var j = 0; j < data.Length / 2; j++) 107 | { 108 | var line = Swap(BitConverter.ToUInt16(data, 2 * j)); 109 | var count = 16; 110 | while (count > 0) 111 | { 112 | if ((line & 1) != 0) 113 | break; 114 | 115 | line = (ushort)(line >> 1); 116 | count--; 117 | 118 | } 119 | maxLen = maxLen > count ? maxLen : count; 120 | } 121 | width = maxLen == 0 ? EMPTY_LETTER_WIDTH : maxLen; 122 | } 123 | else 124 | { 125 | width = EMPTY_LETTER_WIDTH; 126 | } 127 | return width; 128 | } 129 | 130 | public int GetTextWidth(string text) 131 | { 132 | if (_ftFile.Version == 9) 133 | return text.Length * 9; 134 | 135 | var width = 0; 136 | for (var i = 0; i < text.Length; i++) 137 | { 138 | width += GetLetterWidth(text[i]) + LETTER_SPACING; 139 | } 140 | 141 | return width; 142 | } 143 | 144 | public int GetTextHeight() 145 | { 146 | return _ftFile.Version == 8 ? 16 : 15; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/MainForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/WatchScreen.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ColorSelectionForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/UnsafeBitmapContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | 6 | namespace BipEmulator.Host 7 | { 8 | public static class ImageExtensions 9 | { 10 | public static UnsafeBitmapContext CreateUnsafeContext(this Image image) 11 | { 12 | return new UnsafeBitmapContext(image); 13 | } 14 | } 15 | 16 | /// 17 | /// Provides quick but unsafe access to a bitmap's bits.
18 | /// ALWAYS ALWAYS ALWAYS wire this in a using block.
19 | /// DO NOT EVER access the bitmap directly while in this unsafe context.
20 | ///
21 | public sealed unsafe class UnsafeBitmapContext : IDisposable 22 | { 23 | private Stream _originalStream; 24 | private Bitmap _bitmap; 25 | private BitmapData _lockData; 26 | private Byte* _ptrBase; 27 | private int _pixelWidth; 28 | 29 | public int Width { get; private set; } 30 | public int Height { get; private set; } 31 | 32 | /// 33 | /// Provides quick but unsafe access to a bitmap's bits.
34 | /// ALWAYS ALWAYS ALWAYS wire this in a using block.
35 | /// DO NOT EVER access the bitmap directly while in this unsafe context.
36 | ///
37 | /// The bitmap to access 38 | public UnsafeBitmapContext(Bitmap bitmap) 39 | { 40 | _bitmap = bitmap; 41 | LockBits(); 42 | } 43 | 44 | /// 45 | /// Provides quick but unsafe access to a bitmap's bits.
46 | /// ALWAYS ALWAYS ALWAYS wire this in a using block.
47 | /// DO NOT EVER access the bitmap directly while in this unsafe context.
48 | ///
49 | /// The bitmap to access 50 | public UnsafeBitmapContext(Image image) 51 | { 52 | if (!(image is Bitmap)) 53 | { 54 | throw new ArgumentException("Image must be convertable to a bitmap."); 55 | } 56 | _bitmap = (Bitmap)image; 57 | LockBits(); 58 | } 59 | 60 | /// 61 | /// Provides quick but unsafe access to a bitmap's bits.
62 | /// The stream will be updated upon disposal.
63 | /// ALWAYS ALWAYS ALWAYS wire this in a using block.
64 | /// DO NOT EVER access the bitmap directly while in this unsafe context.
65 | ///
66 | /// The stream to read and write to. 67 | public UnsafeBitmapContext(Stream stream) 68 | { 69 | try 70 | { 71 | _originalStream = stream; 72 | stream.Position = 0; 73 | _bitmap = (Bitmap)Image.FromStream(stream); 74 | } 75 | catch { throw new ArgumentException("Stream did not contain a valid image format."); } 76 | LockBits(); 77 | } 78 | 79 | private void LockBits() 80 | { 81 | Width = _bitmap.Width; 82 | Height = _bitmap.Height; 83 | _pixelWidth = sizeof(Pixel); 84 | _lockData = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); 85 | _ptrBase = (Byte*)_lockData.Scan0.ToPointer(); 86 | } 87 | 88 | public void Dispose() 89 | { 90 | _bitmap.UnlockBits(_lockData); 91 | _lockData = null; 92 | if (_originalStream != null) 93 | { 94 | _originalStream.SetLength(0); 95 | _originalStream.Position = 0; 96 | _bitmap.Save(_originalStream, _bitmap.RawFormat); 97 | _bitmap.Dispose(); 98 | _originalStream.Position = 0; 99 | } 100 | _originalStream = null; 101 | _bitmap = null; 102 | } 103 | 104 | /// 105 | /// Access a pixel from the bitmap's memory (slower than GetRawPixel) 106 | /// 107 | public Color GetPixel(int x, int y) 108 | { 109 | var pixel = GetRawPixel(x, y); 110 | return Color.FromArgb(pixel.Alpha, pixel.Red, pixel.Green, pixel.Blue); 111 | } 112 | 113 | /// 114 | /// Access a pixel from the bitmap's memory (faster than GetPixel)
115 | ///
116 | public Pixel GetRawPixel(int x, int y) 117 | { 118 | return *Pointer(x, y); 119 | } 120 | 121 | /// 122 | /// Replace a pixel in the bitmap's memory (slower than by bytes) 123 | /// 124 | public void SetPixel(int x, int y, Color color) 125 | { 126 | SetPixel(x, y, color.A, color.R, color.G, color.B); 127 | } 128 | 129 | /// 130 | /// Replace a pixel in the bitmap's memory (faster than by Color) 131 | /// 132 | public void SetPixel(int x, int y, byte alpha, byte red, byte green, byte blue) 133 | { 134 | var pixel = Pointer(x, y); 135 | (*pixel).Alpha = alpha; 136 | (*pixel).Red = red; 137 | (*pixel).Green = green; 138 | (*pixel).Blue = blue; 139 | } 140 | 141 | private Pixel* Pointer(int x, int y) 142 | { 143 | if (x >= Width || x < 0 || y >= Height || y < 0) 144 | { 145 | Dispose(); 146 | throw new ArgumentException("The X and Y parameters must be within the scope of the image pixel ranges."); 147 | } 148 | return (Pixel*)(_ptrBase + y * _lockData.Stride + x * _pixelWidth); 149 | } 150 | 151 | /// 152 | /// Represents raw pixel data. 153 | /// 154 | public struct Pixel 155 | { 156 | public byte Blue; 157 | public byte Green; 158 | public byte Red; 159 | public byte Alpha; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/BipEmulator.Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {71DD5D93-2F61-47E9-A0A3-8D0D3BC685FD} 8 | WinExe 9 | BipEmulator.Host 10 | BipEmulator.Host 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | true 36 | 37 | 38 | 39 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 40 | 41 | 42 | 43 | 44 | ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Form 63 | 64 | 65 | ColorSelectionForm.cs 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Form 83 | 84 | 85 | MainForm.cs 86 | 87 | 88 | 89 | 90 | UserControl 91 | 92 | 93 | WatchScreen.cs 94 | 95 | 96 | ColorSelectionForm.cs 97 | 98 | 99 | MainForm.cs 100 | 101 | 102 | ResXFileCodeGenerator 103 | Resources.Designer.cs 104 | Designer 105 | 106 | 107 | True 108 | Resources.resx 109 | 110 | 111 | WatchScreen.cs 112 | 113 | 114 | 115 | SettingsSingleFileGenerator 116 | Settings.Designer.cs 117 | 118 | 119 | True 120 | Settings.settings 121 | True 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | {d2090f65-51db-4643-8e60-25aff97321af} 130 | BipEmulator.Proxy 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/BipEmulator.Proxy.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {D2090F65-51DB-4643-8E60-25AFF97321AF} 24 | v4.6.1 25 | ManagedCProj 26 | BipEmulatorProxy 27 | 10.0 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | true 35 | Unicode 36 | 37 | 38 | DynamicLibrary 39 | false 40 | v142 41 | true 42 | Unicode 43 | 44 | 45 | DynamicLibrary 46 | true 47 | v142 48 | true 49 | Unicode 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v142 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | NotUsing 80 | 81 | 82 | Level3 83 | WIN32;_DEBUG;%(PreprocessorDefinitions) 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Use 92 | pch.h 93 | Level3 94 | _DEBUG;%(PreprocessorDefinitions) 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | NotUsing 103 | 104 | 105 | Level3 106 | WIN32;NDEBUG;%(PreprocessorDefinitions) 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Use 115 | pch.h 116 | Level3 117 | NDEBUG;%(PreprocessorDefinitions) 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /src/BipEmulator.Host/WatchScreen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Windows.Forms; 4 | using FastBitmapLib; 5 | 6 | namespace BipEmulator.Host 7 | { 8 | public partial class WatchScreen : UserControl 9 | { 10 | const int SIZE = 176; 11 | public Color BackgroundColor { get; set; } 12 | public Color ForegroundColor { get; set; } 13 | 14 | private Bitmap _shadowImage = new Bitmap(SIZE, SIZE); 15 | private Bitmap _primImage = new Bitmap(SIZE, SIZE); 16 | 17 | private Brush _foreBrush; 18 | private Brush _backBrush; 19 | private Pen _forePen; 20 | 21 | private FtFile _ftFile; 22 | private FtMaker _ftMaker; 23 | 24 | public ColorArray Colors { get; set; } 25 | 26 | public WatchScreen() 27 | { 28 | InitializeComponent(); 29 | DoubleBuffered = true; 30 | } 31 | 32 | public void SetFontFile(string fontFilename) 33 | { 34 | _ftFile = FtFile.Load(fontFilename); 35 | _ftMaker = new FtMaker(_ftFile); 36 | } 37 | 38 | 39 | protected override void OnSizeChanged(EventArgs e) 40 | { 41 | Width = SIZE; 42 | Height = SIZE; 43 | } 44 | 45 | public void RepaintScreenLines(int from, int to) 46 | { 47 | var r = new Rectangle(0, from, SIZE, to - from); 48 | FastBitmap.CopyRegion(_shadowImage, _primImage, r, r); 49 | } 50 | 51 | public int GetTextHeight() 52 | { 53 | if (_ftMaker == null) 54 | return 0; 55 | return _ftMaker.GetTextHeight(); 56 | } 57 | 58 | public int GetTextWidth(string text) 59 | { 60 | if (_ftMaker == null) 61 | return 0; 62 | return _ftMaker.GetTextWidth(text); 63 | } 64 | 65 | public void TextOutCenter(string text, int x, int y) 66 | { 67 | if (_ftMaker == null) 68 | return; 69 | 70 | _ftMaker.TextOutCenter(_shadowImage, text, x, y, ForegroundColor, BackgroundColor); 71 | } 72 | 73 | public void TextOut(string text, int x, int y) 74 | { 75 | if (_ftMaker == null) 76 | return; 77 | _ftMaker.TextOut(_shadowImage, text, x, y, ForegroundColor, BackgroundColor); 78 | } 79 | 80 | public void FillScreenBg() 81 | { 82 | FastBitmap.ClearBitmap(_shadowImage, BackgroundColor); 83 | } 84 | public void FillScreenFg() 85 | { 86 | FastBitmap.ClearBitmap(_shadowImage, ForegroundColor); 87 | } 88 | 89 | public void SetFgColor(Color color) 90 | { 91 | ForegroundColor = GetRealColor(color); 92 | _foreBrush = new SolidBrush(ForegroundColor); 93 | _forePen = new Pen(ForegroundColor); 94 | } 95 | public void SetBgColor(Color color) 96 | { 97 | BackgroundColor = GetRealColor(color); 98 | _backBrush = new SolidBrush(BackgroundColor); 99 | } 100 | 101 | public void DrawLine(int x0, int y0, int x1, int y1) 102 | { 103 | var g = Graphics.FromImage(_shadowImage); 104 | g.DrawLine(_forePen, x0, y0, x1, y1); 105 | } 106 | 107 | public void DrawFilledRectBg(int x0, int y0, int x1, int y1) 108 | { 109 | var g = Graphics.FromImage(_shadowImage); 110 | g.FillRectangle(_backBrush, x0, y0, x1 - x0 + 1, y1 - y0 + 1); 111 | } 112 | 113 | public void DrawFilledRect(int x0, int y0, int x1, int y1) 114 | { 115 | var g = Graphics.FromImage(_shadowImage); 116 | g.FillRectangle(_foreBrush, x0, y0, x1 - x0 + 1, y1 - y0 + 1); 117 | } 118 | 119 | public void DrawRect(int x0, int y0, int x1, int y1) 120 | { 121 | var g = Graphics.FromImage(_shadowImage); 122 | g.DrawRectangle(_forePen, x0, y0, x1 - x0 + 1, y1 - y0 + 1); 123 | } 124 | 125 | public void DrawImage(Bitmap image, int x, int y) 126 | { 127 | var g = Graphics.FromImage(_shadowImage); 128 | 129 | var fastImage = new FastBitmap(image); 130 | fastImage.Lock(); 131 | for (var i = 0; i < image.Width; i++) 132 | for (var j = 0; j < image.Height; j++) 133 | fastImage.SetPixel(i, j, GetRealColor(fastImage.GetPixel(i, j))); 134 | fastImage.Unlock(); 135 | 136 | g.DrawImage(image, x, y); 137 | } 138 | 139 | protected override void OnPaint(PaintEventArgs e) 140 | { 141 | var g = e.Graphics; 142 | g.DrawImage(_primImage, 0, 0); 143 | } 144 | 145 | private Color GetRealColor(Color color) 146 | { 147 | //return color; 148 | switch ((uint)color.ToArgb()) 149 | { 150 | // red 151 | case 0xFFFF0000: 152 | return Colors.Red; // (187, 113, 136) 153 | // green 154 | case 0xFF00FF00: 155 | return Colors.Green; // (89, 181, 132) 156 | // blue 157 | case 0xFF0000FF: 158 | return Colors.Blue; // (28, 110, 194) 159 | // yellow 160 | case 0xFFFFFF00: 161 | return Colors.Yellow; // (221, 214, 133) 162 | // aqua 163 | case 0xFF00FFFF: 164 | return Colors.Aqua; // (70, 198, 207) 165 | // purple 166 | case 0xFFFF00FF: 167 | return Colors.Purple; // (173, 147, 208) 168 | // white 169 | case 0xFFFFFFFF: 170 | return Colors.White; // (220, 220, 220) 171 | // black 172 | case 0xFF000000: 173 | return Colors.Black; // (56, 56, 56) 174 | } 175 | return color; 176 | } 177 | 178 | public static Color GetColorFromGRBInt(int value) 179 | { 180 | return Color.FromArgb(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff); 181 | } 182 | 183 | private Color GetColorByIndex(int index) 184 | { 185 | switch((ColorIndexEnum)index) 186 | { 187 | case ColorIndexEnum.Black: 188 | return Colors.Black; 189 | case ColorIndexEnum.Blue: 190 | return Colors.Blue; 191 | case ColorIndexEnum.Green: 192 | return Colors.Green; 193 | case ColorIndexEnum.Aqua: 194 | return Colors.Aqua; 195 | case ColorIndexEnum.Purple: 196 | return Colors.Purple; 197 | case ColorIndexEnum.Red: 198 | return Colors.Red; 199 | case ColorIndexEnum.Yellow: 200 | return Colors.Yellow; 201 | default: 202 | return Colors.Black; 203 | } 204 | } 205 | 206 | internal void SetVideoData(byte[] videoMemory) 207 | { 208 | var shadow = new FastBitmap(_shadowImage); 209 | shadow.Lock(); 210 | var offset = 0; 211 | for(var y=0;y> 4)); 215 | shadow.SetPixel(x+1, y, GetColorByIndex(videoMemory[offset] & 0xf)); 216 | offset++; 217 | } 218 | shadow.Unlock(); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ResImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.IO; 5 | 6 | namespace BipEmulator.Host 7 | { 8 | public class ResImage 9 | { 10 | private Bitmap _bitmap = null; 11 | 12 | private ushort _bitsPerPixel; 13 | private ushort _height; 14 | private Color[] _palette; 15 | private ushort _paletteColors; 16 | private ushort _rowLengthInBytes; 17 | private bool _transparency; 18 | private ushort _width; 19 | 20 | public Bitmap Bitmap 21 | { 22 | get 23 | { 24 | if (_bitmap == null) 25 | Read(); 26 | return _bitmap; 27 | } 28 | } 29 | 30 | private byte[] _data; 31 | 32 | public ResImage(byte[] data) 33 | { 34 | _data = data; 35 | } 36 | 37 | public void Read() 38 | { 39 | using (var ms = new MemoryStream(_data)) 40 | using (var br = new BinaryReader(ms)) 41 | { 42 | var signature = br.ReadChars(4); 43 | if (signature[0] != 'B' || signature[1] != 'M') 44 | throw new ArgumentException("Image signature doesn't match."); 45 | 46 | ReadHeader(br); 47 | if (_paletteColors > 256) 48 | throw new ArgumentException("Too many palette colors."); 49 | 50 | if (_paletteColors > 0) 51 | ReadPalette(br); 52 | //else if (_bitsPerPixel == 8 || _bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32) 53 | // Debug.WriteLine("The image doesn't use a palette."); 54 | else 55 | throw new ArgumentException( 56 | "The image format is not supported. Please report the issue on https://bitbucket.org/valeronm/amazfitbiptools"); 57 | _bitmap = ReadImage(br); 58 | } 59 | } 60 | 61 | private void ReadHeader(BinaryReader br) 62 | { 63 | //Debug.WriteLine("Reading image header..."); 64 | _width = br.ReadUInt16(); 65 | _height = br.ReadUInt16(); 66 | _rowLengthInBytes = br.ReadUInt16(); 67 | _bitsPerPixel = br.ReadUInt16(); 68 | _paletteColors = br.ReadUInt16(); 69 | _transparency = br.ReadUInt16() > 0; 70 | //Debug.WriteLine("Image header was read:"); 71 | //Debug.WriteLine("Width: {0}, Height: {1}, RowLength: {2}", _width, _height, _rowLengthInBytes); 72 | //Debug.WriteLine("BPP: {0}, PaletteColors: {1}, Transaparency: {2}", _bitsPerPixel, _paletteColors, _transparency); 73 | } 74 | 75 | private void ReadPalette(BinaryReader br) 76 | { 77 | //Debug.WriteLine("Reading palette..."); 78 | _palette = new Color[_paletteColors]; 79 | for (var i = 0; i < _paletteColors; i++) 80 | { 81 | var r = br.ReadByte(); 82 | var g = br.ReadByte(); 83 | var b = br.ReadByte(); 84 | var padding = br.ReadByte(); // always 0 maybe padding 85 | 86 | if (padding != 0) Debug.WriteLine("Palette item {0} last byte is not zero: {1:X2}", i, padding); 87 | 88 | var isColorValid = (r == 0 || r == 0xff) && (g == 0 || g == 0xff) && (b == 0 || b == 0xff); 89 | //if (isColorValid) 90 | // Debug.WriteLine("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}", i, r, g, b); 91 | //else 92 | // Debug.WriteLine("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}, color isn't supported!", i, r, g, b); 93 | 94 | var alpha = _transparency && i == 0 ? 0x00 : 0xff; 95 | _palette[i] = Color.FromArgb(alpha, r, g, b); 96 | } 97 | } 98 | 99 | private Bitmap ReadImage(BinaryReader br) 100 | { 101 | if (_paletteColors > 0) return ReadPaletteImage(br); 102 | if (_bitsPerPixel == 8) return Read8BitImage(br); 103 | if (_bitsPerPixel == 16) return Read16BitImage(br); 104 | if (_bitsPerPixel == 24) return Read24BitImage(br); 105 | if (_bitsPerPixel == 32) return Read32BitImage(br); 106 | throw new ArgumentException($"Unsupported bits per pixel value: {_bitsPerPixel}"); 107 | } 108 | 109 | private Bitmap ReadPaletteImage(BinaryReader br) 110 | { 111 | var image = new Bitmap(_width, _height); 112 | using (var context = image.CreateUnsafeContext()) 113 | { 114 | for (var y = 0; y < _height; y++) 115 | { 116 | var rowBytes = br.ReadBytes(_rowLengthInBytes); 117 | var bitReader = new BitReader(rowBytes); 118 | for (var x = 0; x < _width; x++) 119 | { 120 | var pixelColorIndex = bitReader.ReadBits(_bitsPerPixel); 121 | var color = _palette[(int)pixelColorIndex]; 122 | context.SetPixel(x, y, color); 123 | } 124 | } 125 | } 126 | 127 | return image; 128 | } 129 | 130 | private Bitmap Read8BitImage(BinaryReader br) 131 | { 132 | var image = new Bitmap(_width, _height); 133 | using (var context = image.CreateUnsafeContext()) 134 | { 135 | for (var y = 0; y < _height; y++) 136 | { 137 | var rowBytes = br.ReadBytes(_rowLengthInBytes); 138 | for (var x = 0; x < _width; x++) 139 | { 140 | var data = rowBytes[x]; 141 | var color = Color.FromArgb(0xff, data, data, data); 142 | context.SetPixel(x, y, color); 143 | } 144 | } 145 | } 146 | 147 | return image; 148 | } 149 | 150 | private Bitmap Read16BitImage(BinaryReader br) 151 | { 152 | var image = new Bitmap(_width, _height); 153 | using (var context = image.CreateUnsafeContext()) 154 | { 155 | for (var y = 0; y < _height; y++) 156 | { 157 | var rowBytes = br.ReadBytes(_rowLengthInBytes); 158 | var bitReader = new BitReader(rowBytes); 159 | for (var x = 0; x < _width; x++) 160 | { 161 | var firstByte = (int)bitReader.ReadByte(); 162 | var secondByte = (int)bitReader.ReadByte(); 163 | var b = (byte)((secondByte >> 3) & 0x1f) << 3; 164 | var g = (byte)(((firstByte >> 5) & 0x7) | ((secondByte & 0x07) << 3)) << 2; 165 | var r = (byte)(firstByte & 0x1f) << 3; 166 | var color = Color.FromArgb(0xff, r, g, b); 167 | context.SetPixel(x, y, color); 168 | } 169 | } 170 | } 171 | 172 | return image; 173 | } 174 | 175 | private Bitmap Read24BitImage(BinaryReader br) 176 | { 177 | var image = new Bitmap(_width, _height); 178 | using (var context = image.CreateUnsafeContext()) 179 | { 180 | for (var y = 0; y < _height; y++) 181 | { 182 | var rowBytes = br.ReadBytes(_rowLengthInBytes); 183 | var bitReader = new BitReader(rowBytes); 184 | for (var x = 0; x < _width; x++) 185 | { 186 | var alpha = (int)bitReader.ReadByte(); 187 | var b = (int)(bitReader.ReadBits(5) << 3); 188 | var g = (int)(bitReader.ReadBits(6) << 2); 189 | var r = (int)(bitReader.ReadBits(5) << 3); 190 | var color = Color.FromArgb(0xff - alpha, r, g, b); 191 | context.SetPixel(x, y, color); 192 | } 193 | } 194 | } 195 | 196 | return image; 197 | } 198 | 199 | private Bitmap Read32BitImage(BinaryReader br) 200 | { 201 | var image = new Bitmap(_width, _height); 202 | using (var context = image.CreateUnsafeContext()) 203 | { 204 | for (var y = 0; y < _height; y++) 205 | { 206 | var rowBytes = br.ReadBytes(_rowLengthInBytes); 207 | for (var x = 0; x < _width; x++) 208 | { 209 | var r = rowBytes[x * 4]; 210 | var g = rowBytes[x * 4 + 1]; 211 | var b = rowBytes[x * 4 + 2]; 212 | var alpha = rowBytes[x * 4 + 3]; 213 | var color = Color.FromArgb(0xff - alpha, r, g, b); 214 | context.SetPixel(x, y, color); 215 | } 216 | } 217 | } 218 | 219 | return image; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/ColorSelectionForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | partial class ColorSelectionForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 32 | this.btnReset = new System.Windows.Forms.Button(); 33 | this.lblWhite = new System.Windows.Forms.Label(); 34 | this.lblPurple = new System.Windows.Forms.Label(); 35 | this.lblAqua = new System.Windows.Forms.Label(); 36 | this.lblYellow = new System.Windows.Forms.Label(); 37 | this.lblBlue = new System.Windows.Forms.Label(); 38 | this.lblGreen = new System.Windows.Forms.Label(); 39 | this.lblRed = new System.Windows.Forms.Label(); 40 | this.lblBlack = new System.Windows.Forms.Label(); 41 | this.btnClose = new System.Windows.Forms.Button(); 42 | this.btnOK = new System.Windows.Forms.Button(); 43 | this.groupBox1.SuspendLayout(); 44 | this.SuspendLayout(); 45 | // 46 | // groupBox1 47 | // 48 | this.groupBox1.Controls.Add(this.btnReset); 49 | this.groupBox1.Controls.Add(this.lblWhite); 50 | this.groupBox1.Controls.Add(this.lblPurple); 51 | this.groupBox1.Controls.Add(this.lblAqua); 52 | this.groupBox1.Controls.Add(this.lblYellow); 53 | this.groupBox1.Controls.Add(this.lblBlue); 54 | this.groupBox1.Controls.Add(this.lblGreen); 55 | this.groupBox1.Controls.Add(this.lblRed); 56 | this.groupBox1.Controls.Add(this.lblBlack); 57 | this.groupBox1.Location = new System.Drawing.Point(12, 12); 58 | this.groupBox1.Name = "groupBox1"; 59 | this.groupBox1.Padding = new System.Windows.Forms.Padding(6); 60 | this.groupBox1.Size = new System.Drawing.Size(233, 320); 61 | this.groupBox1.TabIndex = 0; 62 | this.groupBox1.TabStop = false; 63 | this.groupBox1.Text = "Colors"; 64 | // 65 | // btnReset 66 | // 67 | this.btnReset.Enabled = false; 68 | this.btnReset.Location = new System.Drawing.Point(9, 288); 69 | this.btnReset.Name = "btnReset"; 70 | this.btnReset.Size = new System.Drawing.Size(215, 23); 71 | this.btnReset.TabIndex = 0; 72 | this.btnReset.Text = "Reset"; 73 | this.btnReset.UseVisualStyleBackColor = true; 74 | // 75 | // lblWhite 76 | // 77 | this.lblWhite.BackColor = System.Drawing.Color.Red; 78 | this.lblWhite.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 79 | this.lblWhite.Location = new System.Drawing.Point(9, 239); 80 | this.lblWhite.Margin = new System.Windows.Forms.Padding(3); 81 | this.lblWhite.Name = "lblWhite"; 82 | this.lblWhite.Size = new System.Drawing.Size(215, 25); 83 | this.lblWhite.TabIndex = 7; 84 | this.lblWhite.Text = "White"; 85 | this.lblWhite.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 86 | this.lblWhite.Click += new System.EventHandler(this.ColorLabel_Click); 87 | // 88 | // lblPurple 89 | // 90 | this.lblPurple.BackColor = System.Drawing.Color.Red; 91 | this.lblPurple.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 92 | this.lblPurple.Location = new System.Drawing.Point(9, 208); 93 | this.lblPurple.Margin = new System.Windows.Forms.Padding(3); 94 | this.lblPurple.Name = "lblPurple"; 95 | this.lblPurple.Size = new System.Drawing.Size(215, 25); 96 | this.lblPurple.TabIndex = 6; 97 | this.lblPurple.Text = "Purple"; 98 | this.lblPurple.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 99 | this.lblPurple.Click += new System.EventHandler(this.ColorLabel_Click); 100 | // 101 | // lblAqua 102 | // 103 | this.lblAqua.BackColor = System.Drawing.Color.Red; 104 | this.lblAqua.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 105 | this.lblAqua.Location = new System.Drawing.Point(9, 177); 106 | this.lblAqua.Margin = new System.Windows.Forms.Padding(3); 107 | this.lblAqua.Name = "lblAqua"; 108 | this.lblAqua.Size = new System.Drawing.Size(215, 25); 109 | this.lblAqua.TabIndex = 5; 110 | this.lblAqua.Text = "Aqua"; 111 | this.lblAqua.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 112 | this.lblAqua.Click += new System.EventHandler(this.ColorLabel_Click); 113 | // 114 | // lblYellow 115 | // 116 | this.lblYellow.BackColor = System.Drawing.Color.Red; 117 | this.lblYellow.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 118 | this.lblYellow.Location = new System.Drawing.Point(9, 146); 119 | this.lblYellow.Margin = new System.Windows.Forms.Padding(3); 120 | this.lblYellow.Name = "lblYellow"; 121 | this.lblYellow.Size = new System.Drawing.Size(215, 25); 122 | this.lblYellow.TabIndex = 4; 123 | this.lblYellow.Text = "Yellow"; 124 | this.lblYellow.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 125 | this.lblYellow.Click += new System.EventHandler(this.ColorLabel_Click); 126 | // 127 | // lblBlue 128 | // 129 | this.lblBlue.BackColor = System.Drawing.Color.Red; 130 | this.lblBlue.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 131 | this.lblBlue.Location = new System.Drawing.Point(9, 115); 132 | this.lblBlue.Margin = new System.Windows.Forms.Padding(3); 133 | this.lblBlue.Name = "lblBlue"; 134 | this.lblBlue.Size = new System.Drawing.Size(215, 25); 135 | this.lblBlue.TabIndex = 3; 136 | this.lblBlue.Text = "Blue"; 137 | this.lblBlue.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 138 | this.lblBlue.Click += new System.EventHandler(this.ColorLabel_Click); 139 | // 140 | // lblGreen 141 | // 142 | this.lblGreen.BackColor = System.Drawing.Color.Red; 143 | this.lblGreen.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 144 | this.lblGreen.Location = new System.Drawing.Point(9, 84); 145 | this.lblGreen.Margin = new System.Windows.Forms.Padding(3); 146 | this.lblGreen.Name = "lblGreen"; 147 | this.lblGreen.Size = new System.Drawing.Size(215, 25); 148 | this.lblGreen.TabIndex = 2; 149 | this.lblGreen.Text = "Green"; 150 | this.lblGreen.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 151 | this.lblGreen.Click += new System.EventHandler(this.ColorLabel_Click); 152 | // 153 | // lblRed 154 | // 155 | this.lblRed.BackColor = System.Drawing.Color.Red; 156 | this.lblRed.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 157 | this.lblRed.Location = new System.Drawing.Point(9, 53); 158 | this.lblRed.Margin = new System.Windows.Forms.Padding(3); 159 | this.lblRed.Name = "lblRed"; 160 | this.lblRed.Size = new System.Drawing.Size(215, 25); 161 | this.lblRed.TabIndex = 1; 162 | this.lblRed.Text = "Red"; 163 | this.lblRed.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 164 | this.lblRed.Click += new System.EventHandler(this.ColorLabel_Click); 165 | // 166 | // lblBlack 167 | // 168 | this.lblBlack.BackColor = System.Drawing.Color.Gray; 169 | this.lblBlack.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 170 | this.lblBlack.Location = new System.Drawing.Point(9, 22); 171 | this.lblBlack.Margin = new System.Windows.Forms.Padding(3); 172 | this.lblBlack.Name = "lblBlack"; 173 | this.lblBlack.Size = new System.Drawing.Size(215, 25); 174 | this.lblBlack.TabIndex = 0; 175 | this.lblBlack.Text = "Black"; 176 | this.lblBlack.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 177 | this.lblBlack.Click += new System.EventHandler(this.ColorLabel_Click); 178 | // 179 | // btnClose 180 | // 181 | this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; 182 | this.btnClose.Location = new System.Drawing.Point(12, 338); 183 | this.btnClose.Name = "btnClose"; 184 | this.btnClose.Size = new System.Drawing.Size(75, 23); 185 | this.btnClose.TabIndex = 0; 186 | this.btnClose.Text = "Close"; 187 | this.btnClose.UseVisualStyleBackColor = true; 188 | // 189 | // btnOK 190 | // 191 | this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; 192 | this.btnOK.Location = new System.Drawing.Point(173, 338); 193 | this.btnOK.Name = "btnOK"; 194 | this.btnOK.Size = new System.Drawing.Size(75, 23); 195 | this.btnOK.TabIndex = 1; 196 | this.btnOK.Text = "OK"; 197 | this.btnOK.UseVisualStyleBackColor = true; 198 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click); 199 | // 200 | // ColorSelectionForm 201 | // 202 | this.AcceptButton = this.btnOK; 203 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 204 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 205 | this.CancelButton = this.btnClose; 206 | this.ClientSize = new System.Drawing.Size(261, 372); 207 | this.Controls.Add(this.btnOK); 208 | this.Controls.Add(this.btnClose); 209 | this.Controls.Add(this.groupBox1); 210 | this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); 211 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 212 | this.MaximizeBox = false; 213 | this.MinimizeBox = false; 214 | this.Name = "ColorSelectionForm"; 215 | this.ShowIcon = false; 216 | this.ShowInTaskbar = false; 217 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 218 | this.Text = "Color Selection"; 219 | this.groupBox1.ResumeLayout(false); 220 | this.ResumeLayout(false); 221 | 222 | } 223 | 224 | #endregion 225 | 226 | private System.Windows.Forms.GroupBox groupBox1; 227 | private System.Windows.Forms.Button btnReset; 228 | private System.Windows.Forms.Label lblWhite; 229 | private System.Windows.Forms.Label lblPurple; 230 | private System.Windows.Forms.Label lblAqua; 231 | private System.Windows.Forms.Label lblYellow; 232 | private System.Windows.Forms.Label lblBlue; 233 | private System.Windows.Forms.Label lblGreen; 234 | private System.Windows.Forms.Label lblRed; 235 | private System.Windows.Forms.Label lblBlack; 236 | private System.Windows.Forms.Button btnClose; 237 | private System.Windows.Forms.Button btnOK; 238 | } 239 | } -------------------------------------------------------------------------------- /src/BipEmulator.Host/SymbolSet.cs: -------------------------------------------------------------------------------- 1 | namespace BipEmulator.Host 2 | { 3 | public struct SymbolSet 4 | { 5 | public static (int, int) GetResource(int symbolSet, char value) 6 | { 7 | if (symbolSet > 15) 8 | return (-1, - 1); 9 | 10 | var offsetSymbols = 0; 11 | for (var i = 0; i < symbolSet; i++) 12 | offsetSymbols += SymbolSetLengths[i]; 13 | 14 | for (var i = 0; i < SymbolSetLengths[symbolSet]; i++) 15 | if (Preset[offsetSymbols + i].Value == value) 16 | return (Preset[offsetSymbols + i].ResId, Preset[offsetSymbols + i].Width); 17 | return (-1, -1); 18 | } 19 | 20 | private static int[] SymbolSetLengths = { 10, 10, 14, 15, 10, 75, 75, 11, 16, 12, 16, 16, 20, 11, 23, 14 }; 21 | 22 | private static Symbol[] Preset = new Symbol[] 23 | { 24 | // 0 10 25 | new Symbol ('0', 11, 28), 26 | new Symbol ('1', 11, 29), 27 | new Symbol ('2', 11, 30), 28 | new Symbol ('3', 11, 31), 29 | new Symbol ('4', 11, 32), 30 | new Symbol ('5', 11, 33), 31 | new Symbol ('6', 11, 34), 32 | new Symbol ('7', 11, 35), 33 | new Symbol ('8', 11, 36), 34 | new Symbol ('9', 11, 37), 35 | // 1 10 36 | new Symbol ('0', 8, 170), 37 | new Symbol ('1', 8, 171), 38 | new Symbol ('2', 8, 172), 39 | new Symbol ('3', 8, 173), 40 | new Symbol ('4', 8, 174), 41 | new Symbol ('5', 8, 175), 42 | new Symbol ('6', 8, 176), 43 | new Symbol ('7', 8, 177), 44 | new Symbol ('8', 8, 178), 45 | new Symbol ('9', 8, 179), 46 | // 2 14 47 | new Symbol ((char)0x22, 10, 582), 48 | new Symbol ((char)0x27, 4, 583), 49 | new Symbol ('.', 4, 580), 50 | new Symbol ('0', 14, 584), 51 | new Symbol ('1', 14, 585), 52 | new Symbol ('2', 14, 586), 53 | new Symbol ('3', 14, 587), 54 | new Symbol ('4', 14, 588), 55 | new Symbol ('5', 14, 589), 56 | new Symbol ('6', 14, 590), 57 | new Symbol ('7', 14, 591), 58 | new Symbol ('8', 14, 592), 59 | new Symbol ('9', 14, 593), 60 | new Symbol (':', 4, 581), 61 | // 3 15 62 | new Symbol ((char)0xb0, 12, 594), 63 | new Symbol ('-', 13, 595), 64 | new Symbol ('.', 6, 596), 65 | new Symbol ('/', 12, 597), 66 | new Symbol ('0', 17, 599), 67 | new Symbol ('1', 17, 600), 68 | new Symbol ('2', 17, 601), 69 | new Symbol ('3', 17, 602), 70 | new Symbol ('4', 17, 603), 71 | new Symbol ('5', 17, 604), 72 | new Symbol ('6', 17, 605), 73 | new Symbol ('7', 17, 606), 74 | new Symbol ('8', 17, 607), 75 | new Symbol ('9', 17, 608), 76 | new Symbol (':', 6, 598), 77 | // 4 10 78 | new Symbol ('0', 17, 609), 79 | new Symbol ('1', 17, 610), 80 | new Symbol ('2', 17, 611), 81 | new Symbol ('3', 17, 612), 82 | new Symbol ('4', 17, 613), 83 | new Symbol ('5', 17, 614), 84 | new Symbol ('6', 17, 615), 85 | new Symbol ('7', 17, 616), 86 | new Symbol ('8', 17, 617), 87 | new Symbol ('9', 17, 618), 88 | // 5 75 89 | new Symbol ('0', 9, 619), 90 | new Symbol ('1', 9, 620), 91 | new Symbol ('2', 9, 621), 92 | new Symbol ('3', 9, 622), 93 | new Symbol ('4', 9, 623), 94 | new Symbol ('5', 9, 624), 95 | new Symbol ('6', 9, 625), 96 | new Symbol ('7', 9, 626), 97 | new Symbol ('8', 9, 627), 98 | new Symbol ('9', 9, 628), 99 | new Symbol ('-', 6, 629), 100 | new Symbol ('.', 2, 630), 101 | new Symbol ('/', 6, 631), 102 | new Symbol (':', 2, 632), 103 | new Symbol ('A', 15, 633), 104 | new Symbol ('B', 10, 634), 105 | new Symbol ('C', 11, 635), 106 | new Symbol ('D', 11, 636), 107 | new Symbol ('E', 10, 637), 108 | new Symbol ('F', 10, 638), 109 | new Symbol ('G', 12, 639), 110 | new Symbol ('H', 11, 640), 111 | new Symbol ('I', 6, 641), 112 | new Symbol ('J', 7, 642), 113 | new Symbol ('K', 12, 643), 114 | new Symbol ('L', 10, 644), 115 | new Symbol ('M', 14, 645), 116 | new Symbol ('N', 13, 646), 117 | new Symbol ('O', 13, 647), 118 | new Symbol ('P', 12, 648), 119 | new Symbol ('Q', 13, 649), 120 | new Symbol ('R', 11, 650), 121 | new Symbol ('S', 10, 651), 122 | new Symbol ('T', 12, 652), 123 | new Symbol ('U', 12, 653), 124 | new Symbol ('V', 14, 654), 125 | new Symbol ('W', 14, 655), 126 | new Symbol ('X', 15, 656), 127 | new Symbol ('Y', 14, 657), 128 | new Symbol ('Z', 13, 658), 129 | new Symbol ('a', 9, 659), 130 | new Symbol ('b', 9, 660), 131 | new Symbol ('c', 8, 661), 132 | new Symbol ('d', 9, 662), 133 | new Symbol ('e', 9, 663), 134 | new Symbol ('f', 7, 664), 135 | new Symbol ('g', 9, 665), 136 | new Symbol ('h', 8, 666), 137 | new Symbol ('i', 2, 667), 138 | new Symbol ('j', 5, 668), 139 | new Symbol ('k', 9, 669), 140 | new Symbol ('l', 2, 670), 141 | new Symbol ('m', 14, 671), 142 | new Symbol ('n', 9, 672), 143 | new Symbol ('o', 9, 673), 144 | new Symbol ('p', 9, 674), 145 | new Symbol ('q', 9, 675), 146 | new Symbol ('r', 7, 676), 147 | new Symbol ('s', 8, 677), 148 | new Symbol ('t', 7, 678), 149 | new Symbol ('u', 9, 679), 150 | new Symbol ('v', 9, 680), 151 | new Symbol ('w', 14, 681), 152 | new Symbol ('x', 9, 682), 153 | new Symbol ('y', 9, 683), 154 | new Symbol ('z', 9, 684), 155 | new Symbol ((char)0xa5, 8, 975), 156 | new Symbol ('#', 12, 978), 157 | new Symbol ((char)0x24, 8, 979), 158 | new Symbol ('%', 14, 980), 159 | new Symbol ('(', 7, 981), 160 | new Symbol (')', 7, 982), 161 | new Symbol ('@', 14, 983), 162 | new Symbol (' ', 1, 977), 163 | new Symbol ('?', 13, 976), 164 | // 6 75 165 | new Symbol ('0', 12, 685), 166 | new Symbol ('1', 12, 686), 167 | new Symbol ('2', 12, 687), 168 | new Symbol ('3', 12, 688), 169 | new Symbol ('4', 12, 689), 170 | new Symbol ('5', 12, 690), 171 | new Symbol ('6', 12, 691), 172 | new Symbol ('7', 12, 692), 173 | new Symbol ('8', 12, 693), 174 | new Symbol ('9', 12, 694), 175 | new Symbol ('-', 7, 695), 176 | new Symbol ('.', 3, 696), 177 | new Symbol ('/', 9, 697), 178 | new Symbol (':', 3, 698), 179 | new Symbol ('A', 16, 699), 180 | new Symbol ('B', 15, 700), 181 | new Symbol ('C', 16, 701), 182 | new Symbol ('D', 16, 702), 183 | new Symbol ('E', 13, 703), 184 | new Symbol ('F', 13, 704), 185 | new Symbol ('G', 16, 705), 186 | new Symbol ('H', 15, 706), 187 | new Symbol ('I', 9, 707), 188 | new Symbol ('J', 9, 708), 189 | new Symbol ('K', 17, 709), 190 | new Symbol ('L', 12, 710), 191 | new Symbol ('M', 19, 711), 192 | new Symbol ('N', 16, 712), 193 | new Symbol ('O', 17, 713), 194 | new Symbol ('P', 14, 714), 195 | new Symbol ('Q', 17, 715), 196 | new Symbol ('R', 15, 716), 197 | new Symbol ('S', 14, 717), 198 | new Symbol ('T', 15, 718), 199 | new Symbol ('U', 15, 719), 200 | new Symbol ('V', 20, 720), 201 | new Symbol ('W', 19, 721), 202 | new Symbol ('X', 18, 722), 203 | new Symbol ('Y', 19, 723), 204 | new Symbol ('Z', 17, 724), 205 | new Symbol ('a', 13, 725), 206 | new Symbol ('b', 12, 726), 207 | new Symbol ('c', 12, 727), 208 | new Symbol ('d', 12, 728), 209 | new Symbol ('e', 13, 729), 210 | new Symbol ('f', 9, 730), 211 | new Symbol ('g', 12, 731), 212 | new Symbol ('h', 12, 732), 213 | new Symbol ('i', 3, 733), 214 | new Symbol ('j', 7, 734), 215 | new Symbol ('k', 14, 735), 216 | new Symbol ('l', 3, 736), 217 | new Symbol ('m', 21, 737), 218 | new Symbol ('n', 12, 738), 219 | new Symbol ('o', 13, 739), 220 | new Symbol ('p', 13, 740), 221 | new Symbol ('q', 13, 741), 222 | new Symbol ('r', 9, 742), 223 | new Symbol ('s', 12, 743), 224 | new Symbol ('t', 9, 744), 225 | new Symbol ('u', 12, 745), 226 | new Symbol ('v', 16, 746), 227 | new Symbol ('w', 21, 747), 228 | new Symbol ('x', 15, 748), 229 | new Symbol ('y', 14, 749), 230 | new Symbol ('z', 12, 750), 231 | new Symbol ((char)0xa5, 11, 984), 232 | new Symbol ('#', 12, 987), 233 | new Symbol ((char)0x24, 12, 988), 234 | new Symbol ('%', 16, 989), 235 | new Symbol ('(', 8, 990), 236 | new Symbol (')', 8, 991), 237 | new Symbol ('@', 16, 992), 238 | new Symbol (' ', 1, 986), 239 | new Symbol ('?', 17, 985), 240 | // 7 11 241 | new Symbol ('0', 20, 751), 242 | new Symbol ('1', 20, 752), 243 | new Symbol ('2', 20, 753), 244 | new Symbol ('3', 20, 754), 245 | new Symbol ('4', 20, 755), 246 | new Symbol ('5', 20, 756), 247 | new Symbol ('6', 20, 757), 248 | new Symbol ('7', 20, 758), 249 | new Symbol ('8', 20, 759), 250 | new Symbol ('9', 20, 760), 251 | new Symbol (':', 5, 761), 252 | // 8 16 253 | new Symbol ('0', 12, 762), 254 | new Symbol ('1', 10, 763), 255 | new Symbol ('2', 12, 764), 256 | new Symbol ('3', 12, 765), 257 | new Symbol ('4', 14, 766), 258 | new Symbol ('5', 11, 767), 259 | new Symbol ('6', 12, 768), 260 | new Symbol ('7', 12, 769), 261 | new Symbol ('8', 12, 770), 262 | new Symbol ('9', 12, 771), 263 | new Symbol (':', 4, 772), 264 | new Symbol ((char)0x22, 10, 773), 265 | new Symbol ((char)0x27, 4, 774), 266 | new Symbol ('-', 11, 775), 267 | new Symbol ('.', 4, 776), 268 | new Symbol ((char)0xb0, 9, 777), 269 | // 9 12 270 | new Symbol ('0', 18, 778), 271 | new Symbol ('1', 18, 779), 272 | new Symbol ('2', 18, 780), 273 | new Symbol ('3', 18, 781), 274 | new Symbol ('4', 18, 782), 275 | new Symbol ('5', 18, 783), 276 | new Symbol ('6', 18, 784), 277 | new Symbol ('7', 18, 785), 278 | new Symbol ('8', 18, 786), 279 | new Symbol ('9', 18, 787), 280 | new Symbol (':', 6, 788), 281 | new Symbol ('/', 12, 789), 282 | // 10 16 283 | new Symbol ('0', 10, 790), 284 | new Symbol ('1', 10, 791), 285 | new Symbol ('2', 10, 792), 286 | new Symbol ('3', 10, 793), 287 | new Symbol ('4', 10, 794), 288 | new Symbol ('5', 10, 795), 289 | new Symbol ('6', 10, 796), 290 | new Symbol ('7', 10, 797), 291 | new Symbol ('8', 10, 798), 292 | new Symbol ('9', 10, 799), 293 | new Symbol (':', 2, 800), 294 | new Symbol ((char)0x22, 6, 801), 295 | new Symbol ((char)0x27, 2, 802), 296 | new Symbol ('-', 10, 803), 297 | new Symbol ('.', 2, 804), 298 | new Symbol ('/', 9, 805), 299 | // 11 16 300 | new Symbol ('0', 9, 806), 301 | new Symbol ('1', 9, 807), 302 | new Symbol ('2', 9, 808), 303 | new Symbol ('3', 9, 809), 304 | new Symbol ('4', 9, 810), 305 | new Symbol ('5', 9, 811), 306 | new Symbol ('6', 9, 812), 307 | new Symbol ('7', 9, 813), 308 | new Symbol ('8', 9, 814), 309 | new Symbol ('9', 9, 815), 310 | new Symbol (':', 7, 816), 311 | new Symbol ('-', 5, 817), 312 | new Symbol ('~', 10, 818), 313 | new Symbol ((char)0xb0, 4, 819), 314 | new Symbol ((char)0, 0, 0), 315 | new Symbol ((char)0, 0, 0), 316 | // 12 20 317 | new Symbol ('0', 8, 820), 318 | new Symbol ('1', 6, 821), 319 | new Symbol ('2', 8, 822), 320 | new Symbol ('3', 8, 823), 321 | new Symbol ('4', 8, 824), 322 | new Symbol ('5', 8, 825), 323 | new Symbol ('6', 8, 826), 324 | new Symbol ('7', 8, 827), 325 | new Symbol ('8', 8, 828), 326 | new Symbol ('9', 8, 829), 327 | new Symbol ('/', 8, 830), 328 | new Symbol ((char)0x4e00, 12, 831), 329 | new Symbol ((char)0x4e8c, 12, 832), 330 | new Symbol ((char)0x4e09, 12, 833), 331 | new Symbol ((char)0x4e94, 12, 834), 332 | new Symbol ((char)0x56db, 12, 835), 333 | new Symbol ((char)0x516d, 12, 836), 334 | new Symbol ((char)0x65e5, 12, 837), 335 | new Symbol ((char)0x5468, 12, 838), 336 | new Symbol ((char)0x9031, 15, 839), 337 | // 13 11 338 | new Symbol ('0', 6, 840), 339 | new Symbol ('1', 5, 841), 340 | new Symbol ('2', 6, 842), 341 | new Symbol ('3', 6, 843), 342 | new Symbol ('4', 6, 844), 343 | new Symbol ('5', 6, 845), 344 | new Symbol ('6', 6, 846), 345 | new Symbol ('7', 6, 847), 346 | new Symbol ('8', 6, 848), 347 | new Symbol ('9', 6, 849), 348 | new Symbol ('/', 6, 850), 349 | // 14 23 350 | new Symbol ('0', 7, 851), 351 | new Symbol ('1', 7, 852), 352 | new Symbol ('2', 7, 853), 353 | new Symbol ('3', 7, 854), 354 | new Symbol ('4', 7, 855), 355 | new Symbol ('5', 7, 856), 356 | new Symbol ('6', 7, 857), 357 | new Symbol ('7', 7, 858), 358 | new Symbol ('8', 7, 859), 359 | new Symbol ('9', 7, 860), 360 | new Symbol (':', 1, 861), 361 | new Symbol ('-', 7, 863), 362 | new Symbol ('/', 6, 864), 363 | new Symbol (' ', 1, 862), 364 | new Symbol ((char)0x4e00, 11, 865), 365 | new Symbol ((char)0x4e8c, 11, 866), 366 | new Symbol ((char)0x4e09, 11, 867), 367 | new Symbol ((char)0x4e94, 11, 868), 368 | new Symbol ((char)0x56db, 11, 869), 369 | new Symbol ((char)0x516d, 11, 870), 370 | new Symbol ((char)0x65e5, 11, 871), 371 | new Symbol ((char)0x5468, 11, 872), 372 | new Symbol ((char)0x9031, 14, 873), 373 | // 15 14 374 | new Symbol ('0', 7, 874), 375 | new Symbol ('1', 7, 875), 376 | new Symbol ('2', 7, 876), 377 | new Symbol ('3', 7, 877), 378 | new Symbol ('4', 7, 878), 379 | new Symbol ('5', 7, 879), 380 | new Symbol ('6', 7, 880), 381 | new Symbol ('7', 7, 881), 382 | new Symbol ('8', 7, 882), 383 | new Symbol ('9', 7, 883), 384 | new Symbol ('-', 7, 884), 385 | new Symbol ('/', 7, 885), 386 | new Symbol ('~', 7, 886), 387 | new Symbol ((char)0x2103, 7, 887), 388 | }; 389 | } 390 | 391 | public struct Symbol 392 | { 393 | public char Value { get; set; } 394 | public int Width { get; set; } 395 | public int ResId { get; set; } 396 | 397 | public Symbol(char value, int width, int resId) 398 | { 399 | Value = value; 400 | Width = width; 401 | ResId = resId; 402 | } 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /src/BipEmulator.Proxy/calend.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | (С) Волков Максим 2019 ( Maxim.N.Volkov@ya.ru ) 3 | 4 | Календарь v1.0 5 | Приложение простого календаря. 6 | Алгоритм вычисления дня недели работает для любой даты григорианского календаря позднее 1583 года. 7 | Григорианский календарь начал действовать в 1582 — после 4 октября сразу настало 15 октября. 8 | 9 | Календарь от 1600 до 3000 года 10 | Функции перелистывания каленаря вверх-вниз - месяц, стрелками год 11 | При нажатии на название месяца устанавливается текущая дата 12 | 13 | 14 | v.1.1 15 | - исправлены переходы в при запуске из бстрого меню 16 | 17 | */ 18 | 19 | #include "libbip.h" 20 | #include "calend.h" 21 | #define DEBUG_LOG 22 | 23 | // структура меню экрана календаря 24 | struct regmenu_ menu_calend_screen = { 25 | 55, 26 | 1, 27 | 0, 28 | dispatch_calend_screen, 29 | key_press_calend_screen, 30 | calend_screen_job, 31 | 0, 32 | show_calend_screen, 33 | 0, 34 | 0 35 | }; 36 | 37 | int main(int param0, char** argv){ // переменная argv не определена 38 | show_calend_screen((void*) param0); 39 | } 40 | 41 | void show_calend_screen (void *param0){ 42 | struct calend_** calend_p = (calend_ **)get_ptr_temp_buf_2(); // указатель на указатель на данные экрана 43 | struct calend_ * calend; // указатель на данные экрана 44 | struct calend_opt_ calend_opt; // опции календаря 45 | 46 | #ifdef DEBUG_LOG 47 | log_printf(5, "[show_calend_screen] param0=%X; *temp_buf_2=%X; menu_overlay=%d", (int)param0, (int*)get_ptr_temp_buf_2(), get_var_menu_overlay()); 48 | log_printf(5, " #calend_p=%X; *calend_p=%X", (int)calend_p, (int)*calend_p); 49 | #endif 50 | 51 | if ( (param0 == *calend_p) && get_var_menu_overlay()){ // возврат из оверлейного экрана (входящий звонок, уведомление, будильник, цель и т.д.) 52 | 53 | #ifdef DEBUG_LOG 54 | log_printf(5, " #from overlay"); 55 | log_printf(5, "\r\n"); 56 | #endif 57 | 58 | calend = *calend_p; // указатель на данные необходимо сохранить для исключения 59 | // высвобождения памяти функцией reg_menu 60 | *calend_p = (calend_*)NULL; // обнуляем указатель для передачи в функцию reg_menu 61 | 62 | // создаем новый экран, при этом указатели temp_buf_1 и temp_buf_2 были равны 0 и память не была высвобождена 63 | reg_menu(&menu_calend_screen, 0); // menu_overlay=0 64 | 65 | *calend_p = calend; // восстанавливаем указатель на данные после функции reg_menu 66 | 67 | draw_month(0, calend->month, calend->year); 68 | 69 | } else { // если запуск функции произошел из меню, 70 | 71 | #ifdef DEBUG_LOG 72 | log_printf(5, " #from menu"); 73 | log_printf(5, "\r\n"); 74 | #endif 75 | // создаем экран 76 | reg_menu(&menu_calend_screen, 0); 77 | 78 | // выделяем необходимую память и размещаем в ней данные 79 | *calend_p = (struct calend_ *)pvPortMalloc(sizeof(struct calend_)); 80 | calend = *calend_p; // указатель на данные 81 | 82 | // очистим память под данные 83 | _memclr(calend, sizeof(struct calend_)); 84 | 85 | calend->proc = (Elf_proc_*)param0; 86 | 87 | // запомним адрес указателя на функцию в которую необходимо вернуться после завершения данного экрана 88 | if ( param0 && calend->proc->elf_finish ) // если указатель на возврат передан, то возвоащаемся на него 89 | calend->ret_f = calend->proc->elf_finish; 90 | else // если нет, то на циферблат 91 | calend->ret_f = show_watchface; 92 | 93 | struct datetime_ datetime; 94 | _memclr(&datetime, sizeof(struct datetime_)); 95 | 96 | // получим текущую дату 97 | get_current_date_time(&datetime); 98 | 99 | calend->day = datetime.day; 100 | calend->month = datetime.month; 101 | calend->year = datetime.year; 102 | 103 | // считаем опции из flash памяти, если значение в флэш-памяти некорректное то берем первую схему 104 | // текущая цветовая схема хранится о смещению 0 105 | ElfReadSettings(calend->proc->index_listed, &calend_opt, OPT_OFFSET_CALEND_OPT, sizeof(struct calend_opt_)); 106 | 107 | if (calend_opt.color_scheme < COLOR_SCHEME_COUNT) 108 | calend->color_scheme = calend_opt.color_scheme; 109 | else 110 | calend->color_scheme = 0; 111 | 112 | draw_month(calend->day, calend->month, calend->year); 113 | } 114 | 115 | // при бездействии погасить подсветку и не выходить 116 | set_display_state_value(8, 1); 117 | set_display_state_value(2, 1); 118 | 119 | // таймер на job на 20с где выход. 120 | set_update_period(1, INACTIVITY_PERIOD); 121 | 122 | } 123 | 124 | void draw_month(unsigned int day, unsigned int month, unsigned int year){ 125 | struct calend_** calend_p = (calend_ **)get_ptr_temp_buf_2(); // указатель на указатель на данные экрана 126 | struct calend_ * calend = *calend_p; // указатель на данные экрана 127 | 128 | 129 | /* 130 | 0: CALEND_COLOR_BG фон календаря 131 | 1: CALEND_COLOR_MONTH цвет названия текущего месяца 132 | 2: CALEND_COLOR_YEAR цвет текущего года 133 | 3: CALEND_COLOR_WORK_NAME цвет названий дней будни 134 | 4: CALEND_COLOR_HOLY_NAME_BG фон названий дней выходные 135 | 5: CALEND_COLOR_HOLY_NAME_FG цвет названий дней выходные 136 | 6: CALEND_COLOR_SEPAR цвет разделителей календаря 137 | 7: CALEND_COLOR_NOT_CUR_WORK цвет чисел НЕ текущего месяца будни 138 | 8: CALEND_COLOR_NOT_CUR_HOLY_BG фон чисел НЕ текущего месяца выходные 139 | 9: CALEND_COLOR_NOT_CUR_HOLY_FG цвет чисел НЕ текущего месяца выходные 140 | 10: CALEND_COLOR_CUR_WORK цвет чисел текущего месяца будни 141 | 11: CALEND_COLOR_CUR_HOLY_BG фон чисел текущего месяца выходные 142 | 12: CALEND_COLOR_CUR_HOLY_FG цвет чисел текущего месяца выходные 143 | 13: CALEND_COLOR_TODAY_BG фон чисел текущего дня; bit 31 - заливка: =0 заливка цветом фона, =1 только рамка, фон как у числа не текущего месяца 144 | 14: CALEND_COLOR_TODAY_FG цвет чисел текущего дня 145 | */ 146 | 147 | 148 | static unsigned char short_color_scheme[COLOR_SCHEME_COUNT][15] = 149 | /* черная тема без выделения выходных*/ {// 0 1 2 3 4 5 6 150 | {COLOR_SH_BLACK, COLOR_SH_YELLOW, COLOR_SH_AQUA, COLOR_SH_WHITE, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_WHITE, 151 | COLOR_SH_GREEN, COLOR_SH_BLACK, COLOR_SH_AQUA, COLOR_SH_YELLOW, COLOR_SH_BLACK, COLOR_SH_WHITE, COLOR_SH_YELLOW, COLOR_SH_BLACK}, 152 | // 7 8 9 10 11 12 13 14 153 | // 0 1 2 3 4 5 6 154 | /* белая тема без выделения выходных*/ {COLOR_SH_WHITE, COLOR_SH_BLACK, COLOR_SH_BLUE, COLOR_SH_BLACK, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_BLACK, 155 | COLOR_SH_BLUE, COLOR_SH_WHITE, COLOR_SH_AQUA, COLOR_SH_BLACK, COLOR_SH_WHITE, COLOR_SH_RED, COLOR_SH_BLUE, COLOR_SH_WHITE}, 156 | // 7 8 9 10 11 12 13 14 157 | // 0 1 2 3 4 5 6 158 | /* черная тема с выделением выходных*/ {COLOR_SH_BLACK, COLOR_SH_YELLOW, COLOR_SH_AQUA, COLOR_SH_WHITE, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_WHITE, 159 | COLOR_SH_GREEN, COLOR_SH_RED, COLOR_SH_AQUA, COLOR_SH_YELLOW, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_AQUA, COLOR_SH_BLACK}, 160 | // 7 8 9 10 11 12 13 14 161 | // 0 1 2 3 4 5 6 162 | /* белая тема с выделением выходных*/ {COLOR_SH_WHITE, COLOR_SH_BLACK, COLOR_SH_BLUE, COLOR_SH_BLACK, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_BLACK, 163 | COLOR_SH_BLUE, COLOR_SH_RED, COLOR_SH_BLUE, COLOR_SH_BLACK, COLOR_SH_RED, COLOR_SH_BLACK, COLOR_SH_BLUE, COLOR_SH_WHITE}, 164 | // 7 8 9 10 11 12 13 14 165 | // 0 1 2 3 4 5 6 166 | /* черная тема без выделения выходных*/ {COLOR_SH_BLACK, COLOR_SH_YELLOW, COLOR_SH_AQUA, COLOR_SH_WHITE, COLOR_SH_RED, COLOR_SH_WHITE, COLOR_SH_WHITE, 167 | /*с рамкой выделения сегодняшнего дня*/ COLOR_SH_GREEN, COLOR_SH_BLACK, COLOR_SH_AQUA, COLOR_SH_YELLOW, COLOR_SH_BLACK, COLOR_SH_WHITE, COLOR_SH_AQUA|(1<<7), COLOR_SH_BLACK}, 168 | // 7 8 9 10 11 12 13 14 169 | 170 | }; 171 | 172 | int color_scheme[COLOR_SCHEME_COUNT][15]; 173 | 174 | 175 | for (unsigned char i=0;icolor_scheme][CALEND_COLOR_BG]); // фон календаря 268 | fill_screen_bg(); 269 | 270 | set_graph_callback_to_ram_1(); 271 | load_font(); // подгружаем шрифты 272 | 273 | _sprintf(&text_buffer[0], " %d", year); 274 | int month_text_width = text_width(monthname[month]); 275 | int year_text_width = text_width(&text_buffer[0]); 276 | 277 | set_fg_color(color_scheme[calend->color_scheme][CALEND_COLOR_MONTH]); // цвет месяца 278 | text_out(monthname[month], (176-month_text_width-year_text_width)/2 ,5); // вывод названия месяца 279 | 280 | set_fg_color(color_scheme[calend->color_scheme][CALEND_COLOR_YEAR]); // цвет года 281 | text_out(&text_buffer[0], (176+month_text_width-year_text_width)/2 ,5); // вывод года 282 | 283 | text_out("←", 5 ,5); // вывод стрелки влево 284 | text_out("→", 176-5-text_width("→"),5); // вывод стрелки вправо 285 | 286 | int calend_name_height = get_text_height(); 287 | 288 | set_fg_color(color_scheme[calend->color_scheme][CALEND_COLOR_SEPAR]); 289 | draw_horizontal_line(CALEND_Y_BASE, H_MARGIN, 176-H_MARGIN); // Верхний разделитель названий дней недели 290 | draw_horizontal_line(CALEND_Y_BASE+1+V_MARGIN+calend_name_height+1+V_MARGIN, H_MARGIN, 176-H_MARGIN); // Нижний разделитель названий дней недели 291 | //draw_horizontal_line(175, H_MARGIN, 176-H_MARGIN); // Нижний разделитель месяца 292 | 293 | // Названия дней недели 294 | for (unsigned i=1; (i<=7);i++){ 295 | if ( i>5 ){ // выходные 296 | set_bg_color(color_scheme[calend->color_scheme][CALEND_COLOR_HOLY_NAME_BG]); 297 | set_fg_color(color_scheme[calend->color_scheme][CALEND_COLOR_HOLY_NAME_FG]); 298 | } else { // рабочие 299 | set_bg_color(color_scheme[calend->color_scheme][CALEND_COLOR_BG]); 300 | set_fg_color(color_scheme[calend->color_scheme][CALEND_COLOR_WORK_NAME]); 301 | } 302 | 303 | 304 | // отрисовка фона названий выходных 305 | int pos_x1 = H_MARGIN +(i-1)*(WIDTH + H_SPACE); 306 | int pos_y1 = CALEND_Y_BASE+V_MARGIN+1; 307 | int pos_x2 = pos_x1 + WIDTH; 308 | int pos_y2 = pos_y1 + calend_name_height; 309 | 310 | // фон для каждого названия дня недели 311 | draw_filled_rect_bg(pos_x1, pos_y1, pos_x2, pos_y2); 312 | 313 | // вывод названий дней недели. если ширина названия больше чем ширина поля, выводим короткие названия 314 | if (text_width(weekday_string[1]) <= (WIDTH - 2)) 315 | text_out_center(weekday_string[i], pos_x1 + WIDTH/2, pos_y1 + (calend_name_height-get_text_height())/2 ); 316 | else 317 | text_out_center(weekday_string_short[i], pos_x1 + WIDTH/2, pos_y1 + (calend_name_height-get_text_height())/2 ); 318 | } 319 | 320 | 321 | int calend_days_y_base = CALEND_Y_BASE+1+V_MARGIN+calend_name_height+V_MARGIN+1; 322 | 323 | if (isLeapYear(year)>0) day_month[2]=29; 324 | 325 | unsigned char d=wday(1,month, year); 326 | unsigned char m=month; 327 | if (d>1) { 328 | m=(month==1)?12:month-1; 329 | d=day_month[m]-d+2; 330 | } 331 | 332 | // числа месяца 333 | for (unsigned i=1; (i<=7*6);i++){ 334 | 335 | unsigned char row = (i-1)/7; 336 | unsigned char col = (i-1)%7+1; 337 | 338 | _sprintf (&text_buffer[0], "%2.0d", d); 339 | 340 | int bg_color = 0; 341 | int fg_color = 0; 342 | int frame_color = 0; // цветрамки 343 | int frame = 0; // 1-рамка; 0 - заливка 344 | 345 | // если текущий день текущего месяца 346 | if ( (m==month)&&(d==day) ){ 347 | 348 | if ( color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_BG] & (1<<31) ) {// если заливка отключена только рамка 349 | 350 | // цвет рамки устанавливаем CALEND_COLOR_TODAY_BG, фон внутри рамки и цвет текста такой же как был 351 | frame_color = (color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_BG &COLOR_MASK]); 352 | // рисуем рамку 353 | frame = 1; 354 | 355 | if ( col > 5 ){ // если выходные 356 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_HOLY_BG]); 357 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_HOLY_FG]); 358 | } else { // если будни 359 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_BG]); 360 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_WORK]); 361 | }; 362 | 363 | } else { // если включена заливка 364 | if ( col > 5 ){ // если выходные 365 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_BG] & COLOR_MASK); 366 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_FG]); 367 | } else { // если будни 368 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_BG] &COLOR_MASK); 369 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_TODAY_FG]); 370 | }; 371 | }; 372 | 373 | 374 | } else { 375 | if ( col > 5 ){ // если выходные 376 | if (month == m){ 377 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_HOLY_BG]); 378 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_HOLY_FG]); 379 | } else { 380 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_NOT_CUR_HOLY_BG]); 381 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_NOT_CUR_HOLY_FG]); 382 | } 383 | } else { // если будни 384 | if (month == m){ 385 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_BG]); 386 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_CUR_WORK]); 387 | } else { 388 | bg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_BG]); 389 | fg_color = (color_scheme[calend->color_scheme][CALEND_COLOR_NOT_CUR_WORK]); 390 | } 391 | } 392 | } 393 | 394 | 395 | 396 | // строка: от 7 до 169 = 162рх в ширину 7 чисел по 24рх на число 7+(22+2)*6+22+3 397 | // строки: от 57 до 174 = 117рх в высоту 6 строк по 19рх на строку 1+(17+2)*5+18 398 | 399 | // отрисовка фона числа 400 | int pos_x1 = H_MARGIN +(col-1)*(WIDTH + H_SPACE); 401 | int pos_y1 = calend_days_y_base + V_MARGIN + row *(HEIGHT + V_SPACE)+1; 402 | int pos_x2 = pos_x1 + WIDTH; 403 | int pos_y2 = pos_y1 + HEIGHT; 404 | 405 | if (frame){ 406 | // выводим число 407 | set_bg_color(bg_color); 408 | set_fg_color(fg_color); 409 | text_out_center(&text_buffer[0], pos_x1+WIDTH/2, pos_y1+(HEIGHT-get_text_height())/2); 410 | 411 | // рисуем рамку 412 | set_fg_color(frame_color); 413 | draw_rect(pos_x1, pos_y1, pos_x2-1, pos_y2-1); 414 | } else { 415 | // рисуем заливку 416 | set_bg_color(bg_color); 417 | draw_filled_rect_bg(pos_x1, pos_y1, pos_x2, pos_y2); 418 | 419 | // выводим число 420 | set_fg_color(fg_color); 421 | text_out_center(&text_buffer[0], pos_x1+WIDTH/2, pos_y1+(HEIGHT-get_text_height())/2); 422 | }; 423 | 424 | 425 | 426 | 427 | if ( d < day_month[m] ) { 428 | d++; 429 | } else { 430 | d=1; 431 | m=(m==12)?1:(m+1); 432 | } 433 | } 434 | 435 | 436 | 437 | }; 438 | 439 | unsigned char wday(unsigned int day,unsigned int month,unsigned int year) 440 | { 441 | signed int a = (14 - month) / 12; 442 | signed int y = year - a; 443 | signed int m = month + 12 * a - 2; 444 | return 1+(((day + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7) +6)%7; 445 | } 446 | 447 | unsigned char isLeapYear(unsigned int year){ 448 | unsigned char result = 0; 449 | if ( (year % 4) == 0 ) result++; 450 | if ( (year % 100) == 0 ) result--; 451 | if ( (year % 400) == 0 ) result++; 452 | return result; 453 | } 454 | 455 | void key_press_calend_screen(){ 456 | struct calend_** calend_p = (calend_ **)get_ptr_temp_buf_2(); // указатель на указатель на данные экрана 457 | struct calend_ * calend = *calend_p; // указатель на данные экрана 458 | 459 | show_menu_animate(calend->ret_f, (unsigned int)show_calend_screen, ANIMATE_RIGHT); 460 | }; 461 | 462 | 463 | void calend_screen_job(){ 464 | struct calend_** calend_p = (calend_ **)get_ptr_temp_buf_2(); // указатель на указатель на данные экрана 465 | struct calend_ * calend = *calend_p; // указатель на данные экрана 466 | 467 | // при достижении таймера обновления выходим 468 | show_menu_animate(calend->ret_f, (unsigned int)show_calend_screen, ANIMATE_LEFT); 469 | } 470 | 471 | int dispatch_calend_screen (void *param){ 472 | struct calend_** calend_p = (calend_ **)get_ptr_temp_buf_2(); // указатель на указатель на данные экрана 473 | struct calend_ * calend = *calend_p; // указатель на данные экрана 474 | 475 | struct calend_opt_ calend_opt; // опции календаря 476 | 477 | struct datetime_ datetime; 478 | // получим текущую дату 479 | 480 | 481 | get_current_date_time(&datetime); 482 | unsigned int day; 483 | 484 | // char text_buffer[32]; 485 | struct gesture_ *gest = (gesture_*) param; 486 | int result = 0; 487 | 488 | switch (gest->gesture){ 489 | case GESTURE_CLICK: { 490 | 491 | // вибрация при любом нажатии на экран 492 | vibrate (1, 40, 0); 493 | 494 | 495 | if ( gest->touch_pos_y < CALEND_Y_BASE ){ // кликнули по верхней строке 496 | if (gest->touch_pos_x < 44){ 497 | if ( calend->year > 1600 ) calend->year--; 498 | } else 499 | if (gest->touch_pos_x > (176-44)){ 500 | if ( calend->year < 3000 ) calend->year++; 501 | } else { 502 | calend->day = datetime.day; 503 | calend->month = datetime.month; 504 | calend->year = datetime.year; 505 | } 506 | 507 | if ( (calend->year == datetime.year) && (calend->month == datetime.month) ){ 508 | day = datetime.day; 509 | } else { 510 | day = 0; 511 | } 512 | draw_month(day, calend->month, calend->year); 513 | repaint_screen_lines(1, 176); 514 | 515 | } else { // кликнули в календарь 516 | 517 | calend->color_scheme = ((calend->color_scheme+1)%COLOR_SCHEME_COUNT); 518 | 519 | // сначала обновим экран 520 | if ( (calend->year == datetime.year) && (calend->month == datetime.month) ){ 521 | day = datetime.day; 522 | } else { 523 | day = 0; 524 | } 525 | draw_month(day, calend->month, calend->year); 526 | repaint_screen_lines(1, 176); 527 | 528 | // потом запись опций во flash память, т.к. это долгая операция 529 | // TODO: 1. если опций будет больше чем цветовая схема - переделать сохранение, чтобы сохранять перед выходом. 530 | calend_opt.color_scheme = calend->color_scheme; 531 | 532 | // запишем настройки в флэш память 533 | ElfWriteSettings(calend->proc->index_listed, &calend_opt, OPT_OFFSET_CALEND_OPT, sizeof(struct calend_opt_)); 534 | } 535 | 536 | // продлить таймер выхода при бездействии через INACTIVITY_PERIOD с 537 | set_update_period(1, INACTIVITY_PERIOD); 538 | break; 539 | }; 540 | 541 | case GESTURE_SWIPE_RIGHT: // свайп направо 542 | case GESTURE_SWIPE_LEFT: { // справа налево 543 | 544 | if ( get_left_side_menu_active()){ 545 | set_update_period(0,0); 546 | 547 | void* show_f = get_ptr_show_menu_func(); 548 | 549 | // запускаем dispatch_left_side_menu с параметром param в результате произойдет запуск соответствующего бокового экрана 550 | // при этом произойдет выгрузка данных текущего приложения и его деактивация. 551 | dispatch_left_side_menu((gesture_*)param); 552 | 553 | if ( get_ptr_show_menu_func() == show_f ){ 554 | // если dispatch_left_side_menu отработал безуспешно (листать некуда) то в show_menu_func по прежнему будет 555 | // содержаться наша функция show_calend_screen, тогда просто игнорируем этот жест 556 | 557 | // продлить таймер выхода при бездействии через INACTIVITY_PERIOD с 558 | set_update_period(1, INACTIVITY_PERIOD); 559 | return 0; 560 | } 561 | 562 | 563 | // если dispatch_left_side_menu отработал, то завершаем наше приложение, т.к. данные экрана уже выгрузились 564 | // на этом этапе уже выполняется новый экран (тот куда свайпнули) 565 | 566 | 567 | Elf_proc_* proc = get_proc_by_addr(main); 568 | proc->ret_f = NULL; 569 | 570 | elf_finish(main); // выгрузить Elf из памяти 571 | return 0; 572 | } else { // если запуск не из быстрого меню, обрабатываем свайпы по отдельности 573 | switch (gest->gesture){ 574 | case GESTURE_SWIPE_RIGHT: { // свайп направо 575 | return show_menu_animate(calend->ret_f, (unsigned int)show_calend_screen, ANIMATE_RIGHT); 576 | break; 577 | } 578 | case GESTURE_SWIPE_LEFT: { // справа налево 579 | // действие при запуске из меню и дальнейший свайп влево 580 | 581 | 582 | break; 583 | } 584 | } /// switch (gest->gesture) 585 | } 586 | 587 | break; 588 | }; // case GESTURE_SWIPE_LEFT: 589 | 590 | 591 | case GESTURE_SWIPE_UP: { // свайп вверх 592 | if ( calend->month < 12 ) 593 | calend->month++; 594 | else { 595 | calend->month = 1; 596 | calend->year++; 597 | } 598 | 599 | if ( (calend->year == datetime.year) && (calend->month == datetime.month) ) 600 | day = datetime.day; 601 | else 602 | day = 0; 603 | draw_month(day, calend->month, calend->year); 604 | repaint_screen_lines(1, 176); 605 | 606 | // продлить таймер выхода при бездействии через INACTIVITY_PERIOD с 607 | set_update_period(1, INACTIVITY_PERIOD); 608 | break; 609 | }; 610 | case GESTURE_SWIPE_DOWN: { // свайп вниз 611 | if ( calend->month > 1 ) 612 | calend->month--; 613 | else { 614 | calend->month = 12; 615 | calend->year--; 616 | } 617 | 618 | if ( (calend->year == datetime.year) && (calend->month == datetime.month) ) 619 | day = datetime.day; 620 | else 621 | day = 0; 622 | draw_month(day, calend->month, calend->year); 623 | repaint_screen_lines(1, 176); 624 | 625 | // продлить таймер выхода при бездействии через INACTIVITY_PERIOD с 626 | set_update_period(1, INACTIVITY_PERIOD); 627 | break; 628 | }; 629 | default:{ // что-то пошло не так... 630 | break; 631 | }; 632 | 633 | } 634 | 635 | 636 | return result; 637 | }; 638 | -------------------------------------------------------------------------------- /src/BipEmulator.Host/MainForm.cs: -------------------------------------------------------------------------------- 1 | using BipEmulatorProxy; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.IO.MemoryMappedFiles; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using System.Windows.Forms; 13 | 14 | namespace BipEmulator.Host 15 | { 16 | public partial class MainForm : Form, IMessageFilter 17 | { 18 | const int VIDEO_MEMORY_SIZE = 15488; 19 | /// 20 | /// Функция обратного вызова 21 | /// 22 | private static ProxyLib.Callback _callback; 23 | 24 | Point _mouseDownPoint; 25 | 26 | private ResFile _systemResFile = null; 27 | private ResFile _userResFile = null; 28 | 29 | private LocalSettings _settings; 30 | 31 | private int _newDebugOutputLines = 0; 32 | 33 | private IntPtr _hrmDataPointer = IntPtr.Zero; 34 | 35 | private bool _useSharedVideoMemory; 36 | MemoryMappedFile _sharedMemoryFile; 37 | MemoryMappedViewStream _sharedMemoryViewStream; 38 | private byte[] _videoMemory = new byte[VIDEO_MEMORY_SIZE]; 39 | 40 | public MainForm() 41 | { 42 | InitializeComponent(); 43 | // установим фильтр для отлова клавиатурных сообщений 44 | Application.AddMessageFilter(this); 45 | 46 | // locale 47 | foreach (var locale in Enum.GetValues(typeof(LocaleEnum))) 48 | { 49 | var name = DisplayNameAttribute.GetName(locale); 50 | cbLocale.Items.Add(name); 51 | } 52 | 53 | _settings = LocalSettings.Load(); 54 | FillFormValuesFromSettings(); 55 | 56 | // устанавливаем обработчики на мышь для 57 | // создания событий по свайпам 58 | ucScreen.MouseDown += WatchScreen_MouseDown; 59 | ucScreen.MouseUp += WatchScreen_MouseUp; 60 | ucScreen.MouseMove += WatchScreen_MouseMove; 61 | ucScreen.MouseLeave += WatchScreen_MouseLeave; 62 | 63 | // информация о версии 64 | var version = Assembly.GetEntryAssembly()?.GetName().Version; 65 | base.Text = $"Bip Emulator v{version.Major}.{version.Minor}"; 66 | } 67 | 68 | private void MainForm_Load(object sender, EventArgs e) 69 | { 70 | // пробуем загрузить файл шрифтов 71 | if (string.IsNullOrEmpty(tbFontFilename.Text) || !File.Exists(tbFontFilename.Text)) 72 | { 73 | MessageBox.Show("Font file don't exist or not selected!" + Environment.NewLine + "Check Options Tab!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); 74 | } 75 | 76 | // регистрируем функцию обратного вызова 77 | _callback = new ProxyLib.Callback(CallbackParser); 78 | ProxyLib.RegisterCallback(_callback); 79 | 80 | // запускаем на выполнение программу, которая эмулируется 81 | ProxyLib.MainRun(); 82 | } 83 | 84 | /// 85 | /// Разгребатель прилетевших от прокси обратных вызовов функций 86 | /// 87 | /// аргументы функций 88 | /// 89 | private unsafe object CallbackParser(object[] args) 90 | { 91 | if (args == null || args.Length == 0 || args[0] == null) 92 | return null; 93 | 94 | switch(args[0]) 95 | { 96 | // Отрабатываем все функции графического отображения и относящиеся к ним 97 | 98 | case FunctionNames.DRAW_FILLED_RECT: 99 | ucScreen.DrawFilledRect((int)args[1], (int)args[2], (int)args[3], (int)args[4]); 100 | break; 101 | 102 | case FunctionNames.DRAW_FILLED_RECT_BG: 103 | ucScreen.DrawFilledRectBg((int)args[1], (int)args[2], (int)args[3], (int)args[4]); 104 | break; 105 | 106 | case FunctionNames.DRAW_RECT: 107 | ucScreen.DrawRect((int)args[1], (int)args[2], (int)args[3], (int)args[4]); 108 | break; 109 | 110 | case FunctionNames.DRAW_HORIZONTAL_LINE: 111 | ucScreen.DrawLine((int)args[2], (int)args[1], (int)args[3], (int)args[1]); 112 | break; 113 | 114 | case FunctionNames.DRAW_VERTICAL_LINE: 115 | ucScreen.DrawLine((int)args[1], (int)args[2], (int)args[1], (int)args[3]); 116 | break; 117 | 118 | case FunctionNames.FILL_SCREEN_BG: 119 | ucScreen.FillScreenBg(); 120 | break; 121 | 122 | case FunctionNames.TEXT_OUT: 123 | ucScreen.TextOut(args[1].ToString(), (int)args[2], (int)args[3]); 124 | break; 125 | 126 | case FunctionNames.TEXT_OUT_CENTER: 127 | ucScreen.TextOutCenter(args[1].ToString(), (int)args[2], (int)args[3]); 128 | break; 129 | 130 | case FunctionNames.SET_FG_COLOR: 131 | ucScreen.SetFgColor(WatchScreen.GetColorFromGRBInt((int)args[1])); 132 | break; 133 | 134 | case FunctionNames.SET_BG_COLOR: 135 | ucScreen.SetBgColor(WatchScreen.GetColorFromGRBInt((int)args[1])); 136 | break; 137 | 138 | case FunctionNames.GET_TEXT_HEIGHT: 139 | return ucScreen.GetTextHeight(); 140 | 141 | case FunctionNames.TEXT_WIDTH: 142 | return ucScreen.GetTextWidth(args[1].ToString()); 143 | 144 | case FunctionNames.REPAINT_SCREEN_LINES: 145 | 146 | if (_useSharedVideoMemory) 147 | { 148 | _sharedMemoryViewStream.Position = 0; 149 | _sharedMemoryViewStream.Read(_videoMemory, 0, 15488); 150 | ucScreen.SetVideoData(_videoMemory); 151 | } 152 | ucScreen.RepaintScreenLines((int)args[1], (int)args[2]); 153 | 154 | if (ucScreen.InvokeRequired) 155 | { 156 | try 157 | { 158 | Invoke(new Action(() => 159 | { 160 | ucScreen.Refresh(); 161 | })); 162 | } 163 | catch 164 | { 165 | Debug.WriteLine("Exception CallbackParser REPAINT_SCREEN_LINES"); 166 | } 167 | } 168 | else 169 | ucScreen.Refresh(); 170 | break; 171 | 172 | case FunctionNames.SHOW_ELF_RES_BY_ID: 173 | try 174 | { 175 | if (_userResFile == null) 176 | { 177 | _userResFile = ResFile.Load(tbUserResFile.Text); 178 | } 179 | 180 | var resImage = new ResImage(_userResFile.Resources[(int)args[2]]); 181 | ucScreen.DrawImage(resImage.Bitmap, (int)args[3], (int)args[4]); 182 | } 183 | catch 184 | { 185 | Debug.WriteLine("Exception CallbackParser SHOW_ELF_RES_BY_ID"); 186 | } 187 | 188 | return 0; 189 | 190 | case FunctionNames.SHOW_RES_BY_ID: 191 | ShowResById((int)args[1], (int)args[2], (int)args[3]); 192 | return 0; 193 | 194 | case FunctionNames.GET_RES_PARAMS: 195 | { 196 | var res = GetUserResImage((int)args[2]); 197 | if (res == null) 198 | return -1; 199 | 200 | var bmp = res.Bitmap; 201 | 202 | var param = new ResParam { Width = (short)bmp.Width, Height = (short)bmp.Height }; 203 | CopyStructToUnmanagedMemory(param, (IntPtr)args[3]); 204 | return 0; 205 | } 206 | case FunctionNames.READ_ELF_RES_BY_ID: 207 | { 208 | // only own resources 209 | if ((int)args[1] != -1) 210 | return -1; 211 | 212 | var data = GetUserResData(/*resId*/(int)args[2],/*offset*/(int)args[3], (int)args[5]/*len*/); 213 | Marshal.Copy(data, 0, (IntPtr)args[4], data.Length); 214 | return 0; 215 | } 216 | case FunctionNames.SHOW_BIG_DIGITS: 217 | ShowBigDigit((int)args[1], args[2].ToString(), (int)args[3], (int)args[4], (int)args[5]); 218 | break; 219 | 220 | case FunctionNames.LOG_PRINTF: 221 | DebugWriteLine(args[1].ToString()); 222 | return 0; 223 | 224 | case FunctionNames.GET_LAST_HEARTRATE: 225 | int heartRate = 0; 226 | if (nudHeartRate.InvokeRequired) 227 | Invoke(new Action(() => 228 | { 229 | heartRate = (int)nudHeartRate.Value; 230 | })); 231 | else 232 | heartRate = (int)nudHeartRate.Value; 233 | return heartRate; 234 | 235 | case FunctionNames.GET_HRM_STRUCT: 236 | HrmData hrmData = new HrmData(); 237 | if (nudHeartRate.InvokeRequired) 238 | { 239 | Invoke(new Action(() => 240 | { 241 | hrmData = new HrmData 242 | { 243 | heart_rate = (byte)nudHeartRate.Value, 244 | last_hr = (byte)nudHeartRate.Value, 245 | ret_code = (byte)(cbHeartRateMeasurementComplited.Checked ? 0 : 5) 246 | }; 247 | })); 248 | } 249 | else 250 | hrmData = new HrmData 251 | { 252 | heart_rate = (byte)nudHeartRate.Value, 253 | last_hr = (byte)nudHeartRate.Value, 254 | ret_code = (byte)(cbHeartRateMeasurementComplited.Checked ? 0 : 5) 255 | }; 256 | 257 | 258 | if (_hrmDataPointer == IntPtr.Zero) 259 | _hrmDataPointer = Marshal.AllocHGlobal(Marshal.SizeOf(hrmData)); 260 | Marshal.StructureToPtr(hrmData, _hrmDataPointer, false); 261 | return _hrmDataPointer.ToInt32(); 262 | 263 | case FunctionNames.GET_NAVI_DATA: 264 | NaviData naviData = new NaviData(); 265 | if (cbGeoLocationMeasurementComplited.InvokeRequired) 266 | { 267 | Invoke(new Action(() => 268 | { 269 | var alt = GetFloatFromText(tbAltitude.Text); 270 | var lat = GetFloatFromText(tbLatitude.Text); 271 | var lon = GetFloatFromText(tbLongitude.Text); 272 | 273 | naviData = new NaviData 274 | { 275 | ready = (cbPressureMeasurementComplited.Checked ? 1 : 0) | (cbGeoLocationMeasurementComplited.Checked ? 0x0e : 0), 276 | pressure = (uint)((float)nudPressure.Value * 133.322f), 277 | altitude = alt, 278 | latitude = (int)(3e6 * Math.Abs(lat)), 279 | ns = lat < 0 ? 0 : 1, 280 | longitude = (int)(3e6 * Math.Abs(lon)), 281 | ew = lon < 0 ? 2 : 3 282 | }; 283 | })); 284 | } 285 | else 286 | { 287 | var alt = GetFloatFromText(tbAltitude.Text); 288 | var lat = GetFloatFromText(tbLatitude.Text); 289 | var lon = GetFloatFromText(tbLongitude.Text); 290 | 291 | naviData = new NaviData 292 | { 293 | ready = (cbPressureMeasurementComplited.Checked ? 1 : 0) | (cbGeoLocationMeasurementComplited.Checked ? 0x0e : 0), 294 | pressure = (uint)((float)nudPressure.Value * 133.322f), 295 | altitude = alt, 296 | latitude = (int)(3e6 * Math.Abs(lat)), 297 | ns = lat < 0 ? 1 : 0, 298 | longitude = (int)(3e6 * Math.Abs(lon)), 299 | ew = lon < 0 ? 2 : 3 300 | }; 301 | } 302 | CopyStructToUnmanagedMemory(naviData, (IntPtr)args[1]); 303 | break; 304 | 305 | case FunctionNames.IS_GPS_FIXED: 306 | int isGpsFixed = 0; 307 | if (cbGeoLocationMeasurementComplited.InvokeRequired) 308 | { 309 | Invoke(new Action(() => 310 | { 311 | isGpsFixed = cbGeoLocationMeasurementComplited.Checked ? 1 : 0; 312 | })); 313 | } 314 | else 315 | isGpsFixed = cbGeoLocationMeasurementComplited.Checked ? 1 : 0; 316 | return isGpsFixed; 317 | 318 | case FunctionNames.GET_SELECTED_LOCALE: 319 | LocaleEnum locale = LocaleEnum.ru_RU; 320 | if (cbLocale.InvokeRequired) 321 | { 322 | Invoke(new Action(() => 323 | { 324 | locale = GetCurrentLocale(); 325 | })); 326 | } 327 | else 328 | { 329 | locale = GetCurrentLocale(); 330 | } 331 | return (int)locale; 332 | 333 | case FunctionNames.SHARED_MEMORY_ENABLED: 334 | _useSharedVideoMemory = OpenFileMapping(); 335 | return _useSharedVideoMemory ? 1 : 0; 336 | 337 | // заглушки 338 | 339 | case FunctionNames.VIBRATE: 340 | case FunctionNames.SET_UPDATE_PERIOD: 341 | return 0; 342 | 343 | case FunctionNames.LOAD_FONT: 344 | case FunctionNames.REG_MENU: 345 | break; 346 | 347 | default: 348 | Debug.WriteLine($"UNKNOWN FUNCTION: {args[0]}"); 349 | break; 350 | 351 | } 352 | return null; 353 | } 354 | 355 | private bool OpenFileMapping() 356 | { 357 | try 358 | { 359 | _sharedMemoryFile = MemoryMappedFile.OpenExisting("meme", MemoryMappedFileRights.ReadWrite); 360 | _sharedMemoryViewStream = _sharedMemoryFile.CreateViewStream(); 361 | } 362 | catch(Exception e) 363 | { 364 | return false; 365 | } 366 | 367 | return true; 368 | } 369 | 370 | private LocaleEnum GetCurrentLocale() 371 | { 372 | foreach (LocaleEnum locale in Enum.GetValues(typeof(LocaleEnum))) 373 | { 374 | if (DisplayNameAttribute.GetName(locale).Equals(cbLocale.SelectedItem)) 375 | { 376 | return locale; 377 | } 378 | } 379 | return LocaleEnum.ru_RU; 380 | } 381 | 382 | private float GetFloatFromText(string str) 383 | { 384 | if (string.IsNullOrEmpty(str)) 385 | return 0f; 386 | 387 | float res; 388 | if (float.TryParse(str, out res)) 389 | return res; 390 | 391 | if (float.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) 392 | return res; 393 | 394 | return 0f; 395 | } 396 | 397 | private unsafe void CopyStructToUnmanagedMemory(T t, IntPtr dst) where T : struct 398 | { 399 | IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(t)); 400 | Marshal.StructureToPtr(t, pnt, false); 401 | Buffer.MemoryCopy(pnt.ToPointer(), dst.ToPointer(), Marshal.SizeOf(t), Marshal.SizeOf(t)); 402 | Marshal.FreeHGlobal(pnt); 403 | } 404 | 405 | private byte[] GetUserResData(int resId, int offset, int len) 406 | { 407 | try 408 | { 409 | if (_userResFile == null) 410 | { 411 | _userResFile = ResFile.Load(tbUserResFile.Text); 412 | var data = new byte[len]; 413 | if (offset + len > _userResFile.Resources[resId].Length) 414 | return null; 415 | Buffer.BlockCopy(_userResFile.Resources[resId], 0, data, 0, len); 416 | return data; 417 | } 418 | } 419 | catch 420 | { 421 | Debug.WriteLine("Exception GetUserResData"); 422 | } 423 | return null; 424 | } 425 | 426 | private ResImage GetUserResImage(int resId) 427 | { 428 | try 429 | { 430 | if (_userResFile == null) 431 | { 432 | _userResFile = ResFile.Load(tbUserResFile.Text); 433 | } 434 | 435 | return new ResImage(_userResFile.Resources[resId]); 436 | } 437 | catch 438 | { 439 | Debug.WriteLine("Exception GetUserResImage"); 440 | } 441 | return null; 442 | } 443 | 444 | private ResImage GetSystemResImage(int resId) 445 | { 446 | try 447 | { 448 | if (_systemResFile == null) 449 | { 450 | _systemResFile = ResFile.Load(tbSystemResFile.Text); 451 | } 452 | 453 | return new ResImage(_systemResFile.Resources[resId]); 454 | } 455 | catch 456 | { 457 | Debug.WriteLine("Exception GetSystemResImage"); 458 | } 459 | return null; 460 | } 461 | 462 | private void ShowResById(int resId, int x, int y) 463 | { 464 | var resImage = GetSystemResImage(resId); 465 | if (resImage == null) 466 | return; 467 | 468 | ucScreen.DrawImage(resImage.Bitmap, x, y); 469 | } 470 | 471 | private void ShowBigDigit(int symbolSet, string value, int x, int y, int space) 472 | { 473 | for(var i=0; i 487 | /// Перехватываем сообщения с клавиатуры и передаем их прокси 488 | /// 489 | /// 490 | public bool PreFilterMessage(ref Message m) 491 | { 492 | if (m.Msg != 0x0100) 493 | return false; 494 | 495 | switch ((Keys)m.WParam.ToInt32()) 496 | { 497 | case Keys.Up: 498 | lblAction.Text = "Swipe Up"; 499 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_UP, 0, 0); 500 | btnSwipeUp.Focus(); 501 | break; 502 | case Keys.Down: 503 | lblAction.Text = "Swipe Down"; 504 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_DOWN, 0, 0); 505 | btnSwipeDown.Focus(); 506 | break; 507 | case Keys.Left: 508 | lblAction.Text = "Swipe Left"; 509 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_LEFT, 0, 0); 510 | btnSwipeLeft.Focus(); 511 | break; 512 | case Keys.Right: 513 | lblAction.Text = "Swipe Right"; 514 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_RIGHT, 0, 0); 515 | btnSwipeRight.Focus(); 516 | break; 517 | default: 518 | return false; 519 | } 520 | 521 | return true; 522 | } 523 | 524 | private void WatchScreen_MouseUp(object sender, MouseEventArgs e) 525 | { 526 | var dx = e.X - _mouseDownPoint.X; 527 | var dy = e.Y - _mouseDownPoint.Y; 528 | 529 | if (Math.Abs(dx) < _settings.MaxDistanceClick && Math.Abs(dy) < _settings.MaxDistanceClick) 530 | { 531 | lblAction.Text = "Click"; 532 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_CLICK, _mouseDownPoint.X, _mouseDownPoint.Y); 533 | btnClick.Focus(); 534 | } 535 | else if (Math.Abs(dx) > Math.Abs(dy)) 536 | { 537 | lblAction.Text = dx < 0 ? "Swipe Left" : "Swipe Right"; 538 | ProxyLib.RegMenuDispatchScreen(dx < 0 ? (byte)GestureEnum.GESTURE_SWIPE_LEFT : (byte)GestureEnum.GESTURE_SWIPE_RIGHT, e.X, e.Y); 539 | if (dx < 0) 540 | btnSwipeLeft.Focus(); 541 | else 542 | btnSwipeRight.Focus(); 543 | } 544 | else 545 | { 546 | lblAction.Text = dy < 0 ? "Swipe Up" : "Swipe Down"; 547 | ProxyLib.RegMenuDispatchScreen(dy < 0 ? (byte)GestureEnum.GESTURE_SWIPE_UP : (byte)GestureEnum.GESTURE_SWIPE_DOWN, e.X, e.Y); 548 | if (dy < 0) 549 | btnSwipeUp.Focus(); 550 | else 551 | btnSwipeDown.Focus(); 552 | } 553 | } 554 | 555 | private void WatchScreen_MouseMove(object sender, MouseEventArgs e) 556 | { 557 | lblMouseLocation.Text = $"X: {e.X}\r\nY: {e.Y}"; 558 | } 559 | 560 | private void WatchScreen_MouseLeave(object sender, EventArgs e) 561 | { 562 | lblMouseLocation.Text = string.Empty; 563 | } 564 | 565 | private void WatchScreen_MouseDown(object sender, MouseEventArgs e) 566 | { 567 | _mouseDownPoint = e.Location; 568 | } 569 | 570 | /// 571 | /// При закрытии формы все очищаем и сохраняем настройки 572 | /// 573 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 574 | { 575 | ProxyLib.UnregisterCallback(); 576 | 577 | FillSettingsFromFormValues(); 578 | _settings.Save(); 579 | } 580 | 581 | /// 582 | /// Заполняем значения элементов формы параметрами из сохраненных настроек 583 | /// 584 | private void FillFormValuesFromSettings() 585 | { 586 | nudHeartRate.Value = _settings.HeartRate; 587 | cbHeartRateMeasurementComplited.Checked = _settings.HeartRateMeasurementCompleted; 588 | tbLatitude.Text = _settings.Latitude.ToString("F6"); 589 | tbLongitude.Text = _settings.Longitude.ToString("F6"); 590 | tbAltitude.Text = _settings.Altitude.ToString("F2"); 591 | cbGeoLocationMeasurementComplited.Checked = _settings.GeoLocationMeasurementCompleted; 592 | nudPressure.Value = _settings.Pressure; 593 | cbPressureMeasurementComplited.Checked = _settings.PressureMeasurementCompleted; 594 | cbLocale.SelectedItem = DisplayNameAttribute.GetName(_settings.Locale); 595 | tbFontFilename.Text = _settings.FontFilename; 596 | tbSystemResFile.Text = _settings.SystemResourceFilename; 597 | tbUserResFile.Text = _settings.UserResourceFilename; 598 | ucScreen.Colors = _settings.Colors; 599 | 600 | cbLocale.SelectedItem = DisplayNameAttribute.GetName(_settings.Locale); 601 | 602 | if (File.Exists(tbFontFilename.Text)) 603 | ucScreen.SetFontFile(tbFontFilename.Text); 604 | } 605 | 606 | /// 607 | /// Заполняем настройки для сохранения параметрами из элементов формы 608 | /// 609 | private void FillSettingsFromFormValues() 610 | { 611 | _settings.HeartRate = (int)nudHeartRate.Value; 612 | _settings.HeartRateMeasurementCompleted = cbHeartRateMeasurementComplited.Checked; 613 | _settings.Latitude = double.Parse(tbLatitude.Text); 614 | _settings.Longitude = double.Parse(tbLongitude.Text); 615 | _settings.Altitude = double.Parse(tbAltitude.Text); 616 | _settings.GeoLocationMeasurementCompleted = cbGeoLocationMeasurementComplited.Checked; 617 | _settings.Pressure = (int)nudPressure.Value; 618 | _settings.PressureMeasurementCompleted = cbPressureMeasurementComplited.Checked; 619 | _settings.Locale = GetCurrentLocale(); 620 | _settings.FontFilename = tbFontFilename.Text; 621 | _settings.SystemResourceFilename = tbSystemResFile.Text; 622 | _settings.UserResourceFilename = tbUserResFile.Text; 623 | _settings.Colors = ucScreen.Colors; 624 | } 625 | 626 | private void btnClose_Click(object sender, EventArgs e) 627 | { 628 | Close(); 629 | } 630 | 631 | /// 632 | /// Настройка цветовых параметров 633 | /// 634 | private void btnColorsSelect_Click(object sender, EventArgs e) 635 | { 636 | var frm = new ColorSelectionForm(ucScreen.Colors); 637 | if (frm.ShowDialog() == DialogResult.OK) 638 | { 639 | ucScreen.Colors = frm.Colors; 640 | } 641 | } 642 | 643 | /// 644 | /// Выбо файла шрифта 645 | /// 646 | private void btnFontSelect_Click(object sender, EventArgs e) 647 | { 648 | var ofd = new OpenFileDialog 649 | { 650 | Filter = @"Font Files (*.ft)|*.ft|" + @"All files (*.*)|*.*", 651 | FilterIndex = 1, 652 | Multiselect = false, 653 | RestoreDirectory = true, 654 | }; 655 | 656 | if (ofd.ShowDialog() == DialogResult.OK) 657 | { 658 | tbFontFilename.Text = ofd.FileName; 659 | ucScreen.SetFontFile(ofd.FileName); 660 | } 661 | } 662 | 663 | /// 664 | /// Выбор системного файла ресурсов 665 | /// 666 | private void btnSystemResFileSelect_Click(object sender, EventArgs e) 667 | { 668 | var ofd = new OpenFileDialog 669 | { 670 | Filter = @"Res Files (*.res)|*.res|" + @"All files (*.*)|*.*", 671 | FilterIndex = 1, 672 | Multiselect = false, 673 | RestoreDirectory = true, 674 | }; 675 | 676 | if (ofd.ShowDialog() == DialogResult.OK) 677 | { 678 | _systemResFile = null; 679 | tbSystemResFile.Text = ofd.FileName; 680 | } 681 | } 682 | 683 | /// 684 | /// Выбор пользовательского файла ресурсов 685 | /// относящего к эмулируемой программе 686 | /// 687 | private void btnUserResFileSelect_Click(object sender, EventArgs e) 688 | { 689 | var ofd = new OpenFileDialog 690 | { 691 | Filter = @"Res Files (*.res)|*.res|" + @"All files (*.*)|*.*", 692 | FilterIndex = 1, 693 | Multiselect = false, 694 | RestoreDirectory = true, 695 | }; 696 | 697 | if (ofd.ShowDialog() == DialogResult.OK) 698 | { 699 | _userResFile = null; 700 | tbUserResFile.Text = ofd.FileName; 701 | } 702 | } 703 | 704 | /// 705 | /// Отладочный вывод эмулятора 706 | /// 707 | private void DebugWriteLine(string text) 708 | { 709 | if (rtbDebugLog.InvokeRequired) 710 | Invoke(new Action(() => 711 | { 712 | rtbDebugLog.Text += text + Environment.NewLine; 713 | })); 714 | else 715 | rtbDebugLog.Text += text + Environment.NewLine; 716 | 717 | if (tpDebug.InvokeRequired) 718 | { 719 | Invoke(new Action(() => 720 | { 721 | if (tcMain.SelectedTab != tpDebug) 722 | _newDebugOutputLines++; 723 | })); 724 | } 725 | else 726 | { 727 | if (tcMain.SelectedTab != tpDebug) 728 | _newDebugOutputLines++; 729 | } 730 | 731 | ShowDebugTabPageTitle(); 732 | } 733 | 734 | private void ShowDebugTabPageTitle() 735 | { 736 | var title = "Debug Output"; 737 | if (_newDebugOutputLines > 0) 738 | title += $" ({_newDebugOutputLines})"; 739 | 740 | if (tpDebug.InvokeRequired) 741 | { 742 | Invoke(new Action(() => 743 | { 744 | tpDebug.Text = title; 745 | })); 746 | } 747 | else 748 | tpDebug.Text = title; 749 | } 750 | 751 | private void btnControl_Click(object sender, EventArgs e) 752 | { 753 | var btn = (Button)sender; 754 | if (btn == null) 755 | return; 756 | 757 | // bad bad bad practice 758 | switch(btn.Name) 759 | { 760 | case "btnSwipeUp": 761 | lblAction.Text = "Swipe Up"; 762 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_UP, 0, 0); 763 | btnSwipeUp.Focus(); 764 | break; 765 | case "btnSwipeDown": 766 | lblAction.Text = "Swipe Down"; 767 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_DOWN, 0, 0); 768 | btnSwipeDown.Focus(); 769 | break; 770 | case "btnSwipeLeft": 771 | lblAction.Text = "Swipe Left"; 772 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_LEFT, 0, 0); 773 | btnSwipeLeft.Focus(); 774 | break; 775 | case "btnSwipeRight": 776 | lblAction.Text = "Swipe Right"; 777 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_SWIPE_RIGHT, 0, 0); 778 | btnSwipeRight.Focus(); 779 | break; 780 | case "btnClick": 781 | lblAction.Text = "Click"; 782 | ProxyLib.RegMenuDispatchScreen((byte)GestureEnum.GESTURE_CLICK, 176 / 2, 176 / 2); 783 | btnClick.Focus(); 784 | break; 785 | case "btnShortKeypress": 786 | lblAction.Text = "Short Keypress"; 787 | ProxyLib.RegMenuKeyPress(); 788 | btnShortKeypress.Focus(); 789 | break; 790 | case "btnLongKeypress": 791 | lblAction.Text = "Long Keypress"; 792 | ProxyLib.RegMenuLongKeyPress(); 793 | btnLongKeypress.Focus(); 794 | break; 795 | default: 796 | System.Diagnostics.Debug.WriteLine($"UNKNOWN BUTTON: {btn.Name} IN btnControl_Click"); 797 | break; 798 | } 799 | } 800 | 801 | private void tcMain_SelectedIndexChanged(object sender, EventArgs e) 802 | { 803 | if (tcMain.SelectedTab == tpDebug) 804 | _newDebugOutputLines = 0; 805 | ShowDebugTabPageTitle(); 806 | } 807 | } 808 | } 809 | --------------------------------------------------------------------------------