├── .gitattributes ├── .gitignore ├── README.md ├── cmake ├── CMakeLists.txt ├── linux.cmake ├── pi4-opengles.cmake ├── toolchain.cmake ├── win64-opengl.cmake ├── win64-opengles.cmake └── windows_copy_to_sdl2_lib_directory │ └── sdl2-config.cmake ├── docs ├── cannonball.6 └── license.txt ├── res ├── cannonball.ico ├── cannonball.rc ├── cannonball2.ico ├── config.xml ├── gamecontrollerdb.txt ├── icon.png ├── tilemap.bin └── tilepatch.bin ├── roms └── roms.txt └── src └── main ├── directx ├── ffeedback.cpp └── ffeedback.hpp ├── engine ├── audio │ ├── commands.hpp │ ├── osound.cpp │ ├── osound.hpp │ ├── osoundadr.hpp │ ├── osoundint.cpp │ └── osoundint.hpp ├── data │ ├── ozoom_lookup.hpp │ └── sprite_pals.hpp ├── oaddresses.hpp ├── oanimseq.cpp ├── oanimseq.hpp ├── oanimsprite.hpp ├── oattractai.cpp ├── oattractai.hpp ├── obonus.cpp ├── obonus.hpp ├── ocrash.cpp ├── ocrash.hpp ├── oentry.hpp ├── oferrari.cpp ├── oferrari.hpp ├── ohiscore.cpp ├── ohiscore.hpp ├── ohud.cpp ├── ohud.hpp ├── oinitengine.cpp ├── oinitengine.hpp ├── oinputs.cpp ├── oinputs.hpp ├── olevelobjs.cpp ├── olevelobjs.hpp ├── ologo.cpp ├── ologo.hpp ├── omap.cpp ├── omap.hpp ├── omusic.cpp ├── omusic.hpp ├── ooutputs.cpp ├── ooutputs.hpp ├── opalette.cpp ├── opalette.hpp ├── oroad.cpp ├── oroad.hpp ├── osmoke.cpp ├── osmoke.hpp ├── osprite.cpp ├── osprite.hpp ├── osprites.cpp ├── osprites.hpp ├── ostats.cpp ├── ostats.hpp ├── otiles.cpp ├── otiles.hpp ├── otraffic.cpp ├── otraffic.hpp ├── outils.cpp ├── outils.hpp ├── outrun.cpp └── outrun.hpp ├── frontend ├── cabdiag.cpp ├── cabdiag.hpp ├── config.cpp ├── config.hpp ├── menu.cpp ├── menu.hpp ├── menulabels.hpp ├── ttrial.cpp └── ttrial.hpp ├── globals.hpp ├── hwaudio ├── segapcm.cpp ├── segapcm.hpp ├── soundchip.cpp ├── soundchip.hpp ├── ym2151.cpp └── ym2151.hpp ├── hwvideo ├── hwroad.cpp ├── hwroad.hpp ├── hwsprites.cpp ├── hwsprites.hpp ├── hwtiles.cpp └── hwtiles.hpp ├── main.cpp ├── main.hpp ├── romloader.cpp ├── romloader.hpp ├── roms.cpp ├── roms.hpp ├── sdl2 ├── audio.cpp ├── audio.hpp ├── input.cpp ├── input.hpp ├── renderbase.cpp ├── renderbase.hpp ├── rendergl.cpp ├── rendergl.hpp ├── rendergles.cpp ├── rendergles.hpp ├── rendersurface.cpp ├── rendersurface.hpp ├── timer.cpp └── timer.hpp ├── stdint.hpp ├── trackloader.cpp ├── trackloader.hpp ├── utils.cpp ├── utils.hpp ├── video.cpp ├── video.hpp └── windirent.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #################### 2 | ## Project Specific 3 | #################### 4 | 5 | # ROMs 6 | 7 | epr-10187.88 8 | epr-10327a.76 9 | epr-10328a.75 10 | epr-10329a.58 11 | epr-10330a.57 12 | epr-10380b.133 13 | epr-10381a.132 14 | epr-10381b.132 15 | epr-10382b.118 16 | epr-10383b.117 17 | mpr-10371.9 18 | mpr-10372.13 19 | mpr-10373.10 20 | mpr-10374.14 21 | mpr-10375.11 22 | mpr-10376.15 23 | mpr-10377.12 24 | mpr-10378.16 25 | opr-10185.11 26 | opr-10186.47 27 | opr-10188.71 28 | opr-10188.71f 29 | opr-10189.70 30 | opr-10190.69 31 | opr-10191.68 32 | opr-10192.67 33 | opr-10193.66 34 | opr-10230.104 35 | opr-10231.103 36 | opr-10232.102 37 | opr-10266.101 38 | opr-10267.100 39 | opr-10268.99 40 | 41 | epr10187.88 42 | epr10327a.76 43 | epr10328a.75 44 | epr10329a.58 45 | epr10330a.57 46 | epr10380b.133 47 | epr10381a.132 48 | epr10382b.118 49 | epr10383b.117 50 | mpr10371.9 51 | mpr10372.13 52 | mpr10373.10 53 | mpr10374.14 54 | mpr10375.11 55 | mpr10376.15 56 | mpr10377.12 57 | mpr10378.16 58 | opr10185.11 59 | opr10186.47 60 | opr10188.71 61 | opr10188.71f 62 | opr10189.70 63 | opr10190.69 64 | opr10191.68 65 | opr10192.67 66 | opr10193.66 67 | opr10230.104 68 | opr10231.103 69 | opr10232.102 70 | opr10266.101 71 | opr10267.100 72 | opr10268.99 73 | 74 | # Japanese ROMs 75 | 76 | epr-10380.133 77 | epr-10382.118 78 | epr-10381.132 79 | epr-10383.117 80 | 81 | epr-10327.76 82 | epr-10329.58 83 | epr-10328.75 84 | epr-10330.57 85 | 86 | 87 | ################# 88 | ## Other working directories 89 | ################# 90 | 91 | mingw/ 92 | emscripten/ 93 | 94 | 95 | ################# 96 | ## CMake 97 | ################# 98 | 99 | CMakeCache.txt 100 | CMakeFiles/ 101 | 102 | 103 | ################# 104 | ## Eclipse 105 | ################# 106 | 107 | *.pydevproject 108 | .project 109 | .metadata 110 | bin/ 111 | tmp/ 112 | *.tmp 113 | *.bak 114 | *.swp 115 | *~.nib 116 | local.properties 117 | .classpath 118 | .settings/ 119 | .loadpath 120 | 121 | # External tool builders 122 | .externalToolBuilders/ 123 | 124 | # Locally stored "Eclipse launch configurations" 125 | *.launch 126 | 127 | # CDT-specific 128 | .cproject 129 | 130 | # PDT-specific 131 | .buildpath 132 | 133 | 134 | ################# 135 | ## Visual Studio 136 | ################# 137 | 138 | ## Ignore Visual Studio temporary files, build results, and 139 | ## files generated by popular Visual Studio add-ons. 140 | 141 | # User-specific files 142 | *.suo 143 | *.user 144 | *.sln.docstates 145 | 146 | # Build results 147 | [Dd]ebug/ 148 | [Rr]elease/ 149 | *_i.c 150 | *_p.c 151 | *.ilk 152 | *.meta 153 | *.obj 154 | *.pch 155 | *.pdb 156 | *.pgc 157 | *.pgd 158 | *.rsp 159 | *.sbr 160 | *.tlb 161 | *.tli 162 | *.tlh 163 | *.tmp 164 | *.vspscc 165 | .builds 166 | *.dotCover 167 | 168 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 169 | #packages/ 170 | 171 | # Visual C++ cache files 172 | ipch/ 173 | *.aps 174 | *.ncb 175 | *.opensdf 176 | *.sdf 177 | 178 | # Visual Studio profiler 179 | *.psess 180 | *.vsp 181 | 182 | # ReSharper is a .NET coding add-in 183 | _ReSharper* 184 | 185 | # Installshield output folder 186 | [Ee]xpress 187 | 188 | # DocProject is a documentation generator add-in 189 | DocProject/buildhelp/ 190 | DocProject/Help/*.HxT 191 | DocProject/Help/*.HxC 192 | DocProject/Help/*.hhc 193 | DocProject/Help/*.hhk 194 | DocProject/Help/*.hhp 195 | DocProject/Help/Html2 196 | DocProject/Help/html 197 | 198 | # Click-Once directory 199 | publish 200 | 201 | # Others 202 | [Bb]in 203 | [Oo]bj 204 | sql 205 | TestResults 206 | *.Cache 207 | ClientBin 208 | stylecop.* 209 | ~$* 210 | *.dbmdl 211 | Generated_Code #added for RIA/Silverlight projects 212 | 213 | # Backup & report files from converting an old project file to a newer 214 | # Visual Studio version. Backup files are not needed, because we have git ;-) 215 | _UpgradeReport_Files/ 216 | Backup*/ 217 | UpgradeLog*.XML 218 | 219 | 220 | 221 | ############ 222 | ## Windows 223 | ############ 224 | 225 | # Windows image file caches 226 | Thumbs.db 227 | 228 | # Folder config file 229 | Desktop.ini 230 | 231 | 232 | ############# 233 | ## Python 234 | ############# 235 | 236 | *.py[co] 237 | 238 | # Packages 239 | *.egg 240 | *.egg-info 241 | dist 242 | build 243 | eggs 244 | parts 245 | bin 246 | var 247 | sdist 248 | develop-eggs 249 | .installed.cfg 250 | 251 | # Installer logs 252 | pip-log.txt 253 | 254 | # Unit test / coverage reports 255 | .coverage 256 | .tox 257 | 258 | #Translations 259 | *.mo 260 | 261 | #Mr Developer 262 | .mr.developer.cfg 263 | 264 | # Mac crap 265 | .DS_Store 266 | old_version 267 | 268 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cannonball - OutRun Engine 2 | ========================== 3 | 4 | CannonBall is an souped up game engine for the OutRun arcade game. The original 68000 and Z80 assembler code has been rewritten in C++. This makes it possible to make improvements suitable for modern platforms, including a higher frame-rate and widescreen support. It requires the original ROMs, as these contain elements including the graphics and audio data. 5 | 6 | * For an overview of CannonBall and its features, please read the [manual](https://github.com/djyt/cannonball/wiki). 7 | * [Reassembler Blog](http://reassembler.blogspot.co.uk/) 8 | * [Twitter](https://twitter.com/djyt) 9 | 10 | Please note that I maintain the Windows build of CannonBall. Whilst I strive to ensure this is a fully cross-platform project that compiles across Linux, Wii, Mac, Pi4 etc. I do not maintain those ports. Port specific issues should be raised with the respective person. 11 | 12 | Thank you! 13 | Chris White (Project Creator). 14 | 15 | 16 | Getting Started 17 | --------------- 18 | 19 | CannonBall is coded in C++ and makes use of the SDL 2 and Boost libraries. 20 | 21 | CannonBall has been successfully built for Windows, Mac OS X, Linux, Open Pandora and the Raspberry Pi. 22 | 23 | CannonBall can use OpenGL, OpenGLES (mobile platforms) or plain SDL2 for rendering. 24 | 25 | I have recently deprecated support for SDL 1, to focus on SDL 2. But feel free to grab an older version from github if you need it. 26 | 27 | * Install your favourite C++11 environment (e.g. GCC, Visual Studio, Xcode, MingGW etc.) 28 | * Install [CMake](http://www.cmake.org/). This generates your platform and compiler build files. 29 | * Extract the [Boost Library](http://www.boost.org/) somewhere, for example: c:\coding\lib\boost_1_74_0 Note that Boost does not need to be compiled, as only the header libraries are used. This keeps things nice and lightweight. 30 | * Extract the [SDL Development Library](https://www.libsdl.org/download-2.0.php) somewhere, for example: c:\coding\lib\SDL2-2.0.12 31 | * Read the SDL documentation & build the SDL Library for your system. 32 | * Windows only: I needed to copy cannonball/cmake/windows_copy_to_sdl2_lib_directory/sdl2-config.cmake to c:\coding\lib\SDL2-2.0.12 33 | * Windows only: download and install the [Direct 8.1 SDK](https://archive.org/details/dx81sdk_full). This is used for force-feedback and a legacy from when I was using SDL 1. I should update it sometime to use SDL 2 instead. 34 | * Extract the Cannonball code somewhere, for example: c:\coding\cannonball 35 | * You may need to create a .cmake file for your system to configure specific options. See the cmake subdirectory for more details. 36 | 37 | Build 38 | ----- 39 | 40 | * Run CMake to generate the relevant build files for your compiler. You can optionally pass -DTARGET=filename to pass a custom .cmake file 41 | * Compile using your chosen compiler. Further details below. 42 | 43 | ### Visual Studio 2019 Community Edition 44 | 45 | * Create to the sub-directory you want to create your build files in (e.g. or vs_build) 46 | 47 | cmake -G "Visual Studio 16 2019" ../cmake 48 | 49 | * Open the created CannonBall solution in VS 2019. 50 | * Right click and choose 'Set as StartUp project'. 51 | * Set working directory to something sensible. Right click -> Configuration properties -> Debugging 52 | * Ensure config.xml is in the working directory. _You can specify an alternate location on the command line_ 53 | * Edit config.xml to reflect the paths of your roms and res directories. By default, they should be in the working directory. 54 | * Copy the OutRun revision B romset to the roms subdirectory. 55 | * You can then compile, debug and run from Visual Studio as expected. 56 | -------------------------------------------------------------------------------- /cmake/linux.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # CannonBall Linux Setup 3 | # ----------------------------------------------------------------------------- 4 | 5 | # Use OpenGL for rendering. 6 | find_package(OpenGL REQUIRED) 7 | 8 | # Platform Specific Libraries 9 | set(platform_link_libs 10 | ${OPENGL_LIBRARIES} 11 | ) -------------------------------------------------------------------------------- /cmake/pi4-opengles.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # CannonBall Raspberry Pi4 (Smarty Hardware) 3 | # 4 | # I am using Visual Studio 2019 and cross-compiling on a PC with VisualGDB 5 | # (https://visualgdb.com/) 6 | # 7 | # This works well in terms of being able to use the Visual Studio debugger, 8 | # but if your environment is different, you may need a different cmake file. 9 | # 10 | # I referred to the following tutorial: 11 | # https://gnutoolchains.com/raspberry/tutorial/ 12 | # 13 | # I downloaded the 2019-07-10 raspbian-buster-full executable from here: 14 | # https://gnutoolchains.com/raspberry/ 15 | # ----------------------------------------------------------------------------- 16 | 17 | # Library Locations 18 | set(lib_base c:/coding/lib) 19 | set(boost_dir ${lib_base}/boost_1_74_0) 20 | set(sdl2_dir ${lib_base}/SDL2-2.0.12) 21 | 22 | # Use OpenGLES for rendering. 23 | set(OPENGLES 1) 24 | 25 | # GCC Specific flags (optimize for Pi4 CPU) 26 | set(CMAKE_CXX_FLAGS "-O3 -mtune=cortex-a72") 27 | 28 | # Platform Specific Libraries 29 | set(platform_link_libs 30 | SDL2 31 | GLESv2 32 | ) 33 | 34 | # Platform Specific Link Directories 35 | set(platform_link_dirs 36 | 37 | ) -------------------------------------------------------------------------------- /cmake/toolchain.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Linux) 2 | SET(VISUALGDB_TOOLCHAIN_TYPE Linux) 3 | SET(VISUALGDB_TOOLCHAIN_SUBTYPE GCC) 4 | SET(CMAKE_C_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-gcc.exe") 5 | SET(CMAKE_CXX_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-g++.exe") 6 | SET(CMAKE_ASM_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-g++.exe") 7 | SET(CMAKE_SYSROOT "$ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf/sysroot") 8 | 9 | if(EXISTS "$ENV{TOOLCHAIN_ROOT}/Qt/v5-CMake/Qt5Cross.cmake") 10 | include("$ENV{TOOLCHAIN_ROOT}/Qt/v5-CMake/Qt5Cross.cmake") 11 | endif() 12 | -------------------------------------------------------------------------------- /cmake/win64-opengl.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # CannonBall Windows Setup 3 | # ----------------------------------------------------------------------------- 4 | 5 | # Library Locations 6 | set(lib_base c:/coding/lib) 7 | set(boost_dir ${lib_base}/boost_1_74_0) 8 | set(sdl2_dir ${lib_base}/SDL2-2.0.12) 9 | set(dx8_dir c:/dxsdk) 10 | 11 | # Use OpenGL for rendering. 12 | set(OPENGL 1) 13 | 14 | # Platform Specific Libraries 15 | set(platform_link_libs 16 | opengl32 # For OpenGL 17 | glu32 # For OpenGL 18 | dxguid # Direct X Haptic Support 19 | dinput8 # Direct X Haptic Support 20 | ) 21 | 22 | # Platform Specific Link Directories 23 | set(platform_link_dirs 24 | "${dx8_dir}/lib" 25 | ) -------------------------------------------------------------------------------- /cmake/win64-opengles.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # CannonBall Windows Setup 3 | # 4 | # Extra libraries are required to use OpenGLES on Windows. 5 | # Quite a long install process. 6 | # Download & Compile Angle: https://opensource.google/projects/angle 7 | # 8 | # Other notes: 9 | # http://mickcharlesbeaver.blogspot.com/2017/11/using-opengl-es-20-with-sdl2-via-angle.html 10 | # ----------------------------------------------------------------------------- 11 | 12 | # Library Locations 13 | set(lib_base c:/coding/lib) 14 | set(boost_dir ${lib_base}/boost_1_74_0) 15 | set(sdl2_dir ${lib_base}/SDL2-2.0.12) 16 | set(angle_dir ${lib_base}/angle/out/Debug) 17 | set(dx8_dir c:/dxsdk) 18 | 19 | # Use OpenGLES for rendering. 20 | set(OPENGLES 1) 21 | 22 | # Platform Specific Libraries 23 | set(platform_link_libs 24 | "${angle_dir}/libEGL.dll.lib" 25 | "${angle_dir}/libGLESv2.dll.lib" 26 | dxguid # Direct X Haptic Support 27 | dinput8 # Direct X Haptic Support 28 | ) 29 | 30 | # Platform Specific Link Directories 31 | set(platform_link_dirs 32 | "${dx8_dir}/lib" 33 | ) -------------------------------------------------------------------------------- /cmake/windows_copy_to_sdl2_lib_directory/sdl2-config.cmake: -------------------------------------------------------------------------------- 1 | set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") 2 | 3 | # Support both 32 and 64 bit builds 4 | if (${CMAKE_SIZEOF_VOID_P} MATCHES 8) 5 | set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2main.lib") 6 | else () 7 | set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2main.lib") 8 | endif () 9 | 10 | string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES) -------------------------------------------------------------------------------- /docs/cannonball.6: -------------------------------------------------------------------------------- 1 | .\" Manpage for cannonball 2 | .TH "cannonball" "6" "August 2019" "CannonBall 0.3" "CannonBall - Configuration Help" 3 | .SH NAME 4 | cannonball \- An Enhanced OutRun Engine 5 | .SH SYNOPSIS 6 | cannonball 7 | .SH DESCRIPTION 8 | CannonBall is a program which allows you to play an enhanced version 9 | of Yu Suzuki's seminal arcade racer, OutRun, on a variety of systems. 10 | .SH CONFIGURATION 11 | In order for CannonBall to work you need to provide a copy of the 12 | OutRun revision B romset to the roms subdirectory. Rename the files if 13 | necessary. 14 | 15 | The roms subdirectory can be found at $XDG_DATA_HOME/cannonball/roms 16 | which defaults to $HOME/.local/share/cannonball/roms 17 | 18 | Configuration can be found at $XDG_DATA_HOME/cannonball/config.xml 19 | which defaults to $HOME/.local/share/cannonball/config.xml 20 | 21 | NOTE: The directories will be created on the first run of Cannonball 22 | if they do not exist. 23 | .SH OPTIONS 24 | cannonball does not take any options. 25 | .SH SEE ALSO 26 | CannonBall Wiki 27 | 28 | CannonBall online manual 29 | 30 | .SH BUGS 31 | For known issues see . 32 | .SH COPYRIGHT 33 | Copyright (c) 2012-2014 Chris White. 34 | 35 | Users of CannonBall are expected to own licensed copies of the ROM 36 | files that is intended to be used with this program. These ROMs may be 37 | subject to their copyright laws under their jurisdiction. Make sure 38 | you can legally transfer the ROMs to your system before using them 39 | with this program. 40 | 41 | OutRun ROMs may be subject to copyright laws. 42 | 43 | OutRun is a trademark of the SEGA Corporation. This project is not 44 | affiliated with SEGA in any way. 45 | .SH AUTHOR 46 | Chris White - Project creator. 47 | 48 | Arun Horne - Cross platform work. 49 | 50 | Santhosh Raju - Man page. 51 | -------------------------------------------------------------------------------- /docs/license.txt: -------------------------------------------------------------------------------- 1 | Unless otherwise explicitly stated, all code in Cannonball is released under 2 | the following license: 3 | 4 | Copyright Chris White and the Cannonball team 5 | All rights reserved. 6 | 7 | OutRun is a trademark of the SEGA Corporation. 8 | This project is not affiliated with SEGA in any way. 9 | 10 | Redistribution and use of this code or any derivative works are permitted 11 | provided that the following conditions are met: 12 | 13 | * Redistributions may not be sold, nor may they be used in a commercial 14 | product or activity. 15 | 16 | * Redistributions that are modified from the original source must include the 17 | complete source code, including the source code for all components used by a 18 | binary built from the modified sources. However, as a special exception, the 19 | source code distributed need not include anything that is normally distributed 20 | (in either source or binary form) with the major components (compiler, kernel, 21 | and so on) of the operating system on which the executable runs, unless that 22 | component itself accompanies the executable. 23 | 24 | * Redistributions must reproduce the above copyright notice, this list of 25 | conditions and the following disclaimer in the documentation and/or other 26 | materials provided with the distribution. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | POSSIBILITY OF SUCH DAMAGE. 39 | -------------------------------------------------------------------------------- /res/cannonball.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/res/cannonball.ico -------------------------------------------------------------------------------- /res/cannonball.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "cannonball2.ico" -------------------------------------------------------------------------------- /res/cannonball2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/res/cannonball2.ico -------------------------------------------------------------------------------- /res/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/res/icon.png -------------------------------------------------------------------------------- /res/tilemap.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/res/tilemap.bin -------------------------------------------------------------------------------- /res/tilepatch.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/res/tilepatch.bin -------------------------------------------------------------------------------- /roms/roms.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | Place OutRun Revision B ROMs in this directory. 3 | ------------------------------------------------------------------------------- 4 | epr-10187.88 5 | epr-10327a.76 6 | epr-10328a.75 7 | epr-10329a.58 8 | epr-10330a.57 9 | epr-10380b.133 10 | epr-10381a.132 11 | epr-10382b.118 12 | epr-10383b.117 13 | mpr-10371.9 14 | mpr-10372.13 15 | mpr-10373.10 16 | mpr-10374.14 17 | mpr-10375.11 18 | mpr-10376.15 19 | mpr-10377.12 20 | mpr-10378.16 21 | opr-10185.11 22 | opr-10186.47 23 | opr-10188.71 24 | opr-10189.70 25 | opr-10190.69 26 | opr-10191.68 27 | opr-10192.67 28 | opr-10193.66 29 | opr-10230.104 30 | opr-10231.103 31 | opr-10232.102 32 | opr-10266.101 33 | opr-10267.100 34 | opr-10268.99 35 | 36 | ------------------------------------------------------------------------------- 37 | The original Japanese release of OutRun featured slightly different tracks 38 | and course ordering. 39 | 40 | Optionally, you can include the following files from the Japanese version to 41 | play these alternate tracks: 42 | ------------------------------------------------------------------------------- 43 | 44 | epr-10380.133 45 | epr-10382.118 46 | epr-10381.132 47 | epr-10383.117 48 | epr-10327.76 49 | epr-10329.58 50 | epr-10328.75 51 | epr-10330.57 52 | 53 | ------------------------------------------------------------------------------- 54 | Fixed Audio Rom (Optional) 55 | http://reassembler.blogspot.co.uk/2013/01/outrun-original-game-shipped-with.html 56 | ------------------------------------------------------------------------------- 57 | 58 | opr-10188.71f 59 | -------------------------------------------------------------------------------- /src/main/directx/ffeedback.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Microsoft DirectX 8 Force Feedback (aka Haptic) Support 3 | 4 | - Currently, SDL does not support haptic devices. So this is Win32 only. 5 | 6 | - DirectX 8 still works on Windows XP, so I'm not attempting to support 7 | a higher version for now. 8 | 9 | Ref: http://msdn.microsoft.com/en-us/library/windows/desktop/ee417563%28v=vs.85%29.aspx 10 | 11 | Copyright Chris White. 12 | See license.txt for more details. 13 | ***************************************************************************/ 14 | 15 | #pragma once 16 | 17 | //----------------------------------------------------------------------------- 18 | // Function prototypes 19 | //----------------------------------------------------------------------------- 20 | namespace forcefeedback 21 | { 22 | extern bool init(int max_force, int min_force, int force_duration); 23 | extern void close(); 24 | extern int set(int xdirection, int force); 25 | extern bool is_supported(); 26 | }; -------------------------------------------------------------------------------- /src/main/engine/audio/commands.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Shared Sound Commands. 3 | Used by both the ported 68K and Z80 program code. 4 | 5 | Copyright Chris White. 6 | See license.txt for more details. 7 | ***************************************************************************/ 8 | 9 | #pragma once 10 | 11 | namespace sound 12 | { 13 | // ---------------------------------------------------------------------------- 14 | // Commands to send from main program code 15 | // ---------------------------------------------------------------------------- 16 | 17 | enum 18 | { 19 | FM_RESET = 0, // Reset FM Chip (Stop Music etc.) 20 | RESET = 0x80, // Reset sound code 21 | MUSIC_BREEZE = 0x81, // Music: Passing Breeze 22 | MUSIC_SPLASH = 0x82, // Music: Splash Wave 23 | MUSIC_CUSTOM = 0x83, // Enhancement: Play Custom Imported Data 24 | COIN_IN = 0x84, // Coin IN Effect 25 | MUSIC_MAGICAL = 0x85, // Music: Magical Sound Shower 26 | YM_CHECKPOINT = 0x86, // YM: Checkpoint Ding 27 | INIT_SLIP = 0x8A, // Slip (Looped) 28 | STOP_SLIP = 0x8B, 29 | INIT_CHEERS = 0x8D, 30 | STOP_CHEERS = 0x8E, 31 | CRASH1 = 0x8F, 32 | REBOUND = 0x90, 33 | CRASH2 = 0x92, 34 | NEW_COMMAND = 0x93, 35 | SIGNAL1 = 0x94, 36 | SIGNAL2 = 0x95, 37 | INIT_WEIRD = 0x96, 38 | STOP_WEIRD = 0x97, 39 | REVS = 0x98, // New: Added to support revs during WAV playback 40 | BEEP1 = 0x99, // YM Beep 41 | UFO = 0x9A, // Unused sound. Note that the z80 code to play this is not implemented in this conversion. 42 | BEEP2 = 0x9B, // YM Double Beep 43 | INIT_CHEERS2 = 0x9C, // Cheers (Looped) 44 | VOICE_CHECKPOINT = 0x9D, // Voice: Checkpoint 45 | VOICE_CONGRATS = 0x9E, // Voice: Congratulations 46 | VOICE_GETREADY = 0x9F, // Voice: Get Ready 47 | INIT_SAFETYZONE = 0xA0, 48 | STOP_SAFETYZONE = 0xA1, 49 | YM_SET_LEVELS = 0xA2, 50 | // 0xA3 Unused - Should be voice 4, but isn't hooked up 51 | PCM_WAVE = 0xA4, // Wave Sample 52 | MUSIC_LASTWAVE = 0xA5, // Music: Last Wave 53 | }; 54 | 55 | // ---------------------------------------------------------------------------- 56 | // Engine Commands to send from main program code 57 | // ---------------------------------------------------------------------------- 58 | 59 | enum 60 | { 61 | UNUSED, 62 | ENGINE_PITCH_H, 63 | ENGINE_PITCH_L, 64 | ENGINE_VOL, 65 | TRAFFIC1, 66 | TRAFFIC2, 67 | TRAFFIC3, 68 | TRAFFIC4, 69 | }; 70 | }; -------------------------------------------------------------------------------- /src/main/engine/audio/osoundadr.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Z80 Program Code Addresses. 3 | Addresses to data within the Z80 Program ROM. 4 | 5 | These are typically large blocks of data that we don't want to include 6 | in the codebase. 7 | 8 | For example the music tracks are stored and referenced here. 9 | 10 | Copyright Chris White. 11 | See license.txt for more details. 12 | ***************************************************************************/ 13 | 14 | #pragma once 15 | 16 | namespace z80_adr 17 | { 18 | // FM Note & Octave Lookup Table 19 | const static uint16_t YM_NOTE_OCTAVE = 0xAC9; 20 | 21 | // Command Lists to send to FM Chip. Format is register, value pairs 22 | const static uint16_t YM_INIT_CMDS = 0xB29; 23 | // Address is ($60 + (8 * Operator) + Channel). Total Level affects the total output volume of the sound. 24 | const static uint16_t YM_LEVEL_CMDS1 = 0xB49; 25 | const static uint16_t YM_LEVEL_CMDS2 = 0xB81; 26 | const static uint16_t YM_RELEASE_RATE = 0xB8A; 27 | 28 | // PCM Sample Properties 29 | const static uint16_t PCM_INFO = 0xDDD; 30 | 31 | // YM: Passing Breeze 32 | const static uint16_t DATA_BREEZE = 0xE26; 33 | 34 | // YM: Splash Wave 35 | const static uint16_t DATA_SPLASH = 0x20C8; 36 | 37 | // YM: Magical Sound Shower 38 | const static uint16_t DATA_MAGICAL = 0x3D5F; 39 | 40 | // YM: Last Wave 41 | const static uint16_t DATA_LASTWAVE = 0x5F2D; 42 | 43 | // PCM: Safety Zone 44 | const static uint16_t DATA_SAFETY = 0x69A9; 45 | 46 | // PCM: Slip Sound 47 | const static uint16_t DATA_SLIP = 0x69E6; 48 | 49 | // YM: Coin IN Sound 50 | const static uint16_t DATA_COININ = 0x6A24; 51 | 52 | // YM: Checkpoint beeps 53 | const static uint16_t DATA_CHECKPOINT = 0x6A60; 54 | 55 | // YM: Signal 1 Sound 56 | const static uint16_t DATA_SIGNAL1 = 0x6A87; 57 | 58 | // YM: Signal 2 Sound 59 | const static uint16_t DATA_SIGNAL2 = 0x6AA7; 60 | 61 | // YM: Beep 1 Sound 62 | const static uint16_t DATA_BEEP1 = 0x6AC7; 63 | 64 | // PCM: Crash Sound 65 | const static uint16_t DATA_CRASH1 = 0x6C15; 66 | 67 | // PCM: Rebound Sound 68 | const static uint16_t DATA_REBOUND = 0x6CFF; 69 | 70 | // PCM: Crash Sound 71 | const static uint16_t DATA_CRASH2 = 0x6C8A; 72 | 73 | // PCM: Weird Sound 74 | const static uint16_t DATA_WEIRD = 0x6D61; 75 | 76 | // PCM: Crowd Cheers 77 | const static uint16_t DATA_CHEERS = 0x6F16; 78 | 79 | // PCM: Crowd Cheers 2 80 | const static uint16_t DATA_CHEERS2 = 0x6F53; 81 | 82 | // PCM: Voice 1, Checkpoint (Sound Data) 83 | const static uint16_t DATA_VOICE1 = 0x6F91; 84 | 85 | // PCM: Voice 2, Congratulations 86 | const static uint16_t DATA_VOICE2 = 0x6FCA; 87 | 88 | // PCM: Voice 3, Get Ready 89 | const static uint16_t DATA_VOICE3 = 0x7003; 90 | 91 | // YM: UFO (Unused) 92 | const static uint16_t DATA_UFO = 0x703D; 93 | 94 | // YM: Beep 2 Sound 95 | const static uint16_t DATA_BEEP2 = 0x71F9; 96 | 97 | // PCM: Wave Sample 98 | const static uint16_t DATA_WAVE = 0x748B; 99 | 100 | // Enhancement: Add Custom Music Data To End Of Z80 ROM 101 | const static uint16_t DATA_CUSTOM = 0x84B9; 102 | 103 | // Engine Tone Table. 5 Bytes per entry. 104 | // Engine Note: Start Address Low, Start Address High, Volume Multiplier, Unknown, Pitch 105 | const static uint16_t ENGINE_ADR_TABLE = 0x7951; 106 | 107 | // Traffic Volume Multiply Table. How much to increase traffic volume by dependent on distance. 108 | const static uint16_t TRAFFIC_VOL_MULTIPLY = 0x7CEF; 109 | }; -------------------------------------------------------------------------------- /src/main/engine/audio/osoundint.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Interface to Ported Z80 Code. 3 | Handles the interface between 68000 program code and Z80. 4 | 5 | Also abstracted here, so the more complex OSound class isn't exposed 6 | to the main code directly 7 | 8 | Copyright Chris White. 9 | See license.txt for more details. 10 | ***************************************************************************/ 11 | 12 | #include "engine/outrun.hpp" 13 | #include "engine/audio/osound.hpp" 14 | #include "engine/audio/osoundint.hpp" 15 | 16 | OSoundInt osoundint; 17 | OSound osound; 18 | 19 | OSoundInt::OSoundInt() 20 | { 21 | pcm_ram = new uint8_t[PCM_RAM_SIZE]; 22 | has_booted = false; 23 | } 24 | 25 | OSoundInt::~OSoundInt() 26 | { 27 | delete[] pcm_ram; 28 | } 29 | 30 | void OSoundInt::init() 31 | { 32 | if (pcm == NULL) 33 | pcm = new SegaPCM(SOUND_CLOCK, &roms.pcm, pcm_ram, SegaPCM::BANK_512); 34 | 35 | if (ym == NULL) 36 | ym = new YM2151(0.5f, SOUND_CLOCK); 37 | 38 | pcm->init(config.sound.rate, config.fps); 39 | ym->init(config.sound.rate, config.fps); 40 | 41 | reset(); 42 | 43 | // Clear PCM Chip RAM 44 | for (uint16_t i = 0; i < PCM_RAM_SIZE; i++) 45 | pcm_ram[i] = 0; 46 | 47 | for (uint8_t i = 0; i < 8; i++) 48 | engine_data[i] = 0; 49 | 50 | osound.init(ym, pcm_ram); 51 | } 52 | 53 | // Clear sound queue 54 | // Source: 0x5086 55 | void OSoundInt::reset() 56 | { 57 | sound_counter = 0; 58 | sound_head = 0; 59 | sound_tail = 0; 60 | sounds_queued = 0; 61 | 62 | audio_ticks = 0; 63 | } 64 | 65 | void OSoundInt::tick() 66 | { 67 | // The audio code is updated 125 times per second 68 | audio_ticks += (125.0 / config.fps); 69 | 70 | // Ticks per frame will vary between 2 and 3 at 60fps. 71 | const int max_ticks = (int) audio_ticks; 72 | 73 | for (int i = 0; i < max_ticks; i++) 74 | { 75 | play_queued_sound(); // Process audio commands from main program code 76 | osound.tick(); // Tick Ported Z80 Audio Code 77 | } 78 | 79 | audio_ticks -= max_ticks; 80 | } 81 | 82 | // ---------------------------------------------------------------------------- 83 | // Sound Queuing Code 84 | // ---------------------------------------------------------------------------- 85 | 86 | // Play Queued Sounds & Send Engine Noise Commands to Z80 87 | // Was called by horizontal interrupt routine 88 | // Source: 0x564E 89 | void OSoundInt::play_queued_sound() 90 | { 91 | if (!has_booted) 92 | { 93 | sound_head = 0; 94 | sounds_queued = 0; 95 | return; 96 | } 97 | 98 | // Process the lot in one go. 99 | for (int counter = 0; counter < 8; counter++) 100 | { 101 | // Process queued sound 102 | if (counter == 0) 103 | { 104 | if (sounds_queued != 0) 105 | { 106 | osound.command_input = queue[sound_head]; 107 | sound_head = (sound_head + 1) & QUEUE_LENGTH; 108 | sounds_queued--; 109 | } 110 | else 111 | { 112 | osound.command_input = sound::RESET; 113 | } 114 | } 115 | // Process player engine sounds and passing traffic 116 | else 117 | { 118 | osound.engine_data[counter] = engine_data[counter]; 119 | } 120 | } 121 | } 122 | 123 | // Queue a sound in service mode 124 | // Used to trigger both sound effects and music 125 | // Source: 0x56C6 126 | void OSoundInt::queue_sound_service(uint8_t snd) 127 | { 128 | if (has_booted) 129 | add_to_queue(snd); 130 | else 131 | queue_clear(); 132 | } 133 | 134 | // Queue a sound in-game 135 | // Note: This version has an additional check, so that certain sounds aren't played depending on game mode 136 | // Source: 0x56D4 137 | void OSoundInt::queue_sound(uint8_t snd) 138 | { 139 | if (has_booted) 140 | { 141 | if (outrun.game_state == GS_ATTRACT) 142 | { 143 | // Return if we are not playing sound in attract mode 144 | if (!config.sound.advertise && snd != sound::COIN_IN) return; 145 | 146 | // Do not play music in attract mode, even if attract sound enabled 147 | if (snd == sound::MUSIC_BREEZE || snd == sound::MUSIC_MAGICAL || 148 | snd == sound::MUSIC_SPLASH || snd == sound::MUSIC_LASTWAVE) 149 | return; 150 | } 151 | add_to_queue(snd); 152 | } 153 | else 154 | queue_clear(); 155 | } 156 | 157 | void OSoundInt::add_to_queue(uint8_t snd) 158 | { 159 | // Add sound to the tail end of the queue 160 | queue[sound_tail] = snd; 161 | sound_tail = (sound_tail + 1) & QUEUE_LENGTH; 162 | sounds_queued++; 163 | } 164 | 165 | void OSoundInt::queue_clear() 166 | { 167 | sound_tail = 0; 168 | sounds_queued = 0; 169 | } -------------------------------------------------------------------------------- /src/main/engine/audio/osoundint.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Interface to Ported Z80 Code. 3 | Handles the interface between 68000 program code and Z80. 4 | 5 | Also abstracted here, so the more complex OSound class isn't exposed 6 | to the main code directly 7 | 8 | Copyright Chris White. 9 | See license.txt for more details. 10 | ***************************************************************************/ 11 | 12 | #pragma once 13 | 14 | #include "hwaudio/segapcm.hpp" 15 | #include "hwaudio/ym2151.hpp" 16 | #include "engine/audio/commands.hpp" 17 | 18 | class OSoundInt 19 | { 20 | public: 21 | // SoundChip: Sega Custom Sample Generator 22 | SegaPCM* pcm; 23 | 24 | // SoundChip: Yamaha YM2151 25 | YM2151* ym; 26 | 27 | const static uint16_t PCM_RAM_SIZE = 0x100; 28 | 29 | // Note whether the game has booted 30 | bool has_booted; 31 | 32 | // [+0] Unused 33 | // [+1] Engine pitch high 34 | // [+2] Engine pitch low 35 | // [+3] Engine pitch vol 36 | // [+4] Traffic data #1 37 | // [+5] Traffic data #2 38 | // [+6] Traffic data #3 39 | // [+7] Traffic data #4 40 | uint8_t engine_data[8]; 41 | 42 | OSoundInt(); 43 | ~OSoundInt(); 44 | 45 | void init(); 46 | void reset(); 47 | void tick(); 48 | 49 | void play_queued_sound(); 50 | void queue_sound_service(uint8_t snd); 51 | void queue_sound(uint8_t snd); 52 | void queue_clear(); 53 | 54 | private: 55 | // 4 MHz 56 | static const uint32_t SOUND_CLOCK = 4000000; 57 | 58 | // Fractionally counts number of times audio code must be called 59 | // We call the audio code 125 times per frame from a timing perspective. 60 | double audio_ticks; 61 | 62 | // Reference to 0xFF bytes of PCM Chip RAM 63 | uint8_t* pcm_ram; 64 | 65 | // Controls what type of sound we're going to process in the interrupt routine 66 | uint8_t sound_counter; 67 | 68 | static const uint8_t QUEUE_LENGTH = 0x1F; 69 | uint8_t queue[QUEUE_LENGTH + 1]; 70 | 71 | // Number of sounds queued 72 | uint8_t sounds_queued; 73 | 74 | // Positions in the queue 75 | uint8_t sound_head, sound_tail; 76 | 77 | void add_to_queue(uint8_t snd); 78 | }; 79 | 80 | extern OSoundInt osoundint; -------------------------------------------------------------------------------- /src/main/engine/oanimseq.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Animation Sequences. 3 | 4 | Used in three areas of the game: 5 | - The sequence at the start with the Ferrari driving in from the side 6 | - Flag Waving Man 7 | - 5 x End Sequences 8 | 9 | See "oanimsprite.hpp" for the specific format used by animated sprites. 10 | It is essentially a deviation from the normal sprites in the game. 11 | 12 | Copyright Chris White. 13 | See license.txt for more details. 14 | ***************************************************************************/ 15 | 16 | #pragma once 17 | 18 | #include "oanimsprite.hpp" 19 | 20 | class OAnimSeq 21 | { 22 | public: 23 | // Man at line with start flag 24 | oanimsprite anim_flag; 25 | 26 | // Ferrari Animation Sequence 27 | oanimsprite anim_ferrari; // 1 28 | 29 | // Passenger Animation Sequences 30 | oanimsprite anim_pass1; // 2 31 | oanimsprite anim_pass2; // 3 32 | 33 | // End Sequence Stuff 34 | oanimsprite anim_obj1; // 4 35 | oanimsprite anim_obj2; // 5 36 | oanimsprite anim_obj3; // 6 37 | oanimsprite anim_obj4; // 7 38 | oanimsprite anim_obj5; // 8 39 | oanimsprite anim_obj6; // 9 40 | oanimsprite anim_obj7; // 10 41 | oanimsprite anim_obj8; // 10 42 | 43 | // End sequence to display (0-4) 44 | uint8_t end_seq; 45 | 46 | OAnimSeq(void); 47 | ~OAnimSeq(void); 48 | 49 | //void init(oentry*, oentry*, oentry*, oentry*); 50 | void init(oentry*); 51 | void flag_seq(); 52 | void ferrari_seq(); 53 | void anim_seq_intro(oanimsprite*); 54 | void init_end_seq(); 55 | void tick_end_seq(); 56 | 57 | private: 58 | // End Sequence Animation Position 59 | int16_t seq_pos; 60 | 61 | // End Sequence State (0 = Init, 1 = Tick) 62 | uint8_t end_seq_state; 63 | 64 | // Used for Ferrari End Animation Sequence 65 | bool ferrari_stopped; 66 | 67 | void init_end_sprites(); 68 | void tick_ferrari(); 69 | void anim_seq_outro(oanimsprite*, int pal_override = -1); 70 | void anim_seq_shadow(oanimsprite*, oanimsprite*); 71 | void anim_seq_outro_ferrari(); 72 | bool read_anim_data(oanimsprite*); 73 | }; 74 | 75 | extern OAnimSeq oanimseq; 76 | -------------------------------------------------------------------------------- /src/main/engine/oanimsprite.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Animated Sprites. 3 | 4 | This format is essentially a deviation from the normal sprites used in 5 | the game. 6 | 7 | Some of the entries in the block of memory are replaced and used for 8 | other purposes, which can be seen below. 9 | 10 | Copyright Chris White. 11 | See license.txt for more details. 12 | ***************************************************************************/ 13 | 14 | #pragma once 15 | 16 | #include "oentry.hpp" 17 | 18 | class oanimsprite 19 | { 20 | public: 21 | // Base Sprite 22 | oentry* sprite; 23 | 24 | //+0x06 [Byte] Sprite/Object Index Being Processed 25 | // 1 = Car Door 26 | // 2 = Ferrari Interior 27 | // 3 = Car Shadow 28 | // 4 = Man Sprite 29 | // 5 = Man Shadow 30 | // 6 = Female Sprite 31 | // 7 = Female Shadow 32 | // 8 = Trophy Person 33 | // 9 = Trophy Shadow 34 | // A = After Effects (e.g. smoke cloud on genie animation) 35 | 36 | //+0x1E [Long] Reference to the CURRENT block of animation data. 37 | uint32_t anim_addr_curr; 38 | 39 | //+0x22 [Long] Reference to the NEXT block of animation data. 40 | uint32_t anim_addr_next; 41 | 42 | //+0x26 [Word] Animation Frame Number 43 | int16_t anim_frame; 44 | 45 | //+0x28 [Word] Frame Delay (Before increment to next frame) 46 | uint8_t frame_delay; 47 | 48 | //+0x2A [Word] Increment End Sequence Position When Set 49 | uint16_t anim_props; 50 | 51 | //+0x2C [Word] Animation State 52 | int16_t anim_state; 53 | 54 | void init(oentry* s) 55 | { 56 | sprite = s; 57 | sprite->function_holder = -1; 58 | anim_addr_curr = 0; 59 | anim_addr_next = 0; 60 | anim_frame = 0; 61 | frame_delay = 0; 62 | anim_props = 0; 63 | anim_state = 0; 64 | } 65 | }; -------------------------------------------------------------------------------- /src/main/engine/oattractai.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Ferrari AI and Logic Routines. 3 | Used by Attract Mode and the end of game Bonus Sequence. 4 | 5 | This code contains a port of the original AI and a new enhanced AI. 6 | 7 | Enhanced AI Bug-Fixes: 8 | ---------------------- 9 | - AI is much better at driving tracks without crashing into scenery. 10 | - No weird juddering when turning corners. 11 | - No brake light flickering. 12 | - Can drive any stage in the game competently. 13 | - Selects a true random route, rather than a pre-defined route. 14 | - Can handle split tracks correctly. 15 | 16 | It still occasionally collides with scenery on the later stages, but 17 | I think this is ok - as we want to demo a few collisions! 18 | 19 | Notes on the original AI: 20 | ------------------------- 21 | The final behaviour of the AI differs from the original game. 22 | 23 | This is because the core Ferrari logic the AI relies on is in turn 24 | dependent on timing behaviour between the two 68k CPUs. 25 | 26 | Differences in timing lead to subtle differences in the road x position 27 | at that particular point of time. Over time, these differences are 28 | magnified. 29 | 30 | MAME does not accurately reproduce the arcade machine with regard to 31 | this. And the Saturn conversion also exhibits different behaviour. 32 | 33 | The differences are relatively minor but noticeable. 34 | 35 | Copyright Chris White. 36 | See license.txt for more details. 37 | ***************************************************************************/ 38 | 39 | #pragma once 40 | 41 | #include "outrun.hpp" 42 | 43 | class OAttractAI 44 | { 45 | public: 46 | OAttractAI(void); 47 | ~OAttractAI(void); 48 | 49 | void init(); 50 | void tick_ai_enhanced(); 51 | 52 | void tick_ai(); 53 | void check_road_bonus(); 54 | void set_steering_bonus(); 55 | 56 | private: 57 | int8_t last_stage; 58 | void check_road(); 59 | void set_steering(); 60 | }; 61 | 62 | extern OAttractAI oattractai; 63 | -------------------------------------------------------------------------------- /src/main/engine/obonus.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Bonus Points Code. 3 | 4 | This is the code that displays your bonus points on completing the game. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #include "engine/ostats.hpp" 11 | #include "engine/ohud.hpp" 12 | #include "engine/outils.hpp" 13 | #include "engine/obonus.hpp" 14 | 15 | OBonus obonus; 16 | 17 | OBonus::OBonus(void) 18 | { 19 | } 20 | 21 | OBonus::~OBonus(void) 22 | { 23 | } 24 | 25 | void OBonus::init() 26 | { 27 | bonus_control = BONUS_DISABLE; 28 | bonus_state = BONUS_TEXT_INIT; 29 | 30 | bonus_counter = 0; 31 | } 32 | 33 | // Display Text and countdown time for bonus mode 34 | // Source: 0x99E0 35 | void OBonus::do_bonus_text() 36 | { 37 | switch (bonus_state) 38 | { 39 | case BONUS_TEXT_INIT: 40 | init_bonus_text(); 41 | break; 42 | 43 | case BONUS_TEXT_SECONDS: 44 | decrement_bonus_secs(); 45 | break; 46 | 47 | case BONUS_TEXT_CLEAR: 48 | case BONUS_TEXT_DONE: 49 | if (bonus_counter < 60) 50 | bonus_counter++; 51 | else 52 | { 53 | ohud.blit_text2(TEXT2_BONUS_CLEAR1); 54 | ohud.blit_text2(TEXT2_BONUS_CLEAR2); 55 | ohud.blit_text2(TEXT2_BONUS_CLEAR3); 56 | } 57 | break; 58 | } 59 | } 60 | 61 | // Calculate Bonus Seconds. This uses the seconds remaining for every lap raced. 62 | // Print stuff to text layer for bonus mode. 63 | // 64 | // Source: 0x9A9C 65 | void OBonus::init_bonus_text() 66 | { 67 | bonus_state = BONUS_TEXT_SECONDS; 68 | 69 | int16_t time_counter_bak = ostats.time_counter << 8; 70 | ostats.time_counter = 0x30; 71 | 72 | uint16_t total_time = 0; 73 | 74 | if (outrun.cannonball_mode == Outrun::MODE_ORIGINAL) 75 | { 76 | // Add milliseconds remaining from previous stage times 77 | for (int i = 0; i < 5; i++) 78 | { 79 | total_time = outils::bcd_add(outils::DEC_TO_HEX[ostats.stage_times[i][2]], total_time); 80 | } 81 | } 82 | 83 | // Mask on top digit of lap milliseconds 84 | total_time &= 0xF0; 85 | 86 | if (total_time) 87 | { 88 | time_counter_bak |= (10 - (total_time >> 4)); 89 | } 90 | // So 60 seconds remaining on the clock and 3 from lap_seconds would be 0x0603 91 | 92 | // Extract individual 3 digits 93 | uint16_t digit_mid = (time_counter_bak >> 8 & 0x0F) * 10; 94 | uint16_t digit_top = (time_counter_bak >> 12 & 0x0F) * 100; 95 | uint16_t digit_bot = (time_counter_bak & 0x0F); 96 | 97 | // Write them back to final bonus seconds value 98 | bonus_secs = digit_bot + digit_mid + digit_top; 99 | 100 | // Write to text layer 101 | ohud.blit_text2(TEXT2_BONUS_POINTS); // Print "BONUS POINTS" 102 | ohud.blit_text1(TEXT1_BONUS_STOP); // Print full stop after Bonus Points text 103 | ohud.blit_text1(TEXT1_BONUS_SEC); // Print "SEC" 104 | ohud.blit_text1(TEXT1_BONUS_X); // Print 'X' symbol after SEC 105 | ohud.blit_text1(TEXT1_BONUS_PTS); // Print "PTS" 106 | 107 | // Blit big 100K number 108 | uint32_t src_addr = TEXT1_BONUS_100K; 109 | uint32_t dst_addr = 0x11065A; 110 | int8_t count = roms.rom0.read8(&src_addr); 111 | 112 | for (int8_t i = 0; i <= count; i++) 113 | ohud.blit_large_digit(&dst_addr, (roms.rom0.read8(&src_addr) - 0x30) << 1); 114 | 115 | blit_bonus_secs(); 116 | } 117 | 118 | // Decrement bonus seconds. Blit seconds remaining. 119 | // Source: 9A08 120 | void OBonus::decrement_bonus_secs() 121 | { 122 | if (bonus_counter < 60) 123 | { 124 | bonus_counter++; 125 | return; 126 | } 127 | 128 | // Play Signal 1 Sound In A Steady Fashion 129 | if ((((bonus_counter - 1) ^ bonus_counter) & BIT_2) == 0) 130 | osoundint.queue_sound(sound::SIGNAL1); 131 | 132 | // Increment Score by 100K points 133 | ostats.update_score(0x100000); 134 | 135 | // Blit bonus seconds remaining 136 | blit_bonus_secs(); 137 | 138 | if (--bonus_secs < 0) 139 | { 140 | bonus_counter = -1; 141 | bonus_state = BONUS_TEXT_CLEAR; 142 | } 143 | else 144 | bonus_counter++; 145 | 146 | } 147 | 148 | // Blit large yellow second remaining value e.g.: 23.3 149 | // Source: 0x9B7C 150 | void OBonus::blit_bonus_secs() 151 | { 152 | const uint8_t COL2 = 0x80; 153 | const uint16_t TILE_DOT = 0x8C2E; 154 | const uint16_t TILE_ZERO = 0x8420; 155 | 156 | uint32_t d1 = (bonus_secs / 100) << 8; 157 | uint32_t d4 = (bonus_secs / 100) * 100; 158 | 159 | uint32_t d2 = (bonus_secs - d4) / 10; 160 | uint32_t d3 = bonus_secs - d4; 161 | 162 | d4 = d2; 163 | d2 <<= 4; 164 | d4 *= 10; 165 | d3 -= d4; 166 | d1 += d2; 167 | d1 += d3; 168 | 169 | d3 = (d1 & 0xF) << 1; 170 | d2 = (d1 & 0xF0) >> 3; 171 | d1 = (d1 & 0xF00) >> 7; 172 | 173 | uint32_t text_addr = 0x110644; 174 | 175 | // Blit Digit 1 176 | if (d1) 177 | { 178 | ohud.blit_large_digit(&text_addr, d1); 179 | } 180 | else 181 | { 182 | video.write_text16(text_addr, TILE_ZERO); 183 | video.write_text16(text_addr | COL2, TILE_ZERO); 184 | text_addr += 2; 185 | } 186 | 187 | // Blit Digit 2 188 | ohud.blit_large_digit(&text_addr, d2); 189 | // Blit Dot 190 | video.write_text16(text_addr | COL2, TILE_DOT); 191 | text_addr += 2; 192 | // Blit Digit 3 193 | ohud.blit_large_digit(&text_addr, d3); 194 | } 195 | -------------------------------------------------------------------------------- /src/main/engine/obonus.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Bonus Points Code. 3 | 4 | This is the code that displays your bonus points on completing the game. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #pragma once 11 | 12 | #include "outrun.hpp" 13 | 14 | class OBonus 15 | { 16 | public: 17 | // Bonus Control 18 | int8_t bonus_control; 19 | enum 20 | { 21 | BONUS_DISABLE = 0x0, // 0 = Bonus Mode Disabled 22 | BONUS_INIT = 0x4, // 4 = Init Bonus Mode 23 | BONUS_TICK = 0x8, // 8 = Tick Down Bonus Time 24 | BONUS_SEQ0 = 0xC, // C = End Seq Animation Stage #0 - Stages used to set Ferrari frames 25 | BONUS_SEQ1 = 0x10, // 10 = End Seq Animation Stage #1 26 | BONUS_SEQ2 = 0x14, // 14 = End Seq Animation Stage #2 27 | BONUS_SEQ3 = 0x18, // 18 = End Seq Animation Stage #3 28 | BONUS_END = 0x1C, // 1C = End Bonus Sequence 29 | }; 30 | 31 | // Bonus State 32 | // 33 | // 0 = Init Bonus Points Text & Calculate Bonus Score 34 | // 1 = Decrement Bonus Seconds 35 | // 2 = Clear Bonus Text 36 | // 3 = Do Not Execute Bonus Text Block 37 | int8_t bonus_state; 38 | enum 39 | { 40 | BONUS_TEXT_INIT = 0, 41 | BONUS_TEXT_SECONDS = 1, 42 | BONUS_TEXT_CLEAR = 2, 43 | BONUS_TEXT_DONE = 3, 44 | }; 45 | 46 | // Timer used by bonus mode logic (Added from Rev. A onwards) 47 | int16_t bonus_timer; 48 | 49 | OBonus(void); 50 | ~OBonus(void); 51 | void init(); 52 | 53 | void do_bonus_text(); 54 | 55 | private: 56 | // Bonus seconds (for seconds countdown at bonus stage) 57 | // 58 | // Stored as hex, but should be converted to decimal for true value. 59 | // 60 | // So 0x314 would be 78.8 seconds 61 | int16_t bonus_secs; 62 | 63 | // Timing counter used in bonus code logic 64 | int16_t bonus_counter; 65 | 66 | void init_bonus_text(); 67 | void blit_bonus_secs(); 68 | void decrement_bonus_secs(); 69 | }; 70 | 71 | extern OBonus obonus; 72 | 73 | -------------------------------------------------------------------------------- /src/main/engine/ocrash.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Collision & Crash Code. 3 | 4 | There are two types of collision: Scenery & Traffic. 5 | 6 | 1/ Traffic: The Ferrari will spin after a collision. 7 | 2/ Scenery: There are three types of scenery collision: 8 | - Low speed bump. Car rises slightly in the air and stalls. 9 | - Mid speed spin. Car spins and slides after collision. 10 | - High speed flip. If slightly slower, car rolls into screen. 11 | Otherwise, grows towards screen and vanishes 12 | 13 | Known Issues With Original Code: 14 | - Passenger sprites flicker if they land moving in the water on Stage 1 15 | 16 | The Ferrari sprite is used differently by the crash code. 17 | As there's only one of them, I've rolled the additional variables into 18 | this class. 19 | 20 | Copyright Chris White. 21 | See license.txt for more details. 22 | ***************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include "outrun.hpp" 27 | 28 | class OCrash 29 | { 30 | public: 31 | // Reference to sprite used by crash code 32 | oentry* spr_ferrari; 33 | oentry* spr_shadow; 34 | oentry* spr_pass1; 35 | oentry* spr_pass1s; 36 | oentry* spr_pass2; 37 | oentry* spr_pass2s; 38 | 39 | // Default value to reset skid counter to on collision 40 | const static uint8_t SKID_RESET = 20; 41 | 42 | // Maximum value to allow skid reset to be set to during collision 43 | const static uint8_t SKID_MAX = 30; 44 | 45 | // Amount to adjust car x position by when skidding 46 | const static uint8_t SKID_X_ADJ = 24; 47 | 48 | //Crash State [Investigate Further] 49 | // 50 | // 0 = No crash 51 | // 1 = Collision with object. Init Spin if medium speed collision. 52 | // 2 = Flip Car. 53 | // 3 = Slide Car. Trigger Smoke Cloud. 54 | // 4 = Horizontally Flip Car. Trigger Smoke Cloud. 55 | // 5 = SPIN: Remove Smoke From Spin. Girl Points Finger. 56 | // FLIP: Flip Animation Done. 57 | // 6 = Pan Camera To Track Centre 58 | // 7 = Camera Repositioned. Ready For Restart 59 | // 60 | // (Note that hitting another vehicle and skidding does not affect the crash state) 61 | int8_t crash_state; 62 | 63 | // Skid Counter. Set On Collision With Another Vehicle Only. 64 | // 65 | // If positive, skid to the left. 66 | // If negative, skid to the right. 67 | int16_t skid_counter; 68 | int16_t skid_counter_bak; 69 | 70 | // Spin Control 1 - SPIN only 71 | // 72 | // 0 = No Spin 73 | // 1 = Init Spin Car 74 | // 2 = Spin In Progress 75 | uint8_t spin_control1; 76 | 77 | uint8_t spin_control2; 78 | 79 | // Increments on a per collision basis (Follows on from collision_sprite being set) 80 | // 81 | // Used to cycle passenger animations after a crash 82 | // 83 | // coll_count1 != coll_count2 = Crash Subroutines Not Enabled 84 | // coll_count1 == coll_count2 = Crash Subroutines Enabled 85 | int16_t coll_count1; 86 | int16_t coll_count2; 87 | 88 | // Counter that increments per-frame during a crash scenario 89 | int16_t crash_counter; 90 | 91 | // Denotes the spin/flip number following the crash. 92 | // 93 | // 0 = No crash has taken place yet 94 | // 1 = Crash has taken place this level 95 | // 2 = Crash. First Spin/Flip. 96 | // 3 = Crash. Second Spin/Flip. 97 | // 4 = Crash. Third Spin/Flip. 98 | int16_t crash_spin_count; 99 | 100 | // Crash Sprite Z Position 101 | int16_t crash_z; 102 | 103 | OCrash(void); 104 | ~OCrash(void); 105 | void init(oentry* f, oentry* s, oentry* p1, oentry* p1s, oentry* p2, oentry* p2s); 106 | bool is_flip(); 107 | void enable(); 108 | void clear_crash_state(); 109 | void tick(); 110 | 111 | private: 112 | 113 | // This is the rolled Ferrari sprite, which is configured differently for 114 | // the crash code. 115 | // The offsets indicate the original offsets in memory. 116 | 117 | //+1E [Word] Spins/Flips Remaining 118 | int16_t spinflipcount1; 119 | //+22 [Word] Crash Spin/Flip Count Copy 120 | int16_t spinflipcount2; 121 | //+24 [Word] Crash Slide Value (After Spin/Flip etc.) 122 | int16_t slide; 123 | //+26 [Word] Frame (actually an index into the Sprite format below) 124 | int16_t frame; 125 | //+28 [Long] Address of animation sequence (frame address, palette info etc.) 126 | uint32_t addr; 127 | //+2C [Word] Camera Pan X Target (for repositioning after crash) 128 | int16_t camera_x_target; 129 | //+2E [Word] Camera Pan Increment 130 | int16_t camera_xinc; 131 | //+30 [Word] Index into movement lookup table (to set y position of car during low speed bump) 132 | int16_t lookup_index; 133 | //+32 [Word] Frame to restore car to after bump routine 134 | int16_t frame_restore; 135 | //+34 [Word] Used as a shift value to change y position during shunt 136 | int16_t shift; 137 | //+36 [Word] Flip Only: 0 = Fast Crash. 1 = Slow Crash. 138 | int16_t crash_speed; 139 | //+38 [Word] Crash Z Increment (How much to change Crash Z Per Tick) 140 | int16_t crash_zinc; 141 | //+3A [Word] 0 = RHS, 1 = LHS? 142 | int16_t crash_side; 143 | 144 | // Passenger Frame to use during spin 145 | int16_t spin_pass_frame; 146 | 147 | int8_t crash_type; 148 | enum { CRASH_BUMP = 0, CRASH_SPIN = 1, CRASH_FLIP = 2 }; 149 | 150 | // Delay counter after crash. 151 | // Show animation (e.g. girl pointing finger, before car is repositioned) 152 | int16_t crash_delay; 153 | 154 | void do_crash(); 155 | void spin_switch(const uint16_t); 156 | void crash_switch(); 157 | 158 | void init_collision(); 159 | void do_collision(); 160 | void end_collision(); 161 | 162 | void do_bump(); 163 | void do_car_flip(); 164 | void init_finger(uint32_t); 165 | void trigger_smoke(); 166 | void post_flip_anim(); 167 | void pan_camera(); 168 | 169 | void init_spin1(); 170 | void init_spin2(); 171 | void collide_slow(); 172 | void collide_med(); 173 | void collide_fast(); 174 | 175 | void done(oentry*); 176 | 177 | void do_shadow(oentry*, oentry*); 178 | 179 | // Pointers to functions for crash code 180 | void (OCrash::*function_pass1)(oentry*); 181 | void (OCrash::*function_pass2)(oentry*); 182 | 183 | void do_crash_passengers(oentry*); 184 | void flip_start(oentry*); 185 | 186 | void crash_pass1(oentry*); 187 | void crash_pass2(oentry*); 188 | void crash_pass_flip(oentry*); 189 | 190 | void pass_flip(oentry*); 191 | void pass_situp(oentry*); 192 | void pass_turnhead(oentry*); 193 | }; 194 | 195 | extern OCrash ocrash; -------------------------------------------------------------------------------- /src/main/engine/oentry.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sprite Entry. 3 | 4 | This class represents a single sprite object, commonly used by OutRun. 5 | 6 | In the original codebase, each object consists of a 64 byte block of 7 | memory. Each of these blocks forms part of a jump table that is iterated 8 | each tick. The sprite referenced the address of the routine it used. 9 | 10 | Unfortunately, the system is messy and the usage of each 64 byte 11 | entry differs dependent on the game object. 12 | 13 | However, the majority are similar. And as such, I've tried to convert 14 | this structure to a more manageable class. For the conversion, rather 15 | than dynamically editing a jump table, I've called the sprite routines 16 | from the main code. 17 | 18 | It's not perfect, but struck a reasonable balance between clarity and 19 | being able to debug the conversion. 20 | 21 | All in-game objects that populate the gameworld use this structure. 22 | 23 | Copyright Chris White. 24 | See license.txt for more details. 25 | ***************************************************************************/ 26 | 27 | #pragma once 28 | 29 | #include "stdint.hpp" 30 | 31 | class oentry 32 | { 33 | public: 34 | // +00 [Byte] Bit 7 Enables/Disables Address 35 | // Bit 6 36 | // Bit 5 Set to Draw Sprite 37 | // Bit 4 Set to Enable Shadows 38 | // Bit 3 Traffic Sprite - Set to Use Large Shadow, Unset for Small Shadow (If Shadows Enabled) 39 | // Bit 2 Set if road_width >= 0x118 40 | // Bit 1 41 | // Bit 0 Set to Horizontally Flip Image 42 | uint8_t control; 43 | 44 | // +01 [Byte] Index Number of Jump 0,1,2,3 etc. 45 | uint8_t jump_index; 46 | 47 | // +02 [Long] Jump Address 48 | int8_t function_holder; 49 | 50 | // +06 [Byte] Multiple Uses. Used to identify sprites. 51 | // E.g. Passenger Sprites: Denote Man (0) or Woman (1) Sprite. 52 | // Course Map Sprites: Denote piece of map (just iterates from 0 upwards) 53 | uint8_t id; 54 | 55 | // +07 [Byte] Sprite Priority (In relation to tilemaps) & Shadow Settings 56 | // Default = 3 57 | uint8_t shadow; 58 | 59 | // +08 [Byte] Entry Number For Zoom Lookup Table 60 | // Looked up from ROM [0x30000 + (offset * 8)] 61 | // 0 = Hide Sprite 62 | uint8_t zoom; 63 | 64 | // +09 [Byte] 1/ Draw Routine To Use. [4-bits] dddd---- 65 | // 2/ Draw Properties. [4-bits]: ----yyxx 66 | // 67 | // For draw routine see SetupSpriteRoutine function 68 | // 69 | // xx 0 = Anchor Centre. 01 = Anchor Left. 10 = Anchor Right 70 | // yy 0 = Anchor Centre. 01 = Anchor Top. 10 = Anchor Bottom. 71 | uint8_t draw_props; 72 | 73 | enum 74 | { 75 | CENTRE = 0, 76 | LEFT = 1, 77 | RIGHT = 2, 78 | TOP = 4, 79 | BOTTOM = 8 80 | }; 81 | 82 | // +0A [Byte] Sprite Colour Palette: Source index into ROM. 83 | // For the car sprite: 84 | // 0-2 = Wheels turning 85 | // 3-5 = Brake lights on, Wheels turning 86 | uint16_t pal_src; 87 | 88 | // +0B [Byte] Sprite Colour Palette: Destination index into PALETTE RAM. 0 - 7F. 89 | uint8_t pal_dst; 90 | 91 | // +0C [Word] Sprite X (Screen/Camera) \ (Set from world co-ordinates) 92 | // +0E [Word] Sprite Y (Screen/Camera) / 93 | int16_t x, y; 94 | 95 | // +10 [Word] Sprite Width 96 | uint16_t width; 97 | 98 | // +14 [Word] Sprite to Sprite Priority (Lower Number = Draw Earlier) 99 | uint16_t priority; 100 | 101 | // +16 [Word] Entry Number In Sprite Destination Table 102 | // This is the offset address in memory we want to copy the sprite to. 103 | // And is necessary because it ties in with sprite ordering. 104 | uint16_t dst_index; 105 | 106 | // +18 [Long] Address of actual sprite data we want to render. Offset 1/2 into ROM[0x20000]. 107 | uint32_t addr; 108 | 109 | // +1C [Word] Distance into screen or Sprite to Road Priority (High Number = Closer to camera) 110 | uint16_t road_priority; 111 | 112 | // +1E [Word] Sprite Counter Reload Value (For Animations etc.) 113 | int16_t reload; 114 | 115 | // +20 [Word] Sprite Counter (For Animations etc.) 116 | uint16_t counter; 117 | 118 | // +22 [Word] Sprite X (World) COPY 119 | int16_t xw1; 120 | 121 | // +24 [Long] Sprite Z / Zoom (World) - 0x10000 = Most distant value 122 | int32_t z; 123 | 124 | // +28 [Word] Original Traffic Speed 125 | int16_t traffic_speed; 126 | 127 | // +2A [Word] Sprite Type (Actual Object Index) 128 | uint16_t type; 129 | 130 | // +2C [Word] Sprite X (World) COPY 131 | int16_t xw2; 132 | 133 | // +2E [Word] Bit 2 - Denote car is close to other traffic [z-axis] 134 | // Bit 1 - Denote traffic on RHS 135 | // Bit 0 - Denote traffic on LHS 136 | 137 | // Note: Set to 0xFF on collision 138 | uint8_t traffic_proximity; 139 | 140 | // +30 [Word] 141 | uint8_t traffic_fx; 142 | 143 | // +32 [Word] Original Traffic Speed 144 | int16_t traffic_orig_speed; 145 | 146 | // +36 [Word] Nearby Traffic Speed 147 | int16_t traffic_near_speed; 148 | 149 | // +38 [Word] Sprite Y (World) 150 | uint16_t yw; 151 | 152 | // +3A [Word] Passenger Props (Passenger Sprites Only) 153 | int16_t pass_props; 154 | 155 | // Initalize to default values 156 | void init(uint8_t i) 157 | { 158 | control = 0; 159 | jump_index = i; 160 | function_holder = -1; 161 | id = 0; 162 | shadow = 3; 163 | zoom = 0; 164 | draw_props = 0; 165 | pal_src = 0; 166 | pal_dst = 0; 167 | x = 0; 168 | y = 0; 169 | width = 0; 170 | priority = 0; 171 | dst_index = 0; 172 | addr = 0; 173 | road_priority = 0; 174 | reload = 0; 175 | counter = 0; 176 | xw1 = 0; 177 | z = 0; 178 | traffic_speed = 0; 179 | type = 0; 180 | xw2 = 0; 181 | traffic_proximity = 0; 182 | traffic_fx = 0; 183 | traffic_orig_speed = 0; 184 | traffic_near_speed = 0; 185 | yw = 0; 186 | pass_props = 0; 187 | } 188 | }; -------------------------------------------------------------------------------- /src/main/engine/ohiscore.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Best Outrunners Name Entry & Display. 3 | Used in attract mode, and at game end. 4 | 5 | Copyright Chris White. 6 | See license.txt for more details. 7 | ***************************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include "stdint.hpp" 12 | 13 | struct score_entry 14 | { 15 | uint32_t score; 16 | uint8_t initial1; 17 | uint8_t initial2; 18 | uint8_t initial3; 19 | uint32_t maptiles; 20 | uint16_t time; 21 | }; 22 | 23 | class OHiScore 24 | { 25 | public: 26 | // Number of score entries in table 27 | const static uint8_t NO_SCORES = 20; 28 | 29 | // 20 Score Entries 30 | score_entry scores[NO_SCORES]; 31 | 32 | OHiScore(void); 33 | ~OHiScore(void); 34 | 35 | void init(); 36 | void init_def_scores(); 37 | void tick(); 38 | void setup_pal_best(); 39 | void setup_road_best(); 40 | void display_scores(); 41 | 42 | private: 43 | const static uint16_t TILE_PROPS = 0x8030; 44 | 45 | // +C : Best OutRunners State 46 | uint8_t best_or_state; 47 | 48 | // +14: State of score logic 49 | uint8_t state; 50 | 51 | // +16: High Score Position In Table 52 | int8_t score_pos; 53 | 54 | // +17 Selected Initial (0-2) 55 | int8_t initial_selected; 56 | 57 | // +18: Selected Letter 58 | int16_t letter_selected; 59 | 60 | // +1A: Acceleration Value Current 61 | int16_t acc_curr; 62 | 63 | // +1C: Acceleration Value Previous 64 | int16_t acc_prev; 65 | 66 | // +1E: Steering Value 67 | int16_t steer; 68 | 69 | // +22: Flashing counter 70 | uint8_t flash; 71 | 72 | // +24: Total number of minicars that have reached destination 73 | int8_t dest_total; 74 | 75 | // +26: High Score Table Display Position 76 | int8_t score_display_pos; 77 | 78 | enum 79 | { 80 | STATE_GETPOS, // Detect Score Position, Insert Score, Init Table 81 | STATE_DISPLAY, // Display Basic High Score Table 82 | STATE_ENTRY, // Init Name Entry 83 | STATE_DONE // Score Done 84 | }; 85 | 86 | // Mini-car data format. 87 | // These are the mini cars that move across and reveal the high score entries 88 | struct minicar_entry 89 | { 90 | int16_t pos; // [+0] Word 0: Position 91 | int16_t speed; // [+2] Word 1: Speed (increments over time) 92 | int16_t base_speed; // [+4] Word 2: Base Speed 93 | int16_t dst_reached; // [+6] Word 3: Set when reached destination 94 | uint16_t tile_props; // [+8] Word 4: Palette/Priority bits for tile 95 | }; 96 | 97 | // Number of minicar entries 98 | const static uint8_t NO_MINICARS = 7; 99 | 100 | // 20 Score Entries 101 | minicar_entry minicars[NO_MINICARS]; 102 | 103 | // Stores Laptime conversion 104 | // +0: Minutes Digit 1 105 | // +1: Minutes Digit 2 106 | // +2: Seconds Digit 1 107 | // +3: Seconds Digit 2 108 | // +4: Milliseconds Digit 1 109 | // +5: Milliseconds Digit 2 110 | uint16_t laptime[6]; 111 | 112 | void get_score_pos(); 113 | void insert_score(); 114 | void set_display_pos(); 115 | void check_name_entry(); 116 | uint32_t get_score_adr(); 117 | void blit_alphabet(); 118 | void flash_entry(uint32_t adr); 119 | void do_input(uint32_t adr); 120 | int8_t read_controls(); 121 | void setup_minicars(); 122 | void tick_minicars(); 123 | void setup_minicars_pal(minicar_entry*); 124 | void blit_score_table(); 125 | void blit_scores(); 126 | void blit_digit(); 127 | void blit_initials(); 128 | void blit_route_map(); 129 | void blit_lap_time(); 130 | void convert_lap_time(uint16_t); 131 | }; 132 | 133 | extern OHiScore ohiscore; -------------------------------------------------------------------------------- /src/main/engine/ohud.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/src/main/engine/ohud.cpp -------------------------------------------------------------------------------- /src/main/engine/ohud.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Heads-Up Display (HUD) Code 3 | 4 | - Score Rendering 5 | - Timer Rendering 6 | - Rev Rendering 7 | - Minimap Rendering 8 | - Text Rendering 9 | 10 | Copyright Chris White. 11 | See license.txt for more details. 12 | ***************************************************************************/ 13 | 14 | #pragma once 15 | 16 | #include "outrun.hpp" 17 | 18 | class OHud 19 | { 20 | public: 21 | 22 | // Colours for default text palette 23 | enum 24 | { 25 | GREY = 0x84, 26 | PINK = 0x86, 27 | GREEN = 0x92, 28 | } colors; 29 | 30 | // Base for digits, for fast digit drawing 31 | const static uint16_t DIGIT_BASE = 0x30; 32 | 33 | OHud(void); 34 | ~OHud(void); 35 | 36 | void draw_main_hud(); 37 | void draw_fps_counter(int16_t); 38 | void clear_timetrial_text(); 39 | void do_mini_map(); 40 | void draw_timer1(uint16_t); 41 | void draw_timer2(uint16_t, uint32_t, uint16_t); 42 | void draw_lap_timer(uint32_t, uint8_t*, uint8_t); 43 | void draw_score_ingame(uint32_t); 44 | void draw_score(uint32_t, const uint32_t, const uint8_t); 45 | void draw_score_tile(uint32_t, const uint32_t, const uint8_t); 46 | void draw_stage_number(uint32_t, uint8_t, uint16_t col = GREEN); 47 | void draw_rev_counter(); 48 | void draw_debug_info(uint32_t pos, uint16_t height_pat, uint8_t sprite_pat); 49 | void blit_text1(uint32_t); 50 | void blit_text1(uint8_t x, uint8_t y, uint32_t src_addr); 51 | void blit_text2(uint32_t); 52 | void blit_text_big(const uint8_t Y, const char* text, bool do_notes = false); 53 | void blit_text_new(uint16_t, uint16_t, const char* text, uint16_t col = GREY); 54 | void blit_speed(uint32_t, uint16_t); 55 | void blit_large_digit(uint32_t*, uint8_t); 56 | void draw_copyright_text(); 57 | void draw_insert_coin(); 58 | void draw_credits(); 59 | uint32_t setup_mini_map(); 60 | uint32_t translate(const uint16_t x, const uint16_t y, const uint32_t BASE_POS = 0x110030); 61 | 62 | private: 63 | void draw_mini_map(uint32_t); 64 | }; 65 | 66 | extern OHud ohud; -------------------------------------------------------------------------------- /src/main/engine/oinitengine.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Core Game Engine Routines. 3 | 4 | - The main loop which advances the level onto the next segment. 5 | - Code to directly control the road hardware. For example, the road 6 | split and bonus points routines. 7 | - Code to determine whether to initialize certain game modes 8 | (Crash state, Bonus points, road split state) 9 | 10 | Copyright Chris White. 11 | See license.txt for more details. 12 | ***************************************************************************/ 13 | 14 | #pragma once 15 | 16 | #include "outrun.hpp" 17 | 18 | class OInitEngine 19 | { 20 | public: 21 | // Debug: Camera X Offset 22 | int16_t camera_x_off; 23 | 24 | // Is the in-game engine active? 25 | bool ingame_engine; 26 | 27 | // Time to wait before enabling ingame_engine after crash 28 | int16_t ingame_counter; 29 | 30 | // Road Split State 31 | // 0 = No Road Split 32 | // 1 = Init Road Split 33 | // 2 = Road Split 34 | // 3 = Beginning of split. User must choose. 35 | // 4 = Road physically splits into two individual roads 36 | // 5 = Road fully split. Init remove other road 37 | // 6 = Road split. Only one road drawn. 38 | // 7 = Unknown 39 | // 8 = Init Road Merge before checkpoint sign 40 | // 9 = Road Merge before checkpoint sign 41 | // A = Unknown 42 | // B = Checkpoint sign 43 | // C = Unused 44 | // D = Unused 45 | // E = Unused 46 | // F = Unused 47 | // 10 = Init Bonus Points Sequence 48 | // 11 = Bonus Points Sequence 49 | uint16_t rd_split_state; 50 | enum {SPLIT_NONE, SPLIT_INIT, SPLIT_CHOICE1, SPLIT_CHOICE2}; 51 | 52 | // Upcoming Road Type: 53 | // 0 = No change 54 | // 1 = Straight road 55 | // 2 = Right Bend 56 | // 3 = Left Bend 57 | int16_t road_type; 58 | int16_t road_type_next; 59 | enum {ROAD_NOCHANGE, ROAD_STRAIGHT, ROAD_RIGHT, ROAD_LEFT}; 60 | 61 | // End Of Stage Properties 62 | // 63 | // Bit 0: Fade Sky & Ground Palette To New Entry 64 | // Bit 1: Use Current Palette (Don't Bump To Next One) 65 | // Bit 2: Use Current Sky Palette For Fade (Don't Bump To Next One) 66 | // Bit 3: Loop back to stage 1 67 | uint8_t end_stage_props; 68 | 69 | uint32_t car_increment; // NEEDS REPLACING. Implementing here as a quick hack so routine works 70 | 71 | // Car X Position 72 | // 0000 = Centre of two road generators 73 | // 74 | // Turning Right REDUCES value, Turning Left INCREASES value 75 | // 76 | // 0xxx [pos] = Road Generator 1 Position (0 - xxx from centre) 77 | // Fxxx [neg] = Road Generator 2 Position (0 + xxx from centre) 78 | int16_t car_x_pos; 79 | int16_t car_x_old; 80 | 81 | // Checkpoint Marker 82 | 83 | // 0 = Checkpoint Not Past 84 | // -1 = Checkpoint Past 85 | int8_t checkpoint_marker; 86 | 87 | // Something to do with the increment / curve of the road 88 | int16_t road_curve; 89 | int16_t road_curve_next; 90 | 91 | // Road split logic handling to remove split 92 | // 0 = Normal Road Rendering 93 | // 1 = Road Has Split, Old Road now removed 94 | int8_t road_remove_split; 95 | 96 | // Route Selected 97 | // -1 = Left 98 | // 0 = Right 99 | // But confusingly, these values get swapped by a not instruction 100 | int8_t route_selected; 101 | 102 | // Road Width Change 103 | // 0 = No 104 | // -1 = In Progress 105 | int16_t change_width; 106 | 107 | OInitEngine(); 108 | ~OInitEngine(); 109 | void init(int8_t debug_level); 110 | 111 | void init_road_seg_master(); 112 | void init_crash_bonus(); 113 | void update_road(); 114 | void update_engine(); 115 | void update_shadow_offset(); 116 | void set_granular_position(); 117 | void set_fine_position(); 118 | 119 | void init_bonus(int16_t); // moved here for debugging purposes 120 | 121 | private: 122 | // Road width at merge point 123 | const static uint16_t RD_WIDTH_MERGE = 0xD4; 124 | 125 | // Road width of next section 126 | int16_t road_width_next; 127 | 128 | // Speed at which adjustment to road section occurs 129 | int16_t road_width_adj; 130 | 131 | int16_t granular_rem; 132 | 133 | uint16_t pos_fine_old; 134 | 135 | // Road Original Width. Used when adjustments are being made during road split, and end sequence initialisation 136 | int16_t road_width_orig; 137 | 138 | // Used by road merge logic, to control width of road 139 | int16_t road_width_merge; 140 | 141 | // ------------------------------------------------------------------------ 142 | // Route Information 143 | // ------------------------------------------------------------------------ 144 | 145 | // Used as part of road split code. 146 | // 0 = Route Info Not Updated 147 | // 1 = Route Info Updated 148 | int8_t route_updated; 149 | 150 | void setup_stage1(); 151 | void check_road_split(); 152 | void check_stage(); 153 | void init_split1(); 154 | void init_split2(); 155 | void init_split3(); 156 | void init_split4(); 157 | void init_split5(); 158 | void init_split6(); 159 | void init_split7(); 160 | void init_split9(); 161 | void init_split10(); 162 | void bonus1(); 163 | void bonus2(); 164 | void bonus3(); 165 | void bonus4(); 166 | void bonus5(); 167 | void bonus6(); 168 | void reload_stage1(); 169 | void init_split_next_level(); 170 | void test_bonus_mode(bool); 171 | }; 172 | 173 | extern OInitEngine oinitengine; -------------------------------------------------------------------------------- /src/main/engine/oinputs.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Process Inputs. 3 | 4 | - Read & Process inputs and controls. 5 | - Note, this class does not contain platform specific code. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | #include "sdl2/input.hpp" 14 | #include "outrun.hpp" 15 | 16 | class OInputs 17 | { 18 | public: 19 | 20 | const static uint8_t BRAKE_THRESHOLD1 = 0x80; 21 | const static uint8_t BRAKE_THRESHOLD2 = 0xA0; 22 | const static uint8_t BRAKE_THRESHOLD3 = 0xC0; 23 | const static uint8_t BRAKE_THRESHOLD4 = 0xE0; 24 | 25 | int8_t crash_input; 26 | 27 | // Acceleration Input 28 | int16_t input_acc; 29 | 30 | // Steering Input 31 | int16_t input_steering; 32 | 33 | // Processed / Adjusted Values 34 | int16_t steering_adjust; 35 | int16_t acc_adjust; 36 | int16_t brake_adjust; 37 | 38 | // True = High Gear. False = Low Gear. 39 | bool gear; 40 | 41 | OInputs(void); 42 | ~OInputs(void); 43 | 44 | void init(); 45 | void tick(); 46 | void adjust_inputs(); 47 | void do_gear(); 48 | uint8_t do_credits(); 49 | 50 | bool is_analog_l(); 51 | bool is_analog_r(); 52 | bool is_analog_select(); 53 | 54 | private: 55 | // ------------------------------------------------------------------------ 56 | // Variables for port 57 | // ------------------------------------------------------------------------ 58 | 59 | // Amount to adjust steering per tick. (0x3 is a good test value) 60 | uint8_t steering_inc; 61 | 62 | // Amount to adjust acceleration per tick. (0x10 is a good test value) 63 | uint8_t acc_inc; 64 | 65 | // Amount to adjust brake per tick. (0x10 is a good test value) 66 | uint8_t brake_inc; 67 | 68 | static const int DELAY_RESET = 40; 69 | int delay1, delay2, delay3; 70 | 71 | // Coin Inputs (Only used by CannonBoard) 72 | bool coin1, coin2; 73 | 74 | // ------------------------------------------------------------------------ 75 | // Variables from original code 76 | // ------------------------------------------------------------------------ 77 | 78 | const static uint8_t STEERING_MIN = 0x48; 79 | const static uint8_t STEERING_MAX = 0xB8; 80 | const static uint8_t STEERING_CENTRE = 0x80; 81 | 82 | // Current steering value 83 | int16_t steering_old; 84 | int16_t steering_change; 85 | 86 | const static uint8_t PEDAL_MIN = 0x30; 87 | const static uint8_t PEDAL_MAX = 0x90; 88 | 89 | // Brake Input 90 | int16_t input_brake; 91 | 92 | void digital_steering(); 93 | void digital_pedals(); 94 | }; 95 | 96 | extern OInputs oinputs; 97 | -------------------------------------------------------------------------------- /src/main/engine/olevelobjs.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Level Object Logic 3 | 4 | This class handles rendering most of the objects that comprise a typical 5 | level. 6 | 7 | - Configures rendering properties (co-ordinates, zoom etc.) 8 | - Object specific logic, including collision checks & start lights etc. 9 | 10 | The original codebase contains a large amount of code duplication, 11 | much of which is duplicated here. 12 | 13 | Copyright Chris White. 14 | See license.txt for more details. 15 | ***************************************************************************/ 16 | 17 | #pragma once 18 | 19 | #include "outrun.hpp" 20 | 21 | class OLevelObjs 22 | { 23 | public: 24 | // Spray Counter (Going Through Water). 25 | uint16_t spray_counter; 26 | 27 | // Wheel Spray Type 28 | // 00 = Water 29 | // 04 = Yellow Stuff 30 | // 08 = Green Stuff 31 | // 0c = Pink stuff 32 | // 10 = Smoke 33 | uint16_t spray_type; 34 | 35 | // Collision With Sprite Has Ocurred 36 | // 37 | // 0 = No Collision 38 | // 1 = Collision (and increments for every additional collision in this crash cycle) 39 | uint8_t collision_sprite; 40 | 41 | // Sprite Collision Counter (Hitting Scenery) 42 | int16_t sprite_collision_counter; 43 | 44 | OLevelObjs(void); 45 | ~OLevelObjs(void); 46 | 47 | void init_startline_sprites(); 48 | void init_timetrial_sprites(); 49 | void init_hiscore_sprites(); 50 | void setup_sprites(uint32_t); 51 | void do_sprite_routine(); 52 | void hide_sprite(oentry*); 53 | 54 | private: 55 | // Default sprite entries for stage 1 initialization 56 | const static uint8_t DEF_SPRITE_ENTRIES = 0x44; 57 | 58 | // Hi-Score Sprite Entries 59 | const static uint8_t HISCORE_SPRITE_ENTRIES = 0x40; 60 | 61 | const static uint8_t COLLISION_RESET = 4; 62 | const static uint16_t SPRAY_RESET = 0xC; 63 | 64 | void init_entries(uint32_t, const uint8_t start_index, const uint8_t); 65 | void setup_sprite(oentry*, uint32_t); 66 | void setup_sprite_routine(oentry*); 67 | void sprite_collision_z1c(oentry*); 68 | void sprite_lights(oentry*); 69 | void sprite_lights_countdown(oentry*); 70 | void sprite_grass(oentry* sprite); 71 | void sprite_water(oentry* sprite); 72 | void sprite_rocks(oentry* sprite); 73 | void sprite_debris(oentry* sprite); 74 | void sprite_minitree(oentry* sprite); 75 | void do_thickness_sprite(oentry* sprite, const uint32_t); 76 | void sprite_clouds(oentry* sprite); 77 | void sprite_normal(oentry*, uint8_t); 78 | void set_spr_zoom_priority(oentry*, uint8_t); 79 | void set_spr_zoom_priority2(oentry*, uint8_t); 80 | void set_spr_zoom_priority_rocks(oentry*, uint8_t); 81 | }; 82 | 83 | extern OLevelObjs olevelobjs; -------------------------------------------------------------------------------- /src/main/engine/ologo.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Attract Mode: Animated OutRun Logo Graphic 3 | 4 | The logo is built from multiple sprite components. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #pragma once 11 | 12 | class OLogo 13 | { 14 | public: 15 | OLogo(); 16 | ~OLogo(); 17 | void enable(int16_t y); 18 | void disable(); 19 | void tick(); 20 | void blit(); 21 | 22 | private: 23 | // Palm Tree Frame Addresses 24 | uint32_t palm_frames[8]; 25 | 26 | // Background Palette Entries 27 | static const uint8_t bg_pal[]; 28 | 29 | uint8_t entry_start; 30 | 31 | // Y Offset To Draw Logo At 32 | int16_t y_off; 33 | 34 | void setup_sprite1(); 35 | void setup_sprite2(); 36 | void setup_sprite3(); 37 | void setup_sprite4(); 38 | void setup_sprite5(); 39 | void setup_sprite6(); 40 | void setup_sprite7(); 41 | 42 | void sprite_logo_bg(); 43 | void sprite_logo_car(); 44 | void sprite_logo_bird1(); 45 | void sprite_logo_bird2(); 46 | void sprite_logo_road(); 47 | void sprite_logo_palm(); 48 | void sprite_logo_text(); 49 | }; 50 | 51 | extern OLogo ologo; -------------------------------------------------------------------------------- /src/main/engine/omap.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Course Map Logic & Rendering. 3 | 4 | This is the full-screen map that is displayed at the end of the game. 5 | 6 | The logo is built from multiple sprite components. 7 | 8 | The course map itself is made up of sprites and pieced together. 9 | It's not a tilemap. 10 | 11 | Copyright Chris White. 12 | See license.txt for more details. 13 | ***************************************************************************/ 14 | 15 | #pragma once 16 | 17 | #include "outrun.hpp" 18 | 19 | class OMap 20 | { 21 | public: 22 | // Load Sprites Needed for Course Map 23 | bool init_sprites; 24 | 25 | OMap(void); 26 | ~OMap(void); 27 | 28 | void init(); 29 | void tick(); 30 | void blit(); 31 | void load_sprites(); 32 | void draw_course_map(); 33 | void position_ferrari(uint8_t index); 34 | 35 | private: 36 | // Total sprite pieces that comprise course map. 3c 37 | const static uint8_t MAP_PIECES = 0x3C; 38 | 39 | uint8_t map_state; 40 | 41 | enum 42 | { 43 | MAP_INIT = 0, 44 | // Do Route [Note map is displayed from this point on] 45 | MAP_ROUTE = 0x4, 46 | // Do Final Segment Of Route [Car still moving] 47 | MAP_ROUTE_FINAL = 0x08, 48 | // Route Concluded 49 | MAP_ROUTE_DONE = 0x0C, 50 | // Init Delay Counter For Map Display 51 | MAP_INIT_DELAY = 0x10, 52 | // Display Map 53 | MAP_DISPLAY = 0x14, 54 | // Clear Course Map 55 | MAP_CLEAR = 0x18, 56 | }; 57 | 58 | // Direction to move on mini-map 59 | 60 | // Bit 0: 0 = Up (Left Route) 61 | // 1 = Down (Right Route) 62 | uint8_t map_route; 63 | 64 | // Minimap Position (Per Segment Basis) 65 | int16_t map_pos; 66 | 67 | // Minimap Position (Final Segment) 68 | int16_t map_pos_final; 69 | 70 | // Map Delay Counter 71 | int16_t map_delay; 72 | 73 | // Stage counter for course map screen. [Increments] 74 | int16_t map_stage1; 75 | 76 | // Stage counter for course map screen. [Decrements] 77 | // Loaded with stage, then counts down as course map logic runs. 78 | int16_t map_stage2; 79 | 80 | // Minicar Movement Enabled (set = enabled) 81 | uint8_t minicar_enable; 82 | 83 | void draw_horiz_end(oentry*); 84 | void draw_vert_bottom(oentry*); 85 | void draw_vert_top(oentry*); 86 | void draw_piece(oentry*, uint32_t); 87 | void do_route_final(); 88 | void end_route(); 89 | void init_map_delay(); 90 | void map_display(); 91 | void move_mini_car(oentry*); ; 92 | }; 93 | 94 | extern OMap omap; -------------------------------------------------------------------------------- /src/main/engine/omusic.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Music Selection Screen. 3 | 4 | This is a combination of a tilemap and overlayed sprites. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #pragma once 11 | 12 | #include "outrun.hpp" 13 | 14 | class RomLoader; 15 | 16 | class OMusic 17 | { 18 | public: 19 | OMusic(void); 20 | ~OMusic(void); 21 | 22 | bool load_widescreen_map(std::string path); 23 | void enable(); 24 | void disable(); 25 | void tick(); 26 | void blit(); 27 | void check_start(); 28 | void play_music(int index = -1); 29 | void cycle_music(); 30 | 31 | private: 32 | // Modified Widescreen version of the Music Select Tilemap 33 | RomLoader* tilemap; 34 | // Additional Widescreen tiles 35 | RomLoader* tile_patch; 36 | 37 | // Next track to play 38 | music_t* next_track; 39 | 40 | // Music Track Selected By Player 41 | uint8_t music_selected; 42 | 43 | // Total tracks to include in music select (> 3 means user has added extra ones) 44 | int total_tracks; 45 | 46 | // Enahcned: Current Cursor Position 47 | int cursor_pos; 48 | 49 | uint16_t entry_start; 50 | 51 | // Used to preview music track 52 | int16_t last_music_selected; 53 | int8_t preview_counter; 54 | 55 | const static short HAND_LEFT = 0, HAND_CENTRE = 1, HAND_RIGHT = 2; 56 | 57 | void setup_sprite1(); 58 | void setup_sprite2(); 59 | void setup_sprite3(); 60 | void setup_sprite4(); 61 | void setup_sprite5(); 62 | void tick_original(oentry*, oentry*, oentry*); 63 | void tick_enhanced(oentry*, oentry*, oentry*); 64 | void set_hand(short, oentry*, oentry*, oentry*); 65 | void blit_music_select(); 66 | }; 67 | 68 | extern OMusic omusic; 69 | 70 | -------------------------------------------------------------------------------- /src/main/engine/ooutputs.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Process Outputs. 3 | 4 | - Only the Deluxe Moving Motor Code is ported for now. 5 | - This is used by the force-feedback haptic system. 6 | 7 | One thing to note is that this code was originally intended to drive 8 | a moving hydraulic cabinet, not to be mapped to a haptic device. 9 | 10 | Therefore, it's not perfect when used in this way, but the results 11 | aren't bad :) 12 | 13 | Copyright Chris White. 14 | See license.txt for more details. 15 | ***************************************************************************/ 16 | 17 | #pragma once 18 | 19 | #include "stdint.hpp" 20 | 21 | struct CoinChute 22 | { 23 | // Coin Chute Counters 24 | uint8_t counter[3]; 25 | // Output bit 26 | uint8_t output_bit; 27 | }; 28 | 29 | class OOutputs 30 | { 31 | public: 32 | const static int MODE_DISABLED = 0; // Disabled 33 | const static int MODE_CABINET = 1; // SmartyPi Interface / Original Cabinet 34 | const static int MODE_FFEEDBACK = 2; // Force Feedback for Wheels 35 | const static int MODE_RUMBLE = 3; // Simple rumble for controllers 36 | 37 | // Hardware Motor Control: 38 | // 0 = Switch off 39 | // 5 = Left 40 | // 8 = Centre 41 | // B = Right 42 | uint8_t hw_motor_control, hw_motor_control_old; 43 | 44 | // Digital Outputs 45 | enum 46 | { 47 | D_EXT_MUTE = 0x01, // bit 0 = External Amplifier Mute Control 48 | D_BRAKE_LAMP = 0x02, // bit 1 = brake lamp 49 | D_START_LAMP = 0x04, // bit 2 = start lamp 50 | D_COIN1_SUCC = 0x08, // bit 3 = Coin successfully inserted - Chute 2 51 | D_COIN2_SUCC = 0x10, // bit 4 = Coin successfully inserted - Chute 1 52 | D_MOTOR = 0x20, // bit 5 = steering wheel central vibration 53 | D_UNUSED = 0x40, // bit 6 = ? 54 | D_SOUND = 0x80, // bit 7 = sound enable 55 | }; 56 | 57 | CoinChute chute1, chute2; 58 | 59 | OOutputs(void); 60 | ~OOutputs(void); 61 | 62 | void init(); 63 | void set_mode(int); 64 | bool diag_motor(int16_t input_motor, uint8_t hw_motor_limit); 65 | bool calibrate_motor(int16_t input_motor, uint8_t hw_motor_limit); 66 | void tick(int16_t input_motor = 0); 67 | void writeDigitalToConsole(); 68 | void set_digital(uint8_t); 69 | void clear_digital(uint8_t); 70 | int is_set(uint8_t); 71 | void coin_chute_out(CoinChute* chute, bool insert); 72 | 73 | private: 74 | int mode; 75 | 76 | uint8_t dig_out, dig_out_old; 77 | 78 | const static uint16_t STATE_INIT = 0; 79 | const static uint16_t STATE_DELAY = 1; 80 | const static uint16_t STATE_LEFT = 2; 81 | const static uint16_t STATE_RIGHT = 3; 82 | const static uint16_t STATE_CENTRE = 4; 83 | const static uint16_t STATE_DONE = 5; 84 | const static uint16_t STATE_EXIT = 6; 85 | 86 | // Calibration Counter 87 | const static int COUNTER_RESET = 300; 88 | 89 | const static uint8_t MOTOR_OFF = 0; 90 | const static uint8_t MOTOR_RIGHT = 0x5; 91 | const static uint8_t MOTOR_CENTRE = 0x8; 92 | const static uint8_t MOTOR_LEFT = 0xB; 93 | 94 | 95 | // These are calculated during startup in the original game. 96 | // Here we just hardcode them, as the motor init code isn't ported. 97 | const static uint8_t CENTRE_POS = 0x80; 98 | const static uint8_t LEFT_LIMIT = 0xC1; 99 | const static uint8_t RIGHT_LIMIT = 0x3C; 100 | 101 | // Motor Limit Values. Calibrated during startup. 102 | int16_t limit_left; 103 | int16_t limit_right; 104 | 105 | // Motor Centre Position. (We Fudge this for Force Feedback wheel mode.) 106 | int16_t motor_centre_pos; 107 | 108 | // Difference between input_motor and input_motor_old 109 | int16_t motor_x_change; 110 | 111 | uint16_t motor_state; 112 | bool motor_enabled; 113 | 114 | // 0x11: Motor Control Value 115 | int8_t motor_control; 116 | // 0x12: Movement (1 = Left, -1 = Right, 0 = None) 117 | int8_t motor_movement; 118 | // 0x14: Is Motor Centered 119 | bool is_centered; 120 | // 0x16: Motor X Change Latch 121 | int16_t motor_change_latch; 122 | // 0x18: Speed 123 | int16_t speed; 124 | // 0x1A: Road Curve 125 | int16_t curve; 126 | // 0x1E: Increment counter to index motor table for off-road/crash 127 | int16_t vibrate_counter; 128 | // 0x20: Last Motor X_Change > 8. No need to adjust further. 129 | bool was_small_change; 130 | // 0x22: Adjusted movement value based on steering 1 131 | int16_t movement_adjust1; 132 | // 0x24: Adjusted movement value based on steering 2 133 | int16_t movement_adjust2; 134 | // 0x26: Adjusted movement value based on steering 3 135 | int16_t movement_adjust3; 136 | 137 | // Counter control for motor tests 138 | int16_t counter; 139 | 140 | // Columns for output 141 | uint16_t col1, col2; 142 | 143 | void diag_left(int16_t input_motor, uint8_t hw_motor_limit); 144 | void diag_right(int16_t input_motor, uint8_t hw_motor_limit); 145 | void diag_centre(int16_t input_motor, uint8_t hw_motor_limit); 146 | void diag_done(); 147 | 148 | void calibrate_left(int16_t input_motor, uint8_t hw_motor_limit); 149 | void calibrate_right(int16_t input_motor, uint8_t hw_motor_limit); 150 | void calibrate_centre(int16_t input_motor, uint8_t hw_motor_limit); 151 | void calibrate_done(); 152 | 153 | void do_motors(const int MODE, int16_t input_motor); 154 | void car_moving(const int MODE); 155 | void car_stationary(); 156 | void adjust_motor(); 157 | void do_motor_crash(); 158 | void do_motor_offroad(); 159 | void set_value(const uint8_t*, uint8_t); 160 | void done(); 161 | void motor_output(uint8_t cmd); 162 | 163 | void do_vibrate_upright(); 164 | void do_vibrate_mini(); 165 | }; -------------------------------------------------------------------------------- /src/main/engine/opalette.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Palette Control 3 | 4 | Sky, Ground & Road Palettes. Including palette cycling code. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #pragma once 11 | 12 | #include "outrun.hpp" 13 | 14 | class OPalette 15 | { 16 | public: 17 | 18 | // Palette Manipulation Control 19 | // 20 | // 0 = Palette manipulation deactivated 21 | // 22 | // Bit 0 = Set to enable/disable palette manipulation 23 | // Bit 1 = Set when fade difference calculated, and fade in progress 24 | // Bit 2 = Set when memory areas have been setup for fade 25 | uint8_t pal_manip_ctrl; 26 | 27 | OPalette(void); 28 | ~OPalette(void); 29 | void init(); 30 | void setup_sky_palette(); 31 | void setup_sky_change(); 32 | void setup_sky_cycle(); 33 | void cycle_sky_palette(); 34 | void fade_palette(); 35 | void setup_ground_color(); 36 | void setup_road_centre(); 37 | void setup_road_stripes(); 38 | void setup_road_side(); 39 | void setup_road_colour(); 40 | 41 | private: 42 | // Sky Palette Manipulation Data (0x20 longs per palette, 0x1F separate palettes, then times 2 as for current/next sky on level transition) 43 | uint32_t pal_manip[(0x20 * 0x1F) * 2]; 44 | 45 | // Palette Manipulation Area. 46 | // 47 | // Used for fades between colours when switching stages. 48 | // 49 | // Format: 50 | // 51 | // +00 [word] Current Palette Entry 52 | // +02 [word] Repacked RGB bits (from 06, 08, 0A) in final format 53 | // +04 [word] Next Palette Entry 54 | // +06 [word] Current BLUE bits [i.e current fade, this is constantly adjusted] 55 | // +08 [word] Current GREEN bits [i.e current fade, this is constantly adjusted] 56 | // +0A [word] Current RED bits [i.e current fade, this is constantly adjusted] 57 | // +0C [word] BLUE Difference between palette entries 58 | // +0E [word] GREEN Difference between palette entries 59 | // +10 [word] RED Difference between palette entries 60 | //(9 words per entry, 0x18 entries) 61 | uint16_t pal_fade[9 * 0x18]; 62 | 63 | // Has new sky palette initalized (for level transitions) 64 | 65 | // Bit 0 = Set to denote that new sky palette info has been setup/copied to RAM. Cleared when bit 1 set. 66 | // Bit 1 = Set to indicate sky palette data should be cycled 67 | uint8_t sky_palette_init; 68 | 69 | // For palette fade manipulations 70 | uint8_t cycle_counter; 71 | 72 | // Fade counter - how many steps to fade the palette by 73 | int16_t fade_counter; 74 | 75 | // Sky Counter: Used to cycle sky palette on level transition 76 | uint16_t sky_palette_index; 77 | 78 | // Fade byte to setup (0 - 0x3F) 79 | uint8_t sky_fade_offset; 80 | 81 | void setup_fade_data(); 82 | void repack_rgb(const uint32_t); 83 | void write_fade_to_palram(); 84 | void write_current_pal_to_ram(); 85 | void write_next_pal_to_ram(); 86 | void fade_sky_pal_entry(const uint16_t, const uint16_t, uint32_t); 87 | }; 88 | 89 | extern OPalette opalette; -------------------------------------------------------------------------------- /src/main/engine/osmoke.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Smoke & Spray Control. 3 | 4 | Animate the smoke and spray below the Ferrari's wheels. 5 | 6 | Copyright Chris White. 7 | See license.txt for more details. 8 | ***************************************************************************/ 9 | 10 | #pragma once 11 | 12 | #include "outrun.hpp" 13 | 14 | class OSmoke 15 | { 16 | public: 17 | // Load smoke sprites for next level? 18 | int8_t load_smoke_data; 19 | 20 | OSmoke(void); 21 | ~OSmoke(void); 22 | void init(); 23 | void setup_smoke_sprite(bool); 24 | void draw_ferrari_smoke(oentry*); 25 | void draw(oentry*); 26 | 27 | private: 28 | // Ferrari wheel smoke type on road 29 | uint16_t smoke_type_onroad; 30 | 31 | // Ferrari wheel smoke type off road 32 | uint16_t smoke_type_offroad; 33 | 34 | // Ferrari wheel smoke type after car collision 35 | uint16_t smoke_type_slip; 36 | 37 | void tick_smoke_anim(oentry*, int8_t, uint32_t); 38 | }; 39 | 40 | extern OSmoke osmoke; -------------------------------------------------------------------------------- /src/main/engine/osprite.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Hardware Sprites. 3 | 4 | This class stores sprites in the converted format expected by the 5 | OutRun graphics hardware. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #include "engine/osprite.hpp" 12 | 13 | // Out Run/X-Board-style sprites 14 | // 15 | // Offs Bits Usage 16 | // +0 e------- -------- Signify end of sprite list 17 | // +0 -h-h---- -------- Hide this sprite if either bit is set 18 | // +0 ----bbb- -------- Sprite bank 19 | // +0 -------t tttttttt Top scanline of sprite + 256 20 | // +2 oooooooo oooooooo Offset within selected sprite bank 21 | // +4 ppppppp- -------- Signed 7-bit pitch value between scanlines 22 | // +4 -------x xxxxxxxx X position of sprite (position $BE is screen position 0) 23 | // +6 -s------ -------- Enable shadows 24 | // +6 --pp---- -------- Sprite priority, relative to tilemaps 25 | // +6 ------vv vvvvvvvv Vertical zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size) 26 | // +8 y------- -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen 27 | // +8 -f------ -------- Horizontal flip: read the data backwards if set 28 | // +8 --x----- -------- Render from left-to-right (1) or right-to-left (0) on screen 29 | // +8 ------hh hhhhhhhh Horizontal zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size) 30 | // +E dddddddd dddddddd Scratch space for current address 31 | // 32 | // Out Run only: 33 | // +A hhhhhhhh -------- Height in scanlines - 1 34 | // +A -------- -ccccccc Sprite color palette 35 | 36 | osprite::osprite(void) 37 | { 38 | 39 | } 40 | 41 | osprite::~osprite(void) 42 | { 43 | } 44 | 45 | void osprite::init() 46 | { 47 | data[0] = 0; 48 | data[1] = 0; 49 | data[2] = 0; 50 | data[3] = 0; 51 | data[4] = 0; 52 | data[5] = 0; 53 | data[6] = 0; 54 | scratch = 0; 55 | } 56 | 57 | // X is now stored separately (not in the original data structure) 58 | // This is to support wide-screen mode 59 | uint16_t osprite::get_x() 60 | { 61 | return data[0x6]; 62 | } 63 | 64 | uint16_t osprite::get_y() 65 | { 66 | return data[0x0]; // returning y uses whole value 67 | } 68 | 69 | void osprite::set_x(uint16_t x) 70 | { 71 | data[0x6] = x; 72 | } 73 | 74 | void osprite::set_pitch(uint8_t p) 75 | { 76 | data[0x02] = (data[0x2] & 0x1FF) | ((p & 0xFE) << 8); 77 | } 78 | 79 | void osprite::inc_x(uint16_t v) 80 | { 81 | data[0x6] += v; 82 | } 83 | 84 | void osprite::set_y(uint16_t y) 85 | { 86 | data[0x0] = y; // setting y wipes entire value 87 | } 88 | 89 | void osprite::set_vzoom(uint16_t z) 90 | { 91 | data[0x03] = z; 92 | } 93 | 94 | void osprite::set_hzoom(uint16_t z) 95 | { 96 | data[0x4] = z; 97 | } 98 | 99 | void osprite::set_priority(uint8_t p) 100 | { 101 | data[0x03] |= (p << 8); 102 | } 103 | 104 | void osprite::set_offset(uint16_t o) 105 | { 106 | data[0x1] = o; 107 | } 108 | 109 | void osprite::inc_offset(uint16_t o) 110 | { 111 | data[0x1] += o; 112 | } 113 | 114 | void osprite::set_render(uint8_t bits) 115 | { 116 | data[0x4] |= ((bits & 0xE0) << 8); 117 | } 118 | 119 | void osprite::set_pal(uint8_t pal) 120 | { 121 | data[0x5] = (data[0x5] & 0xFF00) + pal; 122 | } 123 | 124 | void osprite::set_height(uint8_t h) 125 | { 126 | data[0x5] = (data[0x5] & 0xFF) + (h << 8); 127 | } 128 | 129 | void osprite::sub_height(uint8_t h) 130 | { 131 | uint8_t height = ((data[0x05] >> 8) - h) & 0xFF; 132 | data[0x5] = (data[0x5] & 0xFF) + (height << 8); 133 | } 134 | 135 | void osprite::set_bank(uint8_t bank) 136 | { 137 | data[0x0] |= (bank << 8); 138 | } 139 | 140 | void osprite::hide(void) 141 | { 142 | data[0x0] |= 0x4000; 143 | data[0x0] &= ~0x8000; // denote sprite list not ended 144 | } -------------------------------------------------------------------------------- /src/main/engine/osprite.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Hardware Sprites. 3 | 4 | This class stores sprites in the converted format expected by the 5 | OutRun graphics hardware. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #include "stdint.hpp" 12 | 13 | class osprite 14 | { 15 | public: 16 | uint16_t data[0x7]; 17 | uint32_t scratch; 18 | 19 | osprite(void); 20 | ~osprite(void); 21 | void init(); 22 | 23 | uint16_t get_x(); 24 | uint16_t get_y(); 25 | void set_x(uint16_t); 26 | void inc_x(uint16_t); 27 | void set_y(uint16_t); 28 | void set_pitch(uint8_t); 29 | void set_vzoom(uint16_t); 30 | void set_hzoom(uint16_t); 31 | void set_priority(uint8_t); 32 | void set_offset(uint16_t o); 33 | void inc_offset(uint16_t o); 34 | void set_render(uint8_t b); 35 | void set_pal(uint8_t); 36 | void set_height(uint8_t); 37 | void sub_height(uint8_t); 38 | void set_bank(uint8_t); 39 | void hide(); 40 | 41 | private: 42 | 43 | }; -------------------------------------------------------------------------------- /src/main/engine/osprites.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sprite Handling Routines. 3 | 4 | - Initializing Sprites from level data. 5 | - Mapping palettes to sprites. 6 | - Ordering sprites by priority. 7 | - Adding shadows to sprites where appropriate. 8 | - Clipping sprites based on priority in relation to road hardware. 9 | - Conversion from internal format to output format required by hardware. 10 | 11 | Copyright Chris White. 12 | See license.txt for more details. 13 | ***************************************************************************/ 14 | 15 | #pragma once 16 | 17 | #include "oentry.hpp" 18 | #include "osprite.hpp" 19 | #include "outrun.hpp" 20 | #include "engine/data/sprite_pals.hpp" 21 | 22 | class OSprites 23 | { 24 | public: 25 | 26 | enum 27 | { 28 | HFLIP = 0x1, // Bit 0: Horizontally flip sprite 29 | WIDE_ROAD = 0x4, // Bit 2: Set if road_width >= 0x118, 30 | TRAFFIC_SPRITE = 0x8, // Bit 3: Traffic Sprite - Set for traffic 31 | SHADOW = 0x10, // Bit 4: Sprite has shadows 32 | DRAW_SPRITE = 0x20, // Bit 5: Toggle sprite visibility 33 | TRAFFIC_RHS = 0x40, // Bit 6: Traffic Sprites - Set to spawn on RHS 34 | ENABLE = 0x80, // Bit 7: Toggle sprite visibility 35 | }; 36 | 37 | // Note, the original game has 0x4F entries. 38 | // But we can set it to a higher value, to fix the broken arches on Gateway. 39 | // this is a limitation in the original game. 40 | // We just leave the larger array in place when changing the settings. 41 | 42 | // Total sprite entries in Jump Table (start at offset #3) 43 | const static uint8_t SPRITE_ENTRIES = 0x62; 44 | 45 | // This is initalized based on the config 46 | uint8_t no_sprites; 47 | 48 | // Total number of object entries, including SPRITE_ENTRIES, FERRARI, PASSENGERS, TRAFFIC etc. 49 | const static uint8_t JUMP_ENTRIES_TOTAL = SPRITE_ENTRIES + 24; 50 | 51 | const static uint8_t SPRITE_FERRARI = SPRITE_ENTRIES + 1; 52 | const static uint8_t SPRITE_PASS1 = SPRITE_ENTRIES + 2; // Passengers 53 | const static uint8_t SPRITE_PASS2 = SPRITE_ENTRIES + 3; 54 | const static uint8_t SPRITE_SHADOW = SPRITE_ENTRIES + 4; // Ferrari Shadow 55 | const static uint8_t SPRITE_SMOKE1 = SPRITE_ENTRIES + 5; // Ferrari Tyre Smoke/Effects 56 | const static uint8_t SPRITE_SMOKE2 = SPRITE_ENTRIES + 6; 57 | const static uint8_t SPRITE_TRAFF1 = SPRITE_ENTRIES + 7; // 8 Traffic Entries 58 | const static uint8_t SPRITE_TRAFF2 = SPRITE_ENTRIES + 8; 59 | const static uint8_t SPRITE_TRAFF3 = SPRITE_ENTRIES + 9; 60 | const static uint8_t SPRITE_TRAFF4 = SPRITE_ENTRIES + 10; 61 | const static uint8_t SPRITE_TRAFF5 = SPRITE_ENTRIES + 11; 62 | const static uint8_t SPRITE_TRAFF6 = SPRITE_ENTRIES + 12; 63 | const static uint8_t SPRITE_TRAFF7 = SPRITE_ENTRIES + 13; 64 | const static uint8_t SPRITE_TRAFF8 = SPRITE_ENTRIES + 14; 65 | 66 | const static uint8_t SPRITE_CRASH = SPRITE_ENTRIES + 15; 67 | const static uint8_t SPRITE_CRASH_SHADOW = SPRITE_ENTRIES + 16; 68 | const static uint8_t SPRITE_CRASH_PASS1 = SPRITE_ENTRIES + 17; 69 | const static uint8_t SPRITE_CRASH_PASS1_S = SPRITE_ENTRIES + 18; 70 | const static uint8_t SPRITE_CRASH_PASS2 = SPRITE_ENTRIES + 19; 71 | const static uint8_t SPRITE_CRASH_PASS2_S = SPRITE_ENTRIES + 20; 72 | 73 | const static uint8_t SPRITE_FLAG = SPRITE_ENTRIES + 21; // Flag Man 74 | 75 | // Jump Table Sprite Entries 76 | oentry jump_table[JUMP_ENTRIES_TOTAL]; 77 | 78 | // Converted sprite entries in RAM for hardware. 79 | osprite sprite_entries[JUMP_ENTRIES_TOTAL]; 80 | 81 | // ------------------------------------------------------------------------- 82 | // Jump Table 2 Entries For Sprite Control 83 | // ------------------------------------------------------------------------- 84 | 85 | // +22 [Word] Road Position For Next Segment Of Sprites 86 | uint16_t seg_pos; 87 | 88 | // +24 [Byte] Number Of Sprites In Segment 89 | uint8_t seg_total_sprites; 90 | 91 | // +26 [Word] Sprite Frequency Bitmask 92 | uint16_t seg_sprite_freq; 93 | 94 | // +28 [Word] Sprite Info Offset - Start Value. Loaded Into 2A. 95 | int16_t seg_spr_offset2; 96 | 97 | // +2A [Word] Sprite Info Offset 98 | int16_t seg_spr_offset1; 99 | 100 | // +2C [Long] Sprite Info Base - Lookup for Sprite X World, Sprite Y World, Sprite Type Table Info [8 byte boundary blocks in ROM] 101 | uint32_t seg_spr_addr; 102 | 103 | // ------------------------------------------------------------------------- 104 | 105 | // Speed at which sprites should scroll. Depends on granular position difference. 106 | uint16_t sprite_scroll_speed; 107 | 108 | // Shadow multiplication value (signed). Offsets the shadow from the sprite. 109 | // Adjusted by the tilemap horizontal scroll, so shadows change depending on how much we've scrolled left and right 110 | int16_t shadow_offset; 111 | 112 | // Number of sprites to draw for sprite drawing routine (sum of spr_cnt_main and spr_cnt_shadow). 113 | uint16_t sprite_count; 114 | 115 | // Number of sprites to draw 116 | uint16_t spr_cnt_main; 117 | 118 | // Number of shadows to draw 119 | uint16_t spr_cnt_shadow; 120 | 121 | OSprites(void); 122 | ~OSprites(void); 123 | void init(); 124 | void disable_sprites(); 125 | void tick(); 126 | void update_sprites(); 127 | 128 | void clear_palette_data(); 129 | void copy_palette_data(); 130 | void map_palette(oentry* spr); 131 | void sprite_copy(); 132 | void blit_sprites(); 133 | 134 | void do_spr_order_shadows(oentry*); 135 | void do_sprite(oentry*); 136 | void set_sprite_xy(oentry*, osprite*, uint16_t, uint16_t); 137 | void set_hrender(oentry*, osprite*, uint16_t, uint16_t); 138 | 139 | void move_sprite(oentry*, uint8_t); 140 | 141 | private: 142 | 143 | // Start of Sprite RAM 144 | static const uint32_t SPRITE_RAM = 0x130000; 145 | 146 | // Palette Ram: Sprite Entries Start Here 147 | static const uint32_t PAL_SPRITES = 0x121000; 148 | 149 | // Denote whether to swap sprite ram 150 | bool do_sprite_swap; 151 | 152 | // Store the next available sprite colour palette (0 - 7F) 153 | uint8_t spr_col_pal; 154 | 155 | // Stores number of palette entries to copy from rom to palram 156 | int16_t pal_copy_count; 157 | 158 | // Palette Addresses. Used in conjunction with palette lookup table. 159 | // Originally stored between 0x61602 - 0x617FF in RAM 160 | // 161 | // Format: 162 | // Word 1: ROM Source Offset 163 | // Word 2: Palette RAM Destination Offset 164 | // 165 | // Word 3: ROM Source Offset 166 | // Word 4: Palette RAM Destination Offset 167 | // 168 | //etc. 169 | const static int PAL_ENTRIES = 0x100; // hardware palette entries (before extra CannonBall palettes) 170 | uint16_t pal_addresses[PAL_ENTRIES]; // todo: rename to pal_mapping 171 | 172 | // Palette Lookup Table (was 0x100, but extended to account for extra palettes in CannonBall) 173 | uint8_t pal_lookup[PAL_LOOKUP_LENGTH]; 174 | 175 | // Converted sprite entries in RAM for hardware. 176 | uint8_t sprite_order[0x2000]; 177 | uint8_t sprite_order2[0x2000]; 178 | 179 | void sprite_control(); 180 | void hide_hwsprite(oentry*, osprite*); 181 | void finalise_sprites(); 182 | }; 183 | 184 | extern OSprites osprites; 185 | -------------------------------------------------------------------------------- /src/main/engine/ostats.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | In-Game Statistics. 3 | - Stage Timers 4 | - Route Info 5 | - Speed to Score Conversion 6 | - Bonus Time Increment 7 | 8 | Copyright Chris White. 9 | See license.txt for more details. 10 | ***************************************************************************/ 11 | 12 | #pragma once 13 | 14 | #include "outrun.hpp" 15 | 16 | class OStats 17 | { 18 | public: 19 | // Current stage 20 | // 0 = Stage 1 21 | // 1 = Stage 2 22 | // 2 = Stage 3 23 | // 3 = Stage 4 24 | // 4 = Stage 5 25 | // -1 = Bonus Points Section 26 | // 27 | // A good way to quickly see the end sequence is to set this to '4' and play 28 | // through the first level. 29 | int8_t cur_stage; 30 | 31 | // Score (Outputs Hex values directly) 32 | uint32_t score; 33 | 34 | // Store info on the route taken by the player 35 | // 36 | // +10 For each stage. 37 | // 38 | // Then increment by the following when Left Hand route selected at each stage. 39 | // 40 | // Stage 1 = +8 (1 << 3 - 0) 41 | // Stage 2 = +4 (1 << 3 - 1) 42 | // Stage 3 = +2 (1 << 3 - 2) 43 | // Stage 4 = +1 (1 << 3 - 3) 44 | // Stage 5 = Road doesn't split on this stage 45 | // 46 | // So if we reach Stage 2 (left route) we do 10 + 8 = 18 47 | uint16_t route_info; 48 | 49 | // Stores route_info for each stage. Used by course map screen 50 | // First entry stores upcoming stage number 51 | uint16_t routes[0x8]; 52 | 53 | // Frame Counter Reset/Load Value. 54 | // Load frame counter with this value when the counter has decremented and expired. 55 | // Note: Values stored and used in hex. 56 | int16_t frame_counter; 57 | const static int16_t frame_reset = 30; 58 | 59 | // Time Counter (Frames). Counts downwards from 30. 60 | // Used in correspondence with 0x60860. 61 | // Note: Values stored and used in hex. 62 | int16_t time_counter; 63 | 64 | // Extend Play Timer. 65 | // 66 | // Loaded to 0x80 when EXTEND PLAY! banner should flash 67 | int16_t extend_play_timer; 68 | 69 | // Time data array 70 | static const uint8_t TIME[]; 71 | 72 | // Counters that increment with each game tick. 73 | // Each stage has an independent counter (increased to 15 from 5 to support continuous mode) 74 | int16_t stage_counters[15]; 75 | 76 | // Set when game completed 77 | bool game_completed; 78 | 79 | const uint8_t* lap_ms; 80 | 81 | // Number of credits inserted 82 | uint8_t credits; 83 | 84 | // Each stage has an entry for minutes, seconds and MS. (Extended to 15 from 5 to support continuous mode) 85 | uint8_t stage_times[15][3]; 86 | 87 | OStats(void); 88 | ~OStats(void); 89 | 90 | void init(bool); 91 | 92 | void clear_stage_times(); 93 | void clear_route_info(); 94 | 95 | void do_mini_map(); 96 | void do_timers(); 97 | void convert_speed_score(uint16_t); 98 | void update_score(uint32_t); 99 | void init_next_level(); 100 | 101 | private: 102 | // Converted Stage Millisecond Value 103 | uint8_t ms_value; 104 | 105 | void inc_lap_timer(); 106 | }; 107 | 108 | extern OStats ostats; 109 | -------------------------------------------------------------------------------- /src/main/engine/otiles.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Tilemap Handling Code. 3 | 4 | Logic for the foreground and background tilemap layers. 5 | 6 | - Read and render tilemaps 7 | - H-Scroll & V-Scroll 8 | - Palette Initialization 9 | 10 | Copyright Chris White. 11 | See license.txt for more details. 12 | ***************************************************************************/ 13 | 14 | #pragma once 15 | 16 | #include "outrun.hpp" 17 | 18 | // Forward definition of video for cyclic dependency 19 | class video; 20 | 21 | class OTiles 22 | { 23 | public: 24 | // + 0x21: Tilemap Control 25 | // 0 = Clear Tile Table 1 & Init Default Tilemap (Stage 1) 26 | // 1 = Scroll Tilemap 27 | // 2 = Init Tilemap 28 | // 3 = New Tilemap Initialized - Scroll both tilemaps during tilesplit 29 | uint8_t tilemap_ctrl; 30 | enum { TILEMAP_CLEAR, TILEMAP_SCROLL, TILEMAP_INIT, TILEMAP_SPLIT }; 31 | 32 | OTiles(); 33 | ~OTiles(); 34 | 35 | void init(); 36 | void set_vertical_swap(); 37 | void setup_palette_tilemap(); 38 | void setup_palette_widescreen(); 39 | void setup_palette_hud(); 40 | void reset_tiles_pal(); 41 | void update_tilemaps(int8_t); 42 | void init_tilemap_palette(uint16_t); 43 | void fill_tilemap_color(uint16_t); 44 | void write_tilemap_hw(); 45 | void set_scroll(int16_t h_scroll = 0, int16_t v_scroll = 0); 46 | 47 | private: 48 | // Page to use for tilemap. Alternates between 0 and 1 dependent on stage number 49 | // to handle switch between tilemaps at stage end. 50 | int8_t page; 51 | 52 | // Enhancement: Used for continuous mode 53 | int16_t vswap_state; 54 | enum {VSWAP_OFF, VSWAP_SCROLL_OFF, VSWAP_SCROLL_ON}; 55 | 56 | int16_t vswap_off; 57 | 58 | // ----------------------------------------------------------------------- 59 | // TILEMAP VARIABLES 60 | // ----------------------------------------------------------------------- 61 | 62 | // Scroll values to write to foreground & background tilemaps 63 | int16_t fg_h_scroll; 64 | int16_t bg_h_scroll; 65 | int16_t fg_v_scroll; 66 | int16_t bg_v_scroll; 67 | 68 | uint16_t fg_psel; 69 | uint16_t bg_psel; 70 | 71 | // + 0xC Current master tilemap scroll values 72 | int16_t tilemap_v_scr; 73 | int32_t tilemap_h_scr; 74 | 75 | // BG & FG Tilemap Height in Tiles 76 | uint16_t fg_v_tiles; 77 | uint16_t bg_v_tiles; 78 | 79 | // + 0x16 Tilemap v-scroll offset. Generally static. 80 | int16_t tilemap_v_off; 81 | 82 | // FG & BG Tilemap ROM Address [long] 83 | uint32_t fg_addr; 84 | uint32_t bg_addr; 85 | 86 | // + 0x20: Toggle between loading palette and loading tiles 87 | uint8_t tilemap_setup; 88 | enum { SETUP_TILES, SETUP_PAL }; 89 | 90 | // + 0x22: Clear Old Name Tables 91 | bool clear_name_tables; 92 | 93 | // + 0x23: Set when road is splitting (used by UpdateFGPage and UpdateBGPage) 94 | bool page_split; 95 | 96 | // + 0x24: H-Scroll Lookup Table 97 | uint16_t h_scroll_lookup; 98 | 99 | // ----------------------------------------------------------------------- 100 | 101 | void clear_tile_info(); 102 | void init_tilemap(int16_t stage_id = 0); 103 | void init_tilemap_props(uint16_t); 104 | void scroll_tilemaps(); 105 | void init_next_tilemap(); 106 | void copy_to_palram(const uint8_t, uint32_t, uint32_t); 107 | void split_tilemaps(); 108 | 109 | void loop_to_stage1(); 110 | void clear_old_name_table(); 111 | void h_scroll_tilemaps(); 112 | void v_scroll_tilemaps(); 113 | void copy_fg_tiles(uint32_t); 114 | void copy_bg_tiles(uint32_t); 115 | void update_fg_page(); 116 | void update_bg_page(); 117 | void update_fg_page_split(); 118 | void update_bg_page_split(); 119 | }; 120 | 121 | extern OTiles otiles; 122 | 123 | -------------------------------------------------------------------------------- /src/main/engine/otraffic.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Traffic Routines. 3 | 4 | - Traffic spawning. 5 | - Traffic logic, lane changing & movement. 6 | - Collisions. 7 | - Traffic panning and volume control to pass to sound program. 8 | 9 | Copyright Chris White. 10 | See license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include "outrun.hpp" 16 | 17 | class OTraffic 18 | { 19 | public: 20 | // AI: Set to denote enemy traffic is close to car. 21 | uint8_t ai_traffic; 22 | 23 | // Set to denote we should go to LHS of road on bonus 24 | uint8_t bonus_lhs; 25 | 26 | // Logic for traffic based on road split 27 | int8_t traffic_split; 28 | 29 | // Denotes Collision With Other Traffic 30 | // 31 | // 0 = No Collision 32 | // 1 = Init Collision Sequence 33 | // 2 = Collision Sequence In Progress 34 | uint16_t collision_traffic; 35 | 36 | uint16_t collision_mask; 37 | 38 | OTraffic(void); 39 | ~OTraffic(void); 40 | void init(); 41 | void init_stage1_traffic(); 42 | void tick(); 43 | void disable_traffic(); 44 | void set_max_traffic(); 45 | void traffic_logic(); 46 | void traffic_sound(); 47 | 48 | private: 49 | // ------------------------------------------------------------------------- 50 | // Function Holders 51 | // ------------------------------------------------------------------------- 52 | enum 53 | { 54 | TRAFFIC_INIT = 0x10, // Initalize Traffic Object 55 | TRAFFIC_ENTRY = 0x11, // First 0x80 Positions Of Road 56 | TRAFFIC_TICK = 0x12 // Tick Normally 57 | }; 58 | 59 | // Onscreen traffic objects 60 | oentry* traffic_adr[9]; 61 | 62 | // Maximum number of on-screen enemies 63 | uint8_t max_traffic; 64 | 65 | // Total speed of all traffic combined 66 | int16_t traffic_speed_total; 67 | 68 | // Average speed of traffic. Used to control wheel frames of traffic sprites. 69 | int16_t traffic_speed_avg; 70 | 71 | // Traffic Palette Cycle. This alternates between 0 and 1. 72 | // Essentially changes the palette, so that the wheels on the traffic appear to be in motion. 73 | uint8_t traffic_pal_cycle; 74 | 75 | // Number of traffic spawned 76 | int16_t traffic_count; 77 | 78 | // +1E [Word] Spawn Tick Counter. Used as a somewhat unrandom way of spawning cars. 79 | int16_t spawn_counter; 80 | 81 | // +20 [Word] Left Hand / Right Hand Spawn Control. Controls where next car should spawn. 82 | int16_t spawn_location; 83 | 84 | // +22 [Word] Wheel Animation Reset Value 85 | int16_t wheel_reset; 86 | 87 | // +24 [Word] Wheel Animation Counter 88 | int16_t wheel_counter; 89 | 90 | void spawn_car(oentry* sprite); 91 | void spawn_traffic(); 92 | void tick_spawned_sprite(oentry* sprite); 93 | void move_spawned_sprite(oentry* sprite); 94 | void update_props(oentry* sprite); 95 | void set_zoom_lookup(oentry* sprite); 96 | void calculate_avg_speed(uint16_t); 97 | void check_collision(oentry* sprite); 98 | }; 99 | 100 | extern OTraffic otraffic; -------------------------------------------------------------------------------- /src/main/engine/outils.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | OutRun Utility Functions & Assembler Helper Functions. 3 | 4 | Common OutRun library functions. 5 | Helper functions used to facilitate 68K to C++ porting process. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | #include 11 | #include "engine/outils.hpp" 12 | #include "engine/ostats.hpp" 13 | 14 | outils::outils(void) 15 | { 16 | 17 | } 18 | 19 | outils::~outils(void) 20 | { 21 | } 22 | 23 | // Generate long random 24 | // 25 | // Source Address: 0x6C8E 26 | // Input: None 27 | // Output: Long Random 28 | 29 | // Seed for random number generator 30 | static uint32_t rnd_seed = 0; 31 | 32 | void outils::reset_random_seed() 33 | { 34 | rnd_seed = 0; 35 | } 36 | 37 | uint32_t outils::random() 38 | { 39 | // New seed value 40 | uint32_t seed = rnd_seed; 41 | 42 | if (seed == 0) 43 | seed = config.engine.randomgen ? 0x2A6D365A : rand(); 44 | 45 | // Random Value To Return 46 | uint32_t rnd = seed; 47 | 48 | seed <<= 2; 49 | seed += rnd; 50 | seed <<= 3; 51 | seed += rnd; 52 | 53 | move16(seed, rnd); 54 | swap32(seed); 55 | add16(seed, rnd); 56 | move16(rnd, seed); 57 | swap32(seed); 58 | 59 | rnd_seed = seed; // Set new seed 60 | 61 | return rnd; 62 | } 63 | 64 | // Square Root Functions 65 | // 66 | // Note: This isn't a direct port of the OutRun routine 67 | // 68 | // To Do: Test this outputs identical results 69 | 70 | int32_t outils::next(const int32_t n, const int32_t i) 71 | { 72 | return (n + i/n) >> 1; 73 | } 74 | 75 | int32_t outils::isqrt(const int32_t number) 76 | { 77 | if (number == 0) 78 | return 0; 79 | 80 | int n = 1; 81 | int n1 = next(n, number); 82 | 83 | while(abs(n1 - n) > 1) 84 | { 85 | n = n1; 86 | n1 = next(n, number); 87 | } 88 | while((n1*n1) > number) 89 | { 90 | n1 -= 1; 91 | } 92 | return n1; 93 | } 94 | 95 | int32_t outils::abs(int32_t n) 96 | { 97 | return n >= 0 ? n : -n; 98 | } 99 | 100 | // Helper routine to BCD add two longs 101 | uint32_t outils::bcd_add(uint32_t src, uint32_t dst) 102 | { 103 | uint8_t carry = 0; 104 | uint32_t final = 0; 105 | 106 | for (uint8_t i = 0; i < 4; i++) 107 | { 108 | int32_t res = (src & 0xF) + (dst & 0xF) + carry; 109 | 110 | if (res > 9) 111 | res += 6; 112 | 113 | res += (src & 0xF0) + (dst & 0xF0); 114 | 115 | if (res > 0x99) 116 | { 117 | res -= 0xA0; 118 | carry = 1; 119 | } 120 | else 121 | { 122 | carry = 0; 123 | } 124 | 125 | src >>= 8; 126 | dst >>= 8; 127 | 128 | final |= ((res & 0xFF) << (i * 8)); 129 | } 130 | 131 | return final; 132 | } 133 | 134 | // Helper routine to BCD subtract two longs 135 | uint32_t outils::bcd_sub(uint32_t src, uint32_t dst) 136 | { 137 | uint8_t carry = 0; 138 | uint32_t final = 0; 139 | 140 | for (uint8_t i = 0; i < 4; i++) 141 | { 142 | int32_t res = (dst & 0xF) - (src & 0xF) - carry; 143 | 144 | if ((res & 0xFF) > 0x9) 145 | res -= 6; 146 | 147 | res += (dst & 0xF0) - (src&0xF0); 148 | 149 | if ((res & 0xFFFF) > 0x99) 150 | { 151 | res += 0xA0; 152 | carry = 1; 153 | } 154 | else 155 | { 156 | carry = 0; 157 | } 158 | 159 | src >>= 8; 160 | dst >>= 8; 161 | 162 | final |= ((res & 0xFF) << (i * 8)); 163 | } 164 | 165 | return final; 166 | } 167 | 168 | // Convert incremented internal counter to actual laptime 169 | void outils::convert_counter_to_time(uint16_t counter, uint8_t* converted) 170 | { 171 | const uint16_t MINUTE = 3600; 172 | 173 | int32_t src_time = counter; // laptime copy [d0] 174 | int16_t minutes = -1; // Store number of minutes 175 | 176 | // Calculate Minutes 177 | do 178 | { 179 | src_time -= MINUTE; 180 | minutes++; 181 | } 182 | while (src_time >= 0); 183 | 184 | src_time += MINUTE; 185 | 186 | // Store Millisecond Lookup 187 | uint8_t ms_lookup = src_time & 0x3F; 188 | 189 | // Calculate Seconds 190 | uint8_t seconds = src_time >> 6; // Store Seconds 191 | 192 | uint8_t s1 = seconds & 0xF; // First digit [d1] 193 | uint8_t s2 = seconds >> 4; // Second digit [d2] 194 | 195 | if (s1 > 9) 196 | seconds += 6; 197 | 198 | s2 = outils::bcd_add(s2, s2); 199 | int16_t d3 = s2; 200 | s2 = outils::bcd_add(s2, s2); 201 | s2 = outils::bcd_add(s2, d3); 202 | seconds = outils::bcd_add(s2, seconds); 203 | 204 | converted[0] = (uint8_t) minutes; 205 | converted[1] = seconds; 206 | converted[2] = ostats.lap_ms[ms_lookup]; 207 | } 208 | 209 | // Convert 16 Bit Decimal Value To Hex. 210 | // 211 | // This is used to convert the speed. 212 | // So an output of 0x180 would be 180 kp/h directly. 213 | // 214 | // Source: 0x7126 215 | uint16_t outils::convert16_dechex(const uint16_t value) 216 | { 217 | int16_t top_byte = -1; // [d3] 218 | int16_t lookup = value; // [d2] 219 | 220 | do 221 | { 222 | lookup -= 100; 223 | top_byte++; 224 | } 225 | while (lookup >= 0); 226 | lookup += 100; 227 | return (top_byte << 8) | DEC_TO_HEX[lookup]; 228 | } 229 | 230 | // Lookup table to convert a decimal value to hex 231 | const uint8_t outils::DEC_TO_HEX[] = 232 | { 233 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 234 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 235 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 236 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 237 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 238 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 239 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 240 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 241 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 242 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 243 | }; 244 | -------------------------------------------------------------------------------- /src/main/engine/outils.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | OutRun Utility Functions & Assembler Helper Functions. 3 | 4 | Common OutRun library functions. 5 | Helper functions used to facilitate 68K to C++ porting process. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | #include "stdint.hpp" 14 | 15 | class outils 16 | { 17 | public: 18 | static const uint8_t DEC_TO_HEX[]; 19 | 20 | outils(); 21 | ~outils(); 22 | 23 | static void reset_random_seed(); 24 | static uint32_t random(); 25 | static int32_t isqrt(int32_t); 26 | static uint16_t convert16_dechex(uint16_t); 27 | static uint32_t bcd_add(uint32_t, uint32_t); 28 | static uint32_t bcd_sub(uint32_t, uint32_t); 29 | 30 | // Inline functions 31 | inline static void move16(uint32_t src, uint32_t& dst) 32 | { 33 | dst = (dst & 0xFFFF0000) + (src & 0xFFFF); 34 | } 35 | 36 | inline static void add16(uint32_t src, uint32_t& dst) 37 | { 38 | dst = (dst & 0xFFFF0000) + (((dst & 0xFFFF) + (src & 0xFFFF)) & 0xFFFF); 39 | } 40 | 41 | inline static void sub16(int32_t src, int32_t& dst) 42 | { 43 | dst = (dst & 0xFFFF0000) + (((dst & 0xFFFF) - (src & 0xFFFF)) & 0xFFFF); 44 | } 45 | 46 | inline static void swap32(int32_t& v) 47 | { 48 | v = ((v & 0xFFFF0000) >> 16) + ((v & 0xFFFF) << 16); 49 | } 50 | 51 | inline static void swap32(uint32_t& v) 52 | { 53 | v = ((v & 0xFFFF0000) >> 16) + ((v & 0xFFFF) << 16); 54 | } 55 | 56 | static void convert_counter_to_time(uint16_t counter, uint8_t* converted); 57 | 58 | private: 59 | static int32_t next(int32_t, int32_t); 60 | static int32_t abs(int32_t); 61 | }; -------------------------------------------------------------------------------- /src/main/frontend/cabdiag.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Cabinet Diagnostics. 3 | 4 | Diagnostics Modes. Based On Original Code, with modifications. 5 | Mostly of use for real cabinets. 6 | 7 | - CRT Check 8 | - SMARTYPI Interface Check 9 | - Motor Hardware Test 10 | - Brake/Start Lamp Test 11 | - Control Input Test 12 | 13 | Copyright Chris White. 14 | See license.txt for more details. 15 | ***************************************************************************/ 16 | 17 | #pragma once 18 | 19 | #include "../stdint.hpp" 20 | 21 | class CabDiag 22 | { 23 | public: 24 | enum 25 | { 26 | STATE_CRT, 27 | STATE_INPUT, 28 | STATE_OUTPUT, 29 | STATE_MOTORT, 30 | }; 31 | 32 | CabDiag(); 33 | ~CabDiag(void); 34 | bool tick(); 35 | void set(uint8_t); 36 | 37 | private: 38 | bool init; 39 | bool done; 40 | uint8_t state; 41 | uint8_t counter; 42 | 43 | // Can user press start to exit mode? 44 | bool press_start_to_exit; 45 | 46 | void reset(); 47 | 48 | void init_output(); 49 | void tick_output(); 50 | void init_input(); 51 | void tick_input(); 52 | void init_crt(); 53 | void blit_box(); 54 | void blit1_block(uint32_t adr, uint32_t data); 55 | void blit7_block(uint32_t* adr, uint32_t data); 56 | void init_motor_test(); 57 | void tick_motor(); 58 | }; -------------------------------------------------------------------------------- /src/main/frontend/config.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | XML Configuration File Handling. 3 | 4 | Load Settings. 5 | Load & Save Hi-Scores. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include "stdint.hpp" 17 | 18 | struct data_settings_t 19 | { 20 | std::string rom_path; 21 | std::string res_path; 22 | std::string save_path; 23 | std::string cfg_file; 24 | int crc32; 25 | 26 | std::string file_scores; // Arcade Hi-Scores (World & Japanese) 27 | std::string file_scores_jap; 28 | std::string file_ttrial; // Time Trial Hi-Scores 29 | std::string file_ttrial_jap; 30 | std::string file_cont; // Continous Mode Hi-Scores 31 | std::string file_cont_jap; 32 | }; 33 | 34 | struct music_t 35 | { 36 | // Audio Format 37 | const static int IS_YM_INT = 0; // Intenal YM Track (from OutRun ROMs) 38 | const static int IS_YM_EXT = 1; // External YM Track (from Binary) 39 | const static int IS_WAV = 2; // External WAV Track 40 | int type; 41 | 42 | int cmd; // Z80 Command 43 | std::string title; 44 | std::string filename; 45 | }; 46 | 47 | struct ttrial_settings_t 48 | { 49 | int laps; 50 | int traffic; 51 | uint16_t best_times[15]; 52 | }; 53 | 54 | struct menu_settings_t 55 | { 56 | int enabled; 57 | int road_scroll_speed; 58 | }; 59 | 60 | struct video_settings_t 61 | { 62 | const static int MODE_WINDOW = 0; 63 | const static int MODE_FULL = 1; 64 | const static int MODE_STRETCH = 2; 65 | 66 | int mode; 67 | int scale; 68 | int scanlines; 69 | int widescreen; 70 | int fps; 71 | int fps_count; 72 | int hires; 73 | int filtering; 74 | int vsync; 75 | int shadow; 76 | }; 77 | 78 | struct sound_settings_t 79 | { 80 | int enabled; 81 | int rate; 82 | int advertise; 83 | int preview; 84 | int fix_samples; 85 | int music_timer; 86 | std::vector music; 87 | }; 88 | 89 | struct controls_settings_t 90 | { 91 | const static int GEAR_BUTTON = 0; 92 | const static int GEAR_PRESS = 1; // For cabinets 93 | const static int GEAR_SEPARATE = 2; // Separate button presses 94 | const static int GEAR_AUTO = 3; 95 | 96 | int gear; 97 | int steer_speed; // Steering Digital Speed 98 | int pedal_speed; // Pedal Digital Speed 99 | int padconfig[15]; // Joypad Button Config 100 | int keyconfig[12]; // Keyboard Button Config 101 | int pad_id; // Use the N'th joystick on the system. 102 | int analog; // Use analog controls 103 | int axis[4]; // Analog Axis 104 | int asettings[2]; // Analog Settings 105 | bool invert[3]; // Invert Analog Axis 106 | 107 | float rumble; // Simple Controller Rumble Support 108 | int haptic; // Force Feedback Enabled 109 | int max_force; 110 | int min_force; 111 | int force_duration; 112 | }; 113 | 114 | struct smartypi_settings_t 115 | { 116 | int enabled; // CannonBall used in conjunction with SMARTYPI in arcade cabinet 117 | int ouputs; // Write Digital Outputs to console 118 | int cabinet; // Cabinet Type 119 | }; 120 | 121 | struct engine_settings_t 122 | { 123 | int dip_time; 124 | int dip_traffic; 125 | bool freeplay; 126 | bool freeze_timer; 127 | bool disable_traffic; 128 | int jap; 129 | int prototype; 130 | int randomgen; 131 | int level_objects; 132 | bool fix_bugs; 133 | bool fix_bugs_backup; 134 | bool fix_timer; 135 | bool layout_debug; 136 | bool hiscore_delete; // Allow deletion of last entry in score table 137 | int hiscore_timer; // Override default timer on high-score entry screen 138 | int new_attract; // New Attract Mode 139 | bool grippy_tyres; // Handling: Stick to track 140 | bool offroad; // Handling: Drive off-road 141 | bool bumper; // Handling: Smash into other cars without spinning 142 | bool turbo; // Handling: Faster Car 143 | int car_pal; // Car Palette 144 | }; 145 | 146 | class Config 147 | { 148 | public: 149 | data_settings_t data; 150 | menu_settings_t menu; 151 | video_settings_t video; 152 | sound_settings_t sound; 153 | controls_settings_t controls; 154 | engine_settings_t engine; 155 | ttrial_settings_t ttrial; 156 | smartypi_settings_t smartypi; 157 | 158 | const static int CABINET_MOVING = 0; 159 | const static int CABINET_UPRIGHT = 1; 160 | const static int CABINET_MINI = 2; 161 | 162 | // Internal screen width and height 163 | uint16_t s16_width, s16_height; 164 | 165 | // Internal screen x offset 166 | uint16_t s16_x_off; 167 | 168 | // 30 or 60 fps 169 | int fps; 170 | 171 | // Original game ticks sprites at 30fps but background scroll at 60fps 172 | int tick_fps; 173 | 174 | // Continuous Mode: Traffic Setting 175 | int cont_traffic; 176 | 177 | Config(void); 178 | ~Config(void); 179 | 180 | void set_config_file(const std::string& filename); 181 | void load(); 182 | bool save(); 183 | void load_scores(bool original_mode); 184 | void save_scores(bool original_mode); 185 | void load_tiletrial_scores(); 186 | void save_tiletrial_scores(); 187 | bool clear_scores(); 188 | void set_fps(int fps); 189 | void inc_time(); 190 | void inc_traffic(); 191 | 192 | private: 193 | }; 194 | 195 | extern Config config; 196 | -------------------------------------------------------------------------------- /src/main/frontend/menu.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djyt/cannonball/27493ebf62be3498dff93ed6a45e8e2db819bae1/src/main/frontend/menu.cpp -------------------------------------------------------------------------------- /src/main/frontend/menu.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Front End Menu System. 3 | 4 | This file is part of Cannonball. 5 | Copyright Chris White. 6 | See license.txt for more details. 7 | ***************************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include "stdint.hpp" 13 | 14 | class CabDiag; 15 | class TTrial; 16 | 17 | class Menu 18 | { 19 | public: 20 | Menu(); 21 | ~Menu(void); 22 | 23 | void populate(); 24 | void init(bool init_main_menu = true); 25 | void tick(); 26 | 27 | private: 28 | CabDiag* cabdiag; 29 | 30 | // Menu state 31 | uint8_t state; 32 | 33 | enum 34 | { 35 | STATE_MENU, 36 | STATE_REDEFINE_KEYS, 37 | STATE_REDEFINE_JOY, 38 | STATE_TTRIAL, 39 | STATE_DIAGNOSTICS, 40 | }; 41 | 42 | TTrial* ttrial; 43 | 44 | // Music track for music test menu 45 | int music_track; 46 | 47 | // Redefine keys/joystick substate 48 | uint8_t redef_state; 49 | 50 | uint32_t frame; 51 | 52 | // Counter for showing messages 53 | int32_t message_counter; 54 | 55 | // Number of seconds to show message for 56 | const static int32_t MESSAGE_TIME = 5; 57 | 58 | // Message text 59 | std::string msg; 60 | 61 | // Cursor 62 | int16_t cursor; 63 | 64 | struct menu_pair 65 | { 66 | int16_t cursor; 67 | std::vector* menu; 68 | }; 69 | 70 | std::vector menu_stack; 71 | 72 | // Stores whether this is a textual menu (i.e. no options that can be chosen) 73 | bool is_text_menu; 74 | 75 | // Used to control the horizon pan effect 76 | uint16_t horizon_pos; 77 | 78 | std::vector* menu_selected; 79 | std::vector menu_main; 80 | std::vector menu_gamemodes; 81 | std::vector menu_cont; 82 | std::vector menu_timetrial; 83 | std::vector menu_about; 84 | std::vector menu_settings; 85 | std::vector menu_video; 86 | std::vector menu_sound; 87 | std::vector menu_controls; 88 | std::vector menu_controls_gp; 89 | std::vector menu_engine; 90 | std::vector menu_enhancements; 91 | std::vector menu_handling; 92 | std::vector menu_musictest; 93 | std::vector menu_s_exsettings; // smartypi specific 94 | std::vector menu_s_tests; // smartypi specific 95 | std::vector menu_s_dips; // smartypi specific 96 | std::vector menu_s_enhance; // smartypi specific 97 | 98 | std::vector text_redefine; 99 | 100 | void populate_for_pc(); 101 | void populate_controls(); 102 | void populate_for_cabinet(); 103 | void tick_ui(); 104 | void draw_menu_options(); 105 | void draw_text(std::string); 106 | void tick_menu(); 107 | bool select_pressed(); 108 | void set_menu(std::vector*); 109 | void menu_back(); 110 | void refresh_menu(); 111 | void set_menu_text(std::string s1, std::string s2); 112 | void redefine_keyboard(); 113 | void redefine_joystick(); 114 | void display_message(std::string); 115 | bool check_jap_roms(); 116 | void restart_video(); 117 | void start_game(int mode, int settings = 0); 118 | }; -------------------------------------------------------------------------------- /src/main/frontend/menulabels.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ------------------------------------------------------------------------------------------------ 4 | // Text Labels for menus 5 | // ------------------------------------------------------------------------------------------------ 6 | 7 | // Back Labels 8 | const static char* ENTRY_BACK = "BACK"; 9 | 10 | // Main Menu 11 | const static char* ENTRY_PLAYGAME = "PLAY GAME"; 12 | const static char* ENTRY_GAMEMODES = "GAME MODES"; 13 | const static char* ENTRY_SETTINGS = "SETTINGS"; 14 | const static char* ENTRY_ABOUT = "ABOUT"; 15 | const static char* ENTRY_EXIT = "EXIT"; 16 | 17 | // Main Menu (cabinet) 18 | const static char* ENTRY_DIPS = "DIP SWITCHES"; 19 | const static char* ENTRY_CABTESTS = "HARDWARE TESTS"; 20 | const static char* ENTRY_EXSETTINGS = "EXTRA SETTINGS"; 21 | 22 | // Game Modes Menu 23 | const static char* ENTRY_ENHANCED = "SET ENHANCED MODE"; 24 | const static char* ENTRY_ORIGINAL = "SET ORIGINAL MODE"; 25 | const static char* ENTRY_CONT = "CONTINUOUS MODE"; 26 | const static char* ENTRY_TIMETRIAL = "TIME TRIAL MODE"; 27 | 28 | // Time Trial Menu 29 | const static char* ENTRY_START = "START TIME TRIAL"; 30 | const static char* ENTRY_LAPS = "NO OF LAPS "; 31 | 32 | // Continuous Menu 33 | const static char* ENTRY_START_CONT = "START CONTINUOUS MODE"; 34 | 35 | // Settings Menu 36 | const static char* ENTRY_VIDEO = "VIDEO"; 37 | const static char* ENTRY_SOUND = "SOUND"; 38 | const static char* ENTRY_CONTROLS = "CONTROLS"; 39 | const static char* ENTRY_ENGINE = "GAME ENGINE"; 40 | const static char* ENTRY_SCORES = "CLEAR HISCORES"; 41 | const static char* ENTRY_SAVE = "SAVE AND RETURN"; 42 | const static char* ENTRY_ENHANCE = "ENHANCEMENTS"; 43 | 44 | // SMARTYPI Extra Settings 45 | const static char* ENTRY_S_CAB = "CABINET TYPE "; 46 | const static char* ENTRY_FREEPLAY = "FREEPLAY "; 47 | const static char* ENTRY_S_MOTOR = "MOTOR TEST"; 48 | const static char* ENTRY_S_INPUTS = "INPUT TEST"; 49 | const static char* ENTRY_S_OUTPUTS = "OUTPUT TEST"; 50 | const static char* ENTRY_S_CRT = "CRT TEST"; 51 | const static char* ENTRY_TIMER = "TIMING FIXES "; 52 | const static char* ENTRY_S_BUGS = "BUG FIXES "; 53 | 54 | // Video Menu 55 | const static char* ENTRY_FPS = "FRAME RATE "; 56 | const static char* ENTRY_FULLSCREEN = "FULL SCREEN "; 57 | const static char* ENTRY_WIDESCREEN = "WIDESCREEN "; 58 | const static char* ENTRY_HIRES = "HIRES "; 59 | const static char* ENTRY_SCALE = "WINDOW SCALE "; 60 | const static char* ENTRY_SCANLINES = "SCANLINES "; 61 | 62 | // Sound Menu 63 | const static char* ENTRY_MUTE = "SOUND "; 64 | const static char* ENTRY_BGM = "BGM VOL "; 65 | const static char* ENTRY_SFX = "SFX VOL "; 66 | const static char* ENTRY_ADVERTISE = "ATTRACT SOUND "; 67 | const static char* ENTRY_PREVIEWSND = "PREVIEW MUSIC "; 68 | const static char* ENTRY_FIXSAMPLES = "FIX SAMPLES "; 69 | const static char* ENTRY_MUSICTEST = "MUSIC TEST"; 70 | 71 | // Controls Menu 72 | const static char* ENTRY_GEAR = "GEAR "; 73 | const static char* ENTRY_CONFIGUREGP = "CONFIGURE GAMEPAD"; 74 | const static char* ENTRY_REDEFKEY = "REDEFINE KEYS"; 75 | const static char* ENTRY_DSTEER = "DIGITAL STEER SPEED "; 76 | const static char* ENTRY_DPEDAL = "DIGITAL PEDAL SPEED "; 77 | 78 | // GamePad Menu 79 | const static char* ENTRY_ANALOG = "ANALOG "; 80 | const static char* ENTRY_RUMBLE = "RUMBLE STRENGTH "; 81 | const static char* ENTRY_REDEFJOY = "REDEFINE GAMEPAD"; 82 | 83 | // Game Engine Menu 84 | const static char* ENTRY_TRACKS = "TRACKS "; 85 | const static char* ENTRY_TIME = "TIME "; 86 | const static char* ENTRY_TRAFFIC = "TRAFFIC "; 87 | const static char* ENTRY_SUB_ENHANCEMENTS = "ENHANCEMENTS"; 88 | const static char* ENTRY_SUB_HANDLING = "CAR SETUP"; 89 | 90 | // Game Engine: Enhancements Sub-Menu 91 | const static char* ENTRY_ATTRACT = "NEW ATTRACT "; 92 | const static char* ENTRY_PROTOTYPE = "PROTOTYPE STAGE 1 "; 93 | const static char* ENTRY_OBJECTS = "OBJECTS "; 94 | 95 | // Game Engine: Car Setup Sub-Menu 96 | const static char* ENTRY_GRIP = "GRIPPY TYRES "; 97 | const static char* ENTRY_OFFROAD = "OFFROAD TYRES "; 98 | const static char* ENTRY_BUMPER = "STRONG BUMPER "; 99 | const static char* ENTRY_TURBO = "FASTER CAR "; 100 | const static char* ENTRY_COLOR = "COLOR "; 101 | 102 | const static char* COLOR_LABELS[5] = { "RED", "BLUE", "YELLOW", "GREEN", "CYAN" }; 103 | 104 | // Music Test Menu 105 | const static char* ENTRY_MUSIC1 = "PLAY TRACK"; 106 | const static char* ENTRY_MUSIC2 = "TRACK - "; 107 | const static char* ENTRY_MUSIC3 = "LAST WAVE"; 108 | 109 | const static char* DIP_DIFFICULTY[4] = { "EASY", "NORMAL", "HARD", "HARDEST" }; 110 | const static char* GEAR_LABELS[4] = {"MANUAL", "MANUAL CABINET", "MANUAL 2 BUTTONS", "AUTOMATIC"}; 111 | const static char* FPS_LABELS[3] = { "30 FPS", "ORIGINAL", "60 FPS" }; 112 | const static char* ANALOG_LABELS[3] = { "OFF", "ON", "ON WHEEL ONLY" }; 113 | const static char* VIDEO_LABELS[3] = { "OFF", "ON", "STRETCH" }; 114 | const static char* RUMBLE_LABELS[5] = { "OFF", "LOW", "MED", "HIGH", "FULL" }; 115 | const static char* CAB_LABELS[3] = { "MOVING", "UPRIGHT", "MINI" }; -------------------------------------------------------------------------------- /src/main/frontend/ttrial.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Time Trial Mode Front End. 3 | 4 | This file is part of Cannonball. 5 | Copyright Chris White. 6 | See license.txt for more details. 7 | ***************************************************************************/ 8 | 9 | #include "sdl2/input.hpp" 10 | 11 | #include "frontend/ttrial.hpp" 12 | 13 | #include "engine/ohud.hpp" 14 | #include "engine/oinputs.hpp" 15 | #include "engine/outils.hpp" 16 | #include "engine/omap.hpp" 17 | #include "engine/ostats.hpp" 18 | #include "engine/otiles.hpp" 19 | 20 | // Track Selection: Ferrari Position Per Track 21 | // This is a link to a sprite object that represents part of the course map. 22 | static const uint8_t FERRARI_POS[] = 23 | { 24 | 1,5,3,11,9,7,19,17,15,13,24,23,22,21,20 25 | }; 26 | 27 | // Map Stage Number to Internal Lookup 28 | static const uint8_t STAGE_LOOKUP[] = 29 | { 30 | 0x00, 31 | 0x09, 0x08, 32 | 0x12, 0x11, 0x10, 33 | 0x1B, 0x1A, 0x19, 0x18, 34 | 0x24, 0x23, 0x22, 0x21, 0x20 35 | }; 36 | 37 | TTrial::TTrial(uint16_t* best_times) 38 | { 39 | this->best_times = best_times; 40 | } 41 | 42 | TTrial::~TTrial(void) 43 | { 44 | 45 | } 46 | 47 | void TTrial::init() 48 | { 49 | state = INIT_COURSEMAP; 50 | } 51 | 52 | int TTrial::tick() 53 | { 54 | switch (state) 55 | { 56 | case INIT_COURSEMAP: 57 | outrun.select_course(config.engine.jap != 0, config.engine.prototype != 0); // Need to setup correct course map graphics. 58 | config.load_tiletrial_scores(); 59 | ostats.init(true); 60 | osprites.init(); 61 | video.enabled = true; 62 | video.sprite_layer->set_x_clip(false); 63 | omap.init(); 64 | omap.load_sprites(); 65 | omap.position_ferrari(FERRARI_POS[level_selected = 0]); 66 | ohud.blit_text_big(1, "STEER TO SELECT TRACK"); 67 | ohud.blit_text1(2, 25, TEXT1_LAPTIME1); 68 | ohud.blit_text1(2, 26, TEXT1_LAPTIME2); 69 | osoundint.queue_sound(sound::PCM_WAVE); 70 | outrun.ttrial.laps = config.ttrial.laps; 71 | outrun.custom_traffic = config.ttrial.traffic; 72 | state = TICK_COURSEMAP; 73 | 74 | case TICK_COURSEMAP: 75 | { 76 | if (input.has_pressed(Input::MENU)) 77 | { 78 | return BACK_TO_MENU; 79 | } 80 | else if (input.has_pressed(Input::LEFT) || oinputs.is_analog_l()) 81 | { 82 | if (--level_selected < 0) 83 | level_selected = sizeof(FERRARI_POS) - 1; 84 | } 85 | else if (input.has_pressed(Input::RIGHT)|| oinputs.is_analog_r()) 86 | { 87 | if (++level_selected > sizeof(FERRARI_POS) - 1) 88 | level_selected = 0; 89 | } 90 | else if (input.has_pressed(Input::START) || input.has_pressed(Input::ACCEL) || oinputs.is_analog_select()) 91 | { 92 | outils::convert_counter_to_time(best_times[level_selected], best_converted); 93 | 94 | outrun.cannonball_mode = Outrun::MODE_TTRIAL; 95 | outrun.ttrial.level = STAGE_LOOKUP[level_selected]; 96 | outrun.ttrial.current_lap = 0; 97 | outrun.ttrial.best_lap_counter = 10000; 98 | outrun.ttrial.best_lap[0] = best_converted[0]; 99 | outrun.ttrial.best_lap[1] = best_converted[1]; 100 | outrun.ttrial.best_lap[2] = best_converted[2]; 101 | outrun.ttrial.best_lap_counter = best_times[level_selected]; 102 | outrun.ttrial.new_high_score = false; 103 | outrun.ttrial.overtakes = 0; 104 | outrun.ttrial.crashes = 0; 105 | outrun.ttrial.vehicle_cols = 0; 106 | ostats.credits = 1; 107 | return INIT_GAME; 108 | } 109 | omap.position_ferrari(FERRARI_POS[level_selected]); 110 | outils::convert_counter_to_time(best_times[level_selected], best_converted); 111 | ohud.draw_lap_timer(ohud.translate(7, 26), best_converted, best_converted[2]); 112 | omap.blit(); 113 | oroad.tick(); 114 | osprites.sprite_copy(); 115 | osprites.update_sprites(); 116 | otiles.write_tilemap_hw(); 117 | otiles.update_tilemaps(0); 118 | } 119 | break; 120 | } 121 | 122 | return CONTINUE; 123 | } 124 | 125 | void TTrial::update_best_time() 126 | { 127 | best_times[level_selected] = outrun.ttrial.best_lap_counter; 128 | config.save_tiletrial_scores(); 129 | } 130 | -------------------------------------------------------------------------------- /src/main/frontend/ttrial.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Time Trial Mode Front End. 3 | 4 | This file is part of Cannonball. 5 | Copyright Chris White. 6 | See license.txt for more details. 7 | ***************************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include "stdint.hpp" 12 | 13 | class TTrial 14 | { 15 | public: 16 | // Maximum number of laps to allow the player to race 17 | static const uint8_t MAX_LAPS = 5; 18 | 19 | // Maximum number of cars to spawn 20 | static const uint8_t MAX_TRAFFIC = 8; 21 | 22 | enum 23 | { 24 | BACK_TO_MENU = -1, 25 | CONTINUE = 0, 26 | INIT_GAME = 1, 27 | }; 28 | 29 | uint8_t state; 30 | int8_t level_selected; 31 | 32 | enum 33 | { 34 | INIT_COURSEMAP, 35 | TICK_COURSEMAP, 36 | TICK_GAME_ENGINE, 37 | }; 38 | 39 | TTrial(uint16_t* best_times); 40 | ~TTrial(void); 41 | 42 | void init(); 43 | int tick(); 44 | void update_best_time(); 45 | 46 | private: 47 | // Best lap times for all 15 tracks. 48 | uint16_t* best_times; 49 | 50 | // Counter converted to actual laptime 51 | uint8_t best_converted[3]; 52 | }; -------------------------------------------------------------------------------- /src/main/globals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.hpp" 4 | 5 | // ------------------------------------------------------------------------------------------------ 6 | // Compiler Settings 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // Comment out to disable SDL specific sound code 10 | #define COMPILE_SOUND_CODE 1 11 | 12 | // ------------------------------------------------------------------------------------------------ 13 | // Debug Settings 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | const bool DEBUG_LEVEL = false; 17 | 18 | // Force AI to play the levels 19 | const bool FORCE_AI = false; 20 | 21 | // ------------------------------------------------------------------------------------------------ 22 | // General useful stuff 23 | // ------------------------------------------------------------------------------------------------ 24 | 25 | // Internal Sega OutRun Screen Properties 26 | const uint16_t S16_WIDTH = 320; 27 | const uint16_t S16_HEIGHT = 224; 28 | 29 | // Internal Widescreen Width 30 | const uint16_t S16_WIDTH_WIDE = 398; 31 | 32 | // Palette Address in Memory 33 | const uint32_t S16_PALETTE_BASE = 0x120000; 34 | 35 | // Number of Palette Entries 36 | const uint16_t S16_PALETTE_ENTRIES = 0x1000; 37 | 38 | // Number of stages 39 | const uint16_t STAGES = 15; 40 | 41 | // Hard Coded End Point of every level 42 | const static uint16_t ROAD_END = 0x79C; 43 | 44 | // End Point of level for CPU1, including horizon 45 | const static uint16_t ROAD_END_CPU1 = 0x904; 46 | 47 | // Default timer used for hi-score entry 48 | const static uint8_t HIGHSCORE_TIMER = 0x30; 49 | 50 | // Default timer used for music selection (was 15 seconds on original/old romset) 51 | const static uint8_t MUSIC_TIMER = 0x30; 52 | 53 | enum 54 | { 55 | BIT_0 = 0x01, 56 | BIT_1 = 0x02, 57 | BIT_2 = 0x04, 58 | BIT_3 = 0x08, 59 | BIT_4 = 0x10, 60 | BIT_5 = 0x20, 61 | BIT_6 = 0x40, 62 | BIT_7 = 0x80, 63 | BIT_8 = 0x100, 64 | BIT_9 = 0x200, 65 | BIT_A = 0x400 66 | }; -------------------------------------------------------------------------------- /src/main/hwaudio/segapcm.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sega 8-Bit PCM Driver. 3 | 4 | This driver is based upon the MAME source code, with some minor 5 | modifications to integrate it into the Cannonball framework. 6 | 7 | Note, that I've altered this driver to output at a fixed 44,100Hz. 8 | This is to avoid the need for downsampling. 9 | 10 | See http://mamedev.org/source/docs/license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | /** 14 | * RAM DESCRIPTION =============== 15 | * 16 | * 0x00 - 0x07, 0x80 - 0x87 : CHANNEL #1 17 | * 0x08 - 0x0F, 0x88 - 0x8F : CHANNEL #2 18 | * 0x10 - 0x17, 0x90 - 0x97 : CHANNEL #3 19 | * 0x18 - 0x1F, 0x98 - 0x9F : CHANNEL #4 20 | * 0x20 - 0x27, 0xA0 - 0xA7 : CHANNEL #5 21 | * 0x28 - 0x2F, 0xA8 - 0xAF : CHANNEL #6 22 | * 0x30 - 0x37, 0xB0 - 0xB7 : CHANNEL #7 23 | * 0x38 - 0x3F, 0xB8 - 0xBF : CHANNEL #8 24 | * 0x40 - 0x47, 0xC0 - 0xC7 : CHANNEL #9 25 | * 0x48 - 0x4F, 0xC8 - 0xCF : CHANNEL #10 26 | * 0x50 - 0x57, 0xD0 - 0xD7 : CHANNEL #11 27 | * 0x58 - 0x5F, 0xD8 - 0xDF : CHANNEL #12 28 | * 0x60 - 0x67, 0xE0 - 0xE7 : CHANNEL #13 29 | * 0x68 - 0x6F, 0xE8 - 0xEF : CHANNEL #14 30 | * 0x70 - 0x77, 0xF0 - 0xF7 : CHANNEL #15 31 | * 0x78 - 0x7F, 0xF8 - 0xFF : CHANNEL #16 32 | * 33 | * 34 | * CHANNEL DESCRIPTION =================== 35 | * 36 | * OFFS | BITS | DESCRIPTION 37 | * -----+----------+--------------------------------- 38 | * 0x00 | -------- | (unknown) 39 | * 0x01 | -------- | (unknown) 40 | * 0x02 | vvvvvvvv | Volume LEFT 41 | * 0x03 | vvvvvvvv | Volume RIGHT 42 | * 0x04 | aaaaaaaa | Wave Start Address LOW 8 bits 43 | * 0x05 | aaaaaaaa | Wave Start Address HIGH 8 bits 44 | * 0x06 | eeeeeeee | Wave End Address HIGH 8 bits 45 | * 0x07 | dddddddd | Delta (pitch) 46 | * 0x80 | -------- | (unknown) 47 | * 0x81 | -------- | (unknown) 48 | * 0x82 | -------- | (unknown) 49 | * 0x83 | -------- | (unknown) 50 | * 0x84 | llllllll | Wave Loop Address LOW 8 bits 51 | * 0x85 | llllllll | Wave Loop Address HIGH 8 bits 52 | * 0x86 | ------la | Flags: a = active (0 = active, 1 = inactive) 53 | * | | l = loop (0 = enabled, 1 = disabled) 54 | * 55 | */ 56 | 57 | #include "hwaudio/segapcm.hpp" 58 | 59 | SegaPCM::SegaPCM(uint32_t clock, RomLoader* rom, uint8_t* ram, int32_t bank) 60 | { 61 | this->ram = ram; 62 | pcm_rom = rom->rom; 63 | low = new uint8_t[16]; 64 | max_addr = rom->length; 65 | bankshift = bank & 0xFF; 66 | rgnmask = max_addr - 1; 67 | 68 | int32_t mask = bank >> 16; 69 | if (mask == 0) 70 | mask = BANK_MASK7 >> 16; 71 | 72 | int32_t rom_mask; 73 | for (rom_mask = 1; rom_mask < max_addr; rom_mask *= 2); 74 | rom_mask--; 75 | 76 | bankmask = mask & (rom_mask >> bankshift); 77 | 78 | for (int32_t i = 0; i < 0x100; i++) 79 | ram[i] = 0xff; 80 | } 81 | 82 | SegaPCM::~SegaPCM() 83 | { 84 | delete[] low; 85 | } 86 | 87 | void SegaPCM::init(int32_t rate, int32_t fps) 88 | { 89 | this->sample_freq = rate; 90 | downsample = (32000.0 / (double) rate); 91 | SoundChip::init(STEREO, rate, fps); 92 | } 93 | 94 | void SegaPCM::stream_update() 95 | { 96 | SoundChip::clear_buffer(); 97 | 98 | // loop over channels 99 | for (int ch = 0; ch < 16; ch++) 100 | { 101 | uint8_t *regs = ram + 8 * ch; 102 | 103 | // only process active channels 104 | if ((regs[0x86] & 1) == 0) 105 | { 106 | uint8_t *rom = pcm_rom + ((regs[0x86] & bankmask) << bankshift); 107 | 108 | uint32_t addr = (regs[0x85] << 16) | (regs[0x84] << 8) | low[ch]; 109 | uint32_t loop = (regs[0x05] << 16) | (regs[0x04] << 8); 110 | uint8_t end = regs[0x06] + 1; 111 | 112 | uint32_t i; 113 | 114 | // loop over samples on this channel 115 | for (i = 0; i < frame_size; i++) 116 | { 117 | int8_t v = 0; 118 | 119 | // handle looping if we've hit the end 120 | if ((addr >> 16) == end) 121 | { 122 | if ((regs[0x86] & 2) == 0) 123 | { 124 | addr = loop; 125 | } 126 | else 127 | { 128 | regs[0x86] |= 1; 129 | break; 130 | } 131 | } 132 | 133 | // fetch the sample 134 | v = rom[(addr >> 8) & rgnmask] - 0x80; 135 | 136 | // apply panning 137 | write_buffer(LEFT, i, read_buffer(LEFT, i) + (v * regs[2])); 138 | write_buffer(RIGHT, i, read_buffer(RIGHT, i) + (v * regs[3])); 139 | 140 | // Advance. 141 | // Cannonball Change: Output at a fixed 44,100Hz. 142 | double increment = ((double)regs[7]) * downsample; 143 | addr = (addr + (int) increment) & 0xffffff; 144 | } 145 | 146 | // store back the updated address and info 147 | regs[0x84] = addr >> 8; 148 | regs[0x85] = addr >> 16; 149 | low[ch] = regs[0x86] & 1 ? 0 : addr; 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/main/hwaudio/segapcm.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sega 8-Bit PCM Driver 3 | 4 | This driver is based upon the MAME source code, with some minor 5 | modifications to integrate it into the Cannonball framework. 6 | 7 | Note, that I've altered this driver to output at a fixed 44,100Hz. 8 | This is to avoid the need for downsampling. 9 | 10 | See http://mamedev.org/source/docs/license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include "stdint.hpp" 16 | #include "romloader.hpp" 17 | #include "hwaudio/soundchip.hpp" 18 | 19 | class SegaPCM : public SoundChip 20 | { 21 | public: 22 | static const uint32_t BANK_256 = (11); 23 | static const uint32_t BANK_512 = (12); 24 | static const uint32_t BANK_12M = (13); 25 | static const uint32_t BANK_MASK7 = (0x70 << 16); 26 | static const uint32_t BANK_MASKF = (0xf0 << 16); 27 | static const uint32_t BANK_MASKF8 = (0xf8 << 16); 28 | 29 | SegaPCM(uint32_t clock, RomLoader* rom, uint8_t* ram, int32_t bank); 30 | ~SegaPCM(); 31 | void init(int32_t rate, int32_t fps); 32 | void stream_update(); 33 | 34 | private: 35 | // PCM Chip Emulation 36 | uint8_t* ram; 37 | uint8_t* low; 38 | uint8_t* pcm_rom; 39 | int32_t max_addr; 40 | int32_t bankshift; 41 | int32_t bankmask; 42 | int32_t rgnmask; 43 | 44 | double downsample; 45 | }; -------------------------------------------------------------------------------- /src/main/hwaudio/soundchip.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sound Chip 3 | 4 | This is an abstract class, used by the Sega PCM and YM2151 chips. 5 | It facilitates writing to a buffer of sound data. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #include "stdint.hpp" 12 | #include "hwaudio/soundchip.hpp" 13 | 14 | SoundChip::SoundChip() 15 | { 16 | volume = 1.0; 17 | initalized = false; 18 | } 19 | 20 | SoundChip::~SoundChip() 21 | { 22 | delete[] buffer; 23 | } 24 | 25 | void SoundChip::init(uint8_t channels, int32_t sample_freq, int32_t fps) 26 | { 27 | this->fps = fps; 28 | this->sample_freq = sample_freq; 29 | this->channels = channels; 30 | 31 | frame_size = sample_freq / fps; 32 | buffer_size = frame_size * channels; 33 | 34 | if (initalized) 35 | delete[] buffer; 36 | 37 | buffer = new int16_t[buffer_size]; 38 | 39 | initalized = true; 40 | } 41 | 42 | // Set soundchip volume (0 = Off, 10 = Loudest) 43 | void SoundChip::set_volume(uint8_t v) 44 | { 45 | if (v > 10) 46 | return; 47 | 48 | volume = (float) (v / 10.0); 49 | } 50 | 51 | void SoundChip::clear_buffer() 52 | { 53 | for (uint32_t i = 0; i < buffer_size; i++) 54 | buffer[i] = 0; 55 | } 56 | 57 | void SoundChip::write_buffer(const uint8_t channel, uint32_t address, int16_t value) 58 | { 59 | //buffer[channel + (address * channels)] = (int16_t) (value * volume); // Unused for now 60 | buffer[channel + (address * channels)] = value; 61 | } 62 | 63 | int16_t SoundChip::read_buffer(const uint8_t channel, uint32_t address) 64 | { 65 | return buffer[channel + (address * channels)]; 66 | } 67 | 68 | int16_t* SoundChip::get_buffer() 69 | { 70 | return buffer; 71 | } -------------------------------------------------------------------------------- /src/main/hwaudio/soundchip.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Sound Chip 3 | 4 | This is an abstract class, used by the Sega PCM and YM2151 chips. 5 | It facilitates writing to a buffer of sound data. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | class SoundChip 14 | { 15 | public: 16 | bool initalized; 17 | 18 | // Sample Frequency in use 19 | uint32_t sample_freq; 20 | 21 | // How many channels to support (mono/stereo) 22 | uint8_t channels; 23 | 24 | // Size of the buffer (including channel info) 25 | uint32_t buffer_size; 26 | 27 | SoundChip(); 28 | ~SoundChip(); 29 | 30 | void init(uint8_t, int32_t, int32_t); 31 | 32 | // Pure virtual function. Denotes virtual class. 33 | virtual void stream_update() = 0; 34 | 35 | int16_t* get_buffer(); 36 | void set_volume(uint8_t); 37 | 38 | protected: 39 | const static uint8_t MONO = 1; 40 | const static uint8_t STEREO = 2; 41 | 42 | const static uint8_t LEFT = 0; 43 | const static uint8_t RIGHT = 1; 44 | 45 | // Buffer size for one frame (excluding channel info) 46 | uint32_t frame_size; 47 | 48 | // Volume of sound chip 49 | float volume; 50 | 51 | void clear_buffer(); 52 | void write_buffer(const uint8_t, uint32_t, int16_t); 53 | int16_t read_buffer(const uint8_t, uint32_t); 54 | 55 | private: 56 | // Sound buffer stream 57 | int16_t* buffer; 58 | 59 | // Frames per second 60 | uint32_t fps; 61 | }; -------------------------------------------------------------------------------- /src/main/hwaudio/ym2151.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Yamaha YM2151 driver (version 2.150 final beta) - May, 11th 2002 3 | 4 | (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net) 5 | Some of the optimizing ideas by Tatsuyuki Satoh 6 | 7 | This driver is based upon the MAME source code, with some minor 8 | modifications to integrate it into the Cannonball framework. 9 | 10 | See http://mamedev.org/source/docs/license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include "stdint.hpp" 16 | #include "romloader.hpp" 17 | #include "hwaudio/soundchip.hpp" 18 | 19 | /* struct describing a single operator */ 20 | typedef struct 21 | { 22 | uint32_t phase; /* accumulated operator phase */ 23 | uint32_t freq; /* operator frequency count */ 24 | int32_t dt1; /* current DT1 (detune 1 phase inc/decrement) value */ 25 | uint32_t mul; /* frequency count multiply */ 26 | uint32_t dt1_i; /* DT1 index * 32 */ 27 | uint32_t dt2; /* current DT2 (detune 2) value */ 28 | 29 | signed int *connects; /* operator output 'direction' */ 30 | 31 | /* only M1 (operator 0) is filled with this data: */ 32 | signed int *mem_connect; /* where to put the delayed sample (MEM) */ 33 | int32_t mem_value; /* delayed sample (MEM) value */ 34 | 35 | /* channel specific data; note: each operator number 0 contains channel specific data */ 36 | uint32_t fb_shift; /* feedback shift value for operators 0 in each channel */ 37 | int32_t fb_out_curr; /* operator feedback value (used only by operators 0) */ 38 | int32_t fb_out_prev; /* previous feedback value (used only by operators 0) */ 39 | uint32_t kc; /* channel KC (copied to all operators) */ 40 | uint32_t kc_i; /* just for speedup */ 41 | uint32_t pms; /* channel PMS */ 42 | uint32_t ams; /* channel AMS */ 43 | /* end of channel specific data */ 44 | 45 | uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ 46 | uint32_t state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */ 47 | uint8_t eg_sh_ar; /* (attack state) */ 48 | uint8_t eg_sel_ar; /* (attack state) */ 49 | uint32_t tl; /* Total attenuation Level */ 50 | int32_t volume; /* current envelope attenuation level */ 51 | uint8_t eg_sh_d1r; /* (decay state) */ 52 | uint8_t eg_sel_d1r; /* (decay state) */ 53 | uint32_t d1l; /* envelope switches to sustain state after reaching this level */ 54 | uint8_t eg_sh_d2r; /* (sustain state) */ 55 | uint8_t eg_sel_d2r; /* (sustain state) */ 56 | uint8_t eg_sh_rr; /* (release state) */ 57 | uint8_t eg_sel_rr; /* (release state) */ 58 | 59 | uint32_t key; /* 0=last key was KEY OFF, 1=last key was KEY ON */ 60 | 61 | uint32_t ks; /* key scale */ 62 | uint32_t ar; /* attack rate */ 63 | uint32_t d1r; /* decay rate */ 64 | uint32_t d2r; /* sustain rate */ 65 | uint32_t rr; /* release rate */ 66 | 67 | uint32_t reserved0; /**/ 68 | uint32_t reserved1; /**/ 69 | 70 | } YM2151Operator; 71 | 72 | class YM2151 : public SoundChip 73 | { 74 | public: 75 | bool irq; 76 | 77 | YM2151(float volume, uint32_t clock); 78 | ~YM2151(); 79 | void init(int rate, int fps); 80 | void stream_update(); 81 | void write_reg(int r, int v); 82 | int read_status(); 83 | 84 | private: 85 | int clock; /*chip clock in Hz (passed from 2151intf.c)*/ 86 | int sampfreq; /*sampling frequency in Hz (passed from 2151intf.c)*/ 87 | float volume; 88 | 89 | void init_tables(); 90 | void init_chip_tables(); 91 | inline void envelope_KONKOFF(YM2151Operator * op, int v); 92 | inline void set_connect(YM2151Operator *om1, int cha, int v); 93 | inline void refresh_EG(YM2151Operator * op); 94 | void ym2151_reset_chip(); 95 | inline signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm); 96 | inline signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm); 97 | inline void chan_calc(unsigned int chan); 98 | inline void chan7_calc(); 99 | inline void advance_eg(); 100 | inline void advance(); 101 | }; -------------------------------------------------------------------------------- /src/main/hwvideo/hwroad.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.hpp" 4 | 5 | class HWRoad 6 | { 7 | public: 8 | HWRoad(); 9 | ~HWRoad(); 10 | 11 | void init(const uint8_t*, const bool hires); 12 | void write16(uint32_t adr, const uint16_t data); 13 | void write16(uint32_t* adr, const uint16_t data); 14 | void write32(uint32_t* adr, const uint32_t data); 15 | uint16_t read_road_control(); 16 | void write_road_control(const uint8_t); 17 | void (HWRoad::*render_background)(uint16_t*); 18 | void (HWRoad::*render_foreground)(uint16_t*); 19 | 20 | private: 21 | uint8_t road_control; 22 | uint16_t color_offset1; 23 | uint16_t color_offset2; 24 | uint16_t color_offset3; 25 | int32_t x_offset; 26 | 27 | static const uint16_t ROAD_RAM_SIZE = 0x1000; 28 | static const uint16_t rom_size = 0x8000; 29 | 30 | // Decoded road graphics 31 | uint8_t roads[0x40200]; 32 | 33 | // Two halves of RAM 34 | uint16_t ram[ROAD_RAM_SIZE / 2]; 35 | uint16_t ramBuff[ROAD_RAM_SIZE / 2]; 36 | 37 | void decode_road(const uint8_t*); 38 | void render_background_lores(uint16_t*); 39 | void render_foreground_lores(uint16_t*); 40 | void render_background_hires(uint16_t*); 41 | void render_foreground_hires(uint16_t*); 42 | }; 43 | 44 | extern HWRoad hwroad; -------------------------------------------------------------------------------- /src/main/hwvideo/hwsprites.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.hpp" 4 | 5 | class video; 6 | 7 | class hwsprites 8 | { 9 | public: 10 | hwsprites(); 11 | ~hwsprites(); 12 | void init(const uint8_t*); 13 | void reset(); 14 | void set_x_clip(bool); 15 | void swap(); 16 | uint8_t read(const uint16_t adr); 17 | void write(const uint16_t adr, const uint16_t data); 18 | void render(const uint8_t); 19 | 20 | private: 21 | // Clip values. 22 | uint16_t x1, x2; 23 | 24 | // 128 sprites, 16 bytes each (0x400) 25 | static const uint16_t SPRITE_RAM_SIZE = 128 * 8; 26 | static const uint32_t SPRITES_LENGTH = 0x100000 >> 2; 27 | static const uint16_t COLOR_BASE = 0x800; 28 | 29 | uint32_t sprites[SPRITES_LENGTH]; // Converted sprites 30 | 31 | // Two halves of RAM 32 | uint16_t ram[SPRITE_RAM_SIZE]; 33 | uint16_t ramBuff[SPRITE_RAM_SIZE]; 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /src/main/hwvideo/hwtiles.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.hpp" 4 | 5 | class RomLoader; 6 | 7 | class hwtiles 8 | { 9 | public: 10 | enum 11 | { 12 | LEFT, 13 | RIGHT, 14 | CENTRE, 15 | }; 16 | 17 | uint8_t text_ram[0x1000]; // Text RAM 18 | uint8_t tile_ram[0x10000]; // Tile RAM 19 | 20 | hwtiles(void); 21 | ~hwtiles(void); 22 | 23 | void init(uint8_t* src_tiles, const bool hires); 24 | void patch_tiles(RomLoader* patch); 25 | void restore_tiles(); 26 | void set_x_clamp(const uint16_t); 27 | void update_tile_values(); 28 | void render_tile_layer(uint16_t*, uint8_t, uint8_t); 29 | void render_text_layer(uint16_t*, uint8_t); 30 | void render_all_tiles(uint16_t*); 31 | 32 | private: 33 | int16_t x_clamp; 34 | 35 | // S16 Width, ignoring widescreen related scaling. 36 | uint16_t s16_width_noscale; 37 | 38 | static const int TILES_LENGTH = 0x10000; 39 | uint32_t tiles[TILES_LENGTH]; // Converted tiles 40 | uint32_t tiles_backup[TILES_LENGTH]; // Converted tiles (backup without patch) 41 | 42 | uint16_t page[4]; 43 | uint16_t scroll_x[4]; 44 | uint16_t scroll_y[4]; 45 | 46 | uint8_t tile_banks[2]; 47 | 48 | static const uint16_t NUM_TILES = 0x2000; // Length of graphic rom / 24 49 | static const uint16_t TILEMAP_COLOUR_OFFSET = 0x1c00; 50 | 51 | void (hwtiles::*render8x8_tile_mask)( 52 | uint16_t *buf, 53 | uint16_t nTileNumber, 54 | uint16_t StartX, 55 | uint16_t StartY, 56 | uint16_t nTilePalette, 57 | uint16_t nColourDepth, 58 | uint16_t nMaskColour, 59 | uint16_t nPaletteOffset); 60 | 61 | void (hwtiles::*render8x8_tile_mask_clip)( 62 | uint16_t *buf, 63 | uint16_t nTileNumber, 64 | int16_t StartX, 65 | int16_t StartY, 66 | uint16_t nTilePalette, 67 | uint16_t nColourDepth, 68 | uint16_t nMaskColour, 69 | uint16_t nPaletteOffset); 70 | 71 | void render8x8_tile_mask_lores( 72 | uint16_t *buf, 73 | uint16_t nTileNumber, 74 | uint16_t StartX, 75 | uint16_t StartY, 76 | uint16_t nTilePalette, 77 | uint16_t nColourDepth, 78 | uint16_t nMaskColour, 79 | uint16_t nPaletteOffset); 80 | 81 | void render8x8_tile_mask_clip_lores( 82 | uint16_t *buf, 83 | uint16_t nTileNumber, 84 | int16_t StartX, 85 | int16_t StartY, 86 | uint16_t nTilePalette, 87 | uint16_t nColourDepth, 88 | uint16_t nMaskColour, 89 | uint16_t nPaletteOffset); 90 | 91 | void render8x8_tile_mask_hires( 92 | uint16_t *buf, 93 | uint16_t nTileNumber, 94 | uint16_t StartX, 95 | uint16_t StartY, 96 | uint16_t nTilePalette, 97 | uint16_t nColourDepth, 98 | uint16_t nMaskColour, 99 | uint16_t nPaletteOffset); 100 | 101 | void render8x8_tile_mask_clip_hires( 102 | uint16_t *buf, 103 | uint16_t nTileNumber, 104 | int16_t StartX, 105 | int16_t StartY, 106 | uint16_t nTilePalette, 107 | uint16_t nColourDepth, 108 | uint16_t nMaskColour, 109 | uint16_t nPaletteOffset); 110 | 111 | inline void set_pixel_x4(uint16_t *buf, uint32_t data); 112 | }; -------------------------------------------------------------------------------- /src/main/main.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "globals.hpp" 4 | #include "sdl2/audio.hpp" 5 | 6 | namespace cannonball 7 | { 8 | extern Audio audio; 9 | 10 | // Frame counter 11 | extern int frame; 12 | 13 | // Tick Logic. Used when running at non-standard > 30 fps 14 | extern bool tick_frame; 15 | 16 | // Millisecond Time Per Frame 17 | extern double frame_ms; 18 | 19 | // FPS Counter 20 | extern int fps_counter; 21 | 22 | // Engine Master State 23 | extern int state; 24 | 25 | enum 26 | { 27 | STATE_BOOT, 28 | STATE_INIT_MENU, 29 | STATE_MENU, 30 | STATE_INIT_GAME, 31 | STATE_GAME, 32 | STATE_QUIT 33 | }; 34 | } 35 | 36 | int main(int argc, char* argv[]); 37 | -------------------------------------------------------------------------------- /src/main/romloader.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Binary File Loader. 3 | 4 | Handles loading an individual binary file to memory. 5 | Supports reading bytes, words and longs from this area of memory. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | class RomLoader 14 | { 15 | 16 | public: 17 | enum {NORMAL = 1, INTERLEAVE2 = 2, INTERLEAVE4 = 4}; 18 | 19 | uint8_t* rom; 20 | 21 | // Size of rom 22 | uint32_t length; 23 | 24 | // Successfully loaded 25 | bool loaded; 26 | 27 | RomLoader(); 28 | ~RomLoader(); 29 | void init(uint32_t); 30 | 31 | int (RomLoader::*load)(const char*, const int, const int, const int, const uint8_t, const bool); 32 | int load_rom(const char* filename, const int offset, const int length, const int expected_crc, const uint8_t mode = NORMAL, const bool verbose = true); 33 | int load_crc32(const char* debug, const int offset, const int length, const int expected_crc, const uint8_t mode = NORMAL, const bool verbose = true); 34 | int load_binary(const char* filename); 35 | void unload(void); 36 | 37 | // ---------------------------------------------------------------------------- 38 | // Used by translated 68000 Code 39 | // ---------------------------------------------------------------------------- 40 | 41 | inline uint32_t read32(uint32_t* addr) 42 | { 43 | uint32_t data = (rom[*addr] << 24) | (rom[*addr+1] << 16) | (rom[*addr+2] << 8) | (rom[*addr+3]); 44 | *addr += 4; 45 | return data; 46 | } 47 | 48 | inline uint16_t read16(uint32_t* addr) 49 | { 50 | uint16_t data = (rom[*addr] << 8) | (rom[*addr+1]); 51 | *addr += 2; 52 | return data; 53 | } 54 | 55 | inline uint8_t read8(uint32_t* addr) 56 | { 57 | return rom[(*addr)++]; 58 | } 59 | 60 | inline uint32_t read32(uint32_t addr) 61 | { 62 | return (rom[addr] << 24) | (rom[addr+1] << 16) | (rom[addr+2] << 8) | rom[addr+3]; 63 | } 64 | 65 | inline uint16_t read16(uint32_t addr) 66 | { 67 | return (rom[addr] << 8) | rom[addr+1]; 68 | } 69 | 70 | inline uint8_t read8(uint32_t addr) 71 | { 72 | return rom[addr]; 73 | } 74 | 75 | // ---------------------------------------------------------------------------- 76 | // Used by translated Z80 Code 77 | // Note that the endian is reversed compared with the 68000 code. 78 | // ---------------------------------------------------------------------------- 79 | 80 | inline uint16_t read16(uint16_t* addr) 81 | { 82 | uint16_t data = (rom[*addr+1] << 8) | (rom[*addr]); 83 | *addr += 2; 84 | return data; 85 | } 86 | 87 | inline uint8_t read8(uint16_t* addr) 88 | { 89 | return rom[(*addr)++]; 90 | } 91 | 92 | inline uint16_t read16(uint16_t addr) 93 | { 94 | return (rom[addr+1] << 8) | rom[addr]; 95 | } 96 | 97 | inline uint8_t read8(uint16_t addr) 98 | { 99 | return rom[addr]; 100 | } 101 | 102 | private: 103 | int create_map(); 104 | int filesize(const char* filename); 105 | }; -------------------------------------------------------------------------------- /src/main/roms.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Load OutRun ROM Set. 3 | 4 | Copyright Chris White. 5 | See license.txt for more details. 6 | ***************************************************************************/ 7 | 8 | #include 9 | #include 10 | #include "stdint.hpp" 11 | #include "roms.hpp" 12 | 13 | Roms roms; 14 | 15 | Roms::Roms() 16 | { 17 | jap_rom_status = -1; 18 | rom0p = NULL; 19 | rom1p = NULL; 20 | } 21 | 22 | Roms::~Roms(void) 23 | { 24 | } 25 | 26 | // Tidier way to address pointer to member function 27 | // Expanded example: (rom0.*(rom0.load))("epr-10380b.133", 0x00000, 0x10000, 0x1f6cadad, RomLoader::INTERLEAVE2); 28 | #define LOAD(rom, args) (rom.*(rom.load)) args 29 | 30 | bool Roms::load_revb_roms(bool fixed_rom) 31 | { 32 | // If incremented, a rom has failed to load. 33 | int status = 0; 34 | 35 | // Load Master CPU ROMs 36 | rom0.init(0x40000); 37 | status += LOAD(rom0, ("epr-10380b.133", 0x00000, 0x10000, 0x1f6cadad, RomLoader::INTERLEAVE2, VERBOSE)); 38 | status += LOAD(rom0, ("epr-10382b.118", 0x00001, 0x10000, 0xc4c3fa1a, RomLoader::INTERLEAVE2, VERBOSE)); 39 | status += LOAD(rom0, ("epr-10381b.132", 0x20000, 0x10000, 0xbe8c412b, RomLoader::INTERLEAVE2, VERBOSE)); 40 | status += LOAD(rom0, ("epr-10383b.117", 0x20001, 0x10000, 0x10a2014a, RomLoader::INTERLEAVE2, VERBOSE)); 41 | 42 | // Load Slave CPU ROMs 43 | rom1.init(0x40000); 44 | status += LOAD(rom1, ("epr-10327a.76", 0x00000, 0x10000, 0xe28a5baf, RomLoader::INTERLEAVE2, VERBOSE)); 45 | status += LOAD(rom1, ("epr-10329a.58", 0x00001, 0x10000, 0xda131c81, RomLoader::INTERLEAVE2, VERBOSE)); 46 | status += LOAD(rom1, ("epr-10328a.75", 0x20000, 0x10000, 0xd5ec5e5d, RomLoader::INTERLEAVE2, VERBOSE)); 47 | status += LOAD(rom1, ("epr-10330a.57", 0x20001, 0x10000, 0xba9ec82a, RomLoader::INTERLEAVE2, VERBOSE)); 48 | 49 | // Load Non-Interleaved Tile ROMs 50 | tiles.init(0x30000); 51 | status += LOAD(tiles, ("opr-10268.99", 0x00000, 0x08000, 0x95344b04, RomLoader::NORMAL, VERBOSE)); 52 | status += LOAD(tiles, ("opr-10232.102", 0x08000, 0x08000, 0x776ba1eb, RomLoader::NORMAL, VERBOSE)); 53 | status += LOAD(tiles, ("opr-10267.100", 0x10000, 0x08000, 0xa85bb823, RomLoader::NORMAL, VERBOSE)); 54 | status += LOAD(tiles, ("opr-10231.103", 0x18000, 0x08000, 0x8908bcbf, RomLoader::NORMAL, VERBOSE)); 55 | status += LOAD(tiles, ("opr-10266.101", 0x20000, 0x08000, 0x9f6f1a74, RomLoader::NORMAL, VERBOSE)); 56 | status += LOAD(tiles, ("opr-10230.104", 0x28000, 0x08000, 0x686f5e50, RomLoader::NORMAL, VERBOSE)); 57 | 58 | // Load Non-Interleaved Road ROMs (2 identical roms, 1 for each road) 59 | road.init(0x10000); 60 | status += LOAD(road, ("opr-10185.11", 0x000000, 0x08000, 0x22794426, RomLoader::NORMAL, VERBOSE)); 61 | status += LOAD(road, ("opr-10186.47", 0x008000, 0x08000, 0x22794426, RomLoader::NORMAL, VERBOSE)); 62 | 63 | // Load Interleaved Sprite ROMs 64 | sprites.init(0x100000); 65 | status += LOAD(sprites, ("mpr-10371.9", 0x000000, 0x20000, 0x7cc86208, RomLoader::INTERLEAVE4, VERBOSE)); 66 | status += LOAD(sprites, ("mpr-10373.10", 0x000001, 0x20000, 0xb0d26ac9, RomLoader::INTERLEAVE4, VERBOSE)); 67 | status += LOAD(sprites, ("mpr-10375.11", 0x000002, 0x20000, 0x59b60bd7, RomLoader::INTERLEAVE4, VERBOSE)); 68 | status += LOAD(sprites, ("mpr-10377.12", 0x000003, 0x20000, 0x17a1b04a, RomLoader::INTERLEAVE4, VERBOSE)); 69 | status += LOAD(sprites, ("mpr-10372.13", 0x080000, 0x20000, 0xb557078c, RomLoader::INTERLEAVE4, VERBOSE)); 70 | status += LOAD(sprites, ("mpr-10374.14", 0x080001, 0x20000, 0x8051e517, RomLoader::INTERLEAVE4, VERBOSE)); 71 | status += LOAD(sprites, ("mpr-10376.15", 0x080002, 0x20000, 0xf3b8f318, RomLoader::INTERLEAVE4, VERBOSE)); 72 | status += LOAD(sprites, ("mpr-10378.16", 0x080003, 0x20000, 0xa1062984, RomLoader::INTERLEAVE4, VERBOSE)); 73 | 74 | // Load Z80 Sound ROM 75 | // Note: This is a deliberate decision to double the Z80 ROM Space to accomodate extra FM based music 76 | z80.init(0x10000); 77 | status += LOAD(z80, ("epr-10187.88", 0x0000, 0x08000, 0xa10abaa9, RomLoader::NORMAL, VERBOSE)); 78 | 79 | // Load Sega PCM Chip Samples 80 | pcm.init(0x60000); 81 | status += LOAD(pcm, ("opr-10193.66", 0x00000, 0x08000, 0xbcd10dde, RomLoader::NORMAL, VERBOSE)); 82 | status += LOAD(pcm, ("opr-10192.67", 0x10000, 0x08000, 0x770f1270, RomLoader::NORMAL, VERBOSE)); 83 | status += LOAD(pcm, ("opr-10191.68", 0x20000, 0x08000, 0x20a284ab, RomLoader::NORMAL, VERBOSE)); 84 | status += LOAD(pcm, ("opr-10190.69", 0x30000, 0x08000, 0x7cab70e2, RomLoader::NORMAL, VERBOSE)); 85 | status += LOAD(pcm, ("opr-10189.70", 0x40000, 0x08000, 0x01366b54, RomLoader::NORMAL, VERBOSE)); 86 | status += LOAD(pcm, ("opr-10188.71", 0x50000, 0x08000, 0xbad30ad9, RomLoader::NORMAL, VERBOSE)); 87 | status += load_pcm_rom(fixed_rom); 88 | 89 | // If status has been incremented, a rom has failed to load. 90 | return status == 0; 91 | } 92 | 93 | bool Roms::load_japanese_roms() 94 | { 95 | // Only attempt to initalize the arrays once. 96 | if (jap_rom_status == -1) 97 | { 98 | j_rom0.init(0x40000); 99 | j_rom1.init(0x40000); 100 | } 101 | 102 | // If incremented, a rom has failed to load. 103 | jap_rom_status = 0; 104 | 105 | // Load Master CPU ROMs 106 | jap_rom_status += LOAD(j_rom0, ("epr-10380.133", 0x00000, 0x10000, 0xe339e87a, RomLoader::INTERLEAVE2, VERBOSE)); 107 | jap_rom_status += LOAD(j_rom0, ("epr-10382.118", 0x00001, 0x10000, 0x65248dd5, RomLoader::INTERLEAVE2, VERBOSE)); 108 | jap_rom_status += LOAD(j_rom0, ("epr-10381.132", 0x20000, 0x10000, 0xbe8c412b, RomLoader::INTERLEAVE2, VERBOSE)); 109 | jap_rom_status += LOAD(j_rom0, ("epr-10383.117", 0x20001, 0x10000, 0xdcc586e7, RomLoader::INTERLEAVE2, VERBOSE)); 110 | 111 | // Load Slave CPU ROMs 112 | jap_rom_status += LOAD(j_rom1, ("epr-10327.76", 0x00000, 0x10000, 0xda99d855, RomLoader::INTERLEAVE2, VERBOSE)); 113 | jap_rom_status += LOAD(j_rom1, ("epr-10329.58", 0x00001, 0x10000, 0xfe0fa5e2, RomLoader::INTERLEAVE2, VERBOSE)); 114 | jap_rom_status += LOAD(j_rom1, ("epr-10328.75", 0x20000, 0x10000, 0x3c0e9a7f, RomLoader::INTERLEAVE2, VERBOSE)); 115 | jap_rom_status += LOAD(j_rom1, ("epr-10330.57", 0x20001, 0x10000, 0x59786e99, RomLoader::INTERLEAVE2, VERBOSE)); 116 | // If status has been incremented, a rom has failed to load. 117 | return jap_rom_status == 0; 118 | } 119 | 120 | int Roms::load_pcm_rom(bool fixed_rom) 121 | { 122 | int status = 0; 123 | if (fixed_rom) 124 | { 125 | status = LOAD(pcm, ("opr-10188.71f", 0x50000, 0x08000, 0x37598616, RomLoader::NORMAL, false)); 126 | if (status == 1) 127 | status = LOAD(pcm, ("opr-10188.71f", 0x50000, 0x08000, 0xC2DE09B2, RomLoader::NORMAL, VERBOSE)); 128 | } 129 | else 130 | { 131 | status = LOAD(pcm, ("opr-10188.71", 0x50000, 0x08000, 0xbad30ad9, RomLoader::NORMAL, VERBOSE)); 132 | } 133 | 134 | //return LOAD(pcm, (fixed_rom ? "opr-10188.71f" : "opr-10188.71", 0x50000, 0x08000, fixed_rom ? 0x37598616 : 0xbad30ad9, RomLoader::NORMAL)) == 0; 135 | return status; 136 | } 137 | 138 | bool Roms::load_ym_data(const char* filename) 139 | { 140 | RomLoader data; 141 | if (data.load_binary(filename) == 0) 142 | { 143 | if (data.length < 0x8000) 144 | { 145 | memcpy(z80.rom + 0x8000, data.rom, data.length); 146 | data.unload(); 147 | return true; 148 | } 149 | else 150 | { 151 | std::cout << "YM Data is too large: " << filename << std::endl; 152 | } 153 | } 154 | return false; 155 | } -------------------------------------------------------------------------------- /src/main/roms.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Load OutRun ROM Set. 3 | 4 | Copyright Chris White. 5 | See license.txt for more details. 6 | ***************************************************************************/ 7 | 8 | #pragma once 9 | 10 | #include "romloader.hpp" 11 | 12 | class Roms 13 | { 14 | public: 15 | // Western ROMs 16 | RomLoader rom0; 17 | RomLoader rom1; 18 | RomLoader tiles; 19 | RomLoader sprites; 20 | RomLoader road; 21 | RomLoader z80; 22 | RomLoader pcm; 23 | 24 | // Japanese ROMs 25 | RomLoader j_rom0; 26 | RomLoader j_rom1; 27 | 28 | // Paged Roms (Swap between Jap and Western) 29 | RomLoader* rom0p; 30 | RomLoader* rom1p; 31 | 32 | Roms(); 33 | ~Roms(); 34 | bool load_revb_roms(bool); 35 | bool load_japanese_roms(); 36 | int load_pcm_rom(bool); 37 | bool load_ym_data(const char* filename); 38 | 39 | private: 40 | int jap_rom_status; 41 | const static bool VERBOSE = true; 42 | }; 43 | 44 | extern Roms roms; -------------------------------------------------------------------------------- /src/main/sdl2/audio.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | SDL Audio Code. 3 | 4 | This is the SDL specific audio code. 5 | If porting to a non-SDL platform, you would need to replace this class. 6 | 7 | It takes the output from the PCM and YM chips, mixes them and then 8 | outputs appropriately. 9 | 10 | In order to achieve seamless audio, when audio is enabled the framerate 11 | is adjusted to essentially sync the video to the audio output. 12 | 13 | This is based upon code from the Atari800 emulator project. 14 | Copyright (c) 1998-2008 Atari800 development team 15 | ***************************************************************************/ 16 | 17 | #pragma once 18 | 19 | #include "globals.hpp" 20 | #include 21 | 22 | #ifdef COMPILE_SOUND_CODE 23 | 24 | struct wav_t { 25 | uint8_t loaded; 26 | int16_t *data; 27 | uint32_t pos; 28 | uint32_t length; 29 | }; 30 | 31 | class Audio 32 | { 33 | public: 34 | Audio(); 35 | ~Audio(); 36 | 37 | void init(); 38 | void tick(); 39 | void start_audio(); 40 | void stop_audio(); 41 | double adjust_speed(); 42 | void load_wav(const char* filename); 43 | void clear_wav(); 44 | 45 | private: 46 | // Enable/Disable Sound 47 | bool sound_enabled; 48 | 49 | // Stereo. Could be changed, requires some recoding. 50 | static const uint32_t CHANNELS = 2; 51 | 52 | // 16-Bit Audio Output. Could be changed, requires some recoding. 53 | static const uint32_t BITS = 16; 54 | 55 | // Low value = Responsiveness, chance of drop out. 56 | // High value = Laggy, less chance of drop out. 57 | static const uint32_t SAMPLES = 1024; 58 | 59 | // Latency (in ms) and thus target buffer size 60 | const static int SND_DELAY = 20; 61 | 62 | // allowed "spread" between too many and too few samples in the buffer (ms) 63 | const static int SND_SPREAD = 7; 64 | 65 | // Buffer used to mix PCM and YM channels together 66 | uint16_t* mix_buffer; 67 | 68 | wav_t wavfile; 69 | 70 | // Estimated gap 71 | int gap_est; 72 | 73 | // Cumulative audio difference 74 | double avg_gap; 75 | 76 | void clear_buffers(); 77 | void pause_audio(); 78 | void resume_audio(); 79 | 80 | // SDL2 audio device 81 | SDL_AudioDeviceID dev; 82 | }; 83 | #else 84 | class Audio 85 | { 86 | public: 87 | // Enable/Disable Sound 88 | bool sound_enabled = false; 89 | 90 | Audio() {} 91 | ~Audio() {} 92 | 93 | void init() {} 94 | void tick() {} 95 | void start_audio() {} 96 | void stop_audio() {} 97 | double adjust_speed() { return 1.0; } 98 | void load_wav(const char* filename); 99 | void clear_wav() {} 100 | }; 101 | #endif 102 | -------------------------------------------------------------------------------- /src/main/sdl2/input.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | SDL Based Input Handling. 3 | 4 | Populates keys array with user input. 5 | If porting to a non-SDL platform, you would need to replace this class. 6 | 7 | Copyright Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class Input 16 | { 17 | public: 18 | enum presses 19 | { 20 | LEFT = 0, 21 | RIGHT = 1, 22 | UP = 2, 23 | DOWN = 3, 24 | ACCEL = 4, 25 | BRAKE = 5, 26 | GEAR1 = 6, 27 | GEAR2 = 7, 28 | 29 | START = 8, 30 | COIN = 9, 31 | VIEWPOINT = 10, 32 | 33 | PAUSE = 11, 34 | STEP = 12, 35 | TIMER = 13, 36 | MENU = 14, 37 | }; 38 | 39 | bool keys[15]; 40 | bool keys_old[15]; 41 | 42 | enum limits 43 | { 44 | SW_LEFT = 0, 45 | SW_CENTRE = 1, 46 | SW_RIGHT = 2, 47 | }; 48 | bool motor_limits[3]; 49 | 50 | // Has gamepad been found? 51 | bool gamepad; 52 | 53 | // Gamepad supports rumble? 54 | int rumble_supported; 55 | 56 | // Use analog controls 57 | int analog; 58 | 59 | // Latch last key press for redefines 60 | int key_press; 61 | 62 | // Latch last joystick button press for redefines 63 | int16_t joy_button; 64 | 65 | // Analog Controls 66 | int wheel, a_wheel; 67 | int a_accel; 68 | int a_brake; 69 | int a_motor; 70 | 71 | Input(void); 72 | ~Input(void); 73 | 74 | void init(int, int*, int*, const int, int*, bool*, int*); 75 | void open_joy(); 76 | void close_joy(); 77 | 78 | void handle_key_up(SDL_Keysym*); 79 | void handle_key_down(SDL_Keysym*); 80 | void handle_joy_axis(SDL_JoyAxisEvent*); 81 | void handle_joy_down(SDL_JoyButtonEvent*); 82 | void handle_joy_up(SDL_JoyButtonEvent*); 83 | void handle_joy_hat(SDL_JoyHatEvent*); 84 | void handle_controller_axis(SDL_ControllerAxisEvent*); 85 | void handle_controller_down(SDL_ControllerButtonEvent*); 86 | void handle_controller_up(SDL_ControllerButtonEvent*); 87 | void frame_done(); 88 | bool is_pressed(presses p); 89 | bool is_pressed_clear(presses p); 90 | bool has_pressed(presses p); 91 | void reset_axis_config(); 92 | int get_axis_config(); 93 | void set_rumble(bool, float strength = 1.0f); 94 | 95 | private: 96 | static const int CENTRE = 0x80; 97 | 98 | // SDL Joystick / Keypad 99 | SDL_Joystick *stick; 100 | SDL_GameController* controller; 101 | SDL_Haptic* haptic; 102 | 103 | // Configurations for keyboard and joypad 104 | int pad_id; 105 | int* pad_config; 106 | int* key_config; 107 | int* axis; 108 | bool* invert; 109 | 110 | int wheel_zone; 111 | int wheel_dead; 112 | 113 | // Last axis used 114 | int axis_last , axis_counter, axis_config; 115 | 116 | void bind_axis(SDL_GameControllerAxis ax, int offset); 117 | void bind_button(SDL_GameControllerButton button, int offset); 118 | void handle_key(const int, const bool); 119 | void handle_joy(const uint8_t, const bool); 120 | void handle_axis(const uint8_t axis, const int16_t value); 121 | void store_last_axis(const uint8_t axis, const int16_t value); 122 | int scale_trigger(const int); 123 | }; 124 | 125 | extern Input input; 126 | -------------------------------------------------------------------------------- /src/main/sdl2/renderbase.cpp: -------------------------------------------------------------------------------- 1 | #include "renderbase.hpp" 2 | #include 3 | #include 4 | 5 | RenderBase::RenderBase() 6 | { 7 | surface = NULL; 8 | screen_pixels = NULL; 9 | 10 | orig_width = 0; 11 | orig_height = 0; 12 | } 13 | 14 | // Setup screen size 15 | bool RenderBase::sdl_screen_size() 16 | { 17 | if (orig_width == 0 || orig_height == 0) 18 | { 19 | SDL_DisplayMode info; 20 | SDL_GetCurrentDisplayMode(0, &info); 21 | 22 | orig_width = info.w; 23 | orig_height = info.h; 24 | } 25 | 26 | scn_width = orig_width; 27 | scn_height = orig_height; 28 | 29 | return true; 30 | } 31 | 32 | // See: SDL_PixelFormat 33 | #define CURRENT_RGB() (r << Rshift) | (g << Gshift) | (b << Bshift); 34 | 35 | void RenderBase::convert_palette(uint32_t adr, uint32_t r1, uint32_t g1, uint32_t b1) 36 | { 37 | adr >>= 1; 38 | 39 | uint32_t r = r1 * 8; 40 | uint32_t g = g1 * 8; 41 | uint32_t b = b1 * 8; 42 | 43 | rgb[adr] = CURRENT_RGB(); 44 | 45 | // Create shadow colours at end of RGB array 46 | r = r1 * shadow_multi / 31; 47 | g = g1 * shadow_multi / 31; 48 | b = b1 * shadow_multi / 31; 49 | 50 | rgb[adr + S16_PALETTE_ENTRIES] = CURRENT_RGB(); // Add to the end of the array 51 | 52 | // Highlight colour code would be added here, but unused. 53 | } 54 | 55 | void RenderBase::set_shadow_intensity(float f) 56 | { 57 | shadow_multi = (int) std::round(255.0f * f); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/sdl2/renderbase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../stdint.hpp" 4 | #include "../globals.hpp" 5 | 6 | #include 7 | 8 | // Abstract Rendering Class 9 | class RenderBase 10 | { 11 | public: 12 | RenderBase(); 13 | 14 | virtual bool init(int src_width, int src_height, 15 | int scale, 16 | int video_mode, 17 | int scanlines) = 0; 18 | virtual void disable() = 0; 19 | virtual bool start_frame() = 0; 20 | virtual bool finalize_frame() = 0; 21 | virtual void draw_frame(uint16_t* pixels) = 0; 22 | void convert_palette(uint32_t adr, uint32_t r1, uint32_t g1, uint32_t b1); 23 | void set_shadow_intensity(float f); 24 | virtual bool supports_window() { return true; } 25 | virtual bool supports_vsync() { return false; } 26 | 27 | protected: 28 | SDL_Surface *surface; 29 | 30 | // Palette Lookup 31 | uint32_t rgb[S16_PALETTE_ENTRIES * 2]; // Extended to hold shadow colours 32 | 33 | uint32_t *screen_pixels; 34 | 35 | // Original Screen Width & Height 36 | uint16_t orig_width, orig_height; 37 | 38 | // -------------------------------------------------------------------------------------------- 39 | // Screen setup properties. Example below: 40 | // ________________________ 41 | // | | | | <- screen size (e.g. 1280 x 720) 42 | // | | | | 43 | // | | |<-|--- destination size (e.g. 1027 x 720) to maintain aspect ratio 44 | // | | | | 45 | // | | | | source size (e.g. 320 x 224) System 16 proportions 46 | // |__|________________|__| 47 | // 48 | // -------------------------------------------------------------------------------------------- 49 | 50 | // Source texture / pixel array that we are going to manipulate 51 | int src_width, src_height; 52 | 53 | // Destination window width and height 54 | int dst_width, dst_height; 55 | 56 | // Screen width and height 57 | int scn_width, scn_height; 58 | 59 | // Full-Screen, Stretch, Window 60 | int video_mode; 61 | 62 | // Scanline density. 0 = Off, 1 = Full 63 | int scanlines; 64 | 65 | // Screen Scale 66 | int scale; 67 | 68 | // Offsets (for full-screen mode, where x/y resolution isn't a multiple of the original height) 69 | uint32_t screen_xoff, screen_yoff; 70 | 71 | // SDL Pixel Format Codes. These differ between platforms. 72 | uint8_t Rshift, Gshift, Bshift; 73 | uint32_t Rmask, Gmask, Bmask; 74 | 75 | // Shadow intensity multiplier 76 | int shadow_multi; 77 | 78 | bool sdl_screen_size(); 79 | }; -------------------------------------------------------------------------------- /src/main/sdl2/rendergl.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Open GL Video Rendering. 3 | 4 | Useful References: 5 | http://www.sdltutorials.com/sdl-opengl-tutorial-basics 6 | http://www.opengl.org/wiki/Common_Mistakes 7 | http://open.gl/textures 8 | 9 | Copyright Chris White. 10 | See license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include "renderbase.hpp" 16 | #include 17 | 18 | class Render : public RenderBase 19 | { 20 | public: 21 | Render(); 22 | bool init(int src_width, int src_height, 23 | int scale, 24 | int video_mode, 25 | int scanlines); 26 | void disable(); 27 | bool start_frame(); 28 | bool finalize_frame(); 29 | void draw_frame(uint16_t* pixels); 30 | bool supports_vsync(); 31 | 32 | private: 33 | // Texture IDs 34 | const static int SCREEN = 0; 35 | const static int SCANLN = 1; 36 | 37 | GLuint textures[2]; 38 | GLuint dlist; // GL display list 39 | 40 | SDL_GLContext glcontext; 41 | SDL_Window *window; 42 | }; 43 | -------------------------------------------------------------------------------- /src/main/sdl2/rendergles.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Open GL Video Rendering. 3 | 4 | Useful References: 5 | http://www.sdltutorials.com/sdl-opengl-tutorial-basics 6 | http://www.opengl.org/wiki/Common_Mistakes 7 | http://open.gl/textures 8 | 9 | Copyright Chris White. 10 | See license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include 16 | #include "renderbase.hpp" 17 | 18 | struct __ShaderInfo 19 | { 20 | GLuint program; 21 | GLint u_vp_matrix; 22 | GLint u_texture; 23 | GLint a_position; // vertex_coord; 24 | GLint a_texcoord; // tex_coord; 25 | GLint a_color; // color 26 | 27 | GLint lut_tex_coord; 28 | 29 | GLint input_size; 30 | GLint output_size; 31 | GLint texture_size; 32 | GLfloat scanline_bright; 33 | }; 34 | 35 | class Render : public RenderBase 36 | { 37 | public: 38 | Render(); 39 | ~Render(); 40 | bool init(int src_width, int src_height, 41 | int scale, 42 | int video_mode, 43 | int scanlines); 44 | void disable(); 45 | bool start_frame(); 46 | bool finalize_frame(); 47 | void draw_frame(uint16_t* pixels); 48 | bool supports_window() { return false; } 49 | bool supports_vsync(); 50 | 51 | 52 | private: 53 | // Texture IDs 54 | const static int SCREEN = 0; 55 | 56 | GLuint buffers[3]; 57 | GLuint texture; 58 | 59 | struct __ShaderInfo shader; 60 | 61 | void gles2_init_shaders (unsigned texture_width, unsigned texture_height, 62 | unsigned output_width, unsigned output_height, int scanlines); 63 | 64 | GLuint CreateProgram(const char *vertex_shader_src, const char *fragment_shader_src); 65 | GLuint CreateShader(GLenum type, const char *shader_src); 66 | void SetOrtho(float m[4][4], float left, float right, float bottom, float top, float near, float far, float scale_x, float scale_y); 67 | 68 | SDL_GLContext glcontext; 69 | SDL_Window *window; 70 | }; 71 | -------------------------------------------------------------------------------- /src/main/sdl2/rendersurface.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | SDL2 Hardware Surface Video Rendering. 3 | 4 | Known Bugs: 5 | - Missing Scanlines 6 | 7 | Copyright Manuel Alfayate, Chris White. 8 | See license.txt for more details. 9 | ***************************************************************************/ 10 | 11 | #include 12 | 13 | #include "rendersurface.hpp" 14 | #include "frontend/config.hpp" 15 | 16 | Render::Render(void) 17 | { 18 | } 19 | 20 | Render::~Render(void) 21 | { 22 | } 23 | 24 | bool Render::init(int src_width, int src_height, 25 | int scale, 26 | int video_mode, 27 | int scanlines) 28 | { 29 | this->src_width = src_width; 30 | this->src_height = src_height; 31 | this->scale = scale; 32 | this->video_mode = video_mode; 33 | this->scanlines = scanlines; 34 | 35 | // Setup SDL Screen size 36 | if (!RenderBase::sdl_screen_size()) 37 | return false; 38 | 39 | int flags = SDL_WINDOW_SHOWN; 40 | 41 | // In SDL2, we calculate the output dimensions, but then in draw_frame() we won't do any scaling: SDL2 42 | // will do that for us, using the rects passed to SDL_RenderCopy(). 43 | // scn_* -> physical screen dimensions OR window dimensions. On FULLSCREEN MODE it has the physical screen 44 | // dimensions and in windowed mode it has the window dimensions. 45 | // src_* -> real, internal, frame dimensions. Will ALWAYS be 320 or 398 x 224. NEVER CHANGES. 46 | // corrected_scn_width_* -> output screen size for scaling. 47 | // In windowed mode it's the size of the window. 48 | 49 | // -------------------------------------------------------------------------------------------- 50 | // Full Screen Mode 51 | // -------------------------------------------------------------------------------------------- 52 | if (video_mode == video_settings_t::MODE_FULL || video_mode == video_settings_t::MODE_STRETCH) 53 | { 54 | flags |= (SDL_WINDOW_FULLSCREEN); // Set SDL flag 55 | 56 | // Fullscreen window size: SDL2 ignores w and h in SDL_CreateWindow() if FULLSCREEN flag 57 | // is enable, which is fine, so the window will be fullscreen of the physical videomode 58 | // size, but then, if we want to preserve ratio, we need dst_width bigger than src_width. 59 | scn_width = orig_width; 60 | scn_height = orig_height; 61 | 62 | src_rect.w = src_width; 63 | src_rect.h = src_height; 64 | src_rect.x = 0; 65 | src_rect.y = 0; 66 | 67 | if (video_mode == video_settings_t::MODE_FULL) 68 | { 69 | uint32_t w = (scn_width << 16) / src_width; 70 | uint32_t h = (scn_height << 16) / src_height; 71 | dst_rect.w = (src_width * std::min(w, h)) >> 16; 72 | dst_rect.h = (src_height * std::min(w, h)) >> 16; 73 | 74 | screen_xoff = scn_width - dst_rect.w; 75 | if (screen_xoff) 76 | screen_xoff = (screen_xoff / 2); 77 | 78 | screen_yoff = scn_height - dst_rect.h; 79 | if (screen_yoff) 80 | screen_yoff = (screen_yoff / 2) * scn_width; 81 | 82 | dst_rect.x = screen_xoff; 83 | dst_rect.y = screen_yoff; 84 | } 85 | else 86 | { 87 | dst_rect.x = 0; 88 | dst_rect.y = 0; 89 | dst_rect.w = scn_width; 90 | dst_rect.h = scn_height; 91 | } 92 | 93 | 94 | SDL_ShowCursor(false); 95 | } 96 | 97 | // -------------------------------------------------------------------------------------------- 98 | // Windowed Mode 99 | // -------------------------------------------------------------------------------------------- 100 | else 101 | { 102 | this->video_mode = video_settings_t::MODE_WINDOW; 103 | 104 | scn_width = src_width * scale; 105 | scn_height = src_height * scale; 106 | 107 | src_rect.w = src_width; 108 | src_rect.h = src_height; 109 | src_rect.x = 0; 110 | src_rect.y = 0; 111 | dst_rect.w = scn_width; 112 | dst_rect.h = scn_height; 113 | dst_rect.x = 0; 114 | dst_rect.y = 0; 115 | 116 | SDL_ShowCursor(true); 117 | } 118 | 119 | //int bpp = info->vfmt->BitsPerPixel; 120 | const int bpp = 32; 121 | 122 | // Frees (Deletes) existing surface 123 | if (surface) 124 | SDL_FreeSurface(surface); 125 | 126 | surface = SDL_CreateRGBSurface(0, 127 | src_width, 128 | src_height, 129 | bpp, 130 | 0, 131 | 0, 132 | 0, 133 | 0); 134 | 135 | if (!surface) 136 | { 137 | std::cerr << "Surface creation failed: " << SDL_GetError() << std::endl; 138 | return false; 139 | } 140 | 141 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, config.video.filtering ? "linear" : "nearest"); 142 | window = SDL_CreateWindow( 143 | "Cannonball", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, scn_width, scn_height, 144 | flags); 145 | 146 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); 147 | texture = SDL_CreateTexture(renderer, 148 | SDL_PIXELFORMAT_ARGB8888, 149 | SDL_TEXTUREACCESS_STREAMING, 150 | src_width, src_height); 151 | 152 | // Convert the SDL pixel surface to 32 bit. 153 | // This is potentially a larger surface area than the internal pixel array. 154 | screen_pixels = (uint32_t*)surface->pixels; 155 | 156 | // SDL Pixel Format Information 157 | Rshift = surface->format->Rshift; 158 | Gshift = surface->format->Gshift; 159 | Bshift = surface->format->Bshift; 160 | Rmask = surface->format->Rmask; 161 | Gmask = surface->format->Gmask; 162 | Bmask = surface->format->Bmask; 163 | 164 | return true; 165 | } 166 | 167 | void Render::disable() 168 | { 169 | SDL_DestroyTexture(texture); 170 | SDL_DestroyRenderer(renderer); 171 | SDL_DestroyWindow(window); 172 | } 173 | 174 | bool Render::start_frame() 175 | { 176 | return true; 177 | } 178 | 179 | bool Render::finalize_frame() 180 | { 181 | SDL_UpdateTexture(texture, NULL, screen_pixels, src_width * sizeof (Uint32)); 182 | SDL_RenderClear(renderer); 183 | SDL_RenderCopy(renderer, texture, &src_rect, &dst_rect); 184 | 185 | // Very basic scanlines 186 | if (scanlines && scale != 1) 187 | { 188 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 189 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, ((scanlines - 1) << 8) / 100); 190 | SDL_Rect r; 191 | r.x = dst_rect.x; 192 | r.w = dst_rect.w; 193 | r.h = scale >> 1; 194 | r.y = scale >> 1; 195 | 196 | for (; r.y < dst_rect.h; r.y += scale) 197 | SDL_RenderDrawRect(renderer, &r); 198 | } 199 | 200 | SDL_RenderPresent(renderer); 201 | return true; 202 | } 203 | 204 | void Render::draw_frame(uint16_t* pixels) 205 | { 206 | uint32_t* spix = screen_pixels; 207 | 208 | // Lookup real RGB value from rgb array for backbuffer 209 | for (int i = 0; i < (src_width * src_height); i++) 210 | *(spix++) = rgb[*(pixels++)]; 211 | } -------------------------------------------------------------------------------- /src/main/sdl2/rendersurface.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | SDL2 Hardware Surface Video Rendering. 3 | 4 | Known Bugs: 5 | - Software scanlines not implemented because we do hardware post-scaling 6 | using the SDL_RenderCopy() rects from the original bitmap, so would not 7 | look good at all because they would be ruined by magnifying. 8 | 9 | Copyright Manuel Alfayate and Chris White. 10 | See license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include "renderbase.hpp" 16 | 17 | class Render : public RenderBase 18 | { 19 | public: 20 | Render(); 21 | ~Render(); 22 | bool init(int src_width, int src_height, 23 | int scale, 24 | int video_mode, 25 | int scanlines); 26 | void disable(); 27 | bool start_frame(); 28 | bool finalize_frame(); 29 | void draw_frame(uint16_t* pixels); 30 | 31 | private: 32 | // SDL2 window 33 | SDL_Window *window; 34 | 35 | // SDL2 renderer 36 | SDL_Renderer *renderer; 37 | 38 | // SDL2 texture 39 | SDL_Texture *texture; 40 | 41 | // SDL2 blitting rects for hw scaling 42 | // ratio correction using SDL_RenderCopy() 43 | SDL_Rect src_rect; 44 | SDL_Rect dst_rect; 45 | }; 46 | -------------------------------------------------------------------------------- /src/main/sdl2/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "sdl2/timer.hpp" 2 | 3 | /*************************************************************************** 4 | SDL Based Timer. 5 | 6 | Will need to be replaced if SDL library is replaced. 7 | 8 | Copyright Chris White. 9 | See license.txt for more details. 10 | ***************************************************************************/ 11 | 12 | Timer::Timer() 13 | { 14 | //Initialize the variables 15 | startTicks = 0; 16 | pausedTicks = 0; 17 | paused = false; 18 | started = false; 19 | } 20 | 21 | void Timer::start() 22 | { 23 | //Start the timer 24 | started = true; 25 | 26 | //Unpause the timer 27 | paused = false; 28 | 29 | //Get the current clock time 30 | startTicks = SDL_GetTicks(); 31 | } 32 | 33 | void Timer::stop() 34 | { 35 | //Stop the timer 36 | started = false; 37 | 38 | //Unpause the timer 39 | paused = false; 40 | } 41 | 42 | void Timer::pause() 43 | { 44 | //If the timer is running and isn't already paused 45 | if( ( started == true ) && ( paused == false ) ) 46 | { 47 | //Pause the timer 48 | paused = true; 49 | 50 | //Calculate the paused ticks 51 | pausedTicks = SDL_GetTicks() - startTicks; 52 | } 53 | } 54 | 55 | void Timer::unpause() 56 | { 57 | //If the timer is paused 58 | if( paused == true ) 59 | { 60 | //Unpause the timer 61 | paused = false; 62 | 63 | //Reset the starting ticks 64 | startTicks = SDL_GetTicks() - pausedTicks; 65 | 66 | //Reset the paused ticks 67 | pausedTicks = 0; 68 | } 69 | } 70 | 71 | int Timer::get_ticks() 72 | { 73 | //If the timer is running 74 | if( started == true ) 75 | { 76 | //If the timer is paused 77 | if( paused == true ) 78 | { 79 | //Return the number of ticks when the timer was paused 80 | return pausedTicks; 81 | } 82 | else 83 | { 84 | //Return the current time minus the start time 85 | return SDL_GetTicks() - startTicks; 86 | } 87 | } 88 | 89 | //If the timer isn't running 90 | return 0; 91 | } 92 | 93 | bool Timer::is_started() 94 | { 95 | return started; 96 | } 97 | 98 | bool Timer::is_paused() 99 | { 100 | return paused; 101 | } 102 | -------------------------------------------------------------------------------- /src/main/sdl2/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Timer 6 | { 7 | private: 8 | //The clock time when the timer started 9 | int startTicks; 10 | 11 | //The ticks stored when the timer was paused 12 | int pausedTicks; 13 | 14 | //The timer status 15 | bool paused; 16 | bool started; 17 | 18 | public: 19 | //Initializes variables 20 | Timer(); 21 | 22 | //The various clock actions 23 | void start(); 24 | void stop(); 25 | void pause(); 26 | void unpause(); 27 | 28 | //Gets the timer's time 29 | int get_ticks(); 30 | 31 | //Checks the status of the timer 32 | bool is_started(); 33 | bool is_paused(); 34 | }; -------------------------------------------------------------------------------- /src/main/stdint.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Data Types. 3 | 4 | The Boost library is only used to enforce data type size at compile 5 | time. 6 | 7 | If you're sure the sizes are correct, it can be removed for your port. 8 | 9 | Copyright Chris White. 10 | See license.txt for more details. 11 | ***************************************************************************/ 12 | 13 | #pragma once 14 | 15 | #include 16 | 17 | /** C99 Standard Naming */ 18 | #if defined(_MSC_VER) 19 | typedef signed char int8_t; 20 | typedef signed short int16_t; 21 | typedef signed int int32_t; 22 | typedef signed long long int64_t; 23 | 24 | typedef unsigned char uint8_t; 25 | typedef unsigned short uint16_t; 26 | typedef unsigned int uint32_t; 27 | typedef unsigned long long uint64_t; 28 | #else 29 | #include 30 | #endif 31 | 32 | /* Report typedef errors */ 33 | BOOST_STATIC_ASSERT_MSG(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); 34 | BOOST_STATIC_ASSERT_MSG(sizeof(int16_t) == 2, "int16_t is not of the correct size"); 35 | BOOST_STATIC_ASSERT_MSG(sizeof(int32_t) == 4, "int32_t is not of the correct size"); 36 | BOOST_STATIC_ASSERT_MSG(sizeof(int64_t) == 8, "int64_t is not of the correct size"); 37 | 38 | BOOST_STATIC_ASSERT_MSG(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); 39 | BOOST_STATIC_ASSERT_MSG(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); 40 | BOOST_STATIC_ASSERT_MSG(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); 41 | BOOST_STATIC_ASSERT_MSG(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); 42 | -------------------------------------------------------------------------------- /src/main/trackloader.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Track Loading Code. 3 | 4 | Abstracts the level format, so that the original ROMs as well as 5 | in conjunction with track data from the LayOut editor. 6 | 7 | - Handles levels (path, width, height, scenery) 8 | - Handles additional level sections (road split, end section) 9 | - Handles road/level related palettes 10 | 11 | Copyright Chris White. 12 | See license.txt for more details. 13 | ***************************************************************************/ 14 | 15 | #pragma once 16 | 17 | #include "globals.hpp" 18 | 19 | // Road Generator Palette Representation 20 | struct RoadPalette 21 | { 22 | uint32_t stripe_centre; // Centre Stripe Colour 23 | uint32_t stripe; // Stripe Colour 24 | uint32_t side; // Side Colour 25 | uint32_t road; // Main Road Colour 26 | }; 27 | 28 | // OutRun Level Representation 29 | struct Level 30 | { 31 | uint8_t* path; // CPU 1 Path Data 32 | uint8_t* curve; // Track Curve Information (Derived From Path) 33 | uint8_t* width_height; // Track Width & Height Lookups 34 | uint8_t* scenery; // Track Scenery Lookups 35 | 36 | uint16_t pal_sky; // Index into Sky Palettes 37 | uint16_t pal_gnd; // Index into Ground Palettes 38 | 39 | RoadPalette palr1; // Road 1 Generator Palette 40 | RoadPalette palr2; // Road 2 Generator Palette 41 | }; 42 | 43 | // LayOut Binary Header Format 44 | struct LayOut 45 | { 46 | static const uint32_t EXPECTED_VERSION = 1; 47 | 48 | static const uint32_t HEADER = 0; 49 | static const uint32_t PATH = HEADER + sizeof(uint32_t) + sizeof(uint8_t); 50 | static const uint32_t LEVELS = PATH + sizeof(uint32_t); 51 | static const uint32_t END_PATH = LEVELS + (STAGES * sizeof(uint32_t)); 52 | static const uint32_t END_LEVELS = END_PATH + sizeof(uint32_t); 53 | static const uint32_t SPLIT_PATH = END_LEVELS + (5 * sizeof(uint32_t)); 54 | static const uint32_t SPLIT_LEVEL = SPLIT_PATH + sizeof(uint32_t); 55 | static const uint32_t PAL_SKY = SPLIT_LEVEL + sizeof(uint32_t); 56 | static const uint32_t PAL_GND = PAL_SKY + sizeof(uint32_t); 57 | static const uint32_t SPRITE_MAPS = PAL_GND + sizeof(uint32_t); 58 | static const uint32_t HEIGHT_MAPS = SPRITE_MAPS + sizeof(uint32_t); 59 | }; 60 | 61 | class RomLoader; 62 | 63 | class TrackLoader 64 | { 65 | 66 | public: 67 | // Reference to stage mapping/ordering table 68 | uint8_t* stage_data; 69 | 70 | Level* current_level; 71 | 72 | const static int MODE_ORIGINAL = 0; 73 | const static int MODE_LAYOUT = 1; 74 | 75 | // Display start line on Stage 1 76 | uint8_t display_start_line; 77 | 78 | uint32_t curve_offset; 79 | uint32_t wh_offset; 80 | uint32_t scenery_offset; 81 | 82 | // Shared Structures 83 | uint8_t* pal_sky_data; 84 | uint8_t* pal_gnd_data; 85 | uint8_t* heightmap_data; 86 | uint8_t* scenerymap_data; 87 | 88 | uint32_t pal_sky_offset; 89 | uint32_t pal_gnd_offset; 90 | uint32_t heightmap_offset; 91 | uint32_t scenerymap_offset; 92 | 93 | TrackLoader(); 94 | ~TrackLoader(); 95 | 96 | void init(bool jap); 97 | bool set_layout_track(const char* filename); 98 | void init_original_tracks(bool jap); 99 | void init_layout_tracks(bool jap); 100 | void init_track(const uint32_t); 101 | void init_track_split(); 102 | void init_track_bonus(const uint32_t); 103 | 104 | void init_path(const uint32_t); 105 | void init_path_split(); 106 | void init_path_end(); 107 | 108 | uint32_t read_pal_sky_table(uint16_t entry); 109 | uint32_t read_pal_gnd_table(uint16_t entry); 110 | uint32_t read_heightmap_table(uint16_t entry); 111 | uint32_t read_scenerymap_table(uint16_t entry); 112 | 113 | int16_t readPath(uint32_t addr); 114 | int16_t readPath(uint32_t* addr); 115 | int16_t read_width_height(uint32_t* addr); 116 | int16_t read_curve(uint32_t addr); 117 | uint16_t read_scenery_pos(); 118 | uint8_t read_total_sprites(); 119 | uint8_t read_sprite_pattern_index(); 120 | 121 | int8_t stage_offset_to_level(uint32_t); 122 | Level* get_level(uint32_t); 123 | 124 | inline int32_t read32(uint8_t* data, uint32_t* addr) 125 | { 126 | int32_t value = (data[*addr] << 24) | (data[*addr+1] << 16) | (data[*addr+2] << 8) | (data[*addr+3]); 127 | *addr += 4; 128 | return value; 129 | } 130 | 131 | inline int16_t read16(uint8_t* data, uint32_t* addr) 132 | { 133 | int16_t value = (data[*addr] << 8) | (data[*addr+1]); 134 | *addr += 2; 135 | return value; 136 | } 137 | 138 | inline int8_t read8(uint8_t* data, uint32_t* addr) 139 | { 140 | return data[(*addr)++]; 141 | } 142 | 143 | inline int32_t read32(uint8_t* data, uint32_t addr) 144 | { 145 | return (data[addr] << 24) | (data[addr+1] << 16) | (data[addr+2] << 8) | data[addr+3]; 146 | } 147 | 148 | inline int16_t read16(uint8_t* data, uint32_t addr) 149 | { 150 | return (data[addr] << 8) | data[addr+1]; 151 | } 152 | 153 | inline int8_t read8(uint8_t* data, uint32_t addr) 154 | { 155 | return data[addr]; 156 | } 157 | 158 | 159 | private: 160 | RomLoader* layout; 161 | 162 | int mode; 163 | 164 | Level* levels; // Normal Stages 165 | Level* level_split; // Split Section 166 | Level* levels_end; // End Section 167 | 168 | uint8_t* current_path; // CPU 1 Road Path 169 | 170 | void setup_level(Level* l, RomLoader* data, const int STAGE_ADR); 171 | void setup_section(Level* l, RomLoader* data, const int STAGE_ADR); 172 | }; 173 | 174 | extern TrackLoader trackloader; -------------------------------------------------------------------------------- /src/main/utils.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | General C++ Helper Functions 3 | 4 | Copyright Chris White. 5 | See license.txt for more details. 6 | ***************************************************************************/ 7 | 8 | #include 9 | #include "utils.hpp" 10 | 11 | // Convert value to string 12 | std::string Utils::to_string(int i) 13 | { 14 | std::stringstream ss; 15 | ss << i; 16 | return ss.str(); 17 | } 18 | 19 | // Convert value to string 20 | std::string Utils::to_string(char c) 21 | { 22 | std::stringstream ss; 23 | ss << c; 24 | return ss.str(); 25 | } 26 | 27 | // Convert value to string 28 | //template 29 | std::string Utils::to_hex_string(int i) 30 | { 31 | std::stringstream ss; 32 | ss << std::hex << i; 33 | return ss.str(); 34 | } 35 | 36 | // Convert hex string to unsigned int 37 | uint32_t Utils::from_hex_string(std::string s) 38 | { 39 | unsigned int x; 40 | std::stringstream ss; 41 | ss << std::hex << s; 42 | ss >> x; 43 | // output it as a signed type 44 | return static_cast(x); 45 | } -------------------------------------------------------------------------------- /src/main/utils.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | General C++ Helper Functions 3 | 4 | Copyright Chris White. 5 | See license.txt for more details. 6 | ***************************************************************************/ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "stdint.hpp" 12 | 13 | class Utils 14 | { 15 | public: 16 | static std::string to_string(int i); 17 | static std::string to_string(char c); 18 | static std::string to_hex_string(int i); 19 | static uint32_t from_hex_string(std::string s); 20 | 21 | private: 22 | }; -------------------------------------------------------------------------------- /src/main/video.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | Video Rendering. 3 | 4 | - Renders the System 16 Video Layers 5 | - Handles Reads and Writes to these layers from the main game code 6 | - Interfaces with platform specific rendering code 7 | 8 | Copyright Chris White. 9 | See license.txt for more details. 10 | ***************************************************************************/ 11 | 12 | #pragma once 13 | 14 | #include "stdint.hpp" 15 | #include "globals.hpp" 16 | #include "roms.hpp" 17 | #include "hwvideo/hwtiles.hpp" 18 | #include "hwvideo/hwsprites.hpp" 19 | #include "hwvideo/hwroad.hpp" 20 | 21 | namespace shadow 22 | { 23 | const static float ORIGINAL = 0.63f; // Hardware Intensity (63%) 24 | const static float MAME = 0.78f; // Mame Intensity (78%) 25 | }; 26 | 27 | class hwsprites; 28 | class RenderBase; 29 | struct video_settings_t; 30 | 31 | class Video 32 | { 33 | public: 34 | hwsprites* sprite_layer; 35 | hwtiles* tile_layer; 36 | uint16_t *pixels; 37 | 38 | bool enabled; 39 | 40 | Video(); 41 | ~Video(); 42 | 43 | int init(Roms* roms, video_settings_t* settings); 44 | void disable(); 45 | int set_video_mode(video_settings_t* settings); 46 | void set_shadow_intensity(float); 47 | void prepare_frame(); 48 | void render_frame(); 49 | bool supports_window(); 50 | bool supports_vsync(); 51 | 52 | void clear_text_ram(); 53 | void write_text8(uint32_t, const uint8_t); 54 | void write_text16(uint32_t*, const uint16_t); 55 | void write_text16(uint32_t, const uint16_t); 56 | void write_text32(uint32_t*, const uint32_t); 57 | void write_text32(uint32_t, const uint32_t); 58 | uint8_t read_text8(uint32_t); 59 | 60 | void clear_tile_ram(); 61 | void write_tile8(uint32_t, const uint8_t); 62 | void write_tile16(uint32_t*, const uint16_t); 63 | void write_tile16(uint32_t, const uint16_t); 64 | void write_tile32(uint32_t*, const uint32_t); 65 | void write_tile32(uint32_t, const uint32_t); 66 | uint8_t read_tile8(uint32_t); 67 | 68 | void write_sprite16(uint32_t*, const uint16_t); 69 | 70 | void write_pal8(uint32_t*, const uint8_t); 71 | void write_pal16(uint32_t*, const uint16_t); 72 | void write_pal32(uint32_t*, const uint32_t); 73 | void write_pal32(uint32_t, const uint32_t); 74 | uint8_t read_pal8(uint32_t); 75 | uint16_t read_pal16(uint32_t*); 76 | uint16_t read_pal16(uint32_t); 77 | uint32_t read_pal32(uint32_t*); 78 | 79 | private: 80 | // SDL Renderer 81 | RenderBase* renderer; 82 | 83 | uint8_t palette[S16_PALETTE_ENTRIES * 2]; // 2 Bytes Per Palette Entry 84 | void refresh_palette(uint32_t); 85 | }; 86 | 87 | extern Video video; --------------------------------------------------------------------------------