diff --git a/WHATSNEW.md b/WHATSNEW.md index 09dd741b..39da2d3d 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -22,7 +22,7 @@ # Version 2022.1 * Updated to [MAME 241](https://www.mamedev.org/releases/whatsnew_0241.txt). * Minimum version is now iOS 13.4, tvOS 13.4, and macOS 10.15.5 (Catalina) -* Removed MAME4mac build Target, build MAME4iOS for Catalyst instead +* Removed MAME4mac build Target, build $(TARGET) for Catalyst instead * Support for Software keyboard for machines that need keyboard input. * Non git-tracked `Developer.xcconfig` * Minimal Benchmark support, results stored in `benchmark.csv` @@ -69,7 +69,7 @@ breakout |Breakout [TTL] ## ROMless Console Machines -The following is a list of *some* of the Consoles and file types supported by MAME4iOS "out of the box" +The following is a list of *some* of the Consoles and file types supported by $(TARGET) "out of the box" Name |Description |Media File Types --------|-----------------------------------------------------------|---------------- @@ -94,7 +94,7 @@ tg16 |TurboGrafx 16 |cue, gdi, t ## Console Machines and Computers (that require BIOS) -The following is a list of *some* of the Consoles, Computers, and file types supported by MAME4iOS, but BIOS files must be installed first. +The following is a list of *some* of the Consoles, Computers, and file types supported by $(TARGET), but BIOS files must be installed first. Name |Description |Media File Types --------|-----------------------------------------------------------|---------------- @@ -144,7 +144,7 @@ bbcb |BBC Micro Model B |adf, mfi, f * added `Use DRC` Option. disable the use of `DRC` on `MAME 2xx`, for some games. # Version 2021.7 -**NOTE** `MAME4iOS` 2021.7 comes in two versions, one that uses `MAME 139u1`, and one that uses the latest `MAME` (currently 234). Make sure you download the version that is compatible with your ROMs, You can get the current version from the `Settings` page. If you want to use software based (ie cartridge, etc) romsets, please read the **Software Lists** section in the HELP or README. +**NOTE** `$(TARGET)` 2021.7 comes in two versions, one that uses `MAME 139u1`, and one that uses the latest `MAME` (currently 234). Make sure you download the version that is compatible with your ROMs, You can get the current version from the `Settings` page. If you want to use software based (ie cartridge, etc) romsets, please read the **Software Lists** section in the HELP or README. * `OSD` changes. * Ability to build with latest `MAME` version @@ -156,7 +156,7 @@ bbcb |BBC Micro Model B |adf, mfi, f * Collapsable sections in `ChooseGameUI` * Snapshot button on `HUD` and ability to use a Snapshot as Title image. * handle import of `7z` files. - - 139 version of `MAME4iOS` will ignore `7z` romsets + - 139 version of `$(TARGET)` will ignore `7z` romsets * Support for new `Apple Remote` with tvOS 14.6 * Added ability to group Clones in a different section in `ChooseGameUI` @@ -183,7 +183,7 @@ bbcb |BBC Micro Model B |adf, mfi, f | | | ---------------- |------------- -MENU |Open MAME4iOS MENU +MENU |Open $(TARGET) MENU MENU+L1 |Player Coin MENU+R1 |Player Start MENU+L2 |Player 2 Coin @@ -207,7 +207,7 @@ MENU+RIGHT |Load State ② * Game Controllers with more than two menu buttons (Xbox, DualShock, Xinput, Nimbus+, new MFi) - Left menu button is always `SELECT` - Right menu button is always `START` - - `SELECT` + `START` will bring up MAME4iOS menu + - `SELECT` + `START` will bring up $(TARGET) menu - holding down `SELECT` or `START` will show a *quick help HUD* * on iOS 14+ (not on tvOS) the XBox `GUIDE` button, and the DualShock `PS` button can be used instead of `SELECT`+`START` @@ -306,7 +306,7 @@ MENU+RIGHT |Load State ② * Added in iCade support for tvOS * Steam Controller support, controller must be in [bluetooth mode and paired](https://support.steampowered.com/kb_article.php?ref=7728-QESJ-4420) * Fixed issue where audio would stop when playing back content from another source or being interrupted by a phone call. -* Set the audio catefory to `AVAudioSessionCategoryAmbient` so MAME4iOS will play nice with other audio apps. +* Set the audio catefory to `AVAudioSessionCategoryAmbient` so $(TARGET) will play nice with other audio apps. * Make the 🅐 🅑 🅧 🅨 layout consistent across iPhone and iPad. * Added a `Nintendo Button Layout` option. if enabled the 🅐 🅑 and 🅧 🅨 buttons will be swapped to match a Nintendo layout. This option has no effect on a custom layout, or a physical game controller. * Better handling for 2 Player games, a 1 Player and a 2 Player Start option will be on the in-game menu. We try to detect how many Players, Inputs, and Coins the current game is looking for and try to adapt to that. diff --git a/iOS-res/hash.zip b/iOS-res/hash.zip index dafef873..013e1ece 100644 Binary files a/iOS-res/hash.zip and b/iOS-res/hash.zip differ diff --git a/iOS-res/help.md b/iOS-res/help.md index f6273845..2b0c604b 100755 --- a/iOS-res/help.md +++ b/iOS-res/help.md @@ -1,50 +1,30 @@ -# MAME4iOS Reloaded +# $(TARGET) ### Version $(APP_VERSION) ($(APP_DATE)) -### MAME (0.139u1 ) by David Valdeita (Seleuco) ## INTRODUCTION -MAME4iOS Reloaded is developed by David Valdeita (Seleuco), port of MAME 0.139u1 emulator by Nicola Salmoria and [TEAM](#PORT-CREDITS). +MAME stands for Multi Arcade Machine Emulator, and lets you play arcade games from the past 30+ years on your iOS device. -MAME4iOS Reloaded emulates arcade games supported by original MAME 0.139u1. +More than 8000 games are supported, and the currently supported romsets are 0.250 (December 2022). -This MAME4iOS version is targeted at 64bit devies (A7 or higher, iPhone 5s or later) , because it is based on a high specs 2010 PC MAME build. Anyway don't expect arcade games of the 90 to work at full speed. Some games are really bad optimized (like outrun or mk series). This is related to MAME build used, since it is targeted to high specs PC's as i said before. This version doesn't have an UML backend ARM dynamic recompiler, which means drivers based on high specs arcade CPUs won't be playable (it has not sense since this games will be slow in any case). +MAME for iOS was originally developed by David Valdeita (Seleuco), and is currently maintained and enhanced by dedicated enthusiasts (Yoshi Sugawara and Todd Laney). -**TIP** You can try to use speed hacks from the `MAME Configure` menu to make playables some games like CPS3 ones. +Since the original version, a large number of features have been added, including: -Said that, with a low end device, use at your own risk. I suggest you use iMAME4all (0.37b5) instead. Remember that games that can be emulated on both versions will run much faster on iMAME4all (0.37b5) than on MAME4iOS Reloaded (0.139u1), and will drain less battery. +- Metal Graphics Renderer +- Game Controller Support +- Touch Screen Lightgun Support +- Touch Screen Mouse Support +- On-screen Keyboard -This version emulates over 8000 different romsets. +See the official [web page](https://github.com/yoshisuga/MAME4iOS) for news, source code & additional information. -Please, try to understand that that with that amount of games, some will run better than others and some might not even run with MAME4iOS Reloaded. Please, don't email me asking for a specific game to run. - -After installing, place your MAME-titled zipped roms in the top folder, use iTunes file sharing, Files.app, built in WebServer or AirDrop (select Open in MAME4iOS). - -MAME4iOS Reloaded uses only '0.139u1' romset. - -Official [web page](https://github.com/yoshisuga/MAME4iOS) for news, source code & additional information: +Chat with us on [Discord](https://discord.gg/ZC6wkmU). To see [MAME license](#MAME4iOS-LICENSE), go to the end of this document. -## Features - -* Autorotate. -* Smoothed image. -* Scanline & TV Effect. -* Full screen, windowed. -* Selectable animated touch DPad, Digital Stick or Analog Stick. -* 1/6 touch buttons selectable. -* External controller support: [iCade (or compatible)](#icade_or_compatible), iControlPad, iMpulse (1 or 2 Players).] -* [Hardware keyboard](#hardware-keyboard) -* MiFI, Xbox, and DualShock [Game Controlers](#game-controlers) -* [TV-OUT](#tv-out) -* [iCloud Import, Export, and Sync.](#iCloud) -* [Simple *smart* romset install](#ROM-INSTALLATION) - -... and more. - ## CONTROLS The emulator controls are the following ones: @@ -61,7 +41,7 @@ The emulator controls are the following ones: **Button EXIT** Exit to selection menu to select another game. -**Button MENU** Open MAME4iOS menu, global settings. +**Button MENU** Open $(TARGET) menu, global settings. **NOTE** To type OK when MAME requires it, press LEFT and then RIGHT. @@ -72,10 +52,10 @@ The emulator controls are the following ones: - **Linear** Apply a smoothing image filter over the emulator screen. **Skin** choose the artwork and layout of the onscreen controlls. -- **Default** - the default MAME4iOS look. +- **Default** - the default $(TARGET) look. - **Light Border** - Default + a bright thick border - **Dark Border** - Default + a think dark border -- **Classic** - the old MAME4iOS look. +- **Classic** - the old $(TARGET) look. **Screen Shader** effect to apply to the emulator screen. - **None** dont use any effect. @@ -91,7 +71,7 @@ The emulator controls are the following ones: **Full Screen** Uses all available screen or shows the emulator windowed. -**Full Screen with Controler** automaticly enters Full Screen when a controler, keyboard, iCade is detected. +**Full Screen with Controller** automaticly enters Full Screen when a controler, keyboard, iCade is detected. **Keep Aspect Ratio** 'Enabled' keeps the aspect ratio; 'Disabled' will use all available screen. @@ -141,7 +121,7 @@ The emulator controls are the following ones: ## FAVORITES -You can mark (or unmark) your ROMS in the MAME4iOS game selection window as favorites by long pressing to get a context menu. +You can mark (or unmark) your ROMS in the $(TARGET) game selection window as favorites by long pressing to get a context menu. You can mark (or unmark) your ROMS in the MAME DOS MENU by pressing the X button. A favorite ROM appears in blue in the game list. The favorites are saved to the file: Favorites.ini. This file is compatible with the standard MAME Favorites.ini file format so you can copy this over from your PC version of MAME to the iOS version. @@ -150,7 +130,7 @@ when you make a mistake and need to undo ### Global Settings Reset (aka Factory Reset) `Settings` > `Reset to Defaults` -* restore all MAME4iOS settings to default. +* restore all $(TARGET) settings to default. * delete Recent and Favorite games. * delete all cached Title Images. * delete all MAME key mappings or settings. @@ -166,25 +146,14 @@ context menu, select `Delete`, then choose `Delete Settings`. ### Delete Game You can also remove a game totally, context menu `Delete`, choose `Delete All Files` -## iCloud -Store your favorite games in the cloud. - -When MAME4iOS is built with a CloudKit entitlement you will see a new iCloud section in Settings -* **Export to iCloud** will copy all local ROM, Artwork, State files up to the *Cloud*. (Only files not already uploaded or have changed are copied) -* **Import from iCloud** will copy down files from the Cloudkit database that dont exist on device, or have been updated. -* **Sync with iCloud** will do both a Import and Export. -* **Erase iCloud** can be used to remove all files in **iCloud**. -**NOTE** files are *never* deleted, only ever copied. (execept for `Erase iCloud`) - ## Hardware keyboard handle input from a hardware keyboard, the following are examples of hardware keyboards. * a USB or Bluetooth keyboard connected to a iOS device or AppleTV * Apple Smart Keyboard connected to an iPad -* macOS keyboard when debugging in Xcode simulator -below is a list of a small subset of the keys supported by MAME4iOS, for a full list look [here](https://docs.mamedev.org/usingmame/defaultkeys.html). +below is a list of a small subset of the keys supported by $(TARGET), for a full list look [here](https://docs.mamedev.org/usingmame/defaultkeys.html). | | | -|- @@ -204,7 +173,7 @@ below is a list of a small subset of the keys supported by MAME4iOS, for a full ⌘+DELETE | MAME toggle UI MODE (aka SCRLOCK) RETURN | MAME UI SELECT (aka 🅐) -These keys are specific to `MAME4iOS` +These keys are specific to `$(TARGET)` | | | -|- @@ -319,12 +288,12 @@ MENU+RIGHT |Load State ② | | | ---------------- |------------- MENU |Game Context Menu -OPTION |MAME4iOS Settings +OPTION |$(TARGET) Settings A |Play ## Multiplayer game start using game controllers -You can start a multiplayer game (1,2,3 or 4) players from the MAME4iOS menu. +You can start a multiplayer game (1,2,3 or 4) players from the $(TARGET) menu. If a user inserts a COIN or hits START with a game controller, it will be interpeted as a COIN/START for that player. @@ -340,7 +309,7 @@ MENU+R2|Player 2 SELECT You can save or load game states by pressing the MENU button when you are gaming, and select save or load state option. Also you can press button MENU+UP (Load) or MENU+DOWN (Save) on a external controller. ## Siri Remote -MAME4iOS is now usable on a AppleTV using only the stock Siri Remote. You can only play games that use only the A and B buttons. +$(TARGET) is now usable on a AppleTV using only the stock Siri Remote. You can only play games that use only the A and B buttons. to start playing a game, hit `MENU` and select `1 Player Start` from the list. @@ -349,12 +318,12 @@ to start playing a game, hit `MENU` and select `1 Player Start` from the list. TRACKPAD MOVE | emulate a dpad or joystick TRAKPAD CLICK | A button PLAY | B button -MENU | bring up the MAME4iOS menu +MENU | bring up the $(TARGET) menu ## iCADE (or compatible) -The best way to use iCade with MAME4iOS is in fullscreen portrait mode, hit the option button and choose options. The onscreen controls will fade out when you start using the iCade buttons. Tap the screen to get MAME4iOS menu. +The best way to use iCade with $(TARGET) is in fullscreen portrait mode, hit the option button and choose options. The onscreen controls will fade out when you start using the iCade buttons. Tap the screen to get $(TARGET) menu. If the iCade is off (the fake coin slot light is off) just hit an iCade button or move the joystick.(you must have paired the iCade via bluetooth before) @@ -362,39 +331,26 @@ Thanks to Todd Laney for sending me patches, and Martijn Bosschaart who has supp ## iMpulse -MAME4iOS works correctly out of the box for iMpulse, also has built-in support for local multiplayer (TwiMpulse). Anayway, if you need to redefine second player buttons, you should press coin (left shoulder button) before so MAME4iOS initializes second iMpulse controller. +$(TARGET) works correctly out of the box for iMpulse, also has built-in support for local multiplayer (TwiMpulse). Anayway, if you need to redefine second player buttons, you should press coin (left shoulder button) before so $(TARGET) initializes second iMpulse controller. ## XInput Controller -If you have an XInput compatible controller, use `Settings` > `Accessibility` > `Switch Control` > `Switches` > `Bluetooth Devices` to pair controller, then use as normal in MAME4iOS. +If you have an XInput compatible controller, use `Settings` > `Accessibility` > `Switch Control` > `Switches` > `Bluetooth Devices` to pair controller, then use as normal in $(TARGET). ## TV-OUT To connect an iPad or iPhone to your TV or a projector, you can either use the Apple HDMI, Component AV Cable, Apple Composite AV Cable, Apple Dock Connector to VGA Adapter, or other compatible cable. -When the cable is connected to a TV or projector, MAME4iOS will automatically use it when playing a game. +When the cable is connected to a TV or projector, $(TARGET) will automatically use it when playing a game. ## ROM INSTALLATION -use `Import...`, `Start Web Server`, or `Import from iCloud` from `MAME4iOS` `Settings` +use `Import...`, `Start Web Server`, or `Import from iCloud` from `$(TARGET)` `Settings` ## MANUAL ROM INSTALLATION -use iTunes file sharing (if your MAME4iOS build has it available) or Files.app, or a 3rd party app like iFunBox or iExplorer to copy ROMs on sandboxed MAME4iOS 'Documents' folder: - -Step 1\. Downloaded iFunBox (or a similar utility) and plug your iOS device into your computer. - -Step 2\. Launch iFunBox and select your iOS device on the left hand side. - -Step 3\. click on apps icon. Now you should see a list of all of your device’s applications. Locate MAME4iOS, click it, and select Documents. - -Step 4\. And that’s all there is to it. Move your ROMs into this folder, launch MAME4iOS, and start playing! - -## Mixing 139 and 2xx ROMs -Some `romsets` are not compatible between MAME 139 and newer versions, the best way to use both `romsets` at the same time is to make sure the newer ones are stored in the `7z` format and the 139 ones in the `zip` format. This way both files can co-exist. - - +Use the Files app to manually add ROM files to the `roms` folder in the $(TARGET) folder. ## DIRECTORIES @@ -403,7 +359,6 @@ Some `romsets` are not compatible between MAME 139 and newer versions, the best `artwork/` | Artwork directory `titles/` | Title images directory `cfg/` | MAME configuration files directory -`hi/` | Hiscores directory (MAME 139) `hiscore/` | Hiscores directory `nvram/` | NVRAM files directory `roms/ `| ROMs directory @@ -414,12 +369,6 @@ Some `romsets` are not compatible between MAME 139 and newer versions, the best ## SUPPORTED GAMES -The 139 version of `MAME4iOS` requires '0.139u1' compatible [romsets](https://archive.org/details/MAME_2010_full_nonmerged_romsets). - -The latest version of `MAME4iOS` requires romsets compatible with the latest `MAME` version. - -You can find `MAME` version `MAME4iOS` is using by opening `Settings`. - Games (zip files) have to be imported into the `roms/` folder. ## ROM NAMES @@ -434,7 +383,7 @@ The sound samples are used to get complete sound in some of the oldest games. Th Starting with the release of MAME 0.107 in July 2006, thanks to Aaron Giles, MAME supports hi-resolution artwork for bezels, backdrops, overlays, marquees, control panels, instruction cards, etc., and includes a new file format for the layout (.lay) -Save these [files](http://mameworld.info/mrdo/mame_artwork.php) to your MAME4iOS `artwork` directory, or import via AirDrop. +Save these [files](http://mameworld.info/mrdo/mame_artwork.php) to your $(TARGET) `artwork` directory, or import via AirDrop. ## ORIGINAL CREDITS @@ -442,19 +391,11 @@ MAME 0.139u1 original version by Nicola Salmoria and the MAME Team. ## PORT CREDITS -Port to iOS by David Valdeita (Seleuco) +Original port to iOS by David Valdeita (Seleuco) -## DEVELOPMENT +Ongoing maintenance, enhancements and modernization by: Yoshi Sugawara and Todd Laney -* 2020-02-26 Version 2020.1Rebirth -* 2013-04-05 Version 1.6\. Added Peer to peer netplay multiplayer over WI-FI or Bluetooth. Options menu reworked. Added Vector defaults options. Added Emulation speed and thread type options. Sixaxis fixes. Some other bug fixes. -* 2013-04-05 Version 1.5\. Added native l2cap bluetooth support for up to 4 PS3 Sixaxis controllers (you must store the Bluetooth address of your iOS device in your controller with a 3rd party utility like SixaxisPairTool). Added button and stick size selectors. Fixed permissions errors when creating files on jailbroken devices. Bluetooth manager bug fixes. -* 2013-03-17 Version 1.4\. Added in app touch layout customization. Added hiscores saving (MKChamp patch). Added switch to force 60Hz video for smoother gameplay in some games (use with caution since could broke other games like cave ones). Added autofire with different speeds. Added threaded video and thread priority switches. Fixed some anonymous timers on sega and cave drivers to fix save states problems (AWJ patch). Fixed 2nd controller mapping issues. Fixed simultaneous analog and digital input on external controllers. Added support for newer Wiimotes. -* 2013-02-09 Version 1.3.1 Minor bug fixes. Updated to Jailbroken devices. -* 2013-01-14 Version 1.3\. Added iPhone 5 support. Universal armv7+armv7s binary. Code refactoring for iOS 6\. Added game filtering (manufacturer, driver source, year, category, keyword, clones). Added favorites. Added option to delete games in rom manager. Added iTunes file sharing to upload roms. Added auto selection for 1-6 buttons & 2-8 ways stick. Added iMpulse controller support (+ TwiMpulse). Added low latency audio option. Improved rom manager. A lot of bug fixes. -* 2012-06-02 Version 1.2\. Fixed compatibility issue with iOS 5.1.1 jailbreak. Added local multiplayer (up to 4 players). Added true analog control (selectable as option). Fixed Taito X system. Added P1 Player as P2,P3,P4 input option. Some bug fixes. -* 2012-04-08 Version 1.1\. Upgraded to MAME 0.139u1\. Added 4/3, pixel aspect video aspects (now works MAME menu aspect ratio selector), improved iPad touch control layout, improved ROM manager, Added emulated resolution selector to improve artwork rendering, added configuration input menus, added missing options like frameskip. -* 2012-03-09 Version 1.0 WIP. First version. +Custom Metal Shaders provided by: MrJ ## KNOWN PROBLEMS @@ -469,13 +410,9 @@ Port to iOS by David Valdeita (Seleuco) Retina skin and touch control layout thanks to Bryn Thompson. -## Thanks - -Todd Laney for sending me iCade patches, and Martijn Bosschaart for support me with an iCade. - -## MAME4iOS LICENSE +## $(TARGET) LICENSE -MAME4iOS is released under a dual-license (GPL / MAME license) of your choice. Under the GPL license in addition you have some extra rights granted by a special license exception which allow you to link the MAME4iOS GPL source with the not GPL MAME source. The exception also gives you the rights to eliminate it if you don't like it or if you want to include the MAME4iOS source in another GPL program. So, MAME4iOS is 100% GPL. You can more easily think at it as a sort of double license. A GPL or a GPL + exception. You have all the rights of the GPL, and, if you want, some others. The only limitation is for MAME4iOS. MAME4iOS cannot include external GPL source without the explicit permission of the source copyright holder. +$(TARGET) is released under a dual-license (GPL / MAME license) of your choice. Under the GPL license in addition you have some extra rights granted by a special license exception which allow you to link the $(TARGET) GPL source with the not GPL MAME source. The exception also gives you the rights to eliminate it if you don't like it or if you want to include the $(TARGET) source in another GPL program. So, $(TARGET) is 100% GPL. You can more easily think at it as a sort of double license. A GPL or a GPL + exception. You have all the rights of the GPL, and, if you want, some others. The only limitation is for $(TARGET). $(TARGET) cannot include external GPL source without the explicit permission of the source copyright holder. ## MAME LICENSE diff --git a/iOS-res/plugins.zip b/iOS-res/plugins.zip index 29038080..de4e69cf 100644 Binary files a/iOS-res/plugins.zip and b/iOS-res/plugins.zip differ diff --git a/iOS/EmulatorController.m b/iOS/EmulatorController.m index 36b61acc..a4bdb090 100644 --- a/iOS/EmulatorController.m +++ b/iOS/EmulatorController.m @@ -76,7 +76,6 @@ #import "WebServer.h" #import "Alert.h" #import "ZipFile.h" -#import "SteamController.h" #import "SkinManager.h" #import "CloudSync.h" #import "SoftwareList.h" @@ -302,8 +301,6 @@ -(void)toggleFullScreen:(id)sender; static NSInteger g_settings_file_count; static Options* g_settings_options; -static BOOL g_bluetooth_enabled; - static EmulatorController *sharedInstance = nil; static const int buttonPressReleaseCycles = 2; @@ -521,7 +518,7 @@ int run_mame(char* system, char* type, char* game, char* options) #undef ARG2 } -static void init_pause() +static void init_pause(void) { g_emulation_paused_cond = [[NSCondition alloc] init]; } @@ -535,7 +532,7 @@ static void change_pause(int pause) [g_emulation_paused_cond unlock]; } -static void check_pause() +static void check_pause(void) { [g_emulation_paused_cond lock]; while (g_emulation_paused == PAUSE_THREAD) @@ -744,7 +741,14 @@ void m4i_game_list(myosd_game_info* game_info, int game_count) NSString* year = @(game_info[i].year ?: ""); if ([year isEqualToString:@"0"]) year = @""; + NSString *driver = [@(game_info[i].source_file ?: "").lastPathComponent stringByDeletingPathExtension]; +#if TARGET_APPSTORE + // App Store release: don't include pong/breakout to avoid copyright issues + if ( [driver isEqualToString:@"pong"] || [driver isEqualToString:@"breakout"]) + continue; +#endif + GameInfo* game = [[GameInfo alloc] initWithDictionary:@{ kGameInfoType: type, kGameInfoName: @(game_info[i].name), @@ -753,7 +757,7 @@ void m4i_game_list(myosd_game_info* game_info, int game_count) kGameInfoParent: parent, kGameInfoManufacturer:@(game_info[i].manufacturer), kGameInfoCategory: find_category(@(game_info[i].name), parent), - kGameInfoDriver: [@(game_info[i].source_file ?: "").lastPathComponent stringByDeletingPathExtension], + kGameInfoDriver: driver, kGameInfoSoftwareMedia:software_list, kGameInfoScreen: screens[(game_info[i].flags & MYOSD_GAME_INFO_VERTICAL) ? 1 : 0 + (game_info[i].flags & MYOSD_GAME_INFO_VECTOR) ? 2 : 0 + @@ -762,6 +766,7 @@ void m4i_game_list(myosd_game_info* game_info, int game_count) #ifdef DEBUG XGameInfo* xgame = [[XGameInfo alloc] initWithDictionary:game.gameDictionary]; + #pragma unused(xgame) #endif NSArray* software = @[]; if (software_list.length != 0) @@ -775,7 +780,8 @@ void m4i_game_list(myosd_game_info* game_info, int game_count) [games addObject:game]; [games addObjectsFromArray:software]; } - + +#if !TARGET_APPSTORE NSString* mame_version = [@((const char *)myosd_get(MYOSD_VERSION_STRING) ?: "") componentsSeparatedByString:@" ("].firstObject; // add a *special* system game that will run the DOS MAME menu. @@ -787,6 +793,7 @@ void m4i_game_list(myosd_game_info* game_info, int game_count) kGameInfoYear:@"1996", kGameInfoManufacturer:@"MAMEDev and contributors", }]]; +#endif // give the list to the main thread to display to user [sharedInstance performSelectorOnMainThread:@selector(chooseGame:) withObject:games waitUntilDone:FALSE]; @@ -806,7 +813,7 @@ void m4i_game_start(myosd_game_info* info) myosd_isLCD = (info->flags & MYOSD_GAME_INFO_LCD) != 0; } -void m4i_game_stop() +void m4i_game_stop(void) { NSLog(@"GAME STOP"); myosd_inGame = 0; @@ -960,7 +967,7 @@ void mame_load_state(int slot) void mame_save_state(int slot) { NSCParameterAssert(slot == 1 || slot == 2); - push_mame_keys(MYOSD_KEY_LSHIFT, MYOSD_KEY_LOADSAVE, (slot == 1) ? MYOSD_KEY_1 : MYOSD_KEY_2, 0); + push_mame_keys(MYOSD_KEY_F6, (slot == 1) ? MYOSD_KEY_1 : MYOSD_KEY_2, 0, 0); } - (void)presentPopup:(UIViewController *)viewController from:(UIView*)view animated:(BOOL)flag completion:(void (^)(void))completion { @@ -1876,17 +1883,6 @@ -(void)viewDidLoad{ // always enable Keyboard for hardware keyboard support keyboardView.active = YES; - // see if bluetooth is enabled... - - if (@available(iOS 13.1, tvOS 13.0, *)) - g_bluetooth_enabled = CBCentralManager.authorization == CBManagerAuthorizationAllowedAlways; - else if (@available(iOS 13.0, *)) - g_bluetooth_enabled = FALSE; // authorization is not in iOS 13.0, so no bluetooth for you. - else - g_bluetooth_enabled = TRUE; // pre-iOS 13.0, bluetooth allways. - - NSLog(@"BLUETOOTH ENABLED: %@", g_bluetooth_enabled ? @"YES" : @"NO"); - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(gameControllerConnected:) name:GCControllerDidConnectNotification object:nil]; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(gameControllerDisconnected:) name:GCControllerDidDisconnectNotification object:nil]; @@ -2476,7 +2472,6 @@ -(void)buildHUD { - (void)resetUI { NSLog(@"RESET UI (MAME VIDEO MODE CHANGE)"); - g_joy_used = 0; // use the touch ui, until a countroller is used. [self changeUI]; } @@ -2603,7 +2598,7 @@ static void push_mame_keys(NSUInteger key1, NSUInteger key2, NSUInteger key3, NS } // flush any pending keys or buttons -static void push_mame_flush() +static void push_mame_flush(void) { [g_mame_buttons_lock lock]; [g_mame_buttons removeAllObjects]; @@ -2657,8 +2652,7 @@ static int handle_buttons(myosd_input_state* myosd) if (myosd->keyboard[key] == 0) { myosd->keyboard[key] = 0x80; - if (g_mame_key != MYOSD_KEY_ESC) - g_mame_buttons_tick = buttonPressReleaseCycles; // keep key DOWN for this long. + g_mame_buttons_tick = buttonPressReleaseCycles; // keep key DOWN for this long. } else { if (key != MYOSD_KEY_LSHIFT && key != MYOSD_KEY_LCONTROL) { @@ -4576,7 +4570,7 @@ -(BOOL)moveROM:(NSString*)romName progressBlock:(void (^)(double progress, NSStr int __block numWAV = 0; int __block numFiles = 0; BOOL result = TRUE; - + if ([ZIP_FILE_TYPES containsObject:romExt]) { result = [ZipFile enumerate:romPath withOptions:ZipFileEnumFiles usingBlock:^(ZipFileInfo* info) { @@ -4595,6 +4589,13 @@ -(BOOL)moveROM:(NSString*)romName progressBlock:(void (^)(double progress, NSStr if ([skin_files containsObject:info.name.lastPathComponent]) numSKIN++; }]; + + // TODO: 7z support currenty broken, we cant look into 7z archives, but will just assume they are ROMSETs not software + if (!result && [romExt isEqualToString:@"7z"]) + { + NSLog(@"%@ is a 7Z (assuming valid, will treat as ROMSET)", romPath); + result = TRUE; + } } NSString* toPath = nil; @@ -5172,13 +5173,6 @@ -(void)setupGameControllers { [controllers addObject:controler]; } - // now add any Steam Controllers, these should always have a extendedGamepad profile - if (g_bluetooth_enabled) { - for (GCController* controler in SteamControllerManager.sharedManager.controllers) { - if (controler.extendedGamepad != nil) - [controllers addObject:controler]; - } - } // only handle upto NUM_JOY (non Siri Remote) controllers if (controllers.count > MYOSD_NUM_JOY) { [controllers removeObjectsInRange:NSMakeRange(MYOSD_NUM_JOY,controllers.count - MYOSD_NUM_JOY)]; @@ -5675,8 +5669,6 @@ -(void)dumpDevice:(NSObject*)_device { -(void)scanForDevices{ [GCController startWirelessControllerDiscoveryWithCompletionHandler:nil]; - if (g_bluetooth_enabled) - [[SteamControllerManager sharedManager] scanForControllers]; } -(void)gameControllerConnected:(NSNotification*)notif{ @@ -6037,14 +6029,16 @@ -(void)chooseGame:(NSArray*)games { NSLog(@"ROMS: %@", [NSFileManager.defaultManager enumeratorAtPath:getDocumentPath(@"roms")].allObjects); NSLog(@"SOFTWARE: %@", [NSFileManager.defaultManager enumeratorAtPath:getDocumentPath(@"software")].allObjects); + // 4/14/24 TODO: commenting this out for App Store release since games are bundled +#if !TARGET_APPSTORE // NOTE: MAME 2xx has a bunch of "no-rom" arcade games, we need to check if `roms` is empty too NSInteger roms_count = [NSFileManager.defaultManager enumeratorAtPath:getDocumentPath(@"roms")].allObjects.count + [NSFileManager.defaultManager enumeratorAtPath:getDocumentPath(@"software")].allObjects.count; - + g_no_roms_found = [games count] <= 1 || roms_count <= 1; // software dir has a single .txt file when empty if (g_no_roms_found && !g_no_roms_found_canceled) { NSLog(@"NO GAMES, ASK USER WHAT TO DO...."); - + // if iCloud is still initializing give it a litte time. if ([CloudSync status] == CloudSyncStatusUnknown) { NSLog(@"....WAITING FOR iCloud"); @@ -6056,6 +6050,8 @@ -(void)chooseGame:(NSArray*)games { [self runAddROMS:nil]; return; } +#endif + if (g_mame_game_error[0] != 0) { NSLog(@"ERROR RUNNING GAME %s", g_mame_game_error); @@ -6083,14 +6079,6 @@ -(void)chooseGame:(NSArray*)games { return; } - // now that we have passed the startup phase, check on and maybe re-enable bluetooth. - if (@available(iOS 13.1, tvOS 13.0, *)) { - if (!g_bluetooth_enabled && CBCentralManager.authorization == CBManagerAuthorizationNotDetermined) { - g_bluetooth_enabled = TRUE; - [self performSelectorOnMainThread:@selector(scanForDevices) withObject:nil waitUntilDone:NO]; - } - } - [self updateUserActivity:nil]; NSLog(@"GAMES: %@", games); diff --git a/iOS/Globals.h b/iOS/Globals.h index 0f0757bc..56dec06c 100644 --- a/iOS/Globals.h +++ b/iOS/Globals.h @@ -61,7 +61,7 @@ #define PRODUCT_NAME_LONG "MAME for iOS" #endif -static inline BOOL IsRunningOnMac() { +static inline BOOL IsRunningOnMac(void) { #if TARGET_OS_MACCATALYST return TRUE; #elif TARGET_OS_IOS && defined(__IPHONE_14_0) diff --git a/iOS/HelpController.m b/iOS/HelpController.m index 0e321e19..156faa32 100644 --- a/iOS/HelpController.m +++ b/iOS/HelpController.m @@ -151,6 +151,9 @@ - (void)loadHTML:(NSString*)name { // NSString* version = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; HTMLData = [HTMLData stringByReplacingOccurrencesOfString:@"$(APP_VERSION)" withString:version]; + + NSString *targetName = [[NSBundle.mainBundle infoDictionary] objectForKey:@"CFBundleName"]; + HTMLData = [HTMLData stringByReplacingOccurrencesOfString:@"$(TARGET)" withString:targetName]; // this last date Info.plist was modifed, if you do a clean build, or change the version, it is the build date. #if TARGET_OS_MACCATALYST diff --git a/iOS/KeyboardView.m b/iOS/KeyboardView.m index 107f948b..ffd2f1af 100644 --- a/iOS/KeyboardView.m +++ b/iOS/KeyboardView.m @@ -584,8 +584,8 @@ - (void)iCadeKey:(NSString *)text { } // emulate a analog joystick too. - myosd_pad_x = (myosd_pad_status & MYOSD_UP) ? +1.0 : (myosd_pad_status & MYOSD_DOWN) ? -1.0 : 0.0; - myosd_pad_y = (myosd_pad_status & MYOSD_RIGHT) ? +1.0 : (myosd_pad_status & MYOSD_LEFT) ? -1.0 : 0.0; + myosd_pad_x = (myosd_pad_status & MYOSD_RIGHT) ? +1.0 : (myosd_pad_status & MYOSD_LEFT) ? -1.0 : 0.0; + myosd_pad_y = (myosd_pad_status & MYOSD_UP) ? +1.0 : (myosd_pad_status & MYOSD_DOWN) ? -1.0 : 0.0; // only treat iCade as a controler when DPAD used for first time. if (g_joy_used == 0 && (myosd_pad_status & (MYOSD_DOWN|MYOSD_UP|MYOSD_RIGHT|MYOSD_LEFT))) diff --git a/iOS/OptionsController.m b/iOS/OptionsController.m index 8e265a83..e0e285d6 100644 --- a/iOS/OptionsController.m +++ b/iOS/OptionsController.m @@ -51,6 +51,7 @@ #import "EmulatorController.h" #import "ImageCache.h" #import "CloudSync.h" + #import "Alert.h" @implementation OptionsController diff --git a/xcode/MAME4iOS/ChooseGameController.m b/xcode/MAME4iOS/ChooseGameController.m index 43a3675d..d4b0282c 100644 --- a/xcode/MAME4iOS/ChooseGameController.m +++ b/xcode/MAME4iOS/ChooseGameController.m @@ -314,6 +314,10 @@ - (void)viewDidLoad // put search in navbar... self.navigationItem.searchController = _searchController; self.navigationItem.hidesSearchBarWhenScrolling = TRUE; + // keep the searchbar out of the main navbar area on iPad + if (@available(iOS 16.0, *)) { + self.navigationItem.preferredSearchBarPlacement = UINavigationItemSearchBarPlacementStacked; + } #else // tvOS if (self.navigationController != nil) { // force light-mode so our buttons look good in navbar @@ -1158,7 +1162,13 @@ -(void)headerTap:(UITapGestureRecognizer*)sender - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - return [_gameSectionTitles count]; + NSInteger count = [_gameSectionTitles count]; + if (count == 0) { + [self.collectionView showZeroState]; + } else { + self.collectionView.backgroundView = nil; + } + return count; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { @@ -1168,6 +1178,7 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSe if ([self isCollapsed:title]) return 0; NSInteger num = [_gameData[title] count]; + // restrict the Recent items to a single row, always if ([title isEqualToString:RECENT_GAMES_TITLE]) num = MIN(num, _layoutCollums); diff --git a/xcode/MAME4iOS/CloudSync.m b/xcode/MAME4iOS/CloudSync.m index 6f9553b5..f5bcf9ea 100644 --- a/xcode/MAME4iOS/CloudSync.m +++ b/xcode/MAME4iOS/CloudSync.m @@ -27,15 +27,6 @@ #import "EmulatorController.h" #import "Alert.h" -#if TARGET_OS_MACCATALYST -#import -#else -// declare *just* the Security APIs we need to check for entitlments on iOS -typedef CFTypeRef SecTaskRef; -extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator); -extern CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error); -#endif - #define DebugLog 1 #if DebugLog == 0 || !defined(DEBUG) #define NSLog(...) (void)0 @@ -71,23 +62,17 @@ +(NSString*)cloudIdentifier { return [NSString stringWithFormat:@"iCloud.%@", NSBundle.mainBundle.bundleIdentifier]; } -// use the Security framework to see if we have the iCloud entitlement +(BOOL)isEntitled { - SecTaskRef task = SecTaskCreateFromSelf(NULL); - if (task == NULL) - return FALSE; - CFTypeRef val = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.developer.icloud-services"), NULL); - CFRelease(task); - if (val == NULL) - return FALSE; - CFRelease(val); +#if ENTITLEMENTS_TYPE == -Full return TRUE; +#else + return FALSE; +#endif } +(void)updateCloudStatus { if (_container == nil) { - if ([self isEntitled]) { @try { // **NOTE** CKContainer.defaultContainer will throw a uncatchable exception, dont use it. diff --git a/xcode/MAME4iOS/GCDWebServer/Core/GCDWebServerFunctions.m b/xcode/MAME4iOS/GCDWebServer/Core/GCDWebServerFunctions.m index 8aed2c27..6e85d98d 100644 --- a/xcode/MAME4iOS/GCDWebServer/Core/GCDWebServerFunctions.m +++ b/xcode/MAME4iOS/GCDWebServer/Core/GCDWebServerFunctions.m @@ -44,7 +44,7 @@ static dispatch_queue_t _dateFormatterQueue = NULL; // TODO: Handle RFC 850 and ANSI C's asctime() format -void GCDWebServerInitializeFunctions() { +void GCDWebServerInitializeFunctions(void) { GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread if (_dateFormatterRFC822 == nil) { _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; diff --git a/xcode/MAME4iOS/MAME4iOS.xcconfig b/xcode/MAME4iOS/MAME4iOS.xcconfig index 2f4b6529..0d4341dd 100644 --- a/xcode/MAME4iOS/MAME4iOS.xcconfig +++ b/xcode/MAME4iOS/MAME4iOS.xcconfig @@ -17,8 +17,8 @@ ORG_IDENTIFIER = com.example // CHANGE this to your Organization Identifier. DEVELOPMENT_TEAM = ABC8675309 // CHANGE this to your Team ID. (or select in Xcode project editor) -CURRENT_PROJECT_VERSION = 2022.5 -MARKETING_VERSION = 2022.5 +CURRENT_PROJECT_VERSION = 2024.1 +MARKETING_VERSION = 2024.1 // 2. enable or disable entitlements // tvOS TopShelf and iCloud import/export require special app entitlements @@ -36,6 +36,9 @@ MAMELIB = libmame-139u1 // USE THIS LINE for a build of current latest MAME (via get-libmame.sh) // MAMELIB = libmame +// AppStore build +TARGET_APPSTORE = 0 // 1=AppStore build, 0=not + // let developer override with optional file. #include? "Developer.xcconfig" @@ -54,3 +57,8 @@ MAMELIB_TVOS[sdk=appletvsimulator*] = $(MAMELIB)-tvos-simulator.a PRODUCT_BUNDLE_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_NAME[sdk=macosx*] = MAME4mac + +// set special #defines +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) TARGET_APPSTORE=$(TARGET_APPSTORE) +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) ENTITLEMENTS_TYPE=$(ENTITLEMENTS_TYPE) + diff --git a/xcode/MAME4iOS/MAME4iOS.xcodeproj/project.pbxproj b/xcode/MAME4iOS/MAME4iOS.xcodeproj/project.pbxproj index 80449484..ec4bbcfe 100644 --- a/xcode/MAME4iOS/MAME4iOS.xcodeproj/project.pbxproj +++ b/xcode/MAME4iOS/MAME4iOS.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 920BBDEF201FC29700AE7D50 /* dpad.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BBDED201FC29700AE7D50 /* dpad.png */; }; 920BBDF220210C3800AE7D50 /* menu.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BBDF020210C3700AE7D50 /* menu.png */; }; + 920D975A2BCECB410038F00B /* UICollectionView+ZeroState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920D97592BCECB410038F00B /* UICollectionView+ZeroState.swift */; }; + 920D975B2BCECB410038F00B /* UICollectionView+ZeroState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920D97592BCECB410038F00B /* UICollectionView+ZeroState.swift */; }; 925ABCA61E46DCC500997182 /* KeyboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8D87DC1653121200685BC4 /* KeyboardView.m */; }; 925ABCA81E46DCC500997182 /* ListOptionController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE90575B166A89D000653872 /* ListOptionController.m */; }; 925ABCA91E46DCC500997182 /* OptionsController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE680C01635BB6100051BC2 /* OptionsController.m */; }; @@ -120,13 +122,13 @@ EF26AE0C241AA6900078FD30 /* FileItemProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = EF26AE0B241AA6900078FD30 /* FileItemProvider.m */; }; EF27F36823F73CD400B1E50A /* ZipFile.m in Sources */ = {isa = PBXBuildFile; fileRef = EF27F36323F73CD400B1E50A /* ZipFile.m */; }; EF27F36A23F7475100B1E50A /* libcompression.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EF27F36923F7475100B1E50A /* libcompression.tbd */; }; - EF286D34253CA930007DA6D3 /* CloudSync.m in Sources */ = {isa = PBXBuildFile; fileRef = EF286D33253CA930007DA6D3 /* CloudSync.m */; }; EF286D35253CA930007DA6D3 /* CloudSync.m in Sources */ = {isa = PBXBuildFile; fileRef = EF286D33253CA930007DA6D3 /* CloudSync.m */; }; EF286D59253CF171007DA6D3 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF286D58253CF171007DA6D3 /* CloudKit.framework */; }; EF286D5B253CF18D007DA6D3 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF286D5A253CF18D007DA6D3 /* CloudKit.framework */; }; EF29C8822401C73A00AFFA4E /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EF29C8802401C73900AFFA4E /* Launch Screen.storyboard */; }; EF301CF3240460F3000510FD /* ZipFile.m in Sources */ = {isa = PBXBuildFile; fileRef = EF27F36323F73CD400B1E50A /* ZipFile.m */; }; EF31447C27BB1E81002C3C6A /* ControllerButtonPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF31447B27BB1E81002C3C6A /* ControllerButtonPress.swift */; }; + EF3D60262BF6B0870028C7C4 /* CloudSync.m in Sources */ = {isa = PBXBuildFile; fileRef = EF286D33253CA930007DA6D3 /* CloudSync.m */; }; EF474B892791E42900578663 /* EmulatorController+EmulatorKeyboardSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF474B882791E42900578663 /* EmulatorController+EmulatorKeyboardSupport.swift */; }; EF474B8B2791E4CD00578663 /* EmulatorKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF474B8A2791E4CD00578663 /* EmulatorKeyboard.swift */; }; EF490D91277A5C8C00407865 /* history.dat.zip in Resources */ = {isa = PBXBuildFile; fileRef = EF490D90277A5C8C00407865 /* history.dat.zip */; }; @@ -162,14 +164,6 @@ EF8E4284249915F30049C84C /* lineTron.metal in Sources */ = {isa = PBXBuildFile; fileRef = EF8E4282249915F20049C84C /* lineTron.metal */; }; EF8E428B249FE5410049C84C /* ulTron.metal in Sources */ = {isa = PBXBuildFile; fileRef = EF8E428A249FE5410049C84C /* ulTron.metal */; }; EF8E428C249FE5410049C84C /* ulTron.metal in Sources */ = {isa = PBXBuildFile; fileRef = EF8E428A249FE5410049C84C /* ulTron.metal */; }; - EF8EAA86244F7F5D00DA02BB /* SteamControllerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA7F244F7F5D00DA02BB /* SteamControllerManager.m */; }; - EF8EAA87244F7F5D00DA02BB /* SteamControllerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA7F244F7F5D00DA02BB /* SteamControllerManager.m */; }; - EF8EAA88244F7F5D00DA02BB /* SteamController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA80244F7F5D00DA02BB /* SteamController.m */; }; - EF8EAA89244F7F5D00DA02BB /* SteamController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA80244F7F5D00DA02BB /* SteamController.m */; }; - EF8EAA8A244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA82244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m */; }; - EF8EAA8B244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA82244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m */; }; - EF8EAA8C244F7F5D00DA02BB /* SteamControllerInput.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA83244F7F5D00DA02BB /* SteamControllerInput.m */; }; - EF8EAA8D244F7F5D00DA02BB /* SteamControllerInput.m in Sources */ = {isa = PBXBuildFile; fileRef = EF8EAA83244F7F5D00DA02BB /* SteamControllerInput.m */; }; EF926C9B24BA36980048392B /* SkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EF926C9A24BA36980048392B /* SkinManager.m */; }; EF941F0024427D1200D58F7F /* OptionsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF941EFF24427D1200D58F7F /* OptionsTableViewController.m */; }; EF941F0124427D1200D58F7F /* OptionsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF941EFF24427D1200D58F7F /* OptionsTableViewController.m */; }; @@ -292,6 +286,7 @@ /* Begin PBXFileReference section */ 920BBDED201FC29700AE7D50 /* dpad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = dpad.png; path = "../../iOS-res/dpad.png"; sourceTree = ""; }; 920BBDF020210C3700AE7D50 /* menu.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = menu.png; path = "../../iOS-res/menu.png"; sourceTree = ""; }; + 920D97592BCECB410038F00B /* UICollectionView+ZeroState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+ZeroState.swift"; sourceTree = ""; }; 925ABCE21E46DCC500997182 /* MAME4iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MAME4iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 925ABCE41E46DD6E00997182 /* libmame.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmame.a; path = ../../libmame.a; sourceTree = ""; }; 926C76FD21F1C87700103EDE /* TVOptionsController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TVOptionsController.h; sourceTree = ""; }; @@ -450,16 +445,6 @@ EF8E427C249348220049C84C /* megaTron.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = megaTron.metal; sourceTree = ""; }; EF8E4282249915F20049C84C /* lineTron.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = lineTron.metal; sourceTree = ""; }; EF8E428A249FE5410049C84C /* ulTron.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = ulTron.metal; sourceTree = ""; }; - EF8EAA7E244F7F5D00DA02BB /* SteamControllerExtendedGamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SteamControllerExtendedGamepad.h; sourceTree = ""; }; - EF8EAA7F244F7F5D00DA02BB /* SteamControllerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SteamControllerManager.m; sourceTree = ""; }; - EF8EAA80244F7F5D00DA02BB /* SteamController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SteamController.m; sourceTree = ""; }; - EF8EAA81244F7F5D00DA02BB /* SteamControllerInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SteamControllerInput.h; sourceTree = ""; }; - EF8EAA82244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SteamControllerExtendedGamepad.m; sourceTree = ""; }; - EF8EAA83244F7F5D00DA02BB /* SteamControllerInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SteamControllerInput.m; sourceTree = ""; }; - EF8EAA84244F7F5D00DA02BB /* SteamController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SteamController.h; sourceTree = ""; }; - EF8EAA85244F7F5D00DA02BB /* SteamControllerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SteamControllerManager.h; sourceTree = ""; }; - EF8EAA8E244F822C00DA02BB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - EF8EAA8F244F822C00DA02BB /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; EF926C9924BA36980048392B /* SkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SkinManager.h; sourceTree = ""; }; EF926C9A24BA36980048392B /* SkinManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SkinManager.m; sourceTree = ""; }; EF941EFE24427D1200D58F7F /* OptionsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionsTableViewController.h; path = ../../iOS/OptionsTableViewController.h; sourceTree = ""; }; @@ -712,6 +697,7 @@ EF286D32253CA930007DA6D3 /* CloudSync.h */, EF286D33253CA930007DA6D3 /* CloudSync.m */, CEBFBC0E163C5BD300A05CD0 /* resources */, + 920D97592BCECB410038F00B /* UICollectionView+ZeroState.swift */, ); name = RELOADED; sourceTree = ""; @@ -745,7 +731,6 @@ EF8E427B24900FE40049C84C /* UIKit Classes */, 92A5338421EBDBAD0089FBB9 /* GCDWebServer */, 92A533CB21EBDDF20089FBB9 /* GCDWebUploader */, - EF8EAA7D244F7F5D00DA02BB /* SteamController */, CEE6808F1635BA7000051BC2 /* MAME4iOS */, 92ECB8EC21EA984E00D1E3D0 /* MAME4tvOS */, EFEEA4B325C9D41E00314132 /* TopShelf */, @@ -935,23 +920,6 @@ name = "UIKit Classes"; sourceTree = ""; }; - EF8EAA7D244F7F5D00DA02BB /* SteamController */ = { - isa = PBXGroup; - children = ( - EF8EAA8E244F822C00DA02BB /* README.md */, - EF8EAA8F244F822C00DA02BB /* LICENSE */, - EF8EAA7E244F7F5D00DA02BB /* SteamControllerExtendedGamepad.h */, - EF8EAA7F244F7F5D00DA02BB /* SteamControllerManager.m */, - EF8EAA80244F7F5D00DA02BB /* SteamController.m */, - EF8EAA81244F7F5D00DA02BB /* SteamControllerInput.h */, - EF8EAA82244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m */, - EF8EAA83244F7F5D00DA02BB /* SteamControllerInput.m */, - EF8EAA84244F7F5D00DA02BB /* SteamController.h */, - EF8EAA85244F7F5D00DA02BB /* SteamControllerManager.h */, - ); - path = SteamController; - sourceTree = ""; - }; EFA7571E2408308000A02AAB /* help */ = { isa = PBXGroup; children = ( @@ -1214,6 +1182,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EF3D60262BF6B0870028C7C4 /* CloudSync.m in Sources */, 92A533B121EBDBAD0089FBB9 /* GCDWebServerConnection.m in Sources */, EFB7B2192479FBD900AD96A4 /* MetalViewShaders.metal in Sources */, 925ABCA61E46DCC500997182 /* KeyboardView.m in Sources */, @@ -1233,8 +1202,6 @@ EFB7B21C247ACFE300AD96A4 /* MameShaders.metal in Sources */, 925ABCA91E46DCC500997182 /* OptionsController.m in Sources */, EFE003382638ACA000E42246 /* XmlFile.m in Sources */, - EF8EAA8A244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m in Sources */, - EF8EAA8C244F7F5D00DA02BB /* SteamControllerInput.m in Sources */, 92A533C021EBDBAD0089FBB9 /* GCDWebServerURLEncodedFormRequest.m in Sources */, 92A533D821EE601D0089FBB9 /* WebServer.m in Sources */, EFB3C6D624890F6200F44780 /* simpleCRT.metal in Sources */, @@ -1248,7 +1215,6 @@ 925ABCAD1E46DCC500997182 /* EmulatorController.m in Sources */, EF1741D6261BB2D600DE8386 /* GameInfo.m in Sources */, 925ABCAE1E46DCC500997182 /* HelpController.m in Sources */, - EF286D34253CA930007DA6D3 /* CloudSync.m in Sources */, 925ABCAF1E46DCC500997182 /* AnalogStick.m in Sources */, 925ABCB01E46DCC500997182 /* Bootstrapper.m in Sources */, EFE0033D2638D97E00E42246 /* SoftwareList.m in Sources */, @@ -1257,12 +1223,12 @@ EFB7B2172479FBD900AD96A4 /* MetalView.m in Sources */, 925ABCB21E46DCC500997182 /* LayoutView.m in Sources */, EFEEA4ED25CC8A2F00314132 /* MacMenu.m in Sources */, - EF8EAA86244F7F5D00DA02BB /* SteamControllerManager.m in Sources */, 92A533A521EBDBAD0089FBB9 /* GCDWebServerResponse.m in Sources */, EFE4B6AB2453D7F80009A6F1 /* InfoDatabase.m in Sources */, 925ABCB61E46DCC500997182 /* InputOptionController.m in Sources */, 92A533C321EBDBAD0089FBB9 /* GCDWebServerMultiPartFormRequest.m in Sources */, EF26AE0C241AA6900078FD30 /* FileItemProvider.m in Sources */, + 920D975A2BCECB410038F00B /* UICollectionView+ZeroState.swift in Sources */, EF7B232C23FD0469001FF51D /* (null) in Sources */, EF8E427D249348220049C84C /* megaTron.metal in Sources */, 92A533B421EBDBAD0089FBB9 /* GCDWebServerErrorResponse.m in Sources */, @@ -1271,7 +1237,6 @@ EFF9F82A2470755800DDA88C /* MetalScreenView.m in Sources */, EF19F22C235D1BDF00C8EE7F /* ChooseGameController.m in Sources */, EF926C9B24BA36980048392B /* SkinManager.m in Sources */, - EF8EAA88244F7F5D00DA02BB /* SteamController.m in Sources */, 92A533AE21EBDBAD0089FBB9 /* GCDWebServer.m in Sources */, EF51444A282C5C68001457B1 /* GameInfo.swift in Sources */, 92A5332321EB57F00089FBB9 /* Options.m in Sources */, @@ -1288,6 +1253,7 @@ 92A533BE21EBDBAD0089FBB9 /* GCDWebServerStreamedResponse.m in Sources */, EF8E4284249915F30049C84C /* lineTron.metal in Sources */, 92A533C421EBDBAD0089FBB9 /* GCDWebServerMultiPartFormRequest.m in Sources */, + 920D975B2BCECB410038F00B /* UICollectionView+ZeroState.swift in Sources */, EFB7B21A2479FBD900AD96A4 /* MetalViewShaders.metal in Sources */, 926C770A21F271A700103EDE /* ListOptionController.m in Sources */, 92A533C721EBDBAD0089FBB9 /* GCDWebServerDataRequest.m in Sources */, @@ -1296,7 +1262,6 @@ EF8E427E249348220049C84C /* megaTron.metal in Sources */, 92A5332621EBC8D00089FBB9 /* Bootstrapper.m in Sources */, 92A533A921EBDBAD0089FBB9 /* GCDWebServerRequest.m in Sources */, - EF8EAA8B244F7F5D00DA02BB /* SteamControllerExtendedGamepad.m in Sources */, EFF9F82B2470755800DDA88C /* MetalScreenView.m in Sources */, EF51444F28305E36001457B1 /* GameInfoCell.swift in Sources */, 92A533B221EBDBAD0089FBB9 /* GCDWebServerConnection.m in Sources */, @@ -1305,21 +1270,18 @@ 92ECB91921EAD57900D1E3D0 /* UIView+Toast.m in Sources */, EF19F22D235D1BDF00C8EE7F /* ChooseGameController.m in Sources */, 928F7B2E27F0223100377C40 /* CommandLineArgsHelper.swift in Sources */, - EF8EAA89244F7F5D00DA02BB /* SteamController.m in Sources */, 92A533A621EBDBAD0089FBB9 /* GCDWebServerResponse.m in Sources */, 92A533D921EE601D0089FBB9 /* WebServer.m in Sources */, EFE0033E2638D97E00E42246 /* SoftwareList.m in Sources */, EFB7B21D247ACFE300AD96A4 /* MameShaders.metal in Sources */, 92A533D421EBDDF20089FBB9 /* GCDWebUploader.m in Sources */, 92ECB91221EAA1B700D1E3D0 /* EmulatorController.m in Sources */, - EF8EAA87244F7F5D00DA02BB /* SteamControllerManager.m in Sources */, EF514448282815E4001457B1 /* GameInfoController.swift in Sources */, EF24BAFD24C786A900D9C55A /* SkinManager.m in Sources */, 92A533AC21EBDBAD0089FBB9 /* GCDWebServerFunctions.m in Sources */, EF51444B282C5C68001457B1 /* GameInfo.swift in Sources */, EF7B232D23FD0469001FF51D /* (null) in Sources */, 92A533AF21EBDBAD0089FBB9 /* GCDWebServer.m in Sources */, - EF8EAA8D244F7F5D00DA02BB /* SteamControllerInput.m in Sources */, 92A533B521EBDBAD0089FBB9 /* GCDWebServerErrorResponse.m in Sources */, EFE003392638ACA000E42246 /* XmlFile.m in Sources */, EFE4B6AC2453D7F80009A6F1 /* InfoDatabase.m in Sources */, @@ -1376,7 +1338,6 @@ ); HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = "MAME4iOS/MAME4iOS-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1414,7 +1375,6 @@ GCC_PREFIX_HEADER = "MAME4iOS/MAME4iOS-Prefix.pch"; HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = "MAME4iOS/MAME4iOS-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1501,7 +1461,6 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.4; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; }; name = Debug; @@ -1570,7 +1529,6 @@ SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(PROJECT_NAME)-Swift.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.4; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; }; name = Release; @@ -1627,6 +1585,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.4; VALIDATE_PRODUCT = YES; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; }; @@ -1678,6 +1637,7 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.4; VALIDATE_PRODUCT = YES; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; }; @@ -1716,7 +1676,6 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.4; }; name = Debug; }; @@ -1753,7 +1712,6 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.4; }; name = Release; }; diff --git a/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/MAME4iOS.xcscheme b/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/MAME4iOS.xcscheme index 36fed5fc..90332f92 100644 --- a/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/MAME4iOS.xcscheme +++ b/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/MAME4iOS.xcscheme @@ -27,15 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - @@ -54,8 +45,8 @@ diff --git a/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/xcschememanagement.plist b/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/xcschememanagement.plist index 775f9269..64b7dbee 100644 --- a/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/xcode/MAME4iOS/MAME4iOS.xcodeproj/xcuserdata/yoshi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,6 +10,11 @@ 1 MAME tvOS Debug.xcscheme_^#shared#^_ + + orderHint + 2 + + MAME tvOS Release (139u1).xcscheme orderHint 4 @@ -17,7 +22,7 @@ MAME tvOS Release.xcscheme_^#shared#^_ orderHint - 5 + 3 MAME4iOS 64-bit Debug.xcscheme_^#shared#^_ @@ -29,16 +34,41 @@ orderHint 3 + MAME4iOS Debug.xcscheme_^#shared#^_ + + orderHint + 5 + + MAME4iOS Release (139u1).xcscheme + + orderHint + 7 + + MAME4iOS Release.xcscheme_^#shared#^_ + + orderHint + 6 + MAME4iOS.xcscheme orderHint 0 + MAME4mac Release (139u1).xcscheme + + orderHint + 8 + MAME4tvOS.xcscheme orderHint 2 + TopShelf.xcscheme_^#shared#^_ + + orderHint + 9 + SuppressBuildableAutocreation diff --git a/xcode/MAME4iOS/MAME4iOS/Launch Screen.storyboard b/xcode/MAME4iOS/MAME4iOS/Launch Screen.storyboard index 052cf5ee..98513d1f 100644 --- a/xcode/MAME4iOS/MAME4iOS/Launch Screen.storyboard +++ b/xcode/MAME4iOS/MAME4iOS/Launch Screen.storyboard @@ -1,9 +1,9 @@ - + - + @@ -16,12 +16,6 @@ - @@ -30,10 +24,7 @@ - - - diff --git a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Base.entitlements b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Base.entitlements index 083d24e5..2861597d 100644 --- a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Base.entitlements +++ b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Base.entitlements @@ -2,17 +2,15 @@ - com.apple.security.app-sandbox - - com.apple.security.device.bluetooth - - com.apple.security.files.downloads.read-only - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - + com.apple.security.app-sandbox + + com.apple.security.files.downloads.read-only + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + diff --git a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Full.entitlements b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Full.entitlements index 0e39f992..4da8a03f 100644 --- a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Full.entitlements +++ b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Full.entitlements @@ -16,9 +16,7 @@ $(APP_GROUP_IDENTIFIER) - com.apple.security.app-sandbox - - com.apple.security.device.bluetooth + com.apple.security.app-sandbox com.apple.security.files.downloads.read-only diff --git a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Info.plist b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Info.plist index eda71c17..e56cde7c 100644 --- a/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Info.plist +++ b/xcode/MAME4iOS/MAME4iOS/MAME4iOS-Info.plist @@ -31,220 +31,220 @@ public.zip-archive - - CFBundleTypeExtensions - - 7z - 7Z - - CFBundleTypeMIMETypes - - application/x-7z-compressed - - CFBundleTypeName - 7-Zip Archive - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.7-zip.7-zip-archive - - - - CFBundleTypeName - MAME Disk Image - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.mamedev.disk-image - - CFBundleTypeExtensions - - chd - - - - CFBundleTypeName - MAME ROM Image - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.mamedev.rom-image - - CFBundleTypeExtensions - - ROM - rom - cue - iso - cdr - bin - a78 - a26 - a52 - sms - smc - fig - sfc - gb - gbc - sgb - gba - nes - fds - lnx - ngp - ngc - ngpc - npc - pce - ws - wsc - vb - n64 - z64 - v64 - gen - md - gg - smd - 32x - 32X - min - cdi - gdi - bios - d64 - t64 - p00 - prg - pgm - crt - dsk - woz - wav - col - unf - unif - - + + CFBundleTypeExtensions + + 7z + 7Z + + CFBundleTypeMIMETypes + + application/x-7z-compressed + + CFBundleTypeName + 7-Zip Archive + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.7-zip.7-zip-archive + + + + CFBundleTypeName + MAME Disk Image + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.mamedev.disk-image + + CFBundleTypeExtensions + + chd + + + + CFBundleTypeName + MAME ROM Image + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.mamedev.rom-image + + CFBundleTypeExtensions + + ROM + rom + cue + iso + cdr + bin + a78 + a26 + a52 + sms + smc + fig + sfc + gb + gbc + sgb + gba + nes + fds + lnx + ngp + ngc + ngpc + npc + pce + ws + wsc + vb + n64 + z64 + v64 + gen + md + gg + smd + 32x + 32X + min + cdi + gdi + bios + d64 + t64 + p00 + prg + pgm + crt + dsk + woz + wav + col + unf + unif + + + + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + MAME Disk Image + UTTypeIdentifier + org.mamedev.disk-image + UTTypeTagSpecification + + public.filename-extension + + chd + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + 7-Zip Archive + UTTypeIdentifier + org.7-zip.7-zip-archive + UTTypeTagSpecification + + public.filename-extension + + 7z + 7Z + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + MAME ROM Image + UTTypeIdentifier + org.mamedev.rom-image + UTTypeTagSpecification + + public.filename-extension + + ROM + rom + cue + iso + cdr + bin + a78 + a26 + a52 + sms + smc + fig + sfc + gb + gbc + sgb + gba + nes + fds + lnx + ngp + ngc + ngpc + npc + pce + ws + wsc + vb + n64 + z64 + v64 + gen + md + gg + smd + 32x + 32X + min + cdi + gdi + bios + d64 + t64 + p00 + prg + pgm + crt + dsk + woz + wav + col + unf + unif + + + - UTImportedTypeDeclarations - - - UTTypeConformsTo - - public.data - - UTTypeDescription - MAME Disk Image - UTTypeIdentifier - org.mamedev.disk-image - UTTypeTagSpecification - - public.filename-extension - - chd - - - - - UTTypeConformsTo - - public.data - - UTTypeDescription - 7-Zip Archive - UTTypeIdentifier - org.7-zip.7-zip-archive - UTTypeTagSpecification - - public.filename-extension - - 7z - 7Z - - - - - UTTypeConformsTo - - public.data - - UTTypeDescription - MAME ROM Image - UTTypeIdentifier - org.mamedev.rom-image - UTTypeTagSpecification - - public.filename-extension - - ROM - rom - cue - iso - cdr - bin - a78 - a26 - a52 - sms - smc - fig - sfc - gb - gbc - sgb - gba - nes - fds - lnx - ngp - ngc - ngpc - npc - pce - ws - wsc - vb - n64 - z64 - v64 - gen - md - gg - smd - 32x - 32X - min - cdi - gdi - bios - d64 - t64 - p00 - prg - pgm - crt - dsk - woz - wav - col - unf - unif - - - - - CFBundleExecutable + CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) @@ -284,8 +284,8 @@ GCSupportsControllerUserInteraction - LSApplicationCategoryType - public.app-category.arcade-games + LSApplicationCategoryType + public.app-category.arcade-games LSRequiresIPhoneOS LSSupportsOpeningDocumentsInPlace @@ -295,8 +295,6 @@ NSAllowsArbitraryLoads - NSBluetoothAlwaysUsageDescription - We need bluetooth for steam controllers NSUserActivityTypes $(PRODUCT_BUNDLE_IDENTIFIER).play @@ -335,7 +333,7 @@ UIApplicationSupportsIndirectInputEvents - UIUserInterfaceStyle - Dark + UIUserInterfaceStyle + Dark diff --git a/xcode/MAME4iOS/MAME4tvOS/Info.plist b/xcode/MAME4iOS/MAME4tvOS/Info.plist index bdded244..fa400b53 100644 --- a/xcode/MAME4iOS/MAME4tvOS/Info.plist +++ b/xcode/MAME4iOS/MAME4tvOS/Info.plist @@ -44,8 +44,8 @@ GCSupportsControllerUserInteraction - LSApplicationCategoryType - public.app-category.arcade-games + LSApplicationCategoryType + public.app-category.arcade-games LSRequiresIPhoneOS LSSupportsOpeningDocumentsInPlace @@ -55,8 +55,6 @@ NSAllowsArbitraryLoads - NSBluetoothAlwaysUsageDescription - We need bluetooth for steam controllers UIFileSharingEnabled UILaunchStoryboardName @@ -67,241 +65,241 @@ UIUserInterfaceStyle Dark - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - zip - ZIP - - CFBundleTypeMIMETypes - - application/zip - - CFBundleTypeName - ZIP Archive - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - public.zip-archive - - - - CFBundleTypeExtensions - - 7z - 7Z - - CFBundleTypeMIMETypes - - application/x-7z-compressed - - CFBundleTypeName - 7-Zip Archive - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.7-zip.7-zip-archive - - - - CFBundleTypeName - MAME Disk Image - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.mamedev.disk-image - - CFBundleTypeExtensions - - chd - - - - CFBundleTypeName - MAME ROM Image - CFBundleTypeRole - Viewer - LSHandlerRank - Alternate - LSItemContentTypes - - org.mamedev.rom-image - - CFBundleTypeExtensions - - ROM - rom - cue - iso - cdr - bin - a78 - a26 - a52 - sms - smc - fig - sfc - gb - gbc - sgb - gba - nes - fds - lnx - ngp - ngc - ngpc - npc - pce - ws - wsc - vb - n64 - z64 - v64 - gen - md - gg - smd - 32x - 32X - min - cdi - gdi - bios - d64 - t64 - p00 - prg - pgm - crt - dsk - woz - wav - col - unf - unif - - - - UTImportedTypeDeclarations - - - UTTypeConformsTo - - public.data - - UTTypeDescription - MAME Disk Image - UTTypeIdentifier - org.mamedev.disk-image - UTTypeTagSpecification - - public.filename-extension - - chd - - - - - UTTypeConformsTo - - public.data - - UTTypeDescription - 7-Zip Archive - UTTypeIdentifier - org.7-zip.7-zip-archive - UTTypeTagSpecification - - public.filename-extension - - 7z - 7Z - - - - - UTTypeConformsTo - - public.data - - UTTypeDescription - MAME ROM Image - UTTypeIdentifier - org.mamedev.rom-image - UTTypeTagSpecification - - public.filename-extension - - ROM - rom - cue - iso - cdr - bin - a78 - a26 - a52 - sms - smc - fig - sfc - gb - gbc - sgb - gba - nes - fds - lnx - ngp - ngc - ngpc - npc - pce - ws - wsc - vb - n64 - z64 - v64 - gen - md - gg - smd - 32x - 32X - min - cdi - gdi - bios - d64 - t64 - p00 - prg - pgm - crt - dsk - woz - wav - col - unf - unif - - - - + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + zip + ZIP + + CFBundleTypeMIMETypes + + application/zip + + CFBundleTypeName + ZIP Archive + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + public.zip-archive + + + + CFBundleTypeExtensions + + 7z + 7Z + + CFBundleTypeMIMETypes + + application/x-7z-compressed + + CFBundleTypeName + 7-Zip Archive + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.7-zip.7-zip-archive + + + + CFBundleTypeName + MAME Disk Image + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.mamedev.disk-image + + CFBundleTypeExtensions + + chd + + + + CFBundleTypeName + MAME ROM Image + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + org.mamedev.rom-image + + CFBundleTypeExtensions + + ROM + rom + cue + iso + cdr + bin + a78 + a26 + a52 + sms + smc + fig + sfc + gb + gbc + sgb + gba + nes + fds + lnx + ngp + ngc + ngpc + npc + pce + ws + wsc + vb + n64 + z64 + v64 + gen + md + gg + smd + 32x + 32X + min + cdi + gdi + bios + d64 + t64 + p00 + prg + pgm + crt + dsk + woz + wav + col + unf + unif + + + + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + MAME Disk Image + UTTypeIdentifier + org.mamedev.disk-image + UTTypeTagSpecification + + public.filename-extension + + chd + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + 7-Zip Archive + UTTypeIdentifier + org.7-zip.7-zip-archive + UTTypeTagSpecification + + public.filename-extension + + 7z + 7Z + + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + MAME ROM Image + UTTypeIdentifier + org.mamedev.rom-image + UTTypeTagSpecification + + public.filename-extension + + ROM + rom + cue + iso + cdr + bin + a78 + a26 + a52 + sms + smc + fig + sfc + gb + gbc + sgb + gba + nes + fds + lnx + ngp + ngc + ngpc + npc + pce + ws + wsc + vb + n64 + z64 + v64 + gen + md + gg + smd + 32x + 32X + min + cdi + gdi + bios + d64 + t64 + p00 + prg + pgm + crt + dsk + woz + wav + col + unf + unif + + + + diff --git a/xcode/MAME4iOS/SteamController/LICENSE b/xcode/MAME4iOS/SteamController/LICENSE deleted file mode 100644 index 93ba7707..00000000 --- a/xcode/MAME4iOS/SteamController/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2018 Jesús A. Álvarez - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/xcode/MAME4iOS/SteamController/README.md b/xcode/MAME4iOS/SteamController/README.md deleted file mode 100644 index 7e3b3896..00000000 --- a/xcode/MAME4iOS/SteamController/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# SteamController - -Drop-in support for Steam Controllers for iOS and tvOS. - -For information about how to use a Steam Controller in Bluetoth LE mode, see [Steam Controller BLE](https://support.steampowered.com/kb_article.php?ref=7728-QESJ-4420#switch). - -forked from [here](https://github.com/zydeco/SteamController) - -## Example - -To run the example project, clone the repo, and run the SteamControllerTestApp target. - -![Screenshot](screenshot.png) - -In the example app, power on your controller (in BLE or BLE pairing mode) and press Scan. Connected controllers will appear in the list, and the UI will reflect the state of the controller. Tapping on a controller from the list will open the settings view for that controller, where you can also see the battery level, and change its configuration (seen above). - -## Requirements - -- iOS 12 or later (not tested on earlier versions). -- Steam Controller with [BLE firmware](https://support.steampowered.com/kb_article.php?ref=7728-QESJ-4420#switch). -- A game supporting MFi controllers using the `GameController` framework. -- Starting on iOS 13, your app's Info.plist needs a `NSBluetoothAlwaysUsageDescription` key with a description of how it uses bluetooth. - -## Installation - -### CocoaPods - -[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: - -```bash -$ gem install cocoapods -``` -To integrate SteamController into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby -pod 'SteamController' -``` - -Then, run the following command: - -```bash -$ pod install -``` - -### Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage -``` - -To integrate SteamController into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "zydeco/SteamController" -``` - -Run `carthage update` to build the framework and drag the built `SteamController.framework` into your Xcode project. - -## Usage - -Everything should work like with MFi controllers. Depending on how your game works, you might not need any changes at all. - -- `#import `. -- To listen for steam controllers, either: - - Call `[SteamControllerManager listenForConnections]` when your app starts (uses private IOKit API). - - Call `[[SteamControllerManager sharedManager] scanForControllers]` when you want to scan for controllers. -- The framework will post `GCControllerDidConnectNotification` and `GCControllerDidDisconnectNotification`, as with native controllers. -- Connected Steam Controllers will be returned in `[GCController controllers]`. -- Steam Controllers are a subclass of `GCController` (`SteamController`) that implements the `extendedGamepad` profile. -- Core buttons are mapped to Apple's MFi Extended Gamepad Profile. -- Trackpads and stick can be mapped to D-pad and thumbsticks. (see below) -- Trackpads can be set to require click for input (default), or not. - -#### Button Mapping -- Analog Stick: L-Thumbstick -- Left Trackpad: D-Pad *(Requires Click)* -- Right Trackpad: R-Thumbstick / C-Buttons *(Requires Click)* -- A, B, X, Y: Equivalent -- Bumpers/Shoulders: L1 / R1 -- Triggers: L2 / R2 -- Grip buttons: L3 / R3 -- Steam Button: Pause handler and combinations via `steamButtonCombinationHandler` -- Analog Stick click: L3 *(Default)* -- Trackpad clicks: L3 / R3 (when click is not required for input) -- Back: Options button -- Forward: Menu button - -##### Alternate mapping for backward compatibility: -Since options and menu buttons were added in iOS 13, back and forward are also added as a class extension to `GCExtendedGamepad`. -- Back: `steamBackButton` -- Forward: `steamForwardButton` - -#### Controller Configuration - -The `SteamController` class has some additional properties to customise its configuration. -See the [documentation](https://namedfork.net/SteamController/Classes/SteamController.html) for more info. -These are available as GUI options in the example app. - -## License - -The SteamController framework is available under the MIT license. See the LICENSE file for more info. diff --git a/xcode/MAME4iOS/SteamController/SteamController.h b/xcode/MAME4iOS/SteamController/SteamController.h deleted file mode 100644 index 71b2d5ac..00000000 --- a/xcode/MAME4iOS/SteamController/SteamController.h +++ /dev/null @@ -1,204 +0,0 @@ -// -// SteamController.h -// SteamController -// -// Created by Jesús A. Álvarez on 20/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import -#import "SteamControllerManager.h" - -@class CBPeripheral; - -/** Represents the mapping of a Steam Controller's trackpad or stick to a GCController thumbstick or directional pad. */ -typedef NS_ENUM(NSUInteger, SteamControllerMapping) { - /// Not mapped to anything. - SteamControllerMappingNone, - /// Mapped to the left thumbstick. - SteamControllerMappingLeftThumbstick, - /// Mapped to the right thumbstick. - SteamControllerMappingRightThumbstick, - /// Mapped to the directional pad. - SteamControllerMappingDPad -}; - -/** Represents a Steam Controller's behaviour. This allows changing between Game Controller emulation (the default), - and keyboard/mouse. - - In Keyboard/Mouse mode, the controls are mapped to the following keys: - * **Left trackpad**: arrows - * **Analog stick**: arrows - * **Analog stick button**: F4 - * **A**: return - * **B**: escape - * **Back**: tab - * **Forward**: escape - * **Left grip**: F7 - * **Right grip**: F9 - * **Left bumper**: space - * **Right bumper**: F9 - - Other controls are not mapped to keys. */ -typedef NS_ENUM(NSUInteger, SteamControllerMode) { - /// The controller behaves as an MFi Game Controller. - SteamControllerModeGameController, - /// The controller behaves as a keyboard and mouse. - SteamControllerModeKeyboardAndMouse -}; - -/** Represents a physical push button input (or combination thereof) from the Steam Controller. - - The values of this enumeration are the same ones used by the controller's bluetooth protocol. - */ -typedef NS_OPTIONS(uint32_t, SteamControllerButton) { - /// The button on the right underside of the controller. - SteamControllerButtonRightGrip = 0x000001, - /// Press on the left trackpad. - SteamControllerButtonLeftTrackpadClick = 0x000002, - /// Press on the right trackpad. - SteamControllerButtonRightTrackpadClick = 0x000004, - /// Touch on the left trackpad. - SteamControllerButtonLeftTrackpadTouch = 0x000008, - /// Touch on the left trackpad. - SteamControllerButtonRightTrackpadTouch = 0x000010, - /// Press on the analog stick. - SteamControllerButtonStick = 0x000040, - /// Press in the up area of the left trackpad. - SteamControllerButtonLeftTrackpadClickUp = 0x000100, - /// Press in the right area of the left trackpad. - SteamControllerButtonLeftTrackpadClickRight = 0x000200, - /// Press in the left area of the left trackpad. - SteamControllerButtonLeftTrackpadClickLeft = 0x000400, - /// Press in the down area of the left trackpad. - SteamControllerButtonLeftTrackpadClickDown = 0x000800, - /// The left pointing button to the left of the Steam button. - SteamControllerButtonBack = 0x001000, - /// Steam Button. The big round one in the middle of the controller. - SteamControllerButtonSteam = 0x002000, - /// The right pointing button to the right of the Steam button. - SteamControllerButtonForward = 0x004000, - /// The button on the left underside of the controller. - SteamControllerButtonLeftGrip = 0x008000, - /// A full press on the right trigger (the button below the right bumper). - SteamControllerButtonRightTrigger = 0x010000, - /// A full press on the left trigger (the button below the left bumper). - SteamControllerButtonLeftTrigger = 0x020000, - /// The right bumper button, also known as right shoulder button. - SteamControllerButtonRightBumper = 0x040000, - /// The left bumper button, also known as left shoulder button. - SteamControllerButtonLeftBumper = 0x080000, - /// The button marked A on the front of the controller. - SteamControllerButtonA = 0x800000, - /// The button marked B on the front of the controller. - SteamControllerButtonB = 0x200000, - /// The button marked X on the front of the controller. - SteamControllerButtonX = 0x400000, - /// The button marked Y on the front of the controller. - SteamControllerButtonY = 0x100000 -}; - -/// Returns a string representing the name of a button. -NSString* _Nonnull NSStringFromSteamControllerButton(SteamControllerButton button); - -/// A block called when a button is pressed or released. -typedef void(^SteamControllerButtonHandler)(SteamController * _Nonnull controller, SteamControllerButton button, BOOL isDown); - -NS_ASSUME_NONNULL_BEGIN - -/** - Steam Controllers are available to an application that links to `SteamController.framework`. To detect connected - or pairing Steam Controllers, call `scanForControllers` on `SteamControllerManager`. Because of the way bluetooth - accessories communicate with iOS apps, it's not possible to detect the connection automatically using public API, - so you will need to call `scanForControllers` accordingly to ensure they're available when needed (e.g. before - starting a game, after a controller is disconnected). - - Once connected, they work in the same way as the native `GCGameController` from `GameController.framework`, and - can be accessed in the same ways: - - 1. Querying for the the current array of controllers using `[GCController controllers]`. - 2. Registering for Connection/Disconnection notifications from `NSNotificationCenter`. - - Steam Controllers are represented by the `SteamController` class, a subclass of `GCController`. It implements the - `GCGamepad` and `GCExtendedGamepad` profiles, and has additional functionality relevant to the Steam Controller: - - - Changing the mapping of the trackpads and stick. - - Requiring clicking on the trackpads for input to be sent. - - Identifying a controller by playing a tune on it. - - Handling combinations of Steam button + another button. - - */ -@interface SteamController : GCController - -#pragma mark - Input Mapping -/** Mapping of the Steam Controller's left trackpad. Defaults to `SteamControllerMappingDPad`. */ -@property (nonatomic, assign) SteamControllerMapping steamLeftTrackpadMapping; -/** Mapping of the Steam Controller's right trackpad. Defaults to `SteamControllerMappingRightThumbstick`. */ -@property (nonatomic, assign) SteamControllerMapping steamRightTrackpadMapping; -/** Mapping of the Steam Controller's analog stick. Defaults to `SteamControllerMappingLeftThumbstick`. */ -@property (nonatomic, assign) SteamControllerMapping steamThumbstickMapping; - -#pragma mark - Trackpad Configuration -/** If `YES`, the input from the left trackpad will only be sent when it is clicked. Otherwise, input -will be sent as soon as it's touched. Defaults to `YES`. */ -@property (nonatomic, assign) BOOL steamLeftTrackpadRequiresClick; -/** If `YES`, the input from the right trackpad will only be sent when it is clicked. Otherwise, input - will be sent as soon as it's touched. Defaults to `YES`. */ -@property (nonatomic, assign) BOOL steamRightTrackpadRequiresClick; - -#pragma mark - Miscellaneous -/** The CoreBluetooth peripheral associated with this controller. */ -@property (nonatomic, readonly, retain) CBPeripheral *peripheral; - -/** Battery level (0.0 to 1.0). - - This is derived from the voltage reported by the controller, 1.0 meaning 3 volts. - This property is KVO-compliant. - */ -@property (nonatomic, readonly) float batteryLevel; - -/** Plays the identify tune on the controller. */ -- (void)identify; - -/// :nodoc: -- (instancetype)initWithPeripheral:(CBPeripheral*)peripheral NS_DESIGNATED_INITIALIZER; - -/** Handler for combinations using the Steam button. - - If set, this handler will be called on the handler queue when another button is pressed or released - in combination with the Steam button: - - - When the Steam button is pressed, this handler will be called once for every other button that is - currently pressed, with `isDown=YES`. - - While the Steam button is held down, this handler will be called whenever the buttons change. - - When the Steam button is released, this handler will be called for every other button that was pressed - with `isDown=NO`, and the other handlers will be updated to reflect the current state of the buttons. If - there were no button presses while the Steam button was down, `controllerPausedHandler` will be called - on the main queue. - */ -@property (nonatomic, copy, nullable) SteamControllerButtonHandler steamButtonCombinationHandler; - -/** Sets the mode of the controller. - - Defaults to SteamControllerModeGameController. */ -@property (nonatomic, assign) SteamControllerMode steamControllerMode; - -@end - -/** - Extension to `GCExtendedGamepad` to support additional buttons in the Steam Controller. - - For a non-steam controller, the additional buttons will return `nil`. -*/ -@interface GCExtendedGamepad (SteamController) -/// The left pointing button to the left of the Steam button. -@property (nonatomic, readonly, nullable) GCControllerButtonInput *steamBackButton; -/// The right pointing button to the right of the Steam button. -@property (nonatomic, readonly, nullable) GCControllerButtonInput *steamForwardButton; -/// The Steam button. -@property (nonatomic, readonly, nullable) GCControllerButtonInput *steamSteamButton; -@end - -NS_ASSUME_NONNULL_END - - diff --git a/xcode/MAME4iOS/SteamController/SteamController.m b/xcode/MAME4iOS/SteamController/SteamController.m deleted file mode 100644 index 5cd04c03..00000000 --- a/xcode/MAME4iOS/SteamController/SteamController.m +++ /dev/null @@ -1,513 +0,0 @@ -// -// SteamController.m -// SteamController -// -// Created by Jesús A. Álvarez on 16/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import "SteamController.h" -#import "SteamControllerInput.h" -#import "SteamControllerExtendedGamepad.h" -#import - -#ifndef STEAMCONTROLLER_NO_PRIVATE_API -static void (*GSEventResetIdleTimer)(void); -static void fakeGSEventResetIdleTimer() {}; -@import Darwin.POSIX.dlfcn; -#endif - - -static inline float S16ToFloat(int16_t value) { - if (value == 0) { - return 0.0; - } else if (value > 0) { - return value / (float)INT16_MAX; - } else { - return value / (float)-INT16_MIN; - } -} - -static void UpdateStatePad(SteamControllerExtendedGamepadSnapshotData* state, SteamControllerMapping pad, float x, float y, BOOL button) { - switch (pad) { - case SteamControllerMappingLeftThumbstick: - state->leftThumbstickX = x; - state->leftThumbstickY = y; - state->leftThumbstickButton = button; - break; - case SteamControllerMappingRightThumbstick: - state->rightThumbstickX = x; - state->rightThumbstickY = y; - state->rightThumbstickButton = button; - break; - case SteamControllerMappingDPad: - state->dpadX = x; - state->dpadY = y; - state->leftThumbstickButton |= button; - break; - default: - break; - } -} - -static CBUUID *SteamControllerInputCharacteristicUUID; -static CBUUID *SteamControllerInputDescriptorUUID; -static CBUUID *SteamControllerReportCharacteristicUUID; - -@interface SteamController () -- (void)playTune:(uint8_t)tune; -@end - -@implementation SteamController -{ - CBCharacteristic *reportCharacteristic; - SteamControllerExtendedGamepad *extendedGamepad; - GCControllerPlayerIndex playerIndex; - dispatch_queue_t handlerQueue; - void (^controllerPausedHandler)(GCController *controller); - BOOL handledSteamCombos; - SteamControllerState state; - uint32_t currentSteamCombos; -} - -+ (void)load { -#ifndef STEAMCONTROLLER_NO_PRIVATE_API - GSEventResetIdleTimer = dlsym(RTLD_DEFAULT, "GSEventResetIdleTimer"); - if (GSEventResetIdleTimer == NULL) { - GSEventResetIdleTimer = fakeGSEventResetIdleTimer; - } -#endif -} - -- (instancetype)init { - @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Please don't init SteamController without a peripheral." userInfo:nil]; - return [self initWithPeripheral:nil]; -} - -- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - SteamControllerInputCharacteristicUUID = [CBUUID UUIDWithString:@"100F6C33-1735-4313-B402-38567131E5F3"]; - SteamControllerInputDescriptorUUID = [CBUUID UUIDWithString:@"00002902-0000-1000-8000-00805f9b34fb"]; - SteamControllerReportCharacteristicUUID = [CBUUID UUIDWithString:@"100F6C34-1735-4313-B402-38567131E5F3"]; - }); - if ((self = [super init])) { - _peripheral = peripheral; - _peripheral.delegate = self; - _steamLeftTrackpadMapping = SteamControllerMappingDPad; - _steamRightTrackpadMapping = SteamControllerMappingRightThumbstick; - _steamThumbstickMapping = SteamControllerMappingLeftThumbstick; - _steamLeftTrackpadRequiresClick = YES; - _steamRightTrackpadRequiresClick = YES; - _steamControllerMode = SteamControllerModeGameController; // will set on connection - memset(&state, 0, sizeof(state)); - extendedGamepad = [[SteamControllerExtendedGamepad alloc] initWithController:self]; - self.playerIndex = GCControllerPlayerIndexUnset; - self.handlerQueue = dispatch_get_main_queue(); - } - return self; -} - -#pragma mark - Accessors - -- (GCControllerPlayerIndex)playerIndex { - return playerIndex; -} - -- (void)setPlayerIndex:(GCControllerPlayerIndex)newPlayerIndex { - playerIndex = newPlayerIndex; -} - -- (BOOL)isAttachedToDevice { - return NO; -} - -- (NSString *)vendorName { - return @"Steam Controller"; -} - -- (NSString *)productCategory { - return @"Steam"; -} - -- (dispatch_queue_t)handlerQueue { - return handlerQueue; -} - -- (void)setHandlerQueue:(dispatch_queue_t)newHandlerQueue { - handlerQueue = newHandlerQueue; -} - -- (GCExtendedGamepad *)extendedGamepad { - return extendedGamepad; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (GCGamepad *)gamepad { - return (GCGamepad *)extendedGamepad; -} -#pragma clang diagnostic pop - -- (GCMicroGamepad *)microGamepad { - return nil; -} - -- (GCMotion *)motion { - return nil; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (void (^)(GCController * _Nonnull))controllerPausedHandler { - return controllerPausedHandler; -} - -- (void)setControllerPausedHandler:(void (^)(GCController * _Nonnull))newHandler { - controllerPausedHandler = newHandler; -} -#pragma clang diagnostic pop - -- (void)setSteamControllerMode:(SteamControllerMode)steamControllerMode { - _steamControllerMode = steamControllerMode; - if (reportCharacteristic == nil) { - return; // will do after discovering characteristic - } - NSData *packet = nil; - switch (steamControllerMode) { - case SteamControllerModeGameController: - packet = [NSData dataWithBytes:"\xC0\x87\x03\x08\x07\x00" length:6]; - break; - case SteamControllerModeKeyboardAndMouse: - packet = [NSData dataWithBytes:"\xC0\x87\x03\x08\x00\x00" length:6]; - break; - default: - break; - } - if (packet) { - [_peripheral writeValue:packet forCharacteristic:reportCharacteristic type:CBCharacteristicWriteWithResponse]; - } -} - -- (BOOL)isSnapshot { - return NO; -} - -#pragma mark - CBPeripheralDelegate - -- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error { - -} - -- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { - for (CBService *service in peripheral.services) { - [peripheral discoverCharacteristics:nil forService:service]; - } -} - -- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { - for (CBCharacteristic *characteristic in service.characteristics) { - if ([characteristic.UUID isEqual:SteamControllerInputCharacteristicUUID]) { - [peripheral setNotifyValue:YES forCharacteristic:characteristic]; - } - [peripheral discoverDescriptorsForCharacteristic:characteristic]; - } -} - -- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { - if ([characteristic.UUID isEqual:SteamControllerReportCharacteristicUUID]) { - reportCharacteristic = characteristic; - self.steamControllerMode = _steamControllerMode; - [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) - withObject:[NSNotification notificationWithName:GCControllerDidConnectNotification object:self] - waitUntilDone:NO]; - } -} - -- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { - -} - -- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { - -} - -- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error { - -} - -- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { - if ([characteristic.UUID isEqual:SteamControllerInputCharacteristicUUID]) { - NSData *value = characteristic.value; - const uint8_t *bytes = value.bytes; - if (value.length < 2 || bytes[0] != 0xc0) return; // interesting events start with c0 - uint16_t packetType = OSReadLittleInt16(bytes, 1); - if ((packetType & 0x5000) == 0x5000) { - [self didReceiveStatus:value]; - } else if (packetType & 0x03b0) { - [self didReceiveInput:packetType data:value]; - } - } -} - -- (void)didConnect { - [_peripheral discoverServices:nil]; -} - -- (void)didDisconnect { - [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) - withObject:[NSNotification notificationWithName:GCControllerDidDisconnectNotification object:self] - waitUntilDone:NO]; -} - -- (void)didReceiveStatus:(NSData*)data { - const uint8_t *bytes = data.bytes; - //uint32_t seqn = OSReadLittleInt32(bytes, 4); - uint16_t voltage = OSReadLittleInt16(bytes, 12); - float level = voltage / 3000.0; - if (level != _batteryLevel) { - [self willChangeValueForKey:@"batteryLevel"]; - _batteryLevel = level; - [self didChangeValueForKey:@"batteryLevel"]; - } -} - -- (void)didReceiveInput:(uint16_t)packetType data:(NSData*)data { - const uint8_t *bytes = data.bytes; - - // Parse update - BOOL hasButtons = packetType & 0x0010; - BOOL hasTriggers = packetType & 0x0020; - BOOL hasStick = packetType & 0x0080; - BOOL hasLeftTrackpad = packetType & 0x0100; - BOOL hasRightTrackpad = packetType & 0x0200; - - // Update internal state - const uint8_t *buf = bytes + 3; - uint32_t previousButtons = state.buttons; - if (hasButtons) { - state.buttons = OSReadBigInt32(buf, -1) & 0xffffff; - buf += 3; - } - - if (hasTriggers) { - state.leftTrigger = buf[0]; - state.rightTrigger = buf[1]; - buf += 2; - } - - if (hasStick) { - state.stick.x = OSReadLittleInt16(buf, 0); - state.stick.y = OSReadLittleInt16(buf, 2); - buf += 4; - } - - if (hasLeftTrackpad) { - state.leftPad.x = OSReadLittleInt16(buf, 0); - state.leftPad.y = OSReadLittleInt16(buf, 2); - buf += 4; - } - - if (hasRightTrackpad) { - state.rightPad.x = OSReadLittleInt16(buf, 0); - state.rightPad.y = OSReadLittleInt16(buf, 2); - buf += 4; - } - - // Update extended gamepad state - SteamControllerExtendedGamepadSnapshotData snapshot = extendedGamepad.state; -#define ButtonToFloat(b) ((state.buttons & b) ? 1.0 : 0.0) -#define ButtonToBool(b) ((state.buttons & b) ? YES : NO) - snapshot.buttonA = ButtonToFloat(SteamControllerButtonA); - snapshot.buttonB = ButtonToFloat(SteamControllerButtonB); - snapshot.buttonX = ButtonToFloat(SteamControllerButtonX); - snapshot.buttonY = ButtonToFloat(SteamControllerButtonY); - snapshot.leftShoulder = ButtonToFloat(SteamControllerButtonLeftBumper); - snapshot.rightShoulder = ButtonToFloat(SteamControllerButtonRightBumper); - snapshot.leftTrigger = (state.buttons & SteamControllerButtonLeftTrigger) ? 1.0 : state.leftTrigger / 255.0; - snapshot.rightTrigger = (state.buttons & SteamControllerButtonRightTrigger) ? 1.0 : state.rightTrigger / 255.0; - snapshot.leftThumbstickButton = (state.buttons & SteamControllerButtonLeftGrip); - snapshot.rightThumbstickButton = (state.buttons & SteamControllerButtonRightGrip); - snapshot.steamBackButton = (state.buttons & SteamControllerButtonBack); - snapshot.steamForwardButton = (state.buttons & SteamControllerButtonForward); - snapshot.steamSteamButton = (state.buttons & SteamControllerButtonSteam); - - BOOL hasUpdatedPads[] = { - [SteamControllerMappingDPad] = NO, - [SteamControllerMappingLeftThumbstick] = NO, - [SteamControllerMappingRightThumbstick] = NO - }; - - if (_steamLeftTrackpadRequiresClick) { - if ((state.buttons & SteamControllerButtonLeftTrackpadClick)) { - UpdateStatePad(&snapshot, _steamLeftTrackpadMapping, S16ToFloat(state.leftPad.x), S16ToFloat(state.leftPad.y), (state.buttons & SteamControllerButtonLeftGrip)); - hasUpdatedPads[_steamLeftTrackpadMapping] = state.leftPad.x || state.leftPad.y; - } else { - UpdateStatePad(&snapshot, _steamLeftTrackpadMapping, 0.0, 0.0, (state.buttons & SteamControllerButtonLeftGrip)); - } - } else { - UpdateStatePad(&snapshot, _steamLeftTrackpadMapping, S16ToFloat(state.leftPad.x), S16ToFloat(state.leftPad.y), (state.buttons & (SteamControllerButtonLeftTrackpadClick))); - hasUpdatedPads[_steamLeftTrackpadMapping] = state.leftPad.x || state.leftPad.y; - } - - if (_steamRightTrackpadRequiresClick) { - if ((state.buttons & SteamControllerButtonRightTrackpadClick)) { - UpdateStatePad(&snapshot, _steamRightTrackpadMapping, S16ToFloat(state.rightPad.x), S16ToFloat(state.rightPad.y), (state.buttons & SteamControllerButtonRightGrip)); - hasUpdatedPads[_steamRightTrackpadMapping] |= state.rightPad.x || state.rightPad.y; - } else { - UpdateStatePad(&snapshot, _steamRightTrackpadMapping, 0.0, 0.0, (state.buttons & SteamControllerButtonRightGrip)); - } - } else { - UpdateStatePad(&snapshot, _steamRightTrackpadMapping, S16ToFloat(state.rightPad.x), S16ToFloat(state.rightPad.y), (state.buttons & (SteamControllerButtonRightTrackpadClick))); - hasUpdatedPads[_steamRightTrackpadMapping] |= state.rightPad.x || state.rightPad.y; - } - - if (_steamThumbstickMapping && !hasUpdatedPads[_steamThumbstickMapping]) { - UpdateStatePad(&snapshot, _steamThumbstickMapping, S16ToFloat(state.stick.x), S16ToFloat(state.stick.y), (state.buttons & SteamControllerButtonStick)); - hasUpdatedPads[_steamThumbstickMapping] = state.stick.x || state.stick.y; - } - - // Ensure grip buttons override thumbstick button state - snapshot.leftThumbstickButton |= (state.buttons & SteamControllerButtonLeftGrip); - snapshot.rightThumbstickButton |= (state.buttons & SteamControllerButtonRightGrip); - - // Reset idle timer -#ifndef STEAMCONTROLLER_NO_PRIVATE_API - GSEventResetIdleTimer(); -#endif - - if (_steamButtonCombinationHandler == nil) { - // Update client - extendedGamepad.state = snapshot; - } - else if (hasButtons && (state.buttons & SteamControllerButtonSteam)) { - // Handle steam button combos - handledSteamCombos |= [self handleSteamButtonCombos:(state.buttons & ~SteamControllerButtonSteam)]; - } else if (hasButtons && (previousButtons & SteamControllerButtonSteam)) { - // Released steam button - if (handledSteamCombos) { - [self handleSteamButtonCombos:0]; - handledSteamCombos = NO; - // Update client - extendedGamepad.state = snapshot; - } else if (controllerPausedHandler) { - dispatch_async(dispatch_get_main_queue(), ^{ - self->controllerPausedHandler(self); - }); - } - } else if (!(state.buttons & SteamControllerButtonSteam)) { - // Update client - extendedGamepad.state = snapshot; - } -} - -- (BOOL)handleSteamButtonCombos:(uint32_t)buttons { - uint32_t changes = buttons ^ currentSteamCombos; - if (changes == 0 || _steamButtonCombinationHandler == nil) { - return NO; - } - for (uint32_t mask = 0x800000; mask; mask >>= 1) { - if (changes & mask) { - dispatch_async(handlerQueue, ^{ - self->_steamButtonCombinationHandler(self, mask, buttons & mask); - }); - } - } - currentSteamCombos = buttons; - return YES; -} - -#pragma mark - Operations - -- (void)identify { - [self playTune:4]; -} - -- (void)playTune:(uint8_t)tune { - if (reportCharacteristic == nil) { - // ignore - return; - } - char command[] = "\xC0\xB6\x04\x04\x00\x00\x00"; - command[3] = (tune & 0xf); - [_peripheral writeValue:[NSData dataWithBytes:command length:7] forCharacteristic:reportCharacteristic type:CBCharacteristicWriteWithResponse]; -} - -- (GCController *)capture { - if (@available(iOS 13.0, tvOS 13.0, *)) { - GCController *controller = [GCController controllerWithExtendedGamepad]; - [controller.extendedGamepad setStateFromExtendedGamepad:extendedGamepad]; - return controller; - } else { - return nil; - } -} - -@end - -NSString* NSStringFromSteamControllerButton(SteamControllerButton button) { - switch (button) { - case SteamControllerButtonRightGrip: - return @"Right Grip"; - case SteamControllerButtonLeftTrackpadClick: - return @"Left Trackpad Click"; - case SteamControllerButtonRightTrackpadClick: - return @"Right Trackpad Click"; - case SteamControllerButtonLeftTrackpadTouch: - return @"Left Trackpad Touch"; - case SteamControllerButtonRightTrackpadTouch: - return @"Right Trackpad Touch"; - case SteamControllerButtonStick: - return @"Stick Click"; - case SteamControllerButtonLeftTrackpadClickUp: - return @"Left Trackpad Click Up"; - case SteamControllerButtonLeftTrackpadClickRight: - return @"Left Trackpad Click Right"; - case SteamControllerButtonLeftTrackpadClickLeft: - return @"Left Trackpad Click Left"; - case SteamControllerButtonLeftTrackpadClickDown: - return @"LeftTrackpad Click Down"; - case SteamControllerButtonBack: - return @"Back"; - case SteamControllerButtonSteam: - return @"Steam"; - case SteamControllerButtonForward: - return @"Forward"; - case SteamControllerButtonLeftGrip: - return @"Left Grip"; - case SteamControllerButtonRightTrigger: - return @"Right Trigger"; - case SteamControllerButtonLeftTrigger: - return @"Left Trigger"; - case SteamControllerButtonRightBumper: - return @"Right Bumper"; - case SteamControllerButtonLeftBumper: - return @"Left Bumper"; - case SteamControllerButtonA: - return @"A"; - case SteamControllerButtonB: - return @"B"; - case SteamControllerButtonX: - return @"X"; - case SteamControllerButtonY: - return @"Y"; - default: - return [NSString stringWithFormat:@"%06x", button & 0xffffff]; - } -} - -@implementation GCExtendedGamepad (SteamController) - -- (GCControllerButtonInput *)steamBackButton { - return nil; -} - -- (GCControllerButtonInput *)steamForwardButton { - return nil; -} - -- (GCControllerButtonInput *)steamSteamButton { - return nil; -} - -@end diff --git a/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.h b/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.h deleted file mode 100644 index 650d01d2..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// SteamControllerExtendedGamepad.h -// SteamController -// -// Created by Jesús A. Álvarez on 18/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import -#import "SteamController.h" -#import "SteamControllerInput.h" - -#pragma pack(push, 1) -typedef struct { - -#pragma mark - GCExtendedGamepadSnapshotDataVersion1+ - uint16_t version; - uint16_t size; - - // Extended gamepad data - // Axes in the range [-1.0, 1.0] - float dpadX; - float dpadY; - - // Buttons in the range [0.0, 1.0] - float buttonA; - float buttonB; - float buttonX; - float buttonY; - float leftShoulder; - float rightShoulder; - - // Axes in the range [-1.0, 1.0] - float leftThumbstickX; - float leftThumbstickY; - float rightThumbstickX; - float rightThumbstickY; - - // Buttons in the range [0.0, 1.0] - float leftTrigger; - float rightTrigger; - -#pragma mark - GCExtendedGamepadSnapshotDataVersion2+ - BOOL supportsClickableThumbsticks; - // Left and right thumbstick clickable values (0, 1) - BOOL leftThumbstickButton; - BOOL rightThumbstickButton; - -#pragma mark - Steam Controller - BOOL steamBackButton; - BOOL steamForwardButton; - BOOL steamSteamButton; -} SteamControllerExtendedGamepadSnapshotData; -#pragma pack(pop) - -NS_ASSUME_NONNULL_BEGIN - -@interface SteamControllerExtendedGamepad : GCExtendedGamepad - -@property (nonatomic, readonly) SteamControllerDirectionPad *dpad; -@property (nonatomic, readonly) SteamControllerButtonInput *buttonA; -@property (nonatomic, readonly) SteamControllerButtonInput *buttonB; -@property (nonatomic, readonly) SteamControllerButtonInput *buttonX; -@property (nonatomic, readonly) SteamControllerButtonInput *buttonY; -@property (nonatomic, readonly) SteamControllerDirectionPad *leftThumbstick; -@property (nonatomic, readonly) SteamControllerDirectionPad *rightThumbstick; -@property (nonatomic, readonly) SteamControllerButtonInput *leftShoulder; -@property (nonatomic, readonly) SteamControllerButtonInput *rightShoulder; -@property (nonatomic, readonly) SteamControllerButtonInput *leftTrigger; -@property (nonatomic, readonly) SteamControllerButtonInput *rightTrigger; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *leftThumbstickButton; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *rightThumbstickButton; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *buttonOptions; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *buttonMenu; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *buttonHome; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *steamBackButton; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *steamForwardButton; -@property (nonatomic, readonly, nullable) SteamControllerButtonInput *steamSteamButton; -@property (nonatomic, assign) SteamControllerExtendedGamepadSnapshotData state; - -- (instancetype)initWithController:(SteamController*)controller; - -@end - -NS_ASSUME_NONNULL_END diff --git a/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.m b/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.m deleted file mode 100644 index 8991942a..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerExtendedGamepad.m +++ /dev/null @@ -1,137 +0,0 @@ -// -// SteamControllerExtendedGamepad.m -// SteamController -// -// Created by Jesús A. Álvarez on 18/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import "SteamControllerExtendedGamepad.h" -#import "SteamController.h" -#import "SteamControllerInput.h" - -@implementation SteamControllerExtendedGamepad -{ - SteamControllerDirectionPad *leftThumbstick, *rightThumbstick, *dpad; - SteamControllerButtonInput *leftShoulder, *rightShoulder; - SteamControllerButtonInput *leftThumbstickButton, *rightThumbstickButton; - SteamControllerButtonInput *leftTrigger, *rightTrigger; - SteamControllerButtonInput *buttonA, *buttonB, *buttonX, *buttonY; - SteamControllerButtonInput *steamBackButton, *steamForwardButton, *steamSteamButton; - __weak SteamController *steamController; - SteamControllerExtendedGamepadSnapshotData state; - GCExtendedGamepadValueChangedHandler valueChangedHandler; -} - -@synthesize leftThumbstick, rightThumbstick, dpad; -@synthesize leftShoulder, rightShoulder; -@synthesize leftTrigger, rightTrigger; -@synthesize buttonA, buttonB, buttonX, buttonY; -@synthesize leftThumbstickButton, rightThumbstickButton; -@synthesize steamBackButton, steamForwardButton, steamSteamButton; -@synthesize state; - -- (instancetype)initWithController:(SteamController *)controller { - if (self = [super init]) { - steamController = controller; - leftThumbstick = [[SteamControllerDirectionPad alloc] initWithController:controller]; - rightThumbstick = [[SteamControllerDirectionPad alloc] initWithController:controller]; - dpad = [[SteamControllerDirectionPad alloc] initWithController:controller]; - leftShoulder = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - rightShoulder = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - leftTrigger = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - rightTrigger = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - buttonA = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - buttonB = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - buttonX = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - buttonY = [[SteamControllerButtonInput alloc] initWithController:controller analog:YES]; - if ([GCExtendedGamepad instancesRespondToSelector:@selector(leftThumbstickButton)]) { - // runtime supports thumbstick buttons - leftThumbstickButton = [[SteamControllerButtonInput alloc] initWithController:controller analog:NO]; - rightThumbstickButton = [[SteamControllerButtonInput alloc] initWithController:controller analog:NO]; - state.version = 0x0101; - state.size = 62; - state.supportsClickableThumbsticks = YES; - } else { - leftThumbstickButton = nil; - rightThumbstickButton = nil; - // pretend to be GCExtendedGamepadSnapShotDataV100 - state.version = 0x0100; - state.size = 60; - state.supportsClickableThumbsticks = NO; - } - steamBackButton = [[SteamControllerButtonInput alloc] initWithController:controller analog:NO]; - steamForwardButton = [[SteamControllerButtonInput alloc] initWithController:controller analog:NO]; - steamSteamButton = [[SteamControllerButtonInput alloc] initWithController:controller analog:NO]; - } - return self; -} - -- (GCController *)controller { - return steamController; -} - -- (GCExtendedGamepadValueChangedHandler)valueChangedHandler { - return valueChangedHandler; -} - -- (void)setValueChangedHandler:(GCExtendedGamepadValueChangedHandler)newHandler { - valueChangedHandler = newHandler; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (GCExtendedGamepadSnapshot *)saveSnapshot { - NSData *snapshotData = [NSData dataWithBytes:&state length:state.size]; - return [[GCExtendedGamepadSnapshot alloc] initWithController:self.controller snapshotData:snapshotData]; -} -#pragma clang diagnostic pop - -- (void)setState:(SteamControllerExtendedGamepadSnapshotData)newState { - SteamControllerExtendedGamepadSnapshotData oldState = state; - state = newState; -#define ChangedState(_field) (oldState._field != newState._field) -#define UpdateStateValue(_field) if (ChangedState(_field)) { _field.value = newState._field; [self didChangeValueForElement:_field]; } -#define UpdateStateBool(_field) if (ChangedState(_field)) { _field.value = newState._field ? 1.0 : 0.0; [self didChangeValueForElement:_field]; } -#define UpdateXYValue(_fieldX, _fieldY, _input) if (ChangedState(_fieldX) || ChangedState(_fieldY)) { [_input setX:newState._fieldX Y:newState._fieldY]; [self didChangeValueForElement:_input]; } - UpdateStateValue(buttonA); - UpdateStateValue(buttonB); - UpdateStateValue(buttonX); - UpdateStateValue(buttonY); - UpdateStateValue(leftShoulder); - UpdateStateValue(leftTrigger); - UpdateStateValue(rightShoulder); - UpdateStateValue(rightTrigger); - UpdateXYValue(dpadX, dpadY, dpad); - UpdateXYValue(leftThumbstickX, leftThumbstickY, leftThumbstick); - UpdateXYValue(rightThumbstickX, rightThumbstickY, rightThumbstick); - UpdateStateBool(leftThumbstickButton); - UpdateStateBool(rightThumbstickButton); - UpdateStateBool(steamBackButton); - UpdateStateBool(steamForwardButton); - UpdateStateBool(steamSteamButton); -} - -- (void)didChangeValueForElement:(GCControllerElement*)element { - if (valueChangedHandler) dispatch_async(steamController.handlerQueue, ^{ - self->valueChangedHandler(self, element); - }); -} - -- (SteamControllerButtonInput *)buttonOptions { - return steamBackButton; -} - -- (SteamControllerButtonInput *)buttonMenu { - return steamForwardButton; -} - -- (SteamControllerButtonInput *)buttonHome { - return steamSteamButton; -} - -- (void)setStateFromExtendedGamepad:(GCExtendedGamepad *)extendedGamepad { - // ignore -} - -@end diff --git a/xcode/MAME4iOS/SteamController/SteamControllerInput.h b/xcode/MAME4iOS/SteamController/SteamControllerInput.h deleted file mode 100644 index 7e7cf9f4..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerInput.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// SteamControllerInput.h -// SteamController -// -// Created by Jesús A. Álvarez on 18/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import -#import - -typedef struct SteamPadState { - int16_t x, y; -} SteamPadState; - -typedef struct SteamControllerState { - uint32_t buttons; - SteamPadState leftPad, rightPad, stick; - uint8_t leftTrigger, rightTrigger; -} SteamControllerState; - -@class SteamControllerDirectionPad, SteamController; - -NS_ASSUME_NONNULL_BEGIN - -@interface SteamControllerButtonInput : GCControllerButtonInput - -- (instancetype)initWithDirectionPad:(SteamControllerDirectionPad*)dpad; -- (instancetype)initWithController:(SteamController *)controller analog:(BOOL)isAnalog; -- (void)setValue:(float)value; - -@end - -@interface SteamControllerAxisInput : GCControllerAxisInput - -- (instancetype)initWithDirectionPad:(SteamControllerDirectionPad*)dpad; -- (instancetype)initWithController:(SteamController *)controller; -- (void)setValue:(float)value; - -@end - -@interface SteamControllerDirectionPad : GCControllerDirectionPad - -@property (nonatomic, readonly) SteamControllerAxisInput *xAxis; -@property (nonatomic, readonly) SteamControllerAxisInput *yAxis; - -@property (nonatomic, readonly) SteamControllerButtonInput *up; -@property (nonatomic, readonly) SteamControllerButtonInput *down; -@property (nonatomic, readonly) SteamControllerButtonInput *left; -@property (nonatomic, readonly) SteamControllerButtonInput *right; - -@property (nonatomic, readonly, weak) SteamController *steamController; - -- (instancetype)initWithController:(SteamController *)controller; -- (void)setX:(float)x Y:(float)y; - -@end - -NS_ASSUME_NONNULL_END diff --git a/xcode/MAME4iOS/SteamController/SteamControllerInput.m b/xcode/MAME4iOS/SteamController/SteamControllerInput.m deleted file mode 100644 index ba7a5aad..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerInput.m +++ /dev/null @@ -1,201 +0,0 @@ -// -// SteamControllerInput.m -// SteamController -// -// Created by Jesús A. Álvarez on 18/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import "SteamControllerInput.h" -#import "SteamController.h" - -@implementation SteamControllerAxisInput -{ - GCControllerAxisValueChangedHandler valueChangedHandler; - SteamControllerDirectionPad *directionPad; - __weak SteamController *steamController; - float value; -} - -- (instancetype)initWithDirectionPad:(SteamControllerDirectionPad*)dpad { - if (self = [super init]) { - directionPad = dpad; - steamController = dpad.steamController; - } - return self; -} - -- (instancetype)initWithController:(SteamController *)controller { - if (self = [super init]) { - steamController = controller; - } - return self; -} - -- (BOOL)isAnalog { - return YES; -} - -- (GCControllerAxisValueChangedHandler)valueChangedHandler { - return valueChangedHandler; -} - -- (void)setValueChangedHandler:(GCControllerAxisValueChangedHandler)newHandler { - valueChangedHandler = newHandler; -} - -- (GCControllerElement *)collection { - return directionPad; -} - -- (float)value { - return value; -} - -- (void)setValue:(float)newValue { - float oldValue = value; - value = newValue; - if (value != oldValue && valueChangedHandler) dispatch_async(steamController.handlerQueue, ^{ - self->valueChangedHandler(self, newValue); - }); -} - -@end - -#define kButtonPressedThreshold 0.3 - -@implementation SteamControllerButtonInput -{ - GCControllerButtonValueChangedHandler valueChangedHandler, pressedChangedHandler; - SteamControllerDirectionPad *directionPad; - __weak SteamController *steamController; - BOOL analog; - float value; -} - -- (instancetype)initWithDirectionPad:(SteamControllerDirectionPad*)dpad { - if (self = [super init]) { - directionPad = dpad; - steamController = dpad.steamController; - analog = YES; - } - return self; -} - -- (instancetype)initWithController:(SteamController *)controller analog:(BOOL)isAnalog { - if (self = [super init]) { - steamController = controller; - analog = isAnalog; - } - return self; -} - -- (BOOL)isAnalog { - return analog; -} - -- (GCControllerButtonValueChangedHandler)valueChangedHandler { - return valueChangedHandler; -} - -- (void)setValueChangedHandler:(GCControllerButtonValueChangedHandler)newHandler { - valueChangedHandler = newHandler; -} - -- (GCControllerButtonValueChangedHandler)pressedChangedHandler { - return pressedChangedHandler; -} - -- (void)setPressedChangedHandler:(GCControllerButtonValueChangedHandler)newHandler { - pressedChangedHandler = newHandler; -} - -- (GCControllerElement *)collection { - return directionPad; -} - -- (float)value { - return value; -} - -- (void)setValue:(float)newValue { - BOOL wasPressed = value > kButtonPressedThreshold; - BOOL pressed = newValue > kButtonPressedThreshold; - float oldValue = value; - value = newValue; - if (value != oldValue && valueChangedHandler) dispatch_async(steamController.handlerQueue, ^{ - self->valueChangedHandler(self, newValue, pressed); - }); - if (pressed != wasPressed && pressedChangedHandler) dispatch_async(steamController.handlerQueue, ^{ - self->pressedChangedHandler(self, newValue, pressed); - }); -} - -- (BOOL)isPressed { - return value > kButtonPressedThreshold; -} - -@end - -@implementation SteamControllerDirectionPad -{ - SteamControllerAxisInput *xAxis, *yAxis; - SteamControllerButtonInput *up, *down, *left, *right; - GCControllerDirectionPadValueChangedHandler valueChangedHandler; -} - -@synthesize xAxis, yAxis; -@synthesize up, down, left, right; - -- (instancetype)initWithController:(SteamController *)controller { - if (self = [super init]) { - _steamController = controller; - xAxis = [[SteamControllerAxisInput alloc] initWithDirectionPad:self]; - yAxis = [[SteamControllerAxisInput alloc] initWithDirectionPad:self]; - up = [[SteamControllerButtonInput alloc] initWithDirectionPad:self]; - down = [[SteamControllerButtonInput alloc] initWithDirectionPad:self]; - left = [[SteamControllerButtonInput alloc] initWithDirectionPad:self]; - right = [[SteamControllerButtonInput alloc] initWithDirectionPad:self]; - } - return self; -} - -- (GCControllerDirectionPadValueChangedHandler)valueChangedHandler { - return valueChangedHandler; -} - -- (void)setValueChangedHandler:(GCControllerDirectionPadValueChangedHandler)newHandler { - valueChangedHandler = newHandler; -} - -- (void)setX:(float)xValue Y:(float)yValue { - [xAxis setValue:xValue]; - if (xValue > 0.0) { - [right setValue:xValue]; - [left setValue:0.0]; - } else if (xValue < 0.0) { - [right setValue:0.0]; - [left setValue:-xValue]; - } else { - [left setValue:0.0]; - [right setValue:0.0]; - } - - [yAxis setValue:yValue]; - if (yValue > 0.0) { - [up setValue:yValue]; - [down setValue:0.0]; - } else if (yValue < 0.0) { - [up setValue:0.0]; - [down setValue:-yValue]; - } else { - [up setValue:0.0]; - [down setValue:0.0]; - } - - if (valueChangedHandler) dispatch_async(_steamController.handlerQueue, ^{ - self->valueChangedHandler(self, xValue, yValue); - }); -} - -@end diff --git a/xcode/MAME4iOS/SteamController/SteamControllerManager.h b/xcode/MAME4iOS/SteamController/SteamControllerManager.h deleted file mode 100644 index f2da6ef9..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerManager.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// SteamControllerManager.h -// SteamController -// -// Created by Jesús A. Álvarez on 16/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import - -#define STEAMCONTROLLER_NO_SWIZZLING -#define STEAMCONTROLLER_NO_PRIVATE_API - -@class SteamController; - -NS_ASSUME_NONNULL_BEGIN - -/** - `SteamControllerManager` handles the connection and disconnection of Steam Controllers, and inserts connected Steam - Controllers into the array of controllers returned by `[GCController controllers]`. - */ -@interface SteamControllerManager : NSObject - -/** Returns the shared instance of `SteamControllerManager`. */ -+ (instancetype)sharedManager; - -/** - Returns the currently connected Steam Controllers. Unless you only want to support Steam Controllers, you should - use `[GCController controllers]` instead of this property. */ -@property (nonatomic, readonly) NSArray *controllers; - -/** - Detects connected and pairing Steam Controllers. - If a controller is in pairing mode, this will initiate the pairing process. If it is already paired and connected, - it will configure it and post a `GCControllerDidConnectNotification` notification when it's ready. */ -- (void)scanForControllers; - -@end - -#ifndef STEAMCONTROLLER_NO_PRIVATE_API -/// Implements listening for controller connections over bluetooth using IOKit. -@interface SteamControllerManager (Listening) -/** Starts listening for controller connections. - - You should call this method in your app delegate's `application:didFinishLaunchingWithOptions:` method. - - This enables controllers to be detected automatically when they connect/reconnect, without having to call `scanForControllers`. - This feature calls IOKit functions dynamically, which is private API on iOS/tvOS, it can be excluded from the build by - passing `-DSTEAMCONTROLLER_NO_PRIVATE_API` to the compiler, or using the `SteamController/no-private-api` subspec in your Podfile. - */ -+ (BOOL)listenForConnections; -@end -#endif - -NS_ASSUME_NONNULL_END diff --git a/xcode/MAME4iOS/SteamController/SteamControllerManager.m b/xcode/MAME4iOS/SteamController/SteamControllerManager.m deleted file mode 100644 index b1f86a26..00000000 --- a/xcode/MAME4iOS/SteamController/SteamControllerManager.m +++ /dev/null @@ -1,214 +0,0 @@ -// -// SteamControllerManager.m -// SteamController -// -// Created by Jesús A. Álvarez on 16/12/2018. -// Copyright © 2018 namedfork. All rights reserved. -// - -#import "SteamControllerManager.h" -#import "SteamController.h" -#import - -#ifndef STEAMCONTROLLER_NO_SWIZZLING -@import ObjectiveC.runtime; -#endif - -#ifndef STEAMCONTROLLER_NO_PRIVATE_API -@import Darwin.POSIX.dlfcn; -#endif - -@interface SteamController (Private) -- (void)didConnect; -- (void)didDisconnect; -@end - -@interface SteamControllerManager () -@end - -@implementation SteamControllerManager -{ - CBCentralManager *centralManager; - CBUUID *controllerServiceUUID; - NSMutableDictionary *controllers; - NSMutableSet *connectingPeripherals; -} - -+ (instancetype)sharedManager { - static dispatch_once_t onceToken; - static SteamControllerManager *sharedManager = nil; - dispatch_once(&onceToken, ^{ - sharedManager = [SteamControllerManager new]; - }); - return sharedManager; -} - -- (instancetype)init { - if ((self = [super init])) { - controllerServiceUUID = [CBUUID UUIDWithString:@"100F6C32-1735-4313-B402-38567131E5F3"]; - centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; - controllers = [NSMutableDictionary dictionaryWithCapacity:4]; - connectingPeripherals = [NSMutableSet setWithCapacity:4]; - } - return self; -} - -- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { - [connectingPeripherals addObject:peripheral]; - [central connectPeripheral:peripheral options:nil]; -} - -- (SteamController*)controllerForPeripheral:(CBPeripheral*)peripheral { - NSUUID *uuid = peripheral.identifier; - SteamController *controller = nil; - @synchronized (controllers) { - controller = controllers[uuid]; - if (controller == nil) { - controller = [[SteamController alloc] initWithPeripheral:peripheral]; - controllers[uuid] = controller; - } - } - return controller; -} - -- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { - SteamController *controller = [self controllerForPeripheral:peripheral]; - [connectingPeripherals removeObject:peripheral]; - [controller didConnect]; -} - -- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { - SteamController *controller = nil; - @synchronized (controllers) { - controller = controllers[peripheral.identifier]; - [controllers removeObjectForKey:peripheral.identifier]; - } - [connectingPeripherals removeObject:peripheral]; - [controller didDisconnect]; -} - -- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central { - if (central.state == CBManagerStatePoweredOn) { - [self scanForControllers]; - } -} - -- (NSArray *)controllers { - return controllers.allValues; -} - -- (void)scanForControllers:(id)sender { - [self scanForControllers]; -} - -- (void)scanForControllers { - if (centralManager.state == CBManagerStatePoweredOn) { - [centralManager scanForPeripheralsWithServices:@[controllerServiceUUID] options:nil]; - NSArray *peripherals = [centralManager retrieveConnectedPeripheralsWithServices:@[controllerServiceUUID]]; - for (CBPeripheral *peripheral in peripherals) { - if (peripheral.state == CBPeripheralStateDisconnected) { - [connectingPeripherals addObject:peripheral]; - [centralManager connectPeripheral:peripheral options:nil]; - } // TODO: something if it's disconnected? - } - } -} - -#pragma mark - Swizzling - -#ifndef STEAMCONTROLLER_NO_SWIZZLING -+ (void)load { - Method m1 = class_getClassMethod([GCController class], @selector(controllers)); - Method m2 = class_getClassMethod([SteamControllerManager class], @selector(controllers)); - Method m3 = class_getClassMethod([SteamControllerManager class], @selector(originalControllers)); - method_exchangeImplementations(m1, m3); - method_exchangeImplementations(m1, m2); -} - -+ (NSArray*)originalControllers { - return @[]; -} - -+ (NSArray*)controllers { - NSArray* originalControllers = [SteamControllerManager originalControllers]; - NSArray* steamControllers = [SteamControllerManager sharedManager].controllers; - return [originalControllers arrayByAddingObjectsFromArray:steamControllers]; -} -#endif - -@end - - -#pragma mark - IOKit - -#ifndef STEAMCONTROLLER_NO_PRIVATE_API -typedef mach_port_t io_object_t; -typedef io_object_t io_connect_t; -typedef io_object_t io_enumerator_t; -typedef io_object_t io_iterator_t; -typedef io_object_t io_registry_entry_t; -typedef io_object_t io_service_t; -typedef char io_name_t[128]; -typedef struct IONotificationPort *IONotificationPortRef; -static IONotificationPortRef (*IONotificationPortCreate)(mach_port_t masterPort); -#define kIOMasterPortDefault 0 -static CFMutableDictionaryRef (*IOServiceMatching)(const char *name); -static CFRunLoopSourceRef (*IONotificationPortGetRunLoopSource)(IONotificationPortRef notify); -typedef void (*IOServiceMatchingCallback)(void *refcon, io_iterator_t iterator); -static kern_return_t (*IOServiceAddMatchingNotification)(IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching CF_RELEASES_ARGUMENT, IOServiceMatchingCallback callback, void *refCon, io_iterator_t *notification); -static io_object_t (*IOIteratorNext)(io_iterator_t iterator); -static kern_return_t (*IOObjectRelease)(io_object_t object); - -static void didConnectHIDDevice(void *refcon, io_iterator_t iterator) { - io_object_t obj; - while ((obj = IOIteratorNext(iterator))) { - IOObjectRelease(obj); - }; - // delay scanning so disconnected notifications fire first - [[SteamControllerManager sharedManager] performSelector:@selector(scanForControllers) withObject:nil afterDelay:0.01]; -} - -@implementation SteamControllerManager (Listening) - -+ (BOOL)listenForConnections { - static dispatch_once_t onceToken; - static BOOL loadedSymbols = NO; - dispatch_once(&onceToken, ^{ - void * IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOLOAD); - if (IOKit) { -#define LoadSymbol(sym) sym = dlsym(IOKit, #sym); if (!sym) {dlclose(IOKit); return;} - LoadSymbol(IONotificationPortCreate); - LoadSymbol(IOServiceMatching); - LoadSymbol(IONotificationPortGetRunLoopSource); - LoadSymbol(IOServiceAddMatchingNotification); - LoadSymbol(IOIteratorNext); - LoadSymbol(IOObjectRelease); - dlclose(IOKit); - loadedSymbols = YES; - } - }); - - if (!loadedSymbols) { - return NO; - } - - IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault); - CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode); - CFMutableDictionaryRef matchingDict = IOServiceMatching("IOHIDUserDevice"); - io_iterator_t portIterator = 0; - kern_return_t result = IOServiceAddMatchingNotification(notificationPort, - "IOServicePublish", - matchingDict, - didConnectHIDDevice, - NULL, - &portIterator); - if (result == KERN_SUCCESS) { - didConnectHIDDevice(NULL, portIterator); - return YES; - } - return NO; -} - -@end - -#endif diff --git a/xcode/MAME4iOS/UICollectionView+ZeroState.swift b/xcode/MAME4iOS/UICollectionView+ZeroState.swift new file mode 100644 index 00000000..f0313387 --- /dev/null +++ b/xcode/MAME4iOS/UICollectionView+ZeroState.swift @@ -0,0 +1,55 @@ +// +// UICollectionView+ZeroState.swift +// MAME4iOS +// +// Created by Yoshi Sugawara on 4/16/24. +// Copyright © 2024 MAME4iOS Team. All rights reserved. +// + +import UIKit + +extension UICollectionView { + + @objc func showZeroState() { + guard self.backgroundView == nil else { return } + + let backgroundView = UIView() + let titleLabel = UILabel() + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.text = "Welcome to MAME!" + titleLabel.font = UIFont.boldSystemFont(ofSize: 24) + backgroundView.addSubview(titleLabel) + let messageLabel = UILabel() + messageLabel.translatesAutoresizingMaskIntoConstraints = false + messageLabel.font = UIFont.systemFont(ofSize: 18) + messageLabel.text = "ROMs are needed to use MAME. Use the ➕ button to import ROMs from Files.\n\nYou can start by downloading ROMs available for free at mamedev.org." + messageLabel.numberOfLines = 0 + backgroundView.addSubview(messageLabel) + messageLabel.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor).isActive = true + messageLabel.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor, constant: 20).isActive = true + messageLabel.leadingAnchor.constraint(equalTo: backgroundView.readableContentGuide.leadingAnchor, constant: 16).isActive = true + messageLabel.trailingAnchor.constraint(equalTo: backgroundView.readableContentGuide.trailingAnchor, constant: -16).isActive = true + + titleLabel.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor).isActive = true + titleLabel.bottomAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -20).isActive = true + + let button = UIButton(type: .custom) + button.setTitleColor(.label, for: .normal) + button.setTitle("Download from mamedev.org", for: .normal) + button.backgroundColor = tintColor + button.layer.cornerRadius = 12 + button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 24, bottom: 8, right: 24) + button.translatesAutoresizingMaskIntoConstraints = false + backgroundView.addSubview(button) + button.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor).isActive = true + button.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 20).isActive = true + button.addTarget(self, action: #selector(goToDownloadLink(_:)), for: .touchUpInside) + self.backgroundView = backgroundView + } + + @objc func goToDownloadLink(_ sender: UIButton?) { + Task { + await UIApplication.shared.open(URL(string: "https://www.mamedev.org/roms")!) + } + } +}