From ae0365ae21875c524af2ce936bf8f2716617d347 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Wed, 17 Dec 2025 04:44:30 -0500 Subject: [PATCH] start of ui game list config etc --- Manual.txt | 2025 ++++++++++ Src/Graphics/New3D/New3D.cpp | 5 - android/app/build.gradle.kts | 1 + android/app/src/main/AndroidManifest.xml | 9 +- android/app/src/main/assets/Assets/DIR.txt | 6 + .../src/main/assets/Assets/p1crosshair.bmp | Bin 0 -> 295992 bytes .../src/main/assets/Assets/p2crosshair.bmp | Bin 0 -> 295992 bytes android/app/src/main/assets/Config/Games.xml | 3283 +++++++++++++++++ android/app/src/main/assets/Config/Music.xml | 96 + .../app/src/main/assets/Config/Supermodel.ini | 165 + .../src/main/assets/GraphicsAnalysis/DIR.txt | 1 + android/app/src/main/assets/NVRAM/DIR.txt | 1 + android/app/src/main/assets/ROMs/DIR.txt | 1 + android/app/src/main/assets/Saves/DIR.txt | 1 + .../app/src/main/cpp/android_input_system.cpp | 145 +- .../app/src/main/cpp/android_input_system.h | 32 +- android/app/src/main/cpp/gles_presenter.cpp | 13 +- android/app/src/main/cpp/gles_presenter.h | 2 + android/app/src/main/cpp/native-lib.cpp | 167 +- .../com/izzy2lost/super3/AssetInstaller.kt | 41 + .../main/java/com/izzy2lost/super3/GameXml.kt | 80 + .../java/com/izzy2lost/super3/MainActivity.kt | 270 ++ .../com/izzy2lost/super3/Super3Activity.kt | 33 +- .../java/com/izzy2lost/super3/UserDataSync.kt | 82 + .../app/src/main/res/layout/activity_main.xml | 79 + 25 files changed, 6468 insertions(+), 70 deletions(-) create mode 100644 Manual.txt create mode 100644 android/app/src/main/assets/Assets/DIR.txt create mode 100644 android/app/src/main/assets/Assets/p1crosshair.bmp create mode 100644 android/app/src/main/assets/Assets/p2crosshair.bmp create mode 100644 android/app/src/main/assets/Config/Games.xml create mode 100644 android/app/src/main/assets/Config/Music.xml create mode 100644 android/app/src/main/assets/Config/Supermodel.ini create mode 100644 android/app/src/main/assets/GraphicsAnalysis/DIR.txt create mode 100644 android/app/src/main/assets/NVRAM/DIR.txt create mode 100644 android/app/src/main/assets/ROMs/DIR.txt create mode 100644 android/app/src/main/assets/Saves/DIR.txt create mode 100644 android/app/src/main/java/com/izzy2lost/super3/AssetInstaller.kt create mode 100644 android/app/src/main/java/com/izzy2lost/super3/GameXml.kt create mode 100644 android/app/src/main/java/com/izzy2lost/super3/MainActivity.kt create mode 100644 android/app/src/main/java/com/izzy2lost/super3/UserDataSync.kt create mode 100644 android/app/src/main/res/layout/activity_main.xml diff --git a/Manual.txt b/Manual.txt new file mode 100644 index 0000000..e44ecc8 --- /dev/null +++ b/Manual.txt @@ -0,0 +1,2025 @@ + + + #### ### ### + ## ## ## ## + ### ## ## ## ### #### ## ### ## ## #### ## #### ## + ### ## ## ## ## ## ## ### ## ####### ## ## ##### ## ## ## + ### ## ## ## ## ###### ## ## ####### ## ## ## ## ###### ## + ## ## ## ## ##### ## ## ## # ## ## ## ## ## ## ## + #### ### ## ## #### #### ## ## #### ### ## #### #### + #### + + A Sega Model 3 Arcade Emulator. + Copyright 2003-2024 The Supermodel Team + + + USER MANUAL FOR SUPERMODEL + + +================ + Introduction +================ + +Supermodel emulates the Sega Model 3 arcade platform. It uses OpenGL 2.1 and +SDL, and can run on Windows, Linux, and Mac OS X. Development began in January +of 2011 and continues to focus on reverse engineering all aspects of the Model +3 arcade platform. + +In order to use Supermodel, you must legally possess ROM images of Model 3 +games. Learning to operate Supermodel will come with a steep learning curve for +most people. Before seeking out help, please read this user manual carefully. + +Supermodel is distributed as free software under the terms of the GNU General +Public License, included in LICENSE.txt. Additional copyright information for +software included within Supermodel is located at the bottom of this file. + +Windows 64-bit builds are updated automatically and are available for download +on the official Supermodel download page. Linux and MacOS users currently have +to compile/build their own executable from the GitHub source code repository. + +The official Supermodel website: + + http://www.Supermodel3.com + +Supermodel's GitHub source code repository: + + https://github.com/trzy/Supermodel + + +============== + Disclaimer +============== + +Supermodel is a work-in-progress, open source emulator. Emulation in Supermodel +has evolved to the point where most games run well, however there may be some +minor visual issues. Your experience may vary. + + +===================== + Table of Contents +===================== + + --- Introduction + --- Disclaimer + --- Table of Contents + 1. Installing Supermodel + 2. Running Supermodel + 3. Game Compatibility + 4. Video Settings + 5. Audio Settings + 6. Controls + 7. Force Feedback + 8. Save States and NVRAM + 9. Game-Specific Comments and Tips + 10. The Configuration File + 11. Index of Command Line Options + 12. Index of Configuration File Settings + 13. Compiling the Source Code + 14. Contact Information + 15. Acknowledgments + + +============================ + 1. Installing Supermodel +============================ + +To install Supermodel on Windows, extract the ZIP archive containing the +Supermodel executable to a folder of your choice. The following files and +directories should be created: + + Name Description + ---- ----------- + Supermodel.exe Supermodel program. Run this. + README.txt This text file. + LICENSE.txt Supermodel license and terms of use. + CHANGES.txt Change log directly produces directly from the + source code repository. + Assets/ Directory containing multimedia assets required by + Supermodel. + Assets/p1crosshair.bmp Bitmap crosshair image for player 1, used when bit- + mapped crosshairs are enabled + ('--crosshair-style=bmp'). + Assets/p2crosshair.bmp Bitmap crosshair image for player 2. + Config/ Directory where required configuration files are + stored. + Config/Supermodel.ini Configuration file containing default input + settings. + Config/Games.xml Game and ROM set definitions. + NVRAM/ Directory where NVRAM contents will be saved. + ROMs/ Directory conveniently included (but not required) + for placing ROM sets. + Saves/ Directory where save states will be saved. + +Supermodel requires OpenGL 2.1 and a substantial amount of both video and system +memory. A modern 64-bit CPU and a mid range GPU are the minimum recommendations +to achieve consistent frame rates of 60 FPS. + + +========================= + 2. Running Supermodel +========================= + +For now, Supermodel does not include a proper user interface. It is operated +entirely from the command line. Run 'supermodel' without any command line +arguments for an explanation of supported options. + +Supermodel uses MAME-compatible ROM sets. It loads from ZIP archives based on +file checksums and automatically detects games; therefore, file names are not +important. Only one ZIP file can be specified on the command line. For +example: + + supermodel scud.zip -fullscreen + +This will load 'scud.zip' (Scud Race) and run it in full screen mode. + +Initially, inputs are assigned according to the settings in 'Supermodel.ini', +located in the 'Config' subdirectory. + +Note that there is no user interface and all messages are printed to the +command prompt. In full screen mode, they will not be visible. + + +========================= + 3. Game Compatibility +========================= + +Supermodel recognizes all known Model 3 ROM sets. Below is a compatibility +matrix. Other than minor graphical issues, if any, major issues are reported +below: + + ROM Set Title Comments ++------------+-----------------------------------------+-----------------------+ +| bassdx | Sega Bass Fishing (USA) | | +| getbassdx | Get Bass: Sega Bass Fishing (Deluxe) | | +| getbassur | Get Bass: Sega Bass Fishing (Upright) | | +| getbass | Get Bass: Sega Bass Fishing | | ++------------+-----------------------------------------+-----------------------+ +| daytona2 | Daytona USA 2 Battle on the Edge | | +| dayto2pe | Daytona USA 2 Power Edition | | ++------------+-----------------------------------------+-----------------------+ +| dirtdvls | Dirt Devils (Export, Revision A) | | +| dirtdvlsu | Dirt Devils (USA, Revision A) | | +| dirtdvlsj | Dirt Devils (Japan, Revision A) | | +| dirtdvlsau| Dirt Devils (Australia, Revision A) | | +| dirtdvlsg | Dirt Devils (Export, Ver. G?) | | ++------------+-----------------------------------------+-----------------------+ +| eca | Emergency Call Ambulance (Export) | | +| ecau | Emergency Call Ambulance (USA) | | +| ecaj | Emergency Call Ambulance (Japan) | | +| ecap | Emergency Call Ambulance (US proto?) | | ++------------+-----------------------------------------+-----------------------+ +| fvipers2 | Fighting Vipers 2 (Japan, Revision A) | | +| fvipers2o | Fighting Vipers 2 (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| harley | Harley Davidson & L.A. Riders (Rev. B) | | +| harleya | Harley Davidson & L.A. Riders (Rev. A) | | ++------------+-----------------------------------------+-----------------------+ +| lamachin | L.A. Machineguns: Rage of the Machines | | ++------------+-----------------------------------------+-----------------------+ +| lemans24 | Le Mans 24 (Japan, Revision B) | | ++------------+-----------------------------------------+-----------------------+ +| lostwsga | The Lost World Arcade (Japan, Rev A) | | +| lostwsgp | The Lost World Aracde (Location Test) | | ++------------+-----------------------------------------+-----------------------+ +| magtruck | Magical Truck Adventure (Japan) | | +| mgtrkbad | Magical Truck Adventure (bit rot dump) | | ++------------+-----------------------------------------+-----------------------+ +| oceanhun | The Ocean Hunter (Japan, Revision A) | | +| oceanhuna | The Ocean Hunter (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| scud | Scud Race (Export, Twin/DX) | | +| scudau | Scud Race (Australia, Twin/DX) | | +| scuddx | Scud Race (Export, Deluxe, Revision A) | | +| scudplus | Scud Race (Export, Twin/DX, Revision A) | | +| scudplusa | Scud Race (Export, Twin/DX) | | ++------------+-----------------------------------------+-----------------------+ +| skichamp | Ski Champ (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| spikeofe | Spikeout Final Edition (Export) | | ++------------+-----------------------------------------+-----------------------+ +| spikeout | Spikeout (Export, Revision C) | | ++------------+-----------------------------------------+-----------------------+ +| srally2 | Sega Rally 2 (Export) | | +| srally2p | Sega Rally 2 (Prototype) | proto is non working | +| srally2pa | Sega Rally 2 (Prototype Version A) | proto is non working | +| srally2dx | Sega Rally 2 (Export, Deluxe) | DX set may run slow | ++------------+-----------------------------------------+-----------------------+ +| swtrilgy | Star Wars Trilogy (Export, Revision A) | see note 1 below | +| swtrilgya | Star Wars Trilogy (Export) | see note 1 below | +| swtrilgyp | Star Wars Trilogy (Location Test) | see note 2 below | ++------------+-----------------------------------------+-----------------------+ +| vf3 | Virtua Fighter 3 (Japan, Revision D) | | +| vf3c | Virtua Fighter 3 (Japan, Revision C) | | +| vf3a | Virtua Fighter 3 (Japan, Revision A) | | +| vf3tb | Virtua Fighter 3 Team Battle (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| von2 | Virtual On Oratorio Tangram (Revision B)| | +| von2a | Virtual On Oratorio Tangram (Revision A)| | +| von2o | Virtual On Oratorio Tangram | | +| von254g | Virtual On Oratorio Tangram (Ver. 5.4g) | | ++------------+-----------------------------------------+-----------------------+ +| vs2 | Virtua Striker 2 (Step 2.0, Export, USA)| | +| vs215 | Virtua Striker 2 (Step 1.5, Export, USA)| | +| vs215o | Virtua Striker 2 (Step 1.5, Japan test?)| | ++------------+-----------------------------------------+-----------------------+ +| vs298 | Virtua Striker 2 '98 (Step 2.0, Japan) | | +| vs29815 | Virtua Striker 2 '98 (Step 1.5, Japan) | | ++------------+-----------------------------------------+------------+----------+ +| vs2v991 | Virtua Striker 2 '99.1 (Step 2.1 Export, USA, Rev B) | | +| vs299a | Virtua Striker 2 '99 (Step 2.1 Export, USA, Rev A) | | +| vs299 | Virtua Striker 2 '99 (Step 2.1 Export, USA) | | +| vs299j | Virtua Striker 2 '99.1 (Step 2.1 Japan, Rev B) | | +| vs29915 | Virtua Striker 2 '99.1 (Step 1.5 Export, USA, Rev B) | | +| vs29915a | Virtua Striker 2 '99 (Step 1.5 Export, USA) | | +| vs29915j | Virtua Striker 2 '99.1 (Step 1.5 Japan, Rev B) | | ++------------+------------------------------------------------------+----------+ + +Note 1: Set game to U/R in game assignment in the TEST MENU to bypass the + "WAIT SETUP THE FEEDBACK STICK" screen +Note 2: Set game to SD in game assignment in the TEST MENU or wait a few seconds + for the game to continue past the "WAIT SETUP THE FEEDBACK STICK" screen + + +===================== + 4. Video Settings +===================== + +Supermodel may be run in either windowed (default) or full screen mode. By +default, it automatically adjusts the display area to retain the aspect ratio of +the Model 3's native (and Supermodel's default) resolution, 496x384, introducing +letterboxing (black borders) as needed. + +To stretch the image to fill the display, use the '-stretch' option. + +Wide aspect ratios that extend the field of view horizontally are supported by +using the '-wide-screen' option. This works well for most common aspect ratio +and additional geometry will be visible but at extremely wide aspect ratios, +objects will be culled by games, which are not aware of the extended field of +view. Tile-based 2D background layers will not be stretched and will be +letterboxed. + +To stretch the 2D background layers, use '-wide-bg'. Many games use 2D back- +grounds instead of 3D skyboxes and when playing in wide screen mode, this option +often enhances their appearance. + +By default, Supermodel limits the frame rate to 60 frames per second. This has +no impact on performance except when the frame rate exceeds 60 FPS on fast +systems. Frame rate limiting can be disabled with the '-no-throttle' option. +Some video drivers may continue to lock to the refresh rate. + +The actual Model 3 refresh rate is 57.524 Hz. The '-true-hz' option will switch +to this rate. + +To change the resolution, use the '-res' command line option. For full screen +mode, use '-fullscreen'. For example, to set a full screen 1920x1080 mode, +try: + + supermodel game.zip -res=1920,1080 -fullscreen + +Video settings may also be specified globally or on a per-game basis in the +configuration file, described elsewhere in this manual. + +Changing video modes at run-time is not yet supported. + + +===================== + 5. Audio Settings +===================== + +All Model 3 games have a sound board that is used for sound effects and, in +some games, background music. A few games use additional Digital Sound Boards +(DSB) for MPEG music. 'Music' in Supermodel refers exclusively to MPEG music +produced by the DSB and 'sound' refers to both the sound effects and background +music produced by the regular sound board. + +Model 3 sound and MPEG music are generated separately and then mixed by an +amplifier. The relative signal levels are not known, so Supermodel simply +outputs all audio at full volume. This causes the MPEG music to be too quiet +in some games ('Scud Race', 'Daytona USA 2') and too loud in others ('Star Wars +Trilogy'). The '-sound-volume' and '-music-volume' options can be used to +change the volume. As arguments, they take a volume level in percent ranging +from 0 (muted) to 200% (maximum, doubled amplitude). For example: + + supermodel game.zip -sound-volume=50 -music-volume=170 + +This command line cuts the sound volume in half and increases the music volume +by 70%. + +The F9 and F10 keys can be used to adjust music volume during run-time, while +F11 and F12 control sound volume. + +Clipping and distortion will occur if the combined sound and music volume +levels become too high. + +To disable sound and music board emulation altogether, use the '-no-sound' and +'-no-dsb' options. These will not merely mute the corresponding audio channels +but will prevent the audio co-processors from being emulated altogether and +may slightly improve performance, especially on single-core systems. Save +states generated with these settings may not be able to properly restore audio +when loaded after emulation is re-enabled. + +Audio settings may also be specified globally or on a per-game basis in the +configuration file, described elsewhere in this document. + +Please keep in mind that MPEG music emulation is preliminary and the decoder is +buggy. Periodic squeaks and pops occur on many music tracks. The Sega Custom +Sound Processor (SCSP) emulator, used for sound emulation, is also still quite +buggy. Sound glitches are known to occur now and then, and many sounds and +tunes do not sound quite correct yet. + + +=============== + 6. Controls +=============== + +Game controls are fully configurable and can be mapped to keyboards, mice, and +game controllers. Emulator functions, on the other hand, cannot be changed and +are listed below. + + Function Key Assignment + -------- -------------- + Exit Escape + Pause Alt-P + Reset Alt-R + Clear NVRAM Alt-N + Crosshairs (for light gun games) Alt-I + Toggle 60 Hz Frame Limiting Alt-T + Save State F5 + Load State F7 + Change Save Slot F6 + Decrease Music Volume F9 + Increase Music Volume F10 + Decrease Sound Volume F11 + Increase Sound Volume F12 + + +Learning and Configuring Game Controls +-------------------------------------- + +Settings for game controls are stored in 'Supermodel.ini'. To learn the +current configuration, use the '-print-inputs' command line option. + + supermodel -print-inputs + +If you delete 'Supermodel.ini', all inputs will become unmapped and will have +to be reconfigured! + +To reconfigure the inputs, use the '-config-inputs' option. A blank window +will open up and instructions will be printed to the command prompt. Read them +carefully! Pressing the Escape key exits without saving changes. Pressing 'q' +will save changes to 'Supermodel.ini'. You can choose which input to configure +using the Up and Down arrow keys. Clearing an input means it will be unusable. + +If the configuration dialog is not responding to your key presses or mouse and +joystick movements, make sure that the blank pop-up window rather than the +command prompt is selected. This may seem counter-intuitive but inputs are +captured by the graphical window while messages are printed to the command +prompt. + + +Calibrating Controls +-------------------- + +Some controls, in particular the accelerator and brake pedals of steering +wheels, may need calibrating before they will function properly with +Supermodel. + +In order to calibrate a control, press 'b' when configuring the inputs and +follow the on-screen instructions carefully. + +NOTE: After calibration, you must remap the control again or it will not +function properly! + +Several input-related settings are stored in the configuration file. Refer to +the section discussing the configuration file for more details. + + +XBox 360 Controllers +-------------------- + +For full XBox 360 controller support, the XInput system must be used on Windows +('-input-system=xinput'). Otherwise, the left and right trigger buttons cannot +be mapped individually and force feedback will not work. Please read the +section titled 'Input Systems', further below. + +Mac OS X users can use Colin Munro's XBox 360 controller driver: +http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver + + +Analog Controls +--------------- + +Analog controls, such as steering wheels, pedals, and the light gun axes, can +be mapped to analog controllers, such as joysticks, wheels, and mice, if +available. Under digital control, the analog value will increment or decrement +until it reaches its maximum/minimum values. The rate of change can only be +set manually in the configuration file. These options are described elsewhere +in this document. + +Most analog inputs, such as the steering wheel, require two settings to be +controlled digitally. For example, the steering wheel can be turned left and +right by setting 'Steer Left' and 'Steer Right', or it can be mapped to a +single analog control ('Full Steering'). + + +Shifting Gears +-------------- + +Racing game gears can be mapped to individual buttons, including one for the +neutral position, or can be shifted sequentially. Games which only provide two +gears name them 'Up' and 'Down'. These should not be confused with 'Shift Up' +and 'Shift Down', the sequential shift commands. + + +Virtual On Twin Joysticks +------------------------- + +'Virtual On Oratorio Tangram' features a twin joystick control scheme similar +to a tracked vehicle (e.g. a tank). Movement is accomplished by pushing both +joysticks in the same direction. Pushing and pulling in opposite directions +will turn the robot, while pulling the joysticks apart sideways or pushing them +inwards are for jumping and falling back down to the ground, respectively. + +Supermodel supports mapping the individual joysticks but also provides 'macro' +controls by default. These allow all the necessary twin joystick commands to +be replicated with individual controls. The mapping is below: + + Macro Control Twin Joystick Equivalent + ------------- ------------------------ + Turn Left Left joystick down, right joystick up. + Turn Right Left joystick up, right joystick down. + Forward Both joysticks up. + Reverse Both joysticks down. + Strafe Left Both joysticks left. + Strafe Right Both joysticks right. + Jump Left joystick left, right joystick right. + Crouch Left joystick right, right joystick left. + + +Light Guns +---------- + +Light gun axes can be mapped to mice, analog controls, or even digital buttons. +To simulate pointing off-screen (required in order to reload), a 'point off- +screen' input is provided which, for as long as it is pressed, will aim the gun +off-screen. To reload, hold down this button and then, while holding it, press +the trigger. For easier reloading, the 'InputAutoTrigger' setting can be +enabled in the configuration file (described elsewhere in this manual). + +Crosshairs will be visible in full screen mode and can also be enabled in +windowed modes. Use Alt-I to cycle through different crosshair options (enable +for a single player, both, or none). + +For multiple mouse support, allowing two mice or PC light guns to be mapped, +Raw Input must be used. This is supported only on Windows and is described +below. + +For specific information on using Sinden Light guns with Supermodel, refer to +the Sinden Light gun Wiki: + +https://www.sindenwiki.org/wiki/Supermodel_M3 + + +Input Systems +------------- + +Supermodel supports multiple input APIs to provide the best possible +compatibility for different input devices and configuration schemes. On +Windows, the default is DirectInput. On all other platforms, SDL is the only +option available. + +Windows users can select between four different input systems: + + - DirectInput. Selected with '-input-system=dinput'. This is the default. + It provides the best support for PC game controllers and, when emulating + force feedback, allows all effects (if the controller supports them). + - XInput. Selected with '-input-system=xinput'. This must be used with + XBox 360 controllers, otherwise some buttons will not function properly + and force feedback will not work. + - Raw Input. Selected with '-input-system=rawinput'. This is intended for + use with multiple mice and keyboards but is not recommended otherwise. + - SDL. Selected with '-input-system=sdl'. The standard, cross-platform + input system intended for non-Windows builds. It is accessible on + Windows but does not provide full support for all devices. + +When switching input systems with '-input-system', you must also configure your +inputs using the same option. For example, when running Supermodel with XInput +('supermodel game.zip -input-system=xinput'), you must also configure with +XInput ('supermodel -config-inputs -input-system=xinput'). Many settings are +not compatible between input systems. + +A common mistake is to configure inputs using one system and then launch +Supermodel with another. + + +Troubleshooting +--------------- + +Common input-related problems are discussed below. + + + Problem: + No response while configuring inputs. + + Solution: + Make sure the main window is top window, especially with the SDL input + system. + + --------- + + Problem: + An attached controller is not recognized at all. + + Solution: + - On the input configuration screen, press 'I' to see information about + the input system and check that the required controller is in the + list. + - If it is not in the list, check that it is plugged in and visible in + the operating system. If a controller is plugged in while Supermodel + is running, Supermodel will need to be restarted in order for it to + recognize the new controller. + - On Windows, if after checking the above points the controller is + still not in the list, try running Supermodel with the SDL input + system by specifying '-input-system=sdl' on the command line. + + --------- + + Problem: + Unable to select a particular joystick axis when configuring a mapping. + + Solution: + This may mean that the joystick axis needs calibrating. This can be + done by pressing 'b' on the configuration screen and following the + instructions. + + --------- + + Problem: + Steering wheel pedals are not being recognized. + + Solution: + For some steering wheels where the pedals have been configured in a + 'split axis' mode (ie. not sharing a combined axis), the values that + the pedals send are inverted. This means that without configuration, + Supermodel will be unable to recognize them. To fix this, they should + be calibrated on the calibration screen in the same way as above. + + --------- + + Problem: + A control isn't working despite having been calibrated. + + Solution: + After calibration, while still in the configuration dialog, you must + remap the control again for the new settings to take effect. + + --------- + + Problem: + XBox 360 controller trigger buttons cannot be triggered simultaneously + or no rumble effects. + + Solution: + On Windows, make sure that you specify '-input-system=xinput'. + + +===================== + 7. Force Feedback +===================== + +Force feedback is presently supported in 'Scud Race' (including 'Scud Race +Plus'), 'Daytona USA 2' (both editions), and 'Sega Rally 2' on Windows only. To +enable it, use the '-force-feedback' option. + +Drive board ROMs are required. They first appear in the MAME 0.143u6 ROM +catalog and at the time of this writing, have not yet widely proliferated. + + Game Drive Board ROM File Size Checksum (CRC32) + ---- -------------------- ---- ---------------- + Daytona USA 2 epr-20985.bin 64 KB B139481D + Scud Race epr-19338a.bin 64 KB C9FAC464 + Sega Rally 2 epr-20512.bin 64 KB CF64350D + +The sizes and checksums must match those listed above. The file names may be +different but will almost certainly contain the same identifying numbers. +Ensure that the appropriate drive board ROM files are present in the +corresponding games' ZIP archives, otherwise Supermodel will silently proceed +without force feedback. + +Force feedback will only work with the DirectInput (the default on Windows) and +XInput input systems. XInput is intended only for XBox 360 controllers, which +do not support force feedback through DirectInput. + + +Tuning Force Feedback +--------------------- + +Force feedback can be enabled and tuned in the configuration file. Setting +'ForceFeedback' to 1 enables it: + + ForceFeedback = 1 + +There are four DirectInput effects: constant force, self centering, friction, +and vibration. The strength of each can be tuned with the following settings: + + DirectInputConstForceMax = 100 + DirectInputSelfCenterMax = 100 + DirectInputFrictionMax = 100 + DirectInputVibrateMax = 100 + +They are given as percentages and represent the maximum strength for each +effect. A setting of 0 disables them entirely. Values above 100% are +accepted but may clip or distort the effect. By default, they are set to 100%, +as shown above. + +XInput devices only support vibration feedback via the left and right motors. +Because the characteristics of the motors are different, the effects will feel +somewhat asymmetric. The constant force effect is simulated with vibrations. +The relevant settings are: + + XInputConstForceThreshold = 30 + XInputConstForceMax = 100 + XInputVibrateMax = 100 + +The constant force threshold specifies how strong a constant force command must +be before it is sent to the controller as a vibration effect (whose strength is +determined by XInputConstForceMax). The default values are shown above and +will require calibration by the user on a game-by-game basis to achieve the +best feel. + + +============================ + 8. Save States and NVRAM +============================ + +Save states are saved and restored by pressing F5 and F7, respectively. Up to +10 different save slots can be selected with F6. All files are written to the +Saves/ directory, which must exist beforehand. If you extracted the Supermodel +ZIP file correctly, it will have been created automatically. + +If a Model 3 co-processor (ie. sound board, DSB, drive board) is disabled when +a save state is taken, it will not resume normal operation when the state is +loaded, even if Supermodel is running with the co-processor re-enabled. The +drive board (and consequently, force feedback effects) is explicitly disabled +for the remainder of the session if a save state with an inactive drive board +is loaded. Audio co-processors are not, and therefore audio playback may +eventually resume after the audio boards have booted themselves up. + +Non-volatile memory (NVRAM) consists of battery-backed backup RAM and an EEPROM +chip. The former is used for high score data and statistics whereas the latter +stores machine settings (often accessed using the Test buttons). NVRAM is +automatically saved each time Supermodel exits and is loaded at start-up. It +can be cleared by deleting the NVRAM files or pressing Alt-N. + + +======================================= + 9. Game-Specific Comments and Tips +======================================= + +This section contains additional game-specific setup information, workarounds +for emulation issues, and region codes for changing the region. + + +Cyber Troopers Virtual-On Oratorio Tangram +------------------------------------------ + +To change the region, enter Test mode and press Start, Start, Service, Start, +Start, Start, Service, Service, Test. + + +Daytona USA 2 and Daytona USA 2 Power Edition +--------------------------------------------- + +By default, the 'Power Edition' ROM set features remixed music lyrics by +Takenobu Mitsuyoshi. These can be changed back to the Dennis St. James version +in the Test Menu, under 'Game Assignments'. + +The region change menu can be accessed in two different ways: + + 1. Enter Test mode and push the Service and Test buttons simultaneously. + 2. Enter Test mode. Then, hold Start and press VR4, VR4, VR2, VR3, VR1, VR3, + VR2. + + +Dirt Devils +----------- + +To access the region change menu, enter Test mode and press Start, Start, +Service, Start, Start, Start, Service, Test. + + +Emergency Call Ambulance +------------------------ + +To play as the paramedics pushing a stretcher, highlight 'Manual' at the +transmission select screen and then perform the following sequences of gear +shifts: Shift Up, Shift Up, Shift Down, Shift Down, Shift Up. + +Supermodel presently only emulates a sequential shifter but cabinets with a 4- +way H-type shifter exist and the shift sequence on this units is: 3, 3, 4, 4, 3 +or 1, 1, 2, 2, 1. + + +Fighting Vipers 2 +----------------- + +To change the region, in the 'Game Assignments' screen in Test mode, set the +cursor at the 'Country' line and press: Left, Left, Left, Right, Right, Left. +The country can then be changed using the Test switch. + + +Get Bass and Sega Bass Fishing +------------------------------ + +To change the region, enter Test mode and go to the second screen of the CRT +test ('C.R.T. Test 2/2'). Press the Service button four times and then exit the +CRT test. Next, enter 'Game Assignments', press Service three times, then press +and hold Service and while holding, press Test. The region select menu will +appear. + + +Harley-Davidson and L.A. Riders +------------------------------- + +To change the region, enter Test mode and in the 'Game Assignments' screen, +press Shift Up, Shift Up, Shift Down, Shift Down, View, Music, View, Music. + + +L.A. Machineguns +---------------- + +To change the region, enter Test mode and press P1 Start, P1 Start, Service, P1 +Start, P1 Start, P1 Start, Service, Test. + + +Le Mans 24 +---------- + +To change the region, enter Test mode and press Start, Start, Service, Service, +Start, Test. + + +Magical Truck Adventure +----------------------- + +To change the region, enter Test mode and press P1 Start, P1 Start, Service, P1 +Start, Service, Test. + + +Sega Rally 2 +------------ + +As with 'Star Wars Trilogy', you may experience problems if you attempt to +start a game before any 3D graphics are displayed (for example, during the Sega +logo). + +To change the region, enter Test mode and perform the following series of short +(press and release) and long (press and hold for 4-5 seconds) Service button +presses: 4 short, 2 long, 2 short, 1 long. + + +Ski Champ +--------- + +To change the region, enter Test mode and press Select 1, Select 3, Select 1, +Select 3, Service, Service. + + +Spikeout +-------- + +To change the region, enter Test mode, place the cursor on the 'Game +Assignments' line and press Charge, Start, Jump, Start, Start, Start, Shift, +Start, Start. The region change menu will be revealed. + + +Spikeout Final Edition +---------------------- + +To change the region, enter Test mode, place the cursor on the +'Game Assignments' line, and while holding the Service button press Jump, Start, +Jump, Start. Then, release the Service button and press Shift, Start, Start, +Start, Charge, Start, Start, Shift, Start, Test. The region change menu will be +revealed. + + +Star Wars Trilogy +----------------- + +Inserting coins and starting a game before any 3D graphics have been displayed +in attract mode will result in a PowerPC crash before the stage loads. This is +caused by an unknown emulation bug. Simply wait until the Darth Vader sequence +appears before attempting to start a game. + +If 'Star Wars Trilogy' is booting directly into the stage select screen, it is +probably because you exited Supermodel with credits still in the machine. Clear +the NVRAM (Alt-N) and reset the game (Alt-R). + +To change the region, enter Test mode and perform the following series of short +(press and release) and long (press and hold for 4-5 seconds) Service button +presses: 3 short, 2 long, 2 short, 1 long. + + +The Lost World +-------------- + +To reload, the light gun must be pointed off-screen by pressing (and holding) +the 'off-screen' button and, simultaneously, pressing the trigger to shoot. +This behavior can be changed with the 'InputAutoTrigger' setting in the +configuration file. + +To change the region, enter Test mode and press P1 Start, P1 Start, Service, P1 +Start, Service, Test. + + +The Ocean Hunter +---------------- + +To change the region, enter Test mode and in 'Game Assignments', press P1 Start, +P2 Start, P1 Start, P2 Start, P1 Start, P2 Start, P2 Start. + + +Virtua Striker 2 '98 +-------------------- + +To change the region, enter Test mode and in 'Game Assignments', perform the +following series of short (press and release) and long (press and hold for 4-5 +seconds) Service button presses: 1 long, 3 short, 1 long. + + +============================== + 10. The Configuration File +============================== + +Supermodel reads configuration settings from 'Supermodel.ini' located in the +'Config' subdirectory. If Supermodel was installed properly, a default file +should have been created. When starting up, Supermodel parses settings in the +following order: + + 1. Global settings are read from 'Supermodel.ini'. These include input + mappings and apply to all games. + 2. If the ROM set was loaded correctly, game-specific settings are read + from 'Supermodel.ini', overriding settings from step 1. + 3. Command line options are applied, overriding settings from the previous + steps. + +In other words, command line options have the highest precedence, followed by +game-specific settings, and lastly, global settings. + +An index of all allowed settings is provided further below in this document. + +NOTE: Type carefully! Supermodel will not report syntax errors nor detect +typos. Carefully read the discussion of the file syntax below. To verify that +your intended settings are taking effect, check the 'error.log' file that is +produced by Supermodel during each run. + + +File Structure and Syntax +------------------------- + +The configuration file is a list of settings grouped by sections. All setting +names and their arguments are case sensitive. They take the form: + + Name = Argument + +Only one setting per line is allowed. Only two types of arguments are allowed: +integers and strings. The choice of which to use is determined by the setting. +Integers can be negative. Strings can contain any characters and are enclosed +by double quotes. Examples: + + IntegerSetting1 = 0 + IntegerSetting2 = 65536 + IntegerSetting3 = -32768 + StringSetting1 = "This is a string." + StringSetting2 = "123" + StringSetting3 = "1+1, abc, these are all valid characters!" + +Many settings are simply boolean variables expecting an integer value of either +0 (disabled) or 1 (enabled). Some settings require values and others take +strings for file names or input mappings. + +Section names appear in between square brackets on their own lines. + + [ SectionName ] + +Settings that appear at the beginning of the file without a preceding section +are automatically assigned to 'Global'. + +Comments begin with a semicolon and extend until the end of the line. + + ; This is a comment. + + +Global and Game-Specific Sections +--------------------------------- + +Sections determine whether settings are applied globally, to all games, or to +specific games. Game-specific settings will override global settings and can +be used to tune Supermodel on a game-by-game basis. Global settings must be +placed in the 'Global' section and game-specific settings in sections named +after the ROM sets. ROM sets must be typed in lower case and use the MAME +(http://www.mamedev.org) naming convention. They can be obtained by running +'supermodel -print-games'. + +Input mappings are special. They are only read from the 'Global' section! + +In the example below, custom configurations are created for 'Scud Race' and +'The Lost World'. All other games will use the global settings. + + [ Global ] + + ; Run full screen at 1024x768 + XResolution = 1024 + YResolution = 768 + FullScreen = 1 + + ; Scud Race + [ scud ] + + ; Run at 1080p + XResolution = 1920 + YResolution = 1080 + + ; Music is too quiet by default + SoundVolume = 50 + MusicVolume = 200 + + ; The Lost World + [ lostwsga ] + + PowerPCFrequency = 25 ; run PowerPC at 25 MHz + +In this example, only 'Scud Race' will run at 1920x1080. All other games will +use 1024x768. 'Scud Race' will also have altered volume settings. 'The Lost +World' will be run with a lower PowerPC frequency but all other games will use +the default. + + +Input Mappings +-------------- + +Input mappings are specified with strings that have their own internal syntax. +They may only be specified in the 'Global' section and are ignored elsewhere. + +A mapping may be specified in one of the following three ways: + + KEY_XXX For a key XXX on a keyboard (eg. KEY_A, KEY_SPACE, KEY_ALT). + + MOUSE_XXX For a mouse axis (X or Y), wheel, or button (1-5) (eg. + MOUSE_XAXIS, MOUSE_WHEEL_UP, MOUSE_BUTTON3). + + JOY1_XXX For a joystick axis (X, Y, Z, RX, RY, RZ), POV controller + JOY2_XXX (1-4), or button (1-12) (e.g. JOY1_XAXIS, JOY3_POV2_UP, + ... JOY2_BUTTON4). + +Joysticks, including game pads and steering wheels, are numbered from 1 on up. +Their ordering is system dependent and if controllers are swapped between +invocations of Supermodel, then the ordering may change. If the number is +omitted then the mapping applies to all joysticks, eg. JOY_BUTTON1 means button +1 on any attached joystick. + +In addition to the above, when using the Raw Input system on Windows (command +line option '-input-system=rawinput') it is possible to refer to a particular +mouse or keyboard by including its number in the mapping, eg. KEY2_A or +MOUSE3_XAXIS. This allows the independent mapping of dual mice or dual light +guns (which present themselves as mice to Supermodel) for 'The Lost World'. + +Mappings may be combined with a plus '+' or a comma ',' and negated with an +exclamation mark '!'. The plus signifies that both mappings must be active to +trigger the input and the comma means that just one needs to be active. The +exclamation mark means that a mapping must not be active. + +Of the above the comma is the most useful. It allows the mapping of several +different control methods to a single input. For example, both a keyboard and +a joystick could be mapped to the same input. When combining mappings in this +way, the ordering is important as mappings earlier in the list will take +precedence over later ones (see example 3 below). + +When an axis is mapped to a digital input, its direction must be qualified with +a _POS or _NEG after the axis name. For example, JOY1_XAXIS_NEG indicates that +joystick 1 must be pushed left (negative direction) in order to activate the +input. Additionally, the following shortcuts are provided for some common +joystick axis directions: + + JOY1_LEFT is the same as JOY1_XAXIS_NEG + JOY1_RIGHT is the same as JOY1_XAXIS_POS + JOY1_UP is the same as JOY1_YAXIS_NEG + JOY1_DOWN is the same as JOY1_YAXIS_POS + +Another axis manipulator is _INV. This inverts an axis when it is mapped to an +analog input. JOY1_YAXIS_INV could be used for example to invert the y-axis +of joystick 1 in Star Wars Trilogy so that it acts like a plane's yoke. + +Examples: + + 1. InputLeft = "KEY_LEFT,JOY1_LEFT" + + Digital input InputLeft will be triggered whenever the left arrow key + is pressed or joystick 1 is pushed left. + + 2. InputStart1 = "KEY_ALT+KEY_S" + + Both Alt and S must be pressed at the same time in order to trigger the + digital input InputStart1 (player 1 Start button). + + 3. InputSteering = "JOY1_XAXIS,MOUSE_XAXIS" + + Analog steering can be controlled with either the joystick or by moving + the mouse. Due to the ordering used, the joystick will take preference + and so the mouse can only control the steering when the joystick is + released. + +The complete list of input mappings can be found in the settings index below or +by generating a configuration file using '-config-inputs'. + + +Controller Settings +------------------- + +Several settings are available to fine tune the way Supermodel handles +controllers. + + +Keyboard Settings: + +The rate at which analog controls' values increase or decrease when controlled +by a key can be set with the InputyKeySensitivity option. It takes a value in +the range 1-100, with 100 being the most sensitive (fastest response) and 1 +being the least sensitive (slowest response). The default value is 25. + +The speed with which analog controls return to their rest or 'off' values after +a key has been released is configured with InputKeyDecaySpeed. This takes a +value in the range 1-200 and is expressed as a percentage of the attack speed. +The default setting is 50, which means that analog controls take twice as long +to return to their off values as they do to reach their maximum and minimum +values. + + +Mouse Settings: + +For a mouse it is possible to specify a rectangular area in the center of +Supermodel's display within which the mouse is considered to be inactive. The +width and height of this zone is set with the options 'InputMouseXDeadZone' and +'InputMouseYDeadZone'. They are expressed as a percentage 0-99 of the width +and height of the display. The default value is 0 which disables this option. + + +Joystick Options: + +Each joystick axis has a set of parameters that can be configured and these are +given below. The joystick number and axis name (X, Y, Z, RX, RY, or RZ) must +be substituted in for '**' like so: InputJoy1XDeadZone. If the joystick number +is omitted, then the option becomes the default for all joysticks, eg. +InputJoyYSaturation. + +InputJoy**Deadzone: The dead zone of a joystick axis is the size of the zone +around its central or 'off' position within which the axis is considered to be +inactive. It is important to specify this since joysticks rarely return to +their perfect center when released. The dead zone is expressed as a percentage +0-99 of the total range of the axis and the default value is 2%. + +InputJoy**Saturation: The saturation of a joystick axis is the point at which +the axis is considered to be at its most extreme position. It can be thought +of as a measure of the sensitivity of the axis. Like the dead zone, it is +expressed as a percentage of the axis range but its value may be larger than +100, up to a maximum of 200. A value of 50 means that the joystick only needs +to be moved halfway in order for Supermodel to see it as fully extended. +Conversely a value of 200 means that when the joystick is at its extreme +position Supermodel will see it as only halfway. The default sensitivity is +100%, which corresponds to a 1-to-1 mapping. For playing driving games with a +game pad, it is sometimes a good idea to use a value larger than 100% so that +the steering feels less sensitive on a thumbstick. + +InputJoy**MinVal, InputJoy**OffVal, InputJoy**MaxVal: Normally a joystick axis +will send values to Supermodel that fall within the range -32768 to +32767, +with 0 representing the central or 'off' position. However, in some cases an +axis may behave differently. This has been observed with certain steering +wheels that have their pedals configured in a 'split axis' mode. The pedals +invert their range so that they send -32768 when released and +32767 when fully +depressed. In order for Supermodel to be able to handle such cases the +minimum, off, and maximum values of the axis can be specified with these three +options. The default values are MinVal -32768, OffVal 0, and MaxVal 32767 but +with the pedal example just given, they would need to be set to MinVal 32767, +OffVal 32767, and MaxVal -32768. + +In most cases it will be unnecessary to alter the DeadZone, MinVal, OffVal, and +MaxVal options by hand as calibrating a joystick axis from within the input +configuration screens of Supermodel will set them automatically. Only the +sensitivity option is not configured in this way. + + +Additional Options: + +InputAutoTrigger, InputAutoTrigger2: When playing light gun games such as 'The +Lost World', the gun must be pointed off-screen and then fired in order to +reload. With a mouse and the default mappings, this requires clicking both the +right and left mouse buttons. In order to be able to reload the gun with just +a single click of the right mouse button, enable these options for players 1 +and 2 by setting them to 1. By default they are disabled (value 0). + +Enabling these options is also recommended when playing with PC light guns as +these often map the action "point off screen and press trigger" to a single +right mouse button click. Supermodel has been tested successfully with +UltiMarc's AimTrak light-guns. In order to use dual PC light-guns for two +player games, the Raw Input system must be selected with the command line +option '-input-system=rawinput' and the input mappings configured for each gun. + + +===================================== + 11. Index of Command Line Options +===================================== + +All valid command line settings are listed here, ordered by category. Defaults +are given under the assumption that they have not been changed in the +configuration file. + +Square brackets ('[' and ']') indicate optional parameters. Angled brackets +('<' and '>') indicate mandatory parameters. Do not type the brackets. No +spaces may appear inside of an option. For example, 'supermodel game.zip +-ppc-frequency=25' is correct but 'supermodel game.zip -ppc-frequency = 25' is +not. All options are case sensitive. + + + Option: -? + -h + + Description: Prints a message with an overview of the command line + options and how to run Supermodel. + + ---------------- + + Option: -print-games + + Description: Lists all supported ROM sets. Not all supported ROM sets + are playable yet. + + ---------------- + + Option: -no-threads + + Description: Disables multi-threading. When enabled (the default), the + PowerPC, graphics rendering, and user interface are run in + one thread, sound emulation in a second thread, and the + drive board in a third. It is enabled by default. On + single-core systems, multi-threading adds overhead and + slows Supermodel down but can make audio sound a bit + smoother. + + ---------------- + + Option: -ppc-frequency= + + Description: Sets the PowerPC frequency in MHz. The default is 50. + Higher frequencies will allow games to do more processing + per frame, making them run faster in 'virtual' time, but + may slow down Supermodel on weaker computers. Supermodel's + performance can frequently be improved by lowering the + PowerPC frequency (25 MHz works in many games). In some + games, this will cause timing problems and jerky + performance. The optimum frequency will vary from game to + game and system to system. Valid values for are 1 to + 1000. + + ---------------- + + Option: -fullscreen + + Description: Runs in full screen mode. The default is to run in a + window. + + ---------------- + + Option: -no-throttle + + Description: Disables 60 FPS throttling. The Model 3 runs at a 60 Hz + refresh rate, which Supermodel enforces if this option is + omitted. Unthrottled operation may not work on some + systems because graphics drivers may lock the refresh rate + on their own. + + ---------------- + + Option: -print-gl-info + + Description: Prints OpenGL driver information and quits. + + ---------------- + + Option: -res=, + + Description: Resolution of the display in pixels, with being width + and being height. The default is 496x384, the Model + 3's native resolution. + + ---------------- + + Option: -crtcolors= + + Description: Emulates other regional TV standards of the time. + These came with the caveat that displaying video content on + other regions TVs would/should have needed color corrections + (which was usually not done for video games, only broadcasting, etc). + Nowadays the world has settled on the same standards, and we + can transform from the old ones to the (for now) common sRGB. + + This means one can choose between 6 different mappings now: + 0=none (default) + 1=ARI/D93 (recommended for all JP developed games, most likely true for all Model 3 games) + 2=PVM_20M2U/D93 + 3=BT601_525/D93 + 4=BT601_525/D65 (recommended for all US developed games) + 5=BT601_625/D65 (recommended for all EUR/AUS developed games) + + ---------------- + + Option: -upscalemode= + + Description: Selects the filter for upscaling the 2D layers when + using a higher output resolution. + + This means one can choose between 4 different filters now: + 0=none (sharp pixels) + 1=biquintic (a bit smoother) + 2=bilinear (smooth, but lowest quality, default) + 3=bicubic (smoothest) + + ---------------- + + Option: -show-fps + + Description: Shows the frame rate in the window title bar. + + ---------------- + + Option: -frag-shader= + -vert-shader= + + Description: Allows external fragment and vertex shaders to be used for + 3D rendering. is the file path. Files should be + ASCII text files containing GLSL source code. The file + extension is not important. By default, Supermodel's + internal shader programs are used. These options are + intended for future extensibility and for use by + developers. + + ---------------- + + Option: -flip-stereo + + Description: Swaps the left and right audio channels. + + ---------------- + + Option: -no-dsb + + Description: Disables Digital Sound Board (MPEG music) emulation. See + the section on audio settings for more information. + + ---------------- + + Option: -no-sound + + Description: Disables sound board (sound effects) emulation. See the + section on audio settings for more information. + + ---------------- + + Option: -music-volume= + -sound-volume= + + Description: Specifies the volume of MPEG music produced by the Digital + Sound Board and the audio produced by the sound board in + percent. The default is 100, which is full volume, and the + valid range of is 0 (muted) to 200. See the section on + audio settings for more information. + + ---------------- + + Option: -config-inputs + + Description: Launches the interactive input configuration utility in the + command prompt window. Allows controls to be remapped. + See the section on controls for more information. + + ---------------- + + Option: -force-feedback + + Description: Enables force feedback. Force feedback is only supported + in a few games (see the section on force feedback for more + details). Available only on Windows. + + ---------------- + + Option: -input-system= + + Description: Sets the input system. This is only available on Windows, + where the default is 'dinput' (DirectInput). SDL is used + for all other platforms. Valid choices for are: + + dinput DirectInput. + xinput XInput + rawinput Raw Input. + sdl SDL. + + See the section on input systems for more details. + + ---------------- + + Option: -print-inputs + + Description: Prints the current input configuration. + + +============================================ + 12. Index of Configuration File Settings +============================================ + +All valid configuration file settings are listed here, ordered by category. +Please read the section describing the configuration file for more information. + +All settings are case sensitive. + + + Name: MultiThreaded + + Argument: Integer. + + Description: If set to 1, enables multi-threading; if set to 0, runs all + emulation in a single thread. Read the description of the + '-no-threads' command line option for more information. + + ---------------- + + Name: PowerPCFrequency + + Argument: Integer. + + Description: The PowerPC frequency in MHz. The default is 50. + Equivalent to the '-ppc-frequency' command line option. + + ---------------- + + Name: FullScreen + + Argument: Integer. + + Description: If set to 1, runs in full screen mode; if set to 0, runs in + a window. Disabled by default. Equivalent to the + '-fullscreen' command line option. + + ---------------- + + Name: ShowFrameRate + + Argument: Integer. + + Description: Shows the frame rate in the window title bar when set to 1. + If set to 0, the frame rate is not computed. Disabled by + default. Equivalent to the '-show-fps' command line + option. + + ---------------- + + Name: Throttle + + Argument: Integer. + + Description: Controls 60 FPS throttling. It is enabled by setting to 1 + and disabled by setting to 0. For more information, read + the description of the '-no-throttle' command line option. + + ---------------- + + Name: XResolution + YResolution + + Argument: Integer. + + Description: Resolution of the display in pixels. The default is + 496x384, the Model 3's native resolution. Equivalent to + the '-res' command line option. + + ---------------- + + Name: FragmentShader + VertexShader + + Argument: String. + + Description: Path to the external fragment or vertex shader file to use + for 3D rendering. By default, these are not set and the + internal default shaders are used. These are equivalent to + the '-frag-shader' and '-vert-shader' command line options. + + ---------------- + + Name: EmulateDSB + + Argument: Integer. + + Description: Emulates the Digital Sound Board if set to 1, disables it + if set to 0. See the section on audio settings for more + information. A setting of 0 is equivalent to the + '-no-dsb' command line option. + + ---------------- + + Name: EmulateSound + + Argument: Integer. + + Description: Emulates the sound board and its two Sega Custom Sound + Processors if set to 1, disables it if set to 0. See the + section on audio settings for more information. A setting + of 0 is equivalent to the '-no-sound' command line option. + + ---------------- + + Name: FlipStereo + + Argument: Integer. + + Description: Swaps the left and right stereo channels if set to 1. If + set to 0, outputs the channels normally. Disabled by + default. A setting of 1 is equivalent to using the + '-flip-stereo' command line option. + + ---------------- + + Name: MusicVolume + SoundVolume + + Argument: Integer. + + Description: Specifies the volume of MPEG music produced by the Digital + Sound Board and the audio produced by the sound board in + percent. The default is 100, which is full volume, and the + valid range is 0 (muted) to 200%. See the section on audio + settings for more information. The equivalent command line + options are '-music-volume' and '-sound-volume'. + + ---------------- + + Name: ForceFeedback + + Argument: Integer. + + Description: If set to 1, enables force feedback emulation; if set to 0, + disables it (the default behavior). Equivalent to the + '-force-feedback' command line option. Available only on + Windows. + + ---------------- + + Name: DirectInputConstForceMax + DirectInputFrictionMax + DirectInputSelfCenterMax + DirectInputVibrateMax + + Argument: Integer value. + + Description: Sets strength of the four DirectInput force feedback + effects in percent. Default is 100, indicating full + strength. Values exceeding 100% will distort the effects. + Available only on Windows. + + ---------------- + + Name: XInputConstForceMax + XInputVibrateMax + + Argument: Integer value. + + Description: Sets strength of XInput force feedback effects in percent. + Default is 100, indicating full strength. Values exceeding + 100% will distort the effects. The constant force effect + is simulated using vibration. Available only on Windows. + + ---------------- + + Name: XInputConstForceThreshold + + Argument: Integer value. + + Description: Minimum strength above which a Model 3 constant force + command will be simulated on an XInput device. + XInputConstForceMax determines the vibration strength for + this effect. The default value is 30. Available only on + Windows. + + ---------------- + + Names: InputStart1 + InputStart2 + InputCoin1 + InputCoin2 + InputServiceA + InputServiceB + InputTestA + InputTestB + + Argument: String. + + Description: Mappings for common inputs: player 1 and 2 Start buttons, + both coin slots, and both Service and Test buttons. Can + only be set in the 'Global' section. + + ---------------- + + Names: InputJoyDown + InputJoyDown2 + InputJoyLeft + InputJoyLeft2 + InputJoyRight + InputJoyRight2 + InputJoyUp + InputJoyUp2 + + Argument: String. + + Description: Mappings for 4-way digital joysticks (players 1 and 2). + These controls appear in 'Virtua Fighter 3', 'Fighting + Vipers 2', 'Spikeout', etc. Can only be set in the + 'Global' section. + + ---------------- + + Names: InputEscape + InputEscape2 + InputGuard + InputGuard2 + InputKick + InputKick2 + InputPunch + InputPunch2 + + Argument: String. + + Description: Mappings for fighting game buttons (players 1 and 2). Can + only be set in the 'Global' section. + + ---------------- + + Names: InputBeat + InputCharge + InputJump + InputShift + + Argument: String. + + Description: Mappings for 'Spikeout' and 'Spikeout Final Edition' + buttons. Can only be set in the 'Global' section. + + ---------------- + + Name: InputLongPass + InputLongPass2 + InputShortPass + InputShortPass2 + InputShoot + InputShoot2 + + Argument: String. + + Description: Mappings for the 'Virtua Striker 2' series of games + (players 1 and 2). Can only be set in the 'Global' + section. + + ---------------- + + Name: InputSteering + + Argument: String. + + Description: Mapping for the analog steering wheel axis, used in all + driving games. Can only be set in the 'Global' section. + + ---------------- + + Name: InputSteeringLeft + InputSteeringRight + + Argument: String. + + Description: Mappings for digital control of the steering wheel, + intended for users who do not have an analog controller. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputBrake + InputAccelerator + + Argument: String. + + Description: Mappings for brake and accelerator pedals used in driving + games. These are analog axes but can also be mapped to + digital inputs if needed. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputGearShift1 + InputGearShift2 + InputGearShift3 + InputGearShift4 + InputGearShiftN + + Argument: String. + + Description: Mappings for the gear box used in driving games. In games + with only two gears (e.g. 'Le Mans 24'), InputGearShift1 + maps to 'Up' and InputGearShift2 to 'Down'. A neutral + position can also be selected. Can only be set in the + 'Global' section. + + ---------------- + + Name: InputGearShiftDown + InputGearShiftUp + + Argument: String. + + Description: Mappings for sequential shifting. These allow gears to be + selected sequentially: N, 1, 2, 3, 4. Can only be set in + the 'Global' section. + + ---------------- + + Name: InputVR1 + InputVR2 + InputVR3 + InputVR4 + + Argument: String. + + Description: Mappings for the 'VR' (color-coded view select) buttons in + racing games (e.g. 'Scud Race', 'Daytona USA 2', etc.) Can + only be set in the 'Global' section. + + ---------------- + + Name: InputViewChange + + Argument: String. + + Description: Mapping for the view change button used in 'Sega Rally 2', + 'Dirt Devils', and 'Emergency Car Ambulance'. Can only be + set in the 'Global' section. + + ---------------- + + Name: InputHandBrake + + Argument: String. + + Description: Mapping for the hand brake button used in 'Sega Rally 2'. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputTwinJoyCrouch + InputTwinJoyForward + InputTwinJoyJump + InputTwinJoyReverse + InputTwinJoyStrafeLeft + InputTwinJoyStrafeRight + InputTwinJoyTurnLeft + InputTwinJoyTurnRight + + Argument: String. + + Description: Mappings for 'macro' commands for 'Virtual On Oratorio + Tangram'. These allow movements to be mapped to single + digital inputs and do not require the use of two joysticks. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputTwinJoyDown1 + InputTwinJoyDown2 + InputTwinJoyLeft1 + InputTwinJoyLeft2 + InputTwinJoyRight1 + InputTwinJoyRight2 + InputTwinJoyUp1 + InputTwinJoyUp2 + + Argument: String. + + Description: Mappings for the individual joysticks used by 'Virtual On + Oratorio Tangram'. The left stick is stick #1 and the + right stick is #2. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputTwinJoyShot1 + InputTwinJoyShot2 + InputTwinJoyTurbo1 + InputTwinJoyTurbo2 + + Argument: String. + + Description: Mappings for the shot (trigger) and turbo buttons located + on the left (1) and right (2) joysticks in 'Virtual On + Oratorio Tangram'. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputAnalogJoyDown + InputAnalogJoyLeft + InputAnalogJoyRight + InputAnalogJoyUp + + Argument: String. + + Description: Mappings for digital control of the analog joystick used in + 'Star Wars Trilogy'. Two inputs are provided for the two + directions available on each axis. Can only be set in the + 'Global' section. + + ---------------- + + Name: InputAnalogJoyX + InputAnalogJoyY + + Argument: String. + + Description: Mappings for X and Y axes of the analog joystick in 'Star + Wars Trilogy'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputAnalogJoyTrigger + InputAnalogJoyEvent + + Argument: String. + + Description: Mappings for the trigger and Event button in 'Star Wars + Trilogy'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputGunDown + InputGunDown2 + InputGunLeft + InputGunLeft2 + InputGunRight + InputGunRight2 + InputGunUp + InputGunUp2 + + Argument: String. + + Description: Mappings for digital control of the light guns in 'The Lost + World'. Inputs for motion in each direction are provided. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputGunX + InputGunX2 + InputGunY + InputGunY2 + + Argument: String. + + Description: Mappings for both analog axes of the light guns in 'The + Lost World'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputOffscreen + InputOffscreen2 + InputTrigger + InputTrigger2 + + Argument: String. + + Description: Mappings for the light gun triggers and off-screen + controls in 'The Lost World'. The off-screen button aims + the light gun off-screen while it is pressed, which makes + re-loading possible. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputAutoTrigger + InputAutoTrigger2 + + Argument: Integer. + + Description: When set to 1, the off-screen button will also + automatically reload (no need to pull the trigger). Can + only be set in the 'Global' section. + + ---------------- + + Name: InputKeySensitivity + + Argument: Integer. + + Description: The rate at which analog control values increase/decrease + when controlled by a key. Valid range is 1-100, with 1 + being the least sensitive (slowest response). The default + is 25. Can only be set in the 'Global' section. + + ---------------- + + Name: InputKeyDecaySpeed + + Argument: Integer. + + Description: The rate at which analog control values return to their + rest or 'off' position after a key has been released. The + value is expressed as a percentage of the attack speed, + ranging from 1-200. The default is 50, meaning analog + controls take twice as long to return to their rest state + as they do to reach their minimum/maximum values. Can only + be set in the 'Global' section. + + ---------------- + + Name: InputMouseXDeadZone + InputMouseYDeadZone + + Argument: Integer. + + Description: Specifies a rectangular region in the center of the display + within which the mouse is considered inactive. Expressed + as percentages, from 0-99, of the width and height of the + display. The default values are 0, which disable this + option. Can only be set in the 'Global' section. + + ---------------- + + Name: InputJoy**DeadZone + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the dead zone as a + percentage, 0-99, of the total range of the axis. Within + the dead zone, the joystick is inactive. This is useful + for joysticks that are 'noisy' when at rest. Can only be + set in the 'Global' section. + + ---------------- + + Name: InputJoy**Saturation + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the saturation, the + position at which the joystick is interpreted as being in + its most extreme position, as a percentage of the total + range of the axis, from 0-200. Values exceeding 100% mean + the axis will not use its full range. A value of 200 means + that the range will be halved (fully pressed will only + register as 50%). The default is 100, a 1-to-1 mapping. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputJoy**MinVal + InputJoy**MaxVal + InputJoy**OffVal + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the maximum, minimum, or + offset value of an axis. The range is -32768 to 32768. By + default, MinVal is -32768, OffVal is 0, and MaxVal is + 32767. Can only be set in the 'Global' section. + + +================================= + 13. Compiling the Source Code +================================= + +First, ensure that OpenGL, SDL (http://www.libsdl.org), and zlib +(http://zlib.net) are installed. GLEW (http://glew.sourceforge.net) is +included in the source tree and does not need to be installed separately. + +Next, extract the Supermodel source code and copy the appropriate Makefile +from the Makefiles/ directory to the base directory (that is, the one above +Src/ and Makefiles/). Makefiles for 32-bit Windows (Microsoft Visual C++ +2008), Linux/UNIX (GCC), and Mac OS X (GCC) are provided, all requiring GNU +Make. For Windows developers, MinGW (http://www.mingw.org) provides GNU Make. +Alternatively, you can write a Makefile compatible with Microsoft Nmake and +submit it for inclusion in the next release. ;) Consult the Visual Studio +documentation to learn how to configure the Microsoft compiler for command line +operation. + +Edit SDL_LIBPATH and SDL_INCLUDEPATH to reflect the location of the SDL +development library and header files. This should only be necessary for +Windows. The UNIX and Mac OS X Makefiles should be able to automatically +locate SDL if it was installed properly. + +On Windows, Supermodel is compiled with the multi-threaded, static version of +the run-time library (/MT option). However, the SDL and zlib development +libraries, and the SDL run-time DLL, are distributed for use with the dynamic +run-time (/MD option). They must all be recompiled using /MT to work with +Supermodel. Alternatively, Supermodel's Makefile can be edited to use the /MD +option. + +When everything is ready, rename the appropriate Makefile to 'Makefile' and run +'make'. If all goes well it should produce a Supermodel binary. + + +=========================== + 14. Contact Information +=========================== + +The official Supermodel web site is: + + http://www.Supermodel3.com + +Questions? Comments? Contributions? Your feedback is welcome! A discussion +forum is available at the web site and the primary author, Bart Trzynadlowski, +can be reached by email at: + + supermodel.emu@gmail.com + +We ask that you remain mindful of the following courtesies: + + - Do NOT ask about ROMs. + - Do NOT request features. + - Do NOT ask about release dates of future versions. + + +======================= + 15. Acknowledgments +======================= + +Numerous people contributed their precious time and energy to this project. +Without them, Supermodel would not have been possible. In no particular order, +we would like to thank: + + - Ville Linde, original Supermodel team member and MAMEDev extraordinaire + - Stefano Teso, original Supermodel team member + - ElSemi, for all sorts of technical information and insight + - R. Belmont, for DSB code and modified Amp library from his music player, + M1, and the Mac OS X port + - Naibo Zhang, for his work on Model 3 graphics + - krom, for adding in the remaining ROM sets + - Andrew Lewis (a.k.a. Andy Geezer), for dumping the drive board ROMs and + providing region codes + - The Guru, for his efforts in dumping Model 3 ROM sets + - Brian Troha & The Dumping Union, for their continuing efforts in adding + new Model 3 ROM sets + - Abelardo Vidal Martos, for providing extremely useful video recordings of + actual Model 3 games + - Andrew Gardner, for fruitful discussion + - Chad Reker, Alex Corrigan, pcvideogamer, and Groni, for being especially + thorough play-testers + - Charles MacDonald, for his helpful description of the System 24 tile + generator + - Andreas Naive, Olivier Galibert, David Haywood & MAME for the interface + to and decryption code for the Sega 315-5881 protection device + +Supermodel includes code from the following projects: + + - zlib: http://zlib.net + - minizip: http://www.winimage.com/zLibDll/minizip.html + - YAZE-AG: http://www.mathematik.uni-ulm.de/users/ag/yaze/ + - Amp by Tomislav Uzalec + - The OpenGL Extension Wrangler Library (GLEW): + http://glew.sourceforge.net + +The OpenGL Extension Wrangler Library +Copyright (C) 2002-2008, Milan Ikits +Copyright (C) 2002-2008, Marcelo E. Magallon +Copyright (C) 2002, Lev Povalahev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* The name of the author may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index f72fa1b..f16589e 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -1716,12 +1716,7 @@ void CNew3D::CalcViewport(Viewport* vp, float near, float far) // and only their scissor box differs) float correction = windowAR / viewableAreaAR; - // Android: use x offset for letterboxed "viewable area" within the physical window. -#ifdef __ANDROID__ - vp->x = m_xOffs; -#else vp->x = 0; -#endif vp->y = m_yOffs + (int)((float)(384 - (vp->vpY + vp->vpHeight))*m_yRatio); vp->width = m_totalXRes; vp->height = (int)((float)vp->vpHeight*m_yRatio); diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index b944522..32d1cec 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -62,6 +62,7 @@ android { dependencies { implementation("androidx.core:core-ktx:1.15.0") implementation("androidx.appcompat:appcompat:1.7.0") + implementation("androidx.documentfile:documentfile:1.0.1") implementation("com.google.android.material:material:1.12.0") } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 224a652..a7836dc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:theme="@style/Theme.Super3"> @@ -18,6 +18,13 @@ + + + + oI^aSb%|n;s5{tqj)q7M$^D( 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�*XHMsH_OnfNaZ3()= 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 CMJFEs literal 0 HcmV?d00001 diff --git a/android/app/src/main/assets/Assets/p2crosshair.bmp b/android/app/src/main/assets/Assets/p2crosshair.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cc3f1ba5c4c5c8956b176211aa985d11b2a216e1 GIT binary patch literal 295992 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 RN5gtF4UDFNk&p%$7ywDZ8<7A2 literal 0 HcmV?d00001 diff --git a/android/app/src/main/assets/Config/Games.xml b/android/app/src/main/assets/Config/Games.xml new file mode 100644 index 0000000..c851c2f --- /dev/null +++ b/android/app/src/main/assets/Config/Games.xml @@ -0,0 +1,3283 @@ + + + + + Sega Bass Fishing + USA, Deluxe + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Deluxe + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Upright + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Standard + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + + Daytona USA 2 - Battle on the Edge + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + true + + + + + + + + 0x29250e16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Daytona USA 2 - Power Edition + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + true + + + + + + + + 0x29222CC4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dirt Devils + Export, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dirt Devils + USA, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Australia, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Export, Ver. G? + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Emergency Call Ambulance + Export + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + US location test? + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + USA + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + Japan + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + Fighting Vipers 2 + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + + + + + + + 0x29260e96 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fighting Vipers 2 + ? + Sega + 1998 + + + Sega Model 3 + 2.0 + + + + + + + 0x29260e96 + + + + + + + + + + + + + + Harley-Davidson and L.A. Riders + Export, Revision B + Sega + 1997 + + + Sega Model 3 + 2.0 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Harley-Davidson and L.A. Riders + Export, Revision A + Sega + 1997 + + + Sega Model 3 + 2.0 + true + + + + + + + + + + + + + + + + + + + + + L.A. Machineguns: Rage of the Machines + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + + 0x292A2BC5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Le Mans 24 + Japan, Revision B + Sega + 1997 + + + Sega Model 3 + 1.5 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Lost World + Japan, Revision A + Sega + 1997 + + + Sega Model 3 + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Lost World + + Location Test + Sega + 1997 + + + Sega Model 3 + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + Magical Truck Adventure + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + 0x29266e45 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magical Truck Adventure + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + 0x29266e45 + + + + + + + + + + + + + + + + + + + + The Ocean Hunter + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + + + + + + 0x292B6A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Ocean Hunter + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + + + + + + 0x292B6A01 + + + + + + + + + + + + + + Scud Race + Export, Twin/DX + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Australia, Twin/DX + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Export, Deluxe, Revision A + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Export, Deluxe + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race Plus + Export, Twin/DX, Revision A + Sega + 1997 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race Plus + Export, Twin/DX + Sega + 1997 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + MPC106 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ski Champ + Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Ski + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spikeout Final Edition + Export + Sega + 1999 + + + Sega Model 3 + 2.1 + DSB2 + true + + + + + + 0x29236fc8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spikeout + Export, Revision C + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + true + + + + + + 0x292f2b04 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Export + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Prototype + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Prototype Version A + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Export, Deluxe + Sega + 1998 + + + + Sega Model 3 + 2.0 + DSB2 + Wheel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Export, Revision A + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Export + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Location Test, 16.09.98 + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision D + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision A + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision C + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 Team Battle + Japan + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + Cyber Troopers Virtual-On Oratorio Tangram + Japan, Revision B + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cyber Troopers Virtual-On Oratorio Tangram + Japan, Ver 5.4g + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Cyber Troopers Virtual-On Oratorio Tangram + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Cyber Troopers Virtual-On Oratorio Tangram + Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Virtua Striker 2 + Step 2.0, Export, USA + Sega + 1997 + + + Sega Model 3 + 2.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 + Step 1.5, Export, USA + Sega + 1997 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 + Step 1.5, Japan (test?) + Sega + 1997 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '98 + Step 2.0, Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + + + + + + + 0x29234e96 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '98 + Step 1.5, Japan + Sega + 1998 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Export, USA, Revision B + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99 + Export, USA, Revision A + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99 + Export, USA + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Japan, Revision B + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Step 1.5 Export, USA, Revision B + Sega + 1999 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99 + Step 1.5 Export, USA + Sega + 1999 + + + Sega Model 3 + 1.5 + MPC106 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Step 1.5 Japan, Revision B + Sega + 1999 + + + Sega Model 3 + 1.5 + MPC106 + Billboard + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/assets/Config/Music.xml b/android/app/src/main/assets/Config/Music.xml new file mode 100644 index 0000000..1d8499e --- /dev/null +++ b/android/app/src/main/assets/Config/Music.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/assets/Config/Supermodel.ini b/android/app/src/main/assets/Config/Supermodel.ini new file mode 100644 index 0000000..e200fcb --- /dev/null +++ b/android/app/src/main/assets/Config/Supermodel.ini @@ -0,0 +1,165 @@ +[ Global ] +; +; Super3 Android default configuration. +; Notes: +; - Touch controls synthesize keyboard inputs. Keep a KEY_* mapping for these inputs. +; - Some desktop options are ignored/overridden on Android for stability. +; + +GameXMLFile = Config/Games.xml +PowerPCFrequency = 50 +MultiThreaded = 1 +GPUMultiThreaded = 0 +EmulateSound = 1 +EmulateDSB = 1 +NbSoundChannels = 4 +SoundFreq = 57.6 +; Android audio uses "200" as a sane default (matches typical Supermodel configs). +SoundVolume = 200 +MusicVolume = 200 +LegacySoundDSP = 0 +ForceFeedback = 0 +Network = 0 +SimulateNet = 0 +Outputs = none + +; Rendering (Android GLES path) +New3DEngine = 0 +QuadRendering = 0 +XResolution = 496 +YResolution = 384 +VSync = 1 +WideScreen = 0 +WideBackground = 0 + +; Input system (Android uses SDL input backend) +InputSystem = sdl + +; Touch defaults: +; - bottom-left: Coin1 +; - bottom-right: Start1 +; - top-left: ServiceA +; - top-right: TestA +; - left-middle: D-pad / steer +; - right-middle: throttle (top) / brake (bottom) +InputCoin1 = KEY_5 +InputStart1 = KEY_1 +InputServiceA = KEY_F1 +InputTestA = KEY_F2 +InputJoyUp = KEY_UP +InputJoyDown = KEY_DOWN +InputJoyLeft = KEY_LEFT +InputJoyRight = KEY_RIGHT +InputSteeringLeft = KEY_LEFT +InputSteeringRight = KEY_RIGHT +InputAccelerator = KEY_W +InputBrake = KEY_S +InputVR1 = KEY_A,JOY1_BUTTON1 +InputVR2 = KEY_S,JOY1_BUTTON2 +InputVR3 = KEY_D,JOY1_BUTTON3 +InputVR4 = KEY_F,JOY1_BUTTON4 +InputViewChange = KEY_A,JOY1_BUTTON1 +InputHandBrake = KEY_S,JOY1_BUTTON2 +InputRearBrake = KEY_S,JOY1_BUTTON2 +InputMusicSelect = KEY_D,JOY1_BUTTON3 +InputTwinJoyTurnLeft = KEY_Q,JOY1_RXAXIS_NEG +InputTwinJoyTurnRight = KEY_W,JOY1_RXAXIS_POS +InputTwinJoyForward = KEY_UP,JOY1_YAXIS_NEG +InputTwinJoyReverse = KEY_DOWN,JOY1_YAXIS_POS +InputTwinJoyStrafeLeft = KEY_LEFT,JOY1_XAXIS_NEG +InputTwinJoyStrafeRight = KEY_RIGHT,JOY1_XAXIS_POS +InputTwinJoyJump = KEY_E,JOY1_BUTTON1 +InputTwinJoyCrouch = KEY_R,JOY1_BUTTON2 +InputTwinJoyLeft1 = NONE +InputTwinJoyLeft2 = NONE +InputTwinJoyRight1 = NONE +InputTwinJoyRight2 = NONE +InputTwinJoyUp1 = NONE +InputTwinJoyUp2 = NONE +InputTwinJoyDown1 = NONE +InputTwinJoyDown2 = NONE +InputTwinJoyShot1 = KEY_A,JOY1_BUTTON5 +InputTwinJoyShot2 = KEY_S,JOY1_BUTTON6 +InputTwinJoyTurbo1 = KEY_Z,JOY1_BUTTON7 +InputTwinJoyTurbo2 = KEY_X,JOY1_BUTTON8 +InputAnalogJoyLeft = KEY_LEFT +InputAnalogJoyRight = KEY_RIGHT +InputAnalogJoyUp = KEY_UP +InputAnalogJoyDown = KEY_DOWN +InputAnalogJoyX = JOY_XAXIS,MOUSE_XAXIS +InputAnalogJoyY = JOY_YAXIS,MOUSE_YAXIS +InputAnalogJoyTrigger = KEY_A,JOY_BUTTON1,MOUSE_LEFT_BUTTON +InputAnalogJoyEvent = KEY_S,JOY_BUTTON2,MOUSE_RIGHT_BUTTON +InputAnalogJoyTrigger2 = KEY_D,JOY_BUTTON2 +InputAnalogJoyEvent2 = NONE +InputGunLeft = KEY_LEFT +InputGunRight = KEY_RIGHT +InputGunUp = KEY_UP +InputGunDown = KEY_DOWN +InputGunX = MOUSE_XAXIS,JOY1_XAXIS +InputGunY = MOUSE_YAXIS,JOY1_YAXIS +InputTrigger = KEY_A,JOY1_BUTTON1,MOUSE_LEFT_BUTTON +InputOffscreen = KEY_S,JOY1_BUTTON2,MOUSE_RIGHT_BUTTON +InputAutoTrigger = 0 +InputGunLeft2 = NONE +InputGunRight2 = NONE +InputGunUp2 = NONE +InputGunDown2 = NONE +InputGunX2 = JOY2_XAXIS +InputGunY2 = JOY2_YAXIS +InputTrigger2 = JOY2_BUTTON1 +InputOffscreen2 = JOY2_BUTTON2 +InputAutoTrigger2 = 0 +InputAnalogGunLeft = KEY_LEFT +InputAnalogGunRight = KEY_RIGHT +InputAnalogGunUp = KEY_UP +InputAnalogGunDown = KEY_DOWN +InputAnalogGunX = MOUSE_XAXIS,JOY1_XAXIS +InputAnalogGunY = MOUSE_YAXIS,JOY1_YAXIS +InputAnalogTriggerLeft = KEY_A,JOY1_BUTTON1,MOUSE_LEFT_BUTTON +InputAnalogTriggerRight = KEY_S,JOY1_BUTTON2,MOUSE_RIGHT_BUTTON +InputAnalogGunLeft2 = NONE +InputAnalogGunRight2 = NONE +InputAnalogGunUp2 = NONE +InputAnalogGunDown2 = NONE +InputAnalogGunX2 = NONE +InputAnalogGunY2 = NONE +InputAnalogTriggerLeft2 = NONE +InputAnalogTriggerRight2 = NONE +InputSkiLeft = KEY_LEFT +InputSkiRight = KEY_RIGHT +InputSkiUp = KEY_UP +InputSkiDown = KEY_DOWN +InputSkiX = JOY1_XAXIS +InputSkiY = JOY1_YAXIS +InputSkiPollLeft = KEY_A,JOY1_BUTTON1 +InputSkiPollRight = KEY_S,JOY1_BUTTON2 +InputSkiSelect1 = KEY_Q,JOY1_BUTTON3 +InputSkiSelect2 = KEY_W,JOY1_BUTTON4 +InputSkiSelect3 = KEY_E,JOY1_BUTTON5 +InputMagicalLeverUp1 = KEY_UP +InputMagicalLeverDown1 = KEY_DOWN +InputMagicalLeverUp2 = NONE +InputMagicalLeverDown2 = NONE +InputMagicalLever1 = JOY1_YAXIS +InputMagicalLever2 = JOY2_YAXIS +InputMagicalPedal1 = KEY_A,JOY1_BUTTON1 +InputMagicalPedal2 = KEY_S,JOY2_BUTTON1 +InputFishingRodLeft = KEY_LEFT +InputFishingRodRight = KEY_RIGHT +InputFishingRodUp = KEY_UP +InputFishingRodDown = KEY_DOWN +InputFishingStickLeft = KEY_A +InputFishingStickRight = KEY_D +InputFishingStickUp = KEY_W +InputFishingStickDown = KEY_S +InputFishingRodX = JOY1_XAXIS +InputFishingRodY = JOY1_YAXIS +InputFishingStickX = JOY1_RXAXIS +InputFishingStickY = JOY1_RYAXIS +InputFishingReel = KEY_SPACE,JOY1_ZAXIS_POS +InputFishingCast = KEY_Z,JOY1_BUTTON1 +InputFishingSelect = KEY_X,JOY1_BUTTON2 +InputFishingTension = KEY_T,JOY1_ZAXIS_NEG + + diff --git a/android/app/src/main/assets/GraphicsAnalysis/DIR.txt b/android/app/src/main/assets/GraphicsAnalysis/DIR.txt new file mode 100644 index 0000000..d1f7ff6 --- /dev/null +++ b/android/app/src/main/assets/GraphicsAnalysis/DIR.txt @@ -0,0 +1 @@ +Graphics Analysis go here. diff --git a/android/app/src/main/assets/NVRAM/DIR.txt b/android/app/src/main/assets/NVRAM/DIR.txt new file mode 100644 index 0000000..0dcc27d --- /dev/null +++ b/android/app/src/main/assets/NVRAM/DIR.txt @@ -0,0 +1 @@ +NVRAM files go here. diff --git a/android/app/src/main/assets/ROMs/DIR.txt b/android/app/src/main/assets/ROMs/DIR.txt new file mode 100644 index 0000000..fab5085 --- /dev/null +++ b/android/app/src/main/assets/ROMs/DIR.txt @@ -0,0 +1 @@ +Recommended (but not mandatory) location for ROM sets. diff --git a/android/app/src/main/assets/Saves/DIR.txt b/android/app/src/main/assets/Saves/DIR.txt new file mode 100644 index 0000000..e89b019 --- /dev/null +++ b/android/app/src/main/assets/Saves/DIR.txt @@ -0,0 +1 @@ +Save states go here. diff --git a/android/app/src/main/cpp/android_input_system.cpp b/android/app/src/main/cpp/android_input_system.cpp index 292a608..e1ad190 100644 --- a/android/app/src/main/cpp/android_input_system.cpp +++ b/android/app/src/main/cpp/android_input_system.cpp @@ -9,6 +9,41 @@ AndroidInputSystem::AndroidInputSystem() { } +void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config) +{ + auto get = [&](const char* key, const char* fallback) -> std::string { + return config[key].ValueAsDefault(fallback); + }; + + auto keySc = [&](const std::string& mapping, SDL_Scancode fallback) -> SDL_Scancode { + if (mapping.empty()) return fallback; + SDL_Scancode sc = ParseFirstKeyboardScancode(mapping, *this); + return (sc == SDL_SCANCODE_UNKNOWN) ? fallback : sc; + }; + + m_touchCoin.a = keySc(get("InputCoin1", "KEY_5"), SDL_SCANCODE_5); + m_touchStart.a = keySc(get("InputStart1", "KEY_1"), SDL_SCANCODE_1); + m_touchService.a = keySc(get("InputServiceA", "KEY_F1"), SDL_SCANCODE_F1); + m_touchTest.a = keySc(get("InputTestA", "KEY_F2"), SDL_SCANCODE_F2); + + // For left/right, we press both joy and steering if they differ, to keep menus and driving working. + const SDL_Scancode joyLeft = keySc(get("InputJoyLeft", "KEY_LEFT"), SDL_SCANCODE_LEFT); + const SDL_Scancode joyRight = keySc(get("InputJoyRight", "KEY_RIGHT"), SDL_SCANCODE_RIGHT); + const SDL_Scancode steerLeft = keySc(get("InputSteeringLeft", "KEY_LEFT"), SDL_SCANCODE_LEFT); + const SDL_Scancode steerRight = keySc(get("InputSteeringRight", "KEY_RIGHT"), SDL_SCANCODE_RIGHT); + + m_touchJoyUp.a = keySc(get("InputJoyUp", "KEY_UP"), SDL_SCANCODE_UP); + m_touchJoyDown.a = keySc(get("InputJoyDown", "KEY_DOWN"), SDL_SCANCODE_DOWN); + + m_touchJoyLeft = {joyLeft, (joyLeft != steerLeft) ? steerLeft : SDL_SCANCODE_UNKNOWN}; + m_touchJoyRight = {joyRight, (joyRight != steerRight) ? steerRight : SDL_SCANCODE_UNKNOWN}; + m_touchSteerLeft = {steerLeft, (steerLeft != joyLeft) ? joyLeft : SDL_SCANCODE_UNKNOWN}; + m_touchSteerRight = {steerRight, (steerRight != joyRight) ? joyRight : SDL_SCANCODE_UNKNOWN}; + + m_touchThrottle.a = keySc(get("InputAccelerator", "KEY_W"), SDL_SCANCODE_W); + m_touchBrake.a = keySc(get("InputBrake", "KEY_S"), SDL_SCANCODE_S); +} + bool AndroidInputSystem::InitializeSystem() { std::fill(m_keys.begin(), m_keys.end(), 0); @@ -33,6 +68,18 @@ void AndroidInputSystem::PulseKey(SDL_Scancode sc, uint32_t durationMs) m_pulseUntilMs[sc] = SDL_GetTicks() + durationMs; } +void AndroidInputSystem::SetKeys(const DualScancode& sc, bool down) +{ + if (sc.a != SDL_SCANCODE_UNKNOWN) SetKey(sc.a, down); + if (sc.b != SDL_SCANCODE_UNKNOWN) SetKey(sc.b, down); +} + +void AndroidInputSystem::PulseKeys(const DualScancode& sc, uint32_t durationMs) +{ + if (sc.a != SDL_SCANCODE_UNKNOWN) PulseKey(sc.a, durationMs); + if (sc.b != SDL_SCANCODE_UNKNOWN) PulseKey(sc.b, durationMs); +} + bool AndroidInputSystem::Poll() { const uint32_t now = SDL_GetTicks(); @@ -94,31 +141,31 @@ void AndroidInputSystem::HandleControllerButtonEvent(const SDL_ControllerButtonE // - Back: TestA // - D-pad: menu navigation // - A/B/X/Y: map to common keys for future bindings - SDL_Scancode sc = SDL_SCANCODE_UNKNOWN; + DualScancode sc; switch (btn.button) { - case SDL_CONTROLLER_BUTTON_START: sc = SDL_SCANCODE_1; break; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: sc = SDL_SCANCODE_5; break; - case SDL_CONTROLLER_BUTTON_BACK: sc = SDL_SCANCODE_F2; break; - case SDL_CONTROLLER_BUTTON_DPAD_UP: sc = SDL_SCANCODE_UP; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: sc = SDL_SCANCODE_DOWN; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: sc = SDL_SCANCODE_LEFT; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: sc = SDL_SCANCODE_RIGHT; break; - case SDL_CONTROLLER_BUTTON_A: sc = SDL_SCANCODE_Z; break; - case SDL_CONTROLLER_BUTTON_B: sc = SDL_SCANCODE_X; break; - case SDL_CONTROLLER_BUTTON_X: sc = SDL_SCANCODE_C; break; - case SDL_CONTROLLER_BUTTON_Y: sc = SDL_SCANCODE_V; break; + case SDL_CONTROLLER_BUTTON_START: sc = m_touchStart; break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: sc = m_touchCoin; break; + case SDL_CONTROLLER_BUTTON_BACK: sc = m_touchTest; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: sc = m_touchJoyUp; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: sc = m_touchJoyDown; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: sc = m_touchJoyLeft; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: sc = m_touchJoyRight; break; + case SDL_CONTROLLER_BUTTON_A: sc = {SDL_SCANCODE_Z, SDL_SCANCODE_UNKNOWN}; break; + case SDL_CONTROLLER_BUTTON_B: sc = {SDL_SCANCODE_X, SDL_SCANCODE_UNKNOWN}; break; + case SDL_CONTROLLER_BUTTON_X: sc = {SDL_SCANCODE_C, SDL_SCANCODE_UNKNOWN}; break; + case SDL_CONTROLLER_BUTTON_Y: sc = {SDL_SCANCODE_V, SDL_SCANCODE_UNKNOWN}; break; default: break; } - if (sc == SDL_SCANCODE_UNKNOWN) + if (sc.a == SDL_SCANCODE_UNKNOWN && sc.b == SDL_SCANCODE_UNKNOWN) return; // For coin/start/test, prefer a pulse to avoid repeating while held. - if (down && (sc == SDL_SCANCODE_1 || sc == SDL_SCANCODE_5 || sc == SDL_SCANCODE_F1 || sc == SDL_SCANCODE_F2)) - PulseKey(sc, 120); + if (down && (sc.a == m_touchStart.a || sc.a == m_touchCoin.a || sc.a == m_touchService.a || sc.a == m_touchTest.a)) + PulseKeys(sc, 120); else - SetKey(sc, down); + SetKeys(sc, down); } void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down) @@ -133,10 +180,10 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down) // - Top-right: Test (KEY_F2) if (down) { - if (x < 0.25f && y > 0.75f) { PulseKey(SDL_SCANCODE_5, 120); return; } - if (x > 0.75f && y > 0.75f) { PulseKey(SDL_SCANCODE_1, 120); return; } - if (x < 0.25f && y < 0.25f) { PulseKey(SDL_SCANCODE_F1, 120); return; } - if (x > 0.75f && y < 0.25f) { PulseKey(SDL_SCANCODE_F2, 120); return; } + if (x < 0.25f && y > 0.75f) { PulseKeys(m_touchCoin, 120); return; } + if (x > 0.75f && y > 0.75f) { PulseKeys(m_touchStart, 120); return; } + if (x < 0.25f && y < 0.25f) { PulseKeys(m_touchService, 120); return; } + if (x > 0.75f && y < 0.25f) { PulseKeys(m_touchTest, 120); return; } } // Held throttle/brake zone (right-middle): hold to accelerate/brake. @@ -150,15 +197,15 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down) auto it = m_fingerHeldKey.find(tf.fingerId); if (it != m_fingerHeldKey.end()) { - SetKey(it->second, false); + SetKeys(it->second, false); m_fingerHeldKey.erase(it); } return; } - SDL_Scancode pedal = (y < 0.575f) ? SDL_SCANCODE_W : SDL_SCANCODE_S; + DualScancode pedal = (y < 0.575f) ? m_touchThrottle : m_touchBrake; m_fingerHeldKey[tf.fingerId] = pedal; - SetKey(pedal, true); + SetKeys(pedal, true); return; } @@ -172,7 +219,7 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down) auto it = m_fingerHeldDir.find(tf.fingerId); if (it != m_fingerHeldDir.end()) { - SetKey(it->second, false); + SetKeys(it->second, false); m_fingerHeldDir.erase(it); } return; @@ -184,16 +231,16 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down) const float dx = x - cx; const float dy = y - cy; - SDL_Scancode dir = SDL_SCANCODE_UNKNOWN; + DualScancode dir; if (std::abs(dx) > std::abs(dy)) - dir = (dx < 0.0f) ? SDL_SCANCODE_LEFT : SDL_SCANCODE_RIGHT; + dir = (dx < 0.0f) ? m_touchJoyLeft : m_touchJoyRight; else - dir = (dy < 0.0f) ? SDL_SCANCODE_UP : SDL_SCANCODE_DOWN; + dir = (dy < 0.0f) ? m_touchJoyUp : m_touchJoyDown; - if (dir != SDL_SCANCODE_UNKNOWN) + if (dir.a != SDL_SCANCODE_UNKNOWN || dir.b != SDL_SCANCODE_UNKNOWN) { m_fingerHeldDir[tf.fingerId] = dir; - SetKey(dir, true); + SetKeys(dir, true); } } @@ -209,14 +256,14 @@ void AndroidInputSystem::HandleTouchMotion(const SDL_TouchFingerEvent& tf) return; // Release previous pedal and re-evaluate based on new Y position. - SetKey(itKey->second, false); + SetKeys(itKey->second, false); m_fingerHeldKey.erase(itKey); HandleTouch(tf, true); return; } // Release previous direction and compute a new one. - SetKey(it->second, false); + SetKeys(it->second, false); m_fingerHeldDir.erase(it); HandleTouch(tf, true); } @@ -249,6 +296,42 @@ SDL_Scancode AndroidInputSystem::ScancodeFromSupermodelKeyName(const char* keyNa return SDL_GetScancodeFromName(keyName); } +SDL_Scancode AndroidInputSystem::ParseFirstKeyboardScancode(const std::string& mapping, const AndroidInputSystem& self) +{ + // mapping examples: + // - "KEY_F2" + // - "KEY_RIGHT,JOY1_XAXIS_POS" + // - "KEY_ALT+KEY_R" + // - "!KEY_ALT+KEY_P" + std::string s = mapping; + size_t start = 0; + while (start < s.size()) + { + // Find next token boundary. + while (start < s.size() && (s[start] == ' ' || s[start] == '\t' || s[start] == ',' || s[start] == '+')) + start++; + if (start >= s.size()) break; + + size_t end = start; + while (end < s.size() && s[end] != ' ' && s[end] != '\t' && s[end] != ',' && s[end] != '+') + end++; + + std::string tok = s.substr(start, end - start); + if (!tok.empty() && tok[0] == '!') + tok.erase(tok.begin()); + + if (tok.rfind("KEY_", 0) == 0 && tok.size() > 4) + { + const std::string keyName = tok.substr(4); + return self.ScancodeFromSupermodelKeyName(keyName.c_str()); + } + + start = end; + } + + return SDL_SCANCODE_UNKNOWN; +} + int AndroidInputSystem::GetKeyIndex(const char* keyName) { const SDL_Scancode sc = ScancodeFromSupermodelKeyName(keyName); diff --git a/android/app/src/main/cpp/android_input_system.h b/android/app/src/main/cpp/android_input_system.h index a7ad186..1d4e6ce 100644 --- a/android/app/src/main/cpp/android_input_system.h +++ b/android/app/src/main/cpp/android_input_system.h @@ -8,6 +8,7 @@ #include "Inputs/InputSystem.h" #include "Inputs/Input.h" +#include "Util/NewConfig.h" // Minimal SDL-backed input system for Android. // - Uses SDL scancodes as key indices. @@ -21,6 +22,9 @@ public: // Called from the SDL event loop (main thread). void HandleEvent(const SDL_Event& ev); + // Update touch/controller bindings from the current config (e.g. Supermodel.ini overrides). + void ApplyConfig(const Util::Config::Node& config); + bool InitializeSystem() override; int GetKeyIndex(const char* keyName) override; const char* GetKeyName(int keyIndex) override; @@ -50,8 +54,15 @@ public: void UngrabMouse() override {} private: + struct DualScancode { + SDL_Scancode a = SDL_SCANCODE_UNKNOWN; + SDL_Scancode b = SDL_SCANCODE_UNKNOWN; + }; + void SetKey(SDL_Scancode sc, bool down); void PulseKey(SDL_Scancode sc, uint32_t durationMs); + void SetKeys(const DualScancode& sc, bool down); + void PulseKeys(const DualScancode& sc, uint32_t durationMs); void HandleKeyEvent(const SDL_KeyboardEvent& key, bool down); void HandleControllerButtonEvent(const SDL_ControllerButtonEvent& btn, bool down); @@ -59,9 +70,26 @@ private: void HandleTouchMotion(const SDL_TouchFingerEvent& tf); SDL_Scancode ScancodeFromSupermodelKeyName(const char* keyName) const; + static SDL_Scancode ParseFirstKeyboardScancode(const std::string& mapping, const AndroidInputSystem& self); + + // Configurable bindings (default to our original hardcoded mapping). + DualScancode m_touchCoin{SDL_SCANCODE_5, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchStart{SDL_SCANCODE_1, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchService{SDL_SCANCODE_F1, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchTest{SDL_SCANCODE_F2, SDL_SCANCODE_UNKNOWN}; + + DualScancode m_touchJoyUp{SDL_SCANCODE_UP, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchJoyDown{SDL_SCANCODE_DOWN, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchJoyLeft{SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchJoyRight{SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchSteerLeft{SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchSteerRight{SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN}; + + DualScancode m_touchThrottle{SDL_SCANCODE_W, SDL_SCANCODE_UNKNOWN}; + DualScancode m_touchBrake{SDL_SCANCODE_S, SDL_SCANCODE_UNKNOWN}; std::vector m_keys; // indexed by SDL_Scancode - std::unordered_map m_fingerHeldDir; - std::unordered_map m_fingerHeldKey; + std::unordered_map m_fingerHeldDir; + std::unordered_map m_fingerHeldKey; std::unordered_map m_pulseUntilMs; }; diff --git a/android/app/src/main/cpp/gles_presenter.cpp b/android/app/src/main/cpp/gles_presenter.cpp index 85cc510..284e706 100644 --- a/android/app/src/main/cpp/gles_presenter.cpp +++ b/android/app/src/main/cpp/gles_presenter.cpp @@ -179,9 +179,16 @@ void GlesPresenter::UpdateQuadVerts() const float srcW = (float)m_srcW; const float srcH = (float)m_srcH; - const float scale = std::min(outW / srcW, outH / srcH); - const float drawW = srcW * scale; - const float drawH = srcH * scale; + float drawW; + float drawH; + if (m_stretch) { + drawW = outW; + drawH = outH; + } else { + const float scale = std::min(outW / srcW, outH / srcH); + drawW = srcW * scale; + drawH = srcH * scale; + } const float ndcW = (drawW / outW) * 2.0f; const float ndcH = (drawH / outH) * 2.0f; diff --git a/android/app/src/main/cpp/gles_presenter.h b/android/app/src/main/cpp/gles_presenter.h index c980100..f6b04d3 100644 --- a/android/app/src/main/cpp/gles_presenter.h +++ b/android/app/src/main/cpp/gles_presenter.h @@ -13,6 +13,7 @@ public: void Shutdown(); void Resize(int outputW, int outputH); + void SetStretch(bool stretch) { m_stretch = stretch; } // pixelsARGB: 0xAARRGGBB packed. void UpdateFrameARGB(const uint32_t* pixelsARGB, int width, int height); @@ -27,6 +28,7 @@ private: int m_outputH = 0; int m_srcW = 0; int m_srcH = 0; + bool m_stretch = false; unsigned m_program = 0; unsigned m_vao = 0; diff --git a/android/app/src/main/cpp/native-lib.cpp b/android/app/src/main/cpp/native-lib.cpp index 631dac1..9a0ea16 100644 --- a/android/app/src/main/cpp/native-lib.cpp +++ b/android/app/src/main/cpp/native-lib.cpp @@ -23,6 +23,7 @@ #include "OSD/Audio.h" #include "OSD/Logger.h" #include "Util/NewConfig.h" +#include "Util/ConfigBuilders.h" #include "BlockFile.h" #include "android_input_system.h" @@ -176,10 +177,99 @@ struct Super3Host { config.Set("InputSteeringRight", "KEY_RIGHT"); config.Set("InputAccelerator", "KEY_W"); config.Set("InputBrake", "KEY_S"); + inputSystem.ApplyConfig(config); + } + + void ApplyAndroidHardOverrides() + { + auto ensureKeyboardFallback = [&](const char* cfgKey, const char* defaultKeyToken) { + std::string v = config[cfgKey].ValueAsDefault(""); + if (v.empty()) { + config.Set(cfgKey, defaultKeyToken); + return; + } + std::string upper = v; + std::transform(upper.begin(), upper.end(), upper.begin(), [](unsigned char c) { return (char)std::toupper(c); }); + if (upper.find("KEY_") == std::string::npos) { + config.Set(cfgKey, std::string(defaultKeyToken) + "," + v); + } + }; + + // Settings that must remain stable on Android (unsupported or not yet wired). + config.Set("InputSystem", "sdl"); + config.Set("Outputs", "none"); + config.Set("ForceFeedback", false); + config.Set("Network", false); + config.Set("SimulateNet", false); + + // Ensure required nodes exist / sane. + config.Set("PowerPCFrequency", "50"); + + // Keep the current Android renderer path stable for now. + config.Set("New3DEngine", false); + config.Set("QuadRendering", false); + + // Android build currently targets the native 496x384 framebuffer. + config.Set("XResolution", "496"); + config.Set("YResolution", "384"); + + // Ensure touch zones always have a working keyboard mapping even if the user remaps to joystick-only. + ensureKeyboardFallback("InputCoin1", "KEY_5"); + ensureKeyboardFallback("InputStart1", "KEY_1"); + ensureKeyboardFallback("InputServiceA", "KEY_F1"); + ensureKeyboardFallback("InputTestA", "KEY_F2"); + ensureKeyboardFallback("InputJoyUp", "KEY_UP"); + ensureKeyboardFallback("InputJoyDown", "KEY_DOWN"); + ensureKeyboardFallback("InputJoyLeft", "KEY_LEFT"); + ensureKeyboardFallback("InputJoyRight", "KEY_RIGHT"); + ensureKeyboardFallback("InputSteeringLeft", "KEY_LEFT"); + ensureKeyboardFallback("InputSteeringRight", "KEY_RIGHT"); + ensureKeyboardFallback("InputAccelerator", "KEY_W"); + ensureKeyboardFallback("InputBrake", "KEY_S"); + + inputSystem.ApplyConfig(config); + } + + void ApplyIniOverrides(const std::string& gameSectionName) + { + const std::string base = userDataRoot.empty() ? std::string("super3") : userDataRoot; + const std::string iniPath = JoinPath(JoinPath(base, "Config"), "Supermodel.ini"); + if (!std::filesystem::exists(iniPath)) { + SDL_Log("Supermodel.ini not found at %s (using built-in defaults)", iniPath.c_str()); + return; + } + + Util::Config::Node iniConfig{"Global"}; + if (Util::Config::FromINIFile(&iniConfig, iniPath)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse %s (using built-in defaults)", iniPath.c_str()); + return; + } + + // Merge global settings: INI overrides built-in defaults. + { + Util::Config::Node merged{"Global"}; + Util::Config::MergeINISections(&merged, config, iniConfig); + config = merged; + } + + // Merge game-specific settings if present. + if (!gameSectionName.empty()) { + const Util::Config::Node* section = iniConfig.TryGet(gameSectionName); + if (section != nullptr) { + Util::Config::Node merged{"Global"}; + Util::Config::MergeINISections(&merged, config, *section); + config = merged; + } + } + + ApplyAndroidHardOverrides(); + inputSystem.ApplyConfig(config); } bool InitLoader(const std::string& gamesXml) { ApplyDefaults(); + ApplyIniOverrides(std::string()); + ApplyAndroidHardOverrides(); if (!std::filesystem::exists(gamesXml)) { ErrorLog("Games XML not found at %s", gamesXml.c_str()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Games XML not found at %s", gamesXml.c_str()); @@ -209,6 +299,10 @@ struct Super3Host { return false; } + // Apply Supermodel.ini overrides (Global + [ game ]) after the loader has determined game->name. + ApplyIniOverrides(game.name); + ApplyAndroidHardOverrides(); + // Initialize inputs before attaching to model if (!inputs.Initialize()) { ErrorLog("Inputs initialization failed"); @@ -274,30 +368,14 @@ struct Super3Host { } } - bool InstallNew3D(unsigned totalXRes, unsigned totalYRes) + bool InstallNew3D(unsigned xOff, unsigned yOff, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes) { if (!model3 || new3d) return !!new3d; - // Letterbox the Model 3 496x384 output into the physical drawable area. - // We pass the "viewable area" size to New3D so it doesn't wide-screen expand the frustum. - const float targetAR = 496.0f / 384.0f; - unsigned viewW = totalXRes; - unsigned viewH = totalYRes; - const float windowAR = (float)totalXRes / (float)totalYRes; - if (windowAR > targetAR) { - viewH = totalYRes; - viewW = (unsigned)std::lround((double)totalYRes * targetAR); - } else { - viewW = totalXRes; - viewH = (unsigned)std::lround((double)totalXRes / targetAR); - } - const unsigned xOff = (totalXRes - viewW) / 2; - const unsigned yOff = (totalYRes - viewH) / 2; - SDL_Log("Initializing New3D (GLES) ..."); new3d = std::make_unique(config, game.name); - if (new3d->Init(xOff, yOff, viewW, viewH, viewW, viewH) != 0) + if (new3d->Init(xOff, yOff, xRes, yRes, totalXRes, totalYRes) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "New3D Init failed"); new3d.reset(); @@ -426,6 +504,12 @@ extern "C" int SDL_main(int argc, char* argv[]) { if (argc > 2 && argv[2]) { gameName = argv[2]; } + if (argc > 3 && argv[3]) { + gamesXml = argv[3]; + } + if (argc > 4 && argv[4] && argv[4][0] != '\0') { + host.SetUserDataRoot(argv[4]); + } SDL_Log("Super3 paths: Games.xml=%s ROM=%s", gamesXml.c_str(), romZip.c_str()); @@ -501,6 +585,42 @@ extern "C" int SDL_main(int argc, char* argv[]) { if (winW <= 0) winW = 1; if (winH <= 0) winH = 1; + // Compute aspect-correct viewable area (xRes/yRes) centered within the drawable (totalXRes/totalYRes). + const unsigned totalXRes = (unsigned)winW; + const unsigned totalYRes = (unsigned)winH; + unsigned xRes = totalXRes; + unsigned yRes = totalYRes; + const float model3AR = 496.0f / 384.0f; + const float outAR = (float)totalXRes / (float)totalYRes; + if (outAR > model3AR) { + // output is wider => reduce width + xRes = (unsigned)std::lround((double)totalYRes * model3AR); + yRes = totalYRes; + } else if (outAR < model3AR) { + // output is taller => reduce height + xRes = totalXRes; + yRes = (unsigned)std::lround((double)totalXRes / model3AR); + } + const unsigned xOff = (totalXRes - xRes) / 2; + const unsigned yOff = (totalYRes - yRes) / 2; + + const bool wideScreen = host.config["WideScreen"].ValueAsDefault(false); + const bool wideBackground = host.config["WideBackground"].ValueAsDefault(false); + + // Clear full drawable (scissor off), then set scissor like desktop. + glDisable(GL_SCISSOR_TEST); + glViewport(0, 0, winW, winH); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + const unsigned correction = (unsigned)(((double)yRes / 384.0) * 2.0 + 0.5); + glEnable(GL_SCISSOR_TEST); + if (wideScreen) { + glScissor(0, (int)correction, (int)totalXRes, (int)(totalYRes - (correction * 2))); + } else { + glScissor((int)(xOff + correction), (int)(yOff + correction), (int)(xRes - (correction * 2)), (int)(yRes - (correction * 2))); + } + const int state = loadState.load(std::memory_order_acquire); if (state == 1) { if (!audioOpened) { @@ -516,18 +636,16 @@ extern "C" int SDL_main(int argc, char* argv[]) { } if (!new3dAttached) { - new3dAttached = host.InstallNew3D((unsigned)winW, (unsigned)winH); + new3dAttached = host.InstallNew3D(xOff, yOff, xRes, yRes, totalXRes, totalYRes); } if (new3dAttached) { - // 3D path: let New3D draw into the default framebuffer from inside the core. - glViewport(0, 0, winW, winH); - glClearColor(0.f, 0.f, 0.f, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + // 3D path: let New3D draw into the default framebuffer from inside the core (scissored). host.RunFrame(); // Overlay TileGen top layers (HUD/menus) on top of 3D. presenter.Resize(winW, winH); + presenter.SetStretch(false); if (host.render2d.HasTopSurface()) { const uint32_t* pixels = host.render2d.GetTopSurfaceARGB(); presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight()); @@ -537,8 +655,7 @@ extern "C" int SDL_main(int argc, char* argv[]) { // 2D-only path: keep showing TileGen software output. host.RunFrame(); presenter.Resize(winW, winH); - glClearColor(0.f, 0.f, 0.f, 1.f); - glClear(GL_COLOR_BUFFER_BIT); + presenter.SetStretch(wideBackground); if (host.render2d.HasFrame()) { const uint32_t* pixels = host.render2d.GetFrameBufferRGBA(); presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight()); diff --git a/android/app/src/main/java/com/izzy2lost/super3/AssetInstaller.kt b/android/app/src/main/java/com/izzy2lost/super3/AssetInstaller.kt new file mode 100644 index 0000000..61ed7ca --- /dev/null +++ b/android/app/src/main/java/com/izzy2lost/super3/AssetInstaller.kt @@ -0,0 +1,41 @@ +package com.izzy2lost.super3 + +import android.content.Context +import java.io.File + +object AssetInstaller { + fun ensureInstalled(context: Context, internalUserRoot: File) { + internalUserRoot.mkdirs() + + // Copy the standard Supermodel folder layout from APK assets into the internal user root. + // We never overwrite existing files so user edits persist. + val topLevel = listOf("Assets", "Config", "GraphicsAnalysis", "NVRAM", "Saves") + for (dir in topLevel) { + copyAssetTree(context, dir, File(internalUserRoot, dir)) + } + } + + private fun copyAssetTree(context: Context, assetPath: String, dest: File) { + val assets = context.assets + val children = assets.list(assetPath) ?: return + + // If list() returns empty, it's a file. + if (children.isEmpty()) { + if (!dest.exists()) { + dest.parentFile?.mkdirs() + assets.open(assetPath).use { input -> + dest.outputStream().use { output -> + input.copyTo(output) + } + } + } + return + } + + if (!dest.exists()) dest.mkdirs() + for (child in children) { + copyAssetTree(context, "$assetPath/$child", File(dest, child)) + } + } +} + diff --git a/android/app/src/main/java/com/izzy2lost/super3/GameXml.kt b/android/app/src/main/java/com/izzy2lost/super3/GameXml.kt new file mode 100644 index 0000000..233e647 --- /dev/null +++ b/android/app/src/main/java/com/izzy2lost/super3/GameXml.kt @@ -0,0 +1,80 @@ +package com.izzy2lost.super3 + +import android.content.Context +import org.xmlpull.v1.XmlPullParser + +data class GameDef( + val name: String, + val parent: String?, + val title: String, + val version: String?, +) { + val displayName: String = buildString { + append(title) + if (!version.isNullOrBlank()) { + append(" (") + append(version) + append(")") + } + } +} + +object GameXml { + fun parseGamesXmlFromAssets(context: Context, assetPath: String = "Config/Games.xml"): List { + context.assets.open(assetPath).use { input -> + val parser = android.util.Xml.newPullParser() + parser.setInput(input, null) + + val games = ArrayList(256) + var event = parser.eventType + + var currentGameName: String? = null + var currentParent: String? = null + var inIdentity = false + var title: String? = null + var version: String? = null + + fun finishGame() { + val name = currentGameName ?: return + val t = title ?: name + games.add(GameDef(name = name, parent = currentParent, title = t, version = version)) + } + + while (event != XmlPullParser.END_DOCUMENT) { + when (event) { + XmlPullParser.START_TAG -> { + when (parser.name) { + "game" -> { + currentGameName = parser.getAttributeValue(null, "name") + currentParent = parser.getAttributeValue(null, "parent") + inIdentity = false + title = null + version = null + } + "identity" -> inIdentity = true + "title" -> if (inIdentity) title = parser.nextText() + "version" -> if (inIdentity) version = parser.nextText() + } + } + + XmlPullParser.END_TAG -> { + when (parser.name) { + "identity" -> inIdentity = false + "game" -> { + finishGame() + currentGameName = null + currentParent = null + inIdentity = false + title = null + version = null + } + } + } + } + event = parser.next() + } + + return games + } + } +} diff --git a/android/app/src/main/java/com/izzy2lost/super3/MainActivity.kt b/android/app/src/main/java/com/izzy2lost/super3/MainActivity.kt new file mode 100644 index 0000000..ac41d25 --- /dev/null +++ b/android/app/src/main/java/com/izzy2lost/super3/MainActivity.kt @@ -0,0 +1,270 @@ +package com.izzy2lost.super3 + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.widget.* +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.documentfile.provider.DocumentFile +import java.io.File +import kotlin.concurrent.thread + +class MainActivity : AppCompatActivity() { + private val prefs by lazy { getSharedPreferences("super3_prefs", MODE_PRIVATE) } + + private lateinit var gamesFolderText: TextView + private lateinit var userFolderText: TextView + private lateinit var gamesList: ListView + private lateinit var statusText: TextView + + private var gamesTreeUri: Uri? = null + private var userTreeUri: Uri? = null + + private var games: List = emptyList() + private var zipDocs: Map = emptyMap() + + private val pickGamesFolder = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + if (uri != null) { + persistTreePermission(uri) + gamesTreeUri = uri + prefs.edit().putString("gamesTreeUri", uri.toString()).apply() + refreshUi() + } + } + + private val pickUserFolder = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + if (uri != null) { + persistTreePermission(uri) + userTreeUri = uri + prefs.edit().putString("userTreeUri", uri.toString()).apply() + refreshUi() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + gamesFolderText = findViewById(R.id.games_folder_text) + userFolderText = findViewById(R.id.user_folder_text) + gamesList = findViewById(R.id.games_list) + statusText = findViewById(R.id.status_text) + + findViewById