├── .gitignore ├── DEBUG.md ├── HOWTO.md ├── HOWTOCRYPTO.md ├── README.md ├── img ├── add.png ├── crypto0.jpg ├── crypto1.jpg ├── crypto2.jpg ├── crypto3.jpg ├── crypto4.jpg ├── crypto5.jpg ├── crypto6.jpg ├── crypto7.jpg ├── crypto8.jpg ├── crypto9.jpg ├── delete.png ├── down.png ├── duplicate.png ├── filtering.png ├── image.png ├── man0.png ├── man1.png ├── man10.png ├── man11.png ├── man2.png ├── man3.png ├── man4.png ├── man5.png ├── man6.png ├── man7.png ├── man8.png ├── man9.png ├── new.png ├── open.png ├── save.png ├── up.png └── wallet.png └── src ├── AboutForm.Designer.cs ├── AboutForm.cs ├── AboutForm.resx ├── AnytoneEncEncryptionMethodForm.Designer.cs ├── AnytoneEncEncryptionMethodForm.cs ├── AnytoneEncEncryptionMethodForm.resx ├── AnytoneEncEncryptionRow.cs ├── BaseEncryptionRow.cs ├── CFT.csproj ├── CFT.sln ├── CftFile.cs ├── DebugLogsFilteringForm.Designer.cs ├── DebugLogsFilteringForm.cs ├── DebugLogsFilteringForm.resx ├── DisplayAdditionalInfo.cs ├── DisplayAdditionalInfoForm.Designer.cs ├── DisplayAdditionalInfoForm.cs ├── DisplayAdditionalInfoForm.resx ├── DisplayAdditionalInfoValuesEnum.cs ├── DisplayNameAttribute.cs ├── DisplayTagObject.cs ├── DmrActivateOptions.cs ├── DmrActivateOptionsControl.Designer.cs ├── DmrActivateOptionsControl.cs ├── DmrActivateOptionsControl.resx ├── DmrAesEncryptionMethodForm.Designer.cs ├── DmrAesEncryptionMethodForm.cs ├── DmrAesEncryptionMethodForm.resx ├── DmrAesEncryptionRow.cs ├── DmrAesKeyLengthEnum.cs ├── DmrColorCodeEnum.cs ├── DmrEncryptionRow.cs ├── DmrEncyptionValueEnum.cs ├── DmrMfidEnum.cs ├── DmrSelectedActivateOptionsEnum.cs ├── DmrTimeSlotEnum.cs ├── DmrTrunkSystemEnum.cs ├── DsdImport.cs ├── EncryptionMethodEnum.cs ├── EncryptionMethodForm.resx ├── HyteraBPEncryptionMethodForm.Designer.cs ├── HyteraBPEncryptionMethodForm.cs ├── HyteraBPEncryptionMethodForm.resx ├── HyteraBPEncryptionRow.cs ├── HyteraEPEncryptionMethodForm.Designer.cs ├── HyteraEPEncryptionMethodForm.cs ├── HyteraEPEncryptionMethodForm.resx ├── HyteraEPEncryptionRow.cs ├── HyteraKeyLengthEnum.cs ├── IActivateOptions.cs ├── IDmrEncryptionMethod.cs ├── IEncryptionRow.cs ├── ImportForm.Designer.cs ├── ImportForm.cs ├── ImportForm.resx ├── ImportItem.cs ├── KeyMapFunctionEnum.cs ├── KeyMapping.cs ├── KeyMappingForm.Designer.cs ├── KeyMappingForm.cs ├── KeyMappingForm.resx ├── KirisunBPEncryptionMethodForm.Designer.cs ├── KirisunBPEncryptionMethodForm.cs ├── KirisunBPEncryptionMethodForm.resx ├── KirisunBPEncryptionRow.cs ├── KirisunKeyLengthEnum.cs ├── Licensing.cs ├── LicensingForm.Designer.cs ├── LicensingForm.cs ├── LicensingForm.resx ├── ListViewExtensions.cs ├── MacAddressForm.Designer.cs ├── MacAddressForm.cs ├── MacAddressForm.resx ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── MotorolaBPEncryptionMethod.cs ├── MotorolaBPEncryptionMethodForm.Designer.cs ├── MotorolaBPEncryptionMethodForm.cs ├── MotorolaBPEncryptionMethodForm.resx ├── MotorolaBPEncryptionRow.cs ├── MotorolaEPEncryptionMethodForm.Designer.cs ├── MotorolaEPEncryptionMethodForm.cs ├── MotorolaEPEncryptionMethodForm.resx ├── MotorolaEPEncryptionRow.cs ├── NxdnActivateOptions.cs ├── NxdnCypherTypeEnum.cs ├── NxdnScramblerEncryptionMethodForm.Designer.cs ├── NxdnScramblerEncryptionMethodForm.cs ├── NxdnScramblerEncryptionMethodForm.resx ├── NxdnScramblerEncryptionRow.cs ├── NxdnSelectOptionsEnum.cs ├── NxdnSelectedActivateOptionsEnum.cs ├── P25ADPEncryptionMethodForm.Designer.cs ├── P25ADPEncryptionMethodForm.cs ├── P25ADPEncryptionMethodForm.resx ├── P25ADPEncryptionRow.cs ├── P25AESEncryptionMethodForm.Designer.cs ├── P25AESEncryptionMethodForm.cs ├── P25AESEncryptionMethodForm.resx ├── P25AESEncryptionRow.cs ├── P25ActivateOptions.cs ├── P25ActivateOptionsControl.Designer.cs ├── P25ActivateOptionsControl.cs ├── P25ActivateOptionsControl.resx ├── P25DESEncryptionMethodForm.Designer.cs ├── P25DESEncryptionMethodForm.cs ├── P25DESEncryptionMethodForm.resx ├── P25DESEncryptionRow.cs ├── P25EncryptionRow.cs ├── P25SelectedActivateOptionsEnum.cs ├── Program.cs ├── Project.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── ProtocolEnum.cs ├── Scanner.cs ├── ScannerAttribute.cs ├── ScannerBaseModelEnum.cs ├── ScannerForm.Designer.cs ├── ScannerForm.cs ├── ScannerForm.resx ├── ScannerListForm.Designer.cs ├── ScannerListForm.cs ├── ScannerListForm.resx ├── ScannerModelEnum.cs ├── SelectEncryptionMethodForm.Designer.cs ├── SelectEncryptionMethodForm.cs ├── SelectEncryptionMethodForm.resx ├── TyteraBPEncryptionMethodForm.Designer.cs ├── TyteraBPEncryptionMethodForm.cs ├── TyteraBPEncryptionMethodForm.resx ├── TyteraBPEncryptionRow.cs ├── TyteraEPEncryptionMethodForm.Designer.cs ├── TyteraEPEncryptionMethodForm.cs ├── TyteraEPEncryptionMethodForm.resx ├── TyteraEPEncryptionRow.cs ├── Utils.cs ├── app.ico ├── fakesign.snk ├── images ├── JD.png ├── add.png ├── delete.png ├── down.png ├── duplicate.png ├── new.png ├── open.png ├── save.png └── up.png └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studo 2015 cache/options directory 25 | .vs/ 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | # NUNIT 32 | *.VisualState.xml 33 | TestResult.xml 34 | 35 | # Build Results of an ATL Project 36 | [Dd]ebugPS/ 37 | [Rr]eleasePS/ 38 | dlldata.c 39 | 40 | *_i.c 41 | *_p.c 42 | *_i.h 43 | *.ilk 44 | *.meta 45 | *.obj 46 | *.pch 47 | *.pdb 48 | *.pgc 49 | *.pgd 50 | *.rsp 51 | *.sbr 52 | *.tlb 53 | *.tli 54 | *.tlh 55 | *.tmp 56 | *.tmp_proj 57 | *.log 58 | *.vspscc 59 | *.vssscc 60 | .builds 61 | *.pidb 62 | *.svclog 63 | *.scc 64 | 65 | # Chutzpah Test files 66 | _Chutzpah* 67 | 68 | # Visual C++ cache files 69 | ipch/ 70 | *.aps 71 | *.ncb 72 | *.opensdf 73 | *.sdf 74 | *.cachefile 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | *.vspx 80 | 81 | # TFS 2012 Local Workspace 82 | $tf/ 83 | 84 | # Guidance Automation Toolkit 85 | *.gpState 86 | 87 | # ReSharper is a .NET coding add-in 88 | _ReSharper*/ 89 | *.[Rr]e[Ss]harper 90 | *.DotSettings.user 91 | 92 | # JustCode is a .NET coding addin-in 93 | .JustCode 94 | 95 | # TeamCity is a build add-in 96 | _TeamCity* 97 | 98 | # DotCover is a Code Coverage Tool 99 | *.dotCover 100 | 101 | # NCrunch 102 | _NCrunch_* 103 | .*crunch*.local.xml 104 | 105 | # MightyMoose 106 | *.mm.* 107 | AutoTest.Net/ 108 | 109 | # Web workbench (sass) 110 | .sass-cache/ 111 | 112 | # Installshield output folder 113 | [Ee]xpress/ 114 | 115 | # DocProject is a documentation generator add-in 116 | DocProject/buildhelp/ 117 | DocProject/Help/*.HxT 118 | DocProject/Help/*.HxC 119 | DocProject/Help/*.hhc 120 | DocProject/Help/*.hhk 121 | DocProject/Help/*.hhp 122 | DocProject/Help/Html2 123 | DocProject/Help/html 124 | 125 | # Click-Once directory 126 | publish/ 127 | 128 | # Publish Web Output 129 | *.[Pp]ublish.xml 130 | *.azurePubxml 131 | # TODO: Comment the next line if you want to checkin your web deploy settings 132 | # but database connection strings (with potential passwords) will be unencrypted 133 | *.pubxml 134 | *.publishproj 135 | 136 | # NuGet Packages 137 | *.nupkg 138 | # The packages folder can be ignored because of Package Restore 139 | **/packages/* 140 | # except build/, which is used as an MSBuild target. 141 | !**/packages/build/ 142 | # Uncomment if necessary however generally it will be regenerated when needed 143 | #!**/packages/repositories.config 144 | 145 | # Windows Azure Build Output 146 | csx/ 147 | *.build.csdef 148 | 149 | # Windows Store app package directory 150 | AppPackages/ 151 | 152 | # Others 153 | *.[Cc]ache 154 | ClientBin/ 155 | [Ss]tyle[Cc]op.* 156 | ~$* 157 | *~ 158 | *.dbmdl 159 | *.dbproj.schemaview 160 | *.publishsettings 161 | node_modules/ 162 | bower_components/ 163 | *.exe 164 | build/*.zip 165 | build/temp 166 | 167 | # RIA/Silverlight projects 168 | Generated_Code/ 169 | 170 | # Backup & report files from converting an old project file 171 | # to a newer Visual Studio version. Backup files are not needed, 172 | # because we have git ;-) 173 | _UpgradeReport_Files/ 174 | Backup*/ 175 | UpgradeLog*.XML 176 | UpgradeLog*.htm 177 | 178 | # SQL Server files 179 | *.mdf 180 | *.ldf 181 | 182 | # Business Intelligence projects 183 | *.rdl.data 184 | *.bim.layout 185 | *.bim_*.settings 186 | 187 | # Microsoft Fakes 188 | FakesAssemblies/ 189 | 190 | # Node.js Tools for Visual Studio 191 | .ntvs_analysis.dat 192 | 193 | # Visual Studio 6 build log 194 | *.plg 195 | 196 | # Visual Studio 6 workspace options file 197 | *.opt 198 | /build/temp 199 | /build/artifacts 200 | /src/main/Properties/launchSettings.json 201 | /src/main.lib/Properties/launchSettings.json 202 | 203 | # Rider 204 | .idea/ -------------------------------------------------------------------------------- /DEBUG.md: -------------------------------------------------------------------------------- 1 | # Debug Logs 2 | 3 | For more precise determination of the parameters of voice traffic decryption activation, control info is output to the scanner debug logs. 4 | 5 | You can activate debug logging via the scanner menu Settings - Set Debug Logs Mode - SD Card (File). 6 | 7 | The generated debug logs can then be found on the scanner SD Card in the /BCDx36HP/debug/ directory. 8 | 9 | #### Debug Log String Format 10 | 11 | ##### MI 12 | 13 | ``` 14 | 0066766 :MI 0 9974B94B 9974B94B 0 21 01 15 | 1 2 3 4 5 6 7 8 16 | ``` 17 | The debug output string consists of 8 fields separated by a space. 18 | 19 | ``` 20 | 1 - Time stamp in microseconds 21 | 2 - Signature ":MI" - IV and addition info 22 | 3 - BPTC error. If =0 then assign new IV 23 | 4 - Calculated IV (by Motorola LFSR algo) 24 | 5 - Extracted IV from control frames. For Moto EP must be Field(4) = Field (5) 25 | 6 - BPTC16 error. If !=0 and known AlgoID then assign new encryption params 26 | 7 - AlgoID 27 | 21 - RC4 28 | 22 - DES 29 | 24 - AES128 30 | 25 - AES256 31 | 8 - KeyID 32 | ``` 33 | ##### N0 - NXDN Control and Voice Info 34 | 35 | ``` 36 | 0067268 :N0 04307750 02 04 02 02 00 00 0000 00000000 00000003 D 1 01 28139 0 105D02A0246A00 90FF381CC07A00 C32EA656008700 98B3C2806C4200 37 | 1 2 3 4 5 6 7 8 9 10 11 12 131415 16 1718 19 20 21 38 | ``` 39 | The debug output string consists of 21 fields separated by a space. 40 | 41 | ``` 42 | 1 - Time stamp in microseconds 43 | 2 - Signature ":N0" - NXDN protocol, version 0 44 | 3 - Frequency in 100 Hz scale (DEC) 45 | 4 - NXDN Frame Type 46 | Range: 0-8 47 | 5 - NXDN Data Type 48 | 0 - SACCH 49 | 1,5,6 - FACCH1 50 | 2 - FACCH2 51 | 3 - CAC 52 | 6 - NXDN Vocoder Mode 53 | Range: 0-3 54 | 7 - RAN 55 | 8 - AREA 56 | 9 - SITE 57 | 10 - SYSTEM 58 | 11 - GROUP ID 59 | 12 - UID 60 | 13 - Voice traffic decryption identifier 61 | D - Parameters are not defined 62 | U - Protocol unlocked 63 | A - Allowed 64 | S - Time session restriction 65 | 14 - NXDN Cypher Type 66 | 0 - Non Ciphered 67 | 1 - Scrambler 68 | 2 - Des 69 | 3 - Aes 70 | 15 - Key ID 71 | 16 - NXDN Current Scrambler Key (DEC) 72 | 17 - NXDN Scrambler Key Offset 73 | Inner control data 74 | 18 - Voice packet 0, 49 bits (HEX) 75 | 19 - Voice packet 1, 49 bits (HEX) 76 | 20 - Voice packet 2, 49 bits (HEX) 77 | 21 - Voice packet 3, 49 bits (HEX) 78 | ``` 79 | 80 | ##### D1 - DMR Control and Voice Info 81 | 82 | ``` 83 | 0201377 :D1 04404400 1 1F 0 01 C 8 68 0000214D 002628B2 0000 1 1 0 0000 0000 0000 D E1F9DA3443BF80 27C2718F2D3600 2F18B5E5C67080 84 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14151617 18 19 2021 22 23 85 | ``` 86 | 87 | The debug output string consists of 23 fields separated by a space. 88 | 89 | ``` 90 | 1 - Time stamp in microseconds 91 | 2 - Signature ":D1" - DMR protocol, version 1 92 | 3 - Frequency in 100 Hz scale (DEC) 93 | 4 - Trunk system type 94 | 0 - DMR One? 95 | 1 - Simple DMR 96 | 2 - Capacity+ 97 | 3 - Connect+ 98 | 4 - DMR Tier III 99 | 5 - Hytera XPT 100 | 5 - Data type 101 | 0 - PI Header 102 | 1 - Voice LC Header 103 | 2 - Terminator with LC 104 | 3 - CSBK 105 | 4 - MBC Header 106 | 5 - MBC Continuation 107 | 6 - Data Header 108 | 7 - Rate 1/2 Data 109 | 8 - Rate 3/4 Data 110 | 9 - Idle 111 | A - Rate 1 Data 112 | B - Unified Single Block Data 113 | C - Reserved for future use 114 | D - Reserved for future use 115 | E - Reserved for future use 116 | F - Reserved for future use 117 | 1E - ? 118 | 1F - ? 119 | 6 - TDMA channel number 120 | 7 - Call type (HEX) 121 | 8 - Color code (HEX) 122 | 0 - F 123 | 9 - Superframe number (HEX) 124 | 0 - B 125 | 10 - EMB FID manufacturer identifier (HEX) 126 | 10 - Motorola 127 | 68 - Hytera 128 | 11 - EMB TGID talkgroup number (HEX) 129 | 12 - EMB User ID (HEX) 130 | 13 - C-CH 131 | 14 - Encrypt Value 132 | 1 - Non encrypted 133 | 2 - Encrypted 134 | 15 - Privacy 135 | 16 - EMB Privacy 136 | 17 - SLC LCN 137 | 18 - SLC NID 138 | 19 - SLC SID 139 | 20 - Voice traffic decryption identifier 140 | D - Parameters are not defined 141 | U - Protocol unlocked 142 | A - Allowed 143 | S - Time session restriction 144 | 21 - Voice packet 0, 49 bits (HEX) 145 | 22 - Voice packet 1, 49 bits (HEX) 146 | 23 - Voice packet 2, 49 bits (HEX) 147 | ``` 148 | 149 | 150 | -------------------------------------------------------------------------------- /img/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/add.png -------------------------------------------------------------------------------- /img/crypto0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto0.jpg -------------------------------------------------------------------------------- /img/crypto1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto1.jpg -------------------------------------------------------------------------------- /img/crypto2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto2.jpg -------------------------------------------------------------------------------- /img/crypto3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto3.jpg -------------------------------------------------------------------------------- /img/crypto4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto4.jpg -------------------------------------------------------------------------------- /img/crypto5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto5.jpg -------------------------------------------------------------------------------- /img/crypto6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto6.jpg -------------------------------------------------------------------------------- /img/crypto7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto7.jpg -------------------------------------------------------------------------------- /img/crypto8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto8.jpg -------------------------------------------------------------------------------- /img/crypto9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/crypto9.jpg -------------------------------------------------------------------------------- /img/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/delete.png -------------------------------------------------------------------------------- /img/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/down.png -------------------------------------------------------------------------------- /img/duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/duplicate.png -------------------------------------------------------------------------------- /img/filtering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/filtering.png -------------------------------------------------------------------------------- /img/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/image.png -------------------------------------------------------------------------------- /img/man0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man0.png -------------------------------------------------------------------------------- /img/man1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man1.png -------------------------------------------------------------------------------- /img/man10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man10.png -------------------------------------------------------------------------------- /img/man11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man11.png -------------------------------------------------------------------------------- /img/man2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man2.png -------------------------------------------------------------------------------- /img/man3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man3.png -------------------------------------------------------------------------------- /img/man4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man4.png -------------------------------------------------------------------------------- /img/man5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man5.png -------------------------------------------------------------------------------- /img/man6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man6.png -------------------------------------------------------------------------------- /img/man7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man7.png -------------------------------------------------------------------------------- /img/man8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man8.png -------------------------------------------------------------------------------- /img/man9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/man9.png -------------------------------------------------------------------------------- /img/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/new.png -------------------------------------------------------------------------------- /img/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/open.png -------------------------------------------------------------------------------- /img/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/save.png -------------------------------------------------------------------------------- /img/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/up.png -------------------------------------------------------------------------------- /img/wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/img/wallet.png -------------------------------------------------------------------------------- /src/AboutForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection; 4 | using System.Windows.Forms; 5 | 6 | namespace CFT 7 | { 8 | public partial class AboutForm : Form 9 | { 10 | public AboutForm() 11 | { 12 | InitializeComponent(); 13 | var fvi = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); 14 | lblVersion.Text = $"v{fvi.FileVersion}"; 15 | } 16 | 17 | private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 18 | { 19 | var target = (sender as LinkLabel).Text; 20 | if (target.IndexOf("@") != -1) 21 | target = "email:" + target; 22 | Process.Start(target); 23 | } 24 | 25 | private void btnClose_Click(object sender, EventArgs e) 26 | { 27 | Close(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/AboutForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/AnytoneEncEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class AnytoneEncEncryptionMethodForm : Form 7 | { 8 | public AnytoneEncEncryptionRow EncryptionRow { get; private set; } 9 | 10 | public bool IsBatchMode { get; private set; } 11 | 12 | public AnytoneEncEncryptionMethodForm(AnytoneEncEncryptionRow row = null, bool batchMode = false) 13 | { 14 | InitializeComponent(); 15 | 16 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 17 | 18 | IsBatchMode = batchMode; 19 | if (IsBatchMode) 20 | { 21 | Text += " (Batch mode)"; 22 | tbFrequency.Enabled = false; 23 | tbNotes.Enabled = false; 24 | } 25 | 26 | if (row == null) 27 | { 28 | EncryptionRow = new AnytoneEncEncryptionRow(); 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | nudKey.Value = row.Key; 40 | tbNotes.Text = row.Notes; 41 | } 42 | OptionsControl_OptionChanged(this, null); 43 | } 44 | 45 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 46 | { 47 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 48 | nudKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 49 | } 50 | 51 | private void btnOK_Click(object sender, EventArgs e) 52 | { 53 | string errorStr; 54 | 55 | if (optionsControl.IsExistErrors(out errorStr)) 56 | { 57 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 58 | return; 59 | } 60 | 61 | uint freq = 0; 62 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 63 | { 64 | tbFrequency.Focus(); 65 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 66 | return; 67 | } 68 | 69 | EncryptionRow.Frequency = freq; 70 | EncryptionRow.ActivateOptions = optionsControl.Options; 71 | EncryptionRow.Key = (ushort)nudKey.Value; 72 | EncryptionRow.Notes = tbNotes.Text.Trim(); 73 | 74 | DialogResult = DialogResult.OK; 75 | Close(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/AnytoneEncEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Anytone Enc")] 7 | 8 | [Serializable] 9 | public class AnytoneEncEncryptionRow : DmrEncryptionRow 10 | { 11 | public ushort Key { get; set; } 12 | 13 | public AnytoneEncEncryptionRow() 14 | { 15 | Key = 1; 16 | ActivateOptions = new DmrActivateOptions(); 17 | } 18 | 19 | [JsonIgnore] 20 | public override string Description 21 | { 22 | get 23 | { 24 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 25 | return $"Anytone Enc: {ActivateOptions}"; 26 | else 27 | return $"Anytone Enc: Key({Key:X04}) {ActivateOptions}"; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BaseEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [Serializable] 6 | public abstract class BaseEncryptionRow : IEncryptionRow 7 | { 8 | public uint Frequency { get; set; } 9 | 10 | public string Notes { get; set; } 11 | 12 | public abstract ProtocolEnum Protocol { get; } 13 | 14 | public abstract string Description { get; } 15 | 16 | public abstract bool IsFrequencyNeed { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CFT.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34601.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CFT", "CFT.csproj", "{5ABFC518-FDBC-4864-B546-AE52ABA4BF63}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5ABFC518-FDBC-4864-B546-AE52ABA4BF63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5ABFC518-FDBC-4864-B546-AE52ABA4BF63}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5ABFC518-FDBC-4864-B546-AE52ABA4BF63}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5ABFC518-FDBC-4864-B546-AE52ABA4BF63}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {07B3180A-E998-4B5E-894A-9491D3F671FA} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/DebugLogsFilteringForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | 5 | namespace CFT 6 | { 7 | public partial class DebugLogsFilteringForm : Form 8 | { 9 | static readonly string [] DEBUG_LINE_SIGNATURES = { ":D", ":B", ":N", ":R", ":P", ":C", ":A", ":M" }; 10 | 11 | public DebugLogsFilteringForm() 12 | { 13 | InitializeComponent(); 14 | UpdateControls(); 15 | } 16 | 17 | private void UpdateControls() 18 | { 19 | nudTime.Enabled = cbInsertSpaceLine.Checked; 20 | btnOk.Enabled = !string.IsNullOrEmpty(tbDebugLogFilename.Text); 21 | } 22 | 23 | private void cbInsertSpaceLine_CheckedChanged(object sender, EventArgs e) 24 | { 25 | UpdateControls(); 26 | } 27 | 28 | private void btnOk_Click(object sender, EventArgs e) 29 | { 30 | var count = 0; 31 | try 32 | { 33 | uint previousTick = 0; 34 | 35 | var outputFileName = Path.ChangeExtension(tbDebugLogFilename.Text, "filtered.txt"); 36 | 37 | using (TextReader tr = new StreamReader(File.Open(tbDebugLogFilename.Text, FileMode.Open, FileAccess.Read, FileShare.Read))) 38 | using (TextWriter tw = new StreamWriter(File.Open(outputFileName, FileMode.Create, FileAccess.Write, FileShare.Write))) 39 | { 40 | string line; 41 | while ((line = tr.ReadLine()) != null) 42 | { 43 | count++; 44 | 45 | var foundSignature = false; 46 | foreach (var sig in DEBUG_LINE_SIGNATURES) 47 | { 48 | if (line.Contains(sig)) 49 | { 50 | foundSignature = true; 51 | break; 52 | } 53 | } 54 | if (!foundSignature) 55 | continue; 56 | 57 | if (cbInsertSpaceLine.Checked) 58 | { 59 | var token = line.Split(' '); 60 | 61 | if (uint.TryParse(token[0], out uint res)) 62 | { 63 | if (res - previousTick > nudTime.Value) 64 | tw.WriteLine(); 65 | previousTick = res; 66 | } 67 | } 68 | tw.WriteLine(line); 69 | } 70 | } 71 | MessageBox.Show("Done.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); 72 | } 73 | catch(Exception ex) 74 | { 75 | MessageBox.Show($"Error in the line {count}\r\n{ex.Message}\r\n{ex.StackTrace}", "Error"); 76 | } 77 | } 78 | 79 | private void btnDebugLogFileSelect_Click(object sender, EventArgs e) 80 | { 81 | try 82 | { 83 | OpenFileDialog dlg = new OpenFileDialog(); 84 | dlg.Filter = "Debug Log Files (*.txt)|*.txt|All Files|*.*"; 85 | dlg.Multiselect = false; 86 | dlg.RestoreDirectory = true; 87 | if (dlg.ShowDialog() == DialogResult.OK) 88 | { 89 | tbDebugLogFilename.Text = dlg.FileName; 90 | UpdateControls(); 91 | } 92 | } 93 | catch (Exception ex) 94 | { 95 | MessageBox.Show(ex.Message + ex.StackTrace, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/DebugLogsFilteringForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/DisplayAdditionalInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [Serializable] 6 | public class DisplayAdditionalInfo 7 | { 8 | public DisplayAdditionalInfoValuesEnum Line0 { get; set; } = DisplayAdditionalInfoValuesEnum.Default; 9 | public DisplayAdditionalInfoValuesEnum Line1 { get; set; } = DisplayAdditionalInfoValuesEnum.Default; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DisplayAdditionalInfoForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Windows.Forms; 4 | 5 | namespace CFT 6 | { 7 | public partial class DisplayAdditionalInfoForm : Form 8 | { 9 | public DisplayAdditionalInfo DisplayAdditionalInfo { get; private set; } 10 | public ScannerModelEnum Model { get; private set; } 11 | 12 | public DisplayAdditionalInfoForm(ScannerModelEnum model, DisplayAdditionalInfo additionalInfo) 13 | { 14 | InitializeComponent(); 15 | 16 | Model = model; 17 | DisplayAdditionalInfo = additionalInfo; 18 | 19 | if (Scanner.GetBaseModel(model) == ScannerBaseModelEnum.BCD) 20 | { 21 | FillComboBoxData(cbLine0); 22 | FillComboBoxData(cbLine1); 23 | 24 | if (DisplayAdditionalInfo != null) 25 | { 26 | if (!Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfo.Line0)) 27 | Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfoValuesEnum.Default); 28 | if (!Utils.SetComboBoxData(cbLine1, DisplayAdditionalInfo.Line1)) 29 | Utils.SetComboBoxData(cbLine1, DisplayAdditionalInfoValuesEnum.Default); 30 | } 31 | else 32 | { 33 | Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfoValuesEnum.Default); 34 | Utils.SetComboBoxData(cbLine1, DisplayAdditionalInfoValuesEnum.Default); 35 | } 36 | } 37 | else 38 | { 39 | lblLine0.Text = "ALGO"; 40 | lblLine1.Visible = false; 41 | cbLine1.Visible = false; 42 | cbLine0.Items.Add(new DisplayTagObject(DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdHex)); 43 | cbLine0.Items.Add(new DisplayTagObject(DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdDec)); 44 | 45 | if (DisplayAdditionalInfo != null) 46 | { 47 | if (DisplayAdditionalInfo.Line0 == DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdDec) 48 | Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdDec); 49 | else 50 | Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdHex); 51 | } 52 | else 53 | { 54 | Utils.SetComboBoxData(cbLine0, DisplayAdditionalInfoValuesEnum.MfidEncAlgoKeyIdHex); 55 | } 56 | } 57 | } 58 | 59 | public void FillComboBoxData(ComboBox cb) 60 | { 61 | cb.Items.Clear(); 62 | foreach (DisplayAdditionalInfoValuesEnum item in Enum.GetValues(typeof(DisplayAdditionalInfoValuesEnum))) 63 | { 64 | cb.Items.Add(new DisplayTagObject(item)); 65 | } 66 | } 67 | 68 | private void btnOk_Click(object sender, EventArgs e) 69 | { 70 | if (DisplayAdditionalInfo == null) 71 | DisplayAdditionalInfo = new DisplayAdditionalInfo(); 72 | 73 | DisplayAdditionalInfo.Line0 = (DisplayAdditionalInfoValuesEnum)Utils.GetComboBoxData(cbLine0.SelectedItem); 74 | if (Scanner.GetBaseModel(Model) == ScannerBaseModelEnum.BCD) 75 | { 76 | DisplayAdditionalInfo.Line1 = (DisplayAdditionalInfoValuesEnum)Utils.GetComboBoxData(cbLine1.SelectedItem); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/DisplayAdditionalInfoForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/DisplayAdditionalInfoValuesEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DisplayAdditionalInfoValuesEnum 4 | { 5 | [DisplayName("Default")] 6 | Default = 0, 7 | [DisplayName("RSSI")] 8 | RSSI = 1, 9 | [DisplayName("MFID, Encrypton Algo and KeyID (HEX)")] 10 | MfidEncAlgoKeyIdHex = 2, 11 | [DisplayName("MFID, Encrypton Algo and KeyID (DEC)")] 12 | MfidEncAlgoKeyIdDec = 3 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/DisplayNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [AttributeUsage(AttributeTargets.All)] 6 | public class DisplayNameAttribute : System.ComponentModel.DisplayNameAttribute 7 | { 8 | private readonly string _name; 9 | public DisplayNameAttribute(string name) 10 | { 11 | _name = name; 12 | } 13 | 14 | public string Name { get { return _name; } } 15 | 16 | public static string GetName(object o) 17 | { 18 | if (o == null) return string.Empty; 19 | 20 | var t = o.GetType(); 21 | if (t.IsEnum) 22 | { 23 | var fi = t.GetFields(); 24 | foreach (var fieldInfo in fi) 25 | { 26 | if (fieldInfo.Name == o.ToString()) 27 | { 28 | var a = (DisplayNameAttribute)GetCustomAttribute(fieldInfo, typeof(DisplayNameAttribute)); 29 | return a == null ? fieldInfo.Name : a.Name; 30 | } 31 | } 32 | } 33 | else 34 | { 35 | var a = (DisplayNameAttribute)GetCustomAttribute(t, typeof(DisplayNameAttribute)); 36 | if (a != null) 37 | return a.Name; 38 | 39 | } 40 | return string.Empty; 41 | } 42 | 43 | public static string GetName(Type t) 44 | { 45 | if (t == null) return string.Empty; 46 | 47 | var a = (DisplayNameAttribute)GetCustomAttribute(t, typeof(DisplayNameAttribute)); 48 | return a != null ? a.Name : string.Empty; 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/DisplayTagObject.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public class DisplayTagObject 4 | { 5 | public string Name { get; private set; } 6 | public object Tag { get; set; } 7 | 8 | public DisplayTagObject(object o) 9 | { 10 | Tag = o; 11 | Name = DisplayNameAttribute.GetName(o); 12 | } 13 | 14 | public override string ToString() 15 | { 16 | return string.IsNullOrEmpty(Name) ? Tag.ToString() : Name; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/DmrActivateOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace CFT 5 | { 6 | [Serializable] 7 | public class DmrActivateOptions : IActivateOptions 8 | { 9 | public DmrSelectedActivateOptionsEnum Options { get; set; } 10 | public DmrTrunkSystemEnum TrunkSystem { get; set; } 11 | public DmrMfidEnum MFID { get; set; } 12 | public DmrColorCodeEnum ColorCode { get; set; } 13 | public uint TGID { get; set; } 14 | public DmrTimeSlotEnum TimeSlot { get; set; } 15 | public DmrEncyptionValueEnum EncryptionValue { get; set; } 16 | 17 | public byte KeyId { get; set; } 18 | 19 | public DmrActivateOptions() 20 | { 21 | Activate(DmrSelectedActivateOptionsEnum.Frequency); 22 | } 23 | 24 | public bool IsActivated(DmrSelectedActivateOptionsEnum option) 25 | { 26 | return (Options & option) == option; 27 | } 28 | 29 | public void Activate(DmrSelectedActivateOptionsEnum option) 30 | { 31 | Options |= option; 32 | } 33 | 34 | public override string ToString() 35 | { 36 | var sb = new StringBuilder(); 37 | if (IsActivated(DmrSelectedActivateOptionsEnum.TrunkSystem)) 38 | sb.Append($"Trunk({DisplayNameAttribute.GetName(TrunkSystem)}) "); 39 | if (IsActivated(DmrSelectedActivateOptionsEnum.TGID)) 40 | sb.Append($"TGID({TGID}) "); 41 | if (IsActivated(DmrSelectedActivateOptionsEnum.MFID)) 42 | sb.Append($"MFID({DisplayNameAttribute.GetName(MFID)}) "); 43 | if (IsActivated(DmrSelectedActivateOptionsEnum.ColorCode)) 44 | sb.Append($"CC({(int)ColorCode}) "); 45 | if (IsActivated(DmrSelectedActivateOptionsEnum.KeyId)) 46 | sb.Append($"KeyID({KeyId}) "); 47 | if (IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 48 | sb.Append($"Force Mute"); 49 | return sb.ToString(); 50 | } 51 | 52 | internal void Deactivate(DmrSelectedActivateOptionsEnum option) 53 | { 54 | Options &= ~option; 55 | } 56 | 57 | public int CompareTo(object obj) 58 | { 59 | var o = obj as DmrActivateOptions; 60 | if (o == null) 61 | return 1; 62 | 63 | if (o.Options != Options) 64 | return 1; 65 | 66 | if (IsActivated(DmrSelectedActivateOptionsEnum.TrunkSystem) && 67 | o.TrunkSystem != TrunkSystem) 68 | return 1; 69 | 70 | if (IsActivated(DmrSelectedActivateOptionsEnum.MFID) && 71 | o.MFID != MFID) 72 | return 1; 73 | 74 | if (IsActivated(DmrSelectedActivateOptionsEnum.ColorCode) && 75 | o.ColorCode != ColorCode) 76 | return 1; 77 | 78 | if (IsActivated(DmrSelectedActivateOptionsEnum.TGID) && 79 | o.TGID != TGID) 80 | return 1; 81 | 82 | if (IsActivated(DmrSelectedActivateOptionsEnum.TimeSlot) && 83 | o.TimeSlot != TimeSlot) 84 | return 1; 85 | 86 | if (IsActivated(DmrSelectedActivateOptionsEnum.EncryptValue) && 87 | o.EncryptionValue != EncryptionValue) 88 | return 1; 89 | 90 | if (IsActivated(DmrSelectedActivateOptionsEnum.KeyId) && 91 | o.KeyId != KeyId) 92 | return 1; 93 | 94 | return 0; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/DmrAesEncryptionMethodForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/DmrAesEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("DMR AES")] 7 | [Serializable] 8 | public class DmrAesEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 32; 11 | public ushort KeyLength { get; set; } 12 | 13 | public byte[] Key { get; set; } 14 | 15 | [JsonIgnore] 16 | public override string Description => $"DMR AES: KeyLen({KeyLength}) {ActivateOptions}"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DmrAesKeyLengthEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrAesKeyLengthEnum 4 | { 5 | [DisplayName("128 bits")] 6 | L128 = 128, 7 | [DisplayName("192 bits")] 8 | L192 = 192, 9 | [DisplayName("256 bits")] 10 | L256 = 256 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DmrColorCodeEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrColorCodeEnum 4 | { 5 | [DisplayName("CC 0")] 6 | CC0 = 0, 7 | [DisplayName("CC 1")] 8 | CC1 = 1, 9 | [DisplayName("CC 2")] 10 | CC2 = 2, 11 | [DisplayName("CC 3")] 12 | CC3 = 3, 13 | [DisplayName("CC 4")] 14 | CC4 = 4, 15 | [DisplayName("CC 5")] 16 | CC5 = 5, 17 | [DisplayName("CC 6")] 18 | CC6 = 6, 19 | [DisplayName("CC 7")] 20 | CC7 = 7, 21 | [DisplayName("CC 8")] 22 | CC8 = 8, 23 | [DisplayName("CC 9")] 24 | CC9 = 9, 25 | [DisplayName("CC 10")] 26 | CC10 = 10, 27 | [DisplayName("CC 11")] 28 | CC11 = 11, 29 | [DisplayName("CC 12")] 30 | CC12 = 12, 31 | [DisplayName("CC 13")] 32 | CC13 = 13, 33 | [DisplayName("CC 14")] 34 | CC14 = 14, 35 | [DisplayName("CC 15")] 36 | CC15 = 15 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/DmrEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [Serializable] 7 | public abstract class DmrEncryptionRow : BaseEncryptionRow 8 | { 9 | [JsonIgnore] 10 | public override ProtocolEnum Protocol => ProtocolEnum.DMR; 11 | 12 | public DmrActivateOptions ActivateOptions { get; set; } 13 | public override bool IsFrequencyNeed => ActivateOptions == null ? true : ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DmrEncyptionValueEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrEncyptionValueEnum 4 | { 5 | [DisplayName("Non Encrypted")] 6 | Value1 = 1, 7 | [DisplayName("Encrypted")] 8 | Value2 = 2 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/DmrMfidEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrMfidEnum 4 | { 5 | [DisplayName("Unknown (0x00)")] 6 | Unknown = 0x00, 7 | [DisplayName("Motorola")] 8 | Motorola = 0x10, 9 | [DisplayName("Hytera")] 10 | Hytera = 0x68 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DmrSelectedActivateOptionsEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | [System.Flags] 4 | public enum DmrSelectedActivateOptionsEnum : uint 5 | { 6 | TrunkSystem = 1 << 0, 7 | MFID = 1 << 1, 8 | Frequency = 1 << 2, 9 | ColorCode = 1 << 3, 10 | TGID = 1 << 4, 11 | TimeSlot = 1 << 5, 12 | EncryptValue = 1 << 6, 13 | KeyId = 1 << 7, 14 | 15 | ForceMute = 1 << 15 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/DmrTimeSlotEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrTimeSlotEnum 4 | { 5 | [DisplayName("Slot 1")] 6 | Slot1 = 0, 7 | [DisplayName("Slot 2")] 8 | Slot2 = 1 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/DmrTrunkSystemEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum DmrTrunkSystemEnum 4 | { 5 | [DisplayName("DMR MS")] 6 | DMRMS = 0, 7 | [DisplayName("Simple DMR")] 8 | DMR = 1, 9 | [DisplayName("Capacity+")] 10 | CAP = 2, 11 | [DisplayName("Connect+")] 12 | CON = 3, 13 | [DisplayName("DMR Tier III")] 14 | DT3 = 4, 15 | [DisplayName("Hytera XPT")] 16 | XPT = 5 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DsdImport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Windows.Forms; 5 | 6 | namespace CFT 7 | { 8 | public class DsdImport 9 | { 10 | public static List Import(string filename) 11 | { 12 | var res = new List(); 13 | var count = 0; 14 | try 15 | { 16 | using (TextReader tr = new StreamReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))) 17 | { 18 | count++; 19 | 20 | string line; 21 | while ((line = tr.ReadLine()) != null) 22 | { 23 | var commentIndex = line.IndexOf(";"); 24 | if (commentIndex != -1) 25 | line = line.Substring(0, commentIndex).Trim(); 26 | 27 | if (string.IsNullOrEmpty(line.Trim())) 28 | continue; 29 | 30 | var token = line.Split(','); 31 | if (token.Length != 7) 32 | throw new Exception($"Wrong item count in row: {token.Length}"); 33 | var item = new ImportItem(); 34 | if (!Utils.ParseFrequency(token[4], out uint freq, out string err)) 35 | throw new Exception(err); 36 | item.Frequency = freq; 37 | item.Notes = $"{token[0]} {token[1]} {token[2]} {token[3]}"; 38 | res.Add(item); 39 | } 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | MessageBox.Show($"Error in the line {count}\r\n{ex.Message}\r\n{ex.StackTrace}", "Error"); 45 | res = null; 46 | } 47 | 48 | return res; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/EncryptionMethodEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum EncryptionMethodEnum 4 | { 5 | [DisplayName("Motorola BP")] 6 | MotorolaBP = 1, 7 | [DisplayName("Hytera BP")] 8 | HyteraBP = 2, 9 | [DisplayName("Nxdn Scrambler")] 10 | NxdnScrambler = 3, 11 | [DisplayName("Motorola EP")] 12 | MotorolaEP = 4, 13 | [DisplayName("Anytone Enc")] 14 | AnytoneEnc = 5, 15 | [DisplayName("P25 ADP")] 16 | P25ADP = 6, 17 | [DisplayName("P25 DES")] 18 | P25DES = 7, 19 | [DisplayName("Tytera EP")] 20 | TyteraEP = 8, 21 | [DisplayName("DMR AES")] 22 | DMRAES = 9, 23 | [DisplayName("Tytera BP")] 24 | TyteraBP = 10, 25 | [DisplayName("Kirisun BP")] 26 | KirisunBP = 11, 27 | [DisplayName("Hytera EP")] 28 | HyteraEP = 12, 29 | [DisplayName("P25 AES")] 30 | P25AES = 13 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/HyteraBPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class HyteraBPEncryptionMethodForm : Form 7 | { 8 | public HyteraBPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public HyteraBPEncryptionMethodForm(HyteraBPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | Utils.FillComboBoxData(cbKeyLength, typeof(HyteraKeyLengthEnum)); 26 | cbKeyLength.SelectedIndex = 0; 27 | 28 | if (row == null) 29 | { 30 | EncryptionRow = new HyteraBPEncryptionRow(); 31 | EncryptionRow.Key = new byte[HyteraBPEncryptionRow.KEY_SIZE]; 32 | if (!IsBatchMode) 33 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 34 | optionsControl.SetOptions(null); 35 | } 36 | else 37 | { 38 | EncryptionRow = row; 39 | if (!IsBatchMode) 40 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 41 | optionsControl.SetOptions(row.ActivateOptions); 42 | 43 | if (row.KeyLength == 40) 44 | Utils.SetComboBoxData(cbKeyLength, HyteraKeyLengthEnum.L40); 45 | else if (row.KeyLength == 128) 46 | Utils.SetComboBoxData(cbKeyLength, HyteraKeyLengthEnum.L128); 47 | else if (row.KeyLength == 256) 48 | Utils.SetComboBoxData(cbKeyLength, HyteraKeyLengthEnum.L256); 49 | else 50 | { 51 | nudKeyLength.Value = row.KeyLength; 52 | cbNonStandard.Checked = true; 53 | } 54 | 55 | if (Utils.IsArrayEmpty(row.Key)) 56 | tbKey.Text = string.Empty; 57 | else 58 | tbKey.Text = Utils.BytesToHexString(row.Key).Substring(0, (int)(row.KeyLength / 4 + (row.KeyLength % 4 != 0 ? 1 : 0))); 59 | 60 | tbNotes.Text = row.Notes; 61 | } 62 | 63 | cbNonStandard_CheckedChanged(this, null); 64 | OptionsControl_OptionChanged(this, null); 65 | } 66 | 67 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 68 | { 69 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 70 | cbKeyLength.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 71 | nudKeyLength.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 72 | cbNonStandard.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 73 | tbKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 74 | } 75 | 76 | private void btnOK_Click(object sender, EventArgs e) 77 | { 78 | string errorStr; 79 | 80 | if (optionsControl.IsExistErrors(out errorStr)) 81 | { 82 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 83 | return; 84 | } 85 | 86 | uint freq = 0; 87 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 88 | { 89 | tbFrequency.Focus(); 90 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 91 | return; 92 | } 93 | 94 | EncryptionRow.Frequency = freq; 95 | EncryptionRow.ActivateOptions = optionsControl.Options; 96 | 97 | byte[] key; 98 | try 99 | { 100 | key = Utils.HexStringToBytes(tbKey.Text); 101 | } 102 | catch 103 | { 104 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 105 | tbKey.Focus(); 106 | return; 107 | } 108 | 109 | if (cbNonStandard.Checked) 110 | EncryptionRow.KeyLength = (uint)nudKeyLength.Value; 111 | else 112 | EncryptionRow.KeyLength = (uint)((HyteraKeyLengthEnum)(cbKeyLength.SelectedItem as DisplayTagObject).Tag); 113 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > HyteraBPEncryptionRow.KEY_SIZE ? HyteraBPEncryptionRow.KEY_SIZE : key.Length); 114 | 115 | EncryptionRow.Notes = tbNotes.Text.Trim(); 116 | 117 | DialogResult = DialogResult.OK; 118 | Close(); 119 | } 120 | 121 | private void cbNonStandard_CheckedChanged(object sender, EventArgs e) 122 | { 123 | cbKeyLength.Visible = !cbNonStandard.Checked; 124 | nudKeyLength.Visible = cbNonStandard.Checked; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/HyteraBPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Hytera BP")] 7 | [Serializable] 8 | public class HyteraBPEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 32; 11 | public uint KeyLength { get; set; } 12 | 13 | public byte[] Key { get; set; } 14 | 15 | [JsonIgnore] 16 | public override string Description 17 | { 18 | get 19 | { 20 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 21 | return $"Hytera BP: {ActivateOptions}"; 22 | else 23 | return $"Hytera BP: KeyLen({(int)KeyLength}) {ActivateOptions}"; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/HyteraEPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class HyteraEPEncryptionMethodForm : Form 7 | { 8 | public HyteraEPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public HyteraEPEncryptionMethodForm(HyteraEPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new HyteraEPEncryptionRow(); 28 | EncryptionRow.Key = new byte[HyteraEPEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | cbKeyID.Checked = row.ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.KeyId); 41 | if (cbKeyID.Checked) 42 | tbKeyID.Text = row.ActivateOptions.KeyId.ToString(); 43 | 44 | if (Utils.IsArrayEmpty(row.Key)) 45 | tbKey.Text = string.Empty; 46 | else 47 | tbKey.Text = Utils.BytesToHexString(row.Key); 48 | 49 | tbNotes.Text = row.Notes; 50 | } 51 | cbKeyID_CheckedChanged(this, null); 52 | OptionsControl_OptionChanged(this, null); 53 | } 54 | 55 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 56 | { 57 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 58 | tbKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 59 | } 60 | 61 | private void btnOK_Click(object sender, EventArgs e) 62 | { 63 | string errorStr; 64 | 65 | if (optionsControl.IsExistErrors(out errorStr)) 66 | { 67 | if (!errorStr.Contains("no Frequency") || !cbKeyID.Checked) 68 | { 69 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 70 | return; 71 | } 72 | } 73 | 74 | uint freq = 0; 75 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 76 | { 77 | tbFrequency.Focus(); 78 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 79 | return; 80 | } 81 | 82 | byte keyID = 0; 83 | if (cbKeyID.Checked) 84 | { 85 | try 86 | { 87 | keyID = byte.Parse(tbKeyID.Text); 88 | } 89 | catch 90 | { 91 | MessageBox.Show("Wrong KeyID", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 92 | tbKeyID.Focus(); 93 | return; 94 | } 95 | } 96 | 97 | byte[] key; 98 | try 99 | { 100 | key = Utils.HexStringToBytes(tbKey.Text); 101 | } 102 | catch 103 | { 104 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 105 | tbKey.Focus(); 106 | return; 107 | } 108 | 109 | Array.Clear(EncryptionRow.Key, 0, 5); 110 | if (key.Length > 0) 111 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > MotorolaEPEncryptionRow.KEY_SIZE ? MotorolaEPEncryptionRow.KEY_SIZE : key.Length); 112 | 113 | EncryptionRow.Frequency = freq; 114 | EncryptionRow.ActivateOptions = optionsControl.Options; 115 | if (cbKeyID.Checked) 116 | { 117 | EncryptionRow.ActivateOptions.Activate(DmrSelectedActivateOptionsEnum.KeyId); 118 | EncryptionRow.ActivateOptions.KeyId = keyID; 119 | } 120 | else 121 | { 122 | EncryptionRow.ActivateOptions.Deactivate(DmrSelectedActivateOptionsEnum.KeyId); 123 | } 124 | 125 | EncryptionRow.Notes = tbNotes.Text.Trim(); 126 | 127 | DialogResult = DialogResult.OK; 128 | Close(); 129 | } 130 | 131 | private void cbKeyID_CheckedChanged(object sender, EventArgs e) 132 | { 133 | tbKeyID.Enabled = cbKeyID.Checked; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/HyteraEPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Hytera EP")] 7 | [Serializable] 8 | public class HyteraEPEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 5; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 20 | return $"Hytera EP: {ActivateOptions}"; 21 | else 22 | return $"Hytera EP: Key({Utils.BytesToHexString(Key)}) {ActivateOptions}"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/HyteraKeyLengthEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum HyteraKeyLengthEnum : ushort 4 | { 5 | [DisplayName("10 symbols (40 bits)")] 6 | L40 = 40, 7 | [DisplayName("32 symbols (128 bits)")] 8 | L128 = 128, 9 | [DisplayName("64 symbols (256 bits)")] 10 | L256 = 256 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/IActivateOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | public interface IActivateOptions : IComparable 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/IDmrEncryptionMethod.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public interface IDmrEncryptionMethod : IEncryptionMethod 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/IEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public interface IEncryptionRow 4 | { 5 | uint Frequency { get; set; } 6 | 7 | string Notes { get; set; } 8 | 9 | ProtocolEnum Protocol { get; } 10 | 11 | string Description { get; } 12 | 13 | bool IsFrequencyNeed { get; } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/ImportForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/ImportItem.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public class ImportItem 4 | { 5 | public uint Frequency { get; set; } 6 | public string Notes { get; set; } 7 | public IEncryptionRow EncryptionRow { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/KeyMapFunctionEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum KeyMapFunctionEnum 4 | { 5 | [Scanner(ScannerModelEnum.BCD436HP)] 6 | [Scanner(ScannerModelEnum.BCD536HP)] 7 | [Scanner(ScannerModelEnum.SDS100)] 8 | [Scanner(ScannerModelEnum.SDS200)] 9 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 10 | [Scanner(ScannerModelEnum.SDS100E)] 11 | [Scanner(ScannerModelEnum.SDS200E)] 12 | [Scanner(ScannerModelEnum.UBCD436PT)] 13 | [Scanner(ScannerModelEnum.UBCD536PT)] 14 | [Scanner(ScannerModelEnum.USDS100)] 15 | [DisplayName("DEFAULT")] 16 | Default = 0, 17 | [Scanner(ScannerModelEnum.BCD436HP)] 18 | [Scanner(ScannerModelEnum.BCD536HP)] 19 | [Scanner(ScannerModelEnum.SDS100)] 20 | [Scanner(ScannerModelEnum.SDS200)] 21 | [DisplayName("Zip Code")] 22 | ZipCodeMenu = 1, 23 | [Scanner(ScannerModelEnum.BCD436HP)] 24 | [Scanner(ScannerModelEnum.BCD536HP)] 25 | [Scanner(ScannerModelEnum.SDS100)] 26 | [Scanner(ScannerModelEnum.SDS200)] 27 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 28 | [Scanner(ScannerModelEnum.SDS100E)] 29 | [Scanner(ScannerModelEnum.SDS200E)] 30 | [DisplayName("Select Service")] 31 | SelectServiceMenu = 2, 32 | [Scanner(ScannerModelEnum.BCD436HP)] 33 | [Scanner(ScannerModelEnum.BCD536HP)] 34 | [Scanner(ScannerModelEnum.SDS100)] 35 | [Scanner(ScannerModelEnum.SDS200)] 36 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 37 | [Scanner(ScannerModelEnum.SDS100E)] 38 | [Scanner(ScannerModelEnum.SDS200E)] 39 | [DisplayName("Search For ...")] 40 | SearchForMenu = 3, 41 | [Scanner(ScannerModelEnum.BCD436HP)] 42 | [Scanner(ScannerModelEnum.BCD536HP)] 43 | [Scanner(ScannerModelEnum.SDS100)] 44 | [Scanner(ScannerModelEnum.SDS200)] 45 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 46 | [Scanner(ScannerModelEnum.SDS100E)] 47 | [Scanner(ScannerModelEnum.SDS200E)] 48 | [DisplayName("Select Lists to Monitor")] 49 | SelectListsToMonitorMenu = 4, 50 | [Scanner(ScannerModelEnum.BCD436HP)] 51 | [Scanner(ScannerModelEnum.BCD536HP)] 52 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 53 | [DisplayName("RF Power Plot")] 54 | RFPowerPlotMenu = 5, 55 | [Scanner(ScannerModelEnum.BCD436HP)] 56 | [Scanner(ScannerModelEnum.BCD536HP)] 57 | [Scanner(ScannerModelEnum.SDS100)] 58 | [Scanner(ScannerModelEnum.SDS200)] 59 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 60 | [Scanner(ScannerModelEnum.SDS100E)] 61 | [Scanner(ScannerModelEnum.SDS200E)] 62 | [DisplayName("Set Dimmer")] 63 | SetDimmerMenu = 6, 64 | [Scanner(ScannerModelEnum.BCD436HP)] 65 | [Scanner(ScannerModelEnum.BCD536HP)] 66 | [Scanner(ScannerModelEnum.SDS100)] 67 | [Scanner(ScannerModelEnum.SDS200)] 68 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 69 | [Scanner(ScannerModelEnum.SDS100E)] 70 | [Scanner(ScannerModelEnum.SDS200E)] 71 | [DisplayName("Set CC Mode")] 72 | SetCCModeMenu = 7, 73 | [Scanner(ScannerModelEnum.SDS100)] 74 | [Scanner(ScannerModelEnum.SDS200)] 75 | [Scanner(ScannerModelEnum.SDS100E)] 76 | [Scanner(ScannerModelEnum.SDS200E)] 77 | [DisplayName("Waterfall")] 78 | WaterfallMenu = 8, 79 | [Scanner(ScannerModelEnum.SDS100)] 80 | [Scanner(ScannerModelEnum.SDS200)] 81 | [Scanner(ScannerModelEnum.SDS100E)] 82 | [Scanner(ScannerModelEnum.SDS200E)] 83 | [DisplayName("Filter")] 84 | FilterMenu = 9, 85 | [Scanner(ScannerModelEnum.BCD536HP)] 86 | [DisplayName("Range")] 87 | SelectRange = 10, 88 | 89 | [Scanner(ScannerModelEnum.BCD436HP)] 90 | [Scanner(ScannerModelEnum.BCD536HP)] 91 | [Scanner(ScannerModelEnum.SDS100)] 92 | [Scanner(ScannerModelEnum.SDS200)] 93 | [Scanner(ScannerModelEnum.UBCD3600XLT)] 94 | [Scanner(ScannerModelEnum.SDS100E)] 95 | [Scanner(ScannerModelEnum.SDS200E)] 96 | [Scanner(ScannerModelEnum.UBCD436PT)] 97 | [Scanner(ScannerModelEnum.UBCD536PT)] 98 | [Scanner(ScannerModelEnum.USDS100)] 99 | [DisplayName("Debug Logs")] 100 | DebugLogs = 11, 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/KeyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [Serializable] 6 | public class KeyMapping 7 | { 8 | public KeyMapFunctionEnum Key1 { get; set; } = KeyMapFunctionEnum.Default; 9 | public KeyMapFunctionEnum Key2 { get; set; } = KeyMapFunctionEnum.Default; 10 | public KeyMapFunctionEnum Key3 { get; set; } = KeyMapFunctionEnum.Default; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/KeyMappingForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class KeyMappingForm : Form 7 | { 8 | public KeyMapping KeyMapping { get; private set; } 9 | 10 | public KeyMappingForm(ScannerModelEnum model, KeyMapping keyMapping) 11 | { 12 | InitializeComponent(); 13 | 14 | KeyMapping = keyMapping; 15 | 16 | switch(model) 17 | { 18 | case ScannerModelEnum.UBCD3600XLT: 19 | case ScannerModelEnum.BCD436HP: 20 | case ScannerModelEnum.UBCD436PT: 21 | lblKey1.Text = "ZIP"; 22 | lblKey2.Text = "FUNC+ZIP"; 23 | lblKey3.Visible = false; 24 | cbKey3Map.Visible = false; 25 | break; 26 | case ScannerModelEnum.BCD536HP: 27 | case ScannerModelEnum.UBCD536PT: 28 | lblKey1.Text = "ZIP"; 29 | lblKey2.Text = "SERV"; 30 | lblKey3.Text = "RANG"; 31 | lblKey3.Visible = true; 32 | cbKey3Map.Visible = true; 33 | break; 34 | case ScannerModelEnum.SDS100: 35 | case ScannerModelEnum.SDS100E: 36 | case ScannerModelEnum.USDS100: 37 | lblKey1.Text = "ZIP"; 38 | lblKey2.Text = "FUNC+ZIP"; 39 | lblKey3.Visible = false; 40 | cbKey3Map.Visible = false; 41 | break; 42 | case ScannerModelEnum.SDS200: 43 | case ScannerModelEnum.SDS200E: 44 | lblKey1.Text = "ZIP"; 45 | lblKey2.Text = "FUNC+ZIP"; 46 | lblKey3.Visible = false; 47 | cbKey3Map.Visible = false; 48 | break; 49 | } 50 | 51 | FillComboBoxData(cbKey1Map, model); 52 | FillComboBoxData(cbKey2Map, model); 53 | FillComboBoxData(cbKey3Map, model); 54 | 55 | if (KeyMapping != null) 56 | { 57 | if (!Utils.SetComboBoxData(cbKey1Map, KeyMapping.Key1)) 58 | Utils.SetComboBoxData(cbKey1Map, KeyMapFunctionEnum.Default); 59 | if (!Utils.SetComboBoxData(cbKey2Map, KeyMapping.Key2)) 60 | Utils.SetComboBoxData(cbKey2Map, KeyMapFunctionEnum.Default); 61 | if (!Utils.SetComboBoxData(cbKey3Map, KeyMapping.Key3)) 62 | Utils.SetComboBoxData(cbKey3Map, KeyMapFunctionEnum.Default); 63 | } 64 | else 65 | { 66 | Utils.SetComboBoxData(cbKey1Map, KeyMapFunctionEnum.Default); 67 | Utils.SetComboBoxData(cbKey2Map, KeyMapFunctionEnum.Default); 68 | Utils.SetComboBoxData(cbKey3Map, KeyMapFunctionEnum.Default); 69 | } 70 | } 71 | 72 | public void FillComboBoxData(ComboBox cb, ScannerModelEnum model) 73 | { 74 | cb.Items.Clear(); 75 | foreach (KeyMapFunctionEnum item in Enum.GetValues(typeof(KeyMapFunctionEnum))) 76 | { 77 | var sa = Utils.GetAttributes(item); 78 | foreach (ScannerAttribute attr in sa) 79 | { 80 | if (attr.Model == model) 81 | { 82 | cb.Items.Add(new DisplayTagObject(item)); 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | 89 | private void btnOk_Click(object sender, EventArgs e) 90 | { 91 | if (KeyMapping == null) 92 | KeyMapping = new KeyMapping(); 93 | 94 | KeyMapping.Key1 = (KeyMapFunctionEnum)Utils.GetComboBoxData(cbKey1Map.SelectedItem); 95 | KeyMapping.Key2 = (KeyMapFunctionEnum)Utils.GetComboBoxData(cbKey2Map.SelectedItem); 96 | KeyMapping.Key3 = (KeyMapFunctionEnum)Utils.GetComboBoxData(cbKey3Map.SelectedItem); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/KeyMappingForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/KirisunBPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class KirisunBPEncryptionMethodForm : Form 7 | { 8 | public KirisunBPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public KirisunBPEncryptionMethodForm(KirisunBPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | Utils.FillComboBoxData(cbKeyLength, typeof(KirisunKeyLengthEnum)); 26 | cbKeyLength.SelectedIndex = 0; 27 | 28 | if (row == null) 29 | { 30 | EncryptionRow = new KirisunBPEncryptionRow(); 31 | EncryptionRow.Key = new byte[KirisunBPEncryptionRow.KEY_SIZE]; 32 | if (!IsBatchMode) 33 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 34 | optionsControl.SetOptions(null); 35 | } 36 | else 37 | { 38 | EncryptionRow = row; 39 | if (!IsBatchMode) 40 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 41 | optionsControl.SetOptions(row.ActivateOptions); 42 | 43 | if (row.KeyLength == 40) 44 | Utils.SetComboBoxData(cbKeyLength, KirisunKeyLengthEnum.L40); 45 | if (row.KeyLength == 64) 46 | Utils.SetComboBoxData(cbKeyLength, KirisunKeyLengthEnum.L64); 47 | else if (row.KeyLength == 128) 48 | Utils.SetComboBoxData(cbKeyLength, KirisunKeyLengthEnum.L128); 49 | else if (row.KeyLength == 192) 50 | Utils.SetComboBoxData(cbKeyLength, KirisunKeyLengthEnum.L192); 51 | else if (row.KeyLength == 256) 52 | Utils.SetComboBoxData(cbKeyLength, KirisunKeyLengthEnum.L256); 53 | 54 | if (Utils.IsArrayEmpty(row.Key)) 55 | tbKey.Text = string.Empty; 56 | else 57 | tbKey.Text = Utils.BytesToHexString(row.Key).Substring(0, (int)(row.KeyLength / 4 + (row.KeyLength % 4 != 0 ? 1 : 0))); 58 | 59 | tbNotes.Text = row.Notes; 60 | } 61 | 62 | OptionsControl_OptionChanged(this, null); 63 | } 64 | 65 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 66 | { 67 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 68 | cbKeyLength.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 69 | tbKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 70 | } 71 | 72 | private void btnOK_Click(object sender, EventArgs e) 73 | { 74 | string errorStr; 75 | 76 | if (optionsControl.IsExistErrors(out errorStr)) 77 | { 78 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 79 | return; 80 | } 81 | 82 | uint freq = 0; 83 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 84 | { 85 | tbFrequency.Focus(); 86 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 87 | return; 88 | } 89 | 90 | EncryptionRow.Frequency = freq; 91 | EncryptionRow.ActivateOptions = optionsControl.Options; 92 | 93 | byte[] key; 94 | try 95 | { 96 | key = Utils.HexStringToBytes(tbKey.Text); 97 | } 98 | catch 99 | { 100 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 101 | tbKey.Focus(); 102 | return; 103 | } 104 | 105 | EncryptionRow.KeyLength = (uint)(KirisunKeyLengthEnum)(cbKeyLength.SelectedItem as DisplayTagObject).Tag; 106 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > KirisunBPEncryptionRow.KEY_SIZE ? KirisunBPEncryptionRow.KEY_SIZE : key.Length); 107 | 108 | EncryptionRow.Notes = tbNotes.Text.Trim(); 109 | 110 | DialogResult = DialogResult.OK; 111 | Close(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/KirisunBPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Kirisun BP")] 7 | [Serializable] 8 | public class KirisunBPEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 32; 11 | public uint KeyLength { get; set; } 12 | 13 | public byte[] Key { get; set; } 14 | 15 | [JsonIgnore] 16 | public override string Description 17 | { 18 | get 19 | { 20 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 21 | return $"Kirisun BP: {ActivateOptions}"; 22 | else 23 | return $"Kirisun BP: KeyLen({(int)KeyLength}) {ActivateOptions}"; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/KirisunKeyLengthEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum KirisunKeyLengthEnum : ushort 4 | { 5 | [DisplayName("10 symbols (40 bits)")] 6 | L40 = 40, 7 | [DisplayName("16 symbols (64 bits)")] 8 | L64 = 64, 9 | [DisplayName("32 symbols (128 bits)")] 10 | L128 = 128, 11 | [DisplayName("48 symbols (192 bits)")] 12 | L192 = 192, 13 | [DisplayName("64 symbols (256 bits)")] 14 | L256 = 256 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Licensing.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | 5 | namespace CFT 6 | { 7 | [Serializable] 8 | public class Licensing 9 | { 10 | public const int UNLOCK_KEY_LEN = 32; 11 | 12 | public byte[] HyteraBPUnlockKey { get; set; } 13 | 14 | public byte[] MotorolaBPUnlockKey { get; set; } 15 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 16 | public byte[] MotorolaEPUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 17 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 18 | public byte[] NxdnScramblerUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 19 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 20 | public byte[] P25ADPUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 21 | public byte[] P25DESUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 22 | public byte[] DMRAESUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 23 | public byte[] HyteraEPUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 24 | public byte[] P25AESUnlockKey { get; set; } = new byte[UNLOCK_KEY_LEN]; 25 | 26 | public bool IsKeysExist 27 | { 28 | get 29 | { 30 | return !Utils.IsArrayEmpty(MotorolaBPUnlockKey) || 31 | !Utils.IsArrayEmpty(HyteraBPUnlockKey) || 32 | !Utils.IsArrayEmpty(HyteraEPUnlockKey) || 33 | !Utils.IsArrayEmpty(MotorolaEPUnlockKey) || 34 | !Utils.IsArrayEmpty(P25ADPUnlockKey) || 35 | !Utils.IsArrayEmpty(P25DESUnlockKey) || 36 | !Utils.IsArrayEmpty(DMRAESUnlockKey) || 37 | !Utils.IsArrayEmpty(NxdnScramblerUnlockKey) || 38 | !Utils.IsArrayEmpty(P25AESUnlockKey); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LicensingForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/ListViewExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace CFT 7 | { 8 | [EditorBrowsable(EditorBrowsableState.Never)] 9 | public static class ListViewExtensions 10 | { 11 | [StructLayout(LayoutKind.Sequential)] 12 | public struct HDITEM 13 | { 14 | public Mask mask; 15 | public int cxy; 16 | [MarshalAs(UnmanagedType.LPTStr)] public string pszText; 17 | public IntPtr hbm; 18 | public int cchTextMax; 19 | public Format fmt; 20 | public IntPtr lParam; 21 | // _WIN32_IE >= 0x0300 22 | public int iImage; 23 | public int iOrder; 24 | // _WIN32_IE >= 0x0500 25 | public uint type; 26 | public IntPtr pvFilter; 27 | // _WIN32_WINNT >= 0x0600 28 | public uint state; 29 | 30 | [Flags] 31 | public enum Mask 32 | { 33 | Format = 0x4, // HDI_FORMAT 34 | }; 35 | 36 | [Flags] 37 | public enum Format 38 | { 39 | SortDown = 0x200, // HDF_SORTDOWN 40 | SortUp = 0x400, // HDF_SORTUP 41 | }; 42 | }; 43 | 44 | public const int LVM_FIRST = 0x1000; 45 | public const int LVM_GETHEADER = LVM_FIRST + 31; 46 | 47 | public const int HDM_FIRST = 0x1200; 48 | public const int HDM_GETITEM = HDM_FIRST + 11; 49 | public const int HDM_SETITEM = HDM_FIRST + 12; 50 | 51 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 52 | public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam); 53 | 54 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 55 | public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, ref HDITEM lParam); 56 | 57 | public static void SetSortIcon(ListView listViewControl, int columnIndex, SortOrder order) 58 | { 59 | IntPtr columnHeader = SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); 60 | for (int columnNumber = 0; columnNumber <= listViewControl.Columns.Count - 1; columnNumber++) 61 | { 62 | var columnPtr = new IntPtr(columnNumber); 63 | var item = new HDITEM 64 | { 65 | mask = HDITEM.Mask.Format 66 | }; 67 | 68 | if (SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero) 69 | { 70 | throw new Win32Exception(); 71 | } 72 | 73 | if (order != SortOrder.None && columnNumber == columnIndex) 74 | { 75 | switch (order) 76 | { 77 | case SortOrder.Ascending: 78 | item.fmt &= ~HDITEM.Format.SortDown; 79 | item.fmt |= HDITEM.Format.SortUp; 80 | break; 81 | case SortOrder.Descending: 82 | item.fmt &= ~HDITEM.Format.SortUp; 83 | item.fmt |= HDITEM.Format.SortDown; 84 | break; 85 | } 86 | } 87 | else 88 | { 89 | item.fmt &= ~HDITEM.Format.SortDown & ~HDITEM.Format.SortUp; 90 | } 91 | 92 | if (SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero) 93 | { 94 | throw new Win32Exception(); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/MacAddressForm.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace CFT 4 | { 5 | public partial class MacAddressForm : Form 6 | { 7 | public byte [] MacAddress { get; set; } 8 | 9 | public MacAddressForm(byte[] macAddress) 10 | { 11 | InitializeComponent(); 12 | 13 | MacAddress = macAddress; 14 | 15 | if(macAddress == null || macAddress.Length != 6) 16 | btnOk.Enabled = false; 17 | else 18 | { 19 | tbMAC3.Text = macAddress[3].ToString("X2"); 20 | tbMAC4.Text = macAddress[4].ToString("X2"); 21 | tbMAC5.Text = macAddress[5].ToString("X2"); 22 | } 23 | } 24 | 25 | private void btnOk_Click(object sender, System.EventArgs e) 26 | { 27 | MacAddress[0] = byte.Parse(tbMAC0.Text, System.Globalization.NumberStyles.HexNumber); 28 | MacAddress[1] = byte.Parse(tbMAC1.Text, System.Globalization.NumberStyles.HexNumber); 29 | MacAddress[2] = byte.Parse(tbMAC2.Text, System.Globalization.NumberStyles.HexNumber); 30 | 31 | try 32 | { 33 | MacAddress[3] = byte.Parse(tbMAC3.Text, System.Globalization.NumberStyles.HexNumber); 34 | } 35 | catch 36 | { 37 | MessageBox.Show("Wrong MAC Address [3]"); 38 | tbMAC3.Focus(); 39 | return; 40 | } 41 | try 42 | { 43 | MacAddress[4] = byte.Parse(tbMAC4.Text, System.Globalization.NumberStyles.HexNumber); 44 | } 45 | catch 46 | { 47 | MessageBox.Show("Wrong MAC Address [4]"); 48 | tbMAC4.Focus(); 49 | return; 50 | } 51 | try 52 | { 53 | MacAddress[5] = byte.Parse(tbMAC5.Text, System.Globalization.NumberStyles.HexNumber); 54 | } 55 | catch 56 | { 57 | MessageBox.Show("Wrong MAC Address [5]"); 58 | tbMAC5.Focus(); 59 | return; 60 | } 61 | 62 | DialogResult = DialogResult.OK; 63 | Close(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/MacAddressForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/MotorolaBPEncryptionMethod.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public class MotorolaBPEncryptionMethod : IEncryptionMethod 4 | { 5 | public string Name => "Motorola BP"; 6 | public byte Key { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/MotorolaBPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class MotorolaBPEncryptionMethodForm : Form 7 | { 8 | public MotorolaBPEncryptionRow EncryptionRow { get; private set; } 9 | 10 | public bool IsBatchMode { get; private set; } 11 | 12 | public MotorolaBPEncryptionMethodForm(MotorolaBPEncryptionRow row = null, bool batchMode = false) 13 | { 14 | InitializeComponent(); 15 | 16 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 17 | 18 | IsBatchMode = batchMode; 19 | if (IsBatchMode) 20 | { 21 | Text += " (Batch mode)"; 22 | tbFrequency.Enabled = false; 23 | tbNotes.Enabled = false; 24 | } 25 | 26 | if (row == null) 27 | { 28 | EncryptionRow = new MotorolaBPEncryptionRow(); 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | nudKey.Value = row.Key; 40 | tbNotes.Text = row.Notes; 41 | } 42 | OptionsControl_OptionChanged(this, null); 43 | } 44 | 45 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 46 | { 47 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 48 | nudKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 49 | } 50 | 51 | private void btnOK_Click(object sender, EventArgs e) 52 | { 53 | string errorStr; 54 | 55 | if (optionsControl.IsExistErrors(out errorStr)) 56 | { 57 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 58 | return; 59 | } 60 | 61 | uint freq = 0; 62 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 63 | { 64 | tbFrequency.Focus(); 65 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 66 | return; 67 | } 68 | 69 | EncryptionRow.Frequency = freq; 70 | EncryptionRow.ActivateOptions = optionsControl.Options; 71 | EncryptionRow.Key = (byte)nudKey.Value; 72 | EncryptionRow.Notes = tbNotes.Text.Trim(); 73 | 74 | DialogResult = DialogResult.OK; 75 | Close(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/MotorolaBPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Motorola BP")] 7 | 8 | [Serializable] 9 | public class MotorolaBPEncryptionRow : DmrEncryptionRow 10 | { 11 | public byte Key { get; set; } 12 | 13 | public MotorolaBPEncryptionRow() 14 | { 15 | Key = 1; 16 | ActivateOptions = new DmrActivateOptions(); 17 | } 18 | 19 | [JsonIgnore] 20 | public override string Description 21 | { 22 | get 23 | { 24 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 25 | return $"Motorola BP: {ActivateOptions}"; 26 | else 27 | return $"Motorola BP: Key({Key}) {ActivateOptions}"; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MotorolaEPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class MotorolaEPEncryptionMethodForm : Form 7 | { 8 | public MotorolaEPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public MotorolaEPEncryptionMethodForm(MotorolaEPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new MotorolaEPEncryptionRow(); 28 | EncryptionRow.Key = new byte[MotorolaEPEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | cbKeyID.Checked = row.ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.KeyId); 41 | if (cbKeyID.Checked) 42 | tbKeyID.Text = row.ActivateOptions.KeyId.ToString(); 43 | 44 | if (Utils.IsArrayEmpty(row.Key)) 45 | tbKey.Text = string.Empty; 46 | else 47 | tbKey.Text = Utils.BytesToHexString(row.Key); 48 | 49 | tbNotes.Text = row.Notes; 50 | } 51 | cbKeyID_CheckedChanged(this, null); 52 | OptionsControl_OptionChanged(this, null); 53 | } 54 | 55 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 56 | { 57 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 58 | tbKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 59 | } 60 | 61 | private void btnOK_Click(object sender, EventArgs e) 62 | { 63 | string errorStr; 64 | 65 | if (optionsControl.IsExistErrors(out errorStr)) 66 | { 67 | if (!errorStr.Contains("no Frequency") || !cbKeyID.Checked) 68 | { 69 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 70 | return; 71 | } 72 | } 73 | 74 | uint freq = 0; 75 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 76 | { 77 | tbFrequency.Focus(); 78 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 79 | return; 80 | } 81 | 82 | byte keyID = 0; 83 | if (cbKeyID.Checked) 84 | { 85 | try 86 | { 87 | keyID = byte.Parse(tbKeyID.Text); 88 | } 89 | catch 90 | { 91 | MessageBox.Show("Wrong KeyID", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 92 | tbKeyID.Focus(); 93 | return; 94 | } 95 | } 96 | 97 | byte[] key; 98 | try 99 | { 100 | key = Utils.HexStringToBytes(tbKey.Text); 101 | } 102 | catch 103 | { 104 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 105 | tbKey.Focus(); 106 | return; 107 | } 108 | 109 | Array.Clear(EncryptionRow.Key, 0, 5); 110 | if (key.Length > 0) 111 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > MotorolaEPEncryptionRow.KEY_SIZE ? MotorolaEPEncryptionRow.KEY_SIZE : key.Length); 112 | 113 | EncryptionRow.Frequency = freq; 114 | EncryptionRow.ActivateOptions = optionsControl.Options; 115 | if (cbKeyID.Checked) 116 | { 117 | EncryptionRow.ActivateOptions.Activate(DmrSelectedActivateOptionsEnum.KeyId); 118 | EncryptionRow.ActivateOptions.KeyId = keyID; 119 | } 120 | else 121 | { 122 | EncryptionRow.ActivateOptions.Deactivate(DmrSelectedActivateOptionsEnum.KeyId); 123 | } 124 | 125 | EncryptionRow.Notes = tbNotes.Text.Trim(); 126 | 127 | DialogResult = DialogResult.OK; 128 | Close(); 129 | } 130 | 131 | private void cbKeyID_CheckedChanged(object sender, EventArgs e) 132 | { 133 | tbKeyID.Enabled = cbKeyID.Checked; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/MotorolaEPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("Motorola EP")] 7 | [Serializable] 8 | public class MotorolaEPEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 5; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (ActivateOptions.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute)) 20 | return $"Motorola EP: {ActivateOptions}"; 21 | else 22 | return $"Motorola EP: Key({Utils.BytesToHexString(Key)}) {ActivateOptions}"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NxdnActivateOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace CFT 5 | { 6 | [Serializable] 7 | public class NxdnActivateOptions : IActivateOptions 8 | { 9 | public NxdnSelectedActivateOptionsEnum Options { get; set; } 10 | public byte RAN { get; set; } 11 | public ushort GroupID { get; set; } 12 | public byte KeyID { get; set; } 13 | public ushort SourceID { get; set; } 14 | 15 | public NxdnActivateOptions() 16 | { 17 | Options = NxdnSelectedActivateOptionsEnum.Frequency; 18 | } 19 | 20 | public bool IsActivated(NxdnSelectedActivateOptionsEnum option) 21 | { 22 | return (Options & option) == option; 23 | } 24 | 25 | public void Activate(NxdnSelectedActivateOptionsEnum option) 26 | { 27 | Options = option | option; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | var sb = new StringBuilder(); 33 | if (IsActivated(NxdnSelectedActivateOptionsEnum.RAN)) 34 | sb.Append($"RAN({RAN}) "); 35 | if (IsActivated(NxdnSelectedActivateOptionsEnum.GroupID)) 36 | sb.Append($"GroupID({GroupID}) "); 37 | if (IsActivated(NxdnSelectedActivateOptionsEnum.KeyID)) 38 | sb.Append($"KeyID({KeyID}) "); 39 | if (IsActivated(NxdnSelectedActivateOptionsEnum.SourceID)) 40 | sb.Append($"SourceID({SourceID}) "); 41 | if (IsActivated(NxdnSelectedActivateOptionsEnum.ForceMute)) 42 | sb.Append($"Force Mute"); 43 | return sb.ToString(); 44 | } 45 | 46 | public int CompareTo(object obj) 47 | { 48 | var o = obj as NxdnActivateOptions; 49 | if (o == null) 50 | return 1; 51 | 52 | if (o.Options != Options) 53 | return 1; 54 | 55 | if (IsActivated(NxdnSelectedActivateOptionsEnum.RAN) && 56 | o.RAN != RAN) 57 | return 1; 58 | 59 | if (IsActivated(NxdnSelectedActivateOptionsEnum.GroupID) && 60 | o.GroupID != GroupID) 61 | return 1; 62 | 63 | if (IsActivated(NxdnSelectedActivateOptionsEnum.KeyID) && 64 | o.KeyID != KeyID) 65 | return 1; 66 | 67 | if (IsActivated(NxdnSelectedActivateOptionsEnum.SourceID) && 68 | o.SourceID != SourceID) 69 | return 1; 70 | 71 | return 0; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/NxdnCypherTypeEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum NxdnCypherTypeEnum 4 | { 5 | None = 0, 6 | Scrambler = 1, 7 | Aes = 2, 8 | Des = 3 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/NxdnScramblerEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("NXDN Scrambler")] 7 | [Serializable] 8 | public class NxdnScramblerEncryptionRow : BaseEncryptionRow 9 | { 10 | public ushort Key { get; set; } 11 | 12 | public NxdnActivateOptions ActivateOptions { get; set; } 13 | 14 | [JsonIgnore] 15 | public override ProtocolEnum Protocol => ProtocolEnum.NXDN; 16 | 17 | [JsonIgnore] 18 | public override string Description 19 | { 20 | get 21 | { 22 | if (ActivateOptions.IsActivated(NxdnSelectedActivateOptionsEnum.ForceMute)) 23 | return $"NXDN Scrambler: {ActivateOptions}"; 24 | else 25 | return $"NXDN Scrambler: Key({Key}) {ActivateOptions}"; 26 | } 27 | } 28 | 29 | public override bool IsFrequencyNeed => ActivateOptions == null ? true : ActivateOptions.IsActivated(NxdnSelectedActivateOptionsEnum.Frequency); 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/NxdnSelectOptionsEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | [System.Flags] 4 | public enum NxdnSelectOptionsEnum : uint 5 | { 6 | RAN = 1 << 0, 7 | GID = 1 << 1, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/NxdnSelectedActivateOptionsEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | [System.Flags] 4 | public enum NxdnSelectedActivateOptionsEnum : uint 5 | { 6 | RAN = 1 << 0, 7 | GroupID = 1 << 1, 8 | Frequency = 1 << 2, 9 | KeyID = 1 << 3, 10 | SourceID = 1 << 4, 11 | 12 | ForceMute = 1 << 15 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/P25ADPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class P25ADPEncryptionMethodForm : Form 7 | { 8 | public P25ADPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public P25ADPEncryptionMethodForm(P25ADPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new P25ADPEncryptionRow(); 28 | EncryptionRow.Key = new byte[P25ADPEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | if (Utils.IsArrayEmpty(row.Key)) 41 | tbKey.Text = string.Empty; 42 | else 43 | tbKey.Text = Utils.BytesToHexString(row.Key); 44 | 45 | tbNotes.Text = row.Notes; 46 | } 47 | OptionsControl_OptionChanged(this, null); 48 | } 49 | 50 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 51 | { 52 | tbFrequency.Enabled = optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.Frequency); 53 | tbKey.Enabled = !optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.ForceMute); 54 | } 55 | 56 | private void btnOK_Click(object sender, EventArgs e) 57 | { 58 | string errorStr; 59 | 60 | if (optionsControl.IsExistErrors(out errorStr)) 61 | { 62 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 63 | return; 64 | } 65 | 66 | uint freq = 0; 67 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 68 | { 69 | tbFrequency.Focus(); 70 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 71 | return; 72 | } 73 | 74 | byte[] key; 75 | try 76 | { 77 | key = Utils.HexStringToBytes(tbKey.Text); 78 | } 79 | catch 80 | { 81 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 82 | tbKey.Focus(); 83 | return; 84 | } 85 | 86 | Array.Clear(EncryptionRow.Key, 0, 5); 87 | if (key.Length > 0) 88 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > P25ADPEncryptionRow.KEY_SIZE ? P25ADPEncryptionRow.KEY_SIZE : key.Length); 89 | 90 | EncryptionRow.Frequency = freq; 91 | EncryptionRow.ActivateOptions = optionsControl.Options; 92 | 93 | EncryptionRow.Notes = tbNotes.Text.Trim(); 94 | 95 | DialogResult = DialogResult.OK; 96 | Close(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/P25ADPEncryptionMethodForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/P25ADPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("P25 ADP")] 7 | [Serializable] 8 | public class P25ADPEncryptionRow : P25EncryptionRow 9 | { 10 | public const int KEY_SIZE = 5; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (!string.IsNullOrEmpty($"{ActivateOptions}")) 20 | return $"P25 ADP: {ActivateOptions}"; 21 | else 22 | return $"P25 ADP"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/P25AESEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class P25AESEncryptionMethodForm : Form 7 | { 8 | public P25AESEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public P25AESEncryptionMethodForm(P25AESEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new P25AESEncryptionRow(); 28 | EncryptionRow.Key = new byte[P25AESEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | if (Utils.IsArrayEmpty(row.Key)) 41 | tbKey.Text = string.Empty; 42 | else 43 | tbKey.Text = Utils.BytesToHexString(row.Key); 44 | 45 | tbNotes.Text = row.Notes; 46 | } 47 | OptionsControl_OptionChanged(this, null); 48 | } 49 | 50 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 51 | { 52 | tbFrequency.Enabled = optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.Frequency); 53 | tbKey.Enabled = !optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.ForceMute); 54 | } 55 | 56 | private void btnOK_Click(object sender, EventArgs e) 57 | { 58 | string errorStr; 59 | 60 | if (optionsControl.IsExistErrors(out errorStr)) 61 | { 62 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 63 | return; 64 | } 65 | 66 | uint freq = 0; 67 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 68 | { 69 | tbFrequency.Focus(); 70 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 71 | return; 72 | } 73 | 74 | byte[] key; 75 | try 76 | { 77 | key = Utils.HexStringToBytes(tbKey.Text); 78 | } 79 | catch 80 | { 81 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 82 | tbKey.Focus(); 83 | return; 84 | } 85 | 86 | Array.Clear(EncryptionRow.Key, 0, 5); 87 | if (key.Length > 0) 88 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > P25AESEncryptionRow.KEY_SIZE ? P25AESEncryptionRow.KEY_SIZE : key.Length); 89 | 90 | EncryptionRow.Frequency = freq; 91 | EncryptionRow.ActivateOptions = optionsControl.Options; 92 | 93 | EncryptionRow.Notes = tbNotes.Text.Trim(); 94 | 95 | DialogResult = DialogResult.OK; 96 | Close(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/P25AESEncryptionMethodForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/P25AESEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("P25 AES")] 7 | [Serializable] 8 | public class P25AESEncryptionRow : P25EncryptionRow 9 | { 10 | public const int KEY_SIZE = 32; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (!string.IsNullOrEmpty($"{ActivateOptions}")) 20 | return $"P25 AES: {ActivateOptions}"; 21 | else 22 | return $"P25 AES"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/P25ActivateOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace CFT 5 | { 6 | [Serializable] 7 | public class P25ActivateOptions : IActivateOptions 8 | { 9 | public P25SelectedActivateOptionsEnum Options { get; set; } 10 | public ushort NAC { get; set; } 11 | public uint GroupID { get; set; } 12 | public ushort KeyID { get; set; } 13 | public uint SourceID { get; set; } 14 | 15 | public P25ActivateOptions() 16 | { 17 | Options = P25SelectedActivateOptionsEnum.Frequency; 18 | } 19 | 20 | public bool IsActivated(P25SelectedActivateOptionsEnum option) 21 | { 22 | return (Options & option) == option; 23 | } 24 | 25 | public void Activate(P25SelectedActivateOptionsEnum option) 26 | { 27 | Options = option | option; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | var sb = new StringBuilder(); 33 | if (IsActivated(P25SelectedActivateOptionsEnum.NAC)) 34 | sb.Append($"NAC({NAC:X}) "); 35 | if (IsActivated(P25SelectedActivateOptionsEnum.GroupID)) 36 | sb.Append($"GroupID({GroupID}) "); 37 | if (IsActivated(P25SelectedActivateOptionsEnum.KeyID)) 38 | sb.Append($"KeyID({KeyID:X}) "); 39 | if (IsActivated(P25SelectedActivateOptionsEnum.SourceID)) 40 | sb.Append($"SourceID({SourceID}) "); 41 | if (IsActivated(P25SelectedActivateOptionsEnum.ForceMute)) 42 | sb.Append($"Force Mute"); 43 | return sb.ToString(); 44 | } 45 | 46 | public int CompareTo(object obj) 47 | { 48 | var o = obj as P25ActivateOptions; 49 | if (o == null) 50 | return 1; 51 | 52 | if (o.Options != Options) 53 | return 1; 54 | 55 | if (IsActivated(P25SelectedActivateOptionsEnum.NAC) && 56 | o.NAC != NAC) 57 | return 1; 58 | 59 | if (IsActivated(P25SelectedActivateOptionsEnum.GroupID) && 60 | o.GroupID != GroupID) 61 | return 1; 62 | 63 | if (IsActivated(P25SelectedActivateOptionsEnum.KeyID) && 64 | o.KeyID != KeyID) 65 | return 1; 66 | 67 | if (IsActivated(P25SelectedActivateOptionsEnum.SourceID) && 68 | o.SourceID != SourceID) 69 | return 1; 70 | 71 | return 0; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/P25DESEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class P25DESEncryptionMethodForm : Form 7 | { 8 | public P25DESEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public P25DESEncryptionMethodForm(P25DESEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new P25DESEncryptionRow(); 28 | EncryptionRow.Key = new byte[P25DESEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | if (Utils.IsArrayEmpty(row.Key)) 41 | tbKey.Text = string.Empty; 42 | else 43 | tbKey.Text = Utils.BytesToHexString(row.Key); 44 | 45 | tbNotes.Text = row.Notes; 46 | } 47 | OptionsControl_OptionChanged(this, null); 48 | } 49 | 50 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 51 | { 52 | tbFrequency.Enabled = optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.Frequency); 53 | tbKey.Enabled = !optionsControl.Options.IsActivated(P25SelectedActivateOptionsEnum.ForceMute); 54 | } 55 | 56 | private void btnOK_Click(object sender, EventArgs e) 57 | { 58 | string errorStr; 59 | 60 | if (optionsControl.IsExistErrors(out errorStr)) 61 | { 62 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 63 | return; 64 | } 65 | 66 | uint freq = 0; 67 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 68 | { 69 | tbFrequency.Focus(); 70 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 71 | return; 72 | } 73 | 74 | byte[] key; 75 | try 76 | { 77 | key = Utils.HexStringToBytes(tbKey.Text); 78 | } 79 | catch 80 | { 81 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 82 | tbKey.Focus(); 83 | return; 84 | } 85 | 86 | Array.Clear(EncryptionRow.Key, 0, 5); 87 | if (key.Length > 0) 88 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > P25DESEncryptionRow.KEY_SIZE ? P25DESEncryptionRow.KEY_SIZE : key.Length); 89 | 90 | EncryptionRow.Frequency = freq; 91 | EncryptionRow.ActivateOptions = optionsControl.Options; 92 | 93 | EncryptionRow.Notes = tbNotes.Text.Trim(); 94 | 95 | DialogResult = DialogResult.OK; 96 | Close(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/P25DESEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("P25 DES")] 7 | [Serializable] 8 | public class P25DESEncryptionRow : P25EncryptionRow 9 | { 10 | public const int KEY_SIZE = 8; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (!string.IsNullOrEmpty($"{ActivateOptions}")) 20 | return $"P25 DES: {ActivateOptions}"; 21 | else 22 | return $"P25 DES"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/P25EncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [Serializable] 7 | public abstract class P25EncryptionRow : BaseEncryptionRow 8 | { 9 | public P25ActivateOptions ActivateOptions { get; set; } 10 | 11 | [JsonIgnore] 12 | public override ProtocolEnum Protocol => ProtocolEnum.P25; 13 | 14 | public override bool IsFrequencyNeed => ActivateOptions == null ? true : ActivateOptions.IsActivated(P25SelectedActivateOptionsEnum.Frequency); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/P25SelectedActivateOptionsEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | [System.Flags] 4 | public enum P25SelectedActivateOptionsEnum : uint 5 | { 6 | NAC = 1 << 0, 7 | GroupID = 1 << 1, 8 | Frequency = 1 << 2, 9 | KeyID = 1 << 3, 10 | SourceID = 1 << 4, 11 | 12 | ForceMute = 1 << 15 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | internal static class Program 7 | { 8 | /// 9 | /// The main entry point for the application. 10 | /// 11 | [STAThread] 12 | static void Main(string[] args) 13 | { 14 | Application.EnableVisualStyles(); 15 | Application.SetCompatibleTextRenderingDefault(false); 16 | Application.Run(new MainForm(args.Length > 0 ? args[0] : null)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Project.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace CFT 8 | { 9 | [Serializable] 10 | public class Project 11 | { 12 | [JsonIgnore] 13 | public string Path { get; set; } 14 | public List Scanners { get; set; } 15 | public List EcryptionRows { get; set; } 16 | 17 | public static Project Load(string fileName) 18 | { 19 | var p = JsonConvert.DeserializeObject(SuperComplexDeobfuscator(File.ReadAllBytes(fileName)), new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 20 | p.Path = fileName; 21 | return p; 22 | } 23 | 24 | public void Save() 25 | { 26 | File.WriteAllBytes(Path, SuperComplexObfuscator(JsonConvert.SerializeObject(this, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto } ))); 27 | } 28 | 29 | private static byte[] SuperComplexObfuscator(string str) 30 | { 31 | var bs = Encoding.UTF8.GetBytes(str); 32 | for(var i=0; i 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 CFT.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/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ProtocolEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum ProtocolEnum 4 | { 5 | All = 0, 6 | DMR = 1, 7 | NXDN = 2, 8 | P25 = 3 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Scanner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [Serializable] 6 | public class Scanner 7 | { 8 | public string Name { get; set; } 9 | 10 | public ScannerModelEnum Model { get; set; } 11 | 12 | public Licensing Licensing { get; set; } 13 | 14 | public KeyMapping KeyMapping { get; set; } 15 | 16 | public bool MuteEncryptedVoiceTraffic { get; set; } 17 | 18 | public byte[] MacAddress { get; set; } 19 | 20 | public DisplayAdditionalInfo DisplayAdditionalInfo { get; set; } 21 | 22 | public bool LEDAlertWhileDigitalVoiceGoesOn { get; set; } 23 | 24 | public Scanner() 25 | { 26 | Model = ScannerModelEnum.BCD436HP; 27 | Name = "Scanner"; 28 | MacAddress = new byte[6]; 29 | } 30 | 31 | public override string ToString() 32 | { 33 | if (string.IsNullOrEmpty(Name)) 34 | return $"{Model}"; 35 | return $"{Name.Trim()} ({Model})"; 36 | } 37 | 38 | public static ScannerBaseModelEnum GetBaseModel(ScannerModelEnum model) 39 | { 40 | switch(model) 41 | { 42 | case ScannerModelEnum.BCD436HP: 43 | case ScannerModelEnum.BCD536HP: 44 | case ScannerModelEnum.UBCD3600XLT: 45 | case ScannerModelEnum.UBCD436PT: 46 | case ScannerModelEnum.UBCD536PT: 47 | return ScannerBaseModelEnum.BCD; 48 | 49 | default: 50 | return ScannerBaseModelEnum.SDS; 51 | } 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ScannerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CFT 4 | { 5 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] 6 | public class ScannerAttribute : Attribute 7 | { 8 | public ScannerModelEnum Model { get; private set; } 9 | public ScannerAttribute(ScannerModelEnum model) 10 | { 11 | Model = model; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ScannerBaseModelEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum ScannerBaseModelEnum 4 | { 5 | BCD = 0, 6 | SDS = 1 7 | } 8 | } -------------------------------------------------------------------------------- /src/ScannerForm.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace CFT 4 | { 5 | public partial class ScannerForm : Form 6 | { 7 | public Scanner Scanner { get; private set; } 8 | private KeyMapping _keyMapping = new KeyMapping(); 9 | 10 | public ScannerForm(Scanner scanner) 11 | { 12 | InitializeComponent(); 13 | 14 | Scanner = scanner == null ? new Scanner() : Utils.DeepClone(scanner); 15 | _keyMapping = Scanner.KeyMapping; 16 | 17 | Utils.FillComboBoxData(cbModel, typeof(ScannerModelEnum)); 18 | if (!Utils.SetComboBoxData(cbModel, Scanner.Model)) 19 | cbModel.SelectedIndex = 0; 20 | 21 | cbMute.Checked = Scanner.MuteEncryptedVoiceTraffic; 22 | cbLedAlertWhile.Checked = Scanner.LEDAlertWhileDigitalVoiceGoesOn; 23 | tbName.Text = Scanner.Name; 24 | UpdateControls(); 25 | } 26 | 27 | private void btnOk_Click(object sender, System.EventArgs e) 28 | { 29 | Scanner.Model = (ScannerModelEnum)Utils.GetComboBoxData(cbModel.SelectedItem); 30 | Scanner.Name = tbName.Text.Trim(); 31 | Scanner.KeyMapping = _keyMapping; 32 | Scanner.MuteEncryptedVoiceTraffic = cbMute.Checked; 33 | Scanner.LEDAlertWhileDigitalVoiceGoesOn = cbLedAlertWhile.Checked; 34 | } 35 | 36 | private void btnLicenses_Click(object sender, System.EventArgs e) 37 | { 38 | var frm = new LicensingForm(Scanner.Licensing); 39 | if (frm.ShowDialog() == DialogResult.OK) 40 | { 41 | Scanner.Licensing = frm.Licensing; 42 | UpdateControls(); 43 | } 44 | } 45 | 46 | private void btnKeyMappings_Click(object sender, System.EventArgs e) 47 | { 48 | var model = (ScannerModelEnum)Utils.GetComboBoxData(cbModel.SelectedItem); 49 | var frm = new KeyMappingForm(model, _keyMapping); 50 | if (frm.ShowDialog() == DialogResult.OK) 51 | { 52 | _keyMapping = frm.KeyMapping; 53 | } 54 | } 55 | 56 | private void cbModel_SelectedIndexChanged(object sender, System.EventArgs e) 57 | { 58 | try 59 | { 60 | var model = (ScannerModelEnum)Utils.GetComboBoxData(cbModel.SelectedItem); 61 | // todo selecting from ScannerModelEnum 62 | btnMacAddress.Enabled = model == ScannerModelEnum.SDS200 || model == ScannerModelEnum.SDS200E; 63 | } 64 | catch 65 | { 66 | btnMacAddress.Enabled = false; 67 | } 68 | } 69 | 70 | private void btnMacAddress_Click(object sender, System.EventArgs e) 71 | { 72 | var frm = new MacAddressForm(Scanner.MacAddress); 73 | if (frm.ShowDialog() == DialogResult.OK) 74 | { 75 | Scanner.MacAddress = frm.MacAddress; 76 | } 77 | } 78 | 79 | private void btnDisplayAdditionalInfo_Click(object sender, System.EventArgs e) 80 | { 81 | var model = (ScannerModelEnum)Utils.GetComboBoxData(cbModel.SelectedItem); 82 | 83 | var frm = new DisplayAdditionalInfoForm(model, Scanner.DisplayAdditionalInfo); 84 | if (frm.ShowDialog() == DialogResult.OK) 85 | { 86 | Scanner.DisplayAdditionalInfo = frm.DisplayAdditionalInfo; 87 | } 88 | } 89 | 90 | private void UpdateControls() 91 | { 92 | cbLedAlertWhile.Enabled = Scanner.Licensing == null ? false : Scanner.Licensing.IsKeysExist; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/ScannerForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/ScannerListForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | 5 | namespace CFT 6 | { 7 | public partial class ScannerListForm : Form 8 | { 9 | public List Scanners { get; private set; } 10 | 11 | public ScannerListForm(List scanners) 12 | { 13 | InitializeComponent(); 14 | 15 | Scanners = scanners == null ? new List() : scanners; 16 | 17 | FillScannerListBox(); 18 | UpdateControls(); 19 | } 20 | 21 | private void FillScannerListBox() 22 | { 23 | lbScanners.Items.Clear(); 24 | foreach (var item in Scanners) 25 | { 26 | lbScanners.Items.Add(new DisplayTagObject(item)); 27 | } 28 | if (lbScanners.Items.Count > 0) 29 | lbScanners.SelectedIndex = 0; 30 | } 31 | 32 | private void UpdateControls() 33 | { 34 | btnEdit.Enabled = lbScanners.SelectedIndex >= 0; 35 | btnDelete.Enabled = lbScanners.SelectedIndex >= 0; 36 | btnLicenses.Enabled = lbScanners.SelectedIndex >= 0; 37 | lbScanners.Invalidate(); 38 | } 39 | 40 | private void lbScanners_SelectedIndexChanged(object sender, EventArgs e) 41 | { 42 | UpdateControls(); 43 | } 44 | 45 | private void btnDelete_Click(object sender, EventArgs e) 46 | { 47 | if (MessageBox.Show("Delete?", "Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes) 48 | { 49 | Scanners.RemoveAt(lbScanners.SelectedIndex); 50 | FillScannerListBox(); 51 | UpdateControls(); 52 | } 53 | } 54 | 55 | private void btnAdd_Click(object sender, EventArgs e) 56 | { 57 | var frm = new ScannerForm(null); 58 | if (frm.ShowDialog() == DialogResult.OK) 59 | { 60 | Scanners.Add(frm.Scanner); 61 | FillScannerListBox(); 62 | UpdateControls(); 63 | } 64 | } 65 | 66 | private void btnEdit_Click(object sender, EventArgs e) 67 | { 68 | if (lbScanners.SelectedIndex == -1) 69 | return; 70 | 71 | var frm = new ScannerForm(Scanners[lbScanners.SelectedIndex]); 72 | if (frm.ShowDialog() == DialogResult.OK) 73 | { 74 | Scanners[lbScanners.SelectedIndex] = frm.Scanner; 75 | FillScannerListBox(); 76 | UpdateControls(); 77 | } 78 | } 79 | 80 | private void lbScanners_DoubleClick(object sender, EventArgs e) 81 | { 82 | btnEdit_Click(sender, e); 83 | } 84 | 85 | private void lbScanners_KeyPress(object sender, KeyPressEventArgs e) 86 | { 87 | if (e.KeyChar == 13) 88 | btnEdit_Click(sender, e); 89 | } 90 | 91 | private void btnLicenses_Click(object sender, EventArgs e) 92 | { 93 | if (lbScanners.SelectedIndex == -1) 94 | return; 95 | 96 | var frm = new LicensingForm(Scanners[lbScanners.SelectedIndex].Licensing); 97 | if (frm.ShowDialog() == DialogResult.OK) 98 | { 99 | Scanners[lbScanners.SelectedIndex].Licensing = frm.Licensing; 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ScannerListForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/ScannerModelEnum.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | public enum ScannerModelEnum : byte 4 | { 5 | BCD436HP = 1, 6 | BCD536HP = 2, 7 | SDS100 = 3, 8 | SDS200 = 4, 9 | UBCD3600XLT = 5, 10 | SDS100E = 6, 11 | SDS200E = 7, 12 | UBCD436PT = 8, 13 | UBCD536PT = 9, 14 | USDS100 = 10 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SelectEncryptionMethodForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace CFT 2 | { 3 | partial class SelectEncryptionMethodForm 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.listBox = new System.Windows.Forms.ListBox(); 32 | this.btnSelect = new System.Windows.Forms.Button(); 33 | this.btnCancel = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // listBox 37 | // 38 | this.listBox.FormattingEnabled = true; 39 | this.listBox.ItemHeight = 17; 40 | this.listBox.Location = new System.Drawing.Point(12, 12); 41 | this.listBox.Name = "listBox"; 42 | this.listBox.Size = new System.Drawing.Size(217, 225); 43 | this.listBox.TabIndex = 0; 44 | // 45 | // btnSelect 46 | // 47 | this.btnSelect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 48 | this.btnSelect.DialogResult = System.Windows.Forms.DialogResult.OK; 49 | this.btnSelect.Location = new System.Drawing.Point(127, 251); 50 | this.btnSelect.Name = "btnSelect"; 51 | this.btnSelect.Size = new System.Drawing.Size(102, 32); 52 | this.btnSelect.TabIndex = 1; 53 | this.btnSelect.Text = "OK"; 54 | this.btnSelect.UseVisualStyleBackColor = true; 55 | this.btnSelect.Click += new System.EventHandler(this.btnSelect_Click); 56 | // 57 | // btnCancel 58 | // 59 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 60 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 61 | this.btnCancel.Location = new System.Drawing.Point(12, 251); 62 | this.btnCancel.Name = "btnCancel"; 63 | this.btnCancel.Size = new System.Drawing.Size(102, 32); 64 | this.btnCancel.TabIndex = 2; 65 | this.btnCancel.Text = "Cancel"; 66 | this.btnCancel.UseVisualStyleBackColor = true; 67 | // 68 | // SelectEncryptionMethodForm 69 | // 70 | this.AcceptButton = this.btnSelect; 71 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); 72 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 73 | this.CancelButton = this.btnCancel; 74 | this.ClientSize = new System.Drawing.Size(241, 295); 75 | this.Controls.Add(this.btnSelect); 76 | this.Controls.Add(this.btnCancel); 77 | this.Controls.Add(this.listBox); 78 | this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); 79 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 80 | this.Margin = new System.Windows.Forms.Padding(4); 81 | this.MaximizeBox = false; 82 | this.MinimizeBox = false; 83 | this.Name = "SelectEncryptionMethodForm"; 84 | this.ShowIcon = false; 85 | this.ShowInTaskbar = false; 86 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 87 | this.Text = "Select Encryption Method"; 88 | this.ResumeLayout(false); 89 | 90 | } 91 | 92 | #endregion 93 | 94 | private System.Windows.Forms.ListBox listBox; 95 | private System.Windows.Forms.Button btnSelect; 96 | private System.Windows.Forms.Button btnCancel; 97 | } 98 | } -------------------------------------------------------------------------------- /src/SelectEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class SelectEncryptionMethodForm : Form 7 | { 8 | public int Selection { get; private set; } 9 | 10 | public SelectEncryptionMethodForm() 11 | { 12 | InitializeComponent(); 13 | 14 | foreach(var item in Enum.GetValues(typeof(EncryptionMethodEnum))) 15 | { 16 | listBox.Items.Add(DisplayNameAttribute.GetName(item)); 17 | } 18 | listBox.SelectedIndex = 0; 19 | } 20 | 21 | private void btnSelect_Click(object sender, System.EventArgs e) 22 | { 23 | Selection = listBox.SelectedIndex; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/TyteraBPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class TyteraBPEncryptionMethodForm : Form 7 | { 8 | public TyteraBPEncryptionRow EncryptionRow { get; private set; } 9 | 10 | public bool IsBatchMode { get; private set; } 11 | 12 | public TyteraBPEncryptionMethodForm(TyteraBPEncryptionRow row = null, bool batchMode = false) 13 | { 14 | InitializeComponent(); 15 | 16 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 17 | 18 | IsBatchMode = batchMode; 19 | if (IsBatchMode) 20 | { 21 | Text += " (Batch mode)"; 22 | tbFrequency.Enabled = false; 23 | tbNotes.Enabled = false; 24 | } 25 | 26 | if (row == null) 27 | { 28 | EncryptionRow = new TyteraBPEncryptionRow(); 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | nudKey.Value = row.Key; 40 | tbNotes.Text = row.Notes; 41 | } 42 | OptionsControl_OptionChanged(this, null); 43 | } 44 | 45 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 46 | { 47 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 48 | nudKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 49 | } 50 | 51 | 52 | private void btnOK_Click(object sender, EventArgs e) 53 | { 54 | string errorStr; 55 | 56 | if (optionsControl.IsExistErrors(out errorStr)) 57 | { 58 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 59 | return; 60 | } 61 | 62 | uint freq = 0; 63 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 64 | { 65 | tbFrequency.Focus(); 66 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 67 | return; 68 | } 69 | 70 | EncryptionRow.Frequency = freq; 71 | EncryptionRow.ActivateOptions = optionsControl.Options; 72 | EncryptionRow.Key = (ushort)nudKey.Value; 73 | EncryptionRow.Notes = tbNotes.Text.Trim(); 74 | 75 | DialogResult = DialogResult.OK; 76 | Close(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/TyteraBPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("TYT BP")] 7 | [Serializable] 8 | public class TyteraBPEncryptionRow : DmrEncryptionRow 9 | { 10 | public ushort Key { get; set; } 11 | 12 | [JsonIgnore] 13 | public override string Description 14 | { 15 | get 16 | { 17 | if (!string.IsNullOrEmpty($"{ActivateOptions}")) 18 | return $"Tytera BP: {ActivateOptions}"; 19 | else 20 | return $"Tytera BP"; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/TyteraEPEncryptionMethodForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace CFT 5 | { 6 | public partial class TyteraEPEncryptionMethodForm : Form 7 | { 8 | public TyteraEPEncryptionRow EncryptionRow { get; private set; } 9 | public bool IsBatchMode { get; private set; } 10 | 11 | public TyteraEPEncryptionMethodForm(TyteraEPEncryptionRow row = null, bool batchMode = false) 12 | { 13 | InitializeComponent(); 14 | 15 | optionsControl.OptionChanged += OptionsControl_OptionChanged; 16 | 17 | IsBatchMode = batchMode; 18 | if (IsBatchMode) 19 | { 20 | Text += " (Batch mode)"; 21 | tbFrequency.Enabled = false; 22 | tbNotes.Enabled = false; 23 | } 24 | 25 | if (row == null) 26 | { 27 | EncryptionRow = new TyteraEPEncryptionRow(); 28 | EncryptionRow.Key = new byte[TyteraEPEncryptionRow.KEY_SIZE]; 29 | if (!IsBatchMode) 30 | tbFrequency.Text = Utils.GetFrequencyString(145500000); 31 | optionsControl.SetOptions(null); 32 | } 33 | else 34 | { 35 | EncryptionRow = row; 36 | if (!IsBatchMode) 37 | tbFrequency.Text = Utils.GetFrequencyString(row.Frequency); 38 | optionsControl.SetOptions(row.ActivateOptions); 39 | 40 | if (Utils.IsArrayEmpty(row.Key)) 41 | tbKey.Text = string.Empty; 42 | else 43 | tbKey.Text = Utils.BytesToHexString(row.Key); 44 | 45 | tbNotes.Text = row.Notes; 46 | } 47 | OptionsControl_OptionChanged(this, null); 48 | } 49 | 50 | private void OptionsControl_OptionChanged(object sender, EventArgs e) 51 | { 52 | tbFrequency.Enabled = optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.Frequency); 53 | tbKey.Enabled = !optionsControl.Options.IsActivated(DmrSelectedActivateOptionsEnum.ForceMute); 54 | } 55 | 56 | private void btnOK_Click(object sender, EventArgs e) 57 | { 58 | string errorStr; 59 | 60 | if (optionsControl.IsExistErrors(out errorStr)) 61 | { 62 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 63 | return; 64 | } 65 | 66 | uint freq = 0; 67 | if (!IsBatchMode && !Utils.ParseFrequency(tbFrequency.Text, out freq, out errorStr)) 68 | { 69 | tbFrequency.Focus(); 70 | MessageBox.Show(errorStr, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 71 | return; 72 | } 73 | 74 | byte[] key; 75 | try 76 | { 77 | key = Utils.HexStringToBytes(tbKey.Text); 78 | } 79 | catch 80 | { 81 | MessageBox.Show("Wrong Encryption Key Format.\r\nMust be HEX!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 82 | tbKey.Focus(); 83 | return; 84 | } 85 | 86 | Array.Clear(EncryptionRow.Key, 0, 5); 87 | if (key.Length > 0) 88 | Buffer.BlockCopy(key, 0, EncryptionRow.Key, 0, key.Length > TyteraEPEncryptionRow.KEY_SIZE ? TyteraEPEncryptionRow.KEY_SIZE : key.Length); 89 | 90 | EncryptionRow.Frequency = freq; 91 | EncryptionRow.ActivateOptions = optionsControl.Options; 92 | 93 | EncryptionRow.Notes = tbNotes.Text.Trim(); 94 | 95 | DialogResult = DialogResult.OK; 96 | Close(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/TyteraEPEncryptionRow.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CFT 5 | { 6 | [DisplayName("TYT EP")] 7 | [Serializable] 8 | public class TyteraEPEncryptionRow : DmrEncryptionRow 9 | { 10 | public const int KEY_SIZE = 16; 11 | 12 | public byte[] Key { get; set; } 13 | 14 | [JsonIgnore] 15 | public override string Description 16 | { 17 | get 18 | { 19 | if (!string.IsNullOrEmpty($"{ActivateOptions}")) 20 | return $"Tytera EP: {ActivateOptions}"; 21 | else 22 | return $"Tytera EP"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/app.ico -------------------------------------------------------------------------------- /src/fakesign.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/fakesign.snk -------------------------------------------------------------------------------- /src/images/JD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/JD.png -------------------------------------------------------------------------------- /src/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/add.png -------------------------------------------------------------------------------- /src/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/delete.png -------------------------------------------------------------------------------- /src/images/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/down.png -------------------------------------------------------------------------------- /src/images/duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/duplicate.png -------------------------------------------------------------------------------- /src/images/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/new.png -------------------------------------------------------------------------------- /src/images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/open.png -------------------------------------------------------------------------------- /src/images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/save.png -------------------------------------------------------------------------------- /src/images/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x27/CFT/2d2456d45351dae49daf81b74c9a3fa59e14e3c4/src/images/up.png -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------