├── .gitignore ├── Maschine 2 ├── Maschine 2.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── samlerner.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── samlerner.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── Maschine 2 │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Managers │ ├── BonjourManager.h │ ├── BonjourManager.m │ ├── MessageManager.h │ ├── MessageManager.m │ ├── SocketManager.h │ └── SocketManager.m │ ├── Messages │ └── messages.h │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── README.md ├── client ├── Makefile ├── client.cpp └── client.h ├── cvt_im_to_bitwise.py ├── handshaker ├── Makefile ├── callbacks.cpp ├── callbacks.h ├── handshaker.cpp └── handshaker.h ├── make_libs.sh ├── messenger ├── Makefile ├── messenger.cpp └── messenger.h ├── notifier ├── Makefile ├── notifier.cpp └── notifier.h ├── parser ├── Makefile ├── parser.cpp └── parser.h ├── picture.jpg ├── server ├── Makefile ├── bonjour_callbacks.cpp ├── bonjour_callbacks.h ├── m2server ├── mach_callbacks.cpp ├── mach_callbacks.h ├── server.cpp ├── sock_callbacks.cpp └── sock_callbacks.h └── testapp ├── Makefile ├── app └── testapp.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | *.ipynb 3 | /helper/ 4 | /hahooks/ 5 | HookCase/ 6 | NIClient/ 7 | NIIPC 8 | *.dSYM 9 | /*.dSYM/ 10 | *.txt 11 | caps/ 12 | client/client 13 | client/m2client 14 | client/*.bin 15 | *.o 16 | jup/ 17 | *.i64 18 | nidriver 19 | niha 20 | *.id* 21 | *.nam 22 | *.til 23 | run.sh 24 | run_daemon.sh 25 | ha/ 26 | driver/ 27 | app/ 28 | notebooks/ 29 | ipchook/*.dylib 30 | *.dylib 31 | *.bin 32 | *.png 33 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B7A2F377230C56C50098629A /* SocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B7A2F376230C56C50098629A /* SocketManager.m */; }; 11 | B7A2F37D230E293E0098629A /* MessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B7A2F37C230E293E0098629A /* MessageManager.m */; }; 12 | B7DD264C2308EB4D00C3E8B6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B7DD264B2308EB4D00C3E8B6 /* AppDelegate.m */; }; 13 | B7DD264F2308EB4D00C3E8B6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B7DD264E2308EB4D00C3E8B6 /* ViewController.m */; }; 14 | B7DD26522308EB4D00C3E8B6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B7DD26502308EB4D00C3E8B6 /* Main.storyboard */; }; 15 | B7DD26542308EB4E00C3E8B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B7DD26532308EB4E00C3E8B6 /* Assets.xcassets */; }; 16 | B7DD26572308EB4E00C3E8B6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B7DD26552308EB4E00C3E8B6 /* LaunchScreen.storyboard */; }; 17 | B7DD265A2308EB4E00C3E8B6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B7DD26592308EB4E00C3E8B6 /* main.m */; }; 18 | B7DD26612308EB8800C3E8B6 /* BonjourManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B7DD26602308EB8800C3E8B6 /* BonjourManager.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | B7A2F375230C56970098629A /* SocketManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocketManager.h; sourceTree = ""; }; 23 | B7A2F376230C56C50098629A /* SocketManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SocketManager.m; sourceTree = ""; }; 24 | B7A2F37A230E287F0098629A /* messages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = messages.h; sourceTree = ""; }; 25 | B7A2F37B230E29360098629A /* MessageManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageManager.h; sourceTree = ""; }; 26 | B7A2F37C230E293E0098629A /* MessageManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessageManager.m; sourceTree = ""; }; 27 | B7DD26472308EB4D00C3E8B6 /* Maschine 2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Maschine 2.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | B7DD264A2308EB4D00C3E8B6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | B7DD264B2308EB4D00C3E8B6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | B7DD264D2308EB4D00C3E8B6 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | B7DD264E2308EB4D00C3E8B6 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | B7DD26512308EB4D00C3E8B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | B7DD26532308EB4E00C3E8B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | B7DD26562308EB4E00C3E8B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | B7DD26582308EB4E00C3E8B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | B7DD26592308EB4E00C3E8B6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | B7DD26602308EB8800C3E8B6 /* BonjourManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BonjourManager.m; sourceTree = ""; }; 38 | B7DD26622308EB9300C3E8B6 /* BonjourManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BonjourManager.h; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | B7DD26442308EB4D00C3E8B6 /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | B7A2F378230E247F0098629A /* Managers */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | B7DD26622308EB9300C3E8B6 /* BonjourManager.h */, 56 | B7DD26602308EB8800C3E8B6 /* BonjourManager.m */, 57 | B7A2F375230C56970098629A /* SocketManager.h */, 58 | B7A2F376230C56C50098629A /* SocketManager.m */, 59 | B7A2F37B230E29360098629A /* MessageManager.h */, 60 | B7A2F37C230E293E0098629A /* MessageManager.m */, 61 | ); 62 | path = Managers; 63 | sourceTree = ""; 64 | }; 65 | B7A2F379230E285F0098629A /* Messages */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | B7A2F37A230E287F0098629A /* messages.h */, 69 | ); 70 | path = Messages; 71 | sourceTree = ""; 72 | }; 73 | B7DD263E2308EB4D00C3E8B6 = { 74 | isa = PBXGroup; 75 | children = ( 76 | B7DD26492308EB4D00C3E8B6 /* Maschine 2 */, 77 | B7DD26482308EB4D00C3E8B6 /* Products */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | B7DD26482308EB4D00C3E8B6 /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | B7DD26472308EB4D00C3E8B6 /* Maschine 2.app */, 85 | ); 86 | name = Products; 87 | sourceTree = ""; 88 | }; 89 | B7DD26492308EB4D00C3E8B6 /* Maschine 2 */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | B7A2F379230E285F0098629A /* Messages */, 93 | B7A2F378230E247F0098629A /* Managers */, 94 | B7DD264A2308EB4D00C3E8B6 /* AppDelegate.h */, 95 | B7DD264B2308EB4D00C3E8B6 /* AppDelegate.m */, 96 | B7DD264D2308EB4D00C3E8B6 /* ViewController.h */, 97 | B7DD264E2308EB4D00C3E8B6 /* ViewController.m */, 98 | B7DD26502308EB4D00C3E8B6 /* Main.storyboard */, 99 | B7DD26532308EB4E00C3E8B6 /* Assets.xcassets */, 100 | B7DD26552308EB4E00C3E8B6 /* LaunchScreen.storyboard */, 101 | B7DD26582308EB4E00C3E8B6 /* Info.plist */, 102 | B7DD26592308EB4E00C3E8B6 /* main.m */, 103 | ); 104 | path = "Maschine 2"; 105 | sourceTree = ""; 106 | }; 107 | /* End PBXGroup section */ 108 | 109 | /* Begin PBXNativeTarget section */ 110 | B7DD26462308EB4D00C3E8B6 /* Maschine 2 */ = { 111 | isa = PBXNativeTarget; 112 | buildConfigurationList = B7DD265D2308EB4E00C3E8B6 /* Build configuration list for PBXNativeTarget "Maschine 2" */; 113 | buildPhases = ( 114 | B7DD26432308EB4D00C3E8B6 /* Sources */, 115 | B7DD26442308EB4D00C3E8B6 /* Frameworks */, 116 | B7DD26452308EB4D00C3E8B6 /* Resources */, 117 | ); 118 | buildRules = ( 119 | ); 120 | dependencies = ( 121 | ); 122 | name = "Maschine 2"; 123 | productName = "Maschine 2"; 124 | productReference = B7DD26472308EB4D00C3E8B6 /* Maschine 2.app */; 125 | productType = "com.apple.product-type.application"; 126 | }; 127 | /* End PBXNativeTarget section */ 128 | 129 | /* Begin PBXProject section */ 130 | B7DD263F2308EB4D00C3E8B6 /* Project object */ = { 131 | isa = PBXProject; 132 | attributes = { 133 | LastUpgradeCheck = 1010; 134 | ORGANIZATIONNAME = "Sam Lerner"; 135 | TargetAttributes = { 136 | B7DD26462308EB4D00C3E8B6 = { 137 | CreatedOnToolsVersion = 10.1; 138 | }; 139 | }; 140 | }; 141 | buildConfigurationList = B7DD26422308EB4D00C3E8B6 /* Build configuration list for PBXProject "Maschine 2" */; 142 | compatibilityVersion = "Xcode 9.3"; 143 | developmentRegion = en; 144 | hasScannedForEncodings = 0; 145 | knownRegions = ( 146 | en, 147 | Base, 148 | ); 149 | mainGroup = B7DD263E2308EB4D00C3E8B6; 150 | productRefGroup = B7DD26482308EB4D00C3E8B6 /* Products */; 151 | projectDirPath = ""; 152 | projectRoot = ""; 153 | targets = ( 154 | B7DD26462308EB4D00C3E8B6 /* Maschine 2 */, 155 | ); 156 | }; 157 | /* End PBXProject section */ 158 | 159 | /* Begin PBXResourcesBuildPhase section */ 160 | B7DD26452308EB4D00C3E8B6 /* Resources */ = { 161 | isa = PBXResourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | B7DD26572308EB4E00C3E8B6 /* LaunchScreen.storyboard in Resources */, 165 | B7DD26542308EB4E00C3E8B6 /* Assets.xcassets in Resources */, 166 | B7DD26522308EB4D00C3E8B6 /* Main.storyboard in Resources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXResourcesBuildPhase section */ 171 | 172 | /* Begin PBXSourcesBuildPhase section */ 173 | B7DD26432308EB4D00C3E8B6 /* Sources */ = { 174 | isa = PBXSourcesBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | B7DD264F2308EB4D00C3E8B6 /* ViewController.m in Sources */, 178 | B7A2F37D230E293E0098629A /* MessageManager.m in Sources */, 179 | B7DD26612308EB8800C3E8B6 /* BonjourManager.m in Sources */, 180 | B7DD265A2308EB4E00C3E8B6 /* main.m in Sources */, 181 | B7A2F377230C56C50098629A /* SocketManager.m in Sources */, 182 | B7DD264C2308EB4D00C3E8B6 /* AppDelegate.m in Sources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXSourcesBuildPhase section */ 187 | 188 | /* Begin PBXVariantGroup section */ 189 | B7DD26502308EB4D00C3E8B6 /* Main.storyboard */ = { 190 | isa = PBXVariantGroup; 191 | children = ( 192 | B7DD26512308EB4D00C3E8B6 /* Base */, 193 | ); 194 | name = Main.storyboard; 195 | sourceTree = ""; 196 | }; 197 | B7DD26552308EB4E00C3E8B6 /* LaunchScreen.storyboard */ = { 198 | isa = PBXVariantGroup; 199 | children = ( 200 | B7DD26562308EB4E00C3E8B6 /* Base */, 201 | ); 202 | name = LaunchScreen.storyboard; 203 | sourceTree = ""; 204 | }; 205 | /* End PBXVariantGroup section */ 206 | 207 | /* Begin XCBuildConfiguration section */ 208 | B7DD265B2308EB4E00C3E8B6 /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | ALWAYS_SEARCH_USER_PATHS = NO; 212 | CLANG_ANALYZER_NONNULL = YES; 213 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 214 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 215 | CLANG_CXX_LIBRARY = "libc++"; 216 | CLANG_ENABLE_MODULES = YES; 217 | CLANG_ENABLE_OBJC_ARC = YES; 218 | CLANG_ENABLE_OBJC_WEAK = YES; 219 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_COMMA = YES; 222 | CLANG_WARN_CONSTANT_CONVERSION = YES; 223 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 226 | CLANG_WARN_EMPTY_BODY = YES; 227 | CLANG_WARN_ENUM_CONVERSION = YES; 228 | CLANG_WARN_INFINITE_RECURSION = YES; 229 | CLANG_WARN_INT_CONVERSION = YES; 230 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 231 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 232 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 234 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 235 | CLANG_WARN_STRICT_PROTOTYPES = YES; 236 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 237 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 238 | CLANG_WARN_UNREACHABLE_CODE = YES; 239 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 240 | CODE_SIGN_IDENTITY = "iPhone Developer"; 241 | COPY_PHASE_STRIP = NO; 242 | DEBUG_INFORMATION_FORMAT = dwarf; 243 | ENABLE_STRICT_OBJC_MSGSEND = YES; 244 | ENABLE_TESTABILITY = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu11; 246 | GCC_DYNAMIC_NO_PIC = NO; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PREPROCESSOR_DEFINITIONS = ( 250 | "DEBUG=1", 251 | "$(inherited)", 252 | ); 253 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 254 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 255 | GCC_WARN_UNDECLARED_SELECTOR = YES; 256 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 257 | GCC_WARN_UNUSED_FUNCTION = YES; 258 | GCC_WARN_UNUSED_VARIABLE = YES; 259 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 260 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 261 | MTL_FAST_MATH = YES; 262 | ONLY_ACTIVE_ARCH = YES; 263 | SDKROOT = iphoneos; 264 | }; 265 | name = Debug; 266 | }; 267 | B7DD265C2308EB4E00C3E8B6 /* Release */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_ANALYZER_NONNULL = YES; 272 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_ENABLE_OBJC_WEAK = YES; 278 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_COMMA = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 285 | CLANG_WARN_EMPTY_BODY = YES; 286 | CLANG_WARN_ENUM_CONVERSION = YES; 287 | CLANG_WARN_INFINITE_RECURSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 290 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 291 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 294 | CLANG_WARN_STRICT_PROTOTYPES = YES; 295 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 296 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | CODE_SIGN_IDENTITY = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu11; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | MTL_FAST_MATH = YES; 315 | SDKROOT = iphoneos; 316 | VALIDATE_PRODUCT = YES; 317 | }; 318 | name = Release; 319 | }; 320 | B7DD265E2308EB4E00C3E8B6 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | CODE_SIGN_STYLE = Automatic; 325 | DEVELOPMENT_TEAM = YDG22WTMXW; 326 | INFOPLIST_FILE = "Maschine 2/Info.plist"; 327 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 328 | LD_RUNPATH_SEARCH_PATHS = ( 329 | "$(inherited)", 330 | "@executable_path/Frameworks", 331 | ); 332 | PRODUCT_BUNDLE_IDENTIFIER = "LernerApps.Maschine-2"; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | TARGETED_DEVICE_FAMILY = "1,2"; 335 | }; 336 | name = Debug; 337 | }; 338 | B7DD265F2308EB4E00C3E8B6 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 342 | CODE_SIGN_STYLE = Automatic; 343 | DEVELOPMENT_TEAM = YDG22WTMXW; 344 | INFOPLIST_FILE = "Maschine 2/Info.plist"; 345 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 346 | LD_RUNPATH_SEARCH_PATHS = ( 347 | "$(inherited)", 348 | "@executable_path/Frameworks", 349 | ); 350 | PRODUCT_BUNDLE_IDENTIFIER = "LernerApps.Maschine-2"; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | TARGETED_DEVICE_FAMILY = "1,2"; 353 | }; 354 | name = Release; 355 | }; 356 | /* End XCBuildConfiguration section */ 357 | 358 | /* Begin XCConfigurationList section */ 359 | B7DD26422308EB4D00C3E8B6 /* Build configuration list for PBXProject "Maschine 2" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | B7DD265B2308EB4E00C3E8B6 /* Debug */, 363 | B7DD265C2308EB4E00C3E8B6 /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | B7DD265D2308EB4E00C3E8B6 /* Build configuration list for PBXNativeTarget "Maschine 2" */ = { 369 | isa = XCConfigurationList; 370 | buildConfigurations = ( 371 | B7DD265E2308EB4E00C3E8B6 /* Debug */, 372 | B7DD265F2308EB4E00C3E8B6 /* Release */, 373 | ); 374 | defaultConfigurationIsVisible = 0; 375 | defaultConfigurationName = Release; 376 | }; 377 | /* End XCConfigurationList section */ 378 | }; 379 | rootObject = B7DD263F2308EB4D00C3E8B6 /* Project object */; 380 | } 381 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2.xcodeproj/project.xcworkspace/xcuserdata/samlerner.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamL98/NIProtocol/fc63ab45a965e7299a38e5299ca652827390e326/Maschine 2/Maschine 2.xcodeproj/project.xcworkspace/xcuserdata/samlerner.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Maschine 2/Maschine 2.xcodeproj/xcuserdata/samlerner.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Maschine 2.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 34 | 41 | 48 | 58 | 65 | 72 | 82 | 89 | 96 | 106 | 113 | 120 | 130 | 137 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/BonjourManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // BonjourManager.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #ifndef BonjourManager_h 10 | #define BonjourManager_h 11 | 12 | #import 13 | 14 | @interface BonjourManager : NSObject 15 | 16 | @property NSString *domain; 17 | @property NSString *type; 18 | @property CFNetServiceClientCallBack clientCallback; 19 | @property void *callerPtr; 20 | 21 | -(id)init:(NSString*)domain withType:(NSString*)type withCallback:(CFNetServiceClientCallBack)callback withCaller:(void *)callerPtr; 22 | -(int)connect; 23 | 24 | @end 25 | 26 | #endif /* BonjourManager_h */ 27 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/BonjourManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // BonjourManager.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #import "BonjourManager.h" 17 | 18 | @implementation BonjourManager 19 | 20 | void 21 | resolve_callback(CFNetServiceRef service, 22 | CFStreamError *error, 23 | void *info) 24 | { 25 | BonjourManager *bonjman; 26 | 27 | if (!info) { 28 | printf("No info in resolve callback\n"); 29 | return; 30 | } 31 | 32 | bonjman = (__bridge BonjourManager *)info; 33 | 34 | if ([bonjman clientCallback]) 35 | [bonjman clientCallback](service, error, [bonjman callerPtr]); 36 | } 37 | 38 | void 39 | browse_callback(CFNetServiceBrowserRef browser, 40 | CFOptionFlags flags, 41 | CFTypeRef domainOrService, 42 | CFStreamError *error, 43 | void *info) 44 | { 45 | CFNetServiceClientContext ctx; 46 | CFNetServiceRef service; 47 | 48 | if (!info) { 49 | printf("No info in resolve callback\n"); 50 | return; 51 | } 52 | 53 | ctx.version = 0; 54 | ctx.info = info; 55 | ctx.retain = NULL; 56 | ctx.release = NULL; 57 | ctx.copyDescription = NULL; 58 | 59 | service = (CFNetServiceRef)domainOrService; 60 | 61 | CFNetServiceSetClient(service, 62 | resolve_callback, 63 | &ctx); 64 | 65 | CFNetServiceScheduleWithRunLoop(service, 66 | CFRunLoopGetCurrent(), 67 | kCFRunLoopDefaultMode); 68 | 69 | if (!CFNetServiceResolveWithTimeout(service, 0, error)) { 70 | printf("Couldn't resolve service\n"); 71 | CFNetServiceUnscheduleFromRunLoop(service, 72 | CFRunLoopGetCurrent(), 73 | kCFRunLoopDefaultMode); 74 | CFNetServiceSetClient(service, 0, 0); 75 | } 76 | } 77 | 78 | -(id)init:(NSString *)domain withType:(NSString *)type withCallback:(CFNetServiceClientCallBack)callback withCaller:(void *)callerPtr 79 | { 80 | self = [super init]; 81 | [self setDomain:domain]; 82 | [self setType:type]; 83 | [self setClientCallback:callback]; 84 | [self setCallerPtr:callerPtr]; 85 | return self; 86 | } 87 | 88 | -(int)connect 89 | { 90 | CFNetServiceClientContext ctx; 91 | CFStreamError error; 92 | CFNetServiceBrowserRef browser; 93 | 94 | // Create a context structure with ourself as the info 95 | ctx.version = 0; 96 | ctx.info = (__bridge void *)self; 97 | ctx.retain = NULL; 98 | ctx.release = NULL; 99 | ctx.copyDescription = NULL; 100 | 101 | browser = CFNetServiceBrowserCreate(kCFAllocatorDefault, 102 | (CFNetServiceBrowserClientCallBack)browse_callback, 103 | &ctx); 104 | if (!browser) { 105 | printf("Couldn't create net service browser\n"); 106 | return 1; 107 | } 108 | 109 | CFNetServiceBrowserScheduleWithRunLoop(browser, 110 | CFRunLoopGetCurrent(), 111 | kCFRunLoopDefaultMode); 112 | 113 | if (!CFNetServiceBrowserSearchForServices(browser, 114 | (__bridge CFStringRef)[self domain], 115 | (__bridge CFStringRef)[self type], 116 | &error)) { 117 | printf("Couldn't search for services\n"); 118 | CFNetServiceBrowserUnscheduleFromRunLoop(browser, 119 | CFRunLoopGetCurrent(), 120 | kCFRunLoopDefaultMode); 121 | CFRelease(browser); 122 | } 123 | 124 | CFRunLoopRun(); 125 | CFRelease(browser); 126 | 127 | return 0; 128 | } 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/MessageManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // MessageManager.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/21/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #ifndef MessageManager_h 10 | #define MessageManager_h 11 | 12 | #include "SocketManager.h" 13 | 14 | @interface MessageManager : NSObject 15 | 16 | @property SocketManager *sockman; 17 | 18 | -(id)init:(SocketManager *)sockman; 19 | -(int)sendPad:(uint)num withPressure:(uint)pressure; 20 | 21 | @end 22 | 23 | #endif /* MessageManager_h */ 24 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/MessageManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // MessageManager.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/21/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MessageManager.h" 11 | #import "messages.h" 12 | 13 | @implementation MessageManager 14 | 15 | -(id)init:(SocketManager *)sockman 16 | { 17 | self = [super init]; 18 | [self setSockman:sockman]; 19 | return self; 20 | } 21 | 22 | -(int)sendPad:(uint)btnNum withPressure:(uint)pressure 23 | { 24 | pad_msg msg; 25 | uint32_t r, c, btnCode; 26 | 27 | btnNum -= 1; 28 | r = 3 - btnNum / 4; 29 | c = btnNum % 4; 30 | btnCode = r * 4 + c; 31 | 32 | msg.ctrl = kPadCtrl; 33 | msg.counter = 0; 34 | msg.msg_unk = 0; 35 | msg.nmsgs = 1; 36 | msg.btn = btnCode; 37 | msg.btn_unk = 0; 38 | msg.pressure = pressure; 39 | 40 | return [[self sockman] sendMsg:(void *)&msg withLength:sizeof(msg)]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/SocketManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketManager.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/20/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #ifndef SocketManager_h 10 | #define SocketManager_h 11 | 12 | @interface SocketManager : NSObject 13 | 14 | @property BOOL connected; 15 | @property int sockfd; 16 | 17 | -(int)connect:(CFNetServiceRef)service; 18 | -(int)sendMsg:(void *)msg withLength:(size_t)len; 19 | 20 | @end 21 | 22 | #endif /* SocketManager_h */ 23 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Managers/SocketManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketManager.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/20/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #import "SocketManager.h" 16 | 17 | @implementation SocketManager 18 | 19 | -(id)init 20 | { 21 | self = [super init]; 22 | [self setConnected:NO]; 23 | return self; 24 | } 25 | 26 | -(int)connect:(CFNetServiceRef)service 27 | { 28 | CFArrayRef addresses; 29 | CFDataRef addrData; 30 | struct sockaddr *sa; 31 | int sockaddrlen; 32 | int sockfd; 33 | 34 | if (!(addresses= CFNetServiceGetAddressing(service))) { 35 | printf("Couldn't get the addresses found by the net service\n"); 36 | return 1; 37 | } 38 | 39 | if (CFArrayGetCount(addresses) < 1) { 40 | printf("No addresses found for the net service\n"); 41 | return 1; 42 | } 43 | 44 | addrData = (CFDataRef)CFArrayGetValueAtIndex(addresses, 0); 45 | sa = (struct sockaddr *)CFDataGetBytePtr(addrData); 46 | 47 | if (sa->sa_family == AF_INET) 48 | sockaddrlen = sizeof(struct sockaddr_in); 49 | else if (sa->sa_family == AF_INET6) 50 | sockaddrlen = sizeof(struct sockaddr_in6); 51 | else { 52 | printf("Unknown sockaddr family: %d\n", sa->sa_family); 53 | return 1; 54 | } 55 | 56 | if ((sockfd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0) { 57 | printf("Couldn't create socket to connect to net service\n"); 58 | return 1; 59 | } 60 | 61 | if (connect(sockfd, sa, sockaddrlen) < 0) { 62 | printf("Couldn't connect to network service\n"); 63 | return 1; 64 | } 65 | 66 | [self setSockfd:sockfd]; 67 | [self setConnected:YES]; 68 | 69 | return 0; 70 | } 71 | 72 | -(int)sendMsg:(void *)msg withLength:(size_t)len 73 | { 74 | size_t bytesSent; 75 | 76 | if (![self connected]) { 77 | printf("Cannot send a message to an unconnected socket\n"); 78 | return 1; 79 | } 80 | 81 | if ((bytesSent = send([self sockfd], msg, len, 0)) < 0) { 82 | printf("Error occurred while sending on socket\n"); 83 | return 1; 84 | } 85 | 86 | if (bytesSent < len) 87 | return [self sendMsg:msg+bytesSent withLength:len-bytesSent]; 88 | 89 | return 0; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/Messages/messages.h: -------------------------------------------------------------------------------- 1 | // 2 | // messages.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/21/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #ifndef messages_h 10 | #define messages_h 11 | 12 | #define kPadCtrl 0x3504e00 13 | 14 | typedef struct { 15 | uint32_t ctrl; 16 | uint32_t counter; 17 | uint32_t msg_unk; 18 | uint32_t nmsgs; 19 | uint32_t btn; 20 | uint32_t btn_unk; 21 | uint32_t pressure; 22 | } pad_msg; 23 | 24 | #endif /* messages_h */ 25 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MessageManager.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet UIButton *pad1; 15 | @property (weak, nonatomic) IBOutlet UIButton *pad2; 16 | @property (weak, nonatomic) IBOutlet UIButton *pad3; 17 | @property (weak, nonatomic) IBOutlet UIButton *pad4; 18 | @property (weak, nonatomic) IBOutlet UIButton *pad5; 19 | @property (weak, nonatomic) IBOutlet UIButton *pad6; 20 | @property (weak, nonatomic) IBOutlet UIButton *pad7; 21 | @property (weak, nonatomic) IBOutlet UIButton *pad8; 22 | @property (weak, nonatomic) IBOutlet UIButton *pad9; 23 | @property (weak, nonatomic) IBOutlet UIButton *pad10; 24 | @property (weak, nonatomic) IBOutlet UIButton *pad11; 25 | @property (weak, nonatomic) IBOutlet UIButton *pad12; 26 | @property (weak, nonatomic) IBOutlet UIButton *pad13; 27 | @property (weak, nonatomic) IBOutlet UIButton *pad14; 28 | @property (weak, nonatomic) IBOutlet UIButton *pad15; 29 | 30 | @property MessageManager *messman; 31 | 32 | - (void)finishConnectionSetup:(CFNetServiceRef)service; 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "BonjourManager.h" 11 | #import "SocketManager.h" 12 | 13 | void 14 | connect_callback(CFNetServiceRef service, 15 | CFStreamError *error, 16 | void *info) 17 | { 18 | if (!info) { 19 | printf("Null info in connect_callback\n"); 20 | return; 21 | } 22 | 23 | [(__bridge ViewController *)info finishConnectionSetup:service]; 24 | } 25 | 26 | @interface ViewController () 27 | 28 | @end 29 | 30 | @implementation ViewController 31 | 32 | - (void)enablePads:(BOOL)enabled 33 | { 34 | size_t i; 35 | char selName[6]; 36 | SEL padSel; 37 | UIButton *pad; 38 | 39 | for (i=1; i<16; i++) { 40 | sprintf(selName, "pad%lu", i); 41 | 42 | if (i<10) selName[4] = 0; 43 | else selName[5] = 0; 44 | 45 | padSel = NSSelectorFromString([NSString stringWithCString:selName encoding:NSASCIIStringEncoding]); 46 | 47 | pad = (UIButton *)[self performSelector:padSel]; 48 | [pad setEnabled:enabled]; 49 | } 50 | } 51 | 52 | - (void)finishConnectionSetup:(CFNetServiceRef)service 53 | { 54 | SocketManager *sockman = [[SocketManager alloc] init]; 55 | 56 | if ([sockman connect:service]) { 57 | printf("Socket manager couldn't connect to net service\n"); 58 | return; 59 | } 60 | 61 | printf("Socket manager successfully connected to net service\n"); 62 | 63 | [self setMessman:[[MessageManager alloc] init:sockman]]; 64 | 65 | dispatch_async(dispatch_get_main_queue(), ^{ 66 | [self enablePads:YES]; 67 | }); 68 | } 69 | 70 | - (void)viewDidLoad { 71 | [super viewDidLoad]; 72 | 73 | [self enablePads:NO]; 74 | 75 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ 76 | [[[BonjourManager alloc] init:@"local." 77 | withType:@"_freehand._tcp" 78 | withCallback:connect_callback 79 | withCaller:(__bridge void *)self] connect]; 80 | }); 81 | } 82 | 83 | - (IBAction)padTap:(id)sender 84 | { 85 | NSInteger tag; 86 | uint padNum; 87 | 88 | if (!(tag = [sender tag])) { 89 | printf("Sender has no tag\n"); 90 | return; 91 | } 92 | 93 | padNum = (uint)tag; 94 | printf("Pad %u tapped\n", padNum); 95 | 96 | [[self messman] sendPad:padNum withPressure:0xffffffff]; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Maschine 2/Maschine 2/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Maschine 2 4 | // 5 | // Created by Sam Lerner on 8/17/19. 6 | // Copyright © 2019 Sam Lerner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NIProtocol 2 | 3 | This repository contains the code necessary to do two things: 4 | 5 | 1. Read button/wheel input from the Native Instruments Maschine Mikro 2. 6 | 2. Send button input from an iPhone to the Native Instruments Maschine 2 application. 7 | 8 | For more information on the project, see the linked blog post: [https://medium.com/@lerner98/rage-against-the-maschine-3357be1abc48] 9 | 10 | NOTE: All of the code is written for MacOS. Significant changes will be needed for this code to work on other operating systems. 11 | 12 | ## Reading the MK2 13 | 14 | The Maschine Mikro 2 (MK2) interfaces with your pc through the Native Instrument drivers. The drivers then forward the data to the NIHardwareAgent (at least on MacOS) which in turns forwards it to the Maschine 2 GUI. 15 | 16 | The `m2client` program, which can be built by running `make` while inside the `client` directory, will act as the Maschine 2 GUI and interact with the NIHardwareAgent. 17 | 18 | When `m2client` is run, it will perform the handshake with the hardware agent and create a `CFMessagePortRef` with name "SIHWMainHandler" for listening applications to send messages to the MK2 through. 19 | 20 | ### Performing the handshake 21 | 22 | To perform the handshake (and build the client), multiple modules first need to be built: 23 | 24 | 1. `niparser`: This module will parse the wheel or button packets from the hardware agent into structs. 25 | 2. `nimessenger`: This module sends the messages necessary for performing the handshake and interacting with the MK2 (such as setting button LED's) to the hardware agent. 26 | 3. `ninotifier`: This module implements the functions necessary for program to register as a listener for incoming packets and for a program to broadcast a packet to any listening program. 27 | 4. `nihandshaker`: This module contains the functionality to perform the handshake from the client side with the hardware agent. The previous three modules need to be built in order for this module to be built. 28 | 29 | These can all be built using the `make_libs.sh` script or each can be built individually with the Makefile in its respective directory. 30 | 31 | Note that when each library is built, its header and compiled dylib will be copied into the `usr/local/include` and `usr/local/lib` directories respectively so that they can be included through system paths. 32 | 33 | After `m2client` performs the handshake, when a new packet is forwarded from the hardware agent, it will then be parsed into a structure and broadcast via a `CFNotification` to any listening program. 34 | 35 | ### Listening to the notification 36 | 37 | Any program can use the `ninotifier` module to listen to the broadcasted packets. There is an example application in the `testapp` directory that registers itself as a listener and prints the pressed button or wheel action when a new packet is received. 38 | 39 | ## Writing to Maschine 2 40 | 41 | The second part of this project is to act as the hardware agent and send packets to the Maschine 2 GUI. The `m2server` program (located in the `server` directory) will perform that task. The server program relies on the same four modules that the client is dependent on. 42 | 43 | `m2server` will also set up a TCP socket on port 6969 to listen for button commands from the `Maschine 2` iPhone application. It will then register a Bonjour service with name `M2` on this port. The `Maschine 2` application, when run, will connect to the published Bonjour service and connect to port 6969. 44 | 45 | Currently, pressure sensitivity is not available in the `Maschine 2` app since my phone does not support 3D Touch. 46 | -------------------------------------------------------------------------------- /client/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | PWD=/Users/samlerner/Projects/NIProtocol/client 3 | 4 | client: 5 | $(CC) -o m2client client.cpp \ 6 | -framework CoreFoundation \ 7 | -lnimessenger -lninotifier -lnihandshaker 8 | 9 | clean: 10 | rm m2client 11 | -------------------------------------------------------------------------------- /client/client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void 6 | setupMK2(CFMessagePortRef reqPort) 7 | { 8 | button_data_t button_data; 9 | screen_data_t screen_data; 10 | size_t i; 11 | FILE *fp; 12 | 13 | initButtonData(&button_data); 14 | setPadColor(&button_data, 1, (char)255, (char)0, (char)0); 15 | 16 | initScreenData(&screen_data); 17 | readScreenDataFromFile(&screen_data, "initials_bitwise.bin"); 18 | 19 | sendButtonDataMsg(reqPort, button_data); 20 | sendScreenDataMsg(reqPort, screen_data); 21 | } 22 | 23 | int main(int argc, const char * argv[]) { 24 | CFMessagePortRef reqPort, 25 | notifPort; 26 | 27 | if (!(reqPort = doHandshake(NULL))) { 28 | printf("Couldn't obtain request port from handshake\n"); 29 | return 1; 30 | } 31 | 32 | printf("Finished handshake\n"); 33 | setupMK2(reqPort); 34 | printf("Performed initial MK2 setup\n"); 35 | 36 | if (!(notifPort = createNotificationPort(kSLBootstrapPortName, 37 | (CFMessagePortCallBack)bootstrap_notif_port_callback, 38 | (void *)reqPort))) { 39 | printf("Couldn't create SL bootstrap notification port\n"); 40 | return 1; 41 | } 42 | 43 | printf("Created %s bootstrap port\n", kSLBootstrapPortName); 44 | CFMessagePortSetDispatchQueue(notifPort, 45 | dispatch_get_main_queue()); 46 | 47 | CFRunLoopRun(); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /client/client.h: -------------------------------------------------------------------------------- 1 | void doHandshake(); -------------------------------------------------------------------------------- /cvt_im_to_bitwise.py: -------------------------------------------------------------------------------- 1 | from skimage.io import imread, imsave 2 | from skimage.color import rgb2gray 3 | import numpy as np 4 | import sys 5 | from os.path import join 6 | 7 | fname_w_ext = sys.argv[1] 8 | fname, ext = tuple(fname_w_ext.split('.')) 9 | im = imread(fname_w_ext) 10 | 11 | if im.dtype != np.uint8: 12 | im = (255*im).astype(np.uint8) 13 | 14 | if len(im.shape) == 3 and im.shape[2] == 3: 15 | im = (255*rgb2gray(im)).astype(np.uint8) 16 | 17 | im[im > 0] = 255 18 | 19 | imsave(fname_w_ext, im) 20 | 21 | #assert im.shape == (48, 128), im.dtype == np.uint8 22 | assert im.shape == (64, 128), im.dtype == np.uint8 23 | 24 | im = im // 255 25 | imb = np.zeros((8, 128), dtype=np.uint8) 26 | #shifts = (np.arange(6) ** 2).reshape(6, 1) 27 | shifts = (2 ** np.arange(8)).reshape(8, 1) 28 | 29 | for i in range(8): 30 | #band = im[i*6:(i+1)*6] 31 | band = im[i*8:(i+1)*8] 32 | imb[i] = (band * shifts).sum(0) 33 | 34 | with open(join('client', '%s_bitwise.bin' % fname), 'wb') as f: 35 | f.write(imb.tobytes()) 36 | -------------------------------------------------------------------------------- /handshaker/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | PWD=/Users/samlerner/Projects/NIProtocol/handshaker 3 | 4 | handshaker.dylib: handshaker.o callbacks.o 5 | $(CC) -o handshaker.dylib handshaker.o callbacks.o \ 6 | -I$(PWD) -L$(PWD) \ 7 | -dynamiclib -framework CoreFoundation \ 8 | -lniparser -lnimessenger -lninotifier \ 9 | -install_name libnihandshaker.dylib 10 | 11 | cp $(PWD)/handshaker.dylib /usr/local/lib/libnihandshaker.dylib 12 | cp $(PWD)/handshaker.h /usr/local/include/nihandshaker.h 13 | 14 | handshaker.o: 15 | $(CC) -o handshaker.o -c handshaker.cpp -I$(PWD) 16 | 17 | callbacks.o: 18 | $(CC) -o callbacks.o -c callbacks.cpp 19 | 20 | clean: 21 | rm handshaker.o callbacks.o handshaker.dylib -------------------------------------------------------------------------------- /handshaker/callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define kSerialNumberPacketDataLen 25 5 | 6 | CFMessagePortRef 7 | createNotificationPort(const char *name, 8 | CFMessagePortCallBack callout, 9 | void *info) 10 | { 11 | Boolean shouldFreeInfo; 12 | CFStringRef cfName; 13 | CFMessagePortRef port; 14 | CFMessagePortContext ctx; 15 | 16 | cfName = CFStringCreateWithCString(kCFAllocatorDefault, 17 | name, 18 | kCFStringEncodingASCII); 19 | 20 | ctx.version = 0; 21 | ctx.info = info; 22 | ctx.retain = NULL;//CFRetain; 23 | ctx.release = NULL;//CFRelease; 24 | ctx.copyDescription = NULL; 25 | 26 | port = CFMessagePortCreateLocal(kCFAllocatorDefault, 27 | cfName, 28 | callout, 29 | &ctx, 30 | &shouldFreeInfo); 31 | 32 | if (shouldFreeInfo) 33 | free(info); 34 | 35 | return port; 36 | } 37 | 38 | CFDataRef 39 | bootstrap_notif_port_callback(CFMessagePortRef local, 40 | SInt32 msgid, 41 | CFDataRef data, 42 | void *info) 43 | { 44 | CFMessagePortRef reqPort; 45 | 46 | if (!data || !CFDataGetBytePtr(data)) { 47 | printf("No data from hardware agent\n"); 48 | return NULL; 49 | } 50 | 51 | if (!info) { 52 | printf("No info in bootstrap notif port callback\n"); 53 | return NULL; 54 | } 55 | 56 | reqPort = (CFMessagePortRef)info; 57 | sendMsg(reqPort, 58 | (uint8_t *)CFDataGetBytePtr(data), 59 | (size_t)CFDataGetLength(data)); 60 | 61 | return NULL; 62 | } 63 | 64 | CFDataRef 65 | mikro_notif_port_callback(CFMessagePortRef local, 66 | SInt32 msgid, 67 | CFDataRef data, 68 | void *info) 69 | { 70 | if (!data || !CFDataGetBytePtr(data)) { 71 | printf("No data from hardware agent\n"); 72 | return NULL; 73 | } 74 | 75 | char portName[kMikroNotificationPortNameLen]; 76 | CFStringGetCString(CFMessagePortGetName(local), 77 | portName, 78 | kMikroNotificationPortNameLen, 79 | kCFStringEncodingASCII); 80 | 81 | //printf("Received %ld bytes of data on %s\n", (long)CFDataGetLength(data), portName); 82 | 83 | if (CFDataGetLength(data) >= 24) 84 | broadcast(data); 85 | 86 | return NULL; 87 | } 88 | 89 | CFDataRef 90 | agent_notif_port_callback(CFMessagePortRef local, 91 | SInt32 msgid, 92 | CFDataRef data, 93 | void *info) 94 | { 95 | serial_num_msg_t serial_num_msg; 96 | CFStringRef cfAgentNotifPortName; 97 | Boolean shouldFreeInfo; 98 | char *agentNotifPortName; 99 | 100 | if (!data || CFDataGetLength(data) != kSerialNumberPacketDataLen) 101 | { 102 | printf("Invalid data from hardware agent\n"); 103 | return NULL; 104 | } 105 | 106 | cfAgentNotifPortName = CFMessagePortGetName(local); 107 | if (!cfAgentNotifPortName) 108 | { 109 | printf("Couldn't get port name for agent notification port\n"); 110 | return NULL; 111 | } 112 | 113 | gReceivedSerial = 1; 114 | agentNotifPortName = (char *)CFStringGetCStringPtr(cfAgentNotifPortName, 115 | kCFStringEncodingASCII); 116 | 117 | // Interpret the data as a serial message packet 118 | serial_num_msg = *(serial_num_msg_t *)CFDataGetBytePtr(data); 119 | strncpy(gSerialNum, serial_num_msg.num, kSerialNumberLen); 120 | 121 | return NULL; 122 | } -------------------------------------------------------------------------------- /handshaker/callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | CFMessagePortRef createNotificationPort(const char *name, CFMessagePortCallBack callout, void *info); 4 | CFDataRef bootstrap_notif_port_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); 5 | CFDataRef mikro_notif_port_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); 6 | CFDataRef agent_notif_port_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); -------------------------------------------------------------------------------- /handshaker/handshaker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "callbacks.h" 3 | #include "handshaker.h" 4 | 5 | #define kAgentNotificationPortNameFormat "NIHWS%04x%04dNotification" 6 | #define kMikroRequestPortNameFormat "NIHWMaschineMikroMK2-%s%04dRequest" 7 | #define kAgentRequestPortNameLen 21 8 | #define kInitMsgUid 40 9 | #define kNumHandshakeIter 8 10 | #define kNumRepRepNonce 4 11 | 12 | uint32_t gNonces[4] = {55797590, 54818048, 54543104, 54817091}; 13 | uint32_t gSerialNonce = 54806784; 14 | uint32_t gSpecialEndNonce = 54749011; 15 | uint32_t gStartNonce = 54739712; 16 | uint16_t gPortUids[kNumHandshakeIter] = {0x1300, 0x1140, 0x808, 0x1200, 0x1110, 0x1350, 0x1500, 0x1200}; 17 | 18 | char gStopAfterSerial = 0; 19 | 20 | CFMessagePortRef 21 | waitForRequestPort(char *name) 22 | { 23 | // Try 10 times to obtain a reference to hardware agent's request port. 24 | // Wait 1 second between tries. Usually don't need all 10 tries. 25 | 26 | CFStringRef cfName; 27 | CFMessagePortRef port = NULL; 28 | size_t tries = 0; 29 | size_t max_tries = 10; 30 | 31 | cfName = CFStringCreateWithCString(kCFAllocatorDefault, 32 | name, 33 | kCFStringEncodingASCII); 34 | if (!cfName) { 35 | printf("Couldn't create CFStringRef for string %s\n", name); 36 | return NULL; 37 | } 38 | 39 | while (!port && tries++ < max_tries) { 40 | port = CFMessagePortCreateRemote(kCFAllocatorDefault, cfName); 41 | if (!port) sleep(1); 42 | } 43 | 44 | CFRelease(cfName); 45 | return port; 46 | } 47 | 48 | CFMessagePortRef 49 | getRequestPort(uint16_t portUid, uint16_t msgUid, size_t handshakeIter) 50 | { 51 | // If we are on the last handshake iteration, we should've sent the request 52 | // port should be specified by the serial number and msgUid. 53 | // 54 | // Otherwise, it should be specified by the portUid and msgUid. 55 | 56 | char *reqPortNamePtr; 57 | 58 | if (handshakeIter == kNumHandshakeIter-1) { 59 | if (!gReceivedSerial) { 60 | printf("No serial to use while getting reference to message port\n"); 61 | return NULL; 62 | } 63 | 64 | char reqPortName[kMikroRequestPortNameLen]; 65 | sprintf(reqPortName, kMikroRequestPortNameFormat, gSerialNum, msgUid); 66 | reqPortNamePtr = reqPortName; 67 | } 68 | else { 69 | char reqPortName[kAgentRequestPortNameLen]; 70 | sprintf(reqPortName, kAgentRequestPortNameFormat, portUid, msgUid); 71 | reqPortNamePtr = reqPortName; 72 | } 73 | 74 | // Wait for the hardware agent to open the request port corresponding to the port and msg uid's 75 | return waitForRequestPort(reqPortNamePtr); 76 | } 77 | 78 | CFMessagePortRef 79 | setNotificationPort(char *name, uint16_t portUid, uint16_t msgUid, size_t handshakeIter) 80 | { 81 | // Similarly to the notification port, the name of the notification port 82 | // to open depends on whether or not we are on the last iteration of the handshake. 83 | 84 | CFMessagePortCallBack callback; 85 | 86 | if (handshakeIter == kNumHandshakeIter-1) { 87 | if (!gReceivedSerial) { 88 | printf("No serial to use while creating mikro notification port\n"); 89 | return NULL; 90 | } 91 | 92 | char notifPortName[kMikroNotificationPortNameLen]; 93 | sprintf(notifPortName, kMikroNotificationPortNameFormat, gSerialNum, msgUid); 94 | 95 | memcpy(name, notifPortName, kMikroNotificationPortNameLen); 96 | callback = (CFMessagePortCallBack)mikro_notif_port_callback; 97 | } 98 | else { 99 | char notifPortName[kAgentNotificationPortNameLen]; 100 | sprintf(notifPortName, kAgentNotificationPortNameFormat, portUid, msgUid); 101 | 102 | memcpy(name, notifPortName, kAgentNotificationPortNameLen); 103 | callback = (CFMessagePortCallBack)agent_notif_port_callback; 104 | } 105 | 106 | return createNotificationPort((const char*)name, callback, NULL); 107 | } 108 | 109 | void 110 | performHandshakeIteration(uint16_t portUid, 111 | uint16_t msgUid, 112 | size_t handshakeIter) 113 | { 114 | CFMessagePortRef bsPort; 115 | CFMessagePortRef reqPort; 116 | CFMessagePortRef notifPort; 117 | char notifPortName[kMikroNotificationPortNameLen]; 118 | uint32_t finalNonce; 119 | 120 | // Get a reference to the bootstrap port 121 | bsPort = getBootstrapPort(kMainPortName); 122 | if (!bsPort) { 123 | printf("Couldn't get bootstrap port\n"); 124 | return; 125 | } 126 | 127 | // Send the initial nonce and the uid 128 | sendNonceMsg(bsPort, gNonces[0]); 129 | 130 | // On the last handshake iteration, we send back the serial number we previously received 131 | if (handshakeIter == kNumHandshakeIter-1) 132 | { 133 | if (!gReceivedSerial) { 134 | printf("Don't have a valid serial to respond with\n"); 135 | return; 136 | } 137 | sendMKSerialMsg(bsPort, gSerialNonce, portUid); 138 | } 139 | else 140 | sendUidMsg(bsPort, gNonces[1], portUid); 141 | 142 | // I have no clue why they do this but they do 143 | if (handshakeIter == kNumHandshakeIter-3) 144 | goto req_port_fail; 145 | 146 | // Hopefully get a reference to the created request port 147 | reqPort = getRequestPort(portUid, msgUid, handshakeIter); 148 | if (!reqPort) { 149 | printf("Couldn't get request port\n"); 150 | goto req_port_fail; 151 | } 152 | 153 | // Create the notification port corresponding to the current port and msg uid's 154 | notifPort = setNotificationPort(notifPortName, portUid, msgUid, handshakeIter); 155 | if (!notifPort) { 156 | printf("Couldn't create notification port\n"); 157 | goto notif_port_fail; 158 | } 159 | 160 | CFMessagePortSetDispatchQueue(notifPort, 161 | dispatch_get_main_queue()); 162 | 163 | // Let the hardware agent know the name of our notification port and send the final nonce 164 | if (handshakeIter == kNumHandshakeIter-1) { 165 | sendMKNameMsg(reqPort, gNonces[2], notifPortName); 166 | finalNonce = gSpecialEndNonce; 167 | } 168 | else { 169 | sendNameMsg(reqPort, gNonces[2], notifPortName); 170 | finalNonce = gNonces[3]; 171 | } 172 | 173 | sendNonceMsg(reqPort, finalNonce); 174 | 175 | notif_port_fail: 176 | CFRelease(reqPort); 177 | req_port_fail: 178 | CFRelease(bsPort); 179 | } 180 | 181 | CFMessagePortRef 182 | doHandshake(char *serial) 183 | { 184 | CFMessagePortRef reqPort; 185 | uint16_t msgUid = kInitMsgUid; 186 | size_t i; 187 | 188 | for (i=0; i 2 | #include "messenger.h" 3 | 4 | #define kButtonNonce 57439488 5 | #define kScreenNonce 56914756 6 | 7 | char gReceivedSerial = 0; 8 | char gSerialNum[kSerialNumberLen] = "\0"; 9 | 10 | CFMessagePortRef 11 | getBootstrapPort(const char *name) 12 | { 13 | CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, 14 | name, 15 | kCFStringEncodingASCII); 16 | return CFMessagePortCreateRemote(kCFAllocatorDefault, 17 | cfName); 18 | } 19 | 20 | void 21 | sendMsg(CFMessagePortRef port, uint8_t *msg, size_t size) 22 | { 23 | CFDataRef msgData; 24 | 25 | msgData = CFDataCreate(kCFAllocatorDefault, 26 | msg, 27 | size); 28 | 29 | if (!msgData) { 30 | printf("Couldn't create message data\n"); 31 | return; 32 | } 33 | 34 | if (!port || !CFMessagePortIsValid(port)) { 35 | printf("Either null or invalid message port\n"); 36 | return; 37 | } 38 | 39 | CFMessagePortSendRequest(port, 40 | 0, 41 | msgData, 42 | 1000, 43 | 1000, 44 | kCFRunLoopDefaultMode, 45 | NULL); 46 | 47 | CFRelease(msgData); 48 | } 49 | 50 | void 51 | sendNonceMsg(CFMessagePortRef port, uint32_t nonce) 52 | { 53 | nonce_msg_t nonce_msg = {}; 54 | nonce_msg.nonce = nonce; 55 | sendMsg(port, (uint8_t *)&nonce_msg, sizeof(nonce_msg)); 56 | } 57 | 58 | void 59 | sendUidMsg(CFMessagePortRef port, uint32_t nonce, uint16_t uid) 60 | { 61 | port_uid_msg_t port_uid_msg = {}; 62 | port_uid_msg.nonce = nonce; 63 | port_uid_msg.uid = (uint32_t)uid; 64 | port_uid_msg.nim2Str = kNim2; 65 | port_uid_msg.prmyStr = kPrmy; 66 | port_uid_msg.unk = 0; 67 | sendMsg(port, (uint8_t *)&port_uid_msg, sizeof(port_uid_msg)); 68 | } 69 | 70 | void 71 | sendNameMsg(CFMessagePortRef port, uint32_t nonce, char *name) 72 | { 73 | port_name_msg_t port_name_msg = {}; 74 | port_name_msg.nonce = nonce; 75 | strncpy(port_name_msg.name, name, kAgentNotificationPortNameLen-1); 76 | port_name_msg.trueStr = kTrue; 77 | port_name_msg.unk = 0; 78 | port_name_msg.len = kAgentNotificationPortNameLen; 79 | sendMsg(port, (uint8_t *)&port_name_msg, sizeof(port_name_msg)); 80 | } 81 | 82 | void 83 | sendMKSerialMsg(CFMessagePortRef port, uint32_t nonce, uint16_t uid) 84 | { 85 | if (!gReceivedSerial) { 86 | printf("Do not have a valid serial number to respond with\n"); 87 | return; 88 | } 89 | 90 | mk_serial_msg_t mk_serial_msg = {}; 91 | mk_serial_msg.nonce = nonce; 92 | mk_serial_msg.uid = (uint32_t)uid; 93 | mk_serial_msg.nim2Str = kNim2; 94 | mk_serial_msg.prmyStr = kPrmy; 95 | mk_serial_msg.len = kSerialNumberLen-1; 96 | strncpy(mk_serial_msg.serial, gSerialNum, kSerialNumberLen-1); 97 | sendMsg(port, (uint8_t *)&mk_serial_msg, sizeof(mk_serial_msg)); 98 | } 99 | 100 | void 101 | sendMKNameMsg(CFMessagePortRef port, uint32_t nonce, char *name) 102 | { 103 | mk_port_name_msg_t mk_port_name_msg = {}; 104 | mk_port_name_msg.nonce = nonce; 105 | mk_port_name_msg.trueStr = kTrue; 106 | mk_port_name_msg.unk = 0x30; 107 | mk_port_name_msg.len = kMikroNotificationPortNameLen; 108 | strncpy(mk_port_name_msg.name, name, kMikroNotificationPortNameLen-1); 109 | sendMsg(port, (uint8_t *)&mk_port_name_msg, sizeof(mk_port_name_msg)); 110 | } 111 | 112 | void 113 | sendCmdMsg(CFMessagePortRef port, uint32_t nonce, uint32_t cmd) 114 | { 115 | cmd_msg_t cmd_msg; 116 | cmd_msg.nonce = nonce; 117 | cmd_msg.cmd = cmd; 118 | sendMsg(port, (uint8_t *)&cmd_msg, sizeof(cmd_msg)); 119 | } 120 | 121 | void 122 | sendButtonDataMsg(CFMessagePortRef port, button_data_t button_data) 123 | { 124 | button_data_msg_t button_data_msg = {}; 125 | button_data_msg.nonce = kButtonNonce; 126 | button_data_msg.len = kButtonDataLen; 127 | memcpy(button_data_msg.button_data, &button_data, kButtonDataLen); 128 | sendMsg(port, (uint8_t*)&button_data_msg, sizeof(button_data_msg)); 129 | } 130 | 131 | void 132 | sendScreenDataMsg(CFMessagePortRef port, screen_data_t screen_data) 133 | { 134 | screen_data_msg_t screen_data_msg = {}; 135 | screen_data_msg.nonce = kScreenNonce; 136 | screen_data_msg.unk1 = 0x10000000; 137 | screen_data_msg.unk2 = 0; 138 | screen_data_msg.unk3 = kProjUid; 139 | screen_data_msg.len = kScreenDataLen; 140 | memcpy(screen_data_msg.screen_data, &screen_data, kScreenDataLen); 141 | sendMsg(port, (uint8_t*)&screen_data_msg, sizeof(screen_data_msg)); 142 | } 143 | 144 | void 145 | initButtonData(button_data_t *button_data) 146 | { 147 | memset((void*)button_data, 0, sizeof(button_data_t)); 148 | } 149 | 150 | void 151 | initScreenData(screen_data_t *screen_data) 152 | { 153 | memset((void*)screen_data, 0, sizeof(screen_data_t)); 154 | } 155 | 156 | void 157 | setPadColor(button_data_t *button_data, uint32_t btn_num, char r, char g, char b) 158 | { 159 | uint32_t btn_code; 160 | size_t base_idx; 161 | 162 | btn_code = btn_num_to_code(btn_num); 163 | base_idx = btn_code * 3; 164 | 165 | button_data->pad_buttons[base_idx] = r; 166 | button_data->pad_buttons[base_idx+1] = g; 167 | button_data->pad_buttons[base_idx+2] = b; 168 | } 169 | 170 | void 171 | readScreenDataFromFile(screen_data_t *screen_data, const char *fname) 172 | { 173 | FILE *fp; 174 | 175 | if ((fp = fopen(fname, "r"))) { 176 | fread(screen_data, 1, kScreenDataLen, fp); 177 | fclose(fp); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /messenger/messenger.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define kMainPortName "NIHWMainHandler" 4 | #define kSLBootstrapPortName "SLHWMainHandler" 5 | #define kMikroNotificationPortNameFormat "NIHWMaschineMikroMK2-%s%04dNotification" 6 | #define kNim2 0x4e694d32 7 | #define kPrmy 0x70726d79 8 | #define kTrue 0x74727565 9 | #define kStrt 0x73747274 10 | #define kSerialNumberLen 9 11 | #define kAgentNotificationPortNameLen 26 12 | #define kAgentRequestPortNameLen 21 13 | #define kMikroNotificationPortNameLen 46 14 | #define kMikroRequestPortNameLen 41 15 | #define kNewProjLen 12 16 | #define kSelBtn 0x1 17 | #define kProjUid 0x800040 18 | #define kScreenDataLen 1024 19 | #define kButtonDataLen 78 20 | #define kPadButtonLen 48 21 | #define kPadButtonOffset 30 22 | 23 | extern char gReceivedSerial; 24 | extern char gSerialNum[kSerialNumberLen]; 25 | 26 | typedef struct { 27 | uint32_t nonce; 28 | } nonce_msg_t; 29 | 30 | typedef struct { 31 | uint32_t nonce; 32 | uint32_t cmd; 33 | } cmd_msg_t; 34 | 35 | typedef struct { 36 | uint32_t nonce; 37 | uint32_t uid; 38 | uint32_t nim2Str; 39 | uint32_t prmyStr; 40 | uint32_t unk; 41 | } port_uid_msg_t; 42 | 43 | typedef struct __attribute__((packed)) { 44 | uint32_t nonce; 45 | uint32_t trueStr; 46 | uint32_t unk; 47 | uint32_t len; 48 | char name[kAgentNotificationPortNameLen]; 49 | } port_name_msg_t; 50 | 51 | typedef struct __attribute__((packed)) { 52 | uint32_t nonce; 53 | uint32_t uid; 54 | uint32_t nim2Str; 55 | uint32_t prmyStr; 56 | uint32_t len; 57 | char serial[kSerialNumberLen]; 58 | } mk_serial_msg_t; 59 | 60 | typedef struct __attribute__((packed)) { 61 | uint32_t nonce; 62 | uint32_t trueStr; 63 | uint32_t unk; 64 | uint32_t len; 65 | char name[kMikroNotificationPortNameLen]; 66 | } mk_port_name_msg_t; 67 | 68 | typedef struct __attribute__((packed)) { 69 | uint32_t nonce; 70 | uint32_t unk; 71 | uint32_t port_uid; 72 | uint32_t len; 73 | char num[kSerialNumberLen]; 74 | } serial_num_msg_t; 75 | 76 | typedef struct __attribute__((packed)) { 77 | uint32_t nonce; 78 | uint32_t len; 79 | char button_data[kButtonDataLen]; 80 | } button_data_msg_t; 81 | 82 | typedef struct __attribute__((packed)) { 83 | uint32_t nonce; 84 | uint32_t unk1; 85 | uint32_t unk2; 86 | uint32_t unk3; 87 | uint32_t len; 88 | char screen_data[kScreenDataLen]; 89 | } screen_data_msg_t; 90 | 91 | typedef struct __attribute__((packed)) { 92 | char ctrl_buttons[kPadButtonOffset]; 93 | char pad_buttons[kPadButtonLen]; 94 | } button_data_t; 95 | 96 | typedef struct { 97 | char pixels[kScreenDataLen]; 98 | } screen_data_t; 99 | 100 | extern char gReceivedSerial; 101 | extern char gSerialNum[kSerialNumberLen]; 102 | 103 | CFMessagePortRef getBootstrapPort(const char *name); 104 | void sendMsg(CFMessagePortRef port, uint8_t *msg, size_t size); 105 | void sendNonceMsg(CFMessagePortRef port, uint32_t nonce); 106 | void sendUidMsg(CFMessagePortRef port, uint32_t nonce, uint16_t uid); 107 | void sendNameMsg(CFMessagePortRef port, uint32_t nonce, char *name); 108 | void sendMKSerialMsg(CFMessagePortRef port, uint32_t nonce, uint16_t uid); 109 | void sendMKNameMsg(CFMessagePortRef port, uint32_t nonce, char *name); 110 | void sendCmdMsg(CFMessagePortRef port, uint32_t nonce, uint32_t cmd); 111 | void sendButtonDataMsg(CFMessagePortRef port, button_data_t button_data); 112 | void sendScreenDataMsg(CFMessagePortRef port, screen_data_t screen_data); 113 | 114 | void initButtonData(button_data_t *button_data); 115 | void initScreenData(screen_data_t *screen_data); 116 | 117 | void setPadColor(button_data_t *button_data, uint32_t btn_num, char r, char g, char b); 118 | void readScreenDataFromFile(screen_data_t *screen_data, const char *fname); -------------------------------------------------------------------------------- /notifier/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | PWD=/Users/samlerner/Projects/NIProtocol/notifier 3 | 4 | notifier.dylib: notifier.o 5 | $(CC) -g -o notifier.dylib notifier.o \ 6 | -dynamiclib -framework CoreFoundation \ 7 | -install_name libninotifier.dylib 8 | 9 | cp $(PWD)/notifier.dylib /usr/local/lib/libninotifier.dylib 10 | cp $(PWD)/notifier.h /usr/local/include/ninotifier.h 11 | 12 | notifier.o: 13 | $(CC) -g -o notifier.o -c notifier.cpp -I$(PWD) 14 | 15 | clean: 16 | rm notifier.o notifier.dylib 17 | -------------------------------------------------------------------------------- /notifier/notifier.cpp: -------------------------------------------------------------------------------- 1 | #include "notifier.h" 2 | 3 | void 4 | broadcast(CFDataRef data) 5 | { 6 | CFMutableDictionaryRef userInfo; 7 | size_t numValues; 8 | 9 | numValues = 1; 10 | userInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 11 | numValues, 12 | &kCFCopyStringDictionaryKeyCallBacks, 13 | &kCFTypeDictionaryValueCallBacks); 14 | 15 | CFDictionaryAddValue(userInfo, CFSTR(kDataKey), data); 16 | 17 | CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), 18 | CFSTR(kMK2NotifName), 19 | NULL, 20 | userInfo, 21 | true); 22 | 23 | release_user_info: 24 | CFRelease(userInfo); 25 | } 26 | 27 | void 28 | listen(CFNotificationCallback callback) 29 | { 30 | CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), 31 | NULL, 32 | callback, 33 | CFSTR(kMK2NotifName), 34 | NULL, 35 | CFNotificationSuspensionBehaviorDeliverImmediately); 36 | } 37 | -------------------------------------------------------------------------------- /notifier/notifier.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define kNotifNameLen 16 4 | #define kMK2NotifName "MK2Notification" 5 | 6 | #define kDataKeyLen 5 7 | #define kDataKey "data" 8 | 9 | void broadcast(CFDataRef data); 10 | void listen(CFNotificationCallback callback); 11 | -------------------------------------------------------------------------------- /parser/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | PWD=/Users/samlerner/Projects/NIProtocol/parser 3 | 4 | parser.dylib: parser.o 5 | $(CC) -g -o parser.dylib parser.o \ 6 | -dynamiclib -framework CoreFoundation \ 7 | -install_name libniparser.dylib 8 | 9 | cp $(PWD)/parser.dylib /usr/local/lib/libniparser.dylib 10 | cp $(PWD)/parser.h /usr/local/include/niparser.h 11 | 12 | parser.o: 13 | $(CC) -g -o parser.o -c parser.cpp -I$(PWD) 14 | 15 | clean: 16 | rm parser.o parser.dylib 17 | -------------------------------------------------------------------------------- /parser/parser.cpp: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | #include 3 | 4 | uint32_t 5 | btn_code_to_num(uint32_t code) 6 | { 7 | // Thinking of the buttons as a 2d array, 8 | // the button code is the linear index into that array, i.e. 9 | // 10 | // 0, 1, 2, 3 11 | // 4, 5, 6, 7 12 | // 8, 9, 10, 11 13 | // 12, 13, 14, 15 14 | // 15 | // We want to convert that to the button number that appears on the MK2, i.e. 16 | // 17 | // 13, 14, 15, 16 18 | // 9, 10, 11, 12 19 | // 5, 6, 7, 8 20 | // 1, 2, 3, 4 21 | // 22 | // Do this by taking the linear index of the code when reflected 23 | // about the y-axis and adding 1. 24 | 25 | uint32_t r,c; 26 | r = code / 4; 27 | c = code % 4; 28 | return (3 - r) * 4 + c + 1; 29 | } 30 | 31 | uint32_t 32 | btn_num_to_code(uint32_t num) 33 | { 34 | // Perform the inverse of btn_num_to_btn_code 35 | 36 | uint32_t r,c; 37 | num -= 1; 38 | r = 3 - num / 4; 39 | c = num % 4; 40 | return r * 4 + c; 41 | } 42 | 43 | void 44 | parse_button_msg(uint32_t *data, 45 | mk2_msg *msg) 46 | { 47 | // The layout of a button msg is: 48 | // btn | state 49 | // --4 bytes-- -- 4 bytes-- 50 | // 51 | // Where btn is 0 when scrolling and 11 when clicking. 52 | // When scrolling, state is 1 when scrolling clockwise and -1 when counterclockwise. 53 | // When clicking, the state is 1 when down and 0 when up. 54 | 55 | msg->type = ButtonType; 56 | msg->msg.button_msg.btn = *data; 57 | msg->msg.button_msg.state = *(int32_t *)(data+1); 58 | } 59 | 60 | void 61 | parse_pad_msg(uint32_t *data, 62 | mk2_msg *msg) 63 | { 64 | // The layout of a pad msg is: 65 | // btn | unknown | pressure 66 | // --4 bytes-- --4 bytes-- --4 bytes-- 67 | // 68 | // Where btn is the button code described in `btn_code_to_btn_num` 69 | // and pressure is the amount of pressure being applied to the button. 70 | 71 | msg->type = PadType; 72 | msg->msg.pad_msg.btn = btn_code_to_num(*data); 73 | msg->msg.pad_msg.pressure = *(data+2); 74 | } 75 | 76 | int 77 | parse_packet(char *packet, 78 | size_t packetLen, 79 | mk2_msg *msg) 80 | { 81 | uint32_t *fields, 82 | ctrl; 83 | void (*parse_fn)(uint32_t*, mk2_msg*); 84 | 85 | // The header of a packet is formatted as such: 86 | // ctrl | counter | unknown | nmsgs 87 | // --4 bytes-- --4 bytes-- --4 bytes-- --4 bytes-- 88 | // 89 | // Where ctrl specified the packet type (wheel or button) 90 | // and nmsgs specifies the number of messages in the packet. 91 | // We don't care about the counter. 92 | 93 | fields = (uint32_t *)packet; 94 | ctrl = *fields; 95 | fields += 4; 96 | 97 | if (ctrl == kWheelCtrl || ctrl == kButtonCtrl) 98 | parse_fn = &parse_button_msg; 99 | else if (ctrl == kPadCtrl) 100 | parse_fn = &parse_pad_msg; 101 | else { 102 | printf("Unrecognized control code: %x\n", ctrl); 103 | return 1; 104 | } 105 | 106 | parse_fn(fields, msg); 107 | return 0; 108 | } 109 | 110 | void 111 | display_msg(mk2_msg msg) 112 | { 113 | if (msg.type == ButtonType) 114 | printf("Button - Button: %u, State: %d\n", msg.msg.button_msg.btn, msg.msg.button_msg.state); 115 | else 116 | printf("Pad - Button: %u, Pressure: %u\n", msg.msg.pad_msg.btn, msg.msg.pad_msg.pressure); 117 | } 118 | -------------------------------------------------------------------------------- /parser/parser.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define kWheelCtrl 0x3774e00 4 | #define kButtonCtrl 0x3734e00 5 | #define kPadCtrl 0x3504e00 6 | 7 | enum MsgType { ButtonType, PadType }; 8 | 9 | typedef struct { 10 | uint32_t btn; 11 | int32_t state; 12 | } mk2_button_msg; 13 | 14 | typedef struct { 15 | uint32_t btn; 16 | uint32_t pressure; 17 | } mk2_pad_msg; 18 | 19 | typedef struct { 20 | enum MsgType type; 21 | union { 22 | mk2_pad_msg pad_msg; 23 | mk2_button_msg button_msg; 24 | } msg; 25 | } mk2_msg; 26 | 27 | uint32_t btn_code_to_num(uint32_t code); 28 | uint32_t btn_num_to_code(uint32_t num); 29 | int parse_packet(char *packet, size_t packetLen, mk2_msg *msg); 30 | void display_msg(mk2_msg msg); 31 | -------------------------------------------------------------------------------- /picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamL98/NIProtocol/fc63ab45a965e7299a38e5299ca652827390e326/picture.jpg -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | PWD=/Users/samlerner/Projects/NIProtocol/server 3 | 4 | server: mach_callbacks sock_callbacks bonjour_callbacks 5 | $(CC) -g -o m2server server.cpp mach_callbacks.o sock_callbacks.o bonjour_callbacks.o \ 6 | -I$(PWD) -L$(PWD) \ 7 | -framework Foundation -framework CFNetwork \ 8 | -lnimessenger -lninotifier -lniparser -lnihandshaker 9 | 10 | mach_callbacks: 11 | $(CC) -g -o mach_callbacks.o -c mach_callbacks.cpp 12 | 13 | sock_callbacks: 14 | $(CC) -g -o sock_callbacks.o -c sock_callbacks.cpp 15 | 16 | bonjour_callbacks: 17 | $(CC) -g -o bonjour_callbacks.o -c bonjour_callbacks.cpp 18 | 19 | clean: 20 | rm m2server mach_callbacks.o sock_callbacks.o bonjour_callbacks.o 21 | -------------------------------------------------------------------------------- /server/bonjour_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include "bonjour_callbacks.h" 2 | 3 | void register_callback(CFNetServiceRef service, 4 | CFStreamError *error, 5 | void *info) 6 | { 7 | printf("Bonjour service successfully registered\n"); 8 | } -------------------------------------------------------------------------------- /server/bonjour_callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void register_callback(CFNetServiceRef service, CFStreamError *error, void *info); -------------------------------------------------------------------------------- /server/m2server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamL98/NIProtocol/fc63ab45a965e7299a38e5299ca652827390e326/server/m2server -------------------------------------------------------------------------------- /server/mach_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mach_callbacks.h" 5 | 6 | #define kAgentNotificationPortNameFormat "NIHWS%04x%04dNotification" 7 | #define kMikroNotificationPortNameFormat "NIHWMaschineMikroMK2-%s%04dNotification" 8 | #define kMikroRequestPortNameFormat "NIHWMaschineMikroMK2-%s%04dRequest" 9 | #define kSerialNumberPacketDataLen 25 10 | 11 | uint16_t gMsgUid = 40; 12 | uint32_t gSerialNonce = 54808107; 13 | uint32_t gCmdNonce = 54808064; 14 | CFMessagePortRef gM2NotifPort = NULL; 15 | 16 | CFMessagePortRef 17 | createRequestPort(const char *name, 18 | CFMessagePortCallBack callout, 19 | void *info) 20 | { 21 | Boolean shouldFreeInfo; 22 | CFStringRef cfName; 23 | CFMessagePortRef port; 24 | CFMessagePortContext ctx; 25 | 26 | cfName = CFStringCreateWithCString(kCFAllocatorDefault, 27 | name, 28 | kCFStringEncodingASCII); 29 | 30 | ctx.version = 0; 31 | ctx.info = info; 32 | ctx.retain = NULL;//CFRetain; 33 | ctx.release = NULL;//CFRelease; 34 | ctx.copyDescription = NULL; 35 | 36 | port = CFMessagePortCreateLocal(kCFAllocatorDefault, 37 | cfName, 38 | callout, 39 | &ctx, 40 | &shouldFreeInfo); 41 | 42 | if (!shouldFreeInfo) 43 | free(info); 44 | 45 | return port; 46 | } 47 | 48 | CFDataRef 49 | handleNonceMsg(uint8_t *dataPtr, size_t dataLen) 50 | { 51 | if (*(uint32_t*)dataPtr != 55797590) 52 | return NULL; 53 | 54 | nonce_response_t response; 55 | 56 | response.unk1 = kNonceResponseUnk1; 57 | response.unk2 = kNonceResponseUnk2; 58 | 59 | return CFDataCreate(kCFAllocatorDefault, 60 | (uint8_t *)&response, 61 | sizeof(response)); 62 | } 63 | 64 | CFDataRef 65 | handleUidMsg(uint8_t *dataPtr, size_t dataLen) 66 | { 67 | CFMessagePortRef reqPort; 68 | port_uid_msg_t port_uid_msg; 69 | char reqPortName[kMikroRequestPortNameLen]; 70 | char notifPortName[kMikroNotificationPortNameLen]; 71 | uint8_t *response; 72 | size_t responseLen; 73 | 74 | port_uid_msg = *(port_uid_msg_t *)dataPtr; 75 | 76 | // Account for the weird 3rd to last handshake iteration 77 | if (port_uid_msg.uid == 0x1350) 78 | return NULL; 79 | 80 | // Account for the difference during the last 81 | else if (port_uid_msg.uid == 0x1200 && dataLen == sizeof(mk_serial_msg_t)) { 82 | serial_response_t response_st; 83 | mk_serial_msg_t mk_serial_msg; 84 | 85 | mk_serial_msg = *(mk_serial_msg_t *)dataPtr; 86 | 87 | sprintf(reqPortName, kMikroRequestPortNameFormat, mk_serial_msg.serial, gMsgUid); 88 | reqPortName[kMikroRequestPortNameLen-1] = 0; 89 | 90 | sprintf(notifPortName, kMikroNotificationPortNameFormat, mk_serial_msg.serial, gMsgUid); 91 | notifPortName[kMikroNotificationPortNameLen-1] = 0; 92 | 93 | response_st.trueStr = kTrue; 94 | response_st.reqPortNameLen = kMikroRequestPortNameLen; 95 | strncpy(response_st.reqPortName, reqPortName, kMikroRequestPortNameLen); 96 | response_st.notifPortNameLen = kMikroNotificationPortNameLen; 97 | strncpy(response_st.notifPortName, notifPortName, kMikroNotificationPortNameLen); 98 | response_st.unk = 0; 99 | 100 | response = (uint8_t *)&response_st; 101 | responseLen = sizeof(response_st); 102 | } 103 | else { 104 | port_uid_response_t response_st; 105 | 106 | sprintf(reqPortName, kAgentRequestPortNameFormat, port_uid_msg.uid, gMsgUid); 107 | reqPortName[kAgentRequestPortNameLen-1] = 0; 108 | 109 | sprintf(notifPortName, kAgentNotificationPortNameFormat, port_uid_msg.uid, gMsgUid); 110 | notifPortName[kAgentNotificationPortNameLen-1] = 0; 111 | 112 | response_st.trueStr = kTrue; 113 | response_st.reqPortNameLen = kAgentRequestPortNameLen; 114 | strncpy(response_st.reqPortName, reqPortName, kAgentRequestPortNameLen); 115 | response_st.notifPortNameLen = kAgentNotificationPortNameLen; 116 | strncpy(response_st.notifPortName, notifPortName, kAgentNotificationPortNameLen); 117 | response_st.unk = 0; 118 | 119 | response = (uint8_t *)&response_st; 120 | responseLen = sizeof(response_st); 121 | } 122 | 123 | if (!(reqPort = createRequestPort(reqPortName, 124 | (CFMessagePortCallBack)req_port_callback, 125 | NULL))) { 126 | printf("Couldn't create request port: %s\n", reqPortName); 127 | exit(1); 128 | } 129 | 130 | CFMessagePortSetDispatchQueue(reqPort, dispatch_get_main_queue()); 131 | ++gMsgUid; 132 | 133 | return CFDataCreate(kCFAllocatorDefault, 134 | response, 135 | responseLen); 136 | } 137 | 138 | CFDataRef 139 | handleNameMsg(uint8_t *dataPtr, size_t dataLen) 140 | { 141 | port_name_response_t response; 142 | 143 | response.trueStr = kTrue; 144 | 145 | return CFDataCreate(kCFAllocatorDefault, 146 | (uint8_t *)&response, 147 | sizeof(response)); 148 | } 149 | 150 | CFDataRef 151 | bootstrap_req_port_callback(CFMessagePortRef local, 152 | SInt32 msgid, 153 | CFDataRef data, 154 | void *info) 155 | { 156 | uint8_t *dataPtr; 157 | size_t dataLen; 158 | 159 | if (!data || !(dataPtr = (uint8_t *)CFDataGetBytePtr(data))) { 160 | printf("No data from hardware agent\n"); 161 | return NULL; 162 | } 163 | 164 | dataLen = CFDataGetLength(data); 165 | 166 | switch (dataLen) { 167 | case sizeof(nonce_msg_t): return handleNonceMsg(dataPtr, dataLen); 168 | case sizeof(port_uid_msg_t): return handleUidMsg(dataPtr, dataLen); 169 | case sizeof(mk_serial_msg_t): return handleUidMsg(dataPtr, dataLen); 170 | default: 171 | printf("Don't know how to handle %lu bytes of data\n", dataLen); 172 | return NULL; 173 | } 174 | } 175 | 176 | CFDataRef 177 | req_port_callback(CFMessagePortRef local, 178 | SInt32 msgid, 179 | CFDataRef data, 180 | void *info) 181 | { 182 | uint8_t *dataPtr; 183 | size_t dataLen; 184 | 185 | if (!data || !(dataPtr = (uint8_t *)CFDataGetBytePtr(data))) { 186 | printf("No data in request port callback\n"); 187 | return NULL; 188 | } 189 | 190 | dataLen = CFDataGetLength(data); 191 | 192 | if (dataLen == sizeof(port_name_msg_t)) 193 | { 194 | CFMessagePortRef notifPort; 195 | serial_num_msg_t serial_num_msg; 196 | port_name_msg_t port_name_msg; 197 | 198 | port_name_msg = *(port_name_msg_t *)dataPtr; 199 | 200 | if (!strcmp(port_name_msg.name, "NIHWS12000043Notification")) 201 | { 202 | serial_num_msg.nonce = gSerialNonce; 203 | serial_num_msg.unk = 0; 204 | serial_num_msg.port_uid = 0x1200; 205 | serial_num_msg.len = 9; 206 | strncpy(serial_num_msg.num, "88CC589C", kSerialNumberLen); 207 | serial_num_msg.num[kSerialNumberLen-1] = 0; 208 | 209 | if (!(notifPort = getBootstrapPort(port_name_msg.name))) { 210 | printf("Couldn't get notification port from name: %s\n", port_name_msg.name); 211 | return NULL; 212 | } 213 | 214 | printf("Sending serial\n"); 215 | sendMsg(notifPort, (uint8_t*)&serial_num_msg, sizeof(serial_num_msg)); 216 | } 217 | } 218 | else if (dataLen == sizeof(mk_port_name_msg_t)) { 219 | CFMessagePortRef notifPort; 220 | mk_port_name_msg_t mk_port_name_msg; 221 | cmd_msg_t cmd_msg; 222 | 223 | mk_port_name_msg = *(mk_port_name_msg_t *)dataPtr; 224 | if (!(gM2NotifPort = getBootstrapPort(mk_port_name_msg.name))) { 225 | printf("Couldn't get mk notification port from name: %s\n", mk_port_name_msg.name); 226 | return NULL; 227 | } 228 | 229 | printf("Obtained reference to notification port: %s\n", mk_port_name_msg.name); 230 | 231 | cmd_msg.nonce = gCmdNonce; 232 | cmd_msg.cmd = kTrue; 233 | 234 | sendMsg(gM2NotifPort, (uint8_t*)&cmd_msg, sizeof(cmd_msg)); 235 | } 236 | else if (dataLen == sizeof(cmd_msg_t)) { 237 | if (!gM2NotifPort) 238 | return NULL; 239 | 240 | cmd_msg_t recv_cmd_msg; 241 | cmd_msg_t send_cmd_msg; 242 | 243 | recv_cmd_msg = *(cmd_msg_t *)dataPtr; 244 | if (recv_cmd_msg.cmd != kStrt) 245 | return NULL; 246 | 247 | send_cmd_msg.nonce = 54742528; 248 | send_cmd_msg.cmd = kTrue; 249 | 250 | sendMsg(gM2NotifPort, (uint8_t*)&send_cmd_msg, sizeof(send_cmd_msg)); 251 | } 252 | else { 253 | printf("Don't know how to handle %lu bytes\n", dataLen); 254 | return NULL; 255 | } 256 | 257 | return handleNameMsg(dataPtr, dataLen); 258 | } -------------------------------------------------------------------------------- /server/mach_callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define kNonceResponseUnk1 0x20200 4 | #define kNonceResponseUnk2 0x3 5 | 6 | typedef struct { 7 | uint32_t unk1; 8 | uint32_t unk2; 9 | } nonce_response_t; 10 | 11 | typedef struct __attribute__((packed)) { 12 | uint32_t trueStr; 13 | uint32_t reqPortNameLen; 14 | char reqPortName[kAgentRequestPortNameLen]; 15 | uint32_t notifPortNameLen; 16 | char notifPortName[kAgentNotificationPortNameLen]; 17 | uint32_t unk; 18 | } port_uid_response_t; 19 | 20 | typedef struct __attribute__((packed)) { 21 | uint32_t trueStr; 22 | uint32_t reqPortNameLen; 23 | char reqPortName[kMikroRequestPortNameLen]; 24 | uint32_t notifPortNameLen; 25 | char notifPortName[kMikroNotificationPortNameLen]; 26 | uint32_t unk; 27 | } serial_response_t; 28 | 29 | typedef struct { 30 | uint32_t trueStr; 31 | } port_name_response_t; 32 | 33 | extern CFMessagePortRef gM2NotifPort; 34 | 35 | CFMessagePortRef createRequestPort(const char *name, CFMessagePortCallBack callout, void *info); 36 | CFDataRef bootstrap_req_port_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); 37 | CFDataRef req_port_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); -------------------------------------------------------------------------------- /server/server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "mach_callbacks.h" 9 | #include "sock_callbacks.h" 10 | #include "bonjour_callbacks.h" 11 | 12 | #define kM2NotifPortMsgUid 46 13 | #define kListenPort "6969" 14 | 15 | CFSocketRef 16 | create_cfsocket() 17 | { 18 | CFSocketRef cfsock = NULL; 19 | struct addrinfo hints, 20 | *res; 21 | int sockfd, 22 | status; 23 | 24 | memset(&hints, 0, sizeof(hints)); 25 | hints.ai_family = AF_UNSPEC; 26 | hints.ai_socktype = SOCK_STREAM; 27 | hints.ai_flags = AI_PASSIVE; 28 | 29 | if ((status = getaddrinfo(NULL, 30 | kListenPort, 31 | &hints, 32 | &res))) { 33 | printf("Couldn't get addr info: %s\n", gai_strerror(status)); 34 | return NULL; 35 | } 36 | 37 | if ((sockfd = socket(res->ai_family, 38 | res->ai_socktype, 39 | res->ai_protocol)) < 0) { 40 | printf("Couldn't get valid socket fd\n"); 41 | goto ret; 42 | } 43 | 44 | if ((status = bind(sockfd, 45 | res->ai_addr, 46 | res->ai_addrlen)) < 0) { 47 | printf("Couldn't bind socket to port: %s\n", kListenPort); 48 | goto ret; 49 | } 50 | 51 | listen(sockfd, 5); 52 | 53 | cfsock = CFSocketCreateWithNative(kCFAllocatorDefault, 54 | sockfd, 55 | kCFSocketReadCallBack,//kCFSocketDataCallBack, 56 | (CFSocketCallBack)conn_accept_callback, 57 | NULL); 58 | 59 | ret: 60 | freeaddrinfo(res); 61 | return cfsock; 62 | } 63 | 64 | int 65 | get_serial(char *serial) 66 | { 67 | memset(serial, 0, kSerialNumberLen); 68 | gStopAfterSerial = 1; 69 | doHandshake(serial); 70 | return strlen(serial) == kSerialNumberLen-1; 71 | } 72 | 73 | CFNetServiceRef 74 | create_netservice(int port, CFNetServiceClientCallBack callback) 75 | { 76 | CFNetServiceClientContext ctx; 77 | CFNetServiceRef cfnetservice; 78 | CFStreamError error; 79 | 80 | cfnetservice = CFNetServiceCreate(NULL, 81 | CFSTR("local."), 82 | CFSTR("_freehand._tcp"), 83 | CFSTR("M2"), 84 | port); 85 | 86 | ctx.version = 0; 87 | ctx.info = NULL; 88 | ctx.retain = NULL; 89 | ctx.release = NULL; 90 | ctx.copyDescription = NULL; 91 | CFNetServiceSetClient(cfnetservice, callback, &ctx); 92 | 93 | CFNetServiceScheduleWithRunLoop(cfnetservice, 94 | CFRunLoopGetCurrent(), 95 | kCFRunLoopDefaultMode); 96 | 97 | if (!CFNetServiceRegisterWithOptions(cfnetservice, 98 | 0, 99 | &error)) 100 | { 101 | CFNetServiceUnscheduleFromRunLoop(cfnetservice, 102 | CFRunLoopGetCurrent(), 103 | kCFRunLoopDefaultMode); 104 | 105 | CFRelease(cfnetservice); 106 | cfnetservice = NULL; 107 | } 108 | 109 | return cfnetservice; 110 | } 111 | 112 | int main(int argc, const char * argv[]) { 113 | CFRunLoopSourceRef cfRunLoopSrc; 114 | CFSocketRef cfsock; 115 | CFNetServiceRef cfnetservice; 116 | int retval = 0; 117 | 118 | // If the port NIHWMainHandler is available, then that means the hardware agent is running. 119 | // In this case, we can simply perform the handshake from the client side with the hardware 120 | // agent until we obtain the serial number. Then we can open the M2 notification port using that serial. 121 | // 122 | // Otherwise, we will create the NIHWMainHandler port and perform the handshake from the server side, 123 | // passing a hardcoded serial number. Then M2 will send us the name of its notification port that we can then open. 124 | // We open a request port for M2 but ignore all requests. 125 | if (getBootstrapPort(kMainPortName)) 126 | { 127 | printf("Hardware agent running\n"); 128 | 129 | char serial[kSerialNumberLen]; 130 | char m2NotifPortName[kMikroNotificationPortNameLen]; 131 | 132 | printf("Trying to obtain serial number\n"); 133 | 134 | if (!get_serial(serial)) { 135 | printf("Couldn't get serial number\n"); 136 | return 1; 137 | } 138 | 139 | printf("Received serial number: %s\n", serial); 140 | 141 | sprintf(m2NotifPortName, kMikroNotificationPortNameFormat, serial, kM2NotifPortMsgUid); 142 | m2NotifPortName[kMikroNotificationPortNameLen-1] = 0; 143 | 144 | if (!(gM2NotifPort = getBootstrapPort(m2NotifPortName))) { 145 | printf("Couldn't obtain M2 notification port\n"); 146 | return 1; 147 | } 148 | 149 | printf("Obtained reference to notification port: %s\n", m2NotifPortName); 150 | } 151 | else { 152 | printf("Hardware agent not running\n"); 153 | 154 | CFMessagePortRef bsPort = createRequestPort(kMainPortName, 155 | (CFMessagePortCallBack)bootstrap_req_port_callback, 156 | NULL); 157 | 158 | CFMessagePortSetDispatchQueue(bsPort, dispatch_get_main_queue()); 159 | 160 | printf("Created port %s\n", kMainPortName); 161 | } 162 | 163 | if (!(cfsock = create_cfsocket())) { 164 | printf("Couldn't create CFSocket\n"); 165 | return 1; 166 | } 167 | 168 | printf("Created socket listening on %s\n", kListenPort); 169 | 170 | if (!(cfRunLoopSrc = CFSocketCreateRunLoopSource(kCFAllocatorDefault, 171 | cfsock, 172 | 0))) { 173 | printf("Couldn't create a run loop source for the socket\n"); 174 | retval = 1; 175 | goto release_sock; 176 | } 177 | 178 | CFRunLoopAddSource(CFRunLoopGetCurrent(), 179 | cfRunLoopSrc, 180 | kCFRunLoopDefaultMode); 181 | 182 | if (!(cfnetservice = create_netservice(atoi(kListenPort), 183 | (CFNetServiceClientCallBack)register_callback))) { 184 | printf("Couldn't create cfnetservice\n"); 185 | retval = 1; 186 | goto release_rlsrc; 187 | } 188 | 189 | CFRunLoopRun(); 190 | 191 | release_rlsrc: 192 | CFRelease(cfRunLoopSrc); 193 | 194 | release_sock: 195 | CFSocketInvalidate(cfsock); 196 | CFRelease(cfsock); 197 | return retval; 198 | } 199 | -------------------------------------------------------------------------------- /server/sock_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sock_callbacks.h" 5 | 6 | #define kMaxPacketLen 28 7 | 8 | extern CFMessagePortRef gM2NotifPort; 9 | 10 | void conn_read_callback(CFSocketRef s, 11 | CFSocketCallBackType callbackType, 12 | CFDataRef address, 13 | void *data, 14 | void *info) 15 | { 16 | char packet[kMaxPacketLen]; 17 | int packetLen; 18 | mk2_msg msg; 19 | 20 | if ((packetLen = recv(CFSocketGetNative(s), 21 | packet, 22 | kMaxPacketLen, 23 | 0)) <= 0) 24 | { 25 | if (packetLen == 0) { 26 | CFSocketInvalidate(s); 27 | CFRelease(s); 28 | } 29 | return; 30 | } 31 | 32 | if (packetLen < 24) { 33 | printf("Packet of length %d is too short\n", packetLen); 34 | return; 35 | } 36 | 37 | if (parse_packet(packet, 38 | (size_t)packetLen, 39 | &msg)) { 40 | printf("Couldn't parse packet\n"); 41 | return; 42 | } 43 | 44 | display_msg(msg); 45 | sendMsg(gM2NotifPort, (uint8_t *)packet, packetLen); 46 | } 47 | 48 | void conn_accept_callback(CFSocketRef s, 49 | CFSocketCallBackType callbackType, 50 | CFDataRef address, 51 | void *data, 52 | void *info) 53 | { 54 | struct sockaddr_storage sa; 55 | CFRunLoopSourceRef cfRunLoopSrc; 56 | CFSocketRef cfsock; 57 | socklen_t sasize = sizeof(sa); 58 | int new_fd; 59 | 60 | if (!gM2NotifPort) { 61 | printf("Null M2 notification port\n"); 62 | return; 63 | } 64 | 65 | if ((new_fd = accept(CFSocketGetNative(s), 66 | (struct sockaddr *)&sa, 67 | &sasize)) < 0) { 68 | printf("Couldn't accept\n"); 69 | return; 70 | } 71 | 72 | cfsock = CFSocketCreateWithNative(kCFAllocatorDefault, 73 | new_fd, 74 | kCFSocketReadCallBack, 75 | (CFSocketCallBack)conn_read_callback, 76 | NULL); 77 | 78 | if (!(cfRunLoopSrc = CFSocketCreateRunLoopSource(kCFAllocatorDefault, 79 | cfsock, 80 | 0))) { 81 | printf("Couldn't create a run loop source for the socket\n"); 82 | CFRelease(cfsock); 83 | return; 84 | } 85 | 86 | CFRunLoopAddSource(CFRunLoopGetCurrent(), 87 | cfRunLoopSrc, 88 | kCFRunLoopDefaultMode); 89 | } -------------------------------------------------------------------------------- /server/sock_callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void conn_read_callback(CFSocketRef s, 4 | CFSocketCallBackType callbackType, 5 | CFDataRef address, 6 | void *data, 7 | void *info); 8 | 9 | void conn_accept_callback(CFSocketRef s, 10 | CFSocketCallBackType callbackType, 11 | CFDataRef address, 12 | void *data, 13 | void *info); -------------------------------------------------------------------------------- /testapp/Makefile: -------------------------------------------------------------------------------- 1 | CC=/usr/bin/clang++ 2 | 3 | testapp: 4 | $(CC) -g -o app testapp.cpp \ 5 | -framework Foundation \ 6 | -lniparser -lninotifier -lnimessenger 7 | 8 | clean: 9 | rm app 10 | -------------------------------------------------------------------------------- /testapp/app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamL98/NIProtocol/fc63ab45a965e7299a38e5299ca652827390e326/testapp/app -------------------------------------------------------------------------------- /testapp/testapp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | CFMessagePortRef gBsPort = NULL; 7 | uint32_t gPrevBtn = 0; 8 | 9 | size_t callbackcount = 0; 10 | 11 | void 12 | notif_callback(CFNotificationCenterRef center, 13 | void *observer, 14 | CFNotificationName name, 15 | CFStringRef object, 16 | CFDictionaryRef userInfo) 17 | { 18 | CFDataRef data; 19 | char *packet; 20 | mk2_msg msg; 21 | size_t packetLen; 22 | button_data_t button_data; 23 | int parseResult; 24 | 25 | initButtonData(&button_data); 26 | 27 | if (!gBsPort) { 28 | if (!(gBsPort = getBootstrapPort(kSLBootstrapPortName))) { 29 | printf("Couldn't get reference to SL bootstrap port\n"); 30 | return; 31 | } 32 | } 33 | 34 | if (!userInfo) { 35 | printf("No user info from notification\n"); 36 | return; 37 | } 38 | 39 | if (!CFDictionaryGetValueIfPresent(userInfo, 40 | (const void*)CFSTR(kDataKey), 41 | (const void**)&data) || !data) { 42 | printf("No data key in user info\n"); 43 | return; 44 | } 45 | 46 | packet = (char *)CFDataGetBytePtr(data); 47 | packetLen = CFDataGetLength(data); 48 | 49 | if (!packet || packetLen < 0) { 50 | printf("No data from packet\n"); 51 | return; 52 | } 53 | 54 | if (parse_packet(packet, packetLen, &msg)) { 55 | printf("Could not parse message from packet\n"); 56 | return; 57 | } 58 | 59 | display_msg(msg); 60 | 61 | if (msg.type == PadType && msg.msg.pad_msg.btn != gPrevBtn) 62 | gPrevBtn = msg.msg.pad_msg.btn; 63 | setPadColor(&button_data, gPrevBtn, char(255), 0, 0); 64 | sendButtonDataMsg(gBsPort, button_data); 65 | } 66 | } 67 | 68 | int main() 69 | { 70 | listen((CFNotificationCallback)notif_callback); 71 | CFRunLoopRun(); 72 | } 73 | --------------------------------------------------------------------------------